https://github.com/benedekaibas updated 
https://github.com/llvm/llvm-project/pull/205521

>From ed9afadd50a70193aa48fa4d6082409b445a91d6 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 24 Jun 2026 12:05:46 +0200
Subject: [PATCH 1/6] [analyzer] Implement LifetimeAnnotations checker.

---
 .../clang/StaticAnalyzer/Checkers/Checkers.td |  11 +
 .../StaticAnalyzer/Checkers/CMakeLists.txt    |   2 +
 .../Checkers/LifetimeAnnotations.cpp          | 305 ++++++++++++++++++
 clang/test/Analysis/debug-lifetime-bound.cpp  |  10 +
 clang/test/Analysis/lifetime-bound.cpp        | 171 ++++++++++
 5 files changed, 499 insertions(+)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp
 create mode 100644 clang/test/Analysis/debug-lifetime-bound.cpp
 create mode 100644 clang/test/Analysis/lifetime-bound.cpp

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index d02c3195069f3..5ba220ab6d60e 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -788,6 +788,11 @@ def SmartPtrChecker: Checker<"SmartPtr">,
   Dependencies<[SmartPtrModeling]>,
   Documentation<HasDocumentation>;
 
+def LifetimeAnnotations : Checker<"LifetimeAnnotations">,
+  HelpText<"Check for lifetime violations by incorporating lifetime "
+           "annotations into the analysis">,
+  Documentation<NotDocumented>;
+
 } // end: "alpha.cplusplus"
 
 
//===----------------------------------------------------------------------===//
@@ -1576,6 +1581,12 @@ def CheckerDocumentationChecker : 
Checker<"CheckerDocumentation">,
   HelpText<"Defines an empty checker callback for all possible handlers.">,
   Documentation<NotDocumented>;
 
+def DebugLifetimeAnnotations : Checker<"DebugLifetimeAnnotations">,
+  HelpText<"Prints the bindings recorded by the LifetimeAnnotations checker. "
+           "Use with clang_analyzer_lifetime_bound().">,
+  WeakDependencies<[LifetimeAnnotations]>,
+  Documentation<NotDocumented>;
+
 } // end "debug"
 
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt 
b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 8a0621077b977..8363f345f4cc8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -55,6 +55,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   IteratorModeling.cpp
   IteratorRangeChecker.cpp
   IvarInvalidationChecker.cpp
+  LifetimeAnnotations.cpp
   LLVMConventionsChecker.cpp
   LocalizationChecker.cpp
   MacOSKeychainAPIChecker.cpp
@@ -146,6 +147,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   clangAST
   clangASTMatchers
   clangAnalysis
+  clangAnalysisLifetimeSafety
   clangBasic
   clangLex
   clangStaticAnalyzerCore
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp
new file mode 100644
index 0000000000000..e25d076dd0bd5
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp
@@ -0,0 +1,305 @@
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
+#include "clang/AST/Attrs.inc"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
+REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
+
+REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *)
+
+namespace {
+class LifetimeAnnotations
+    : public Checker<check::PostCall, check::EndFunction, check::Location,
+                     check::DeadSymbols> {
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
+  void reportDanglingSource(const MemRegion *Region, ExplodedNode *N,
+                            CheckerContext &C) const;
+  void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N,
+                           CheckerContext &C) const;
+
+  void checkReturnedBorrower(SVal Val, ProgramStateRef State,
+                             CheckerContext &C) const;
+  void reportDanglingBorrower(const LifetimeSourceSet *Sources,
+                              CheckerContext &C) const;
+  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+  void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+                     CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+
+  const BugType BugMsg{this, "LifetimeAnnotations", "LifetimeBound"};
+};
+
+} // namespace
+
+static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal,
+                                  const MemRegion *Source) {
+  LifetimeSourceSet::Factory &F =
+      State->getStateManager().get_context<LifetimeSourceSet>();
+
+  const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
+  LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
+  Set = F.add(Set, Source);
+  State = State->set<LifetimeBoundMap>(RetVal, Set);
+  return State;
+}
+
+void LifetimeAnnotations::checkPostCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const auto *FC = dyn_cast<AnyFunctionCall>(&Call);
+  if (!FC)
+    return;
+
+  const FunctionDecl *FD = FC->getDecl();
+  if (!FD)
+    return;
+
+  SVal RetVal = Call.getReturnValue();
+
+  for (const ParmVarDecl *PVD : FD->parameters()) {
+    if (PVD->hasAttr<LifetimeBoundAttr>()) {
+      unsigned Idx = PVD->getFunctionScopeIndex();
+      SVal Arg = Call.getArgSVal(Idx);
+      if (const MemRegion *ArgValRegion = Arg.getAsRegion())
+        State = bindValues(State, RetVal, ArgValRegion);
+    }
+  }
+
+  if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) {
+    if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
+      if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) {
+        State = bindValues(State, RetVal, AttrRegion);
+      }
+    }
+  }
+  C.addTransition(State);
+}
+
+static bool hasDanglingSource(const MemRegion *Source, ProgramStateRef State,
+                              CheckerContext &C) {
+  // FIXME: The checker currently handles stack-region sources. Other
+  // region kinds require separate methodology. For example, heap
+  // regions do not go out of scope at the end of a stack frame, so
+  // in order to detect those type of dangling sources the function
+  // needs to be expanded to an event-driven approach as well.
+  if (const auto *StackSpace =
+          Source->getMemorySpaceAs<StackSpaceRegion>(State)) {
+    const StackFrame *SF = StackSpace->getStackFrame();
+    const StackFrame *CurrentSF = C.getStackFrame();
+    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+      return true;
+  }
+  return false;
+}
+
+void LifetimeAnnotations::checkReturnedBorrower(SVal Val, ProgramStateRef 
State,
+                                                CheckerContext &C) const {
+  if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
+    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+      for (const MemRegion *Region : *SourceSet) {
+        if (hasDanglingSource(Region, State, C))
+          reportDanglingSource(Region, N, C);
+      }
+    }
+  }
+}
+
+void LifetimeAnnotations::checkEndFunction(const ReturnStmt *RS,
+                                           CheckerContext &C) const {
+  if (!RS)
+    return;
+
+  ProgramStateRef State = C.getState();
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  const Expr *RetExpr = RS->getRetValue();
+  if (!RetExpr)
+    return;
+
+  RetExpr = RetExpr->IgnoreParens();
+  SVal RetVal = C.getSVal(RetExpr);
+  checkReturnedBorrower(RetVal, State, C);
+}
+
+void LifetimeAnnotations::reportDanglingBorrower(
+    const LifetimeSourceSet *Sources, CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  ExplodedNode *N = C.generateNonFatalErrorNode();
+  if (!N)
+    return;
+
+  for (const MemRegion *Source : *Sources) {
+    if (State->contains<DeallocatedSourceSet>(Source)) {
+      reportUseAfterScope(Source, N, C);
+    }
+  }
+}
+
+void LifetimeAnnotations::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+                                        CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  // FIXME: If a borrower has multiple bound sources the callback
+  // warns if any of the sources have died. PathDiagnosticVisitor
+  // should be used to trace and identify which annotated parameter
+  // recorded the binding. Attaching this information as path notes
+  // would make the diagnostics more useful to the user.
+  if (auto *SourceSet = State->get<LifetimeBoundMap>(Loc))
+    reportDanglingBorrower(SourceSet, C);
+}
+
+void LifetimeAnnotations::reportDanglingSource(const MemRegion *Region,
+                                               ExplodedNode *N,
+                                               CheckerContext &C) const {
+  auto BR = std::make_unique<PathSensitiveBugReport>(
+      BugMsg,
+      (llvm::Twine("Returning value bound to '") + Region->getString() +
+       "' that will go out of scope")
+          .str(),
+      N);
+  C.emitReport(std::move(BR));
+}
+
+void LifetimeAnnotations::reportUseAfterScope(const MemRegion *Region,
+                                              ExplodedNode *N,
+                                              CheckerContext &C) const {
+  auto BR = std::make_unique<PathSensitiveBugReport>(
+      BugMsg,
+      (llvm::Twine("Use of '") + Region->getString() +
+       "' after its lifetime ended.")
+          .str(),
+      N);
+  C.emitReport(std::move(BR));
+}
+
+void LifetimeAnnotations::checkDeadSymbols(SymbolReaper &SymReaper,
+                                           CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
+
+  DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
+
+  for (SVal Val : llvm::make_first_range(LBMap)) {
+    if (const MemRegion *ValRegion = Val.getAsRegion()) {
+      if (!SymReaper.isLiveRegion(ValRegion))
+        State = State->remove<LifetimeBoundMap>(Val);
+    } else if (SymbolRef ValRef =
+                   Val.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+      if (!SymReaper.isLive(ValRef))
+        State = State->remove<LifetimeBoundMap>(Val);
+    }
+  }
+
+  for (const MemRegion *Region : Sources) {
+    if (!SymReaper.isLiveRegion(Region))
+      State = State->remove<DeallocatedSourceSet>(Region);
+  }
+
+  C.addTransition(State);
+}
+
+void LifetimeAnnotations::printState(raw_ostream &Out, ProgramStateRef State,
+                                     const char *NL, const char *Sep) const {
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  Out << Sep << "LifetimeBound bindings:" << NL;
+  for (auto &&[OriginSym, SourceSet] : LBMap) {
+    for (const auto *Region : SourceSet)
+      Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+  }
+}
+
+namespace {
+class DebugLifetimeAnnotations : public Checker<eval::Call> {
+public:
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+  void analyzerLifetimeBound(const CallEvent &Call, CheckerContext &C) const;
+
+  const BugType BugMsg{this, "DebugLifetimeAnnotations", "DebugLifetimeBound"};
+  using FnCheck = void (DebugLifetimeAnnotations::*)(const CallEvent &Call,
+                                                     CheckerContext &C) const;
+
+  const CallDescriptionMap<FnCheck> Callbacks = {
+      {{CDM::SimpleFunc, {"clang_analyzer_lifetime_bound"}},
+       &DebugLifetimeAnnotations::analyzerLifetimeBound},
+  };
+};
+
+} // namespace
+
+bool DebugLifetimeAnnotations::evalCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+
+  const auto *CE = dyn_cast_if_present<CallExpr>(Call.getOriginExpr());
+  if (!CE)
+    return false;
+
+  const FnCheck *Handler = Callbacks.lookup(Call);
+  if (!Handler)
+    return false;
+
+  (this->*(*Handler))(Call, C);
+  return true;
+}
+
+void DebugLifetimeAnnotations::analyzerLifetimeBound(const CallEvent &Call,
+                                                     CheckerContext &C) const {
+
+  ProgramStateRef State = C.getState();
+  unsigned int ArgCount = Call.getNumArgs();
+  if (ArgCount != 1)
+    return;
+
+  SVal ArgSVal = Call.getArgSVal(0);
+
+  if (auto *SourceSet = State->get<LifetimeBoundMap>(ArgSVal)) {
+    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+      for (const auto *Region : *SourceSet) {
+        llvm::SmallString<128> Str;
+        llvm::raw_svector_ostream OS(Str);
+        OS << " Origin " << ArgSVal << " bound to " << Region;
+        auto BR = std::make_unique<PathSensitiveBugReport>(BugMsg, OS.str(), 
N);
+        C.emitReport(std::move(BR));
+      }
+    }
+  }
+}
+
+void ento::registerLifetimeAnnotations(CheckerManager &mgr) {
+  mgr.registerChecker<LifetimeAnnotations>();
+}
+
+bool ento::shouldRegisterLifetimeAnnotations(const CheckerManager &mgr) {
+  return true;
+}
+
+void ento::registerDebugLifetimeAnnotations(CheckerManager &mgr) {
+  mgr.registerChecker<DebugLifetimeAnnotations>();
+}
+
+bool ento::shouldRegisterDebugLifetimeAnnotations(const CheckerManager &mgr) {
+  return true;
+}
diff --git a/clang/test/Analysis/debug-lifetime-bound.cpp 
b/clang/test/Analysis/debug-lifetime-bound.cpp
new file mode 100644
index 0000000000000..e62c51ae6bc53
--- /dev/null
+++ b/clang/test/Analysis/debug-lifetime-bound.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.LifetimeAnnotations,debug.DebugLifetimeAnnotations
 -verify %s
