https://github.com/ziqingluo-90 updated 
https://github.com/llvm/llvm-project/pull/201946

>From 0bb7ad1c4c46367b62fe7c08cf3a78c4dbde7a91 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Fri, 5 Jun 2026 13:59:02 -0700
Subject: [PATCH 1/5] [SSAF] Let function parameters inherit linkage from their
 parent functions

SSAF treats parameters as entities and may not always associate them
back to their parent functions. Therefore, it needs to identify
parameters of functions with external linkage across different
TUs. Treating them as having no linkage (as in C++) causes the same
parameter in different TUs to be assigned different EntityIDs. As a
result, the behavior of the parameter across multiple TUs cannot be
correlated.

rdar://178844032
---
 .../Core/Model/EntityLinkage.h                |  6 +-
 .../Core/TUSummary/TUSummaryExtractor.cpp     | 18 ++++++
 .../external-inline-function-in-multi-tu.test | 63 +++++++++++++++++++
 .../TUSummaryBuilderTest.cpp                  | 42 +++++++++++++
 4 files changed, 127 insertions(+), 2 deletions(-)
 create mode 100644 
clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
index cdae99ad0b6b2..b144d735a75a1 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
@@ -15,9 +15,11 @@
 namespace clang::ssaf {
 
 enum class EntityLinkageType {
-  None,     ///< local variables, function parameters
+  None,     ///< local variables, parameters of functions with no external
+            ///< linkage
   Internal, ///< static functions/variables, anonymous namespace
-  External  ///< globally visible across translation units
+  External  ///< globally visible across translation units (including 
parameters
+            ///< of functions with external linkage)
 };
 
 /// Represents the linkage properties of an entity in the program model.
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
index 9594a6b179db5..88583f8fe73e2 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
@@ -8,10 +8,12 @@
 
 #include 
"clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.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 "llvm/Support/Casting.h"
 #include <optional>
 
 using namespace clang;
@@ -22,6 +24,22 @@ static EntityLinkageType getLinkageForDecl(const Decl *D) {
   if (!ND)
     return EntityLinkageType::None;
 
+  // Parameters have no linkage in C++, but SSAF needs them to inherit
+  // the external linkage from their parent functions.
+  // Here is why:
+  //   SSAF treats parameters as entities and may not always associate them 
back
+  //   to their parent functions. Therefore, it needs to identify parameters of
+  //   functions with external linkage across different TUs. Treating them as
+  //   having no linkage (as in C++) causes the same parameter in different TUs
+  //   to be assigned different EntityIDs. As a result, the behavior of the
+  //   parameter across multiple TUs cannot be correlated.
+  if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
+    if (const FunctionDecl *FD = llvm::dyn_cast_or_null<FunctionDecl>(
+            PVD->getParentFunctionOrMethod())) {
+      return getLinkageForDecl(FD);
+    }
+  }
+
   switch (ND->getFormalLinkage()) {
   case Linkage::Invalid: {
     llvm_unreachable("Shouldn't be invalid");
diff --git 
a/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
 
b/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
new file mode 100644
index 0000000000000..dce0b28748b80
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
@@ -0,0 +1,63 @@
+// For an 'inline' function with multiple definitions in different
+// translation units (TUs), its call sites in different TUs and one of
+// its definitions, which is chosen by the linker, are properly connected
+// during bounds propagation.
+
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// Extract per-TU PointerFlow + UnsafeBufferUsage summaries.
+// RUN: %clang_cc1 -fsyntax-only -I %t %t/tu1.cpp \
+// RUN:   --ssaf-extract-summaries=PointerFlow \
+// RUN:   --ssaf-extract-summaries=UnsafeBufferUsage \
+// RUN:   --ssaf-tu-summary-file=%t/tu1.summary.json
+// RUN: %clang_cc1 -fsyntax-only -I %t %t/tu2.cpp \
+// RUN:   --ssaf-extract-summaries=PointerFlow \
+// RUN:   --ssaf-extract-summaries=UnsafeBufferUsage \
+// RUN:   --ssaf-tu-summary-file=%t/tu2.summary.json
+
+// Link the two TU summaries into a single LU summary.
+// RUN: clang-ssaf-linker %t/tu1.summary.json %t/tu2.summary.json \
+// RUN:   -o %t/lu.json
+
+// Run UnsafeBufferReachableAnalysis on the linked LU summary.
+// RUN: clang-ssaf-analyzer %t/lu.json -o %t/wpa.json \
+// RUN:   -a UnsafeBufferReachableAnalysisResult
+
+// RUN: FileCheck %s --input-file=%t/wpa.json
+
+//--- shared.h
+inline int shared_inline(int *p) {
+  int * l = p;      
+  return l[5];
+}
+
+//--- tu1.cpp
+#include "shared.h"
+int f(int *x) {
+  return shared_inline(x);
+}
+
+//--- tu2.cpp
+#include "shared.h"
+int g(int *y) {
+  return shared_inline(y);
+}
+
+
+// Check that these parameters are all reachable from the local variable 'l':
+// parameter 'y' of function 'g',
+// parameter 'x' of function 'f', and
+// parameter 'p' of function 'shared_inline'
+
+// CHECK-DAG: "suffix": "1",{{[[:space:]]+}}"usr": "c:@F@f#*I#"
+// CHECK-DAG: "suffix": "1",{{[[:space:]]+}}"usr": "c:@F@g#*I#"
+// CHECK-DAG: "suffix": "1",{{[[:space:]]+}}"usr": "c:@F@shared_inline#*I#"
+// CHECK-DAG: "usr": "c:shared.h@{{[0-9]+}}@F@shared_inline#{{.*}}@l"
+
+// Confirm UnsafeBufferReachableAnalysisResult is present in the output.
+// CHECK: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+
+
diff --git 
a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp 
b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
index 3fff10f08d38a..b29b8e874c0af 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
@@ -275,6 +275,7 @@ struct TUSummaryBuilderLinkageTest : TUSummaryBuilderTest {
   std::unique_ptr<ASTUnit> AST;
   static constexpr auto Internal = EntityLinkageType::Internal;
   static constexpr auto External = EntityLinkageType::External;
+  static constexpr auto None = EntityLinkageType::None;
 
   const FunctionDecl *findFnByName(StringRef Name) {
     return ssaf::findFnByName(Name, AST->getASTContext());
@@ -336,4 +337,45 @@ TEST_F(TUSummaryBuilderLinkageTest, 
ConstGlobalHasInternalLinkage) {
   EXPECT_EQ(getLinkageFor(Extractor.addEntity(VD)), Internal);
 }
 
+// See 'getLinkageForDecl' in
+// ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
+
+TEST_F(TUSummaryBuilderLinkageTest, ParamOfExternalFunctionIsExternal) {
+  AST = tooling::buildASTFromCode("void target(int *ptr) {}");
+  const auto *PVD = findDeclByName<ParmVarDecl>("ptr", AST->getASTContext());
+  ASSERT_TRUE(PVD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(PVD)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ParamOfInlineFunctionIsExternal) {
+  AST = tooling::buildASTFromCode("inline void target(int *ptr) {}");
+  const auto *PVD = findDeclByName<ParmVarDecl>("ptr", AST->getASTContext());
+  ASSERT_TRUE(PVD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(PVD)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ParamOfStaticFunctionIsInternal) {
+  AST = tooling::buildASTFromCode("static void target(int *ptr) {}");
+  const auto *PVD = findDeclByName<ParmVarDecl>("ptr", AST->getASTContext());
+  ASSERT_TRUE(PVD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(PVD)), Internal);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ParamOfAnonNamespaceFunctionIsInternal) {
+  AST = tooling::buildASTFromCode("namespace {\n"
+                                  "  void target(int *ptr) {}\n"
+                                  "}");
+  const auto *PVD = findDeclByName<ParmVarDecl>("ptr", AST->getASTContext());
+  ASSERT_TRUE(PVD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(PVD)), Internal);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ParamOfClassMemberFunctionIsExternal) {
+  AST = tooling::buildASTFromCode(
+      "class C { public: void target(int *ptr) {} };");
+  const auto *PVD = findDeclByName<ParmVarDecl>("ptr", AST->getASTContext());
+  ASSERT_TRUE(PVD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(PVD)), External);
+}
+
 } // namespace

>From c51d3f4da22d67cbde3343dec7615e14163cd1e1 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Fri, 5 Jun 2026 16:55:04 -0700
Subject: [PATCH 2/5] remove unused variable

---
 .../ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp     | 1 -
 1 file changed, 1 deletion(-)

diff --git 
a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp 
b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
index b29b8e874c0af..8e1be166f1ce5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
@@ -275,7 +275,6 @@ struct TUSummaryBuilderLinkageTest : TUSummaryBuilderTest {
   std::unique_ptr<ASTUnit> AST;
   static constexpr auto Internal = EntityLinkageType::Internal;
   static constexpr auto External = EntityLinkageType::External;
-  static constexpr auto None = EntityLinkageType::None;
 
   const FunctionDecl *findFnByName(StringRef Name) {
     return ssaf::findFnByName(Name, AST->getASTContext());

>From 01ab5734f4fed29c94f4cce724daae35c7b340f6 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Fri, 5 Jun 2026 18:09:32 -0700
Subject: [PATCH 3/5] Add tests for function return and fields to ensure their
 linkages are not affected.

---
 .../TUSummaryBuilderTest.cpp                  | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git 
a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp 
b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
index 8e1be166f1ce5..cbd6dc9c59399 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/TUSummaryBuilderTest.cpp
@@ -377,4 +377,25 @@ TEST_F(TUSummaryBuilderLinkageTest, 
ParamOfClassMemberFunctionIsExternal) {
   EXPECT_EQ(getLinkageFor(Extractor.addEntity(PVD)), External);
 }
 
+TEST_F(TUSummaryBuilderLinkageTest, FieldOfExternalClassIsExternal) {
+  AST = tooling::buildASTFromCode("struct S { int *p; };");
+  const auto *FD = findDeclByName<FieldDecl>("p", AST->getASTContext());
+  ASSERT_TRUE(FD);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntity(FD)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ReturnSlotInheritsParentLinkage) {
+  AST = tooling::buildASTFromCode("int *target() { return nullptr; }");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntityForReturn(Fn)), External);
+}
+
+TEST_F(TUSummaryBuilderLinkageTest, ReturnSlotInheritsParentLinkageInternal) {
+  AST = tooling::buildASTFromCode("static int *target() { return nullptr; }");
+  const FunctionDecl *Fn = findFnByName("target");
+  ASSERT_TRUE(Fn);
+  EXPECT_EQ(getLinkageFor(Extractor.addEntityForReturn(Fn)), Internal);
+}
+
 } // namespace

>From 5504751654995153d3ebb0dfdf42244a360bb07a Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 8 Jun 2026 12:36:29 -0700
Subject: [PATCH 4/5] Update
 clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <[email protected]>
---
 .../Core/TUSummary/TUSummaryExtractor.cpp                       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
index 88583f8fe73e2..fcfbb7aec2bf3 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
@@ -34,7 +34,7 @@ static EntityLinkageType getLinkageForDecl(const Decl *D) {
   //   to be assigned different EntityIDs. As a result, the behavior of the
   //   parameter across multiple TUs cannot be correlated.
   if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
-    if (const FunctionDecl *FD = llvm::dyn_cast_or_null<FunctionDecl>(
+    if (const auto *FD = llvm::dyn_cast<FunctionDecl>(
             PVD->getParentFunctionOrMethod())) {
       return getLinkageForDecl(FD);
     }

>From 1552e58ef7a88b9a630f98ed4ae23bb82e28e360 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <[email protected]>
Date: Mon, 8 Jun 2026 12:58:52 -0700
Subject: [PATCH 5/5] address comments

---
 .../Core/Model/EntityLinkage.h                       |  3 +--
 .../Core/TUSummary/TUSummaryExtractor.cpp            |  2 +-
 .../external-inline-function-in-multi-tu.test        | 12 ++++--------
 3 files changed, 6 insertions(+), 11 deletions(-)

diff --git 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
index b144d735a75a1..2efddffd9a29c 100644
--- 
a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
+++ 
b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h
@@ -15,8 +15,7 @@
 namespace clang::ssaf {
 
 enum class EntityLinkageType {
-  None,     ///< local variables, parameters of functions with no external
-            ///< linkage
+  None,     ///< local variables
   Internal, ///< static functions/variables, anonymous namespace
   External  ///< globally visible across translation units (including 
parameters
             ///< of functions with external linkage)
diff --git 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
index fcfbb7aec2bf3..f0602e0d5550f 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.cpp
@@ -34,7 +34,7 @@ static EntityLinkageType getLinkageForDecl(const Decl *D) {
   //   to be assigned different EntityIDs. As a result, the behavior of the
   //   parameter across multiple TUs cannot be correlated.
   if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
-    if (const auto *FD = llvm::dyn_cast<FunctionDecl>(
+    if (const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(
             PVD->getParentFunctionOrMethod())) {
       return getLinkageForDecl(FD);
     }
diff --git 
a/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
 
b/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
index dce0b28748b80..4d595a4d222bb 100644
--- 
a/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
+++ 
b/clang/test/Analysis/Scalable/PointerFlow/external-inline-function-in-multi-tu.test
@@ -4,19 +4,15 @@
 // during bounds propagation.
 
 
+// DEFINE: %{extract} = %clang_cc1 -fsyntax-only -I %t 
--ssaf-extract-summaries=PointerFlow,UnsafeBufferUsage
+
 // RUN: rm -rf %t
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 
 // Extract per-TU PointerFlow + UnsafeBufferUsage summaries.
-// RUN: %clang_cc1 -fsyntax-only -I %t %t/tu1.cpp \
-// RUN:   --ssaf-extract-summaries=PointerFlow \
-// RUN:   --ssaf-extract-summaries=UnsafeBufferUsage \
-// RUN:   --ssaf-tu-summary-file=%t/tu1.summary.json
-// RUN: %clang_cc1 -fsyntax-only -I %t %t/tu2.cpp \
-// RUN:   --ssaf-extract-summaries=PointerFlow \
-// RUN:   --ssaf-extract-summaries=UnsafeBufferUsage \
-// RUN:   --ssaf-tu-summary-file=%t/tu2.summary.json
+// RUN: %{extract} %t/tu1.cpp --ssaf-tu-summary-file=%t/tu1.summary.json
+// RUN: %{extract} %t/tu2.cpp --ssaf-tu-summary-file=%t/tu2.summary.json
 
 // Link the two TU summaries into a single LU summary.
 // RUN: clang-ssaf-linker %t/tu1.summary.json %t/tu2.summary.json \

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

Reply via email to