[llvm-branch-commits] [clang] [analyzer] Only underline the exact parameter that is bound to the return value in UseAfterLifetimeEnd (PR #215651)
Benedek Kaibas via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 12 05:43:01 PDT 2026
https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/215651
>From 2427200e01b1ceca74a934004f1425f88f106e0d Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Tue, 11 Aug 2026 22:22:07 +0200
Subject: [PATCH 1/2] [analyzer] Only underline the parameter that is bound to
the return value
---
.../Checkers/UseAfterLifetimeEnd.cpp | 24 +++++++++++++------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
index a9065352adae6..de89836ee5dc2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
@@ -43,20 +43,30 @@ class UseAfterLifetimeEndBRVisitor : public BugReporterVisitor {
} // namespace
-static const Expr *getLifetimeBoundArg(const Expr *RetExpr) {
+static const Expr *getLifetimeBoundArg(const Expr *RetExpr,
+ const MemRegion *Region,
+ const ExplodedNode *N) {
const CallExpr *Expr = dyn_cast_or_null<CallExpr>(RetExpr);
if (!Expr)
return nullptr;
+
const FunctionDecl *FD = Expr->getDirectCallee();
if (!FD)
return nullptr;
+ const MemRegion *BaseReg = Region->getBaseRegion();
+
for (const ParmVarDecl *PVD : FD->parameters()) {
- if (PVD->hasAttr<LifetimeBoundAttr>()) {
- unsigned Idx = PVD->getFunctionScopeIndex();
- if (Idx < Expr->getNumArgs())
- return Expr->getArg(Idx);
- }
+ if (!PVD->hasAttr<LifetimeBoundAttr>())
+ continue;
+ unsigned Idx = PVD->getFunctionScopeIndex();
+
+ if (Idx >= Expr->getNumArgs())
+ continue;
+
+ const MemRegion *R = N->getSVal(Expr->getArg(Idx)).getAsRegion();
+ if (R && R->getBaseRegion() == BaseReg)
+ return Expr->getArg(Idx);
}
return nullptr;
}
@@ -117,7 +127,7 @@ PathDiagnosticPieceRef UseAfterLifetimeEndBRVisitor::createSourcePiece(
return nullptr;
const Expr *RetExpr = dyn_cast_or_null<Expr>(S);
- const Expr *Arg = getLifetimeBoundArg(RetExpr);
+ const Expr *Arg = getLifetimeBoundArg(RetExpr, SourceRegion, N);
PathDiagnosticLocation Pos;
>From 3e273b5debb2c3a03b2cb131c0cb1c9c196fb2fe Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Wed, 12 Aug 2026 14:41:57 +0200
Subject: [PATCH 2/2] Add test case for testing highlighting.
---
clang/test/Analysis/lifetime-bound.cpp | 35 +++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/clang/test/Analysis/lifetime-bound.cpp b/clang/test/Analysis/lifetime-bound.cpp
index d29c37f639993..e34796009c299 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugLifetimeModeling \
// 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-output=text %s 2>&1 | FileCheck %s
struct A {};
struct Pair {
@@ -410,3 +411,35 @@ void no_dangling_by_value_argument() {
// The returned reference does not dangle.
takes_by_value(BoundToSelf());
}
+
+int multi_params_annotated(int *p_one [[clang::lifetimebound]], int *p_two [[clang::lifetimebound]]);
+
+int test_multi_param_highlight() {
+ int local_one = 1, local_two = 2;
+ // expected-note at -1 {{'local_one' initialized here}}
+ // expected-note at -2 {{'local_two' initialized here}}
+ return multi_params_annotated(&local_one, &local_two);
+ // expected-warning at -1 {{address of stack memory associated with local variable 'local_one' returned}}
+ // expected-warning at -2 {{address of stack memory associated with local variable 'local_two' returned}}
+ // expected-warning at -3 {{Returning value bound to 'local_one' that will go out of scope}}
+ // expected-note at -4 {{Value's lifetime bound to the lifetime of 'local_one' here}}
+ // expected-note at -5 {{Lifetime of 'local_one' ended here}}
+ // expected-warning at -6 {{Returning value bound to 'local_two' that will go out of scope}}
+ // expected-note at -7 {{Value's lifetime bound to the lifetime of 'local_two' here}}
+ // expected-note at -8 {{Lifetime of 'local_two' ended here}}
+
+ // CHECK: :[[@LINE-10]]:33: note: Value's lifetime bound to the lifetime of 'local_one' here
+ // CHECK-NEXT: [[@LINE-14]] | int local_one = 1, local_two = 2;
+ // CHECK-NEXT: ~~~~~~~~~~~~~~~~~
+ // CHECK-NEXT: [[@LINE-15]] | // expected{{-}}note at -1 {{.*}}
+ // CHECK-NEXT: [[@LINE-15]] | // expected{{-}}note at -2 {{.*}}
+ // CHECK-NEXT: [[@LINE-15]] | return multi_params_annotated(&local_one, &local_two);
+ // CHECK-NEXT: ^~~~~~~~~~
+ // CHECK: :[[@LINE-17]]:45: note: Value's lifetime bound to the lifetime of 'local_two' here
+ // CHECK-NEXT: [[@LINE-21]] | int local_one = 1, local_two = 2;
+ // CHECK-NEXT: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // CHECK-NEXT: [[@LINE-22]] | // expected{{-}}note at -1 {{.*}}
+ // CHECK-NEXT: [[@LINE-22]] | // expected{{-}}note at -2 {{.*}}
+ // CHECK-NEXT: [[@LINE-22]] | return multi_params_annotated(&local_one, &local_two);
+ // CHECK-NEXT: ^~~~~~~~~~
+}
More information about the llvm-branch-commits
mailing list