[clang] [clang][ssaf] Add EntityLinkage support to TUSummaryBuilder and CallGraphExtractor (PR #194448)

Balázs Benics via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 12:54:33 PDT 2026


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

Add TUSummaryBuilder::setLinkage to record linkage for each entity.
CallGraphExtractor now calls setLinkage for both callers and callees.

This API won't live for long. I already have the patches to bundle this into `addEntity`.
However, for making reviews easier, I think it's better to land this first and then the patches I mentioned to reduce the PR sizes.
Hopefully, we can merge these two in close succession to not confuse anynoe rebasing and constantly changing these APIs.

Assisted-By: claude

>From a5fb55354f74749cb3dee877f74df09cf9ceff17 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Fri, 10 Apr 2026 11:04:55 +0100
Subject: [PATCH] [clang][ssaf] Add EntityLinkage support to TUSummaryBuilder
 and CallGraphExtractor

Add TUSummaryBuilder::setLinkage to record linkage for each entity.
CallGraphExtractor now calls setLinkage for both callers and callees.

This API won't live for long. I already have the patches to bundle this
into `addEntity`. However, for making reviews easier, I think it's
better to land this first and then the patches I mentioned to reduce the
PR sizes.
Hopefully, we can merge these two in close succession to not confuse
anynoe rebasing and constantly changing these APIs.

Assisted-By: claude
---
 .../Core/TUSummary/TUSummaryBuilder.h         |   8 +
 .../Analyses/CallGraph/CallGraphExtractor.cpp |   2 +
 .../Core/Model/EntityIdTable.cpp              |   2 +-
 .../Core/TUSummary/TUSummaryBuilder.cpp       |  35 ++++
 .../Scalable/ssaf-linker/Outputs/lu-1+2.json  | 194 +++++++++---------
 .../Scalable/ssaf-linker/Outputs/lu-1.json    | 106 +++++-----
 .../Scalable/ssaf-linker/Outputs/lu-2.json    | 108 +++++-----
 .../EntityIdTableTest.cpp                     |  29 ++-
 .../TUSummaryBuilderTest.cpp                  |  54 +++++
 9 files changed, 325 insertions(+), 213 deletions(-)

diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
index d386876675873..666d1b71dd42c 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h
@@ -9,7 +9,9 @@
 #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/Model/EntityLinkage.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
 #include <memory>
 #include <utility>
@@ -23,6 +25,12 @@ class TUSummaryBuilder {
 public:
   explicit TUSummaryBuilder(TUSummary &Summary) : Summary(Summary) {}
 
+  /// Set the linkage of the \p Entity.
+  void setLinkage(EntityId Entity, EntityLinkageType Linkage);
+
+  /// Set the linkage of the \p Entity.
+  void setLinkage(EntityId Entity, const Decl *D);
+
   /// 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);
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
index 1dbed7e0b0d8a..e0d24af1f441b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
@@ -85,6 +85,7 @@ void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx,
       continue;
 
     EntityId CalleeId = SummaryBuilder.addEntity(*CalleeName);
+    SummaryBuilder.setLinkage(CalleeId, CalleeDecl);
     if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(CalleeDecl);
         MD && MD->isVirtual()) {
       FnSummary->VirtualCallees.insert(CalleeId);
@@ -94,6 +95,7 @@ void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx,
   }
 
   EntityId CallerId = SummaryBuilder.addEntity(*CallerName);
+  SummaryBuilder.setLinkage(CallerId, Definition);
   SummaryBuilder.addSummary(CallerId, std::move(FnSummary));
 }
 
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.cpp
index cb514581ca9ec..7b64a1ca5249e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.cpp
@@ -12,7 +12,7 @@
 namespace clang::ssaf {
 
 EntityId EntityIdTable::getId(const EntityName &Name) {
-  EntityId Id(Entities.size());
+  EntityId Id(Entities.size() + 1);
   const auto Res = Entities.try_emplace(Name, Id);
   return Res.first->second;
 }
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
index f3280b02ce5ef..79051d488c0aa 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp
@@ -1,5 +1,7 @@
 #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/AST/Decl.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>
@@ -8,6 +10,39 @@
 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");
+}
+
+void TUSummaryBuilder::setLinkage(EntityId Entity, const Decl *D) {
+  Summary.LinkageTable.try_emplace(Entity, getLinkageForDecl(D));
+}
+
+void TUSummaryBuilder::setLinkage(EntityId Entity, EntityLinkageType Linkage) {
+  Summary.LinkageTable.try_emplace(Entity, Linkage);
+}
+
 EntityId TUSummaryBuilder::addEntity(const EntityName &E) {
   return Summary.IdTable.getId(E);
 }
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1+2.json b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1+2.json
index 568a175730ce6..ff388bfe4b41b 100644
--- a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1+2.json
+++ b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1+2.json
@@ -3,152 +3,152 @@
     {
       "summary_data": [
         {
-          "entity_id": 0,
+          "entity_id": 1,
           "entity_summary": {
             "call_count": 3,
             "callees": [
-              {
-                "@": 1
-              },
               {
                 "@": 2
               },
               {
                 "@": 3
+              },
+              {
+                "@": 4
               }
             ]
           }
         },
         {
-          "entity_id": 1,
+          "entity_id": 2,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 4
+                "@": 5
               }
             ]
           }
         },
         {
-          "entity_id": 2,
+          "entity_id": 3,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 5
+                "@": 6
               }
             ]
           }
         },
         {
-          "entity_id": 3,
+          "entity_id": 4,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 1
+                "@": 2
               }
             ]
           }
         },
         {
-          "entity_id": 4,
+          "entity_id": 5,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 3
+                "@": 4
               }
             ]
           }
         },
         {
-          "entity_id": 5,
+          "entity_id": 6,
           "entity_summary": {
             "call_count": 3,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 3
+                "@": 4
               },
               {
-                "@": 4
+                "@": 5
               }
             ]
           }
         },
         {
-          "entity_id": 6,
+          "entity_id": 7,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 8
+                "@": 9
               }
             ]
           }
         },
         {
-          "entity_id": 7,
+          "entity_id": 8,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 9
+                "@": 10
               }
             ]
           }
         },
         {
-          "entity_id": 8,
+          "entity_id": 9,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 7
+                "@": 8
               }
             ]
           }
         },
         {
-          "entity_id": 9,
+          "entity_id": 10,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 8
+                "@": 9
               }
             ]
           }
         },
         {
-          "entity_id": 10,
+          "entity_id": 11,
           "entity_summary": {
             "call_count": 3,
             "callees": [
-              {
-                "@": 6
-              },
               {
                 "@": 7
               },
               {
                 "@": 8
+              },
+              {
+                "@": 9
               }
             ]
           }
@@ -159,21 +159,21 @@
     {
       "summary_data": [
         {
-          "entity_id": 0,
+          "entity_id": 1,
           "entity_summary": {
             "direct": {
-              "@": 3
+              "@": 4
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 1
+                  "@": 2
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 4
+                  "@": 5
                 },
                 "level": 2
               }
@@ -181,21 +181,21 @@
           }
         },
         {
-          "entity_id": 1,
+          "entity_id": 2,
           "entity_summary": {
             "direct": {
-              "@": 0
+              "@": 1
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 5
+                  "@": 6
                 },
                 "level": 2
               }
@@ -203,15 +203,15 @@
           }
         },
         {
-          "entity_id": 2,
+          "entity_id": 3,
           "entity_summary": {
             "direct": {
-              "@": 1
+              "@": 2
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 3
+                  "@": 4
                 },
                 "level": 1
               }
@@ -219,21 +219,21 @@
           }
         },
         {
-          "entity_id": 3,
+          "entity_id": 4,
           "entity_summary": {
             "direct": {
-              "@": 4
+              "@": 5
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 2
               }
@@ -241,15 +241,15 @@
           }
         },
         {
-          "entity_id": 4,
+          "entity_id": 5,
           "entity_summary": {
             "direct": {
-              "@": 5
+              "@": 6
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 1
+                  "@": 2
                 },
                 "level": 1
               }
@@ -257,27 +257,27 @@
           }
         },
         {
-          "entity_id": 5,
+          "entity_id": 6,
           "entity_summary": {
             "direct": {
-              "@": 2
+              "@": 3
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 3
+                  "@": 4
                 },
                 "level": 2
               },
               {
                 "entity": {
-                  "@": 4
+                  "@": 5
                 },
                 "level": 3
               }
@@ -285,15 +285,15 @@
           }
         },
         {
-          "entity_id": 6,
+          "entity_id": 7,
           "entity_summary": {
             "direct": {
-              "@": 0
+              "@": 1
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 9
+                  "@": 10
                 },
                 "level": 1
               }
@@ -301,21 +301,21 @@
           }
         },
         {
-          "entity_id": 7,
+          "entity_id": 8,
           "entity_summary": {
             "direct": {
-              "@": 10
+              "@": 11
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 8
+                  "@": 9
                 },
                 "level": 2
               }
@@ -323,21 +323,21 @@
           }
         },
         {
-          "entity_id": 8,
+          "entity_id": 9,
           "entity_summary": {
             "direct": {
-              "@": 6
+              "@": 7
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 7
+                  "@": 8
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 2
               }
@@ -345,21 +345,21 @@
           }
         },
         {
-          "entity_id": 9,
+          "entity_id": 10,
           "entity_summary": {
             "direct": {
-              "@": 7
+              "@": 8
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 10
+                  "@": 11
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 8
+                  "@": 9
                 },
                 "level": 2
               }
@@ -367,27 +367,27 @@
           }
         },
         {
-          "entity_id": 10,
+          "entity_id": 11,
           "entity_summary": {
             "direct": {
-              "@": 9
+              "@": 10
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 6
+                  "@": 7
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 7
+                  "@": 8
                 },
                 "level": 2
               },
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 3
               }