+
+// expected-no-diagnostics
+
+void clang_analyzer_lifetime_bound(int);
+
+void test() {
+  int x = 5;
+  clang_analyzer_lifetime_bound(x); // no-warning: verifies debug checker does 
not crash standalone
+}
diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
new file mode 100644
index 0000000000000..ca97419b63e53
--- /dev/null
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -0,0 +1,171 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.LifetimeAnnotations,debug.DebugLifetimeAnnotations
 \
+// RUN:   -analyzer-config cfg-lifetime=true -verify %s
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.LifetimeAnnotations,debug.DebugLifetimeAnnotations
 \
+// RUN:   -analyzer-config c++-container-inlining=false -analyzer-config 
cfg-lifetime=true -verify %s
+
+struct A {};
+
+void clang_analyzer_lifetime_bound(int*);
+void clang_analyzer_lifetime_bound(int&);
+void clang_analyzer_lifetime_bound(A*);
+void clang_analyzer_lifetime_bound(A&);
+
+// These are the cases when the result of function calls are MemRegions.
+
+// Ref type parameter annotated case
+struct X {
+  int& choose(int& a [[clang::lifetimebound]]) { return a; }
+};
+
+void caller() {
+  int v = 0;
+  X obj;
+  int& r = obj.choose(v);
+  clang_analyzer_lifetime_bound(r); // expected-warning {{Origin &v bound to 
v}}
+}
+
+// Obj ref type function return annotated case
+struct Y {
+  A a;
+  A& getA() [[clang::lifetimebound]] { return a; }
+};
+
+void caller_two() {
+  // Return statement is annotated case.
+  Y y;
+  A& f = y.getA();
+  clang_analyzer_lifetime_bound(f); // expected-warning {{Origin &y.a bound to 
y}}
+}
+
+// Obj ptr type function return annotated case
+struct Z {
+  A a;
+  A* getA() [[clang::lifetimebound]] { return &a; }
+};
+
+void caller_three() {
+  Z z;
+  A* func = z.getA();
+  clang_analyzer_lifetime_bound(func); // expected-warning {{Origin &z.a bound 
to z}}
+}
+
+// Free function with annotated param and ref return
+int& foo(int& num [[clang::lifetimebound]]) { return num; }
+
+void caller_four() {
+  int num = 5;
+  int& s = foo(num);
+  clang_analyzer_lifetime_bound(s); // expected-warning {{Origin &num bound to 
num}}
+}
+
+// Free function with annotated param and ptr return
+int* boo(int* num [[clang::lifetimebound]]) { return num; }
+
+void caller_five() {
+  int n = 55;
+  int* n_ptr = &n;
+  int* s = boo(n_ptr);
+
+  clang_analyzer_lifetime_bound(s); // expected-warning {{Origin &n bound to 
n}}
+}
+
+// Free function with both annotated and non-annotated parameters.
+int& fn(int& f, int& s [[clang::lifetimebound]]) { return s; }
+
+void caller_six() {
+  int even = 50;
+  int odd = 55;
+  int& s = fn(even, odd);
+
+  clang_analyzer_lifetime_bound(s); // expected-warning {{Origin &odd bound to 
odd}}
+}
+
+
+
+// These are the cases when the result of function calls are SymbolRefs.
+
+// Function returns ptr and has an annotated parameter
+int* foo(int* n [[clang::lifetimebound]]);
+
+void caller_seven() {
+  int y = 15;
+  int* y_ptr = &y;
+  auto* bind = foo(y_ptr);
+
+  clang_analyzer_lifetime_bound(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to y}}
+}
+
+// Function returns a reference and has an annotated parameter
+int& func(int& some_number [[clang::lifetimebound]]);
+
+void caller_eight() {
+  int f = 15;
+  auto& bind = func(f);
+
+  clang_analyzer_lifetime_bound(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to f}}
+}
+
+// Function returns a reference and has two annotated parameters.
+int& f(int& a [[clang::lifetimebound]], int& b [[clang::lifetimebound]]);
+
+void caller_nine() {
+  int first_num = 1;
+  int second_num = 2;
+  int& numbers = f(first_num, second_num);
+
+  clang_analyzer_lifetime_bound(numbers);
+  // expected-warning-re@-1 {{Origin &SymRegion{{.*}} bound to first_num}}
+  // expected-warning-re@-2 {{Origin &SymRegion{{.*}} bound to second_num}}
+}
+
+struct View {
+  int* p;
+};
+View makeView(int& x [[clang::lifetimebound]]);
+
+void clang_analyzer_lifetime_bound(View);
+
+void caller_view() {
+  int v = 42;
+  View w = makeView(v);
+  // FIXME: Currently none of the maps cover LazyCompoundVal
+  clang_analyzer_lifetime_bound(w); // no-warning
+}
+
+
+
+// These are the test cases for testing the correctness of the emitted warning 
from the LifetimeAnnotations checker.
+
+// Return value bound to annotated param cases
+int *test_func(int *p [[clang::lifetimebound]]);
+
+
+int *direct_return() {
+  int i = 5;
+  return test_func(&i);
+  // expected-warning@-1 {{Returning value bound to 'i' that will go out of 
scope}}
+  // expected-warning@-2 {{address of stack memory associated with local 
variable 'i' returned}}
+}
+
+int *variable_return() {
+  int y = 5;
+  int *p = test_func(&y);
+  return p; // expected-warning {{Returning value bound to 'y' that will go 
out of scope}}
+}
+
+int *borrow_from_caller(int *b [[clang::lifetimebound]]) {
+  return test_func(b); // no-warning
+}
+
+void no_return() {
+  int i = 5;
+  int *p = test_func(&i);
+  (void)p; // no-warning
+}
+
+int* g() {
+  int i = 5;
+  int* p = test_func(&i);
+  (void)p;
+  return nullptr; // no-warning
+}

