[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