Author: Balázs Benics
Date: 2026-05-06T15:03:32Z
New Revision: 6bcc877da20e6701e01e536d78062ba58f266b5b

URL: 
https://github.com/llvm/llvm-project/commit/6bcc877da20e6701e01e536d78062ba58f266b5b
DIFF: 
https://github.com/llvm/llvm-project/commit/6bcc877da20e6701e01e536d78062ba58f266b5b.diff

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

This patch changes `addEntity` to take a `NamedDecl` to be able to
calculate the linkage for the entity.
It will try to get an `EntityName` and then try to get a Linkage for it.

Bundling the EntityName creation and then setting the linkage makes this
API safer as nobody can forget to set the linkage this way.

Assisted-By: claude

Added: 
    
clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp

Modified: 
    
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
    
clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
    
clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
    
clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h
    
clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
    
clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
    
clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
    
clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
    clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
    
clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.cpp
    
clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
    
clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
    
clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
    clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
    
llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Core/BUILD.gn

Removed: 
    


################################################################################
diff  --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 98812e0ea04e3..35eae269105f7 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 TUSummaryExtractor;
 
 /// 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,
+                            TUSummaryExtractor &Extractor);
 
 /// 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, TUSummaryExtractor &Extractor,
                          bool IsFunRet = false);
 
 /// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s

diff  --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
index da57838c64b2f..8639439dc1616 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h
@@ -41,8 +41,8 @@ bool isTUSummaryExtractorRegistered(llvm::StringRef 
SummaryName);
 /// This might return null if the construction of the desired 
TUSummaryExtractor
 /// failed.
 /// It's a fatal error if there is no extractor registered with the name.
-std::unique_ptr<ASTConsumer> makeTUSummaryExtractor(llvm::StringRef 
SummaryName,
-                                                    TUSummaryBuilder &Builder);
+std::unique_ptr<TUSummaryExtractor>
+makeTUSummaryExtractor(llvm::StringRef SummaryName, TUSummaryBuilder &Builder);
 
 /// Print the list of available TUSummaryExtractors.
 void printAvailableTUSummaryExtractors(llvm::raw_ostream &OS);

diff  --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
index fd07abfab8c87..f9ebe5358b585 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
@@ -10,6 +10,7 @@
 #define 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_TUSUMMARY_TUSUMMARYBUILDER_H
 
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include <memory>
 #include <utility>
@@ -23,9 +24,7 @@ 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);
+  EntityId addEntity(const EntityName &EN, EntityLinkageType Linkage);
 
   /// 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

diff  --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h
index 629a8d0e35ae4..46b0ae835d729 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h
@@ -10,6 +10,9 @@
 #define 
LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_TUSUMMARY_TUSUMMARYEXTRACTOR_H
 
 #include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include <optional>
 
 namespace clang::ssaf {
 class TUSummaryBuilder;
@@ -19,6 +22,16 @@ class TUSummaryExtractor : public ASTConsumer {
   explicit TUSummaryExtractor(TUSummaryBuilder &Builder)
       : SummaryBuilder(Builder) {}
 
+  /// 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);
+
 protected:
   TUSummaryBuilder &SummaryBuilder;
 };

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
index 1dbed7e0b0d8a..3436da7c2244b 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 = 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 = 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 ce301f7cc1b10..da6084f4d41a8 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -11,9 +11,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/StmtVisitor.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include <map>
+#include 
"clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
 #include <optional>
 
 using namespace clang;
@@ -51,8 +49,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 = Extractor.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 = Extractor.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 +81,23 @@ class EntityPointerLevelTranslator
     return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
   }
 
-  std::function<EntityId(EntityName EN)> AddEntity;
+  TUSummaryExtractor &Extractor;
   ASTContext &Ctx;
 
 public:
-  EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> 
AddEntity,
-                               ASTContext &Ctx)
-      : AddEntity(AddEntity), Ctx(Ctx) {}
+  EntityPointerLevelTranslator(TUSummaryExtractor &Extractor, ASTContext &Ctx)
+      : Extractor(Extractor), 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 get entity for return of %s",
+                         D->getDeclKindName());
   }
 
   static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) 
{
@@ -167,10 +175,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 = Extractor.addEntityForReturn(FD))
+        return EntityPointerLevelSet{buildEntityPointerLevel(*ReturnId, 1)};
+    }
     return EntityPointerLevelSet{};
   }
 
