https://github.com/benedekaibas created https://github.com/llvm/llvm-project/pull/212158
Currently the `UseAfterLifetimeEnd` checker used `getString()` for constructing error message. However, `getString()` is a debug only stringification and should not be used for emitting reports to the users. That is why I have changed it to `getDescriptiveName()` and also implemented the `getRegionName`(#211552) function to return the region's descriptive name. The `getRegionName()` function got also moved to the modeling checker since both of the reporting checkers consume it (#211818). This PR also uses the `trackStoredValue()` for value tracking path notes, so the report points at where the value's source came from. >From 1025b0c91d2bf411a5fa27357ee61cd2e6c7b31e Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> 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@-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@-1 {{'n' is destroyed here}} // expected-note@-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@-1 {{'num' is destroyed here}} @@ -75,8 +75,8 @@ void test_case_seven() { // expected-note@+3 {{Loop condition is true. Entering loop body}} // expected-note@+2 {{Assuming 'i' is >= 10}} // expected-note@+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@-1 {{'i' is destroyed here}} >From 415befa8f27112f0152f0adf4340d1cd6fd1a321 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> 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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-1 {{'tmp_buffer.buffer[1]' is destroyed here}} >From c6b5fb0873b33217fcf485be0f4288a7e26ca84c Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> 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 <[email protected]> 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 <[email protected]> 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@-1 {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}} // expected-note@-2 {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}} } + >From fc6cf144525b0ae5be9d15b739c89710153f6c96 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> 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@-1 {{Origin &v bound to v}} + // expected-note@-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@-1 {{Origin &y.a bound to y}} + // expected-note@-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@-1 {{Origin &z.a bound to z}} + // expected-note@-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@-1 {{Origin &num bound to num}} + // expected-note@-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@-1 {{Origin &n bound to n}} + // expected-note@-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@-1 {{Origin &odd bound to odd}} + // expected-note@-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@-1 {{Origin &SymRegion{{.*}} bound to y}} + // expected-note-re@-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@-1 {{Origin &SymRegion{{.*}} bound to f}} + // expected-note-re@-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@-1 {{Origin &SymRegion{{.*}} bound to first_num, second_num}} + // expected-note-re@-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@-1 {{Returning value bound to 'i' that will go out of scope}} // expected-warning@-2 {{address of stack memory associated with local variable 'i' returned}} + // expected-note@-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@-1 {{Returning value bound to 'y' that will go out of scope}} + // expected-note@-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@-1 {{'x' initialized here}} + // expected-note@-2 {{'y' initialized here}} return multi_param_test_ref(x, y); // expected-warning@-1 {{Returning value bound to 'x' that will go out of scope}} - // expected-warning@-2 {{Returning value bound to 'y' that will go out of scope}} - // expected-warning@-3 {{reference to stack memory associated with local variable 'x' returned}} - // expected-warning@-4 {{reference to stack memory associated with local variable 'y' returned}} + // expected-note@-2 {{Returning value bound to 'x' that will go out of scope}} + // expected-warning@-3 {{Returning value bound to 'y' that will go out of scope}} + // expected-note@-4 {{Returning value bound to 'y' that will go out of scope}} + // expected-warning@-5 {{reference to stack memory associated with local variable 'x' returned}} + // expected-warning@-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@-1 {{Returning value bound to 'x' that will go out of scope}} - // expected-warning@-2 {{reference to stack memory associated with local variable 'x' returned}} + // expected-note@-2 {{Returning value bound to 'x' that will go out of scope}} + // expected-warning@-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@-1 {{'x' initialized here}} + // expected-note@-2 {{'y' initialized here}} int *x_ptr = &x; int *y_ptr = &y; return multi_param_test_ptr(x_ptr, y_ptr); // expected-warning@-1 {{Returning value bound to 'x' that will go out of scope}} - // expected-warning@-2 {{Returning value bound to 'y' that will go out of scope}} + // expected-note@-2 {{Returning value bound to 'x' that will go out of scope}} + // expected-warning@-3 {{Returning value bound to 'y' that will go out of scope}} + // expected-note@-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@-1 {{Returning value bound to 'x' that will go out of scope}} + // expected-note@-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@-1 {{Returning value bound to 's' that will go out of scope}} // expected-warning@-2 {{Address of stack memory associated with local variable 's' returned}} - // expected-warning@-3 {{address of stack memory associated with local variable 's' returned}} + // expected-note@-3 {{Address of stack memory associated with local variable 's' returned to caller}} + // expected-warning@-4 {{address of stack memory associated with local variable 's' returned}} } int *danglingParam(S param) { return param.get(); // expected-warning@-1 {{Returning value bound to 'param' that will go out of scope}} - // expected-warning@-2 {{Address of stack memory associated with local variable 'param' returned}} - // expected-warning@-3 {{address of stack memory associated with parameter 'param' returned}} + // expected-note@-2 {{Returning value bound to 'param' that will go out of scope}} + // expected-warning@-3 {{Address of stack memory associated with local variable 'param' returned}} + // expected-note@-4 {{Address of stack memory associated with local variable 'param' returned to caller}} + // expected-warning@-5 {{address of stack memory associated with parameter 'param' returned}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
