[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