>From ea22d128673996408833b7a30ecd97513df19591 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Wed, 24 Jun 2026 23:36:37 +0200
Subject: [PATCH 2/6] Changed checker naming and resolved comments.

---
 .../clang/StaticAnalyzer/Checkers/Checkers.td |  10 +-
 .../StaticAnalyzer/Checkers/CMakeLists.txt    |   2 +-
 .../Checkers/UseAfterLifetimeEnd.cpp          | 317 ++++++++++++++++++
 clang/test/Analysis/debug-lifetime-bound.cpp  |   7 +-
 clang/test/Analysis/lifetime-bound.cpp        |  57 ++--
 5 files changed, 355 insertions(+), 38 deletions(-)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 5ba220ab6d60e..2c59fddc89ca1 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -788,9 +788,9 @@ def SmartPtrChecker: Checker<"SmartPtr">,
   Dependencies<[SmartPtrModeling]>,
   Documentation<HasDocumentation>;
 
-def LifetimeAnnotations : Checker<"LifetimeAnnotations">,
-  HelpText<"Check for lifetime violations by incorporating lifetime "
-           "annotations into the analysis">,
+def UseAfterLifetimeEnd : Checker<"UseAfterLifetimeEnd">,
+  HelpText<"Check for uses of references or pointers that "
+           "outlive their bound object">,
   Documentation<NotDocumented>;
 
 } // end: "alpha.cplusplus"
@@ -1581,10 +1581,10 @@ def CheckerDocumentationChecker : 
Checker<"CheckerDocumentation">,
   HelpText<"Defines an empty checker callback for all possible handlers.">,
   Documentation<NotDocumented>;
 
-def DebugLifetimeAnnotations : Checker<"DebugLifetimeAnnotations">,
+def DebugUseAfterLifetimeEnd : Checker<"DebugUseAfterLifetimeEnd">,
   HelpText<"Prints the bindings recorded by the LifetimeAnnotations checker. "
            "Use with clang_analyzer_lifetime_bound().">,