@@ -400,7 +400,7 @@
   ],
   "id_table": [
     {
-      "id": 0,
+      "id": 1,
       "name": {
         "namespace": [
           {
@@ -413,7 +413,7 @@
       }
     },
     {
-      "id": 1,
+      "id": 2,
       "name": {
         "namespace": [
           {
@@ -430,7 +430,7 @@
       }
     },
     {
-      "id": 6,
+      "id": 7,
       "name": {
         "namespace": [
           {
@@ -447,7 +447,7 @@
       }
     },
     {
-      "id": 2,
+      "id": 3,
       "name": {
         "namespace": [
           {
@@ -464,7 +464,7 @@
       }
     },
     {
-      "id": 7,
+      "id": 8,
       "name": {
         "namespace": [
           {
@@ -481,7 +481,7 @@
       }
     },
     {
-      "id": 3,
+      "id": 4,
       "name": {
         "namespace": [
           {
@@ -494,7 +494,7 @@
       }
     },
     {
-      "id": 8,
+      "id": 9,
       "name": {
         "namespace": [
           {
@@ -507,7 +507,7 @@
       }
     },
     {
-      "id": 4,
+      "id": 5,
       "name": {
         "namespace": [
           {
@@ -524,7 +524,7 @@
       }
     },
     {
-      "id": 9,
+      "id": 10,
       "name": {
         "namespace": [
           {
@@ -541,7 +541,7 @@
       }
     },
     {
-      "id": 5,
+      "id": 6,
       "name": {
         "namespace": [
           {
@@ -558,7 +558,7 @@
       }
     },
     {
-      "id": 10,
+      "id": 11,
       "name": {
         "namespace": [
           {
@@ -577,67 +577,67 @@
   ],
   "linkage_table": [
     {
-      "id": 0,
+      "id": 1,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 1,
+      "id": 2,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 2,
+      "id": 3,
       "linkage": {
         "type": "None"
       }
     },
     {
-      "id": 3,
+      "id": 4,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 4,
+      "id": 5,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 5,
+      "id": 6,
       "linkage": {
         "type": "None"
       }
     },
     {
-      "id": 6,
+      "id": 7,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 7,
+      "id": 8,
       "linkage": {
         "type": "None"
       }
     },
     {
-      "id": 8,
+      "id": 9,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 9,
+      "id": 10,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 10,
+      "id": 11,
       "linkage": {
         "type": "None"
       }
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1.json b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1.json
index 004e5490bba26..7be03f9b76e30 100644
--- a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1.json
+++ b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-1.json
@@ -3,85 +3,85 @@
     {
       "summary_data": [
         {
-          "entity_id": 0,
+          "entity_id": 1,
           "entity_summary": {
             "call_count": 3,
             "callees": [
-              {
-                "@": 1
-              },
               {
                 "@": 2
               },
               {
                 "@": 3
+              },
+              {
+                "@": 4
               }
             ]
           }
         },
         {
-          "entity_id": 1,
+          "entity_id": 2,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 4
+                "@": 5
               }
             ]
           }
         },
         {
-          "entity_id": 2,
+          "entity_id": 3,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 5
+                "@": 6
               }
             ]
           }
         },
         {
-          "entity_id": 3,
+          "entity_id": 4,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 1
+                "@": 2
               }
             ]
           }
         },
         {
-          "entity_id": 4,
+          "entity_id": 5,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 3
+                "@": 4
               }
             ]
           }
         },
         {
-          "entity_id": 5,
+          "entity_id": 6,
           "entity_summary": {
             "call_count": 3,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 3
+                "@": 4
               },
               {
-                "@": 4
+                "@": 5
               }
             ]
           }
@@ -92,21 +92,21 @@
     {
       "summary_data": [
         {
-          "entity_id": 0,
+          "entity_id": 1,
           "entity_summary": {
             "direct": {
-              "@": 3
+              "@": 4
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 1
+                  "@": 2
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 4
+                  "@": 5
                 },
                 "level": 2
               }
@@ -114,21 +114,21 @@
           }
         },
         {
-          "entity_id": 1,
+          "entity_id": 2,
           "entity_summary": {
             "direct": {
-              "@": 0
+              "@": 1
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 5
+                  "@": 6
                 },
                 "level": 2
               }
@@ -136,15 +136,15 @@
           }
         },
         {
-          "entity_id": 2,
+          "entity_id": 3,
           "entity_summary": {
             "direct": {
-              "@": 1
+              "@": 2
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 3
+                  "@": 4
                 },
                 "level": 1
               }
@@ -152,21 +152,21 @@
           }
         },
         {
-          "entity_id": 3,
+          "entity_id": 4,
           "entity_summary": {
             "direct": {
-              "@": 4
+              "@": 5
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 2
               }
@@ -174,15 +174,15 @@
           }
         },
         {
-          "entity_id": 4,
+          "entity_id": 5,
           "entity_summary": {
             "direct": {
-              "@": 5
+              "@": 6
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 1
+                  "@": 2
                 },
                 "level": 1
               }
@@ -190,27 +190,27 @@
           }
         },
         {
-          "entity_id": 5,
+          "entity_id": 6,
           "entity_summary": {
             "direct": {
-              "@": 2
+              "@": 3
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 3
+                  "@": 4
                 },
                 "level": 2
               },
               {
                 "entity": {
-                  "@": 4
+                  "@": 5
                 },
                 "level": 3
               }
@@ -223,7 +223,7 @@
   ],
   "id_table": [
     {
-      "id": 0,
+      "id": 1,
       "name": {
         "namespace": [
           {
@@ -236,7 +236,7 @@
       }
     },
     {
-      "id": 1,
+      "id": 2,
       "name": {
         "namespace": [
           {
@@ -253,7 +253,7 @@
       }
     },
     {
-      "id": 2,
+      "id": 3,
       "name": {
         "namespace": [
           {
@@ -270,7 +270,7 @@
       }
     },
     {
-      "id": 3,
+      "id": 4,
       "name": {
         "namespace": [
           {
@@ -283,7 +283,7 @@
       }
     },
     {
-      "id": 4,
+      "id": 5,
       "name": {
         "namespace": [
           {
@@ -300,7 +300,7 @@
       }
     },
     {
-      "id": 5,
+      "id": 6,
       "name": {
         "namespace": [
           {
@@ -319,37 +319,37 @@
   ],
   "linkage_table": [
     {
-      "id": 0,
+      "id": 1,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 1,
+      "id": 2,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 2,
+      "id": 3,
       "linkage": {
         "type": "None"
       }
     },
     {
-      "id": 3,
+      "id": 4,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 4,
+      "id": 5,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 5,
+      "id": 6,
       "linkage": {
         "type": "None"
       }
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-2.json b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-2.json
index 1ed0e7fd38f6c..e5fb7bbe1479a 100644
--- a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-2.json
+++ b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lu-2.json
@@ -3,85 +3,85 @@
     {
       "summary_data": [
         {
-          "entity_id": 0,
+          "entity_id": 1,
           "entity_summary": {
             "call_count": 3,
             "callees": [
               {
-                "@": 1
+                "@": 2
               },
               {
-                "@": 2
+                "@": 3
               },
               {
-                "@": 5
+                "@": 6
               }
             ]
           }
         },
         {
-          "entity_id": 1,
+          "entity_id": 2,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 3
+                "@": 4
               }
             ]
           }
         },
         {
-          "entity_id": 2,
+          "entity_id": 3,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 4
+                "@": 5
               }
             ]
           }
         },
         {
-          "entity_id": 3,
+          "entity_id": 4,
           "entity_summary": {
             "call_count": 2,
             "callees": [
               {
-                "@": 0
+                "@": 1
               },
               {
-                "@": 2
+                "@": 3
               }
             ]
           }
         },
         {
-          "entity_id": 4,
+          "entity_id": 5,
           "entity_summary": {
             "call_count": 1,
             "callees": [
               {
-                "@": 3
+                "@": 4
               }
             ]
           }
         },
         {
-          "entity_id": 5,
+          "entity_id": 6,
           "entity_summary": {
             "call_count": 3,
             "callees": [
-              {
-                "@": 1
-              },
               {
                 "@": 2
               },
               {
                 "@": 3
+              },
+              {
+                "@": 4
               }
             ]
           }
@@ -92,21 +92,21 @@
     {
       "summary_data": [
         {
-          "entity_id": 0,
+          "entity_id": 1,
           "entity_summary": {
             "direct": {
-              "@": 3
+              "@": 4
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 5
+                  "@": 6
                 },
                 "level": 2
               }
@@ -114,15 +114,15 @@
           }
         },
         {
-          "entity_id": 1,
+          "entity_id": 2,
           "entity_summary": {
             "direct": {
-              "@": 0
+              "@": 1
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 4
+                  "@": 5
                 },
                 "level": 1
               }
@@ -130,21 +130,21 @@
           }
         },
         {
-          "entity_id": 2,
+          "entity_id": 3,
           "entity_summary": {
             "direct": {
-              "@": 5
+              "@": 6
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 3
+                  "@": 4
                 },
                 "level": 2
               }
@@ -152,21 +152,21 @@
           }
         },
         {
-          "entity_id": 3,
+          "entity_id": 4,
           "entity_summary": {
             "direct": {
-              "@": 1
+              "@": 2
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 2
               }
@@ -174,21 +174,21 @@
           }
         },
         {
-          "entity_id": 4,
+          "entity_id": 5,
           "entity_summary": {
             "direct": {
-              "@": 2
+              "@": 3
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 5
+                  "@": 6
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 3
+                  "@": 4
                 },
                 "level": 2
               }
@@ -196,27 +196,27 @@
           }
         },
         {
-          "entity_id": 5,
+          "entity_id": 6,
           "entity_summary": {
             "direct": {
-              "@": 4
+              "@": 5
             },
             "indirect": [
               {
                 "entity": {
-                  "@": 1
+                  "@": 2
                 },
                 "level": 1
               },
               {
                 "entity": {
-                  "@": 2
+                  "@": 3
                 },
                 "level": 2
               },
               {
                 "entity": {
-                  "@": 0
+                  "@": 1
                 },
                 "level": 3
               }
@@ -229,7 +229,7 @@
   ],
   "id_table": [
     {
-      "id": 0,
+      "id": 1,
       "name": {
         "namespace": [
           {
@@ -242,7 +242,7 @@
       }
     },
     {
-      "id": 1,
+      "id": 2,
       "name": {
         "namespace": [
           {
@@ -259,7 +259,7 @@
       }
     },
     {
-      "id": 2,
+      "id": 3,
       "name": {
         "namespace": [
           {
@@ -276,7 +276,7 @@
       }
     },
     {
-      "id": 3,
+      "id": 4,
       "name": {
         "namespace": [
           {
@@ -289,7 +289,7 @@
       }
     },
     {
-      "id": 4,
+      "id": 5,
       "name": {
         "namespace": [
           {
@@ -306,7 +306,7 @@
       }
     },
     {
-      "id": 5,
+      "id": 6,
       "name": {
         "namespace": [
           {
@@ -325,37 +325,37 @@
   ],
   "linkage_table": [
     {
-      "id": 0,
+      "id": 1,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 1,
+      "id": 2,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 2,
+      "id": 3,
       "linkage": {
         "type": "None"
       }
     },
     {
-      "id": 3,
+      "id": 4,
       "linkage": {
         "type": "External"
       }
     },
     {
-      "id": 4,
+      "id": 5,
       "linkage": {
         "type": "Internal"
       }
     },
     {
-      "id": 5,
+      "id": 6,
       "linkage": {
         "type": "None"
       }
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/EntityIdTableTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/EntityIdTableTest.cpp
index 7afa4943bcb79..cb1eff88fec18 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/EntityIdTableTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/EntityIdTableTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
+#include "TestFixture.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
 #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
@@ -16,7 +17,9 @@ namespace clang {
 namespace ssaf {
 namespace {
 
-TEST(EntityIdTableTest, CreateNewEntity) {
+struct EntityIdTableTest : ssaf::TestFixture {};
+
+TEST_F(EntityIdTableTest, CreateNewEntity) {
   EntityIdTable Table;
 
   EntityName Entity("c:@F at foo", "", {});
@@ -25,7 +28,7 @@ TEST(EntityIdTableTest, CreateNewEntity) {
   EXPECT_TRUE(Table.contains(Entity));
 }
 
-TEST(EntityIdTableTest, Idempotency) {
+TEST_F(EntityIdTableTest, Idempotency) {
   EntityIdTable Table;
 
   EntityName Entity("c:@F at foo", "", {});
@@ -40,7 +43,7 @@ TEST(EntityIdTableTest, Idempotency) {
   EXPECT_EQ(Id1, Id3);
 }
 
-TEST(EntityIdTableTest, ExistsTrue) {
+TEST_F(EntityIdTableTest, ExistsTrue) {
   EntityIdTable Table;
 
   EntityName Entity1("c:@F at foo", "", {});
@@ -53,7 +56,7 @@ TEST(EntityIdTableTest, ExistsTrue) {
   EXPECT_TRUE(Table.contains(Entity2));
 }
 
-TEST(EntityIdTableTest, ExistsFalse) {
+TEST_F(EntityIdTableTest, ExistsFalse) {
   EntityIdTable Table;
 
   EntityName Entity1("c:@F at foo", "", {});
@@ -65,7 +68,7 @@ TEST(EntityIdTableTest, ExistsFalse) {
   EXPECT_FALSE(Table.contains(Entity2));
 }
 
-TEST(EntityIdTableTest, MultipleEntities) {
+TEST_F(EntityIdTableTest, MultipleEntities) {
   EntityIdTable Table;
 
   EntityName Entity1("c:@F at foo", "", {});
@@ -81,7 +84,7 @@ TEST(EntityIdTableTest, MultipleEntities) {
   EXPECT_NE(Id2, Id3);
 }
 
-TEST(EntityIdTableTest, WithBuildNamespace) {
+TEST_F(EntityIdTableTest, WithBuildNamespace) {
   EntityIdTable Table;
 
   NestedBuildNamespace NS = NestedBuildNamespace::makeCompilationUnit("test.o");
@@ -96,7 +99,7 @@ TEST(EntityIdTableTest, WithBuildNamespace) {
   EXPECT_NE(Id1, Id2);
 }
 
-TEST(EntityIdTableTest, ForEachEmptyTable) {
+TEST_F(EntityIdTableTest, ForEachEmptyTable) {
   EntityIdTable Table;
 
   int CallbackCount = 0;
@@ -106,7 +109,7 @@ TEST(EntityIdTableTest, ForEachEmptyTable) {
   EXPECT_EQ(CallbackCount, 0);
 }
 
-TEST(EntityIdTableTest, ForEachMultipleEntities) {
+TEST_F(EntityIdTableTest, ForEachMultipleEntities) {
   EntityIdTable Table;
 
   EntityName Entity1("c:@F at foo", "", {});
@@ -137,6 +140,16 @@ TEST(EntityIdTableTest, ForEachMultipleEntities) {
   EXPECT_TRUE(VisitedNames.count(Entity3));
 }
 
+TEST_F(EntityIdTableTest, IndexZeroIsReserved) {
+  EntityIdTable Table;
+  EntityId Id1 = Table.getId(EntityName{"c:@F at foo", "", {}});
+  EntityId Id2 = Table.getId(EntityName{"c:@F at bar", "", {}});
+
+  // Index 0 is reserved; the first assigned ID should start at 1.
+  EXPECT_NE(getIndex(Id1), 0U);
+  EXPECT_NE(getIndex(Id2), 0U);
+}
+
 } // namespace
 } // namespace ssaf
 } // namespace clang
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
index ece39e2164df7..23f8fb8abf4b0 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
@@ -10,6 +10,7 @@
 #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"
@@ -266,4 +267,57 @@ TEST_F(TUSummaryBuilderTest, AddConflictingSummaryToSameEntity) {
               Optional(Field(&MockSummaryData1::Value, 30)));
 }
 
+TEST_F(TUSummaryBuilderTest, SetLinkageWithExplicitType) {
+  EntityId ID = addTestEntity(Builder, "c:@F at foo");
+  Builder.setLinkage(ID, EntityLinkageType::External);
+
+  const auto &LT = getLinkageTable(Summary);
+  auto It = LT.find(ID);
+  ASSERT_NE(It, LT.end());
+  EXPECT_EQ(It->second.getLinkage(), EntityLinkageType::External);
+}
+
+TEST_F(TUSummaryBuilderTest, SetLinkageMultipleEntities) {
+  EntityId ID1 = addTestEntity(Builder, "c:@F at foo");
+  EntityId ID2 = addTestEntity(Builder, "c:@F at bar");
+
+  Builder.setLinkage(ID1, EntityLinkageType::External);
+  Builder.setLinkage(ID2, EntityLinkageType::Internal);
+
+  const auto &LT = getLinkageTable(Summary);
+  ASSERT_EQ(LT.size(), 2U);
+
+  auto It1 = LT.find(ID1);
+  ASSERT_NE(It1, LT.end());
+  EXPECT_EQ(It1->second.getLinkage(), EntityLinkageType::External);
+
+  auto It2 = LT.find(ID2);
+  ASSERT_NE(It2, LT.end());
+  EXPECT_EQ(It2->second.getLinkage(), EntityLinkageType::Internal);
+}
+
+TEST_F(TUSummaryBuilderTest, SetLinkageIsIdempotent) {
+  EntityId ID = addTestEntity(Builder, "c:@F at foo");
+
+  // First call sets the linkage.
+  Builder.setLinkage(ID, EntityLinkageType::Internal);
+  // Second call with a different value should not overwrite (try_emplace).
+  Builder.setLinkage(ID, EntityLinkageType::External);
+
+  const auto &LT = getLinkageTable(Summary);
+  auto It = LT.find(ID);
+  ASSERT_NE(It, LT.end());
+  EXPECT_EQ(It->second.getLinkage(), EntityLinkageType::Internal);
+}
+
+TEST_F(TUSummaryBuilderTest, SetLinkageNone) {
+  EntityId ID = addTestEntity(Builder, "c:@F at foo");
+  Builder.setLinkage(ID, EntityLinkageType::None);
+
+  const auto &LT = getLinkageTable(Summary);
+  auto It = LT.find(ID);
+  ASSERT_NE(It, LT.end());
+  EXPECT_EQ(It->second.getLinkage(), EntityLinkageType::None);
+}
+
 } // namespace



More information about the cfe-commits mailing list