@@ -210,16 +218,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};
   }
 
   // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
@@ -235,19 +245,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,
+                                         TUSummaryExtractor &Extractor) {
+  EntityPointerLevelTranslator Translator(Extractor, 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, TUSummaryExtractor &Extractor, bool IsFunRet) {
+  EntityPointerLevelTranslator Translator(Extractor, ND->getASTContext());
 
   return Translator.translate(ND, IsFunRet);
 }

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index 473ce5b11ae5b..8f79c987cc290 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -42,10 +42,10 @@ class PointerFlowMatcher {
 public:
   EdgeSet Results;
   ASTContext &Ctx;
+  TUSummaryExtractor &Extractor;
 
-  PointerFlowMatcher(ASTContext &Ctx,
-                     std::function<EntityId(const EntityName &)> AddEntity)
-      : Ctx(Ctx), AddEntity(std::move(AddEntity)) {}
+  PointerFlowMatcher(ASTContext &Ctx, TUSummaryExtractor &Extractor)
+      : Ctx(Ctx), Extractor(Extractor) {}
 
   llvm::Error matches(const DynTypedNode &DynNode, const NamedDecl *RootDecl);
 
@@ -88,7 +88,7 @@ class PointerFlowMatcher {
 
 Expected<EntityPointerLevelSet> PointerFlowMatcher::toEPL(const NamedDecl *N,
                                                           bool IsRet) const {
-  auto Ret = createEntityPointerLevel(N, AddEntity, IsRet);
+  auto Ret = createEntityPointerLevel(N, Extractor, IsRet);
 
   if (Ret)
     return EntityPointerLevelSet{*Ret};
@@ -96,7 +96,7 @@ Expected<EntityPointerLevelSet> 
PointerFlowMatcher::toEPL(const NamedDecl *N,
 }
 
 Expected<EntityPointerLevelSet> PointerFlowMatcher::toEPL(const Expr *N) const 
{
-  return translateEntityPointerLevel(N, Ctx, AddEntity);
+  return translateEntityPointerLevel(N, Ctx, Extractor);
 }
 
 llvm::Error
@@ -309,14 +309,10 @@ class PointerFlowTUSummaryExtractor : public 
TUSummaryExtractor {
   PointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder)
       : TUSummaryExtractor(Builder) {}
 
-  EntityId addEntity(const EntityName &EN) {
-    return SummaryBuilder.addEntity(EN);
-  }
-
   Expected<std::unique_ptr<PointerFlowEntitySummary>>
-  extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
-    PointerFlowMatcher Matcher(
-        Ctx, [this](const EntityName &EN) { return addEntity(EN); });
+  extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx,
+                       TUSummaryExtractor &Extractor) {
+    PointerFlowMatcher Matcher(Ctx, Extractor);
     auto MatchAction = [&Matcher, &Contributor](const DynTypedNode &Node) {
       auto Err = Matcher.matches(Node, Contributor);
 
@@ -334,7 +330,7 @@ class PointerFlowTUSummaryExtractor : public 
TUSummaryExtractor {
 
     findContributors(Ctx, Contributors);
     for (auto *CD : Contributors) {
-      auto EntitySummary = extractEntitySummary(CD, Ctx);
+      auto EntitySummary = extractEntitySummary(CD, Ctx, *this);
 
       if (!EntitySummary)
         llvm::reportFatalInternalError(EntitySummary.takeError());
@@ -342,13 +338,12 @@ class PointerFlowTUSummaryExtractor : public 
TUSummaryExtractor {
       if ((*EntitySummary)->empty())
         continue;
 
-      auto ContributorName = getEntityName(CD);
-
-      if (!ContributorName)
+      std::optional<EntityId> ContributorId = addEntity(CD);
+      if (!ContributorId)
         llvm::reportFatalInternalError(makeEntityNameErr(Ctx, CD));
 
-      [[maybe_unused]] auto [_, InsertionSucceeded] = 
SummaryBuilder.addSummary(
-          addEntity(*ContributorName), std::move(*EntitySummary));
+      [[maybe_unused]] auto [_, InsertionSucceeded] =
+          SummaryBuilder.addSummary(*ContributorId, std::move(*EntitySummary));
 
       assert(InsertionSucceeded && "duplicated contributor extraction");
     }

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 5e202d8a443c7..9103778d585fb 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, *this);
 
     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 = 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/CMakeLists.txt 
b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
index 83772ceff58bf..2c43d645f7e74 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/CMakeLists.txt
@@ -24,6 +24,7 @@ add_clang_library(clangScalableStaticAnalysisFrameworkCore
   Support/ErrorBuilder.cpp
   TUSummary/ExtractorRegistry.cpp
   TUSummary/TUSummaryBuilder.cpp
+  TUSummary/TUSummaryExtractor.cpp
   WholeProgramAnalysis/AnalysisDriver.cpp
   WholeProgramAnalysis/AnalysisName.cpp
   WholeProgramAnalysis/AnalysisRegistry.cpp

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.cpp
index 01acc64f6f26c..b59d190dde519 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include 
"clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include 
"clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
 #include <memory>
 
 using namespace clang;
@@ -21,7 +22,7 @@ bool ssaf::isTUSummaryExtractorRegistered(llvm::StringRef 
SummaryName) {
   return false;
 }
 
-std::unique_ptr<ASTConsumer>
+std::unique_ptr<TUSummaryExtractor>
 ssaf::makeTUSummaryExtractor(llvm::StringRef SummaryName,
                              TUSummaryBuilder &Builder) {
   for (const auto &Entry : TUSummaryExtractorRegistry::entries())

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
index 180a78cae33a9..daea76f7001cb 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
@@ -8,6 +8,7 @@
 
 #include 
"clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.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>
@@ -16,8 +17,14 @@
 using namespace clang;
 using namespace ssaf;
 
-EntityId TUSummaryBuilder::addEntity(const EntityName &E) {
-  return Summary.IdTable.getId(E);
+EntityId TUSummaryBuilder::addEntity(const EntityName &EN,
+                                     EntityLinkageType Linkage) {
+  EntityId Id = Summary.IdTable.getId(EN);
+  [[maybe_unused]] EntityLinkageType Link =
+      Summary.LinkageTable.try_emplace(Id, Linkage).first->second.getLinkage();
+  // Even if we had in the past a linkage, that must bee the same as we set 
now.
+  assert(Link == Linkage);
+  return Id;
 }
 
 std::pair<EntitySummary *, bool>

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
new file mode 100644
index 0000000000000..9594a6b179db5
--- /dev/null
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
@@ -0,0 +1,58 @@
+//===- TUSummaryExtractor.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/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.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/TUSummaryBuilder.h"
+#include <optional>
+
+using namespace clang;
+using namespace ssaf;
+
+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");
+}
+
+std::optional<EntityId> TUSummaryExtractor::addEntity(const NamedDecl *D) {
+  auto Name = getEntityName(D);
+  if (!Name)
+    return std::nullopt;
+  return SummaryBuilder.addEntity(*Name, getLinkageForDecl(D));
+}
+
+std::optional<EntityId>
+TUSummaryExtractor::addEntityForReturn(const FunctionDecl *FD) {
+  auto Name = getEntityNameForReturn(FD);
+  if (!Name)
+    return std::nullopt;
+  return SummaryBuilder.addEntity(*Name, getLinkageForDecl(FD));
+}

diff  --git 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 2d6a958ce4eac..9f7a508ea35fd 100644
--- 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -183,15 +183,15 @@ class PointerFlowTest : public TestFixture {
       return nullptr;
     }
 
-    std::optional<EntityName> EN = getEntityName(ContributorDefn);
+    std::optional<EntityId> ContributorEntityId =
+        Extractor->addEntity(ContributorDefn);
 
-    if (!EN) {
+    if (!ContributorEntityId) {
       ADD_FAILURE() << "failed to get EntityName for contributor \""
                     << toStringRef(Name) << "\"";
       return nullptr;
     }
 
-    EntityId ContributorEntityId = Builder.addEntity(*EN);
     auto &TUSumData = getData(TUSum);
     auto EntitiesSumIter =
         TUSumData.find(PointerFlowEntitySummary::summaryName());
@@ -201,7 +201,7 @@ class PointerFlowTest : 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())
@@ -213,16 +213,14 @@ class PointerFlowTest : public TestFixture {
 public:
   std::optional<EntityId> getEntityId(FindEntityByName Name) {
     if (const auto *D = findEntityByName(Name, AST->getASTContext())) {
-      if (auto EntityName = getEntityName(D))
-        return Builder.addEntity(*EntityName);
+      return Extractor->addEntity(D);
     }
     return std::nullopt;
   }
 
   std::optional<EntityId> getEntityIdForReturn(FindEntityByName FunName) {
     if (const auto *D = findFnByName(FunName, AST->getASTContext())) {
-      if (auto EntityName = getEntityNameForReturn(D))
-        return Builder.addEntity(*EntityName);
+      return Extractor->addEntityForReturn(D);
     }
     return std::nullopt;
   }

diff  --git 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 465488357e20e..7dc13a890cad4 100644
--- 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -13,7 +13,6 @@
 #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"
@@ -40,7 +39,7 @@ class UnsafeBufferUsageTest : public TestFixture {
 protected:
   TUSummary TUSum;
   TUSummaryBuilder Builder;
-  std::unique_ptr<ASTConsumer> Extractor;
+  std::unique_ptr<TUSummaryExtractor> Extractor;
   std::unique_ptr<ASTUnit> AST;
 
   UnsafeBufferUsageTest()
@@ -74,15 +73,14 @@ class UnsafeBufferUsageTest : public TestFixture {
       return nullptr;
     }
 
-    std::optional<EntityName> EN = getEntityName(ContributorDefn);
-
-    if (!EN) {
+    std::optional<EntityId> ContributorEntityId =
+        Extractor->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());
@@ -92,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())
@@ -103,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 Extractor->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 Extractor->addEntityForReturn(D);
     return std::nullopt;
   }
 
@@ -151,8 +147,9 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId 
Entity) {
 }
 
 TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
-  EntityId E1 = Builder.addEntity({"c:@F@foo", "", {}});
-  EntityId E2 = Builder.addEntity({"c:@F@bar", "", {}});
+  EntityIdTable Table;
+  EntityId E1 = Table.getId({"c:@F@foo", "", {}});
+  EntityId E2 = Table.getId({"c:@F@bar", "", {}});
 
   auto P1 = buildEntityPointerLevel(E1, 2);
   auto P2 = buildEntityPointerLevel(E1, 2);
@@ -170,9 +167,10 @@ TEST_F(UnsafeBufferUsageTest, 
EntityPointerLevelComparison) {
 }
 
 TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
-  EntityId E1 = Builder.addEntity({"c:@F@foo", "", {}});
-  EntityId E2 = Builder.addEntity({"c:@F@bar", "", {}});
-  EntityId E3 = Builder.addEntity({"c:@F@baz", "", {}});
+  EntityIdTable Table;
+  EntityId E1 = Table.getId({"c:@F@foo", "", {}});
+  EntityId E2 = Table.getId({"c:@F@bar", "", {}});
+  EntityId E3 = Table.getId({"c:@F@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..3fff10f08d38a 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
@@ -7,14 +7,17 @@
 
//===----------------------------------------------------------------------===//
 
 #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/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
@@ -32,11 +35,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;
@@ -93,7 +91,13 @@ void PrintTo(const MockSummaryData3 &S, std::ostream *OS) {
 struct TUSummaryBuilderTest : ssaf::TestFixture {
   TUSummary Summary{
       BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")};
-  TUSummaryBuilder Builder = TUSummaryBuilder(this->Summary);
+  TUSummaryBuilder Builder{Summary};
+  TUSummaryExtractor Extractor{Builder};
+
+  [[nodiscard]] EntityId addTestEntity(llvm::StringRef USR) {
+    return getIdTable(Summary).getId(
+        EntityName(USR, /*Suffix=*/"", /*Namespace=*/{}));
+  }
 
   [[nodiscard]] static SmallVector<SummaryName>
   summaryNames(const TUSummary &Summary) {
@@ -128,22 +132,23 @@ TEST_F(TUSummaryBuilderTest, AddEntity) {
   EntityName EN1("c:@F@foo", "", /*Namespace=*/{});
   EntityName EN2("c:@F@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@foo");
+  EntityId ID = addTestEntity("c:@F@foo");
   auto [Name, Res] = addSummaryTo(Builder, ID, MockSummaryData1(10));
   ASSERT_TRUE(Res.Inserted);
   ASSERT_TRUE(Res.Summary);
@@ -157,7 +162,7 @@ TEST_F(TUSummaryBuilderTest, 
TUSummaryBuilderAddSingleSummary) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddMultipleSummariesToSameEntity) {
-  EntityId ID = addTestEntity(Builder, "c:@F@foo");
+  EntityId ID = addTestEntity("c:@F@foo");
 
   // Add 
diff erent summary types to the same entity.
   auto [Name1, Res1] = addSummaryTo(Builder, ID, MockSummaryData1(42));
@@ -188,9 +193,9 @@ TEST_F(TUSummaryBuilderTest, 
AddMultipleSummariesToSameEntity) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddSameSummaryTypeToMultipleEntities) {
-  EntityId ID1 = addTestEntity(Builder, "c:@F@foo");
-  EntityId ID2 = addTestEntity(Builder, "c:@F@bar");
-  EntityId ID3 = addTestEntity(Builder, "c:@F@baz");
+  EntityId ID1 = addTestEntity("c:@F@foo");
+  EntityId ID2 = addTestEntity("c:@F@bar");
+  EntityId ID3 = addTestEntity("c:@F@baz");
 
   // Add the same summary type to 
diff erent entities.
   auto [Name1, Res1] = addSummaryTo(Builder, ID1, MockSummaryData1(1));
@@ -220,7 +225,7 @@ TEST_F(TUSummaryBuilderTest, 
AddSameSummaryTypeToMultipleEntities) {
 }
 
 TEST_F(TUSummaryBuilderTest, AddConflictingSummaryToSameEntity) {
-  EntityId ID = addTestEntity(Builder, "c:@F@foo");
+  EntityId ID = addTestEntity("c:@F@foo");
 
   auto [Name, Res] = addSummaryTo(Builder, ID, MockSummaryData1(10));
   ASSERT_TRUE(Res.Inserted);
@@ -266,4 +271,69 @@ TEST_F(TUSummaryBuilderTest, 
AddConflictingSummaryToSameEntity) {
               Optional(Field(&MockSummaryData1::Value, 30)));
 }
 
+struct TUSummaryBuilderLinkageTest : TUSummaryBuilderTest {
+  std::unique_ptr<ASTUnit> AST;
+  static constexpr auto Internal = EntityLinkageType::Internal;
+  static constexpr auto External = EntityLinkageType::External;
+
+  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(Extractor.addEntity(Fn)), Internal);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, HasExternalLinkage) {
+  AST = tooling::buildASTFromCode("void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(Fn)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, HasExternalLinkageWithInline) {
+  AST = tooling::buildASTFromCode("inline void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(Fn)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, HasInternalLinkageWithStaticInline) {
+  AST = tooling::buildASTFromCode("static inline void target() {}");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(Fn)), Internal);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ConstVolatileGlobalHasExternalLinkage) {
+  AST = tooling::buildASTFromCode("namespace ns {\n"
+                                  "  const volatile int glob = 0;\n"
+                                  "}");
+  const auto *VD = findDeclByName<VarDecl>("glob", AST->getASTContext());
+  ASSERT_TRUE(VD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(VD)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ConstGlobalHasInternalLinkage) {
+  AST = tooling::buildASTFromCode("namespace ns {\n"
+                                  "  const int glob = 0;\n"
+                                  "}");
+  const auto *VD = findDeclByName<VarDecl>("glob", AST->getASTContext());
+  ASSERT_TRUE(VD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(VD)), Internal);
+}
+
 } // namespace

diff  --git 
a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Core/BUILD.gn
 
b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Core/BUILD.gn
index 70879d66e4700..e2da7fcd82e62 100644
--- 
a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Core/BUILD.gn
+++ 
b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Core/BUILD.gn
@@ -28,6 +28,7 @@ static_library("Core") {
     "Support/ErrorBuilder.cpp",
     "TUSummary/ExtractorRegistry.cpp",
     "TUSummary/TUSummaryBuilder.cpp",
+    "TUSummary/TUSummaryExtractor.cpp",
     "WholeProgramAnalysis/AnalysisDriver.cpp",
     "WholeProgramAnalysis/AnalysisName.cpp",
     "WholeProgramAnalysis/AnalysisRegistry.cpp",


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to