[clang] 28896ff - [analyzer][NFC] Add test cases for the lifetime test suite (#212254)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 31 01:15:33 PDT 2026
Author: Benedek Kaibas
Date: 2026-07-31T10:15:28+02:00
New Revision: 28896ffcde9a773361e5d61191ebc2100abf5bcf
URL: https://github.com/llvm/llvm-project/commit/28896ffcde9a773361e5d61191ebc2100abf5bcf
DIFF: https://github.com/llvm/llvm-project/commit/28896ffcde9a773361e5d61191ebc2100abf5bcf.diff
LOG: [analyzer][NFC] Add test cases for the lifetime test suite (#212254)
During the development of the lifetime checkers I had many important
test cases saved locally and were never imported to the test files of
the checkers. This PR adds those missing test cases to the test files.
By adding these test cases it helps tracking the state of the lifetime
checkers and what improvements they need in order to have better
coverage and reduce the false positive rate.
*AI policy*: During the development I have consulted with claude sonnet
5 to list me test case scenarios it thinks are useful and then reviewed
its feedback and implemented/wrote those test cases by myself. Since
some of the test cases are edge cases I wanted to make sure the
implementation is on the right track.
Added:
Modified:
clang/test/Analysis/dangling-ptr-deref.cpp
clang/test/Analysis/lifetime-bound.cpp
Removed:
################################################################################
diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp
index f210195762770..7f13c241dadf0 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -272,3 +272,62 @@ char member_subregion_dangling_deref_increment() {
// expected-note at -2 {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}}
}
+void chain() {
+ int *ptr = nullptr;
+ {
+ int local = 5; // expected-note {{'local' initialized to 5}}
+ int *a = &local; // expected-note {{'a' initialized here}}
+ int *b = a; // expected-note {{'b' initialized to the value of 'a'}}
+ int *c = b; // expected-note {{'c' initialized to the value of 'b'}}
+ ptr = c; // expected-note {{The value of 'c' is assigned to 'ptr'}}
+ }
+ // expected-note at -1 {{'local' is destroyed here}}
+ *ptr = 6;
+ // expected-warning at -1 {{Use of 'local' after its lifetime ended}}
+ // expected-note at -2 {{Use of 'local' after its lifetime ended}}
+}
+
+void branch(bool cond) {
+ int *ptr = nullptr;
+ {
+ int x = 1; // expected-note {{'x' initialized to 1}}
+ int y = 2; // expected-note {{'y' initialized to 2}}
+ if (cond) {
+ // expected-note at -1 {{Assuming 'cond' is true}}
+ // expected-note at -2 {{Taking true branch}}
+ // expected-note at -3 {{Assuming 'cond' is false}}
+ // expected-note at -4 {{Taking false branch}}
+ ptr = &x; // expected-note {{Value assigned to 'ptr'}}
+ } else {
+ ptr = &y; // expected-note {{Value assigned to 'ptr'}}
+ }
+ }
+ // expected-note at -1 {{'x' is destroyed here}}
+ // expected-note at -2 {{'y' is destroyed here}}
+ *ptr = 6;
+ // expected-warning at -1 {{Use of 'x' after its lifetime ended}}
+ // expected-note at -2 {{Use of 'x' after its lifetime ended}}
+ // expected-warning at -3 {{Use of 'y' after its lifetime ended}}
+ // expected-note at -4 {{Use of 'y' after its lifetime ended}}
+}
+
+int *func(int *p) { return p; }
+int *call_func(int *p) { return func(p); }
+// expected-note at -1 {{Passing value via 1st parameter 'p'}}
+// expected-note at -2 {{Returning pointer}}
+
+void dangling_through_calls() {
+ int *ptr = nullptr;
+ {
+ int local = 5; // expected-note {{'local' initialized to 5}}
+ ptr = call_func(&local);
+ // expected-note at -1 {{Passing value via 1st parameter 'p'}}
+ // expected-note at -2 {{Calling 'call_func'}}
+ // expected-note at -3 {{Returning from 'call_func'}}
+ // expected-note at -4 {{Value assigned to 'ptr'}}
+ }
+ // expected-note at -1 {{'local' is destroyed here}}
+ *ptr = 6;
+ // expected-warning at -1 {{Use of 'local' after its lifetime ended}}
+ // expected-note at -2 {{Use of 'local' after its lifetime ended}}
+}
diff --git a/clang/test/Analysis/lifetime-bound.cpp b/clang/test/Analysis/lifetime-bound.cpp
index 5ed7ea365b236..ef8ffeb87a8dd 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -4,6 +4,31 @@
// RUN: -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s
struct A {};
+struct Pair {
+ int a;
+ int b;
+};
+
+struct Base {
+ int x;
+};
+
+struct Derived : Base {
+ int y;
+};
+
+struct Inner {
+ int val;
+};
+
+struct Outer {
+ Inner inner;
+};
+
+struct Buffer {
+ int arr[4];
+};
+
void clang_analyzer_dumpLifetimeOriginsOf(int*);
void clang_analyzer_dumpLifetimeOriginsOf(int&);
void clang_analyzer_dumpLifetimeOriginsOf(A*);
@@ -103,7 +128,7 @@ void caller_seven() {
clang_analyzer_dumpLifetimeOriginsOf(bind);
// expected-warning-re at -1 {{Origin '&SymRegion{{.*}}' bound to 'y'}}
- // expected-note-re at -2 {{Origin '&SymRegion{{.*}}' bound to 'y'}}
+ // expected-note-re at -2 {{Origin '&SymRegion{{.*}}' bound to 'y'}}
}
// Function returns a reference and has an annotated parameter.
@@ -280,3 +305,76 @@ int *danglingParam(S param) {
// 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}}
}
+
+int *getFieldPtr(Pair &p [[clang::lifetimebound]]) { return &p.a; }
+
+int *field_subobject_dangling() {
+ Pair pair{3, 5}; // expected-note {{'pair' initialized here}}
+ return getFieldPtr(pair);
+ // expected-warning at -1 {{Returning value bound to 'pair' that will go out of scope}}
+ // expected-note at -2 {{Returning value bound to 'pair' that will go out of scope}}
+ // expected-warning at -3 {{Address of stack memory associated with local variable 'pair' returned to caller}}
+ // expected-note at -4 {{Address of stack memory associated with local variable 'pair' returned to caller}}
+ // expected-warning at -5 {{address of stack memory associated with local variable 'pair' returned}}
+}
+
+int *getBasePtr(Derived &d [[clang::lifetimebound]]) {
+ return &static_cast<Base &>(d).x;
+}
+
+int *base_subobject_dangling() {
+ Derived derived{}; // expected-note {{'derived' initialized here}}
+ return getBasePtr(derived);
+ // expected-warning at -1 {{Returning value bound to 'derived' that will go out of scope}}
+ // expected-note at -2 {{Returning value bound to 'derived' that will go out of scope}}
+ // expected-warning at -3 {{Address of stack memory associated with local variable 'derived' returned to caller}}
+ // expected-note at -4 {{Address of stack memory associated with local variable 'derived' returned to caller}}
+ // expected-warning at -5 {{address of stack memory associated with local variable 'derived' returned}}
+}
+
+int *getNestedFieldPtr(Outer &o [[clang::lifetimebound]]) {
+ return &o.inner.val;
+}
+
+int *nested_subobject_dangling() {
+ Outer outer{}; // expected-note {{'outer' initialized here}}
+ return getNestedFieldPtr(outer);
+ // expected-warning at -1 {{Returning value bound to 'outer' that will go out of scope}}
+ // expected-note at -2 {{Returning value bound to 'outer' that will go out of scope}}
+ // expected-warning at -3 {{Address of stack memory associated with local variable 'outer' returned to caller}}
+ // expected-note at -4 {{Address of stack memory associated with local variable 'outer' returned to caller}}
+ // expected-warning at -5 {{address of stack memory associated with local variable 'outer' returned}}
+}
+
+int *getArrayElementPtr(Buffer &b [[clang::lifetimebound]]) {
+ return &b.arr[0];
+}
+
+int *array_member_subobject_dangling() {
+ Buffer buf{}; // expected-note {{'buf' initialized here}}
+ return getArrayElementPtr(buf);
+ // expected-warning at -1 {{Returning value bound to 'buf' that will go out of scope}}
+ // expected-note at -2 {{Returning value bound to 'buf' that will go out of scope}}
+ // expected-warning at -3 {{Address of stack memory associated with local variable 'buf' returned to caller}}
+ // expected-note at -4 {{Address of stack memory associated with local variable 'buf' returned to caller}}
+ // expected-warning at -5 {{address of stack memory associated with local variable 'buf' returned}}
+}
+
+// FIXME: Heap allocated memory regions are not yet handled by the lifetime checkers.
+int *heap_dangling_source_lifetimebound() {
+ int *i = new int(5);
+ int *p = test_func(i);
+ delete i;
+ return p; // no-warning
+}
+
+struct CustomStringView {
+ CustomStringView(const char *s [[clang::lifetimebound]]);
+};
+
+// FIXME: The StringView return is a struct returned by value which is represented
+// as a LazyCompoundVal that the lifetime checkers do not support as of now.
+CustomStringView dangling_sv() {
+ char s[] = "dangling";
+ return CustomStringView(s); // expected-warning {{address of stack memory associated with local variable 's' returned}}
+}
More information about the cfe-commits
mailing list