[llvm-branch-commits] [clang] [SSAF][PointerFlow] Change unsafe-buffer reachability analysis back to simple graph search (PR #218209)
Ziqing Luo via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Aug 27 12:01:38 PDT 2026
https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/218209
>From 5e2623c98229603cdf75d03219167c91c0d07a9e Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 21 Aug 2026 18:22:03 -0700
Subject: [PATCH 1/2] [SSAF][PointerFlow] Change unsafe-buffer reachability
analysis back to simple graph search
Because of commit 30cd4297b, we no longer need unsafe-buffer
reachability analysis to "uncompress" pointer flow graphs. It can go
back to simple DFS. Since it deals with large data, simplicity is
important.
In addition, unit tests for the "compressed" pointer flow graphs are
moved to lit tests because they are no longer suitable as WPA unit
tests. As lit tests, they are end-to-end tests where the extractor is
involved and is responsible for generating "uncompressed" graphs.
Final step of
rdar://183529483
---
.../UnsafeBufferUsageAnalysis.cpp | 50 +--
.../unsafe-buffer-reachable-cast-cycle.test | 52 +++
.../unsafe-buffer-reachable-topologies.test | 367 ++++++++++++++++++
.../UnsafeBufferReachableAnalysisTest.cpp | 190 ---------
4 files changed, 424 insertions(+), 235 deletions(-)
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-topologies.test
diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
index 4c92a37a078d1..90a0e3633151a 100644
--- a/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
+++ b/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
@@ -145,56 +145,16 @@ class UnsafeBufferReachableAnalysis
TypeConstrainedPointersAnalysisResult,
UnsafeBufferUsageAnalysisResult> {
- /// BoundsPropagationGraph adds bounds propagation semantics to the
- /// pointer-flow graph, which represents the set of static pointer assignment
- /// sites collected from the source code. Consider the following example:
- ///
- /// void f(int ***p, int **q) {
- /// *p = q;
- /// (**p)[5] = 0;
- /// }
- ///
- /// There is one static pointer assignment thus one pointer-flow edge: (p, 2)
- /// -> (q, 1). In terms of bounds propagation, this assignment implies that if
- /// 'p' at pointer level 2 requires bounds, 'q' at pointer level 1 must also
- /// have them. Furthermore, this relationship propagates to deeper indirection
- /// levels: if 'p' at level 3 requires bounds, so does 'q' at level 2.
- ///
- /// In the example above, `(**p)` requires bounds (due to the array index),
- /// and therefore `*q` must require bounds as well.
- ///
- /// To generalize the idea, the BoundsPropagationGraph is defined as a super
- /// graph of the input pointer-flow graph by:
- ///
- /// For each edge (src, i) -> (dest, j) in the pointer-flow graph, the
- /// BoundsPropagationGraph has a finite set of edges
- /// {(src, i + d) -> (dest, j + d) | 0 <= d < UB}, where UB is an upper
- /// bound based on the maximum pointer level the pointer type can have.
struct BoundsPropagationGraph {
- private:
EdgeSet PointerFlows;
- public:
- BoundsPropagationGraph(EdgeSet PointerFlows)
- : PointerFlows(std::move(PointerFlows)) {}
-
/// Returns the EntityPointerLevelSet that are reachable from \p Src by
/// one edge in the BoundsPropagationGraph.
EntityPointerLevelSet getDestNodes(const EntityPointerLevel &Src) const {
- unsigned SrcPtrLv = Src.getPointerLevel();
- EntityPointerLevelSet Result;
-
- for (unsigned P = 1; P <= SrcPtrLv; ++P) {
- auto I = PointerFlows.find(buildEntityPointerLevel(Src.getEntity(), P));
-
- if (I != PointerFlows.end()) {
- unsigned Delta = SrcPtrLv - P;
- for (const auto &EPL : I->second)
- Result.insert(buildEntityPointerLevel(
- EPL.getEntity(), EPL.getPointerLevel() + Delta));
- }
- }
- return Result;
+ auto I = PointerFlows.find(Src);
+ if (I == PointerFlows.end())
+ return {};
+ return I->second;
}
};
@@ -265,7 +225,7 @@ class UnsafeBufferReachableAnalysis
FilteredDstRange.end());
}
if (!FilteredSubGraph.empty())
- BPG.try_emplace(Id, std::move(FilteredSubGraph));
+ BPG.try_emplace(Id, BoundsPropagationGraph{std::move(FilteredSubGraph)});
}
// Filter out type-constrained pointers from `UnsafePtrs`:
diff --git a/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test b/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test
new file mode 100644
index 0000000000000..46a3092b801ea
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test
@@ -0,0 +1,52 @@
+// Regression test: UnsafeBufferReachableAnalysis used to hang on
+//
+// void f(char **a, char ***b, int i, int j) {
+// a[i][j] = 0; // 'a' is unsafe at levels 1 and 2
+// a = *b; // (a, 1) -> (b, 2)
+// b = (char ***)*a; // (b, 1) -> (a, 2)
+// }
+//
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -fsyntax-only %t/src.cpp \
+// RUN: --ssaf-extract-summaries=PointerFlow,UnsafeBufferUsage,TypeConstrainedPointers \
+// RUN: --ssaf-compilation-unit-id="tu-1" \
+// RUN: --ssaf-tu-summary-file=%t/src.summary.json
+
+// RUN: clang-ssaf-linker %t/src.summary.json -o %t/lu.json
+
+// RUN: clang-ssaf-analyzer %t/lu.json -o %t/wpa.json \
+// RUN: -a UnsafeBufferReachableAnalysisResult
+
+// The CHECK lines below use readable tokens instead of inline FileCheck regex.
+// Expand the tokens into regex, then run FileCheck on the expanded copy:
+// $NS - skip the "namespace" array, up to its closing ']'.
+// $WS - whitespace, possibly spanning newlines.
+// $PTR_Ln - a reachable-set entry closing as "}, n ]", i.e. pointer level n.
+// RUN: sed -e 's|$NS|{{([^]]\|[[:space:]])+\\],}}|g' \
+// RUN: -e 's|$WS|{{[[:space:]]+}}|g' \
+// RUN: -e 's|$PTR_L1|{{[[:space:]]+\\},[[:space:]]+1[[:space:]]+\\]}}|g' \
+// RUN: -e 's|$PTR_L2|{{[[:space:]]+\\},[[:space:]]+2[[:space:]]+\\]}}|g' \
+// RUN: -e 's|$PTR_L3|{{[[:space:]]+\\},[[:space:]]+3[[:space:]]+\\]}}|g' \
+// RUN: %s | FileCheck - --input-file=%t/wpa.json
+
+//--- src.cpp
+void f(char **a, char ***b, int i, int j) {
+ a[i][j] = 0; // 'a' is unsafe at levels 1 and 2
+ a = *b; // (a, 1) -> (b, 2)
+ b = (char ***)*a; // (b, 1) -> (a, 2)
+}
+
+// Capture the entity ids for parameters 'a' (suffix "1") and 'b' (suffix "2").
+// CHECK-DAG: "id": [[a_ID:[0-9]+]],$NS$WS"suffix": "1",$WS"usr": "c:@F at f#**C#*S0_#I#I#"
+// CHECK-DAG: "id": [[b_ID:[0-9]+]],$NS$WS"suffix": "2",$WS"usr": "c:@F at f#**C#*S0_#I#I#"
+
+// The reachable set is exactly {(a, 1), (a, 2), (b, 2), (b, 3)}
+// CHECK: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// CHECK: "@": [[a_ID]]$PTR_L1
+// CHECK: "@": [[a_ID]]$PTR_L2
+// CHECK: "@": [[b_ID]]$PTR_L2
+// CHECK: "@": [[b_ID]]$PTR_L3
+// CHECK-NOT: "@":
+// CHECK: "analysis_name":
diff --git a/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-topologies.test b/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-topologies.test
new file mode 100644
index 0000000000000..397b7df48b51e
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-topologies.test
@@ -0,0 +1,367 @@
+// End-to-end tests of UnsafeBufferReachableAnalysis. Testing against
+// pointer flow graphs of different shapes.
+//
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: split-file %s %t
+
+// The extract -> link -> analyze -> check pipeline is defined once and reused;
+// each topology only redefines its source name and FileCheck prefix.
+//
+// The CHECK lines use readable tokens instead of inline FileCheck regex; the
+// sed below expands them into regex on the fly and pipes the result straight
+// into FileCheck (no intermediate file):
+// $NS - skip the "namespace" array, up to its closing ']'.
+// $WS - whitespace, possibly spanning newlines.
+// $PTR_Ln - a reachable-set entry closing as "}, n ]", i.e. pointer level n.
+//
+// DEFINE: %{name} =
+// DEFINE: %{prefix} =
+// DEFINE: %{check} = \
+// DEFINE: %clang_cc1 -fsyntax-only -Wno-self-assign %t/%{name}.cpp \
+// DEFINE: --ssaf-extract-summaries=PointerFlow,UnsafeBufferUsage,TypeConstrainedPointers \
+// DEFINE: --ssaf-compilation-unit-id=%{name} \
+// DEFINE: --ssaf-tu-summary-file=%t/%{name}.summary.json && \
+// DEFINE: clang-ssaf-linker %t/%{name}.summary.json -o %t/%{name}.lu.json && \
+// DEFINE: clang-ssaf-analyzer %t/%{name}.lu.json -o %t/%{name}.wpa.json \
+// DEFINE: -a UnsafeBufferReachableAnalysisResult && \
+// DEFINE: sed -e 's|$NS|{{([^]]\|[[:space:]])+\],}}|g' \
+// DEFINE: -e 's|$WS|{{[[:space:]]+}}|g' \
+// DEFINE: -e 's|$PTR_L1|{{[[:space:]]+\\},[[:space:]]+1[[:space:]]+\\]}}|g' \
+// DEFINE: -e 's|$PTR_L2|{{[[:space:]]+\\},[[:space:]]+2[[:space:]]+\\]}}|g' \
+// DEFINE: -e 's|$PTR_L3|{{[[:space:]]+\\},[[:space:]]+3[[:space:]]+\\]}}|g' \
+// DEFINE: -e 's|$PTR_L4|{{[[:space:]]+\\},[[:space:]]+4[[:space:]]+\\]}}|g' \
+// DEFINE: -e 's|$PTR_L5|{{[[:space:]]+\\},[[:space:]]+5[[:space:]]+\\]}}|g' \
+// DEFINE: %s \
+// DEFINE: | FileCheck - --check-prefix=%{prefix} --input-file=%t/%{name}.wpa.json
+
+// REDEFINE: %{name} = linear_chain
+// REDEFINE: %{prefix} = CHAIN
+// RUN: %{check}
+//--- linear_chain.cpp
+// (a,1)->(b,2), (b,1)->(c,2), (c,1)->(d,2); start (a,2).
+// Result: {(a,2),(b,3),(c,4),(d,5)}.
+void f(char **a, char ***b, char ****c, char *****d, int i) {
+ a = *b;
+ b = *c;
+ c = *d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// CHAIN-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// CHAIN-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// CHAIN-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// CHAIN-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// CHAIN: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// CHAIN: "@": [[A]]$PTR_L2
+// CHAIN: "@": [[B]]$PTR_L3
+// CHAIN: "@": [[C]]$PTR_L4
+// CHAIN: "@": [[D]]$PTR_L5
+// CHAIN-NOT: "@":
+// CHAIN: "analysis_name":
+
+// REDEFINE: %{name} = linear_chain_disconnected
+// REDEFINE: %{prefix} = CHAINDISC
+// RUN: %{check}
+//--- linear_chain_disconnected.cpp
+// (a,1)->(b,2), (b,4)->(c,1), (c,1)->(d,1); start (a,2).
+// Result: {(a,2),(b,3)}.
+void f(char ***a, char ****b, char *c, char *d, int i) {
+ a = *b;
+ ***b = c;
+ c = d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// CHAINDISC-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// CHAINDISC-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// CHAINDISC: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// CHAINDISC: "@": [[A]]$PTR_L2
+// CHAINDISC: "@": [[B]]$PTR_L3
+// CHAINDISC-NOT: "@":
+// CHAINDISC: "analysis_name":
+
+// REDEFINE: %{name} = diamond
+// REDEFINE: %{prefix} = DIAMOND
+// RUN: %{check}
+//--- diamond.cpp
+// (a,1)->(b,2), (a,1)->(c,2), (b,1)->(d,2), (c,1)->(d,2); start (a,2).
+// Result: {(a,2),(b,3),(c,3),(d,4)}.
+void f(char **a, char ***b, char ***c, char ****d, int i) {
+ a = *b;
+ a = *c;
+ b = *d;
+ c = *d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// DIAMOND-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// DIAMOND-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// DIAMOND-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// DIAMOND-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// DIAMOND: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// DIAMOND: "@": [[A]]$PTR_L2
+// DIAMOND: "@": [[B]]$PTR_L3
+// DIAMOND: "@": [[C]]$PTR_L3
+// DIAMOND: "@": [[D]]$PTR_L4
+// DIAMOND-NOT: "@":
+// DIAMOND: "analysis_name":
+
+// REDEFINE: %{name} = disconnected_diamond
+// REDEFINE: %{prefix} = DISCONN
+// RUN: %{check}
+//--- disconnected_diamond.cpp
+// (a,1)->(b,2), (a,1)->(c,2), (b,5)->(d,1), (c,5)->(d,1); start (a,2).
+// Result: {(a,2),(b,3),(c,3)}.
+void f(char ****a, char *****b, char *****c, char *d, int i) {
+ a = *b;
+ a = *c;
+ ****b = d;
+ ****c = d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// DISCONN-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// DISCONN-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// DISCONN-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// DISCONN: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// DISCONN: "@": [[A]]$PTR_L2
+// DISCONN: "@": [[B]]$PTR_L3
+// DISCONN: "@": [[C]]$PTR_L3
+// DISCONN-NOT: "@":
+// DISCONN: "analysis_name":
+
+// REDEFINE: %{name} = disconnected_subgraphs
+// REDEFINE: %{prefix} = DISCONNSUB
+// RUN: %{check}
+//--- disconnected_subgraphs.cpp
+// (a,1)->(b,1), (c,1)->(d,1); start from tail (b,1).
+// Result: {(b,1)}.
+void f(char *a, char *b, char *c, char *d, int i) {
+ a = b;
+ c = d;
+ b[i] = 0; // starter: (b,1)
+}
+// DISCONNSUB-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// DISCONNSUB: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// DISCONNSUB: "@": [[B]]$PTR_L1
+// DISCONNSUB-NOT: "@":
+// DISCONNSUB: "analysis_name":
+
+// REDEFINE: %{name} = cycle
+// REDEFINE: %{prefix} = CYCLE
+// RUN: %{check}
+//--- cycle.cpp
+// (a,1)->(b,1)->(c,1)->(d,1)->(a,1); start (c,2).
+// Result: {(a,2),(b,2),(c,2),(d,2)}.
+void f(char **a, char **b, char **c, char **d, int i) {
+ a = b;
+ b = c;
+ c = d;
+ d = a;
+ (*c)[i] = 0; // starter: (c,2)
+}
+// CYCLE-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// CYCLE-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// CYCLE-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// CYCLE-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// CYCLE: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// CYCLE: "@": [[A]]$PTR_L2
+// CYCLE: "@": [[B]]$PTR_L2
+// CYCLE: "@": [[C]]$PTR_L2
+// CYCLE: "@": [[D]]$PTR_L2
+// CYCLE-NOT: "@":
+// CYCLE: "analysis_name":
+
+// REDEFINE: %{name} = cycle_increasing
+// REDEFINE: %{prefix} = CYCLEINC
+// RUN: %{check}
+//--- cycle_increasing.cpp
+// (a,1)->(b,2)->(c,3)->(d,4)->(a,1); start (a,2).
+// Result: {(a,2),(b,3),(c,4),(d,5)}.
+void f(char **a, char ***b, char ****c, char *****d, int i) {
+ a = *b;
+ *b = **c;
+ **c = ***d;
+ ***d = a;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// CYCLEINC-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// CYCLEINC-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// CYCLEINC-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// CYCLEINC-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// CYCLEINC: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// CYCLEINC: "@": [[A]]$PTR_L2
+// CYCLEINC: "@": [[B]]$PTR_L3
+// CYCLEINC: "@": [[C]]$PTR_L4
+// CYCLEINC: "@": [[D]]$PTR_L5
+// CYCLEINC-NOT: "@":
+// CYCLEINC: "analysis_name":
+
+// REDEFINE: %{name} = star_from_hub
+// REDEFINE: %{prefix} = HUB
+// RUN: %{check}
+//--- star_from_hub.cpp
+// (a,1)->(b,2), (a,1)->(c,2), (a,1)->(d,2); start (a,2).
+// Result: {(a,2),(b,3),(c,3),(d,3)}.
+void f(char **a, char ***b, char ***c, char ***d, int i) {
+ a = *b;
+ a = *c;
+ a = *d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// HUB-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// HUB-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// HUB-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// HUB-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// HUB: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// HUB: "@": [[A]]$PTR_L2
+// HUB: "@": [[B]]$PTR_L3
+// HUB: "@": [[C]]$PTR_L3
+// HUB: "@": [[D]]$PTR_L3
+// HUB-NOT: "@":
+// HUB: "analysis_name":
+
+// REDEFINE: %{name} = star_from_hub_below_edge
+// REDEFINE: %{prefix} = HUBBELOW
+// RUN: %{check}
+//--- star_from_hub_below_edge.cpp
+// (a,2)->(b,1), (a,2)->(c,1), (a,2)->(d,1); start (a,1).
+// Result: {(a,1)}.
+void f(char **a, char *b, char *c, char *d, int i) {
+ *a = b;
+ *a = c;
+ *a = d;
+ a[i] = 0; // starter: (a,1)
+}
+// HUBBELOW-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// HUBBELOW: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// HUBBELOW: "@": [[A]]$PTR_L1
+// HUBBELOW-NOT: "@":
+// HUBBELOW: "analysis_name":
+
+// REDEFINE: %{name} = reverse_star_from_source
+// REDEFINE: %{prefix} = REVSRC
+// RUN: %{check}
+//--- reverse_star_from_source.cpp
+// (a,1)->(d,2), (b,1)->(d,2), (c,1)->(d,2); start (a,2).
+// Result: {(a,2),(d,3)}.
+void f(char **a, char **b, char **c, char ***d, int i) {
+ a = *d;
+ b = *d;
+ c = *d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// REVSRC-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// REVSRC-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// REVSRC: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// REVSRC: "@": [[A]]$PTR_L2
+// REVSRC: "@": [[D]]$PTR_L3
+// REVSRC-NOT: "@":
+// REVSRC: "analysis_name":
+
+// REDEFINE: %{name} = reverse_star_from_sink
+// REDEFINE: %{prefix} = REVSINK
+// RUN: %{check}
+//--- reverse_star_from_sink.cpp
+// (a,1)->(d,1), (b,1)->(d,1), (c,1)->(d,1); start sink (d,2).
+// Result: {(d,2)}.
+void f(char **a, char **b, char **c, char **d, int i) {
+ a = d;
+ b = d;
+ c = d;
+ (*d)[i] = 0; // starter: (d,2)
+}
+// REVSINK-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// REVSINK: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// REVSINK: "@": [[D]]$PTR_L2
+// REVSINK-NOT: "@":
+// REVSINK: "analysis_name":
+
+// REDEFINE: %{name} = self_loop_from_root
+// REDEFINE: %{prefix} = LOOPROOT
+// RUN: %{check}
+//--- self_loop_from_root.cpp
+// (a,1)->(b,1), (b,1)->(b,1), (b,1)->(c,2), (c,1)->(d,2); start (a,2).
+// Result: {(a,2),(b,2),(c,3),(d,4)}.
+void f(char **a, char **b, char ***c, char ****d, int i) {
+ a = b;
+ b = b;
+ b = *c;
+ c = *d;
+ (*a)[i] = 0; // starter: (a,2)
+}
+// LOOPROOT-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// LOOPROOT-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// LOOPROOT-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// LOOPROOT-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// LOOPROOT: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// LOOPROOT: "@": [[A]]$PTR_L2
+// LOOPROOT: "@": [[B]]$PTR_L2
+// LOOPROOT: "@": [[C]]$PTR_L3
+// LOOPROOT: "@": [[D]]$PTR_L4
+// LOOPROOT-NOT: "@":
+// LOOPROOT: "analysis_name":
+
+// REDEFINE: %{name} = self_loop_from_loop_node
+// REDEFINE: %{prefix} = LOOPNODE
+// RUN: %{check}
+//--- self_loop_from_loop_node.cpp
+// Same graph as self_loop_from_root; start on the loop node (b,2).
+// Result: {(b,2),(c,3),(d,4)}.
+void f(char **a, char **b, char ***c, char ****d, int i) {
+ a = b;
+ b = b;
+ b = *c;
+ c = *d;
+ (*b)[i] = 0; // starter: (b,2)
+}
+// LOOPNODE-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// LOOPNODE-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// LOOPNODE-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// LOOPNODE: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// LOOPNODE: "@": [[B]]$PTR_L2
+// LOOPNODE: "@": [[C]]$PTR_L3
+// LOOPNODE: "@": [[D]]$PTR_L4
+// LOOPNODE-NOT: "@":
+// LOOPNODE: "analysis_name":
+
+// REDEFINE: %{name} = multiple_starters
+// REDEFINE: %{prefix} = MULTISTART
+// RUN: %{check}
+//--- multiple_starters.cpp
+// (a,1)->(b,2), (c,1)->(d,2); start {(a,2),(c,2)}.
+// Result: {(a,2),(b,3),(c,2),(d,3)}.
+void f(char **a, char ***b, char **c, char ***d, int i) {
+ a = *b;
+ c = *d;
+ (*a)[i] = 0; // starter: (a,2)
+ (*c)[i] = 0; // starter: (c,2)
+}
+// MULTISTART-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// MULTISTART-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// MULTISTART-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// MULTISTART-DAG: "id": [[D:[0-9]+]],$NS$WS"suffix": "4"
+// MULTISTART: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// MULTISTART: "@": [[A]]$PTR_L2
+// MULTISTART: "@": [[B]]$PTR_L3
+// MULTISTART: "@": [[C]]$PTR_L2
+// MULTISTART: "@": [[D]]$PTR_L3
+// MULTISTART-NOT: "@":
+// MULTISTART: "analysis_name":
+
+// REDEFINE: %{name} = multiple_keys_same_entity
+// REDEFINE: %{prefix} = MULTIKEY
+// RUN: %{check}
+//--- multiple_keys_same_entity.cpp
+// (a,1)->(b,1), (a,2)->(c,1); start (a,3).
+// Result: {(a,3),(b,3),(c,2)}.
+void f(char ***a, char ***b, char **c, int i) {
+ a = b;
+ *a = c;
+ (**a)[i] = 0; // starter: (a,3)
+}
+// MULTIKEY-DAG: "id": [[A:[0-9]+]],$NS$WS"suffix": "1"
+// MULTIKEY-DAG: "id": [[B:[0-9]+]],$NS$WS"suffix": "2"
+// MULTIKEY-DAG: "id": [[C:[0-9]+]],$NS$WS"suffix": "3"
+// MULTIKEY: "analysis_name": "UnsafeBufferReachableAnalysisResult"
+// MULTIKEY: "@": [[A]]$PTR_L3
+// MULTIKEY: "@": [[B]]$PTR_L3
+// MULTIKEY: "@": [[C]]$PTR_L2
+// MULTIKEY-NOT: "@":
+// MULTIKEY: "analysis_name":
diff --git a/clang/unittests/ScalableStaticAnalysis/WholeProgramAnalysis/UnsafeBufferReachableAnalysisTest.cpp b/clang/unittests/ScalableStaticAnalysis/WholeProgramAnalysis/UnsafeBufferReachableAnalysisTest.cpp
index cf5420b35e7dc..64486d1a22226 100644
--- a/clang/unittests/ScalableStaticAnalysis/WholeProgramAnalysis/UnsafeBufferReachableAnalysisTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysis/WholeProgramAnalysis/UnsafeBufferReachableAnalysisTest.cpp
@@ -205,31 +205,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, LinearChain) {
EXPECT_EQ(Reachables.size(), 4u);
}
-// Linear chain: (a,1) -> (b,2), (b,1) -> (c,2), (c,1) -> (d,2).
-// Start from {(a,2)} => {(a,2), (b,3), (c,4), (d,5)}
-TEST_F(UnsafeBufferReachableAnalysisTest, LinearChain2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 2}}, {{'b', 1}, {'c', 2}}, {{'c', 1}, {'d', 2}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables.size(), 4u);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 4}, {'d', 5}}));
-}
-
-// Linear chain: (a,1) -> (b,2), (b,4) -> (c,1) -> (d,1).
-// Start from {(a,2)} => {(a,2), (b,3)} (halted at (b,3) — no key (b,j<=3))
-TEST_F(UnsafeBufferReachableAnalysisTest, LinearChain3) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 2}}, {{'b', 4}, {'c', 1}}, {{'c', 1}, {'d', 1}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables.size(), 2u);
- EXPECT_EQ(Reachables, (std::set<Node>{{'a', 2}, {'b', 3}}));
-}
-
// Linear chain: (a,1) -> (b,1) -> (c,1) -> (d,1).
// Start from mid-chain {(c,1)} => {(c,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, LinearChainFromMiddle) {
@@ -257,35 +232,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, Diamond) {
EXPECT_EQ(Reachables.size(), 4u);
}
-// Diamond: (a,1) -> (b,2), (a,1) -> (c,2), (b,1) -> (d,2), (c,1) -> (d,2).
-// Start from {(a,2)} => {(a,2), (b,3), (c,3), (d,4)}
-TEST_F(UnsafeBufferReachableAnalysisTest, Diamond2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 2}},
- {{'a', 1}, {'c', 2}},
- {{'b', 1}, {'d', 2}},
- {{'c', 1}, {'d', 2}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 3}, {'d', 4}}));
-}
-
-// DisconnectedDiamond: (a,1) -> (b,2), (a,1) -> (c,2), (b,5) -> (d,1), (c,5) ->
-// (d,1). Start from {(a,2)} => {(a,2), (b,3), (c,3)}
-TEST_F(UnsafeBufferReachableAnalysisTest, DisconnectedDiamond) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 2}},
- {{'a', 1}, {'c', 2}},
- {{'b', 5}, {'d', 1}},
- {{'c', 5}, {'d', 1}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables, (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 3}}));
-}
-
// Diamond: (a,1) -> (b,1), (a,1) -> (c,1), (b,1) -> (d,1), (c,1) -> (d,1).
// Start from one branch {(b,1)} => {(b,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, DiamondFromBranch) {
@@ -314,17 +260,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, DisconnectedSubgraphs) {
EXPECT_TRUE(Reachables.count({'b', 1}));
}
-// Disconnected subgraphs: (a,1) -> (b,1), (c,1) -> (d,1).
-// Start from tail {(b,1)} => {(b,1)}
-TEST_F(UnsafeBufferReachableAnalysisTest, DisconnectedSubgraphs2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'c', 1}, {'d', 1}}},
- /* StarterLayout */ {{'b', 1}}, __LINE__);
- EXPECT_EQ(Reachables.size(), 1u);
- EXPECT_TRUE(Reachables.count({'b', 1}));
-}
-
// Cycle: (a,1) -> (b,1) -> (c,1) -> (d,1) -> (a,1).
// Start from {(c,1)} => {(a,1), (b,1), (c,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, Cycle) {
@@ -343,36 +278,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, Cycle) {
EXPECT_TRUE(Reachables.count({'d', 1}));
}
-// Cycle: (a,1) -> (b,1) -> (c,1) -> (d,1) -> (a,1).
-// Start from {(c,2)} => {(a,2), (b,2), (c,2), (d,2)}
-TEST_F(UnsafeBufferReachableAnalysisTest, Cycle2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 1}},
- {{'b', 1}, {'c', 1}},
- {{'c', 1}, {'d', 1}},
- {{'d', 1}, {'a', 1}}},
- /* StarterLayout */ {{'c', 2}}, __LINE__);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 2}, {'c', 2}, {'d', 2}}));
-}
-
-// Cycle: (a,1) -> (b,2) -> (c,3) -> (d,4) -> (a,1).
-// Start from {(a,2)} => {(a,2), (b,3), (c,4), (d,5)}
-TEST_F(UnsafeBufferReachableAnalysisTest, Cycle3) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 2}},
- {{'b', 2}, {'c', 3}},
- {{'c', 3}, {'d', 4}},
- {{'d', 4}, {'a', 1}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 4}, {'d', 5}}));
-}
-
// Empty graph: no edges, start from {(a,1)} => {(a,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, EmptyGraph) {
auto Reachables = singlePartition(
@@ -394,29 +299,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, StarFromHub) {
EXPECT_EQ(Reachables.size(), 4u);
}
-// Star: (a,1) -> (b,2), (a,1) -> (c,2), (a,1) -> (d,2).
-// Start from {(a,2)} => {(a,2), (b,3), (c,3), (d,3)}
-TEST_F(UnsafeBufferReachableAnalysisTest, StarFromHub2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 2}}, {{'a', 1}, {'c', 2}}, {{'a', 1}, {'d', 2}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 3}, {'d', 3}}));
-}
-
-// Star: (a,2) -> (b,1), (a,2) -> (c,1), (a,2) -> (d,1).
-// Start from {(a,1)} => {(a,1)}
-TEST_F(UnsafeBufferReachableAnalysisTest, StarFromHub3) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 2}, {'b', 1}}, {{'a', 2}, {'c', 1}}, {{'a', 2}, {'d', 1}}},
- /* StarterLayout */ {{'a', 1}}, __LINE__);
- EXPECT_EQ(Reachables, (std::set<Node>{{'a', 1}}));
-}
-
// Star: (a,1) -> (b,1), (a,1) -> (c,1), (a,1) -> (d,1).
// Start from leaf {(c,1)} => {(c,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, StarFromLeaf) {
@@ -442,17 +324,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSource) {
EXPECT_TRUE(Reachables.count({'d', 1}));
}
-// Reverse star: (a,1) -> (d,2), (b,1) -> (d,2), (c,1) -> (d,2).
-// Start from {(a,2)} => {(a,2), (d,3)}
-TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSource2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'d', 2}}, {{'b', 1}, {'d', 2}}, {{'c', 1}, {'d', 2}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables, (std::set<Node>{{'a', 2}, {'d', 3}}));
-}
-
// Reverse star: (a,1) -> (d,1), (b,1) -> (d,1), (c,1) -> (d,1).
// Start from sink {(d,1)} => {(d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSink) {
@@ -465,17 +336,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSink) {
EXPECT_TRUE(Reachables.count({'d', 1}));
}
-// Reverse star: (a,1) -> (d,1), (b,1) -> (d,1), (c,1) -> (d,1).
-// Start from sink {(d,2)} => {(d,2)}
-TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSink2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'d', 1}}, {{'b', 1}, {'d', 1}}, {{'c', 1}, {'d', 1}}},
- /* StarterLayout */ {{'d', 2}}, __LINE__);
- EXPECT_EQ(Reachables, (std::set<Node>{{'d', 2}}));
-}
-
// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,1) -> (d,1).
// Start from {(a,1)} => {(a,1), (b,1), (c,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromRoot) {
@@ -490,21 +350,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromRoot) {
EXPECT_EQ(Reachables.size(), 4u);
}
-// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,2) -> (d,2).
-// Start from {(a,2)} => {(a,2), (b,2), (c,3), (d,4)}
-TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromRoot2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 1}},
- {{'b', 1}, {'b', 1}},
- {{'b', 1}, {'c', 2}},
- {{'c', 1}, {'d', 2}}},
- /* StarterLayout */ {{'a', 2}}, __LINE__);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 2}, {'c', 3}, {'d', 4}}));
-}
-
// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,1) -> (d,1).
// Start from {(b,1)} => {(b,1), (c,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromLoopNode) {
@@ -522,20 +367,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromLoopNode) {
EXPECT_TRUE(Reachables.count({'d', 1}));
}
-// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,2) -> (d,2).
-// Start from {(b,2)} => {(b,2), (c,3), (d,4)}
-TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromLoopNode2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */
- {{{'a', 1}, {'b', 1}},
- {{'b', 1}, {'b', 1}},
- {{'b', 1}, {'c', 2}},
- {{'c', 1}, {'d', 2}}},
- /* StarterLayout */ {{'b', 2}}, __LINE__);
- EXPECT_EQ(Reachables, (std::set<Node>{{'b', 2}, {'c', 3}, {'d', 4}}));
-}
-
// Multiple starters: (a,1) -> (b,1), (c,1) -> (d,1) (disconnected).
// Start from {(a,1), (c,1)} => {(a,1), (b,1), (c,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersBothChains) {
@@ -546,17 +377,6 @@ TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersBothChains) {
EXPECT_EQ(Reachables.size(), 4u);
}
-// Multiple starters: (a,1) -> (b,2), (c,1) -> (d,2).
-// Start from {(a,2), (c,2)} => {(a,2), (b,3), (c,2), (d,3)}
-TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersBothChains2) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c', 'd'},
- /* EdgeLayout */ {{{'a', 1}, {'b', 2}}, {{'c', 1}, {'d', 2}}},
- /* StarterLayout */ {{'a', 2}, {'c', 2}}, __LINE__);
- EXPECT_EQ(Reachables,
- (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 2}, {'d', 3}}));
-}
-
// Multiple starters: (a,1) -> (b,1), (c,1) -> (d,1) (disconnected).
// Start from leaves {(b,1), (d,1)} => {(b,1), (d,1)}
TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersLeaves) {
@@ -569,14 +389,4 @@ TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersLeaves) {
EXPECT_TRUE(Reachables.count({'d', 1}));
}
-// Multi-key, same source entity: (a,1) -> (b,1), (a,2) -> (c,1).
-// Start from {(a,3)} => {(a,3), (b,3), (c,2)}
-TEST_F(UnsafeBufferReachableAnalysisTest, MultipleKeysSameEntity) {
- auto Reachables = singlePartition(
- /* EntityDomain */ {'a', 'b', 'c'},
- /* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'a', 2}, {'c', 1}}},
- /* StarterLayout */ {{'a', 3}}, __LINE__);
- EXPECT_EQ(Reachables, (std::set<Node>{{'a', 3}, {'b', 3}, {'c', 2}}));
-}
-
} // namespace
>From 540b37b9b7e4bb9b161eb9b800dd372884dd675c Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 27 Aug 2026 12:01:13 -0700
Subject: [PATCH 2/2] fix clang-format
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp | 3 ++-
.../PointerFlow/unsafe-buffer-reachable-cast-cycle.test | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
index 90a0e3633151a..4fc6d058de368 100644
--- a/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
+++ b/clang/lib/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.cpp
@@ -225,7 +225,8 @@ class UnsafeBufferReachableAnalysis
FilteredDstRange.end());
}
if (!FilteredSubGraph.empty())
- BPG.try_emplace(Id, BoundsPropagationGraph{std::move(FilteredSubGraph)});
+ BPG.try_emplace(Id,
+ BoundsPropagationGraph{std::move(FilteredSubGraph)});
}
// Filter out type-constrained pointers from `UnsafePtrs`:
diff --git a/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test b/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test
index 46a3092b801ea..f64c043f855a9 100644
--- a/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test
+++ b/clang/test/Analysis/Scalable/PointerFlow/unsafe-buffer-reachable-cast-cycle.test
@@ -42,7 +42,7 @@ void f(char **a, char ***b, int i, int j) {
// CHECK-DAG: "id": [[a_ID:[0-9]+]],$NS$WS"suffix": "1",$WS"usr": "c:@F at f#**C#*S0_#I#I#"
// CHECK-DAG: "id": [[b_ID:[0-9]+]],$NS$WS"suffix": "2",$WS"usr": "c:@F at f#**C#*S0_#I#I#"
-// The reachable set is exactly {(a, 1), (a, 2), (b, 2), (b, 3)}
+// The reachable set is exactly {(a, 1), (a, 2), (b, 2), (b, 3)}
// CHECK: "analysis_name": "UnsafeBufferReachableAnalysisResult"
// CHECK: "@": [[a_ID]]$PTR_L1
// CHECK: "@": [[a_ID]]$PTR_L2
More information about the llvm-branch-commits
mailing list