[clang] [analyzer] Improve UseAfterLifetimeEnd checker's diagnostic with descriptive names and value tracking (PR #212158)

Benedek Kaibas via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 28 08:20:14 PDT 2026


https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/212158

>From 1025b0c91d2bf411a5fa27357ee61cd2e6c7b31e Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Fri, 24 Jul 2026 16:53:31 +0200
Subject: [PATCH 1/6] [analyzer] Improve dangling value tracking in
 DanglingPtrDeref

---
 .../Checkers/DanglingPtrDeref.cpp              | 11 ++++++-----
 clang/test/Analysis/dangling-ptr-deref.cpp     | 18 +++++++++---------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
index 9b4b1056c8ee6..51a1cb891f6cc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
@@ -15,8 +15,8 @@ class DanglingPtrDeref : public Checker<check::Location, check::PostCall> {
   void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
                      CheckerContext &C) const;
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-  void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N,
-                           CheckerContext &C) const;
+  void reportUseAfterScope(const MemRegion *Region, const Stmt *S,
+                           ExplodedNode *N, CheckerContext &C) const;
   const BugType BugMsg{this, "ReportDanglingPtrDeref", "LifetimeBound"};
 };
 
@@ -45,7 +45,7 @@ void DanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
   if (const MemRegion *LocRegion = Loc.getAsRegion()) {
     if (lifetime_modeling::isDeallocated(State, LocRegion)) {
       if (ExplodedNode *N = C.generateNonFatalErrorNode(State))
-        reportUseAfterScope(LocRegion, N, C);
+        reportUseAfterScope(LocRegion, S, N, C);
     }
   }
 }
@@ -62,7 +62,7 @@ void DanglingPtrDeref::checkPostCall(const CallEvent &Call,
     if (const MemRegion *ArgRegion = Call.getArgSVal(Idx).getAsRegion())
       if (lifetime_modeling::isDeallocated(State, ArgRegion))
         if (ExplodedNode *N = C.generateNonFatalErrorNode())
-          reportUseAfterScope(ArgRegion, N, C);
+          reportUseAfterScope(ArgRegion, Call.getArgExpr(Idx), N, C);
   }
 }
 
@@ -75,7 +75,7 @@ static std::string getRegionName(const MemRegion *Reg) {
 }
 
 void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region,
-                                           ExplodedNode *N,
+                                           const Stmt *S, ExplodedNode *N,
                                            CheckerContext &C) const {
   auto BR = std::make_unique<PathSensitiveBugReport>(
       BugMsg,
@@ -83,6 +83,7 @@ void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region,
        " after its lifetime ended."),
       N);
   BR->addVisitor<DanglingPtrDerefBRVisitor>(Region);
+  bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *BR);
   C.emitReport(std::move(BR));
 }
 
diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp
index fa9dae41421bc..652accd1f359a 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -4,8 +4,8 @@
 void test_case_one() {
   int *ptr = nullptr;
   {
-    int num = 5;
-    ptr = #
+    int num = 5; // expected-note {{'num' initialized to 5}}
+    ptr = # // expected-note  {{Value assigned to 'ptr'}}
   }
   // expected-note at -1 {{'num' is destroyed here}}
   *ptr = 6;
@@ -17,10 +17,10 @@ void test_case_two() {
   int *ptr_one = nullptr;
   int *ptr_two = nullptr;
   {
-    int n = 1;
-    int m = 2;
-    ptr_one = &n;
-    ptr_two = &m;
+    int n = 1; // expected-note {{'n' initialized to 1}}
+    int m = 2; // expected-note {{'m' initialized to 2}}
+    ptr_one = &n; // expected-note {{Value assigned to 'ptr_one'}}
+    ptr_two = &m; // expected-note {{Value assigned to 'ptr_two'}}
   }
   // expected-note at -1 {{'n' is destroyed here}}
   // expected-note at -2 {{'m' is destroyed here}}
@@ -45,7 +45,7 @@ void test_case_three() {
 void test_case_four() {
   int *ptr = nullptr;
   {
-    int num = 5;
+    int num = 5; // expected-note {{'num' initialized to 5}}
     ptr = #
   }
   // expected-note at -1 {{'num' is destroyed here}}
@@ -75,8 +75,8 @@ void test_case_seven() {
   // expected-note at +3 {{Loop condition is true.  Entering loop body}}
   // expected-note at +2 {{Assuming 'i' is >= 10}}
   // expected-note at +1 {{Loop condition is false. Execution continues on line}}
-  for (int i = 0; i < 10; ++i) {
-    ptr = &i;
+  for (int i = 0; i < 10; ++i) { // expected-note {{'i' initialized to 0}}
+    ptr = &i; // expected-note {{Value assigned to 'ptr'}}
     escape(ptr);
   }
   // expected-note at -1 {{'i' is destroyed here}}

>From 415befa8f27112f0152f0adf4340d1cd6fd1a321 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Sun, 26 Jul 2026 18:36:34 +0200
Subject: [PATCH 2/6] Add notes to the test suite.

---
 clang/test/Analysis/dangling-ptr-deref.cpp | 28 +++++++++++-----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp
index 652accd1f359a..d7cdae75a1d95 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -88,8 +88,8 @@ void test_case_seven() {
 void passing_dangling_ptr_to_opaque_func() {
   int *ptr = nullptr;
   {
-    int num = 5;
-    ptr = #
+    int num = 5; // expected-note {{'num' initialized to 5}}
+    ptr = # // expected-note  {{Value assigned to 'ptr'}}
   }
   // expected-note at -1 {{'num' is destroyed here}}
   escape(ptr);
@@ -104,7 +104,7 @@ int deref_param(int *p) { return *p; }
 void inlined_callee_single_report() {
   int *ptr = nullptr;
   {
-    int num = 5;
+    int num = 5; // expected-note {{'num' initialized to 5}}
     ptr = #
   }
   // expected-note at -1 {{'num' is destroyed here}}
@@ -131,7 +131,7 @@ struct A {
 char member_subregion_dangling_deref() {
   const char *p = nullptr;
   {
-    MyBuffer tmp_buffer = {};
+    MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
     p = tmp_buffer.buffer;
   }
   // expected-note at -1 {{'tmp_buffer.buffer[0]' is destroyed here}}
@@ -147,7 +147,7 @@ void passing_dangling_to_call() {
   const char *p = nullptr;
   {
     MyBuffer tmp_buffer = {};
-    p = tmp_buffer.buffer;
+    p = tmp_buffer.buffer; // expected-note {{Value assigned to 'p'}}
   }
   // expected-note at -1 {{'tmp_buffer.buffer[0]' is destroyed here}}
   opaque(p);
@@ -178,8 +178,8 @@ char member_subregion_alive_deref_pp() {
 void arr_elem_subreg_dangling_deref() {
   int *ptr = nullptr;
   {
-    int local_arr[4];
-    ptr = &local_arr[1];
+    int local_arr[4]; // expected-note    {{'local_arr' declared without an initial value}}
+    ptr = &local_arr[1]; // expected-note {{Value assigned to 'ptr'}}
   }
   // expected-note at -1 {{'local_arr[1]' is destroyed here}}
   *ptr = 7;
@@ -190,7 +190,7 @@ void arr_elem_subreg_dangling_deref() {
 char member_array_elem_dangling_deref() {
   const char *p = nullptr;
   {
-    MyBuffer tmp_buffer = {};
+    MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
     p = tmp_buffer.buffer + 3;
   }
   // expected-note at -1 {{'tmp_buffer.buffer[3]' is destroyed here}}
@@ -202,7 +202,7 @@ char member_array_elem_dangling_deref() {
 char member_array_out_of_bounds_dangling_deref() {
   const char *p = nullptr;
   {
-    MyBuffer tmp_buffer = {};
+    MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
     p = tmp_buffer.buffer + 10;
   }
   // expected-note at -1 {{'tmp_buffer.buffer[10]' is destroyed here}}
@@ -214,7 +214,7 @@ char member_array_out_of_bounds_dangling_deref() {
 int struct_field_dangling_deref() {
   int *p = nullptr;
   {
-    MyStruct s = {};
+    MyStruct s = {}; // expected-note {{'s.x' initialized to 0}}
     p = &s.x;
   }
   // expected-note at -1 {{'s.x' is destroyed here}}
@@ -226,7 +226,7 @@ int struct_field_dangling_deref() {
 int struct_array_element_dangling_deref() {
   int *p = nullptr;
   {
-    MyStruct arr[4] = {};
+    MyStruct arr[4] = {}; // expected-note {{field 'x' initialized to 0}}
     p = &arr[2].x;
   }
   // expected-note at -1 {{'arr[2].x' is destroyed here}}
@@ -238,7 +238,7 @@ int struct_array_element_dangling_deref() {
 int nested_field_dangling_deref() {
   int *p = nullptr;
   {
-    Outer o = {};
+    Outer o = {}; // expected-note {{'o.inner.x' initialized to 0}}
     p = &o.inner.x;
   }
   // expected-note at -1 {{'o.inner.x' is destroyed here}}
@@ -250,7 +250,7 @@ int nested_field_dangling_deref() {
 int nested_type_field_dangling_deref() {
   int *p = nullptr;
   {
-    A a = {};
+    A a = {}; // expected-note {{'a.b.x' initialized to 0}}
     p = &a.b.x;
   }
   // expected-note at -1 {{'a.b.x' is destroyed here}}
@@ -262,7 +262,7 @@ int nested_type_field_dangling_deref() {
 char member_subregion_dangling_deref_increment() {
   const char *p = nullptr;
   {
-    MyBuffer tmp_buffer = {};
+    MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
     p = tmp_buffer.buffer;
   }
   // expected-note at -1 {{'tmp_buffer.buffer[1]' is destroyed here}}

>From c6b5fb0873b33217fcf485be0f4288a7e26ca84c Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Sun, 26 Jul 2026 18:53:46 +0200
Subject: [PATCH 3/6] Remove struct keyword.

---
 clang/test/Analysis/dangling-ptr-deref.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp
index d7cdae75a1d95..a4d919c168e3b 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -120,7 +120,7 @@ struct MyStruct { int x; };
 
 struct Inner { int x; };
 
-struct Outer { struct Inner inner; };
+struct Outer { Inner inner; };
 
 struct A {
   struct B { int x; };

>From ba419426dfb0cbfeacc2f239a4314a461cd1c5cb Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Sun, 26 Jul 2026 23:46:45 +0200
Subject: [PATCH 4/6] Move getRegionName into the modeling checker.

---
 .../StaticAnalyzer/Checkers/DanglingPtrDeref.cpp   | 14 ++++----------
 .../StaticAnalyzer/Checkers/LifetimeModeling.cpp   |  8 ++++++++
 .../lib/StaticAnalyzer/Checkers/LifetimeModeling.h |  4 ++++
 3 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
index 51a1cb891f6cc..3daad5f131ae6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
@@ -66,20 +66,12 @@ void DanglingPtrDeref::checkPostCall(const CallEvent &Call,
   }
 }
 
-static std::string getRegionName(const MemRegion *Reg) {
-  // FIXME: Once the checker supports heap allocation, more region kinds
-  // should be handled to produce the correct descriptive name.
-  if (const std::string &RegName = Reg->getDescriptiveName(); !RegName.empty())
-    return RegName;
-  llvm_unreachable("unhandled region");
-}
-
 void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region,
                                            const Stmt *S, ExplodedNode *N,
                                            CheckerContext &C) const {
   auto BR = std::make_unique<PathSensitiveBugReport>(
       BugMsg,
-      (llvm::Twine("Use of ") + getRegionName(Region) +
+      (llvm::Twine("Use of ") + lifetime_modeling::getRegionName(Region) +
        " after its lifetime ended."),
       N);
   BR->addVisitor<DanglingPtrDerefBRVisitor>(Region);
@@ -108,7 +100,9 @@ DanglingPtrDerefBRVisitor::VisitNode(const ExplodedNode *N,
       S, BRC.getSourceManager(), N->getStackFrame());
   return std::make_shared<PathDiagnosticEventPiece>(
       Pos,
-      (getRegionName(SourceRegion) + llvm::Twine(" is destroyed here")).str(),
+      (lifetime_modeling::getRegionName(SourceRegion) +
+       llvm::Twine(" is destroyed here"))
+          .str(),
       true);
 }
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 765a95c008053..df8a554e43157 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -87,6 +87,14 @@ static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal,
   return State;
 }
 
+std::string lifetime_modeling::getRegionName(const MemRegion *Reg) {
+  // FIXME: Once the checker supports heap allocation, more region kinds
+  // should be handled to produce the correct descriptive name.
+  if (const std::string &RegName = Reg->getDescriptiveName(); !RegName.empty())
+    return RegName;
+  llvm_unreachable("unhandled region");
+}
+
 void LifetimeModeling::checkPostCall(const CallEvent &Call,
                                      CheckerContext &C) const {
   ProgramStateRef State = C.getState();
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index cea2799db4b1d..1e2ef8f7810ac 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -15,6 +15,10 @@ getDanglingRegionsAfterReturn(SVal Source, ProgramStateRef State,
 
 /// Returns true if the underlying MemRegion is deallocated.
 bool isDeallocated(ProgramStateRef State, const MemRegion *Region);
+
+/// Returns the descriptive name of the memory region or a placeholder if a
+/// descriptive name cannot be constructed for it.
+std::string getRegionName(const MemRegion *Reg);
 } // namespace clang::ento::lifetime_modeling
 
 #endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H

>From ee2d0ffdcc3b787accfd6cc058eb8cf7d58e61fe Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Sun, 26 Jul 2026 23:58:48 +0200
Subject: [PATCH 5/6] Update dangling-ptr-deref for tracking notes.

---
 clang/test/Analysis/dangling-ptr-deref.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp
index a4d919c168e3b..f210195762770 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -271,3 +271,4 @@ char member_subregion_dangling_deref_increment() {
   // expected-warning at -1 {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}}
   // expected-note at -2    {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}}
 }
+

>From fc6cf144525b0ae5be9d15b739c89710153f6c96 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Mon, 27 Jul 2026 01:47:57 +0200
Subject: [PATCH 6/6] [analyzer] Improve UseAfterLifetimeEnd checker's
 diagnostic with descriptive names and value tracking

---
 .../Checkers/UseAfterLifetimeEnd.cpp          | 12 ++-
 clang/test/Analysis/lifetime-bound.cpp        | 94 +++++++++++++------
 2 files changed, 70 insertions(+), 36 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
index 6a2933900c01f..0f320eb910930 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
@@ -1,5 +1,6 @@
 #include "LifetimeModeling.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 
 using namespace clang;
@@ -8,7 +9,7 @@ using namespace ento;
 namespace {
 class UseAfterLifetimeEnd : public Checker<check::EndFunction> {
 public:
-  void reportDanglingSource(const MemRegion *Source, ExplodedNode *N,
+  void reportDanglingSource(const MemRegion *Source, SVal Val, ExplodedNode *N,
                             CheckerContext &C) const;
   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
   const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
@@ -38,18 +39,19 @@ void UseAfterLifetimeEnd::checkEndFunction(const ReturnStmt *RS,
   if (ExplodedNode *N =
           C.generateNonFatalErrorNode(State, C.getPredecessor())) {
     for (const MemRegion *R : RetValRegion)
-      reportDanglingSource(R, N, C);
+      reportDanglingSource(R, RetVal, N, C);
   }
 }
 
 void UseAfterLifetimeEnd::reportDanglingSource(const MemRegion *Source,
-                                               ExplodedNode *N,
+                                               SVal RetVal, ExplodedNode *N,
                                                CheckerContext &C) const {
   auto BR = std::make_unique<PathSensitiveBugReport>(
       BugMsg,
-      (llvm::Twine("Returning value bound to '") + Source->getString() +
-       "' that will go out of scope"),
+      (llvm::Twine("Returning value bound to ") +
+       lifetime_modeling::getRegionName(Source) + " that will go out of scope"),
       N);
+  bugreporter::trackStoredValue(RetVal, Source, *BR);
   C.emitReport(std::move(BR));
 }
 
diff --git a/clang/test/Analysis/lifetime-bound.cpp b/clang/test/Analysis/lifetime-bound.cpp
index 4920a3c928fb8..f9c3ddf524ac0 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -1,8 +1,7 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugLifetimeModeling \
-// RUN:   -analyzer-config cfg-lifetime=true -verify %s
+// RUN:   -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugLifetimeModeling \
-// RUN:   -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -verify %s
-
+// RUN:   -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s
 struct A {};
 
 void clang_analyzer_dumpLifetimeOriginsOf(int*);
@@ -21,7 +20,9 @@ void caller() {
   int v = 0;
   X obj;
   int &r = obj.choose(v);
-  clang_analyzer_dumpLifetimeOriginsOf(r); // expected-warning {{Origin &v bound to v}}
+  clang_analyzer_dumpLifetimeOriginsOf(r);
+  // expected-warning at -1 {{Origin &v bound to v}}
+  // expected-note at -2    {{Origin &v bound to v}}
 }
 
 // Obj ref type function return annotated case.
@@ -34,7 +35,9 @@ void caller_two() {
   // Return statement is annotated case.
   Y y;
   A &f = y.getA();
-  clang_analyzer_dumpLifetimeOriginsOf(f); // expected-warning {{Origin &y.a bound to y}}
+  clang_analyzer_dumpLifetimeOriginsOf(f);
+  // expected-warning at -1 {{Origin &y.a bound to y}}
+  // expected-note at -2    {{Origin &y.a bound to y}}
 }
 
 // Obj ptr type function return annotated case.
@@ -46,7 +49,9 @@ struct Z {
 void caller_three() {
   Z z;
   A *func = z.getA();
-  clang_analyzer_dumpLifetimeOriginsOf(func); // expected-warning {{Origin &z.a bound to z}}
+  clang_analyzer_dumpLifetimeOriginsOf(func);
+  // expected-warning at -1 {{Origin &z.a bound to z}}
+  // expected-note at -2    {{Origin &z.a bound to z}}
 }
 
 // Free function with annotated param and ref return.
@@ -55,7 +60,9 @@ int &foo(int &num [[clang::lifetimebound]]) { return num; }
 void caller_four() {
   int num = 5;
   int &s = foo(num);
-  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &num bound to num}}
+  clang_analyzer_dumpLifetimeOriginsOf(s);
+  // expected-warning at -1 {{Origin &num bound to num}}
+  // expected-note at -2    {{Origin &num bound to num}}
 }
 
 // Free function with annotated param and ptr return.
@@ -66,7 +73,9 @@ void caller_five() {
   int *n_ptr = &n;
   int *s = boo(n_ptr);
 
-  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &n bound to n}}
+  clang_analyzer_dumpLifetimeOriginsOf(s);
+  // expected-warning at -1 {{Origin &n bound to n}}
+  // expected-note at -2  {{Origin &n bound to n}}
 }
 
 // Free function with both annotated and non-annotated parameters.
@@ -77,12 +86,12 @@ void caller_six() {
   int odd = 55;
   int &s = fn(even, odd);
 
-  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &odd bound to odd}}
+  clang_analyzer_dumpLifetimeOriginsOf(s);
+  // expected-warning at -1 {{Origin &odd bound to odd}}
+  // expected-note at -2    {{Origin &odd bound to odd}}
 }
 
-
-
-// These are the cases when the result of function calls are SymbolRefs.
+// Test cases for testing when the result of function calls are SymbolRefs.
 
 // Function returns ptr and has an annotated parameter.
 int *foo(int *n [[clang::lifetimebound]]);
@@ -92,7 +101,9 @@ void caller_seven() {
   int *y_ptr = &y;
   auto *bind = foo(y_ptr);
 
-  clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin &SymRegion{{.*}} bound to y}}
+  clang_analyzer_dumpLifetimeOriginsOf(bind);
+  // expected-warning-re at -1 {{Origin &SymRegion{{.*}} bound to y}}
+  // expected-note-re at -2    {{Origin &SymRegion{{.*}} bound to y}} 
 }
 
 // Function returns a reference and has an annotated parameter.
@@ -102,7 +113,9 @@ void caller_eight() {
   int f = 15;
   auto &bind = func(f);
 
-  clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin &SymRegion{{.*}} bound to f}}
+  clang_analyzer_dumpLifetimeOriginsOf(bind);
+  // expected-warning-re at -1 {{Origin &SymRegion{{.*}} bound to f}}
+  // expected-note-re at -2    {{Origin &SymRegion{{.*}} bound to f}}
 }
 
 // Function returns a reference and has two annotated parameters.
@@ -113,7 +126,9 @@ void caller_nine() {
   int second_num = 2;
   int &numbers = f(first_num, second_num);
 
-  clang_analyzer_dumpLifetimeOriginsOf(numbers); // expected-warning-re {{Origin &SymRegion{{.*}} bound to first_num, second_num}}
+  clang_analyzer_dumpLifetimeOriginsOf(numbers);
+  // expected-warning-re at -1 {{Origin &SymRegion{{.*}} bound to first_num, second_num}}
+  // expected-note-re at -2    {{Origin &SymRegion{{.*}} bound to first_num, second_num}}
 }
 
 struct View {
@@ -139,16 +154,19 @@ int *test_func(int *p [[clang::lifetimebound]]);
 
 
 int *direct_return() {
-  int i = 5;
+  int i = 5; //expected-note {{'i' initialized here}}
   return test_func(&i);
   // expected-warning at -1 {{Returning value bound to 'i' that will go out of scope}}
   // expected-warning at -2 {{address of stack memory associated with local variable 'i' returned}}
+  // expected-note at -3    {{Returning value bound to 'i' that will go out of scope}}
 }
 
 int *variable_return() {
-  int y = 5;
+  int y = 5; // expected-note {{'y' initialized here}}
   int *p = test_func(&y);
-  return p; // expected-warning {{Returning value bound to 'y' that will go out of scope}}
+  return p;
+  // expected-warning at -1 {{Returning value bound to 'y' that will go out of scope}}
+  // expected-note at -2    {{Returning value bound to 'y' that will go out of scope}}
 }
 
 int *borrow_from_caller(int *b [[clang::lifetimebound]]) {
@@ -173,11 +191,15 @@ int &multi_param_test_ref(int &a [[clang::lifetimebound]], int &b [[clang::lifet
 // Return value bound to annotated parameters (two dangling sources).
 int &dangling_sources_ref() {
   int x = 1, y = 2;
+  // expected-note at -1 {{'x' initialized here}}
+  // expected-note at -2 {{'y' initialized here}}
   return multi_param_test_ref(x, y);
   // expected-warning at -1 {{Returning value bound to 'x' that will go out of scope}}
-  // expected-warning at -2 {{Returning value bound to 'y' that will go out of scope}}
-  // expected-warning at -3 {{reference to stack memory associated with local variable 'x' returned}}
-  // expected-warning at -4 {{reference to stack memory associated with local variable 'y' returned}}
+  // expected-note at -2    {{Returning value bound to 'x' that will go out of scope}}
+  // expected-warning at -3 {{Returning value bound to 'y' that will go out of scope}}
+  // expected-note at -4    {{Returning value bound to 'y' that will go out of scope}}
+  // expected-warning at -5 {{reference to stack memory associated with local variable 'x' returned}}
+  // expected-warning at -6 {{reference to stack memory associated with local variable 'y' returned}}
 }
 
 // Return value bound to annotated parameters (no dangling sources).
@@ -187,10 +209,11 @@ int &no_dangling_sources_ref(int &a [[clang::lifetimebound]], int &b [[clang::li
 
 // Return value bound to annotated parameters (one dangling source).
 int &one_dangling_source_ref(int &a [[clang::lifetimebound]]) {
-  int x = 1;
+  int x = 1; // expected-note {{'x' initialized here}}
   return multi_param_test_ref(a, x);
   // expected-warning at -1 {{Returning value bound to 'x' that will go out of scope}}
-  // expected-warning at -2 {{reference to stack memory associated with local variable 'x' returned}}
+  // expected-note at -2    {{Returning value bound to 'x' that will go out of scope}}
+  // expected-warning at -3 {{reference to stack memory associated with local variable 'x' returned}}
 }
 
 int *multi_param_test_ptr(int *a [[clang::lifetimebound]], int *b [[clang::lifetimebound]]);
@@ -198,11 +221,15 @@ int *multi_param_test_ptr(int *a [[clang::lifetimebound]], int *b [[clang::lifet
 // Return value bound to annotated parameters (two dangling sources).
 int *dangling_sources_ptr() {
   int x = 1, y = 2;
+  // expected-note at -1 {{'x' initialized here}}
+  // expected-note at -2 {{'y' initialized here}}
   int *x_ptr = &x;
   int *y_ptr = &y;
   return multi_param_test_ptr(x_ptr, y_ptr);
   // expected-warning at -1 {{Returning value bound to 'x' that will go out of scope}}
-  // expected-warning at -2 {{Returning value bound to 'y' that will go out of scope}}
+  // expected-note at -2    {{Returning value bound to 'x' that will go out of scope}}
+  // expected-warning at -3 {{Returning value bound to 'y' that will go out of scope}}
+  // expected-note at -4    {{Returning value bound to 'y' that will go out of scope}}
 }
 
 // Return value bound to annotated parameters (no dangling sources).
@@ -212,9 +239,11 @@ int *no_dangling_sources_ptr(int *a [[clang::lifetimebound]], int *b [[clang::li
 
 // Return value bound to annotated parameters (one dangling source).
 int *one_dangling_source_ptr(int *a [[clang::lifetimebound]]) {
-  int x = 1;
+  int x = 1; // expected-note {{'x' initialized here}} 
   int *x_ptr = &x;
-  return multi_param_test_ptr(a, x_ptr); // expected-warning {{Returning value bound to 'x' that will go out of scope}}
+  return multi_param_test_ptr(a, x_ptr);
+  // expected-warning at -1 {{Returning value bound to 'x' that will go out of scope}}
+  // expected-note at -2    {{Returning value bound to 'x' that will go out of scope}}
 }
 
 struct S {
@@ -235,17 +264,20 @@ void outer() {
 }
 
 int *danglingLocal() {
-  S s;
-  return s.get();
+  S s; // expected-note {{'s' initialized here}}
+  return s.get(); // expected-note {{Returning value bound to 's' that will go out of scope}}
   // expected-warning at -1 {{Returning value bound to 's' that will go out of scope}}
   // expected-warning at -2 {{Address of stack memory associated with local variable 's' returned}}
-  // expected-warning at -3 {{address of stack memory associated with local variable 's' returned}}
+  // expected-note at -3    {{Address of stack memory associated with local variable 's' returned to caller}}
+  // expected-warning at -4 {{address of stack memory associated with local variable 's' returned}}
 }
 
 int *danglingParam(S param) {
   return param.get();
   // expected-warning at -1 {{Returning value bound to 'param' that will go out of scope}}
-  // expected-warning at -2 {{Address of stack memory associated with local variable 'param' returned}}
-  // expected-warning at -3 {{address of stack memory associated with parameter 'param' returned}}
+  // expected-note at -2    {{Returning value bound to 'param' that will go out of scope}}
+  // expected-warning at -3 {{Address of stack memory associated with local variable 'param' returned}}
+  // expected-note at -4    {{Address of stack memory associated with local variable 'param' returned to caller}}
+  // expected-warning at -5 {{address of stack memory associated with parameter 'param' returned}}
 }
 



More information about the cfe-commits mailing list