-  WeakDependencies<[LifetimeAnnotations]>,
+  WeakDependencies<[UseAfterLifetimeEnd]>,
   Documentation<NotDocumented>;
 
 } // end "debug"
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt 
b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 8363f345f4cc8..46c0c36fda736 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -55,7 +55,6 @@ add_clang_library(clangStaticAnalyzerCheckers
   IteratorModeling.cpp
   IteratorRangeChecker.cpp
   IvarInvalidationChecker.cpp
-  LifetimeAnnotations.cpp
   LLVMConventionsChecker.cpp
   LocalizationChecker.cpp
   MacOSKeychainAPIChecker.cpp
@@ -127,6 +126,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   UninitializedObject/UninitializedPointee.cpp
   UnixAPIChecker.cpp
   UnreachableCodeChecker.cpp
+  UseAfterLifetimeEnd.cpp
   VforkChecker.cpp
   VLASizeChecker.cpp
   VAListChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp 
b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
new file mode 100644
index 0000000000000..81a7aabd6f7a4
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
@@ -0,0 +1,317 @@
+#include "clang/AST/Attr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
+REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
+
+REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *)
+
+namespace {
+class UseAfterLifetimeEnd
+    : public Checker<check::PostCall, check::EndFunction, check::Location,
+                     check::DeadSymbols> {
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
+  void reportDanglingSource(const MemRegion *Region, ExplodedNode *N,
+                            CheckerContext &C) const;
+  void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N,
+                           CheckerContext &C) const;
+
+  void checkReturnedBorrower(SVal Val, ProgramStateRef State,
+                             CheckerContext &C) const;
+  void reportDanglingBorrower(const LifetimeSourceSet *Sources,
+                              CheckerContext &C) const;
+  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+  void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+                     CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+
+  const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
+};
+
+} // namespace
+
+static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal,
+                                  const MemRegion *Source) {
+  LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
+
+  const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
+  LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
+  Set = F.add(Set, Source);
+  State = State->set<LifetimeBoundMap>(RetVal, Set);
+  return State;
+}
+
+void UseAfterLifetimeEnd::checkPostCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const auto *FC = dyn_cast<AnyFunctionCall>(&Call);
+  if (!FC)
+    return;
+
+  const FunctionDecl *FD = FC->getDecl();
+  if (!FD)
+    return;
+
+  SVal RetVal = Call.getReturnValue();
+
+  for (const ParmVarDecl *PVD : FD->parameters()) {
+    if (PVD->hasAttr<LifetimeBoundAttr>()) {
+      unsigned Idx = PVD->getFunctionScopeIndex();
+      SVal Arg = Call.getArgSVal(Idx);
+      if (const MemRegion *ArgValRegion = Arg.getAsRegion())
+        State = bindValues(State, RetVal, ArgValRegion);
+    }
+  }
+
+  if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) {
+    if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
+      if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) {
+        State = bindValues(State, RetVal, AttrRegion);
+      }
+    }
+  }
+  C.addTransition(State);
+}
+
+static bool hasDanglingSource(const MemRegion *Source, ProgramStateRef State,
+                              CheckerContext &C) {
+  // FIXME: The checker currently handles stack-region sources. Other
+  // region kinds require separate methodology. For example, heap
+  // regions do not go out of scope at the end of a stack frame, so
+  // in order to detect those type of dangling sources the function
+  // needs to be expanded to an event-driven approach as well.
+  if (const auto *StackSpace =
+          Source->getMemorySpaceAs<StackSpaceRegion>(State)) {
+    const StackFrame *SF = StackSpace->getStackFrame();
+    const StackFrame *CurrentSF = C.getStackFrame();
+    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
+      return true;
+  }
+  return false;
+}
+
+void UseAfterLifetimeEnd::checkReturnedBorrower(SVal Val, ProgramStateRef 
State,
+                                                CheckerContext &C) const {
+  if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
+    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+      for (const MemRegion *Region : *SourceSet) {
+        if (hasDanglingSource(Region, State, C))
+          reportDanglingSource(Region, N, C);
+      }
+    }
+  }
+}
+
+void UseAfterLifetimeEnd::checkEndFunction(const ReturnStmt *RS,
+                                           CheckerContext &C) const {
+  if (!RS)
+    return;
+
+  ProgramStateRef State = C.getState();
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  const Expr *RetExpr = RS->getRetValue();
+  if (!RetExpr)
+    return;
+
+  RetExpr = RetExpr->IgnoreParens();
+  SVal RetVal = C.getSVal(RetExpr);
+  checkReturnedBorrower(RetVal, State, C);
+}
+
+void UseAfterLifetimeEnd::reportDanglingBorrower(
+    const LifetimeSourceSet *Sources, CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  ExplodedNode *N = C.generateNonFatalErrorNode();
+  if (!N)
+    return;
+
+  for (const MemRegion *Source : *Sources) {
+    if (State->contains<DeallocatedSourceSet>(Source)) {
+      reportUseAfterScope(Source, N, C);
+    }
+  }
+}
+
+void UseAfterLifetimeEnd::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+                                        CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  // FIXME: If a borrower has multiple bound sources the callback
+  // warns if any of the sources have died. PathDiagnosticVisitor
+  // should be used to trace and identify which annotated parameter
+  // recorded the binding. Attaching this information as path notes
+  // would make the diagnostics more useful to the user.
+  if (auto *SourceSet = State->get<LifetimeBoundMap>(Loc))
+    reportDanglingBorrower(SourceSet, C);
+}
+
+void UseAfterLifetimeEnd::reportDanglingSource(const MemRegion *Region,
+                                               ExplodedNode *N,
+                                               CheckerContext &C) const {
+  auto BR = std::make_unique<PathSensitiveBugReport>(
+      BugMsg,
+      (llvm::Twine("Returning value bound to '") + Region->getString() +
+       "' that will go out of scope"),
+      N);
+  C.emitReport(std::move(BR));
+}
+
+void UseAfterLifetimeEnd::reportUseAfterScope(const MemRegion *Region,
+                                              ExplodedNode *N,
+                                              CheckerContext &C) const {
+  auto BR = std::make_unique<PathSensitiveBugReport>(
+      BugMsg,
+      (llvm::Twine("Use of '") + Region->getString() +
+       "' after its lifetime ended."),
+      N);
+  C.emitReport(std::move(BR));
+}
+
+void UseAfterLifetimeEnd::checkDeadSymbols(SymbolReaper &SymReaper,
+                                           CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
+
+  DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
+
+  for (SVal Val : llvm::make_first_range(LBMap)) {
+    if (const MemRegion *ValRegion = Val.getAsRegion()) {
+      if (!SymReaper.isLiveRegion(ValRegion))
+        State = State->remove<LifetimeBoundMap>(Val);
+    } else if (SymbolRef ValRef =
+                   Val.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+      if (!SymReaper.isLive(ValRef))
+        State = State->remove<LifetimeBoundMap>(Val);
+    }
+  }
+
+  for (const MemRegion *Region : Sources) {
+    if (!SymReaper.isLiveRegion(Region))
+      State = State->remove<DeallocatedSourceSet>(Region);
+  }
+
+  C.addTransition(State);
+}
+
+void UseAfterLifetimeEnd::printState(raw_ostream &Out, ProgramStateRef State,
+                                     const char *NL, const char *Sep) const {
+  auto LBMap = State->get<LifetimeBoundMap>();
+
+  if (LBMap.isEmpty())
+    return;
+
+  Out << Sep << "LifetimeBound bindings:" << NL;
+  for (auto &&[OriginSym, SourceSet] : LBMap) {
+    for (const auto *Region : SourceSet)
+      Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+  }
+}
+
+namespace {
+class DebugUseAfterLifetimeEnd : public Checker<eval::Call> {
+public:
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+  void analyzerDumpLifetimeOriginsOf(const CallEvent &Call,
+                                     CheckerContext &C) const;
+
+  const BugType BugMsg{this, "DebugUseAfterLifetimeEnd",
+                       "DebugUseAfterLifetimeEnd"};
+  using FnCheck = void (DebugUseAfterLifetimeEnd::*)(const CallEvent &Call,
+                                                     CheckerContext &C) const;
+
+  const CallDescriptionMap<FnCheck> Callbacks = {
+      {{CDM::SimpleFunc, {"clang_analyzer_dumpLifetimeOriginsOf"}},
+       &DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf},
+  };
+};
+
+} // namespace
+
+bool DebugUseAfterLifetimeEnd::evalCall(const CallEvent &Call,
+                                        CheckerContext &C) const {
+  const auto *CE = dyn_cast_if_present<CallExpr>(Call.getOriginExpr());
+  if (!CE)
+    return false;
+
+  const FnCheck *Handler = Callbacks.lookup(Call);
+  if (!Handler)
+    return false;
+
+  (this->*(*Handler))(Call, C);
+  return true;
+}
+
+void DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf(
+    const CallEvent &Call, CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  if (Call.getNumArgs() != 1) {
+    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+      auto BR = std::make_unique<PathSensitiveBugReport>(
+          BugMsg,
+          "clang_analyzer_dumpLifetimeOriginsOf requires exactly 1 argument",
+          N);
+      C.emitReport(std::move(BR));
+    }
+    return;
+  }
+
+  SVal ArgSVal = Call.getArgSVal(0);
+  const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
+
+  llvm::SmallString<128> Str;
+  llvm::raw_svector_ostream OS(Str);
+  OS << " Origin " << ArgSVal << " bound to ";
+
+  if (!SourceSet)
+    return;
+
+  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+    bool First = true;
+    for (const MemRegion *Region : *SourceSet) {
+      if (!First)
+        OS << ", ";
+      OS << Region;
+      First = false;
+    }
+    C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg, OS.str(), 
N));
+  }
+}
+
+void ento::registerUseAfterLifetimeEnd(CheckerManager &Mgr) {
+  Mgr.registerChecker<UseAfterLifetimeEnd>();
+}
+
+bool ento::shouldRegisterUseAfterLifetimeEnd(const CheckerManager &Mgr) {
+  return true;
+}
+
+void ento::registerDebugUseAfterLifetimeEnd(CheckerManager &Mgr) {
+  Mgr.registerChecker<DebugUseAfterLifetimeEnd>();
+}
+
+bool ento::shouldRegisterDebugUseAfterLifetimeEnd(const CheckerManager &Mgr) {
+  return true;
+}
diff --git a/clang/test/Analysis/debug-lifetime-bound.cpp 
b/clang/test/Analysis/debug-lifetime-bound.cpp
index e62c51ae6bc53..8ef704195dcc6 100644
--- a/clang/test/Analysis/debug-lifetime-bound.cpp
+++ b/clang/test/Analysis/debug-lifetime-bound.cpp
@@ -1,10 +1,11 @@
-// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.LifetimeAnnotations,debug.DebugLifetimeAnnotations
 -verify %s
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugUseAfterLifetimeEnd
 -verify %s
 
 // expected-no-diagnostics
 
-void clang_analyzer_lifetime_bound(int);
+void clang_analyzer_dumpLifetimeOriginsOf(int);
 
 void test() {
   int x = 5;
-  clang_analyzer_lifetime_bound(x); // no-warning: verifies debug checker does 
not crash standalone
+  clang_analyzer_dumpLifetimeOriginsOf(x); // no-warning
 }
+
diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index ca97419b63e53..1f870a94293cf 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -1,18 +1,18 @@
-// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.LifetimeAnnotations,debug.DebugLifetimeAnnotations
 \
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugUseAfterLifetimeEnd
 \
 // RUN:   -analyzer-config cfg-lifetime=true -verify %s
-// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.LifetimeAnnotations,debug.DebugLifetimeAnnotations
 \
+// RUN: %clang_analyze_cc1 
-analyzer-checker=core,alpha.cplusplus.UseAfterLifetimeEnd,debug.DebugUseAfterLifetimeEnd
 \
 // RUN:   -analyzer-config c++-container-inlining=false -analyzer-config 
