[clang] [clang][ssaf] Rework TUSummaryBuilder::addEntity to take NamedDecl (PR #194448)

Balázs Benics via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 28 08:23:49 PDT 2026


https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/194448

>From 072e87c3f6cedcfa34d181e5a10afa3a8ee2a8cc Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Tue, 28 Apr 2026 16:20:17 +0100
Subject: [PATCH 1/2] [clang][ssaf][NFC] Hoist findFnByName and findDeclByName

---
 .../ASTEntityMappingTest.cpp                  | 31 ++++--------
 .../FindDecl.h                                | 49 +++++++++++++++++++
 2 files changed, 59 insertions(+), 21 deletions(-)
 create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/FindDecl.h

diff --git a/clang/unittests/ScalableStaticAnalysisFramework/ASTEntityMappingTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/ASTEntityMappingTest.cpp
index da921b7b7ab35..9d79647f841c8 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/ASTEntityMappingTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/ASTEntityMappingTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "FindDecl.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
@@ -20,23 +21,11 @@ using namespace clang::ast_matchers;
 namespace clang::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;
-  if (auto Result = Matches[0].getNodeAs<DeclType>("decl"))
-    return dyn_cast<DeclType>(Result->getCanonicalDecl());
-  return nullptr;
-}
-
 TEST(ASTEntityMappingTest, FunctionDecl) {
   auto AST = tooling::buildASTFromCode(R"cpp(void foo() {})cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+  const auto *FD = findFnByName("foo", Ctx);
   ASSERT_NE(FD, nullptr);
 
   auto EntityName = getEntityName(FD);
@@ -47,7 +36,7 @@ TEST(ASTEntityMappingTest, VarDecl) {
   auto AST = tooling::buildASTFromCode(R"cpp(int x = 42;)cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *VD = findDecl<VarDecl>(Ctx, "x");
+  const auto *VD = findDeclByName<VarDecl>("x", Ctx);
   ASSERT_NE(VD, nullptr);
 
   auto EntityName = getEntityName(VD);
@@ -58,7 +47,7 @@ TEST(ASTEntityMappingTest, ParmVarDecl) {
   auto AST = tooling::buildASTFromCode(R"cpp(void foo(int x) {})cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+  const auto *FD = findFnByName("foo", Ctx);
   ASSERT_NE(FD, nullptr);
   ASSERT_EQ(FD->param_size(), 1u);
 
@@ -73,7 +62,7 @@ TEST(ASTEntityMappingTest, RecordDecl) {
   auto AST = tooling::buildASTFromCode(R"cpp(struct S {};)cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *RD = findDecl<RecordDecl>(Ctx, "S");
+  const auto *RD = findDeclByName<RecordDecl>("S", Ctx);
   ASSERT_NE(RD, nullptr);
 
   auto EntityName = getEntityName(RD);
@@ -84,7 +73,7 @@ TEST(ASTEntityMappingTest, FieldDecl) {
   auto AST = tooling::buildASTFromCode(R"cpp(struct S { int field; };)cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *FD = findDecl<FieldDecl>(Ctx, "field");
+  const auto *FD = findDeclByName<FieldDecl>("field", Ctx);
   ASSERT_NE(FD, nullptr);
 
   auto EntityName = getEntityName(FD);
@@ -143,7 +132,7 @@ TEST(ASTEntityMappingTest, UnsupportedDecl) {
   auto AST = tooling::buildASTFromCode(R"cpp(namespace N {})cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *ND = findDecl<NamespaceDecl>(Ctx, "N");
+  const auto *ND = findDeclByName<NamespaceDecl>("N", Ctx);
   ASSERT_NE(ND, nullptr);
 
   auto EntityName = getEntityName(ND);
@@ -154,7 +143,7 @@ TEST(ASTEntityMappingTest, FunctionReturn) {
   auto AST = tooling::buildASTFromCode(R"cpp(int foo() { return 42; })cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *FD = findDecl<FunctionDecl>(Ctx, "foo");
+  const auto *FD = findFnByName("foo", Ctx);
   ASSERT_NE(FD, nullptr);
 
   auto EntityName = getEntityNameForReturn(FD);
@@ -196,8 +185,8 @@ TEST(ASTEntityMappingTest, DifferentFunctionsDifferentNames) {
   )cpp");
   auto &Ctx = AST->getASTContext();
 
-  const auto *Foo = findDecl<FunctionDecl>(Ctx, "foo");
-  const auto *Bar = findDecl<FunctionDecl>(Ctx, "bar");
+  const auto *Foo = findFnByName("foo", Ctx);
+  const auto *Bar = findFnByName("bar", Ctx);
   ASSERT_NE(Foo, nullptr);
   ASSERT_NE(Bar, nullptr);
 
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/FindDecl.h b/clang/unittests/ScalableStaticAnalysisFramework/FindDecl.h
new file mode 100644
index 0000000000000..07c28c610380c
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/FindDecl.h
@@ -0,0 +1,49 @@
+//===- FindDecl.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_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_FINDDECL_H
+#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_FINDDECL_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+
+namespace clang::ssaf {
+
+template <typename SomeDecl = clang::NamedDecl>
+const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
+  class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+  public:
+    StringRef SearchingName;
+    const NamedDecl *FoundDecl = nullptr;
+
+    NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
+
+    bool VisitDecl(Decl *D) override {
+      if (const auto *ND = dyn_cast<SomeDecl>(D)) {
+        if (ND->getNameAsString() == SearchingName) {
+          FoundDecl = ND;
+          return false;
+        }
+      }
+      return true;
+    }
+  };
+
+  NamedDeclFinder Finder(Name);
+
+  Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+  return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+inline const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
+  return findDeclByName<FunctionDecl>(Name, Ctx);
+}
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_FINDDECL_H

>From c60b9b9c149ffa4d93b0517a7890c52c9c1c515a Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Tue, 28 Apr 2026 16:21:21 +0100
Subject: [PATCH 2/2] [clang][ssaf] Rework addEntity to populate linkage as
 well

---
 .../EntityPointerLevel/EntityPointerLevel.h   | 12 ++-
 .../Core/TUSummary/TUSummaryBuilder.h         | 16 +++-
 .../Analyses/CallGraph/CallGraphExtractor.cpp | 17 ++--
 .../EntityPointerLevel/EntityPointerLevel.cpp | 86 +++++++++++--------
 .../UnsafeBufferUsageExtractor.cpp            | 13 +--
 .../Core/TUSummary/TUSummaryBuilder.cpp       | 51 ++++++++++-
 .../UnsafeBufferUsageTest.cpp                 | 60 +++----------
 .../TUSummaryBuilderTest.cpp                  | 80 +++++++++++++----
 8 files changed, 205 insertions(+), 130 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 429bb74fb417e..b46f0573177b3 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -11,11 +11,10 @@
 
 #include "clang/AST/Expr.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "llvm/ADT/STLFunctionalExtras.h"
 #include <set>
 
 namespace clang::ssaf {
+class TUSummaryBuilder;
 
 /// An EntityPointerLevel is associated with a level of the declared
 /// pointer/array type of an entity.  In the fully-expanded spelling of the
@@ -93,9 +92,9 @@ using EntityPointerLevelSet =
 /// \param Ctx the AST context of `E`
 /// \param AddEntity the callback provided by the caller to convert EntityNames
 /// to EntityIds.
-llvm::Expected<EntityPointerLevelSet> translateEntityPointerLevel(
-    const Expr *E, ASTContext &Ctx,
-    llvm::function_ref<EntityId(EntityName EN)> AddEntity);
+llvm::Expected<EntityPointerLevelSet>
+translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+                            TUSummaryBuilder &Builder);
 
 /// Creates a `EntityPointerLevel` from a pair of an EntityId and a pointer
 /// level:
@@ -109,8 +108,7 @@ EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
 /// \param IsFunRet true iff the created EPL is associated with the return type
 /// of a function entity.
 llvm::Expected<EntityPointerLevel>
-createEntityPointerLevel(const NamedDecl *ND,
-                         llvm::function_ref<EntityId(EntityName EN)> AddEntity,
+createEntityPointerLevel(const NamedDecl *ND, TUSummaryBuilder &Builder,
                          bool IsFunRet = false);
 
 /// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
index d386876675873..7b76dc49dc706 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
@@ -9,9 +9,11 @@
 #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_TUSUMMARY_TUSUMMARYBUILDER_H
 #define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_TUSUMMARY_TUSUMMARYBUILDER_H
 
+#include "clang/AST/DeclBase.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include <memory>
+#include <optional>
 #include <utility>
 
 namespace clang::ssaf {
@@ -23,9 +25,15 @@ class TUSummaryBuilder {
 public:
   explicit TUSummaryBuilder(TUSummary &Summary) : Summary(Summary) {}
 
-  /// Add an entity to the summary and return its EntityId.
-  /// If the entity already exists, returns the existing ID (idempotent).
-  EntityId addEntity(const EntityName &E);
+  /// Creates EntityName from the Decl, registers the entity, and sets its
+  /// linkage atomically.
+  /// \returns the EntityId, or std::nullopt if EntityName creation fails.
+  std::optional<EntityId> addEntity(const NamedDecl *D);
+
+  /// Creates EntityName for the return value of \p FD, registers the entity,
+  /// and sets its linkage atomically.
+  /// \returns the EntityId, or std::nullopt if EntityName creation fails.
+  std::optional<EntityId> addEntityForReturn(const FunctionDecl *FD);
 
   /// Associate the \p Data \c EntitySummary with the \p Entity.
   /// This consumes the \p Data only if \p Entity wasn't associated yet with the
@@ -39,6 +47,8 @@ class TUSummaryBuilder {
 private:
   TUSummary &Summary;
 
+  EntityId addEntityImpl(const EntityName &Name, const Decl *D);
+
   std::pair<EntitySummary *, bool>
   addSummaryImpl(EntityId Entity, std::unique_ptr<EntitySummary> &&Data);
 };
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
index 1dbed7e0b0d8a..22038e915a32b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
@@ -14,7 +14,6 @@
 #include "clang/Analysis/CallGraph.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "llvm/ADT/STLExtras.h"
@@ -53,8 +52,8 @@ void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx,
   // FIXME: `clang::CallGraph` does not create entries for primary templates.
   assert(!Definition->isTemplated());
 
-  auto CallerName = getEntityName(Definition);
-  if (!CallerName)
+  auto CallerId = SummaryBuilder.addEntity(Definition);
+  if (!CallerId)
     return;
 
   auto FnSummary = std::make_unique<CallGraphSummary>();
@@ -80,21 +79,19 @@ void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx,
     // FIXME: `clang::CallGraph` does not create entries for primary templates.
     assert(!CalleeDecl->isTemplated());
 
-    auto CalleeName = getEntityName(CalleeDecl);
-    if (!CalleeName)
+    auto CalleeId = SummaryBuilder.addEntity(cast<NamedDecl>(CalleeDecl));
+    if (!CalleeId)
       continue;
 
-    EntityId CalleeId = SummaryBuilder.addEntity(*CalleeName);
     if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(CalleeDecl);
         MD && MD->isVirtual()) {
-      FnSummary->VirtualCallees.insert(CalleeId);
+      FnSummary->VirtualCallees.insert(*CalleeId);
       continue;
     }
-    FnSummary->DirectCallees.insert(CalleeId);
+    FnSummary->DirectCallees.insert(*CalleeId);
   }
 
-  EntityId CallerId = SummaryBuilder.addEntity(*CallerName);
-  SummaryBuilder.addSummary(CallerId, std::move(FnSummary));
+  SummaryBuilder.addSummary(*CallerId, std::move(FnSummary));
 }
 
 static TUSummaryExtractorRegistry::Add<CallGraphExtractor>
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 52e6705ec43af..7a760b72c9d11 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -12,8 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include <optional>
 
 using namespace clang;
@@ -51,8 +50,23 @@ class EntityPointerLevelTranslator
                          E->getStmtClassName());
   }
 
-  EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
-    return EntityPointerLevel({AddEntity(Name), 1});
+  Expected<EntityPointerLevel>
+  createEntityPointerLevelFor(const NamedDecl *ND) {
+    std::optional<EntityId> Id = Builder.addEntity(ND);
+    if (!Id)
+      return makeErrAtNode(Ctx, ND, "failed to create EntityId for %s",
+                           ND->getDeclKindName());
+    return EntityPointerLevel{buildEntityPointerLevel(*Id, 1)};
+  }
+
+  Expected<EntityPointerLevel>
+  createEntityPointerLevelForReturn(const FunctionDecl *FD) {
+    std::optional<EntityId> Id = Builder.addEntityForReturn(FD);
+    if (!Id) {
+      return makeErrAtNode(Ctx, FD, "failed to create EntityId for function %s",
+                           cast<NamedDecl>(FD)->getNameAsString().c_str());
+    }
+    return EntityPointerLevel{buildEntityPointerLevel(*Id, 1)};
   }
 
   // The common helper function for Translate(*base):
@@ -68,28 +82,25 @@ class EntityPointerLevelTranslator
     return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
   }
 
-  std::function<EntityId(EntityName EN)> AddEntity;
+  TUSummaryBuilder &Builder;
   ASTContext &Ctx;
 
 public:
-  EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity,
-                               ASTContext &Ctx)
-      : AddEntity(AddEntity), Ctx(Ctx) {}
+  EntityPointerLevelTranslator(TUSummaryBuilder &Builder, ASTContext &Ctx)
+      : Builder(Builder), Ctx(Ctx) {}
 
   Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
   Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
-    if (IsRet && !isa<FunctionDecl>(D))
-      return makeErrAtNode(
-          Ctx, D,
-          "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
-          D->getDeclKindName());
-
-    std::optional<EntityName> EN =
-        IsRet ? getEntityNameForReturn(cast<FunctionDecl>(D))
-              : getEntityName(D);
-    if (EN)
-      return createEntityPointerLevelFor(*EN);
-    return makeEntityNameErr(Ctx, D);
+    if (!IsRet)
+      return createEntityPointerLevelFor(D);
+
+    if (const auto *FD = dyn_cast<FunctionDecl>(D))
+      return createEntityPointerLevelForReturn(FD);
+
+    return makeErrAtNode(
+        Ctx, D,
+        "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
+        D->getDeclKindName());
   }
 
   static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -167,10 +178,10 @@ class EntityPointerLevelTranslator
   // Translate(f(...)) -> {} if it is an indirect call
   //                   -> {(f_return, 1)}, otherwise
   Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
-    if (auto *FD = E->getDirectCallee())
-      if (auto FDEntityName = getEntityNameForReturn(FD))
-        return EntityPointerLevelSet{
-            createEntityPointerLevelFor(*FDEntityName)};
+    if (auto *FD = E->getDirectCallee()) {
+      if (auto ReturnId = Builder.addEntityForReturn(FD))
+        return EntityPointerLevelSet{buildEntityPointerLevel(*ReturnId, 1)};
+    }
     return EntityPointerLevelSet{};
   }
 
@@ -210,16 +221,18 @@ class EntityPointerLevelTranslator
 
   // Translate(DRE) -> {(Decl, 1)}
   Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
-    if (auto EntityName = getEntityName(E->getDecl()))
-      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return makeEntityNameErr(Ctx, E->getDecl());
+    auto Res = createEntityPointerLevelFor(E->getDecl());
+    if (!Res)
+      return Res.takeError();
+    return EntityPointerLevelSet{*Res};
   }
 
   // Translate({., ->}f) -> {(MemberDecl, 1)}
   Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
-    if (auto EntityName = getEntityName(E->getMemberDecl()))
-      return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
-    return makeEntityNameErr(Ctx, E->getMemberDecl());
+    auto Res = createEntityPointerLevelFor(E->getMemberDecl());
+    if (!Res)
+      return Res.takeError();
+    return EntityPointerLevelSet{*Res};
   }
 
   Expected<EntityPointerLevelSet>
@@ -229,19 +242,18 @@ class EntityPointerLevelTranslator
 };
 } // namespace clang::ssaf
 
-Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
-    const Expr *E, ASTContext &Ctx,
-    llvm::function_ref<EntityId(EntityName EN)> AddEntity) {
-  EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+Expected<EntityPointerLevelSet>
+clang::ssaf::translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+                                         TUSummaryBuilder &Builder) {
+  EntityPointerLevelTranslator Translator(Builder, Ctx);
 
   return Translator.translate(E);
 }
 
 /// Create an EntityPointerLevel from a ValueDecl of a pointer type.
 Expected<EntityPointerLevel> clang::ssaf::createEntityPointerLevel(
-    const NamedDecl *ND, llvm::function_ref<EntityId(EntityName EN)> AddEntity,
-    bool IsFunRet) {
-  EntityPointerLevelTranslator Translator(AddEntity, ND->getASTContext());
+    const NamedDecl *ND, TUSummaryBuilder &Builder, bool IsFunRet) {
+  EntityPointerLevelTranslator Translator(Builder, ND->getASTContext());
 
   return Translator.translate(ND, IsFunRet);
 }
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 550416c353fb7..dda2fb6131b8f 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -13,8 +13,6 @@
 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
@@ -51,9 +49,7 @@ clang::ssaf::UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
 
   for (const Expr *Ptr : UnsafePointers) {
     Expected<EntityPointerLevelSet> Translation =
-        translateEntityPointerLevel(Ptr, Ctx, [this](const EntityName &EN) {
-          return SummaryBuilder.addEntity(EN);
-        });
+        translateEntityPointerLevel(Ptr, Ctx, SummaryBuilder);
 
     if (Translation) {
       // Filter out those temporary invalid EntityPointerLevels associated
@@ -86,14 +82,13 @@ void clang::ssaf::UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
     if ((*EntitySummary)->empty())
       continue;
 
-    auto ContributorName = getEntityName(CD);
+    auto ContributorId = SummaryBuilder.addEntity(CD);
 
-    if (!ContributorName)
+    if (!ContributorId)
       llvm::reportFatalInternalError(makeEntityNameErr(Ctx, CD));
 
     [[maybe_unused]] auto [Ignored, InsertionSucceeded] =
-        SummaryBuilder.addSummary(SummaryBuilder.addEntity(*ContributorName),
-                                  std::move(*EntitySummary));
+        SummaryBuilder.addSummary(*ContributorId, std::move(*EntitySummary));
 
     assert(InsertionSucceeded && "duplicated contributor extraction");
   }
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
index f3280b02ce5ef..00c1f72cc9996 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
@@ -1,15 +1,62 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/AST/Decl.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
 #include <memory>
+#include <optional>
 #include <utility>
 
 using namespace clang;
 using namespace ssaf;
 
-EntityId TUSummaryBuilder::addEntity(const EntityName &E) {
-  return Summary.IdTable.getId(E);
+static EntityLinkageType getLinkageForDecl(const Decl *D) {
+  const auto *ND = dyn_cast<NamedDecl>(D);
+  if (!ND)
+    return EntityLinkageType::None;
+
+  switch (ND->getFormalLinkage()) {
+  case Linkage::Invalid: {
+    llvm_unreachable("Shouldn't be invalid");
+  }
+  case Linkage::None:
+    return EntityLinkageType::None;
+  case Linkage::Internal:
+    return EntityLinkageType::Internal;
+  case Linkage::UniqueExternal:
+    return EntityLinkageType::Internal;
+  case Linkage::VisibleNone:
+    return EntityLinkageType::Internal;
+  case Linkage::Module:
+    return EntityLinkageType::External;
+  case Linkage::External:
+    return EntityLinkageType::External;
+  }
+  llvm_unreachable("Unhandled clang::Linkage kind");
+}
+
+EntityId TUSummaryBuilder::addEntityImpl(const EntityName &Name,
+                                         const Decl *D) {
+  EntityId Id = Summary.IdTable.getId(Name);
+  Summary.LinkageTable.try_emplace(Id, getLinkageForDecl(D));
+  return Id;
+}
+
+std::optional<EntityId> TUSummaryBuilder::addEntity(const NamedDecl *D) {
+  auto Name = getEntityName(D);
+  if (!Name)
+    return std::nullopt;
+  return addEntityImpl(*Name, D);
+}
+
+std::optional<EntityId>
+TUSummaryBuilder::addEntityForReturn(const FunctionDecl *FD) {
+  auto Name = getEntityNameForReturn(FD);
+  if (!Name)
+    return std::nullopt;
+  return addEntityImpl(*Name, FD);
 }
 
 std::pair<EntitySummary *, bool>
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 4a631a28897d0..2490d43992bfa 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,13 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "FindDecl.h"
 #include "TestFixture.h"
 #include "clang/AST/ASTConsumer.h"
-#include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
 #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
@@ -36,36 +35,6 @@ using namespace ssaf;
 using testing::UnorderedElementsAre;
 
 namespace {
-template <typename SomeDecl = NamedDecl>
-const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
-  class NamedDeclFinder : public DynamicRecursiveASTVisitor {
-  public:
-    StringRef SearchingName;
-    const NamedDecl *FoundDecl = nullptr;
-
-    NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
-
-    bool VisitDecl(Decl *D) override {
-      if (const auto *ND = dyn_cast<SomeDecl>(D)) {
-        if (ND->getNameAsString() == SearchingName) {
-          FoundDecl = ND;
-          return false;
-        }
-      }
-      return true;
-    }
-  };
-
-  NamedDeclFinder Finder(Name);
-
-  Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
-  return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
-}
-
-const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
-  return findDeclByName<FunctionDecl>(Name, Ctx);
-}
-
 class UnsafeBufferUsageTest : public TestFixture {
 protected:
   TUSummary TUSum;
@@ -104,15 +73,14 @@ class UnsafeBufferUsageTest : public TestFixture {
       return nullptr;
     }
 
-    std::optional<EntityName> EN = getEntityName(ContributorDefn);
-
-    if (!EN) {
+    std::optional<EntityId> ContributorEntityId =
+        Builder.addEntity(ContributorDefn);
+    if (!ContributorEntityId) {
       ADD_FAILURE() << "failed to get EntityName for contributor \""
                     << ContributorEntityName << "\"";
       return nullptr;
     }
 
-    EntityId ContributorEntityId = Builder.addEntity(*EN);
     auto &TUSumData = getData(TUSum);
     auto EntitiesSumIter =
         TUSumData.find(UnsafeBufferUsageEntitySummary::summaryName());
@@ -122,7 +90,7 @@ class UnsafeBufferUsageTest : public TestFixture {
     if (EntitiesSumIter == TUSumData.end())
       return nullptr;
 
-    auto EntitySumIter = EntitiesSumIter->second.find(ContributorEntityId);
+    auto EntitySumIter = EntitiesSumIter->second.find(*ContributorEntityId);
 
     // If entity summary is empty, it may not exist:
     if (EntitySumIter == EntitiesSumIter->second.end())
@@ -133,15 +101,13 @@ class UnsafeBufferUsageTest : public TestFixture {
 
   std::optional<EntityId> getEntityId(StringRef Name) {
     if (const auto *D = findDeclByName(Name, AST->getASTContext()))
-      if (auto EntityName = getEntityName(D))
-        return Builder.addEntity(*EntityName);
+      return Builder.addEntity(D);
     return std::nullopt;
   }
 
   std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
     if (const auto *D = findFnByName(FunName, AST->getASTContext()))
-      if (auto EntityName = getEntityNameForReturn(D))
-        return Builder.addEntity(*EntityName);
+      return Builder.addEntityForReturn(D);
     return std::nullopt;
   }
 
@@ -181,8 +147,9 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
 }
 
 TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
-  EntityId E1 = Builder.addEntity({"c:@F at foo", "", {}});
-  EntityId E2 = Builder.addEntity({"c:@F at bar", "", {}});
+  EntityIdTable Table;
+  EntityId E1 = Table.getId({"c:@F at foo", "", {}});
+  EntityId E2 = Table.getId({"c:@F at bar", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 2);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -200,9 +167,10 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
-  EntityId E1 = Builder.addEntity({"c:@F at foo", "", {}});
-  EntityId E2 = Builder.addEntity({"c:@F at bar", "", {}});
-  EntityId E3 = Builder.addEntity({"c:@F at baz", "", {}});
+  EntityIdTable Table;
+  EntityId E1 = Table.getId({"c:@F at foo", "", {}});
+  EntityId E2 = Table.getId({"c:@F at bar", "", {}});
+  EntityId E3 = Table.getId({"c:@F at baz", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 1);
   auto P2 = buildEntityPointerLevel(E1, 2);
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
index ece39e2164df7..0eae3a3a0fe32 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
@@ -7,14 +7,16 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "FindDecl.h"
 #include "TestFixture.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormat.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
@@ -32,11 +34,6 @@ using testing::Field;
 using testing::Optional;
 using testing::UnorderedElementsAre;
 
-[[nodiscard]]
-static EntityId addTestEntity(TUSummaryBuilder &Builder, llvm::StringRef USR) {
-  return Builder.addEntity(EntityName(USR, /*Suffix=*/"", /*Namespace=*/{}));
-}
-
 struct SummaryResult {
   EntitySummary *Summary;
   bool Inserted;
@@ -95,6 +92,11 @@ struct TUSummaryBuilderTest : ssaf::TestFixture {
       BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")};
   TUSummaryBuilder Builder = TUSummaryBuilder(this->Summary);
 
+  [[nodiscard]] EntityId addTestEntity(llvm::StringRef USR) {
+    return getIdTable(Summary).getId(
+        EntityName(USR, /*Suffix=*/"", /*Namespace=*/{}));
+  }
+
   [[nodiscard]] static SmallVector<SummaryName>
   summaryNames(const TUSummary &Summary) {
     return llvm::to_vector(llvm::make_first_range(getData(Summary)));
@@ -128,22 +130,23 @@ TEST_F(TUSummaryBuilderTest, AddEntity) {
   EntityName EN1("c:@F at foo", "", /*Namespace=*/{});
   EntityName EN2("c:@F at bar", "", /*Namespace=*/{});
 
-  EntityId ID = Builder.addEntity(EN1);
-  EntityId IDAlias = Builder.addEntity(EN1);
+  EntityIdTable &IdTable = getIdTable(Summary);
+
+  EntityId ID = IdTable.getId(EN1);
+  EntityId IDAlias = IdTable.getId(EN1);
   EXPECT_EQ(ID, IDAlias); // Idenpotency
 
-  EntityId ID2 = Builder.addEntity(EN2);
+  EntityId ID2 = IdTable.getId(EN2);
   EXPECT_NE(ID, ID2);
   EXPECT_NE(IDAlias, ID2);
 
-  const EntityIdTable &IdTable = getIdTable(Summary);
   EXPECT_EQ(IdTable.count(), 2U);
   EXPECT_TRUE(IdTable.contains(EN1));
   EXPECT_TRUE(IdTable.contains(EN2));
 }
 
 TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleSummary) {
-  EntityId ID = addTestEntity(Builder, "c:@F at foo");
+  EntityId ID = addTestEntity("c:@F at foo");
   auto [Name, Res] = addSummaryTo(Builder, ID, MockSummaryData1(10));
   ASSERT_TRUE(Res.Inserted);
   ASSERT_TRUE(Res.Summary);
@@ -157,7 +160,7 @@ TEST_F(TUSummaryBuilderTest, TUSummaryBuilderAddSingleSummary) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddMultipleSummariesToSameEntity) {
-  EntityId ID = addTestEntity(Builder, "c:@F at foo");
+  EntityId ID = addTestEntity("c:@F at foo");
 
   // Add different summary types to the same entity.
   auto [Name1, Res1] = addSummaryTo(Builder, ID, MockSummaryData1(42));
@@ -188,9 +191,9 @@ TEST_F(TUSummaryBuilderTest, AddMultipleSummariesToSameEntity) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddSameSummaryTypeToMultipleEntities) {
-  EntityId ID1 = addTestEntity(Builder, "c:@F at foo");
-  EntityId ID2 = addTestEntity(Builder, "c:@F at bar");
-  EntityId ID3 = addTestEntity(Builder, "c:@F at baz");
+  EntityId ID1 = addTestEntity("c:@F at foo");
+  EntityId ID2 = addTestEntity("c:@F at bar");
+  EntityId ID3 = addTestEntity("c:@F at baz");
 
   // Add the same summary type to different entities.
   auto [Name1, Res1] = addSummaryTo(Builder, ID1, MockSummaryData1(1));
@@ -220,7 +223,7 @@ TEST_F(TUSummaryBuilderTest, AddSameSummaryTypeToMultipleEntities) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddConflictingSummaryToSameEntity) {
-  EntityId ID = addTestEntity(Builder, "c:@F at foo");
+  EntityId ID = addTestEntity("c:@F at foo");
 
   auto [Name, Res] = addSummaryTo(Builder, ID, MockSummaryData1(10));
   ASSERT_TRUE(Res.Inserted);
@@ -266,4 +269,49 @@ TEST_F(TUSummaryBuilderTest, AddConflictingSummaryToSameEntity) {
               Optional(Field(&MockSummaryData1::Value, 30)));
 }
 
+struct TUSummaryBuilderLinkageTest : TUSummaryBuilderTest {
+  std::unique_ptr<ASTUnit> AST;
+
+  const FunctionDecl *findFnByName(StringRef Name) {
+    return ssaf::findFnByName(Name, AST->getASTContext());
+  }
+
+  std::optional<EntityLinkageType> getLinkageFor(std::optional<EntityId> ID) {
+    if (!ID)
+      return std::nullopt;
+    if (auto It = getLinkageTable(Summary).find(*ID);
+        It != getLinkageTable(Summary).end())
+      return It->second.getLinkage();
+    return std::nullopt;
+  }
+};
+
+TEST_F(TUSummaryBuilderLinkageTest, HasInternalLinkage) {
+  AST = tooling::buildASTFromCode("static void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Builder.addEntity(Fn)), EntityLinkageType::Internal);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, HasExternalLinkage) {
+  AST = tooling::buildASTFromCode("void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Builder.addEntity(Fn)), EntityLinkageType::External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, HasExternalLinkageWithInline) {
+  AST = tooling::buildASTFromCode("inline void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Builder.addEntity(Fn)), EntityLinkageType::External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, HasInternalLinkageWithStaticInline) {
+  AST = tooling::buildASTFromCode("static inline void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Builder.addEntity(Fn)), EntityLinkageType::Internal);
+}
+
 } // namespace



More information about the cfe-commits mailing list