[clang] [SSAF][PointerFlow] Recognize reference-to-pointer/array Decls (PR #203633)
Ziqing Luo via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 12 14:04:24 PDT 2026
https://github.com/ziqingluo-90 created https://github.com/llvm/llvm-project/pull/203633
Decls of reference-to-pointer/array types are now treated the same as those of pointer/array type.
rdar://179173940
>From 00b39efd86ec25b128d624ed0f99c3d6bab738b6 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 12 Jun 2026 12:37:45 -0700
Subject: [PATCH] [SSAF][PointerFlow] Recognize reference-to-pointer/array
Decls
Decls of reference-to-pointer/array types are now treated the same as
those of pointer/array type.
rdar://179173940
---
.../Analyses/SSAFAnalysesCommon.h | 9 ++-
.../PointerFlow/lref-to-rref-cast.test | 40 ++++++++++++
.../Analyses/PointerFlow/PointerFlowTest.cpp | 64 ++++++++++++++++++-
3 files changed, 110 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 63755304ea3c9..144bab532d4bf 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -42,11 +42,18 @@ llvm::Error makeSawButExpectedError(const JSONTy &Saw, llvm::StringRef Expected,
return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
}
-template <typename DeclOrExpr> bool hasPtrOrArrType(const DeclOrExpr *E) {
+///\return true iff expression `E` has pointer or array type.
+inline bool hasPtrOrArrType(const Expr *E) {
return llvm::isa<clang::PointerType, clang::ArrayType>(
E->getType().getCanonicalType());
}
+///\return true iff Decl `D` has (reference-to) pointer or array type.
+inline bool hasPtrOrArrType(const ValueDecl *D) {
+ return llvm::isa<clang::PointerType, clang::ArrayType>(
+ D->getType().getNonReferenceType().getCanonicalType());
+}
+
llvm::Error makeEntityNameErr(clang::ASTContext &Ctx,
const clang::NamedDecl *D);
diff --git a/clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test b/clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test
new file mode 100644
index 0000000000000..195a4d47ae54a
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/lref-to-rref-cast.test
@@ -0,0 +1,40 @@
+// Test that entities representing references-to-pointers are
+// connected through 'fake_move' (my fake 'std::move').
+
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: split-file %s %t
+
+// Extract per-TU PointerFlow + UnsafeBufferUsage summaries.
+// RUN: %clang_cc1 -fsyntax-only %t/tu.cpp \
+// RUN: --ssaf-extract-summaries=PointerFlow,UnsafeBufferUsage \
+// RUN: --ssaf-tu-summary-file=%t/tu.summary.json
+
+// Link into a single LU summary.
+// RUN: clang-ssaf-linker %t/tu.summary.json -o %t/lu.json
+
+// RUN: clang-ssaf-analyzer %t/lu.json -o %t/wpa.json \
+// RUN: -a UnsafeBufferReachableAnalysisResult
+
+//--- tu.cpp
+constexpr int* &&fake_move(int* &arg) noexcept { //FIXME: after #198927, we can use template
+ return static_cast<int* &&>(arg);
+}
+
+void foo(int *p) {
+ int *&&rp = fake_move(p);
+ rp[5] = 0;
+}
+
+// Check that unsafe buffer propagates from 'rp' to 'p' by ensuring
+// that both 'rp' and 'p' are in the reachable set.
+
+// RUN: FileCheck %s --input-file=%t/wpa.json
+
+// CHECK-DAG: "id": [[RP_ID:[0-9]+]],{{([^]]|[[:space:]])+\],[[:space:]]+"suffix": "",[[:space:]]+"usr": "[^"]*}}@rp"
+// CHECK-DAG: "id": [[P_ID:[0-9]+]],{{([^]]|[[:space:]])+\],[[:space:]]+"suffix": "1",[[:space:]]+"usr": }}"c:@F at foo#*I#"
+
+// CHECK: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// CHECK-DAG: {{\{[[:space:]]+}}"@": [[RP_ID]]{{[[:space:]]+\},[[:space:]]+1[[:space:]]+\]}}
+// CHECK-DAG: {{\{[[:space:]]+}}"@": [[P_ID]]{{[[:space:]]+\},[[:space:]]+1[[:space:]]+\]}}
+// CHECK: "analysis_name"
+
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index bff9dc10bfc1f..c1f5416718c4c 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -13,9 +13,7 @@
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Frontend/ASTUnit.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
@@ -1193,4 +1191,66 @@ TEST_F(PointerFlowTest, CXXConstructExprArrayInit) {
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"arr", 1U}}}));
}
+
+//////////////////////////////////////////////////////////////
+// Reference-to-pointer Tests //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerFlowTest, ArgToRefParam) {
+ ASSERT_TRUE(setUpTest(R"cpp(
+ void callee(int *&rp);
+ void caller(int *p) {
+ callee(p);
+ }
+ )cpp"));
+
+ auto *Sum = getEntitySummary("caller");
+
+ ASSERT_TRUE(Sum);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"rp", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerFlowTest, ArgToRefParamLevel2) {
+ ASSERT_TRUE(setUpTest(R"cpp(
+ void callee(int **&rp);
+ void caller(int **pp) {
+ callee(pp);
+ }
+ )cpp"));
+
+ auto *Sum = getEntitySummary("caller");
+
+ ASSERT_TRUE(Sum);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"rp", 1U}, {"pp", 1U}}}));
+}
+
+TEST_F(PointerFlowTest, InitRefPtr) {
+ ASSERT_TRUE(setUpTest(R"cpp(
+ void foo(int *p) {
+ int *&rp = p;
+ int * const & crp = p;
+ }
+ )cpp"));
+
+ auto *Sum = getEntitySummary("foo");
+
+ ASSERT_TRUE(Sum);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"rp", 1U}, {"p", 1U}},
+ {{"crp", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerFlowTest, ReturnRefPtr) {
+ ASSERT_TRUE(setUpTest(R"cpp(
+ int *& f();
+ int *& foo() {
+ return f();
+ }
+ )cpp"));
+
+ auto *Sum = getEntitySummary("foo");
+
+ ASSERT_TRUE(Sum);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"f", 1U, true}}}));
+}
+
} // namespace
More information about the cfe-commits
mailing list