cfg-lifetime=true -verify %s
 
 struct A {};
 
-void clang_analyzer_lifetime_bound(int*);
-void clang_analyzer_lifetime_bound(int&);
-void clang_analyzer_lifetime_bound(A*);
-void clang_analyzer_lifetime_bound(A&);
+void clang_analyzer_dumpLifetimeOriginsOf(int*);
+void clang_analyzer_dumpLifetimeOriginsOf(int&);
+void clang_analyzer_dumpLifetimeOriginsOf(A*);
+void clang_analyzer_dumpLifetimeOriginsOf(A&);
 
 // These are the cases when the result of function calls are MemRegions.
 
-// Ref type parameter annotated case
+// Ref type parameter annotated case.
 struct X {
   int& choose(int& a [[clang::lifetimebound]]) { return a; }
 };
@@ -21,10 +21,10 @@ void caller() {
   int v = 0;
   X obj;
   int& r = obj.choose(v);
-  clang_analyzer_lifetime_bound(r); // expected-warning {{Origin &v bound to 
v}}
+  clang_analyzer_dumpLifetimeOriginsOf(r); // expected-warning {{Origin &v 
bound to v}}
 }
 
-// Obj ref type function return annotated case
+// Obj ref type function return annotated case.
 struct Y {
   A a;
   A& getA() [[clang::lifetimebound]] { return a; }
@@ -34,10 +34,10 @@ void caller_two() {
   // Return statement is annotated case.
   Y y;
   A& f = y.getA();
-  clang_analyzer_lifetime_bound(f); // expected-warning {{Origin &y.a bound to 
y}}
+  clang_analyzer_dumpLifetimeOriginsOf(f); // expected-warning {{Origin &y.a 
bound to y}}
 }
 
-// Obj ptr type function return annotated case
+// Obj ptr type function return annotated case.
 struct Z {
   A a;
   A* getA() [[clang::lifetimebound]] { return &a; }
@@ -46,19 +46,19 @@ struct Z {
 void caller_three() {
   Z z;
   A* func = z.getA();
-  clang_analyzer_lifetime_bound(func); // expected-warning {{Origin &z.a bound 
to z}}
+  clang_analyzer_dumpLifetimeOriginsOf(func); // expected-warning {{Origin 
&z.a bound to z}}
 }
 
-// Free function with annotated param and ref return
+// Free function with annotated param and ref return.
 int& foo(int& num [[clang::lifetimebound]]) { return num; }
 
 void caller_four() {
   int num = 5;
   int& s = foo(num);
-  clang_analyzer_lifetime_bound(s); // expected-warning {{Origin &num bound to 
num}}
+  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &num 
bound to num}}
 }
 
-// Free function with annotated param and ptr return
+// Free function with annotated param and ptr return.
 int* boo(int* num [[clang::lifetimebound]]) { return num; }
 
 void caller_five() {
@@ -66,7 +66,7 @@ void caller_five() {
   int* n_ptr = &n;
   int* s = boo(n_ptr);
 
-  clang_analyzer_lifetime_bound(s); // expected-warning {{Origin &n bound to 
n}}
+  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &n 
bound to n}}
 }
 
 // Free function with both annotated and non-annotated parameters.
@@ -77,14 +77,14 @@ void caller_six() {
   int odd = 55;
   int& s = fn(even, odd);
 
-  clang_analyzer_lifetime_bound(s); // expected-warning {{Origin &odd bound to 
odd}}
+  clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &odd 
bound to odd}}
 }
 
 
 
 // These are the cases when the result of function calls are SymbolRefs.
 
-// Function returns ptr and has an annotated parameter
+// Function returns ptr and has an annotated parameter.
 int* foo(int* n [[clang::lifetimebound]]);
 
 void caller_seven() {
@@ -92,17 +92,17 @@ void caller_seven() {
   int* y_ptr = &y;
   auto* bind = foo(y_ptr);
 
-  clang_analyzer_lifetime_bound(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to y}}
+  clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to y}}
 }
 
-// Function returns a reference and has an annotated parameter
+// Function returns a reference and has an annotated parameter.
 int& func(int& some_number [[clang::lifetimebound]]);
 
 void caller_eight() {
   int f = 15;
   auto& bind = func(f);
 
-  clang_analyzer_lifetime_bound(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to f}}
+  clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to f}}
 }
 
 // Function returns a reference and has two annotated parameters.
