[clang] Infer [[clang::lifetimebound]] annotation #170418 (PR #171081)

Kashika Akhouri via cfe-commits cfe-commits at lists.llvm.org
Sun Dec 7 22:21:33 PST 2025


https://github.com/kashika0112 created https://github.com/llvm/llvm-project/pull/171081

Adding Annotation Inference in Lifetime Analysis.

This PR implicitly adds lifetime bound annotations to the AST which is then used by functions which are parsed later to detect UARs etc. Example:

```
std::string_view f1(std::string_view a) {
  return a;
}

std::string_view f2(std::string_view a) {
  return f1(a);
}

std::string_view ff(std::string_view a) {
  std::string stack = "something on stack";
  return f2(stack); // warning: return-stack-addr
}
```

Note:
1. We only add lifetime bound annotations to the functions being analyzed currently.
2. Currently, both annotation suggestion and inference work simultaneously. This can be modified based on requirements.
3. The current approach works given that functions are already present in the correct order (callee-before-caller). For not so ideal cases, we can create a CallGraph prior to calling the analysis. This can be done in the next PR.

>From d9e30d12154c68a956bbf8eabb1a56a572e8e118 Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <akhourik at google.com>
Date: Mon, 8 Dec 2025 06:14:28 +0000
Subject: [PATCH] Add annotation inference

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp   | 17 ++++++++++++++++-
 .../Sema/warn-lifetime-safety-suggestions.cpp   | 12 ++++++++++++
 clang/test/Sema/warn-lifetime-safety.cpp        | 16 +++++++++++++++-
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 74792768e2c57..f5236f63da34a 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -55,13 +55,14 @@ class LifetimeChecker {
   const LiveOriginsAnalysis &LiveOrigins;
   const FactManager &FactMgr;
   LifetimeSafetyReporter *Reporter;
+  AnalysisDeclContext &AC;
 
 public:
   LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,
                   const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM,
                   AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)
       : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM),
-        Reporter(Reporter) {
+        Reporter(Reporter), AC(ADC) {
     for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
       for (const Fact *F : FactMgr.getFacts(B))
         if (const auto *EF = F->getAs<ExpireFact>())
@@ -70,6 +71,7 @@ class LifetimeChecker {
           checkAnnotations(OEF);
     issuePendingWarnings();
     suggestAnnotations();
+    inferAnnotations();
   }
 
   /// Checks if an escaping origin holds a placeholder loan, indicating a
@@ -160,6 +162,19 @@ class LifetimeChecker {
     for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap)
       Reporter->suggestAnnotation(PVD, EscapeExpr);
   }
+
+  void inferAnnotations() {
+    /// NOTE: This currently only adds the attribute to the function definition
+    /// being analyzed. For full inter-procedural inference to work reliably
+    /// (e.g., with out-of-order definitions), this attribute would also need to
+    /// be added to all other redeclarations of the function.
+    for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) {
+      ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD);
+      ASTContext &Ctx = AC.getASTContext();
+      auto *Attr = LifetimeBoundAttr::CreateImplicit(Ctx, SourceLocation());
+      PVD->addAttr(Attr);
+    }
+  }
 };
 } // namespace
 
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index c0f675a301d14..087899b9f5d3f 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -89,6 +89,18 @@ void test_getView_on_temporary() {
   (void)sv;
 }
 
+//===----------------------------------------------------------------------===//
+// Annotation Inference Test Cases
+//===----------------------------------------------------------------------===//
+
+View return_view_by_func (View a) {    // expected-warning {{param should be marked [[clang::lifetimebound]]}}.
+  return return_view_directly(a);      // expected-note {{param returned here}}
+}
+
+MyObj* return_pointer_by_func (MyObj* a) {         // expected-warning {{param should be marked [[clang::lifetimebound]]}}.
+  return return_pointer_object(a);                 // expected-note {{param returned here}} 
+}
+
 //===----------------------------------------------------------------------===//
 // Negative Test Cases
 //===----------------------------------------------------------------------===//
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 1191469e23df1..251324f968acd 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -Wno-experimental-lifetime-safety-suggestions -verify %s
 
 struct MyObj {
   int id;
@@ -552,6 +552,20 @@ const int& get_ref_to_local() {
                       // expected-note at -1 {{returned here}}
 }
 
+View inference_callee_return_identity(View a) {
+  return a;
+}
+
+View inference_caller_forwards_callee(View a) {
+  return inference_callee_return_identity(a);
+}
+
+View inference_top_level_return_stack_view() {
+  MyObj local_stack;
+  return inference_caller_forwards_callee(local_stack);     // expected-warning {{address of stack memory is returned later}}
+                                                            // expected-note at -1 {{returned here}}
+}
+
 //===----------------------------------------------------------------------===//
 // Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined
 // These are cases where the diagnostic kind is determined by location



More information about the cfe-commits mailing list