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/2] [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/2] 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());

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

Reply via email to