@@ -113,9 +113,7 @@ void caller_nine() {
   int second_num = 2;
   int& numbers = f(first_num, second_num);
 
-  clang_analyzer_lifetime_bound(numbers);
-  // expected-warning-re@-1 {{Origin &SymRegion{{.*}} bound to first_num}}
-  // expected-warning-re@-2 {{Origin &SymRegion{{.*}} bound to second_num}}
+  clang_analyzer_dumpLifetimeOriginsOf(numbers); // expected-warning-re 
{{Origin &SymRegion{{.*}} bound to first_num, second_num}}
 }
 
 struct View {
@@ -123,20 +121,20 @@ struct View {
 };
 View makeView(int& x [[clang::lifetimebound]]);
 
-void clang_analyzer_lifetime_bound(View);
+void clang_analyzer_dumpLifetimeOriginsOf(View);
 
 void caller_view() {
   int v = 42;
   View w = makeView(v);
-  // FIXME: Currently none of the maps cover LazyCompoundVal
-  clang_analyzer_lifetime_bound(w); // no-warning
+  // FIXME: Currently none of the maps cover LazyCompoundVal.
+  clang_analyzer_dumpLifetimeOriginsOf(w); // no-warning
 }
 
 
 
-// These are the test cases for testing the correctness of the emitted warning 
from the LifetimeAnnotations checker.
+// These are the test cases for testing the correctness of the emitted warning 
from the UseAfterLifetimeEnd checker.
 
-// Return value bound to annotated param cases
+// Return value bound to annotated param cases.
 int *test_func(int *p [[clang::lifetimebound]]);
 
 
@@ -169,3 +167,4 @@ int* g() {
   (void)p;
   return nullptr; // no-warning
 }
+

>From 3d04217388bb20497786031a9c38964b24a25bca Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Thu, 25 Jun 2026 15:25:21 +0200
Subject: [PATCH 3/6] Removed dead codes.

---
 .../Checkers/LifetimeAnnotations.cpp          | 305 ------------------
 .../Checkers/UseAfterLifetimeEnd.cpp          |  81 +----
 2 files changed, 12 insertions(+), 374 deletions(-)
 delete mode 100644 clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp
deleted file mode 100644
index e25d076dd0bd5..0000000000000
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeAnnotations.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
-#include "clang/AST/Attrs.inc"
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
-#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "llvm/Support/raw_ostream.h"
-
-using namespace clang;
-using namespace ento;
-
-REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
-REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
-
-REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *)
-
-namespace {
-class LifetimeAnnotations
-    : public Checker<check::PostCall, check::EndFunction, check::Location,
-                     check::DeadSymbols> {
-public:
-  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
-                  const char *Sep) const override;
-  void reportDanglingSource(const MemRegion *Region, ExplodedNode *N,
-                            CheckerContext &C) const;
-  void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N,
-                           CheckerContext &C) const;
-
-  void checkReturnedBorrower(SVal Val, ProgramStateRef State,
-                             CheckerContext &C) const;
-  void reportDanglingBorrower(const LifetimeSourceSet *Sources,
-                              CheckerContext &C) const;
-  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
-  void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
-                     CheckerContext &C) const;
-  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
-
-  const BugType BugMsg{this, "LifetimeAnnotations", "LifetimeBound"};
-};
-
-} // namespace
-
-static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal,
-                                  const MemRegion *Source) {
-  LifetimeSourceSet::Factory &F =
-      State->getStateManager().get_context<LifetimeSourceSet>();
-
-  const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal);
-  LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet();
-  Set = F.add(Set, Source);
-  State = State->set<LifetimeBoundMap>(RetVal, Set);
-  return State;
-}
-
-void LifetimeAnnotations::checkPostCall(const CallEvent &Call,
-                                        CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-
-  const auto *FC = dyn_cast<AnyFunctionCall>(&Call);
-  if (!FC)
-    return;
-
-  const FunctionDecl *FD = FC->getDecl();
-  if (!FD)
-    return;
-
-  SVal RetVal = Call.getReturnValue();
-
-  for (const ParmVarDecl *PVD : FD->parameters()) {
-    if (PVD->hasAttr<LifetimeBoundAttr>()) {
-      unsigned Idx = PVD->getFunctionScopeIndex();
-      SVal Arg = Call.getArgSVal(Idx);
-      if (const MemRegion *ArgValRegion = Arg.getAsRegion())
-        State = bindValues(State, RetVal, ArgValRegion);
-    }
-  }
-
-  if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) {
-    if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) {
-      if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) {
-        State = bindValues(State, RetVal, AttrRegion);
-      }
-    }
-  }
-  C.addTransition(State);
-}
-
-static bool hasDanglingSource(const MemRegion *Source, ProgramStateRef State,
-                              CheckerContext &C) {
-  // FIXME: The checker currently handles stack-region sources. Other
-  // region kinds require separate methodology. For example, heap
-  // regions do not go out of scope at the end of a stack frame, so
-  // in order to detect those type of dangling sources the function
-  // needs to be expanded to an event-driven approach as well.
-  if (const auto *StackSpace =
-          Source->getMemorySpaceAs<StackSpaceRegion>(State)) {
-    const StackFrame *SF = StackSpace->getStackFrame();
-    const StackFrame *CurrentSF = C.getStackFrame();
-    if (SF == CurrentSF || !SF->isParentOf(CurrentSF))
-      return true;
-  }
-  return false;
-}
-
-void LifetimeAnnotations::checkReturnedBorrower(SVal Val, ProgramStateRef 
State,
-                                                CheckerContext &C) const {
-  if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
-    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
-      for (const MemRegion *Region : *SourceSet) {
-        if (hasDanglingSource(Region, State, C))
-          reportDanglingSource(Region, N, C);
-      }
-    }
-  }
-}
-
-void LifetimeAnnotations::checkEndFunction(const ReturnStmt *RS,
-                                           CheckerContext &C) const {
-  if (!RS)
-    return;
-
-  ProgramStateRef State = C.getState();
-  auto LBMap = State->get<LifetimeBoundMap>();
-
-  if (LBMap.isEmpty())
-    return;
-
-  const Expr *RetExpr = RS->getRetValue();
-  if (!RetExpr)
-    return;
-
-  RetExpr = RetExpr->IgnoreParens();
-  SVal RetVal = C.getSVal(RetExpr);
-  checkReturnedBorrower(RetVal, State, C);
-}
-
-void LifetimeAnnotations::reportDanglingBorrower(
-    const LifetimeSourceSet *Sources, CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-
-  ExplodedNode *N = C.generateNonFatalErrorNode();
-  if (!N)
-    return;
-
-  for (const MemRegion *Source : *Sources) {
-    if (State->contains<DeallocatedSourceSet>(Source)) {
-      reportUseAfterScope(Source, N, C);
-    }
-  }
-}
-
-void LifetimeAnnotations::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
-                                        CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-  auto LBMap = State->get<LifetimeBoundMap>();
-
-  if (LBMap.isEmpty())
-    return;
-
-  // FIXME: If a borrower has multiple bound sources the callback
-  // warns if any of the sources have died. PathDiagnosticVisitor
-  // should be used to trace and identify which annotated parameter
-  // recorded the binding. Attaching this information as path notes
-  // would make the diagnostics more useful to the user.
-  if (auto *SourceSet = State->get<LifetimeBoundMap>(Loc))
-    reportDanglingBorrower(SourceSet, C);
-}
-
-void LifetimeAnnotations::reportDanglingSource(const MemRegion *Region,
-                                               ExplodedNode *N,
-                                               CheckerContext &C) const {
-  auto BR = std::make_unique<PathSensitiveBugReport>(
-      BugMsg,
-      (llvm::Twine("Returning value bound to '") + Region->getString() +
-       "' that will go out of scope")
-          .str(),
-      N);
-  C.emitReport(std::move(BR));
-}
-
-void LifetimeAnnotations::reportUseAfterScope(const MemRegion *Region,
-                                              ExplodedNode *N,
-                                              CheckerContext &C) const {
-  auto BR = std::make_unique<PathSensitiveBugReport>(
-      BugMsg,
-      (llvm::Twine("Use of '") + Region->getString() +
-       "' after its lifetime ended.")
-          .str(),
-      N);
-  C.emitReport(std::move(BR));
-}
-
-void LifetimeAnnotations::checkDeadSymbols(SymbolReaper &SymReaper,
-                                           CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-  LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
-
-  DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
-
-  for (SVal Val : llvm::make_first_range(LBMap)) {
-    if (const MemRegion *ValRegion = Val.getAsRegion()) {
-      if (!SymReaper.isLiveRegion(ValRegion))
-        State = State->remove<LifetimeBoundMap>(Val);
-    } else if (SymbolRef ValRef =
-                   Val.getAsSymbol(/*IncludeBaseRegions=*/true)) {
-      if (!SymReaper.isLive(ValRef))
-        State = State->remove<LifetimeBoundMap>(Val);
-    }
-  }
-
-  for (const MemRegion *Region : Sources) {
-    if (!SymReaper.isLiveRegion(Region))
-      State = State->remove<DeallocatedSourceSet>(Region);
-  }
-
-  C.addTransition(State);
-}
-
-void LifetimeAnnotations::printState(raw_ostream &Out, ProgramStateRef State,
-                                     const char *NL, const char *Sep) const {
-  auto LBMap = State->get<LifetimeBoundMap>();
-
-  if (LBMap.isEmpty())
-    return;
-
-  Out << Sep << "LifetimeBound bindings:" << NL;
-  for (auto &&[OriginSym, SourceSet] : LBMap) {
-    for (const auto *Region : SourceSet)
-      Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
-  }
-}
-
-namespace {
-class DebugLifetimeAnnotations : public Checker<eval::Call> {
-public:
-  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
-  void analyzerLifetimeBound(const CallEvent &Call, CheckerContext &C) const;
-
-  const BugType BugMsg{this, "DebugLifetimeAnnotations", "DebugLifetimeBound"};
-  using FnCheck = void (DebugLifetimeAnnotations::*)(const CallEvent &Call,
-                                                     CheckerContext &C) const;
-
-  const CallDescriptionMap<FnCheck> Callbacks = {
-      {{CDM::SimpleFunc, {"clang_analyzer_lifetime_bound"}},
-       &DebugLifetimeAnnotations::analyzerLifetimeBound},
-  };
-};
-
-} // namespace
-
-bool DebugLifetimeAnnotations::evalCall(const CallEvent &Call,
-                                        CheckerContext &C) const {
-
-  const auto *CE = dyn_cast_if_present<CallExpr>(Call.getOriginExpr());
-  if (!CE)
-    return false;
-
-  const FnCheck *Handler = Callbacks.lookup(Call);
-  if (!Handler)
-    return false;
-
-  (this->*(*Handler))(Call, C);
-  return true;
-}
-
-void DebugLifetimeAnnotations::analyzerLifetimeBound(const CallEvent &Call,
-                                                     CheckerContext &C) const {
-
-  ProgramStateRef State = C.getState();
-  unsigned int ArgCount = Call.getNumArgs();
-  if (ArgCount != 1)
-    return;
-
-  SVal ArgSVal = Call.getArgSVal(0);
-
-  if (auto *SourceSet = State->get<LifetimeBoundMap>(ArgSVal)) {
-    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
-      for (const auto *Region : *SourceSet) {
-        llvm::SmallString<128> Str;
-        llvm::raw_svector_ostream OS(Str);
-        OS << " Origin " << ArgSVal << " bound to " << Region;
-        auto BR = std::make_unique<PathSensitiveBugReport>(BugMsg, OS.str(), 
N);
-        C.emitReport(std::move(BR));
-      }
-    }
-  }
-}
-
-void ento::registerLifetimeAnnotations(CheckerManager &mgr) {
-  mgr.registerChecker<LifetimeAnnotations>();
-}
-
-bool ento::shouldRegisterLifetimeAnnotations(const CheckerManager &mgr) {
-  return true;
-}
-
-void ento::registerDebugLifetimeAnnotations(CheckerManager &mgr) {
-  mgr.registerChecker<DebugLifetimeAnnotations>();
-}
-
-bool ento::shouldRegisterDebugLifetimeAnnotations(const CheckerManager &mgr) {
-  return true;
-}
diff --git a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp 
b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
index 81a7aabd6f7a4..1f11becd39046 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
@@ -13,30 +13,19 @@ using namespace ento;
 REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
 REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
 
-REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *)
-
 namespace {
 class UseAfterLifetimeEnd
-    : public Checker<check::PostCall, check::EndFunction, check::Location,
-                     check::DeadSymbols> {
+    : public Checker<check::PostCall, check::EndFunction, check::DeadSymbols> {
 public:
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
                   const char *Sep) const override;
   void reportDanglingSource(const MemRegion *Region, ExplodedNode *N,
                             CheckerContext &C) const;
-  void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N,
-                           CheckerContext &C) const;
-
   void checkReturnedBorrower(SVal Val, ProgramStateRef State,
                              CheckerContext &C) const;
-  void reportDanglingBorrower(const LifetimeSourceSet *Sources,
-                              CheckerContext &C) const;
   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
-  void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
-                     CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
-
   const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
 };
 
@@ -106,10 +95,14 @@ static bool hasDanglingSource(const MemRegion *Source, 
ProgramStateRef State,
 void UseAfterLifetimeEnd::checkReturnedBorrower(SVal Val, ProgramStateRef 
State,
                                                 CheckerContext &C) const {
   if (auto *SourceSet = State->get<LifetimeBoundMap>(Val)) {
-    if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
-      for (const MemRegion *Region : *SourceSet) {
-        if (hasDanglingSource(Region, State, C))
-          reportDanglingSource(Region, N, C);
+    ExplodedNode *N = nullptr;
+    for (const MemRegion *Region : *SourceSet) {
+      if (hasDanglingSource(Region, State, C)) {
+        if (!N)
+          N = C.generateNonFatalErrorNode();
+        if (!N)
+          return;
+        reportDanglingSource(Region, N, C);
       }
     }
   }
@@ -135,38 +128,6 @@ void UseAfterLifetimeEnd::checkEndFunction(const 
ReturnStmt *RS,
   checkReturnedBorrower(RetVal, State, C);
 }
 
-void UseAfterLifetimeEnd::reportDanglingBorrower(
-    const LifetimeSourceSet *Sources, CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-
-  ExplodedNode *N = C.generateNonFatalErrorNode();
-  if (!N)
-    return;
-
-  for (const MemRegion *Source : *Sources) {
-    if (State->contains<DeallocatedSourceSet>(Source)) {
-      reportUseAfterScope(Source, N, C);
-    }
-  }
-}
-
-void UseAfterLifetimeEnd::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
-                                        CheckerContext &C) const {
-  ProgramStateRef State = C.getState();
-  auto LBMap = State->get<LifetimeBoundMap>();
-
-  if (LBMap.isEmpty())
-    return;
-
-  // FIXME: If a borrower has multiple bound sources the callback
-  // warns if any of the sources have died. PathDiagnosticVisitor
-  // should be used to trace and identify which annotated parameter
-  // recorded the binding. Attaching this information as path notes
-  // would make the diagnostics more useful to the user.
-  if (auto *SourceSet = State->get<LifetimeBoundMap>(Loc))
-    reportDanglingBorrower(SourceSet, C);
-}
-
 void UseAfterLifetimeEnd::reportDanglingSource(const MemRegion *Region,
                                                ExplodedNode *N,
                                                CheckerContext &C) const {
@@ -178,24 +139,11 @@ void UseAfterLifetimeEnd::reportDanglingSource(const 
MemRegion *Region,
   C.emitReport(std::move(BR));
 }
 
-void UseAfterLifetimeEnd::reportUseAfterScope(const MemRegion *Region,
-                                              ExplodedNode *N,
-                                              CheckerContext &C) const {
-  auto BR = std::make_unique<PathSensitiveBugReport>(
-      BugMsg,
-      (llvm::Twine("Use of '") + Region->getString() +
-       "' after its lifetime ended."),
-      N);
-  C.emitReport(std::move(BR));
-}
-
 void UseAfterLifetimeEnd::checkDeadSymbols(SymbolReaper &SymReaper,
                                            CheckerContext &C) const {
   ProgramStateRef State = C.getState();
   LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
 
-  DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
-
   for (SVal Val : llvm::make_first_range(LBMap)) {
     if (const MemRegion *ValRegion = Val.getAsRegion()) {
       if (!SymReaper.isLiveRegion(ValRegion))
@@ -207,11 +155,6 @@ void UseAfterLifetimeEnd::checkDeadSymbols(SymbolReaper 
&SymReaper,
     }
   }
 
-  for (const MemRegion *Region : Sources) {
-    if (!SymReaper.isLiveRegion(Region))
-      State = State->remove<DeallocatedSourceSet>(Region);
-  }
-
   C.addTransition(State);
 }
 
@@ -281,13 +224,13 @@ void 
DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf(
   SVal ArgSVal = Call.getArgSVal(0);
   const LifetimeSourceSet *SourceSet = State->get<LifetimeBoundMap>(ArgSVal);
 
+  if (!SourceSet)
+    return;
+
   llvm::SmallString<128> Str;
   llvm::raw_svector_ostream OS(Str);
   OS << " Origin " << ArgSVal << " bound to ";
 
-  if (!SourceSet)
-    return;
-
   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
     bool First = true;
     for (const MemRegion *Region : *SourceSet) {

>From 8b106907b2d8b305f76e05a9d635dbbe92f338dc Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Thu, 25 Jun 2026 16:24:18 +0200
Subject: [PATCH 4/6] Corrected HelpText in the DebugUseAfterLifetimeEnd
 checker.

---
 clang/include/clang/StaticAnalyzer/Checkers/Checkers.td | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 2c59fddc89ca1..b565481d28fdb 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1582,8 +1582,8 @@ def CheckerDocumentationChecker : 
Checker<"CheckerDocumentation">,
   Documentation<NotDocumented>;
 
 def DebugUseAfterLifetimeEnd : Checker<"DebugUseAfterLifetimeEnd">,
-  HelpText<"Prints the bindings recorded by the LifetimeAnnotations checker. "
-           "Use with clang_analyzer_lifetime_bound().">,
+  HelpText<"Prints the bindings recorded by the UseAfterLifetimeEnd checker. "
+           "Use with clang_analyzer_dumpLifetimeOriginsOf().">,
   WeakDependencies<[UseAfterLifetimeEnd]>,
   Documentation<NotDocumented>;
 

>From 5317b14b7a56c77b8087e8ccfd9237bfcae273ee Mon Sep 17 00:00:00 2001
From: Benedek Kaibas <[email protected]>
Date: Fri, 26 Jun 2026 12:56:03 +0200
Subject: [PATCH 5/6] Sort the elements of the `SourceSet`, so their order
 becomes deterministic.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <[email protected]>
---
 .../Checkers/UseAfterLifetimeEnd.cpp          | 20 +++++++++----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp 
b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
index 1f11becd39046..db776f1c1c49d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UseAfterLifetimeEnd.cpp
@@ -227,18 +227,16 @@ void 
DebugUseAfterLifetimeEnd::analyzerDumpLifetimeOriginsOf(
   if (!SourceSet)
     return;
 
-  llvm::SmallString<128> Str;
-  llvm::raw_svector_ostream OS(Str);
-  OS << " Origin " << ArgSVal << " bound to ";
-
   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
-    bool First = true;
-    for (const MemRegion *Region : *SourceSet) {
-      if (!First)
-        OS << ", ";
-      OS << Region;
-      First = false;
-    }
+    llvm::SmallVector<std::string> RegionNames =
+        to_vector(map_range(llvm::make_pointee_range(*SourceSet),
+                            std::mem_fn(&MemRegion::getString)));
+    llvm::sort(RegionNames);
+
+    llvm::SmallString<128> Str;
+    llvm::raw_svector_ostream OS(Str);
+    OS << " Origin " << ArgSVal << " bound to ";
+    llvm::interleaveComma(RegionNames, OS);
     C.emitReport(std::make_unique<PathSensitiveBugReport>(BugMsg, OS.str(), 
N));
   }
 }

>From d8b158e9aa5c34955d999c6bb1607e09d8c0dc7a Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 26 Jun 2026 14:06:13 +0200
Subject: [PATCH 6/6] Added test cases to test checkReturnedBorrower.

---
 clang/test/Analysis/lifetime-bound.cpp | 97 +++++++++++++++++++-------
 1 file changed, 73 insertions(+), 24 deletions(-)

diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index 1f870a94293cf..e99299b5d03bf 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -14,68 +14,68 @@ void clang_analyzer_dumpLifetimeOriginsOf(A&);
 
 // Ref type parameter annotated case.
 struct X {
-  int& choose(int& a [[clang::lifetimebound]]) { return a; }
+  int &choose(int &a [[clang::lifetimebound]]) { return a; }
 };
 
 void caller() {
   int v = 0;
   X obj;
-  int& r = obj.choose(v);
+  int &r = obj.choose(v);
   clang_analyzer_dumpLifetimeOriginsOf(r); // expected-warning {{Origin &v 
bound to v}}
 }
 
 // Obj ref type function return annotated case.
 struct Y {
   A a;
-  A& getA() [[clang::lifetimebound]] { return a; }
+  A &getA() [[clang::lifetimebound]] { return a; }
 };
 
 void caller_two() {
   // Return statement is annotated case.
   Y y;
-  A& f = y.getA();
+  A &f = y.getA();
   clang_analyzer_dumpLifetimeOriginsOf(f); // expected-warning {{Origin &y.a 
bound to y}}
 }
 
 // Obj ptr type function return annotated case.
 struct Z {
   A a;
-  A* getA() [[clang::lifetimebound]] { return &a; }
+  A *getA() [[clang::lifetimebound]] { return &a; }
 };
 
 void caller_three() {
   Z z;
-  A* func = z.getA();
+  A *func = z.getA();
   clang_analyzer_dumpLifetimeOriginsOf(func); // expected-warning {{Origin 
&z.a bound to z}}
 }
 
 // Free function with annotated param and ref return.
