[clang] [analyzer] Skip LifetimeEnd nodes in getNextStmtForDiagnostics (PR #228045)

Benedek Kaibas via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 1 04:32:22 PDT 2026


https://github.com/benedekaibas created https://github.com/llvm/llvm-project/pull/228045

None

>From b65cfba070d96275efb405a26fa46596f2063b90 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Thu, 1 Oct 2026 13:29:01 +0200
Subject: [PATCH] [analyzer] Skip LifetimeEnd nodes in
 getNextStmtForDiagnostics

---
 .../lib/StaticAnalyzer/Core/ExplodedGraph.cpp |  3 ++
 .../test/Analysis/lifetime-end-path-notes.cpp | 51 +++++++++++++++++++
 2 files changed, 54 insertions(+)
 create mode 100644 clang/test/Analysis/lifetime-end-path-notes.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index a73593baaa184..11f65a295a5af 100644
--- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -347,6 +347,9 @@ const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
   for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
     if (N->getLocation().isPurgeKind())
       continue;
+    if (ProgramPoint P = N->getLocation(); P.getAs<LifetimeEnd>())
+      // P.dump();
+      continue;
     if (const Stmt *S = N->getStmtForDiagnostics()) {
       // Check if the statement is '?' or '&&'/'||'.  These are "merges",
       // not actual statement points.
diff --git a/clang/test/Analysis/lifetime-end-path-notes.cpp b/clang/test/Analysis/lifetime-end-path-notes.cpp
new file mode 100644
index 0000000000000..9fba6b7f84530
--- /dev/null
+++ b/clang/test/Analysis/lifetime-end-path-notes.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection,alpha.core.DanglingPtrDeref -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Enabling CFG lifetime-end elements should not cause regression in the
+// path notes. The loop exit path note must point to the next executed
+// statmeent and not back to the loop.
+void testPathNotesWithLoopScopeEnd() {
+  int *p = nullptr;
+  for (int i = 0; i < 3; ++i) {
+  // expected-note at -1 3 {{Loop condition is true.  Entering loop body}}
+  // expected-note at -2   {{The value 2 is assigned to 'i'}}
+  // expected-note at -3   {{Loop condition is false. Execution continues on line 17}}
+    p = &i; // expected-note {{Value assigned to 'p'}}
+  }
+  // expected-note at -1 {{'i' is destroyed here}}
+  *p = 4;
+  // expected-warning at -1 {{Use of 'i' after its lifetime ended}}
+  // expected-note at -2    {{Use of 'i' after its lifetime ended}}
+}
+
+void testPathNotesWithBlockScopeEnd() {
+  int *p = nullptr;
+  {
+    int n = 0;
+    while (n < 3) {
+    // expected-note at -1 3 {{Loop condition is true.  Entering loop body}}
+    // expected-note at -2   {{Loop condition is false. Execution continues on line 34}}
+      p = &n; // expected-note {{Value assigned to 'p'}}
+      ++n;    // expected-note {{The value 2 is assigned to 'n'}}
+    }
+  }
+  // expected-note at -1 {{'n' is destroyed here}}
+  *p = 4;
+  // expected-warning at -1 {{Use of 'n' after its lifetime ended}}
+  // expected-note at -2    {{Use of 'n' after its lifetime ended}}
+}
+
+void testPathNotesWithWarnIfReached() {
+  {
+    int i = 0;
+    while (i < 3) {
+    // expected-note at -1 3 {{Loop condition is true.  Entering loop body}}
+    // expected-note at -2   {{Loop condition is false. Execution continues on line 48}} 
+      ++i;
+    }
+  }
+  clang_analyzer_warnIfReached();
+  // expected-warning at -1 {{REACHABLE}}
+  // expected-note at -2    {{REACHABLE}}
+}



More information about the cfe-commits mailing list