[clang] [clang][ssaf] Introduce entity abstraction for SSAF (PR #169131)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 21 16:04:53 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
Author: Jan Korous (jkorous-apple)
<details>
<summary>Changes</summary>
Add core abstractions for identifying program entities across compilation and link unit boundaries in the Scalable Static Analysis Framework (SSAF).
Introduces three key components:
- BuildNamespace: Represents build artifacts (compilation units, link units)
- EntityName: Globally unique entity identifiers across compilation boundaries
- AST mapping: Functions to map Clang AST declarations to EntityNames
Entity identification uses Unified Symbol Resolution (USR) as the underlying mechanism, with extensions for sub-entities (parameters, return values) via suffixes. The abstraction allows whole-program analysis by providing stable identifiers that persist across separately compiled translation units.
---
Patch is 30.31 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169131.diff
13 Files Affected:
- (added) clang/include/clang/Analysis/Scalable/ASTEntityMapping.h (+46)
- (added) clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h (+84)
- (added) clang/include/clang/Analysis/Scalable/Model/EntityName.h (+47)
- (modified) clang/lib/Analysis/CMakeLists.txt (+1)
- (added) clang/lib/Analysis/Scalable/ASTEntityMapping.cpp (+85)
- (added) clang/lib/Analysis/Scalable/CMakeLists.txt (+19)
- (added) clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp (+72)
- (added) clang/lib/Analysis/Scalable/Model/EntityName.cpp (+44)
- (modified) clang/unittests/Analysis/CMakeLists.txt (+1)
- (added) clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp (+343)
- (added) clang/unittests/Analysis/Scalable/BuildNamespaceTest.cpp (+99)
- (added) clang/unittests/Analysis/Scalable/CMakeLists.txt (+18)
- (added) clang/unittests/Analysis/Scalable/EntityNameTest.cpp (+62)
``````````diff
diff --git a/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
new file mode 100644
index 0000000000000..a137e8b741821
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/ASTEntityMapping.h
@@ -0,0 +1,46 @@
+//===- ASTMapping.h - AST to SSAF Entity mapping ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
+
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringRef.h"
+#include <optional>
+
+namespace clang {
+namespace ssaf {
+
+/// Maps a declaration to an EntityName.
+///
+/// Supported declaration types for entity mapping:
+/// - Functions and methods
+/// - Global Variables
+/// - Function parameters
+/// - Struct/class/union type definitions
+/// - Struct/class/union fields
+///
+/// Implicit declarations and compiler builtins are not mapped.
+///
+/// \param D The declaration to map. Must not be null.
+///
+/// \return An EntityName if the declaration can be mapped, std::nullopt otherwise.
+std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D);
+
+/// Maps a function return type to an EntityName.
+///
+/// \param FD The function declaration. Must not be null.
+///
+/// \return An EntityName for the function's return type.
+std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD);
+
+} // namespace ssaf
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ASTMAPPING_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
new file mode 100644
index 0000000000000..c4bf7146e461f
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Model/BuildNamespace.h
@@ -0,0 +1,84 @@
+//===- BuildNamespace.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
+
+#include "llvm/ADT/StringRef.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace ssaf {
+
+enum class BuildNamespaceKind : unsigned short {
+ CompilationUnit,
+ LinkUnit
+};
+
+std::string toString(BuildNamespaceKind BNK);
+
+std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str);
+
+/// Represents a single step in the build process.
+class BuildNamespace {
+ BuildNamespaceKind Kind;
+ std::string Name;
+public:
+ BuildNamespace(BuildNamespaceKind Kind, llvm::StringRef Name)
+ : Kind(Kind), Name(Name.str()) {}
+
+ static BuildNamespace makeTU(llvm::StringRef CompilationId);
+
+ bool operator==(const BuildNamespace& Other) const;
+ bool operator!=(const BuildNamespace& Other) const;
+ bool operator<(const BuildNamespace& Other) const;
+
+ friend class SerializationFormat;
+};
+
+/// Represents a sequence of steps in the build process.
+class NestedBuildNamespace {
+ friend class SerializationFormat;
+
+ std::vector<BuildNamespace> Namespaces;
+
+public:
+ NestedBuildNamespace() = default;
+
+ explicit NestedBuildNamespace(const std::vector<BuildNamespace>& Namespaces)
+ : Namespaces(Namespaces) {}
+
+ explicit NestedBuildNamespace(const BuildNamespace& N) {
+ Namespaces.push_back(N);
+ }
+
+ static NestedBuildNamespace makeTU(llvm::StringRef CompilationId);
+
+ NestedBuildNamespace makeQualified(NestedBuildNamespace Namespace) {
+ auto Copy = *this;
+ for (const auto& N : Namespace.Namespaces)
+ Copy.Namespaces.push_back(N);
+ return Copy;
+ }
+
+ bool empty() const;
+
+ bool operator==(const NestedBuildNamespace& Other) const;
+ bool operator!=(const NestedBuildNamespace& Other) const;
+ bool operator<(const NestedBuildNamespace& Other) const;
+
+ friend class JSONWriter;
+ friend class LinkUnitResolution;
+};
+
+} // namespace ssaf
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_BUILD_NAMESPACE_H
diff --git a/clang/include/clang/Analysis/Scalable/Model/EntityName.h b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
new file mode 100644
index 0000000000000..7f11ef0589bf5
--- /dev/null
+++ b/clang/include/clang/Analysis/Scalable/Model/EntityName.h
@@ -0,0 +1,47 @@
+//===- EntityName.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
+#define LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
+
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace ssaf {
+
+/// Uniquely identifies an entity in a program.
+///
+/// EntityName provides a globally unique identifier for program entities that remains
+/// stable across compilation boundaries. This enables whole-program analysis to track
+/// and relate entities across separately compiled translation units.
+class EntityName {
+ std::string USR;
+ llvm::SmallString<16> Suffix;
+ NestedBuildNamespace Namespace;
+
+public:
+ EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
+ NestedBuildNamespace Namespace);
+
+ bool operator==(const EntityName& Other) const;
+ bool operator!=(const EntityName& Other) const;
+ bool operator<(const EntityName& Other) const;
+
+ EntityName makeQualified(NestedBuildNamespace Namespace);
+
+ friend class LinkUnitResolution;
+ friend class SerializationFormat;
+};
+
+} // namespace ssaf
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ENTITY_NAME_H
diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt
index 1dbd4153d856f..99a2ec684e149 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -50,3 +50,4 @@ add_clang_library(clangAnalysis
add_subdirectory(plugins)
add_subdirectory(FlowSensitive)
add_subdirectory(LifetimeSafety)
+add_subdirectory(Scalable)
diff --git a/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
new file mode 100644
index 0000000000000..87d05e8aa5dc3
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/ASTEntityMapping.cpp
@@ -0,0 +1,85 @@
+//===- ASTMapping.cpp - AST to SSAF Entity mapping --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements utilities for mapping AST declarations to SSAF entities.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/ASTEntityMapping.h"
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/SmallString.h"
+
+namespace clang {
+namespace ssaf {
+
+std::optional<EntityName> getLocalEntityNameForDecl(const Decl* D) {
+ if (!D)
+ return std::nullopt;
+
+ if (D->isImplicit())
+ return std::nullopt;
+
+ if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->getBuiltinID())
+ return std::nullopt;
+
+ if (!isa<FunctionDecl>(D) && !isa<ParmVarDecl>(D) && !isa<VarDecl>(D) &&
+ !isa<FieldDecl>(D) && !isa<RecordDecl>(D))
+ return std::nullopt;
+
+ llvm::SmallString<16> Suffix;
+ const Decl *USRDecl = D;
+
+ // For parameters, use the parent function's USR with parameter index as suffix
+ if (const auto * PVD = dyn_cast<ParmVarDecl>(D)) {
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(PVD->getParentFunctionOrMethod());
+ if (!FD)
+ return std::nullopt;
+ USRDecl = FD;
+
+ const auto ParamIdx = PVD->getFunctionScopeIndex();
+ llvm::raw_svector_ostream OS(Suffix);
+ // Parameter uses function's USR with 1-based index as suffix
+ OS << (ParamIdx + 1);
+ }
+
+ llvm::SmallString<128> USRBuf;
+ if (clang::index::generateUSRForDecl(USRDecl, USRBuf)) {
+ return std::nullopt;
+ }
+
+ if (USRBuf.empty())
+ return std::nullopt;
+
+ return EntityName(USRBuf.str(), Suffix, {});
+}
+
+std::optional<EntityName> getLocalEntityNameForFunctionReturn(const FunctionDecl* FD) {
+ if (!FD)
+ return std::nullopt;
+
+ if (FD->isImplicit())
+ return std::nullopt;
+
+ if (FD->getBuiltinID())
+ return std::nullopt;
+
+ llvm::SmallString<128> USRBuf;
+ if (clang::index::generateUSRForDecl(FD, USRBuf)) {
+ return std::nullopt;
+ }
+
+ if (USRBuf.empty())
+ return std::nullopt;
+
+ return EntityName(USRBuf.str(), "0", {});
+}
+
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt
new file mode 100644
index 0000000000000..ea4693f102cb2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangAnalysisScalable
+ ASTEntityMapping.cpp
+ Model/BuildNamespace.cpp
+ Model/EntityName.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangIndex
+ clangLex
+ clangFrontend
+
+ DEPENDS
+ )
diff --git a/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
new file mode 100644
index 0000000000000..5284a9a87a33a
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/BuildNamespace.cpp
@@ -0,0 +1,72 @@
+//===- BuildNamespace.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+namespace ssaf {
+
+std::string toString(BuildNamespaceKind BNK) {
+ switch(BNK) {
+ case BuildNamespaceKind::CompilationUnit: return "compilation_unit";
+ case BuildNamespaceKind::LinkUnit: return "link_unit";
+ }
+ llvm_unreachable("Unknown BuildNamespaceKind");
+}
+
+std::optional<BuildNamespaceKind> parseBuildNamespaceKind(llvm::StringRef Str) {
+ if (Str == "compilation_unit")
+ return BuildNamespaceKind::CompilationUnit;
+ if (Str == "link_unit")
+ return BuildNamespaceKind::LinkUnit;
+ return std::nullopt;
+}
+
+BuildNamespace BuildNamespace::makeTU(llvm::StringRef CompilationId) {
+ return BuildNamespace{BuildNamespaceKind::CompilationUnit, CompilationId.str()};
+}
+
+bool BuildNamespace::operator==(const BuildNamespace& Other) const {
+ return Kind == Other.Kind && Name == Other.Name;
+}
+
+bool BuildNamespace::operator!=(const BuildNamespace& Other) const {
+ return !(*this == Other);
+}
+
+bool BuildNamespace::operator<(const BuildNamespace& Other) const {
+ if (Kind != Other.Kind)
+ return Kind < Other.Kind;
+ return Name < Other.Name;
+}
+
+NestedBuildNamespace NestedBuildNamespace::makeTU(llvm::StringRef CompilationId) {
+ NestedBuildNamespace Result;
+ Result.Namespaces.push_back(BuildNamespace::makeTU(CompilationId));
+ return Result;
+}
+
+bool NestedBuildNamespace::empty() const {
+ return Namespaces.empty();
+}
+
+bool NestedBuildNamespace::operator==(const NestedBuildNamespace& Other) const {
+ return Namespaces == Other.Namespaces;
+}
+
+bool NestedBuildNamespace::operator!=(const NestedBuildNamespace& Other) const {
+ return !(*this == Other);
+}
+
+bool NestedBuildNamespace::operator<(const NestedBuildNamespace& Other) const {
+ return Namespaces < Other.Namespaces;
+}
+
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/lib/Analysis/Scalable/Model/EntityName.cpp b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
new file mode 100644
index 0000000000000..3404ecc58fac2
--- /dev/null
+++ b/clang/lib/Analysis/Scalable/Model/EntityName.cpp
@@ -0,0 +1,44 @@
+//===- EntityName.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/Model/EntityName.h"
+
+namespace clang {
+namespace ssaf {
+
+EntityName::EntityName(llvm::StringRef USR, llvm::StringRef Suffix,
+ NestedBuildNamespace Namespace)
+ : USR(USR.str()), Suffix(Suffix), Namespace(std::move(Namespace)) {}
+
+bool EntityName::operator==(const EntityName& Other) const {
+ return USR == Other.USR &&
+ Suffix == Other.Suffix &&
+ Namespace == Other.Namespace;
+}
+
+bool EntityName::operator!=(const EntityName& Other) const {
+ return !(*this == Other);
+}
+
+bool EntityName::operator<(const EntityName& Other) const {
+ if (USR != Other.USR)
+ return USR < Other.USR;
+ if (Suffix != Other.Suffix)
+ return Suffix.str() < Other.Suffix.str();
+ return Namespace < Other.Namespace;
+}
+
+EntityName EntityName::makeQualified(NestedBuildNamespace Namespace) {
+ auto Copy = *this;
+ Copy.Namespace = Copy.Namespace.makeQualified(Namespace);
+
+ return Copy;
+}
+
+} // namespace ssaf
+} // namespace clang
diff --git a/clang/unittests/Analysis/CMakeLists.txt b/clang/unittests/Analysis/CMakeLists.txt
index e0acf436b37c7..97e768b11db69 100644
--- a/clang/unittests/Analysis/CMakeLists.txt
+++ b/clang/unittests/Analysis/CMakeLists.txt
@@ -26,3 +26,4 @@ add_clang_unittest(ClangAnalysisTests
)
add_subdirectory(FlowSensitive)
+add_subdirectory(Scalable)
diff --git a/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
new file mode 100644
index 0000000000000..8de0df246cb65
--- /dev/null
+++ b/clang/unittests/Analysis/Scalable/ASTEntityMappingTest.cpp
@@ -0,0 +1,343 @@
+//===- unittests/Analysis/Scalable/ASTEntityMappingTest.cpp --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/ASTEntityMapping.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace ssaf {
+namespace {
+
+// Helper function to find a declaration by name
+template <typename DeclType>
+const DeclType *findDecl(ASTContext &Ctx, StringRef Name) {
+ auto Matcher = namedDecl(hasName(Name)).bind("decl");
+ auto Matches = match(Matcher, Ctx);
+ if (Matches.empty())
+ return nullptr;
+ return Matches[0].getNodeAs<DeclType>("decl");
+}
+
+TEST(ASTEntityMappingTest, FunctionDecl) {
+ auto AST = tooling::buildASTFromCode("void foo() {}");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+ ASSERT_NE(FD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(FD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, VarDecl) {
+ auto AST = tooling::buildASTFromCode("int x = 42;");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *VD = findDecl<VarDecl>(Ctx, "x");
+ ASSERT_NE(VD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(VD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, ParmVarDecl) {
+ auto AST = tooling::buildASTFromCode("void foo(int x) {}");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+ ASSERT_NE(FD, nullptr);
+ ASSERT_GT(FD->param_size(), 0u);
+
+ const auto *PVD = FD->getParamDecl(0);
+ ASSERT_NE(PVD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(PVD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, RecordDecl) {
+ auto AST = tooling::buildASTFromCode("struct S {};");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *RD = findDecl<RecordDecl>(Ctx, "S");
+ ASSERT_NE(RD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(RD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FieldDecl) {
+ auto AST = tooling::buildASTFromCode("struct S { int field; };");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FieldDecl>(Ctx, "field");
+ ASSERT_NE(FD, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(FD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, NullDecl) {
+ auto EntityName = getLocalEntityNameForDecl(nullptr);
+ EXPECT_FALSE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, ImplicitDecl) {
+ auto AST = tooling::buildASTFromCode(R"(
+ struct S {
+ S() = default;
+ };
+ )", "test.cpp", std::make_shared<PCHContainerOperations>());
+ auto &Ctx = AST->getASTContext();
+
+ const auto *RD = findDecl<CXXRecordDecl>(Ctx, "S");
+ ASSERT_NE(RD, nullptr);
+
+ // Find the implicitly-declared copy constructor
+ for (const auto *Ctor : RD->ctors()) {
+ if (Ctor->isCopyConstructor() && Ctor->isImplicit()) {
+ auto EntityName = getLocalEntityNameForDecl(Ctor);
+ EXPECT_FALSE(EntityName.has_value());
+ return;
+ }
+ }
+}
+
+TEST(ASTEntityMappingTest, BuiltinFunction) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void test() {
+ __builtin_memcpy(0, 0, 0);
+ }
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ // Find the builtin call
+ auto Matcher = callExpr().bind("call");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_FALSE(Matches.empty());
+
+ const auto *CE = Matches[0].getNodeAs<CallExpr>("call");
+ ASSERT_NE(CE, nullptr);
+
+ const auto *Callee = CE->getDirectCallee();
+ if (Callee && Callee->getBuiltinID()) {
+ auto EntityName = getLocalEntityNameForDecl(Callee);
+ EXPECT_FALSE(EntityName.has_value());
+ }
+}
+
+TEST(ASTEntityMappingTest, UnsupportedDecl) {
+ auto AST = tooling::buildASTFromCode("namespace N {}");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *ND = findDecl<NamespaceDecl>(Ctx, "N");
+ ASSERT_NE(ND, nullptr);
+
+ auto EntityName = getLocalEntityNameForDecl(ND);
+ EXPECT_FALSE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FunctionReturn) {
+ auto AST = tooling::buildASTFromCode("int foo() { return 42; }");
+ auto &Ctx = AST->getASTContext();
+
+ const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+ ASSERT_NE(FD, nullptr);
+
+ auto EntityName = getLocalEntityNameForFunctionReturn(FD);
+ EXPECT_TRUE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FunctionReturnNull) {
+ auto EntityName = getLocalEntityNameForFunctionReturn(nullptr);
+ EXPECT_FALSE(EntityName.has_value());
+}
+
+TEST(ASTEntityMappingTest, FunctionReturnBuiltin) {
+ auto AST = tooling::buildASTFromCode(R"(
+ void test() {
+ __builtin_memcpy(0, 0, 0);
+ }
+ )");
+ auto &Ctx = AST->getASTContext();
+
+ // Find the builtin call
+ auto Matcher = callExpr().bind("call");
+ auto Matches = match(Matcher, Ctx);
+ ASSERT_FALSE(Matches.empty());
+
+ const auto *CE = Matches[0].getNodeAs<CallExpr>("call");
+ ASSERT_NE(CE, nullptr);
+
+ const auto *Callee = CE->getDi...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/169131
More information about the cfe-commits
mailing list