-int& foo(int& num [[clang::lifetimebound]]) { return num; }
+int &foo(int &num [[clang::lifetimebound]]) { return num; }
 
 void caller_four() {
   int num = 5;
-  int& s = foo(num);
+  int &s = foo(num);
   clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &num 
bound to num}}
 }
 
 // Free function with annotated param and ptr return.
-int* boo(int* num [[clang::lifetimebound]]) { return num; }
+int *boo(int *num [[clang::lifetimebound]]) { return num; }
 
 void caller_five() {
   int n = 55;
-  int* n_ptr = &n;
-  int* s = boo(n_ptr);
+  int *n_ptr = &n;
+  int *s = boo(n_ptr);
 
   clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &n 
bound to n}}
 }
 
 // Free function with both annotated and non-annotated parameters.
-int& fn(int& f, int& s [[clang::lifetimebound]]) { return s; }
+int &fn(int &f, int &s [[clang::lifetimebound]]) { return s; }
 
 void caller_six() {
   int even = 50;
   int odd = 55;
-  int& s = fn(even, odd);
+  int &s = fn(even, odd);
 
   clang_analyzer_dumpLifetimeOriginsOf(s); // expected-warning {{Origin &odd 
bound to odd}}
 }
@@ -85,41 +85,41 @@ void caller_six() {
 // These are the cases when the result of function calls are SymbolRefs.
 
 // Function returns ptr and has an annotated parameter.
-int* foo(int* n [[clang::lifetimebound]]);
+int *foo(int *n [[clang::lifetimebound]]);
 
 void caller_seven() {
   int y = 15;
-  int* y_ptr = &y;
-  auto* bind = foo(y_ptr);
+  int *y_ptr = &y;
+  auto *bind = foo(y_ptr);
 
   clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to y}}
 }
 
 // Function returns a reference and has an annotated parameter.
-int& func(int& some_number [[clang::lifetimebound]]);
+int &func(int &some_number [[clang::lifetimebound]]);
 
 void caller_eight() {
   int f = 15;
-  auto& bind = func(f);
+  auto &bind = func(f);
 
   clang_analyzer_dumpLifetimeOriginsOf(bind); // expected-warning-re {{Origin 
&SymRegion{{.*}} bound to f}}
 }
 
 // Function returns a reference and has two annotated parameters.
-int& f(int& a [[clang::lifetimebound]], int& b [[clang::lifetimebound]]);
+int &f(int &a [[clang::lifetimebound]], int &b [[clang::lifetimebound]]);
 
 void caller_nine() {
   int first_num = 1;
   int second_num = 2;
-  int& numbers = f(first_num, second_num);
+  int &numbers = f(first_num, second_num);
 
   clang_analyzer_dumpLifetimeOriginsOf(numbers); // expected-warning-re 
{{Origin &SymRegion{{.*}} bound to first_num, second_num}}
 }
 
 struct View {
-  int* p;
+  int *p;
 };
-View makeView(int& x [[clang::lifetimebound]]);
+View makeView(int &x [[clang::lifetimebound]]);
 
 void clang_analyzer_dumpLifetimeOriginsOf(View);
 
@@ -161,10 +161,59 @@ void no_return() {
   (void)p; // no-warning
 }
 
-int* g() {
+int *g() {
   int i = 5;
-  int* p = test_func(&i);
+  int *p = test_func(&i);
   (void)p;
   return nullptr; // no-warning
 }
 
+int &multi_param_test_ref(int &a [[clang::lifetimebound]], int &b 
[[clang::lifetimebound]]);
+
+// Return value bound to annotated parameters (two dangling sources).
+int &dangling_sources_ref() {
+  int x = 1, y = 2;
+  return multi_param_test_ref(x, y);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-2 {{Returning value bound to 'y' that will go out of 
scope}}
+  // expected-warning@-3 {{reference to stack memory associated with local 
variable 'x' returned}}
+  // expected-warning@-4 {{reference to stack memory associated with local 
variable 'y' returned}}
+}
+
+// Return value bound to annotated parameters (no dangling sources).
+int &no_dangling_sources_ref(int &a [[clang::lifetimebound]], int &b 
[[clang::lifetimebound]]) {
+  return multi_param_test_ref(a, b); // no-warning
+}
+
+// Return value bound to annotated parameters (one dangling source).
+int &one_dangling_source_ref(int &a [[clang::lifetimebound]]) {
+  int x = 1;
+  return multi_param_test_ref(a, x);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-2 {{reference to stack memory associated with local 
variable 'x' returned}}
+}
+
+int *multi_param_test_ptr(int *a [[clang::lifetimebound]], int *b 
[[clang::lifetimebound]]);
+
+// Return value bound to annotated parameters (two dangling sources).
+int *dangling_sources_ptr() {
+  int x = 1, y = 2;
+  int *x_ptr = &x;
+  int *y_ptr = &y;
+  return multi_param_test_ptr(x_ptr, y_ptr);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-2 {{Returning value bound to 'y' that will go out of 
scope}}
+}
+
+// Return value bound to annotated parameters (no dangling sources).
+int *no_dangling_sources_ptr(int *a [[clang::lifetimebound]], int *b 
[[clang::lifetimebound]]) {
+  return multi_param_test_ptr(a, b); // no-warning
+}
+
+// Return value bound to annotated parameters (one dangling source).
+int *one_dangling_source_ptr(int *a [[clang::lifetimebound]]) {
+  int x = 1;
+  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}}
+}
+

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to