[clang] [LifetimeSafety] Trace assignment history for use-after-scope errors (PR #188467)

Yuan Suo via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 14 06:04:09 PDT 2026


https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/188467

>From 6ad8e7b80cd63b09d48f2df3bd2f84f3e3871d72 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Mon, 16 Mar 2026 09:55:36 +0800
Subject: [PATCH 01/22] [LifetimeSafety] Trace assignment history for
 use-after-scope errors

Currently, when the compiler detects a use-after-scope error, it only emits notes for the point of use, the destruction point, and the initial declaration of the destroyed variable. In complex control flows, it can be difficult for users to figure out how the dangling pointer/reference reached the use point.

This patch introduces reverse tracing at the CFGBlock corresponding to UseFact->getUseExpr, using BFS to trace the assignment history of dangling variables. It adds extra comments to the assignment statements, clearly showing the event chain that led to the use-after-scope error.

Fixes #177986

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  13 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   1 +
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 212 +++++++++-
 clang/lib/Sema/SemaLifetimeSafety.h           |  47 +++
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |  85 ++--
 .../Sema/warn-lifetime-safety-cfg-bailout.cpp |   4 +-
 .../Sema/warn-lifetime-safety-suggestions.cpp |   9 +-
 clang/test/Sema/warn-lifetime-safety.cpp      | 369 ++++++++++++------
 8 files changed, 576 insertions(+), 164 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 83c3c455c4c81..d3762a739682f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -46,6 +46,11 @@ enum class SuggestionScope {
   IntraTU  // For suggestions on definitions local to a Translation Unit.
 };
 
+using OriginSrcExpr =
+    llvm::PointerUnion<const DeclRefExpr *, const CXXTemporaryObjectExpr *,
+                       const CallExpr *>;
+using AssignmentPair = std::pair<OriginSrcExpr, const ValueDecl *>;
+
 /// Abstract interface for operations requiring Sema access.
 ///
 /// This class exists to break a circular dependency: the LifetimeSafety
@@ -61,9 +66,11 @@ class LifetimeSafetySemaHelper {
   LifetimeSafetySemaHelper() = default;
   virtual ~LifetimeSafetySemaHelper() = default;
 
-  virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
-                                  const Expr *MovedExpr,
-                                  SourceLocation FreeLoc) {}
+  virtual void
+  reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
+                     const Expr *MovedExpr,
+                     const llvm::SmallVector<AssignmentPair> AliasList,
+                     SourceLocation FreeLoc) {}
 
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4cd4efc55c416..041fad8249115 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11010,6 +11010,7 @@ def note_lifetime_safety_dangling_static_here: Note<"this static storage dangles
 def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
 def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global storage">;
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">;
+def note_lifetime_safety_note_alias_chain : Note<"variable `%0` is now an alias of `%1`">;
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index fe103739dbcb0..bfb2a718a4bcc 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -55,6 +55,13 @@ using AnnotationTarget =
     llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
 using EscapingTarget = LifetimeSafetySemaHelper::EscapingTarget;
 
+struct AliasAssignmentSearchResult {
+  const llvm::SmallVector<AssignmentPair> Payload;
+  bool SearchComplete;
+  const ValueDecl *LastDestDecl;
+  std::optional<OriginID> LastOrigin;
+};
+
 class LifetimeChecker {
 private:
   llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
@@ -67,6 +74,7 @@ class LifetimeChecker {
   LifetimeSafetySemaHelper *SemaHelper;
   ASTContext &AST;
   const Decl *FD;
+  AnalysisDeclContext &ADC;
 
   static SourceLocation
   GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) {
@@ -89,7 +97,7 @@ class LifetimeChecker {
                   LifetimeSafetySemaHelper *SemaHelper)
       : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans),
         LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper),
-        AST(ADC.getASTContext()), FD(ADC.getDecl()) {
+        AST(ADC.getASTContext()), FD(ADC.getDecl()), ADC(ADC) {
     for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
       for (const Fact *F : FactMgr.getFacts(B))
         if (const auto *EF = F->getAs<ExpireFact>())
@@ -224,6 +232,194 @@ class LifetimeChecker {
     }
   }
 
+  std::optional<llvm::SmallVector<AssignmentPair>>
+  getAliasListInMultiBlock(const CFGBlock *StartBlock, const LoanID EndLoanID,
+                           OriginID *StartOID) {
+    const ValueDecl *LastDestDecl = nullptr;
+    llvm::SmallVector<const CFGBlock *> PendingBlocks;
+    std::optional<AssignmentPair> StartStmt = std::nullopt;
+    std::optional<AssignmentPair> EndStmt = std::nullopt;
+    std::optional<OriginID> LastOriginID = std::nullopt;
+    llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
+    llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs;
+
+    const auto AliasStmtFilter = [&VistedExprs](const AssignmentPair StartStmt,
+                                                const AssignmentPair EndStmt) {
+      llvm::SmallVector<AssignmentPair> AliasStmts;
+      for (auto Stmt = StartStmt; Stmt != EndStmt;
+           Stmt = VistedExprs.at(Stmt)) {
+        AliasStmts.push_back(Stmt);
+      }
+      AliasStmts.push_back(EndStmt);
+      return AliasStmts;
+    };
+
+    PendingBlocks.push_back(StartBlock);
+
+    for (size_t i = 0; i < PendingBlocks.size(); ++i) {
+      const CFGBlock *CurrBlock = PendingBlocks[i];
+
+      const auto [BlockAliasList, Success, CurrLastDestDecl, CurrLastOriginID] =
+          getAliasListCore(CurrBlock, EndLoanID, StartOID, LastDestDecl,
+                           LastOriginID);
+      if (CurrLastDestDecl)
+        LastDestDecl = CurrLastDestDecl;
+      if (CurrLastOriginID.has_value())
+        LastOriginID = CurrLastOriginID;
+
+      if (!BlockAliasList.empty()) {
+        if (VistedExprs.empty()) {
+          StartStmt = BlockAliasList[0];
+        }
+
+        for (size_t i = 0; i < BlockAliasList.size() - 1; ++i) {
+          VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]});
+        }
+
+        if (EndStmt.has_value())
+          VistedExprs.insert({EndStmt.value(), BlockAliasList[0]});
+
+        EndStmt = BlockAliasList[BlockAliasList.size() - 1];
+      }
+
+      if (Success && StartStmt.has_value() && EndStmt.has_value()) {
+        return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+      }
+
+      for (const auto Block : CurrBlock->preds()) {
+        if (Block && VistedBlocks.insert(Block).second)
+          PendingBlocks.push_back(Block);
+      }
+
+      if (VistedBlocks.size() >= 32 && StartStmt.has_value() &&
+          EndStmt.has_value()) {
+        return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+      }
+    }
+
+    if (StartStmt.has_value() && EndStmt.has_value()) {
+      return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+    }
+
+    return std::nullopt;
+  }
+
+  std::optional<OriginSrcExpr> GetPureSrcExpr(const Expr *TargetExpr) {
+    if (!TargetExpr)
+      return std::nullopt;
+    const Expr *SExpr = TargetExpr->IgnoreParenCasts();
+    if (!SExpr)
+      return std::nullopt;
+
+    if (const auto *SDRExpr = llvm::dyn_cast<DeclRefExpr>(SExpr)) {
+      return SDRExpr;
+    }
+    if (const auto *STMExpr = llvm::dyn_cast<CXXTemporaryObjectExpr>(SExpr)) {
+      return STMExpr;
+    }
+    if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr)) {
+      return SCExpr;
+    }
+
+    if (const auto *SCCExpr = llvm::dyn_cast<CXXConstructExpr>(SExpr)) {
+      if (SCCExpr->getNumArgs() > 0)
+        return GetPureSrcExpr(SCCExpr->getArg(0));
+    }
+    if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr)) {
+      return GetPureSrcExpr(SUOExpr->getSubExpr());
+    }
+    if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr)) {
+      return GetPureSrcExpr(SCBExpr->getSubExpr());
+    }
+
+    return std::nullopt;
+  }
+
+  /// Retrieves a list of assignment chains for use-after-scope analysis.
+  ///
+  /// To help users understand the data flow, we track where the problematic
+  /// address originated.
+  AliasAssignmentSearchResult
+  getAliasListCore(const CFGBlock *Block, const LoanID EndLoanID,
+                   OriginID *TargetOID, const ValueDecl *LastDestDecl = nullptr,
+                   const std::optional<OriginID> LastOriginID = std::nullopt) {
+    llvm::SmallVector<AssignmentPair> AliasStmts;
+    const ValueDecl *DestDecl = LastDestDecl;
+    const auto Facts = FactMgr.getFacts(Block);
+    bool FetchLoan = false;
+    auto IssueOriginID = LastOriginID;
+
+    for (auto F = Facts.rbegin(); F != Facts.rend(); ++F) {
+      if (const auto *OFF = (*F)->getAs<OriginFlowFact>()) {
+        if (IssueOriginID.has_value() &&
+            OFF->getDestOriginID() == IssueOriginID.value()) {
+          FetchLoan = true;
+        }
+        if (OFF->getDestOriginID() == *TargetOID) {
+          const auto HeldLoans =
+              LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
+
+          if (HeldLoans.contains(EndLoanID)) {
+            const auto TargetOrigin =
+                FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
+
+            if (DestDecl == nullptr) {
+              if (const ValueDecl *DDecl = TargetOrigin.getDecl()) {
+                DestDecl = DDecl;
+              }
+            } else {
+              auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
+              if (!SExpr.has_value()) {
+                const auto SrcOrigin =
+                    FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID());
+                SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
+              }
+
+              if (SExpr.has_value()) {
+                AliasStmts.push_back({SExpr.value(), DestDecl});
+                DestDecl = nullptr;
+              }
+            }
+            *TargetOID = OFF->getSrcOriginID();
+          }
+        }
+      } else if (const auto *IF = (*F)->getAs<IssueFact>()) {
+        if (IF->getLoanID() == EndLoanID) {
+          IssueOriginID = IF->getOriginID();
+        }
+      }
+
+      if (FetchLoan) {
+        return {AliasStmts, true, DestDecl, IssueOriginID};
+      }
+    }
+    return {AliasStmts, false, DestDecl, IssueOriginID};
+  }
+
+  std::optional<llvm::SmallVector<AssignmentPair>>
+  getAliasList(const UseFact *UF, const LoanID End, const bool InOneBlock) {
+    const CFGBlock *IssueBlock =
+        ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
+    assert(IssueBlock && "Searching CFGBlock failed");
+
+    for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+         Cur = Cur->peelOuterOrigin()) {
+      auto TargetOID = Cur->getOuterOriginID();
+      if (InOneBlock) {
+        AliasAssignmentSearchResult Result =
+            getAliasListCore(IssueBlock, End, &TargetOID);
+        if (!Result.Payload.empty())
+          return Result.Payload;
+      } else {
+        auto Result = getAliasListInMultiBlock(IssueBlock, End, &TargetOID);
+        if (Result.has_value())
+          return Result.value();
+      }
+    }
+
+    return std::nullopt;
+  }
+
   void issuePendingWarnings() {
     if (!SemaHelper)
       return;
@@ -248,10 +444,20 @@ class LifetimeChecker {
             SemaHelper->reportUseAfterInvalidation(
                 InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr);
 
-        } else
+        } else {
           // Scope-based expiry (use-after-scope).
+          const CFGStmtMap *CurrCFGStmtMap = ADC.getCFGStmtMap();
+          const auto AliasExprs =
+              getAliasList(UF, LID,
+                           CurrCFGStmtMap->getBlock(UF->getUseExpr()) ==
+                               CurrCFGStmtMap->getBlock(IssueExpr));
+          if (!AliasExprs.has_value()) {
+            llvm::dbgs() << "Search variable assignment chain failed\n";
+          }
+
           SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
-                                         ExpiryLoc);
+                                         AliasExprs.value_or({}), ExpiryLoc);
+        }
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
         if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index c56a9692abe1a..5d4872f3a38f8 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -45,6 +45,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
 
   void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
                           const Expr *MovedExpr,
+                          const llvm::SmallVector<AssignmentPair> AliasList,
                           SourceLocation FreeLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved
@@ -54,6 +55,52 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
+
+    for (auto AliasStmt = AliasList.rbegin(); AliasStmt != AliasList.rend();
+         ++AliasStmt) {
+      if (const auto *CurrDeclExpr =
+              llvm::dyn_cast<const DeclRefExpr *>((*AliasStmt).first)) {
+        S.Diag(CurrDeclExpr->getExprLoc(),
+               diag::note_lifetime_safety_note_alias_chain)
+            << (*AliasStmt).second->getNameAsString()
+            << CurrDeclExpr->getDecl()->getNameAsString();
+      } else if (const auto *CurrDeclExpr =
+                     llvm::dyn_cast<const CXXTemporaryObjectExpr *>(
+                         (*AliasStmt).first)) {
+        S.Diag(CurrDeclExpr->getExprLoc(),
+               diag::note_lifetime_safety_note_alias_chain)
+            << (*AliasStmt).second->getNameAsString()
+            << CurrDeclExpr->getConstructor()->getNameAsString() + "()";
+      } else if (const auto *CurrCallExpr =
+                     llvm::dyn_cast<const CallExpr *>((*AliasStmt).first)) {
+        std::string OutStr;
+        llvm::raw_string_ostream OutStream(OutStr);
+        LangOptions Lo;
+        PrintingPolicy Policy(Lo);
+
+        if (const Expr *Callee = CurrCallExpr->getCallee()) {
+          Callee->IgnoreParenCasts()->printPretty(OutStream, nullptr, Policy);
+        }
+
+        OutStream << "(";
+        for (size_t i = 0; i < CurrCallExpr->getNumArgs(); ++i) {
+          const Expr *CurrArg = CurrCallExpr->getArg(i);
+          if (CurrArg) {
+            CurrArg->printPretty(OutStream, nullptr, Policy);
+          }
+
+          if (i < CurrCallExpr->getNumArgs() - 1) {
+            OutStream << ", ";
+          }
+        }
+        OutStream << ")";
+
+        S.Diag(CurrCallExpr->getExprLoc(),
+               diag::note_lifetime_safety_note_alias_chain)
+            << (*AliasStmt).second->getNameAsString() << OutStream.str();
+      }
+    }
+
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
         << UseExpr->getSourceRange();
   }
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 2b6c2d3d8bdb6..acef9844a91c0 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -145,14 +145,16 @@ MyLongPointerFromConversion global2;
 
 void initLocalGslPtrWithTempOwner() {
   MyIntPointer p = MyIntOwner{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                 // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                 // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                 // cfg-note {{variable `p` is now an alias of `MyIntOwner()`}}
   use(p);                        // cfg-note {{later used here}}
 
   MyIntPointer pp = p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' will be}} \
-                                      // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                      // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                      // cfg-note {{variable `p` is now an alias of `MyIntOwner()`}}
   use(p, pp);                         // cfg-note {{later used here}}
 
-  p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} \
+  p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} cfg-note {{variable `p` is now an alias of `MyIntOwner()`}} \
                     // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(p);           // cfg-note {{later used here}}
 
@@ -160,16 +162,20 @@ void initLocalGslPtrWithTempOwner() {
   use(p, pp);
 
   global = MyIntOwner{}; // expected-warning {{object backing the pointer 'global' }} \
+                         // cfg-note {{variable `global` is now an alias of `MyIntOwner()`}} \
                          // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(global);           // cfg-note {{later used here}}
 
   MyLongPointerFromConversion p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                                                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                                                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                                // cfg-note {{variable `p2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}}
   use(p2);                                                      // cfg-note {{later used here}}
 
   p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer 'p2' }} \
+                                    // cfg-note {{variable `p2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} \
                                     // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   global2 = MyLongOwnerWithConversion{};  // expected-warning {{object backing the pointer 'global2' }} \
+                                          // cfg-note {{variable `global2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} \
                                           // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(global2, p2);                       // cfg-note 2 {{later used here}}
 }
@@ -183,6 +189,7 @@ struct Unannotated {
 
 void modelIterators() {
   std::vector<int>::iterator it = std::vector<int>().begin(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                                              // cfg-note {{variable `it` is now an alias of `std::vector<int>().begin()`}} \
                                                               // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   (void)it; // cfg-note {{later used here}}
 }
@@ -231,11 +238,13 @@ int &danglingRawPtrFromLocal3() {
 // GH100384
 std::string_view containerWithAnnotatedElements() {
   std::string_view c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                                          // cfg-note {{variable `c1` is now an alias of `std::vector<std::string>().at(0).operator basic_string_view()`}} \
                                                           // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(c1);                                                // cfg-note {{later used here}}
 
   c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}} \
-                                         // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                         // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                         // cfg-note {{variable `c1` is now an alias of `std::vector<std::string>().at(0).operator basic_string_view()`}}
   use(c1);                               // cfg-note {{later used here}}
 
   // no warning on constructing from gsl-pointer
@@ -296,22 +305,28 @@ std::string_view danglingRefToOptionalFromTemp4() {
 
 void danglingReferenceFromTempOwner() {
   int &&r = *std::optional<int>();          // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                            // cfg-note {{variable `r` is now an alias of `operator*(std::optional<int>())`}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   // https://github.com/llvm/llvm-project/issues/175893
   int &&r2 = *std::optional<int>(5);        // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                              // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                            // cfg-note {{variable `r2` is now an alias of `operator*(std::optional<int>(5))`}} \
+                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   // https://github.com/llvm/llvm-project/issues/175893
   int &&r3 = std::optional<int>(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                              // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                            // cfg-note {{variable `r3` is now an alias of `std::optional<int>(5).value()`}} \
+                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   const int &r4 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                            // cfg-note {{variable `r4` is now an alias of `std::vector<int>().at(3)`}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   int &&r5 = std::vector<int>().at(3);      // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                            // cfg-note {{variable `r5` is now an alias of `std::vector<int>().at(3)`}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(r, r2, r3, r4, r5);                   // cfg-note 5 {{later used here}}
 
   std::string_view sv = *getTempOptStr();  // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                           // cfg-note {{variable `sv` is now an alias of `* getTempOptStr().operator basic_string_view()`}} \
                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(sv);                                 // cfg-note {{later used here}}
 }
@@ -323,6 +338,7 @@ void testLoops() {
   for (auto i : getTempVec()) // ok
     ;
   for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                  // cfg-note {{variable `__range1` is now an alias of `operator*(getTempOptVec())`}} \
                                   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}}
     ;
 }
@@ -385,6 +401,7 @@ void handleGslPtrInitsThroughReference2() {
 void handleTernaryOperator(bool cond) {
     std::basic_string<char> def;
     std::basic_string_view<char> v = cond ? def : ""; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
+                                                      // cfg-note {{variable `v` is now an alias of `cond ? def : "".operator basic_string_view()`}} \
                                                       // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
     use(v); // cfg-note {{later used here}}
 }
@@ -392,11 +409,13 @@ void handleTernaryOperator(bool cond) {
 std::string operator+(std::string_view s1, std::string_view s2);
 void danglingStringviewAssignment(std::string_view a1, std::string_view a2) {
   a1 = std::string(); // expected-warning {{object backing}} \
-                      // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                      // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                      // cfg-note {{variable `a1` is now an alias of `std::string().operator basic_string_view()`}}
   use(a1);            // cfg-note {{later used here}}
 
   a2 = a1 + a1; // expected-warning {{object backing}} \
-                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                // cfg-note {{variable `a2` is now an alias of `a1 + a1.operator basic_string_view()`}}
   use(a2);      // cfg-note {{later used here}}
 }
 
@@ -600,7 +619,8 @@ std::string_view ReturnStringView(std::string_view abc [[clang::lifetimebound]])
 
 void test() {
   std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                                                  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                                                  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                                  // cfg-note {{variable `svjkk1` is now an alias of `ReturnStringView(StrCat("bar", "x"))`}}
   use(svjkk1);                                                    // cfg-note {{later used here}}
 }
 } // namespace GH100549
@@ -834,7 +854,8 @@ namespace GH118064{
 
 void test() {
   auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}} \
-  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+  // cfg-note {{variable `y` is now an alias of `std::set<int>{}.begin()`}}
   use(y); // cfg-note {{later used here}}
 }
 } // namespace GH118064
@@ -849,10 +870,12 @@ std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
 
 std::string_view test1_1() {
   std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} \
-                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                            // cfg-note {{variable `t1` is now an alias of `Ref(std::string()).operator basic_string_view()`}}
   use(t1);                                  // cfg-note {{later used here}}
   t1 = Ref(std::string()); // expected-warning {{object backing}} \
-                           // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                           // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                           // cfg-note {{variable `t1` is now an alias of `Ref(std::string()).operator basic_string_view()`}}
   use(t1);                 // cfg-note {{later used here}}
   return Ref(std::string()); // expected-warning {{returning address}} \
                              // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
@@ -860,10 +883,12 @@ std::string_view test1_1() {
 
 std::string_view test1_2() {
   std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
-                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                            // cfg-note {{variable `t2` is now an alias of `TakeSv(std::string())`}}
   use(t2);                                  // cfg-note {{later used here}}
   t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
-                              // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                              // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                              // cfg-note {{variable `t2` is now an alias of `TakeSv(std::string())`}}
   use(t2);                    // cfg-note {{later used here}}
 
   return TakeSv(std::string()); // expected-warning {{returning address}} \
@@ -872,9 +897,11 @@ std::string_view test1_2() {
 
 std::string_view test1_3() {
   std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \
+                                                   // cfg-note {{variable `t3` is now an alias of `TakeStrRef(std::string())`}} \
                                                    // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(t3);                                         // cfg-note {{later used here}}
   t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \
+                                  // cfg-note {{variable `t3` is now an alias of `TakeStrRef(std::string())`}} \
                                   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(t3);                        // cfg-note {{later used here}}
   return TakeStrRef(std::string()); // expected-warning {{returning address}} \
@@ -897,10 +924,12 @@ struct Foo {
 };
 std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
   std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
+                                                  // cfg-note {{variable `t1` is now an alias of `Foo<std::string>().get().operator basic_string_view()`}} \
                                                   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(t1);                                        // cfg-note {{later used here}}
   t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
-                                 // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                 // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                 // cfg-note {{variable `t1` is now an alias of `Foo<std::string>().get().operator basic_string_view()`}}
   use(t1);                       // cfg-note {{later used here}}
   return r1.get(); // expected-warning {{address of stack}} \
                    // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
@@ -1018,9 +1047,12 @@ void operator_star_arrow_reference() {
   const std::string& r = *v.begin();
 
   auto temporary = []() { return std::vector<std::string>{{"1"}}; };
-  const char* x = temporary().begin()->data();    // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
-  const char* y = (*temporary().begin()).data();  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
-  const std::string& z = (*temporary().begin());  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  const char* x = temporary().begin()->data();    // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                  // cfg-note {{variable `x` is now an alias of `temporary().begin()->data()`}}
+  const char* y = (*temporary().begin()).data();  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                  // cfg-note {{variable `y` is now an alias of `(* temporary().begin()).data()`}}
+  const std::string& z = (*temporary().begin());  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                  // cfg-note {{variable `z` is now an alias of `operator*(temporary().begin())`}}
 
   use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
 }
@@ -1032,9 +1064,12 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
   const std::string& r = (*v.begin()).second;
 
   auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, "1"}}; };
-  const char* x = temporary().begin()->second.data();   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
-  const char* y = (*temporary().begin()).second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
-  const std::string& z = (*temporary().begin()).second; // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  const char* x = temporary().begin()->second.data();   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                        // cfg-note {{variable `x` is now an alias of `temporary().begin()->second.data()`}}
+  const char* y = (*temporary().begin()).second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                        // cfg-note {{variable `y` is now an alias of `(* temporary().begin()).second.data()`}}
+  const std::string& z = (*temporary().begin()).second; // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                        // cfg-note {{variable `z` is now an alias of `operator*(temporary().begin())`}}
 
   use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
 }
@@ -1084,16 +1119,20 @@ std::string_view foo(std::string_view sv [[clang::lifetimebound]]);
 void test1() {
   std::string_view k1 = S().sv; // OK
   std::string_view k2 = S().s; // expected-warning {{object backing the pointer will}} \
+                               // cfg-note {{variable `k2` is now an alias of `S().s.operator basic_string_view()`}} \
                                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   std::string_view k3 = Q().get()->sv; // OK
   std::string_view k4  = Q().get()->s; // expected-warning {{object backing the pointer will}} \
-                                       // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+                                       // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                       // cfg-note {{variable `k4` is now an alias of `Q().get()->s.operator basic_string_view()`}}
 
 
   std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \
+                                     // cfg-note {{variable `lb1` is now an alias of `foo(S().s)`}} \
                                      // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \
+                                            // cfg-note {{variable `lb2` is now an alias of `foo(Q().get()->s)`}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   use(k1, k2, k3, k4, lb1, lb2);  // cfg-note 4 {{later used here}}
diff --git a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
index 7c5d61e23e710..51cd8a6e5b349 100644
--- a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
@@ -27,7 +27,7 @@ void single_block_cfg() {
   MyObj* p;
   {
     MyObj s;
-    p = &s;     // bailout-warning {{object whose reference is captured does not live long enough}}
+    p = &s;     // bailout-warning {{object whose reference is captured does not live long enough}} bailout-note {{variable `p` is now an alias of `s`}}
   }             // bailout-note {{destroyed here}}
   (void)*p;     // bailout-note {{later used here}}
 }
@@ -39,7 +39,7 @@ void multiple_block_cfg() {
   {
     if (a > 5) {
       MyObj s;
-      p = &s;    // nobailout-warning {{object whose reference is captured does not live long enough}}
+      p = &s;    // nobailout-warning {{object whose reference is captured does not live long enough}} nobailout-note {{variable `p` is now an alias of `s`}}
     } else {     // nobailout-note {{destroyed here}}
       p = &safe;
     }     
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index d2cf1c175eb57..14d04c63215db 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -221,19 +221,22 @@ View return_view_field(const ViewProvider& v) {    // expected-warning {{paramet
 
 void test_get_on_temporary_pointer() {
   const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{object whose reference is captured does not live long enough}}.
-                                                   // expected-note at -1 {{destroyed here}}
+                                                   // expected-note at -1 {{destroyed here}}.
+                                                   // expected-note at -2 {{variable `s_ref` is now an alias of `ReturnsSelf().get()`}}
   (void)s_ref;                                     // expected-note {{later used here}}
 }
 
 void test_get_on_temporary_ref() {
   const ReturnsSelf& s_ref = ReturnsSelf().get();  // expected-warning {{object whose reference is captured does not live long enough}}.
-                                                   // expected-note at -1 {{destroyed here}}
+                                                   // expected-note at -1 {{destroyed here}}.
+                                                   // expected-note at -2 {{variable `s_ref` is now an alias of `ReturnsSelf().get()`}}
   (void)s_ref;                                     // expected-note {{later used here}}
 }
 
 void test_getView_on_temporary() {
   View sv = ViewProvider{1}.getView();      // expected-warning {{object whose reference is captured does not live long enough}}.
-                                            // expected-note at -1 {{destroyed here}}
+                                            // expected-note at -1 {{destroyed here}}.
+                                            // expected-note at -2 {{variable `sv` is now an alias of `ViewProvider{1}.getView()`}}
   (void)sv;                                 // expected-note {{later used here}}
 }
 
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index c083c30f5855d..38744e08f49db 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -54,7 +54,8 @@ void simple_case() {
   MyObj* p;
   {
     MyObj s;
-    p = &s;     // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &s;     // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -63,7 +64,8 @@ void simple_case_gsl() {
   View v;
   {
     MyObj s;
-    v = s;      // expected-warning {{object whose reference is captured does not live long enough}}
+    v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -91,8 +93,9 @@ void pointer_chain() {
   MyObj* q;
   {
     MyObj s;
-    p = &s;     // expected-warning {{does not live long enough}}
-    q = p;
+    p = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `s`}}
+    q = p;      // expected-note {{variable `q` is now an alias of `p`}}
   }             // expected-note {{destroyed here}}
   (void)*q;     // expected-note {{later used here}}
 }
@@ -101,8 +104,9 @@ void propagation_gsl() {
   View v1, v2;
   {
     MyObj s;
-    v1 = s;     // expected-warning {{object whose reference is captured does not live long enough}}
-    v2 = v1;
+    v1 = s;     // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v1` is now an alias of `s`}}
+    v2 = v1;    // expected-note {{`v2` is now an alias of `v1`}}
   }             // expected-note {{destroyed here}}
   v2.use();     // expected-note {{later used here}}
 }
@@ -111,7 +115,8 @@ void multiple_uses_one_warning() {
   MyObj* p;
   {
     MyObj s;
-    p = &s;     // expected-warning {{does not live long enough}}
+    p = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   // No second warning for the same loan.
@@ -124,9 +129,12 @@ void multiple_pointers() {
   MyObj *p, *q, *r;
   {
     MyObj s;
-    p = &s;     // expected-warning {{does not live long enough}}
-    q = &s;     // expected-warning {{does not live long enough}}
-    r = &s;     // expected-warning {{does not live long enough}}
+    p = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `s`}}
+    q = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `q` is now an alias of `s`}}
+    r = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `r` is now an alias of `s`}}
   }             // expected-note 3 {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   (void)*q;     // expected-note {{later used here}}
@@ -137,11 +145,13 @@ void single_pointer_multiple_loans(bool cond) {
   MyObj *p;
   if (cond){
     MyObj s;
-    p = &s;     // expected-warning {{does not live long enough}}
+    p = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{`p` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   else {
     MyObj t;
-    p = &t;     // expected-warning {{does not live long enough}}
+    p = &t;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `t`}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note 2  {{later used here}}
 }
@@ -150,11 +160,13 @@ void single_pointer_multiple_loans_gsl(bool cond) {
   View v;
   if (cond){
     MyObj s;
-    v = s;      // expected-warning {{object whose reference is captured does not live long enough}}
+    v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   else {
     MyObj t;
-    v = t;      // expected-warning {{object whose reference is captured does not live long enough}}
+    v = t;      // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `t`}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note 2 {{later used here}}
 }
@@ -164,7 +176,8 @@ void if_branch(bool cond) {
   MyObj* p = &safe;
   if (cond) {
     MyObj temp;
-    p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `temp`}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -174,7 +187,8 @@ void if_branch_potential(bool cond) {
   MyObj* p = &safe;
   if (cond) {
     MyObj temp;
-    p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `temp`}}
   }             // expected-note {{destroyed here}}
   if (!cond)
     (void)*p;   // expected-note {{later used here}}
@@ -187,7 +201,8 @@ void if_branch_gsl(bool cond) {
   View v = safe;
   if (cond) {
     MyObj temp;
-    v = temp;   // expected-warning {{object whose reference is captured does not live long enough}}
+    v = temp;   // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `temp`}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -200,9 +215,11 @@ void potential_together(bool cond) {
   {
     MyObj s;
     if (cond)
-      p_definite = &s;  // expected-warning {{does not live long enough}}
+      p_definite = &s;  // expected-warning {{does not live long enough}} \
+                        // expected-note {{variable `p_definite` is now an alias of `s`}}
     if (cond)
-      p_maybe = &s;     // expected-warning {{does not live long enough}}         
+      p_maybe = &s;     // expected-warning {{does not live long enough}} \
+                        // expected-note {{variable `p_maybe` is now an alias of `s`}}
   }                     // expected-note 2 {{destroyed here}}
   (void)*p_definite;    // expected-note {{later used here}}
   if (!cond)
@@ -215,8 +232,9 @@ void overrides_potential(bool cond) {
   MyObj* q;
   {
     MyObj s;
-    q = &s;       // expected-warning {{does not live long enough}}
-    p = q;
+    q = &s;       // expected-warning {{does not live long enough}} \
+                  // expected-note {{variable `q` is now an alias of `s`}}
+    p = q;        // expected-note {{variable `p` is now an alias of `q`}}
   }               // expected-note {{destroyed here}}
 
   if (cond) {
@@ -235,7 +253,8 @@ void due_to_conditional_killing(bool cond) {
   MyObj* q;
   {
     MyObj s;
-    q = &s;       // expected-warning {{does not live long enough}}
+    q = &s;       // expected-warning {{does not live long enough}} \
+                  // expected-note {{variable `q` is now an alias of `s`}}
   }               // expected-note {{destroyed here}}
   if (cond) {
     // 'q' is conditionally "rescued". 'p' is not.
@@ -248,7 +267,8 @@ void for_loop_use_after_loop_body(MyObj safe) {
   MyObj* p = &safe;
   for (int i = 0; i < 1; ++i) {
     MyObj s;
-    p = &s;     // expected-warning {{does not live long enough}}
+    p = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -268,7 +288,8 @@ void for_loop_gsl() {
   View v = safe;
   for (int i = 0; i < 1; ++i) {
     MyObj s;
-    v = s;      // expected-warning {{object whose reference is captured does not live long enough}}
+    v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -279,7 +300,8 @@ void for_loop_use_before_loop_body(MyObj safe) {
   for (int i = 0; i < 1; ++i) {
     (void)*p;   // expected-note {{later used here}}
     MyObj s;
-    p = &s;     // expected-warning {{does not live long enough}}
+    p = &s;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   (void)*p;
 }
@@ -290,7 +312,8 @@ void loop_with_break(bool cond) {
   for (int i = 0; i < 10; ++i) {
     if (cond) {
       MyObj temp;
-      p = &temp; // expected-warning {{does not live long enough}}
+      p = &temp; // expected-warning {{does not live long enough}} \
+                 // expected-note {{variable `p` is now an alias of `temp`}}
       break;     // expected-note {{destroyed here}}
     }           
   } 
@@ -303,7 +326,8 @@ void loop_with_break_gsl(bool cond) {
   for (int i = 0; i < 10; ++i) {
     if (cond) {
       MyObj temp;
-      v = temp;   // expected-warning {{object whose reference is captured does not live long enough}}
+      v = temp;   // expected-warning {{object whose reference is captured does not live long enough}} \
+                  // expected-note {{variable `v` is now an alias of `temp`}}
       break;      // expected-note {{destroyed here}}
     }
   }
@@ -317,8 +341,9 @@ void multiple_expiry_of_same_loan(bool cond) {
   for (int i = 0; i < 10; ++i) {
     MyObj unsafe;
     if (cond) {
-      p = &unsafe; // expected-warning {{does not live long enough}}
-      break;       // expected-note {{destroyed here}} 
+      p = &unsafe; // expected-warning {{does not live long enough}} \
+                   // expected-note {{variable `p` is now an alias of `unsafe`}}
+      break;       // expected-note {{destroyed here}}
     }
   }
   (void)*p;       // expected-note {{later used here}}
@@ -327,7 +352,8 @@ void multiple_expiry_of_same_loan(bool cond) {
   for (int i = 0; i < 10; ++i) {
     MyObj unsafe;
     if (cond) {
-      p = &unsafe;    // expected-warning {{does not live long enough}}
+      p = &unsafe;    // expected-warning {{does not live long enough}} \
+                      // expected-note {{variable `p` is now an alias of `unsafe`}}
       if (cond)
         break;        // expected-note {{destroyed here}}
     }
@@ -338,7 +364,8 @@ void multiple_expiry_of_same_loan(bool cond) {
   for (int i = 0; i < 10; ++i) {
     if (cond) {
       MyObj unsafe2;
-      p = &unsafe2;   // expected-warning {{does not live long enough}}
+      p = &unsafe2;   // expected-warning {{does not live long enough}} \
+                      // expected-note {{variable `p` is now an alias of `unsafe2`}}
       break;          // expected-note {{destroyed here}}
     }
   }
@@ -348,7 +375,8 @@ void multiple_expiry_of_same_loan(bool cond) {
   for (int i = 0; i < 10; ++i) {
     MyObj unsafe;
     if (cond)
-      p = &unsafe;    // expected-warning {{does not live long enough}}
+      p = &unsafe;    // expected-warning {{does not live long enough}} \
+                      // expected-note {{variable `p` is now an alias of `unsafe`}}
     if (cond)
       break;          // expected-note {{destroyed here}}
   }
@@ -361,7 +389,8 @@ void switch_potential(int mode) {
   switch (mode) {
   case 1: {
     MyObj temp;
-    p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `temp`}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
@@ -380,17 +409,20 @@ void switch_uaf(int mode) {
   switch (mode) {
   case 1: {
     MyObj temp1;
-    p = &temp1; // expected-warning {{does not live long enough}}
+    p = &temp1; // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `temp1`}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
     MyObj temp2;
-    p = &temp2; // expected-warning {{does not live long enough}}
+    p = &temp2; // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `temp2`}}
     break;      // expected-note {{destroyed here}}
   }
   default: {
     MyObj temp2;
-    p = &temp2; // expected-warning {{does not live long enough}}
+    p = &temp2; // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `temp2`}}
     break;      // expected-note {{destroyed here}}
   }
   }
@@ -402,17 +434,20 @@ void switch_gsl(int mode) {
   switch (mode) {
   case 1: {
     MyObj temp1;
-    v = temp1;  // expected-warning {{object whose reference is captured does not live long enough}}
+    v = temp1;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `temp1`}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
     MyObj temp2;
-    v = temp2;  // expected-warning {{object whose reference is captured does not live long enough}}
+    v = temp2;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `temp2`}}
     break;      // expected-note {{destroyed here}}
   }
   default: {
     MyObj temp3;
-    v = temp3;  // expected-warning {{object whose reference is captured does not live long enough}}
+    v = temp3;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `v` is now an alias of `temp3`}}
     break;      // expected-note {{destroyed here}}
   }
   }
@@ -425,7 +460,8 @@ void loan_from_previous_iteration(MyObj safe, bool condition) {
 
   while (condition) {
     MyObj x;
-    p = &x;     // expected-warning {{does not live long enough}}
+    p = &x;     // expected-warning {{does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `x`}}
 
     if (condition)
       q = p;
@@ -438,7 +474,8 @@ void trivial_int_uaf() {
   int * a;
   {
       int b = 1;
-      a = &b;  // expected-warning {{object whose reference is captured does not live long enough}}
+      a = &b;  // expected-warning {{object whose reference is captured does not live long enough}} \
+               // expected-note {{variable `a` is now an alias of `b`}}
   }            // expected-note {{destroyed here}}
   (void)*a;    // expected-note {{later used here}}
 }
@@ -447,7 +484,8 @@ void trivial_class_uaf() {
   TriviallyDestructedClass* ptr;
   {
       TriviallyDestructedClass s;
-      ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}}
+      ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `ptr` is now an alias of `s`}}
   }             // expected-note {{destroyed here}}
   (void)ptr;    // expected-note {{later used here}}
 }
@@ -639,7 +677,8 @@ void test_view_pointer() {
   View* vp;
   {
     View v;
-    vp = &v;     // expected-warning {{object whose reference is captured does not live long enough}}
+    vp = &v;     // expected-warning {{object whose reference is captured does not live long enough}} \
+                 // expected-note {{variable `vp` is now an alias of `v`}}
   }              // expected-note {{destroyed here}}
   vp->use();     // expected-note {{later used here}}
 }
@@ -648,7 +687,8 @@ void test_view_double_pointer() {
   View** vpp;
   {
     View* vp = nullptr;
-    vpp = &vp;   // expected-warning {{object whose reference is captured does not live long enough}}
+    vpp = &vp;   // expected-warning {{object whose reference is captured does not live long enough}} \
+                 // expected-note {{variable `vpp` is now an alias of `vp`}}
   }              // expected-note {{destroyed here}}
   (**vpp).use(); // expected-note {{later used here}}
 }
@@ -674,9 +714,10 @@ void test_lifetimebound_multi_level() {
   int** result;
   {
     int* p = nullptr;
-    int** pp = &p;  
-    int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}}
-    result = return_inner_ptr_addr(ppp);
+    int** pp = &p;
+    int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}} \
+                      // expected-note {{variable `ppp` is now an alias of `pp`}}
+    result = return_inner_ptr_addr(ppp); // expected-note {{variable `result` is now an alias of `return_inner_ptr_addr(ppp)`}}
   }                   // expected-note {{destroyed here}}
   (void)**result;     // expected-note {{used here}}
 }
@@ -708,8 +749,9 @@ int** test_ternary_double_ptr(bool cond) {
 MyObj* uaf_before_uar() {
   MyObj* p;
   {
-    MyObj local_obj; 
-    p = &local_obj;  // expected-warning {{object whose reference is captured does not live long enough}}
+    MyObj local_obj;
+    p = &local_obj;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                     // expected-note {{variable `p` is now an alias of `local_obj`}}
   }                  // expected-note {{destroyed here}}
   return p;          // expected-note {{later used here}}
 }
@@ -799,7 +841,8 @@ void lifetimebound_simple_function() {
   View v;
   {
     MyObj obj;
-    v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+    v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}} \
+                       // expected-note {{variable `v` is now an alias of `Identity(obj)`}}
   }                    // expected-note {{destroyed here}}
   v.use();             // expected-note {{later used here}}
 }
@@ -808,7 +851,8 @@ void lifetimebound_multiple_args_definite() {
   View v;
   {
     MyObj obj1, obj2;
-    v = Choose(true,
+    v = Choose(true,  // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} \
+                      // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}}
                obj1,  // expected-warning {{object whose reference is captured does not live long enough}}
                obj2); // expected-warning {{object whose reference is captured does not live long enough}}
   }                              // expected-note 2 {{destroyed here}}
@@ -822,7 +866,8 @@ void lifetimebound_multiple_args_potential(bool cond) {
     MyObj obj1;
     if (cond) {
       MyObj obj2;
-      v = Choose(true,
+      v = Choose(true,             // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} \
+                                   // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}}
                  obj1,             // expected-warning {{object whose reference is captured does not live long enough}}
                  obj2);            // expected-warning {{object whose reference is captured does not live long enough}}
     }                              // expected-note {{destroyed here}}
@@ -835,7 +880,8 @@ void lifetimebound_mixed_args() {
   View v;
   {
     MyObj obj1, obj2;
-    v = SelectFirst(obj1,        // expected-warning {{object whose reference is captured does not live long enough}}
+    v = SelectFirst(obj1,        // expected-warning {{object whose reference is captured does not live long enough}} \
+                                 // expected-note {{variable `v` is now an alias of `SelectFirst(obj1, obj2)`}}
                     obj2);
   }                              // expected-note {{destroyed here}}
   v.use();                       // expected-note {{later used here}}
@@ -851,7 +897,8 @@ void lifetimebound_member_function() {
   View v;
   {
     MyObj obj;
-    v  = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}}
+    v  = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}} \
+                        // expected-note {{variable `v` is now an alias of `obj.getView()`}}
   }                     // expected-note {{destroyed here}}
   v.use();              // expected-note {{later used here}}
 }
@@ -866,7 +913,8 @@ void lifetimebound_conversion_operator() {
   View v;
   {
     LifetimeBoundConversionView obj;
-    v = obj;  // expected-warning {{object whose reference is captured does not live long enough}}
+    v = obj;  // expected-warning {{object whose reference is captured does not live long enough}} \
+              // expected-note {{variable `v` is now an alias of `obj.operator View()`}}
   }           // expected-note {{destroyed here}}
   v.use();    // expected-note {{later used here}}
 }
@@ -875,7 +923,8 @@ void lifetimebound_chained_calls() {
   View v;
   {
     MyObj obj;
-    v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}}
+    v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                           // expected-note {{variable `v` is now an alias of `Identity(Identity(Identity(obj)))`}}
   }                                        // expected-note {{destroyed here}}
   v.use();                                 // expected-note {{later used here}}
 }
@@ -884,7 +933,8 @@ void lifetimebound_with_pointers() {
   MyObj* ptr;
   {
     MyObj obj;
-    ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+    ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}} \
+                           // expected-note {{variable `ptr` is now an alias of `GetPointer(obj)`}}
   }                        // expected-note {{destroyed here}}
   (void)*ptr;              // expected-note {{later used here}}
 }
@@ -902,7 +952,7 @@ void lifetimebound_partial_safety(bool cond) {
   
   if (cond) {
     MyObj temp_obj;
-    v = Choose(true, 
+    v = Choose(true,       // expected-note {{variable `v` is now an alias of `Choose(true, safe_obj, temp_obj)`}}
                safe_obj,
                temp_obj); // expected-warning {{object whose reference is captured does not live long enough}}
   }                       // expected-note {{destroyed here}}
@@ -915,9 +965,10 @@ void lifetimebound_return_reference() {
   const MyObj* ptr;
   {
     MyObj obj;
-    View temp_v = obj;  // expected-warning {{object whose reference is captured does not live long enough}}
-    const MyObj& ref = GetObject(temp_v);
-    ptr = &ref;
+    View temp_v = obj;     // expected-warning {{object whose reference is captured does not live long enough}} \
+                           // expected-note {{variable `temp_v` is now an alias of `obj`}}
+    const MyObj& ref = GetObject(temp_v); // expected-note {{variable `ref` is now an alias of `GetObject(temp_v)`}}
+    ptr = &ref;           // expected-note {{variable `ptr` is now an alias of `ref`}}
   }                       // expected-note {{destroyed here}}
   (void)*ptr;             // expected-note {{later used here}}
 }
@@ -1007,7 +1058,8 @@ void conditional_operator_one_unsafe_branch(bool cond) {
   MyObj* p = &safe;
   {
     MyObj temp;
-    p = cond ? &temp  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = cond ? &temp  // expected-warning {{object whose reference is captured does not live long enough}} \
+                      // expected-note {{variable `p` is now an alias of `temp`}}
              : &safe;
   }  // expected-note {{destroyed here}}
 
@@ -1023,8 +1075,10 @@ void conditional_operator_two_unsafe_branches(bool cond) {
   MyObj* p;
   {
     MyObj a, b;
-    p = cond ? &a   // expected-warning {{object whose reference is captured does not live long enough}}
-             : &b;  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = cond ? &a   // expected-warning {{object whose reference is captured does not live long enough}} \
+                    // expected-note {{variable `p` is now an alias of `a`}}
+             : &b;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                    // expected-note {{variable `p` is now an alias of `b`}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
 }
@@ -1033,10 +1087,14 @@ void conditional_operator_nested(bool cond) {
   MyObj* p;
   {
     MyObj a, b, c, d;
-    p = cond ? cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}}.
-                    : &b    // expected-warning {{object whose reference is captured does not live long enough}}.
-             : cond ? &c    // expected-warning {{object whose reference is captured does not live long enough}}.
-                    : &d;   // expected-warning {{object whose reference is captured does not live long enough}}.
+    p = cond ? cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}}. \
+                            // expected-note {{variable `p` is now an alias of `a`}}
+                    : &b    // expected-warning {{object whose reference is captured does not live long enough}}. \
+                            // expected-note {{variable `p` is now an alias of `b`}}
+             : cond ? &c    // expected-warning {{object whose reference is captured does not live long enough}}. \
+                            // expected-note {{variable `p` is now an alias of `c`}}
+                    : &d;   // expected-warning {{object whose reference is captured does not live long enough}}. \
+                            // expected-note {{variable `p` is now an alias of `d`}}
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 }
@@ -1045,7 +1103,9 @@ void conditional_operator_lifetimebound(bool cond) {
   MyObj* p;
   {
     MyObj a, b;
-    p = Identity(cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}}
+    p = Identity(cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}} \
+                              // expected-note {{variable `p` is now an alias of `Identity(cond ? &a : &b)`}} \
+                              // expected-note {{variable `p` is now an alias of `Identity(cond ? &a : &b)`}}
                       : &b);  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1055,7 +1115,9 @@ void conditional_operator_lifetimebound_nested(bool cond) {
   MyObj* p;
   {
     MyObj a, b;
-    p = Identity(cond ? Identity(&a)    // expected-warning {{object whose reference is captured does not live long enough}}
+    p = Identity(cond ? Identity(&a)    // expected-warning {{object whose reference is captured does not live long enough}} \
+                                        // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(&a) : Identity(&b))`}} \
+                                        // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(&a) : Identity(&b))`}}
                       : Identity(&b));  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1065,7 +1127,11 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
   MyObj* p;
   {
     MyObj a, b, c, d;
-    p = Identity(cond ? Identity(cond ? &a     // expected-warning {{object whose reference is captured does not live long enough}}
+    p = Identity(cond ? Identity(cond ? &a     // expected-warning {{object whose reference is captured does not live long enough}} \
+                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \
+                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \
+                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \
+                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}}
                                       : &b)    // expected-warning {{object whose reference is captured does not live long enough}}
                       : Identity(cond ? &c     // expected-warning {{object whose reference is captured does not live long enough}}
                                       : &d));  // expected-warning {{object whose reference is captured does not live long enough}}
@@ -1077,29 +1143,31 @@ void parentheses(bool cond) {
   MyObj* p;
   {
     MyObj a;
-    p = &((((a))));  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &((((a))));  // expected-warning {{object whose reference is captured does not live long enough}} \
+                     // expected-note {{variable `p` is now an alias of `a`}}
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 
   {
     MyObj a;
-    p = ((GetPointer((a))));  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = ((GetPointer((a))));  // expected-warning {{object whose reference is captured does not live long enough}} \
+                              // expected-note {{variable `p` is now an alias of `GetPointer((a))`}}
   }                           // expected-note {{destroyed here}}
   (void)*p;                   // expected-note {{later used here}}
 
   {
     MyObj a, b, c, d;
-    p = &(cond ? (cond ? a     // expected-warning {{object whose reference is captured does not live long enough}}.
-                       : b)    // expected-warning {{object whose reference is captured does not live long enough}}.
-               : (cond ? c     // expected-warning {{object whose reference is captured does not live long enough}}.
-                       : d));  // expected-warning {{object whose reference is captured does not live long enough}}.
+    p = &(cond ? (cond ? a     // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `a`}}
+                       : b)    // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `b`}}
+               : (cond ? c     // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `c`}}
+                       : d));  // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `d`}}
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 
   {
     MyObj a, b, c, d;
-    p = ((cond ? (((cond ? &a : &b)))   // expected-warning 2 {{object whose reference is captured does not live long enough}}.
-              : &(((cond ? c : d)))));  // expected-warning 2 {{object whose reference is captured does not live long enough}}.
+    p = ((cond ? (((cond ? &a : &b)))   // expected-warning 2 {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `b`}} expected-note {{variable `p` is now an alias of `a`}}
+              : &(((cond ? c : d)))));  // expected-warning 2 {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `d`}} expected-note {{variable `p` is now an alias of `c`}}
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 
@@ -1108,20 +1176,20 @@ void parentheses(bool cond) {
 void use_temporary_after_destruction() {
   View a;
   a = non_trivially_destructed_temporary(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                  expected-note {{destroyed here}}
+                  expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `non_trivially_destructed_temporary()`}}
   use(a); // expected-note {{later used here}}
 }
 
 void passing_temporary_to_lifetime_bound_function() {
   View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{object whose reference is captured does not live long enough}} \
-                expected-note {{destroyed here}}
+                expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `construct_view(non_trivially_destructed_temporary())`}}
   use(a); // expected-note {{later used here}}
 }
 
 void use_trivial_temporary_after_destruction() {
   View a;
   a = trivially_destructed_temporary(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                expected-note {{destroyed here}}
+                expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `trivially_destructed_temporary()`}}
   use(a); // expected-note {{later used here}}
 }
 
@@ -1163,7 +1231,7 @@ void foobar() {
   {
     StatusOr<MyObj> string_or = getStringOr();
     view = string_or. // expected-warning {{object whose reference is captured does not live long enough}}
-            value();
+            value();  // expected-note {{variable `view` is now an alias of `string_or.value()`}}
   }                     // expected-note {{destroyed here}}
   (void)view;           // expected-note {{later used here}}
 }
@@ -1182,8 +1250,9 @@ void range_based_for_use_after_scope() {
   View v;
   {
     MyObjStorage s;
-    for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}}
-      v = o;
+    for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} \
+                               // expected-note {{variable `o` is now an alias of `__begin2`}}
+      v = o;                  // expected-note {{variable `v` is now an alias of `o`}}
     }
   } // expected-note {{destroyed here}}
   v.use(); // expected-note {{later used here}}
@@ -1203,7 +1272,8 @@ void range_based_for_not_reference() {
   {
     MyObjStorage s;
     for (MyObj o : s) { // expected-note {{destroyed here}}
-      v = o; // expected-warning {{object whose reference is captured does not live long enough}}
+      v = o; // expected-warning {{object whose reference is captured does not live long enough}} \
+             // expected-note {{variable `v` is now an alias of `o`}}
     }
   }
   v.use();  // expected-note {{later used here}}
@@ -1236,7 +1306,8 @@ void test_user_defined_deref_uaf() {
   {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
-    p = &(*smart_ptr);  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &(*smart_ptr);  // expected-warning {{object whose reference is captured does not live long enough}} \
+                        // expected-note {{variable `p` is now an alias of `operator*(smart_ptr)`}}
   }                     // expected-note {{destroyed here}}
   (void)*p;             // expected-note {{later used here}}
 }
@@ -1253,7 +1324,8 @@ void test_user_defined_deref_with_view() {
   {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
-    v = *smart_ptr;  // expected-warning {{object whose reference is captured does not live long enough}}
+    v = *smart_ptr;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                     // expected-note {{variable `v` is now an alias of `operator*(smart_ptr)`}}
   }                  // expected-note {{destroyed here}}
   v.use();           // expected-note {{later used here}}
 }
@@ -1263,7 +1335,8 @@ void test_user_defined_deref_arrow() {
   {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
-    p = smart_ptr.operator->();  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = smart_ptr.operator->();  // expected-warning {{object whose reference is captured does not live long enough}} \
+                                 // expected-note {{variable `p` is now an alias of `smart_ptr.operator->()`}}
   }                              // expected-note {{destroyed here}}
   (void)*p;                      // expected-note {{later used here}}
 }
@@ -1273,7 +1346,8 @@ void test_user_defined_deref_chained() {
   {
     MyObj obj;
     SmartPtr<SmartPtr<MyObj>> double_ptr;
-    p = &(**double_ptr);  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &(**double_ptr);  // expected-warning {{object whose reference is captured does not live long enough}} \
+                          // expected-note {{variable `p` is now an alias of `operator*(* double_ptr)`}}
   }                       // expected-note {{destroyed here}}
   (void)*p;               // expected-note {{later used here}}
 }
@@ -1307,15 +1381,15 @@ T&& MaxT(T&& a [[clang::lifetimebound]], T&& b [[clang::lifetimebound]]);
 
 const MyObj& call_max_with_obj() {
   MyObj oa, ob;
-  return  MaxT(oa,    // expected-warning {{address of stack memory is returned later}}          
+  return  MaxT(oa,    // expected-warning {{address of stack memory is returned later}}
                       // expected-note at -1 2 {{returned here}}
                ob);   // expected-warning {{address of stack memory is returned later}}
-                    
+
 }
 
 MyObj* call_max_with_obj_error() {
   MyObj oa, ob;
-  return  &MaxT(oa,   // expected-warning {{address of stack memory is returned later}}          
+  return  &MaxT(oa,   // expected-warning {{address of stack memory is returned later}}
                       // expected-note at -1 2 {{returned here}}
                 ob);  // expected-warning {{address of stack memory is returned later}}
 }
@@ -1424,7 +1498,8 @@ void strict_warn_on_move() {
   View v;
   {
     MyObj a;
-    v = a;            // expected-warning-re {{object whose reference {{.*}} may have been moved}}
+    v = a;            // expected-warning-re {{object whose reference {{.*}} may have been moved}} \
+                      // expected-note {{variable `v` is now an alias of `a`}}
     b = std::move(a); // expected-note {{potentially moved here}}
   }                   // expected-note {{destroyed here}}
   (void)v;            // expected-note {{later used here}}
@@ -1438,7 +1513,7 @@ void flow_sensitive(bool c) {
       MyObj b = std::move(a);
       return;
     }
-    v = a;  // expected-warning {{object whose reference}}
+    v = a;  // expected-warning {{object whose reference}} expected-note {{variable `v` is now an alias of `a`}}
   }         // expected-note {{destroyed here}}
   (void)v;  // expected-note {{later used here}}
 }
@@ -1448,7 +1523,9 @@ void detect_conditional(bool cond) {
   View v;
   {
     MyObj a, b;
-    v = cond ? a : b; // expected-warning-re 2 {{object whose reference {{.*}} may have been moved}}
+    v = cond ? a : b; // expected-warning-re 2 {{object whose reference {{.*}} may have been moved}} \
+                      // expected-note {{variable `v` is now an alias of `b`}} \
+                      // expected-note {{variable `v` is now an alias of `a`}}
     take(std::move(cond ? a : b)); // expected-note 2 {{potentially moved here}}
   }         // expected-note 2 {{destroyed here}}
   (void)v;  // expected-note 2 {{later used here}}
@@ -1458,13 +1535,15 @@ void wrong_use_of_move_is_permissive() {
   View v;
   {
     MyObj a;
-    v = std::move(a); // expected-warning {{object whose reference is captured does not live long enough}}
+    v = std::move(a); // expected-warning {{object whose reference is captured does not live long enough}} \
+                      // expected-note {{variable `v` is now an alias of `std::move(a)`}}
   }         // expected-note {{destroyed here}}
   (void)v;  // expected-note {{later used here}}
   const int* p;
   {
     MyObj a;
-    p = std::move(a).getData(); // expected-warning {{object whose reference is captured does not live long enough}}
+    p = std::move(a).getData(); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                // expected-note {{variable `p` is now an alias of `std::move(a).getData()`}}
   }         // expected-note {{destroyed here}}
   (void)p;  // expected-note {{later used here}}
 }
@@ -1475,7 +1554,8 @@ void test_release_no_uaf() {
   // Calling release() marks p as moved from, so its destruction doesn't invalidate r.
   {
     std::unique_ptr<int> p;
-    r = p.get();        // expected-warning-re {{object whose reference {{.*}} may have been moved}}
+    r = p.get();        // expected-warning-re {{object whose reference {{.*}} may have been moved}} \
+                        // expected-note {{variable `r` is now an alias of `p.get()`}}
     take(p.release());  // expected-note {{potentially moved here}}
   }                     // expected-note {{destroyed here}}
   (void)*r;             // expected-note {{later used here}}
@@ -1497,9 +1577,10 @@ void bar() {
     View x;
     {
         S s;
-        x = s.x(); // expected-warning {{object whose reference is captured does not live long enough}}
+        x = s.x();        // expected-warning {{object whose reference is captured does not live long enough}} \
+                          // expected-note {{variable `x` is now an alias of `s.x()`}}
         View y = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                             expected-note {{destroyed here}}
+                             expected-note {{destroyed here}} expected-note {{variable `y` is now an alias of `S().x()`}}
         (void)y; // expected-note {{used here}}
     } // expected-note {{destroyed here}}
     (void)x; // expected-note {{used here}}
@@ -1587,17 +1668,22 @@ const std::string& identity(const std::string& in [[clang::lifetimebound]]);
 const S& identity(const S& in [[clang::lifetimebound]]);
 
 void test_temporary() {
-  const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+  const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                  // expected-note {{destroyed here}} \
+                                  // expected-note {{variable `x` is now an alias of `S().x()`}}
   (void)x; // expected-note {{later used here}}
 
-  const std::string& y = identity(S().x()); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+  const std::string& y = identity(S().x()); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                            // expected-note {{destroyed here}} \
+                                            // expected-note {{variable `y` is now an alias of `identity(S().x())`}}
   (void)y; // expected-note {{later used here}}
 
   std::string_view z;
   {
     S s;
-    const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}}
-    z = zz;
+    const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                   // expected-note {{variable `zz` is now an alias of `s.x()`}}
+    z = zz;                        // expected-note {{variable `z` is now an alias of `zz.operator basic_string_view()`}}
   } // expected-note {{destroyed here}}
   (void)z; // expected-note {{later used here}}
 }
@@ -1605,12 +1691,16 @@ void test_temporary() {
 void test_lifetime_extension_ok() {
   const S& x = S();
   (void)x;
-  const S& y = identity(S()); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+  const S& y = identity(S()); // expected-warning {{object whose reference is captured does not live long enough}} \
+                              // expected-note {{destroyed here}} \
+                              // expected-note {{variable `y` is now an alias of `identity(S())`}}
   (void)y; // expected-note {{later used here}}
 }
 
 const std::string& test_return() {
-  const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+  const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                  // expected-note {{destroyed here}} \
+                                  // expected-note {{variable `x` is now an alias of `S().x()`}}
   return x; // expected-note {{later used here}}
 }
 } // namespace reference_type_decl_ref_expr
@@ -1626,8 +1716,9 @@ void uaf() {
   std::string_view view;
   {
     S str;
-    S* p = &str;  // expected-warning {{object whose reference is captured does not live long enough}}
-    view = p->s;
+    S* p = &str;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                  // expected-note {{variable `p` is now an alias of `str`}}
+    view = p->s;  // expected-note {{variable `view` is now an alias of `p->s.operator basic_string_view()`}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1652,8 +1743,9 @@ void uaf_union() {
   std::string_view view;
   {
     U u = U{"hello"};
-    U* up = &u;  // expected-warning {{object whose reference is captured does not live long enough}}
-    view = up->s;
+    U* up = &u;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                 // expected-note {{variable `up` is now an alias of `u`}}
+    view = up->s; // expected-note {{variable `view` is now an alias of `up->s.operator basic_string_view()`}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1669,8 +1761,8 @@ void uaf_anonymous_union() {
   int* ip;
   {
     AnonymousUnion au;
-    AnonymousUnion* up = &au;  // expected-warning {{object whose reference is captured does not live long enough}}
-    ip = &up->x;
+    AnonymousUnion* up = &au;  // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{variable `up` is now an alias of `au`}}
+    ip = &up->x; // expected-note {{variable `ip` is now an alias of `up`}}
   } // expected-note {{destroyed here}}
   (void)ip;  // expected-note {{later used here}}
 }
@@ -1728,9 +1820,15 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) {
 
 void test() {
   MemberFuncsTpl<MyObj> mtf;
-  const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}}
-  const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{object whose reference is captured does not live long enough}} // tu-note {{destroyed here}}
-  const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}}
+  const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                            // expected-note {{destroyed here}} \
+                                            // expected-note {{variable `pTMA` is now an alias of `mtf.memberA(MyObj())`}}
+  const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{object whose reference is captured does not live long enough}} \
+                                            // tu-note {{destroyed here}} \
+                                            // tu-note {{variable `pTMB` is now an alias of `mtf.memberB(MyObj())`}}
+  const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                            // expected-note {{destroyed here}} \
+                                            // expected-note {{variable `pTMC` is now an alias of `mtf.memberC(MyObj())`}}
   (void)pTMA; // expected-note {{later used here}}
   (void)pTMB; // tu-note {{later used here}}
   (void)pTMC; // expected-note {{later used here}}
@@ -1765,7 +1863,8 @@ void test_optional_arrow() {
   const char* p;
   {
     std::optional<std::string> opt;
-    p = opt->data();  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = opt->data();  // expected-warning {{object whose reference is captured does not live long enough}} \
+                      // expected-note {{variable `p` is now an alias of `opt->data()`}}
   }                   // expected-note {{destroyed here}}
   (void)*p;           // expected-note {{later used here}}
 }
@@ -1774,7 +1873,8 @@ void test_optional_arrow_lifetimebound() {
   View v;
   {
     std::optional<MyObj> opt;
-    v = opt->getView();  // expected-warning {{object whose reference is captured does not live long enough}}
+    v = opt->getView();  // expected-warning {{object whose reference is captured does not live long enough}} \
+                         // expected-note {{variable `v` is now an alias of `opt->getView()`}}
   }                      // expected-note {{destroyed here}}
   v.use();               // expected-note {{later used here}}
 }
@@ -1783,7 +1883,8 @@ void test_unique_ptr_arrow() {
   const char* p;
   {
     std::unique_ptr<std::string> up;
-    p = up->data();  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = up->data();  // expected-warning {{object whose reference is captured does not live long enough}} \
+                     // expected-note {{variable `p` is now an alias of `up->data()`}}
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 }
@@ -1974,8 +2075,9 @@ void multi_level_pointer_in_loop() {
     MyObj* p;
     MyObj** pp;
     if (i > 5) {
-      p = &obj; // expected-warning {{object whose reference is captured does not live long enough}}
-      pp = &p;
+      p = &obj; // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable `p` is now an alias of `obj`}}
+      pp = &p;  // expected-note {{variable `pp` is now an alias of `p`}}
     }
     (void)**pp; // expected-note {{later used here}}
   }             // expected-note {{destroyed here}}
@@ -1986,7 +2088,8 @@ void outer_pointer_outlives_inner_pointee() {
   MyObj* view = &safe;
   for (int i = 0; i < 10; ++i) {
     MyObj obj;
-    view = &obj;     // expected-warning {{object whose reference is captured does not live long enough}}
+    view = &obj;     // expected-warning {{object whose reference is captured does not live long enough}} \
+                     // expected-note {{variable `view` is now an alias of `obj`}}
   }                  // expected-note {{destroyed here}}
   (void)*view;       // expected-note {{later used here}}
 }
@@ -1999,7 +2102,8 @@ void element_use_after_scope() {
   int* p;
   {
     int a[10]{};
-    p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} \
+               // expected-note {{variable `p` is now an alias of `a`}}
   }            // expected-note {{destroyed here}}
   (void)*p;    // expected-note {{later used here}}
 }
@@ -2031,7 +2135,8 @@ void multidimensional_use_after_scope() {
   int* p;
   {
     int a[3][4]{};
-    p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} \
+                  // expected-note {{variable `p` is now an alias of `a`}}
   }               // expected-note {{destroyed here}}
   (void)*p;       // expected-note {{later used here}}
 }
@@ -2044,7 +2149,8 @@ void member_array_element_use_after_scope() {
   int* p;
   {
     S s;
-    p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} \
+                   // expected-note {{variable `p` is now an alias of `s`}}
   }                // expected-note {{destroyed here}}
   (void)*p;        // expected-note {{later used here}}
 }
@@ -2053,7 +2159,8 @@ void array_of_pointers_use_after_scope() {
   int** p;
   {
     int* a[10]{};
-    p = a;  // expected-warning {{object whose reference is captured does not live long enough}}
+    p = a;  // expected-warning {{object whose reference is captured does not live long enough}} \
+            // expected-note {{variable `p` is now an alias of `a`}}
   }         // expected-note {{destroyed here}}
   (void)*p; // expected-note {{later used here}}
 }
@@ -2062,7 +2169,8 @@ void reversed_subscript_use_after_scope() {
   int* p;
   {
     int a[10]{};
-    p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}}
+    p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} \
+                 // expected-note {{variable `p` is now an alias of `a`}}
   }              // expected-note {{destroyed here}}
   (void)*p;      // expected-note {{later used here}}
 }
@@ -2135,9 +2243,10 @@ struct S {
 
 void indexing_with_static_operator() {
   S()(1, 2);
-  S& x = S()("1",
-             2,  // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
-             3); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+  S& x = S()("1", //expected-note {{variable `x` is now an alias of `operator()(S(), "1", 2, 3)`}} \
+                  //expected-note {{variable `x` is now an alias of `operator()(S(), "1", 2, 3)`}}
+             2,   // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+             3);  // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
 
   (void)x; // expected-note 2 {{later used here}}
 

>From 13c0b3917f565883c6c09b4b5c6681974988b568 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 27 Mar 2026 19:47:01 +0800
Subject: [PATCH 02/22] [LifetimeSafety] Refactor assignment history lookup
 into AssignmentQuery

Independent AssignmentQuery now handles the logic for searching  assignment history during error reporting, decoupling it from the diagnostic logic.

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  52 +++++
 .../LifetimeSafety/AssignmentQuery.cpp        | 215 ++++++++++++++++++
 .../Analysis/LifetimeSafety/CMakeLists.txt    |   1 +
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 200 +---------------
 4 files changed, 272 insertions(+), 196 deletions(-)
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
 create mode 100644 clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
new file mode 100644
index 0000000000000..ecae93f250b69
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -0,0 +1,52 @@
+//===- AssignmentQuery.cpp - C++ Lifetime Safety Checker --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LifetimeChecker, which detects use-after-free
+// errors by checking if live origins hold loans that have expired.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_ASSIGNMENTQUERY_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_ASSIGNMENTQUERY_H
+
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/MovedLoans.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+
+namespace clang::lifetimes::internal {
+
+struct AliasAssignmentSearchResult {
+  const llvm::SmallVector<AssignmentPair> Payload;
+  bool SearchComplete;
+  const ValueDecl *LastDestDecl;
+  std::optional<OriginID> LastOrigin;
+};
+
+struct AssignmentQueryContext {
+  const LoanPropagationAnalysis &LoanPropagation;
+  const MovedLoansAnalysis &MovedLoans;
+  const LiveOriginsAnalysis &LiveOrigins;
+  FactManager &FactMgr;
+  AnalysisDeclContext &ADC;
+};
+
+/// Retrieves a list of assignment chains for use-after-scope analysis.
+///
+/// To help users understand the data flow, we track where the problematic
+/// address originated.
+std::optional<llvm::SmallVector<AssignmentPair>>
+getAliasList(const AssignmentQueryContext &Context, const UseFact *UF,
+             const LoanID End, const bool InOneBlock);
+} // namespace clang::lifetimes::internal
+
+#endif
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
new file mode 100644
index 0000000000000..ed19e06a12323
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -0,0 +1,215 @@
+//===- AssignmentQuery.cpp - C++ Lifetime Safety Checker --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LifetimeChecker, which detects use-after-free
+// errors by checking if live origins hold loans that have expired.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include <cstddef>
+
+namespace {
+
+using namespace clang;
+using namespace clang::lifetimes;
+using namespace clang::lifetimes::internal;
+
+std::optional<OriginSrcExpr> GetPureSrcExpr(const Expr *TargetExpr) {
+  if (!TargetExpr)
+    return std::nullopt;
+  const Expr *SExpr = TargetExpr->IgnoreParenCasts();
+  if (!SExpr)
+    return std::nullopt;
+
+  if (const auto *SDRExpr = llvm::dyn_cast<DeclRefExpr>(SExpr)) {
+    return SDRExpr;
+  }
+  if (const auto *STMExpr = llvm::dyn_cast<CXXTemporaryObjectExpr>(SExpr)) {
+    return STMExpr;
+  }
+  if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr)) {
+    return SCExpr;
+  }
+
+  if (const auto *SCCExpr = llvm::dyn_cast<CXXConstructExpr>(SExpr)) {
+    if (SCCExpr->getNumArgs() > 0)
+      return GetPureSrcExpr(SCCExpr->getArg(0));
+  }
+  if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr)) {
+    return GetPureSrcExpr(SUOExpr->getSubExpr());
+  }
+  if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr)) {
+    return GetPureSrcExpr(SCBExpr->getSubExpr());
+  }
+
+  return std::nullopt;
+}
+
+AliasAssignmentSearchResult
+getAliasListCore(const AssignmentQueryContext &Context, const CFGBlock *Block,
+                 const LoanID EndLoanID, OriginID *TargetOID,
+                 const ValueDecl *LastDestDecl = nullptr,
+                 const std::optional<OriginID> LastOriginID = std::nullopt) {
+  llvm::SmallVector<AssignmentPair> AliasStmts;
+  const ValueDecl *DestDecl = LastDestDecl;
+  const auto Facts = Context.FactMgr.getFacts(Block);
+  bool FetchLoan = false;
+  auto IssueOriginID = LastOriginID;
+
+  for (auto F = Facts.rbegin(); F != Facts.rend(); ++F) {
+    if (const auto *OFF = (*F)->getAs<OriginFlowFact>()) {
+      if (IssueOriginID.has_value() &&
+          OFF->getDestOriginID() == IssueOriginID.value()) {
+        FetchLoan = true;
+      }
+      if (OFF->getDestOriginID() == *TargetOID) {
+        const auto HeldLoans =
+            Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
+
+        if (HeldLoans.contains(EndLoanID)) {
+          const auto TargetOrigin =
+              Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
+
+          if (DestDecl == nullptr) {
+            if (const ValueDecl *DDecl = TargetOrigin.getDecl()) {
+              DestDecl = DDecl;
+            }
+          } else {
+            auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
+            if (!SExpr.has_value()) {
+              const auto SrcOrigin = Context.FactMgr.getOriginMgr().getOrigin(
+                  OFF->getSrcOriginID());
+              SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
+            }
+
+            if (SExpr.has_value()) {
+              AliasStmts.push_back({SExpr.value(), DestDecl});
+              DestDecl = nullptr;
+            }
+          }
+          *TargetOID = OFF->getSrcOriginID();
+        }
+      }
+    } else if (const auto *IF = (*F)->getAs<IssueFact>()) {
+      if (IF->getLoanID() == EndLoanID) {
+        IssueOriginID = IF->getOriginID();
+      }
+    }
+
+    if (FetchLoan) {
+      return {AliasStmts, true, DestDecl, IssueOriginID};
+    }
+  }
+  return {AliasStmts, false, DestDecl, IssueOriginID};
+}
+
+std::optional<llvm::SmallVector<AssignmentPair>>
+getAliasListInMultiBlock(const AssignmentQueryContext &Context,
+                         const CFGBlock *StartBlock, const LoanID EndLoanID,
+                         OriginID *StartOID) {
+  const ValueDecl *LastDestDecl = nullptr;
+  llvm::SmallVector<const CFGBlock *> PendingBlocks;
+  std::optional<AssignmentPair> StartStmt = std::nullopt;
+  std::optional<AssignmentPair> EndStmt = std::nullopt;
+  std::optional<OriginID> LastOriginID = std::nullopt;
+  llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
+  llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs;
+
+  const auto AliasStmtFilter = [&VistedExprs](const AssignmentPair StartStmt,
+                                              const AssignmentPair EndStmt) {
+    llvm::SmallVector<AssignmentPair> AliasStmts;
+    for (auto Stmt = StartStmt; Stmt != EndStmt; Stmt = VistedExprs.at(Stmt)) {
+      AliasStmts.push_back(Stmt);
+    }
+    AliasStmts.push_back(EndStmt);
+    return AliasStmts;
+  };
+
+  PendingBlocks.push_back(StartBlock);
+
+  for (size_t i = 0; i < PendingBlocks.size(); ++i) {
+    const CFGBlock *CurrBlock = PendingBlocks[i];
+
+    const auto [BlockAliasList, Success, CurrLastDestDecl, CurrLastOriginID] =
+        getAliasListCore(Context, CurrBlock, EndLoanID, StartOID, LastDestDecl,
+                         LastOriginID);
+    if (CurrLastDestDecl)
+      LastDestDecl = CurrLastDestDecl;
+    if (CurrLastOriginID.has_value())
+      LastOriginID = CurrLastOriginID;
+
+    if (!BlockAliasList.empty()) {
+      if (VistedExprs.empty()) {
+        StartStmt = BlockAliasList[0];
+      }
+
+      for (size_t i = 0; i < BlockAliasList.size() - 1; ++i) {
+        VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]});
+      }
+
+      if (EndStmt.has_value())
+        VistedExprs.insert({EndStmt.value(), BlockAliasList[0]});
+
+      EndStmt = BlockAliasList[BlockAliasList.size() - 1];
+    }
+
+    if (Success && StartStmt.has_value() && EndStmt.has_value()) {
+      return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+    }
+
+    for (const auto Block : CurrBlock->preds()) {
+      if (Block && VistedBlocks.insert(Block).second)
+        PendingBlocks.push_back(Block);
+    }
+
+    if (VistedBlocks.size() >= 32 && StartStmt.has_value() &&
+        EndStmt.has_value()) {
+      return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+    }
+  }
+
+  if (StartStmt.has_value() && EndStmt.has_value()) {
+    return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+  }
+
+  return std::nullopt;
+}
+} // namespace
+
+namespace clang::lifetimes::internal {
+
+std::optional<llvm::SmallVector<AssignmentPair>>
+getAliasList(const AssignmentQueryContext &Context, const UseFact *UF,
+             const LoanID End, const bool InOneBlock) {
+  const CFGBlock *IssueBlock =
+      Context.ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
+  assert(IssueBlock && "Searching CFGBlock failed");
+
+  for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+       Cur = Cur->peelOuterOrigin()) {
+    auto TargetOID = Cur->getOuterOriginID();
+    if (InOneBlock) {
+      AliasAssignmentSearchResult Result =
+          getAliasListCore(Context, IssueBlock, End, &TargetOID);
+      if (!Result.Payload.empty())
+        return Result.Payload;
+    } else {
+      auto Result =
+          getAliasListInMultiBlock(Context, IssueBlock, End, &TargetOID);
+      if (Result.has_value())
+        return Result.value();
+    }
+  }
+  return std::nullopt;
+}
+} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
index 247377c7256d9..6c4d4e123908a 100644
--- a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
+++ b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_clang_library(clangAnalysisLifetimeSafety
+  AssignmentQuery.cpp
   Checker.cpp
   Facts.cpp
   FactsGenerator.cpp
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index bfb2a718a4bcc..802bdb25cd325 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
@@ -55,13 +56,6 @@ using AnnotationTarget =
     llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
 using EscapingTarget = LifetimeSafetySemaHelper::EscapingTarget;
 
-struct AliasAssignmentSearchResult {
-  const llvm::SmallVector<AssignmentPair> Payload;
-  bool SearchComplete;
-  const ValueDecl *LastDestDecl;
-  std::optional<OriginID> LastOrigin;
-};
-
 class LifetimeChecker {
 private:
   llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
@@ -232,194 +226,6 @@ class LifetimeChecker {
     }
   }
 
-  std::optional<llvm::SmallVector<AssignmentPair>>
-  getAliasListInMultiBlock(const CFGBlock *StartBlock, const LoanID EndLoanID,
-                           OriginID *StartOID) {
-    const ValueDecl *LastDestDecl = nullptr;
-    llvm::SmallVector<const CFGBlock *> PendingBlocks;
-    std::optional<AssignmentPair> StartStmt = std::nullopt;
-    std::optional<AssignmentPair> EndStmt = std::nullopt;
-    std::optional<OriginID> LastOriginID = std::nullopt;
-    llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
-    llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs;
-
-    const auto AliasStmtFilter = [&VistedExprs](const AssignmentPair StartStmt,
-                                                const AssignmentPair EndStmt) {
-      llvm::SmallVector<AssignmentPair> AliasStmts;
-      for (auto Stmt = StartStmt; Stmt != EndStmt;
-           Stmt = VistedExprs.at(Stmt)) {
-        AliasStmts.push_back(Stmt);
-      }
-      AliasStmts.push_back(EndStmt);
-      return AliasStmts;
-    };
-
-    PendingBlocks.push_back(StartBlock);
-
-    for (size_t i = 0; i < PendingBlocks.size(); ++i) {
-      const CFGBlock *CurrBlock = PendingBlocks[i];
-
-      const auto [BlockAliasList, Success, CurrLastDestDecl, CurrLastOriginID] =
-          getAliasListCore(CurrBlock, EndLoanID, StartOID, LastDestDecl,
-                           LastOriginID);
-      if (CurrLastDestDecl)
-        LastDestDecl = CurrLastDestDecl;
-      if (CurrLastOriginID.has_value())
-        LastOriginID = CurrLastOriginID;
-
-      if (!BlockAliasList.empty()) {
-        if (VistedExprs.empty()) {
-          StartStmt = BlockAliasList[0];
-        }
-
-        for (size_t i = 0; i < BlockAliasList.size() - 1; ++i) {
-          VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]});
-        }
-
-        if (EndStmt.has_value())
-          VistedExprs.insert({EndStmt.value(), BlockAliasList[0]});
-
-        EndStmt = BlockAliasList[BlockAliasList.size() - 1];
-      }
-
-      if (Success && StartStmt.has_value() && EndStmt.has_value()) {
-        return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-      }
-
-      for (const auto Block : CurrBlock->preds()) {
-        if (Block && VistedBlocks.insert(Block).second)
-          PendingBlocks.push_back(Block);
-      }
-
-      if (VistedBlocks.size() >= 32 && StartStmt.has_value() &&
-          EndStmt.has_value()) {
-        return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-      }
-    }
-
-    if (StartStmt.has_value() && EndStmt.has_value()) {
-      return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-    }
-
-    return std::nullopt;
-  }
-
-  std::optional<OriginSrcExpr> GetPureSrcExpr(const Expr *TargetExpr) {
-    if (!TargetExpr)
-      return std::nullopt;
-    const Expr *SExpr = TargetExpr->IgnoreParenCasts();
-    if (!SExpr)
-      return std::nullopt;
-
-    if (const auto *SDRExpr = llvm::dyn_cast<DeclRefExpr>(SExpr)) {
-      return SDRExpr;
-    }
-    if (const auto *STMExpr = llvm::dyn_cast<CXXTemporaryObjectExpr>(SExpr)) {
-      return STMExpr;
-    }
-    if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr)) {
-      return SCExpr;
-    }
-
-    if (const auto *SCCExpr = llvm::dyn_cast<CXXConstructExpr>(SExpr)) {
-      if (SCCExpr->getNumArgs() > 0)
-        return GetPureSrcExpr(SCCExpr->getArg(0));
-    }
-    if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr)) {
-      return GetPureSrcExpr(SUOExpr->getSubExpr());
-    }
-    if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr)) {
-      return GetPureSrcExpr(SCBExpr->getSubExpr());
-    }
-
-    return std::nullopt;
-  }
-
-  /// Retrieves a list of assignment chains for use-after-scope analysis.
-  ///
-  /// To help users understand the data flow, we track where the problematic
-  /// address originated.
-  AliasAssignmentSearchResult
-  getAliasListCore(const CFGBlock *Block, const LoanID EndLoanID,
-                   OriginID *TargetOID, const ValueDecl *LastDestDecl = nullptr,
-                   const std::optional<OriginID> LastOriginID = std::nullopt) {
-    llvm::SmallVector<AssignmentPair> AliasStmts;
-    const ValueDecl *DestDecl = LastDestDecl;
-    const auto Facts = FactMgr.getFacts(Block);
-    bool FetchLoan = false;
-    auto IssueOriginID = LastOriginID;
-
-    for (auto F = Facts.rbegin(); F != Facts.rend(); ++F) {
-      if (const auto *OFF = (*F)->getAs<OriginFlowFact>()) {
-        if (IssueOriginID.has_value() &&
-            OFF->getDestOriginID() == IssueOriginID.value()) {
-          FetchLoan = true;
-        }
-        if (OFF->getDestOriginID() == *TargetOID) {
-          const auto HeldLoans =
-              LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
-
-          if (HeldLoans.contains(EndLoanID)) {
-            const auto TargetOrigin =
-                FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
-
-            if (DestDecl == nullptr) {
-              if (const ValueDecl *DDecl = TargetOrigin.getDecl()) {
-                DestDecl = DDecl;
-              }
-            } else {
-              auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
-              if (!SExpr.has_value()) {
-                const auto SrcOrigin =
-                    FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID());
-                SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
-              }
-
-              if (SExpr.has_value()) {
-                AliasStmts.push_back({SExpr.value(), DestDecl});
-                DestDecl = nullptr;
-              }
-            }
-            *TargetOID = OFF->getSrcOriginID();
-          }
-        }
-      } else if (const auto *IF = (*F)->getAs<IssueFact>()) {
-        if (IF->getLoanID() == EndLoanID) {
-          IssueOriginID = IF->getOriginID();
-        }
-      }
-
-      if (FetchLoan) {
-        return {AliasStmts, true, DestDecl, IssueOriginID};
-      }
-    }
-    return {AliasStmts, false, DestDecl, IssueOriginID};
-  }
-
-  std::optional<llvm::SmallVector<AssignmentPair>>
-  getAliasList(const UseFact *UF, const LoanID End, const bool InOneBlock) {
-    const CFGBlock *IssueBlock =
-        ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
-    assert(IssueBlock && "Searching CFGBlock failed");
-
-    for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
-         Cur = Cur->peelOuterOrigin()) {
-      auto TargetOID = Cur->getOuterOriginID();
-      if (InOneBlock) {
-        AliasAssignmentSearchResult Result =
-            getAliasListCore(IssueBlock, End, &TargetOID);
-        if (!Result.Payload.empty())
-          return Result.Payload;
-      } else {
-        auto Result = getAliasListInMultiBlock(IssueBlock, End, &TargetOID);
-        if (Result.has_value())
-          return Result.value();
-      }
-    }
-
-    return std::nullopt;
-  }
-
   void issuePendingWarnings() {
     if (!SemaHelper)
       return;
@@ -446,9 +252,11 @@ class LifetimeChecker {
 
         } else {
           // Scope-based expiry (use-after-scope).
+          const struct AssignmentQueryContext Context = {
+              LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
           const CFGStmtMap *CurrCFGStmtMap = ADC.getCFGStmtMap();
           const auto AliasExprs =
-              getAliasList(UF, LID,
+              getAliasList(Context, UF, LID,
                            CurrCFGStmtMap->getBlock(UF->getUseExpr()) ==
                                CurrCFGStmtMap->getBlock(IssueExpr));
           if (!AliasExprs.has_value()) {

>From 93743a31225fb2392ed872bffb90e237d930825a Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 27 Mar 2026 21:54:48 +0800
Subject: [PATCH 03/22] [LifetimeSafety]: refactor AssignmentQuery to support
 other erros handling

Redesign error handling for assignment statements to match the format suggested in PR review.

Note: This commit does not yet integrate the search into other error scenarios; that will be addressed in a follow-up commit.

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  34 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  15 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 .../LifetimeSafety/AssignmentQuery.cpp        | 208 +++++++---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |   8 +-
 clang/lib/Sema/SemaLifetimeSafety.h           |  87 ++---
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 123 ++++--
 .../Sema/warn-lifetime-safety-cfg-bailout.cpp |   6 +-
 .../Sema/warn-lifetime-safety-suggestions.cpp |   9 +-
 clang/test/Sema/warn-lifetime-safety.cpp      | 368 +++++++++++-------
 10 files changed, 549 insertions(+), 311 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index ecae93f250b69..b0f99a8b094ce 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -15,7 +15,6 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_ASSIGNMENTQUERY_H
 
 #include "clang/AST/Decl.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
@@ -23,12 +22,39 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 
+namespace clang::lifetimes {
+
+using OriginDestExpr =
+    llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *>;
+
+using AssignmentPair = std::pair<OriginDestExpr, const Expr *>;
+
+struct ExprPrintingResult {
+  llvm::SmallString<32> Value;
+  const Expr *CurrExpr;
+};
+
+ExprPrintingResult FormatIssueExprForSema(const Expr *IssueExpr);
+llvm::SmallVector<ExprPrintingResult> FormatSrcExprForSema(const Expr *SrcExpr);
+
+inline __attribute__((always_inline)) llvm::SmallString<32>
+FormatValueDeclForSema(const ValueDecl *TargetValue) {
+  llvm::SmallString<32> Result;
+  if (TargetValue) {
+    Result += "variable '";
+    Result += TargetValue->getName();
+    Result += "'";
+  }
+  return Result;
+}
+} // namespace clang::lifetimes
+
 namespace clang::lifetimes::internal {
 
 struct AliasAssignmentSearchResult {
   const llvm::SmallVector<AssignmentPair> Payload;
   bool SearchComplete;
-  const ValueDecl *LastDestDecl;
+  const std::optional<OriginDestExpr> LastDestDecl;
   std::optional<OriginID> LastOrigin;
 };
 
@@ -45,8 +71,8 @@ struct AssignmentQueryContext {
 /// To help users understand the data flow, we track where the problematic
 /// address originated.
 std::optional<llvm::SmallVector<AssignmentPair>>
-getAliasList(const AssignmentQueryContext &Context, const UseFact *UF,
-             const LoanID End, const bool InOneBlock);
+getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
+             const LoanID End, const Expr *IssueExpr);
 } // namespace clang::lifetimes::internal
 
 #endif
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index d3762a739682f..fac48afa9860b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -21,6 +21,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
 
 #include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
@@ -46,11 +47,6 @@ enum class SuggestionScope {
   IntraTU  // For suggestions on definitions local to a Translation Unit.
 };
 
-using OriginSrcExpr =
-    llvm::PointerUnion<const DeclRefExpr *, const CXXTemporaryObjectExpr *,
-                       const CallExpr *>;
-using AssignmentPair = std::pair<OriginSrcExpr, const ValueDecl *>;
-
 /// Abstract interface for operations requiring Sema access.
 ///
 /// This class exists to break a circular dependency: the LifetimeSafety
@@ -66,11 +62,10 @@ class LifetimeSafetySemaHelper {
   LifetimeSafetySemaHelper() = default;
   virtual ~LifetimeSafetySemaHelper() = default;
 
-  virtual void
-  reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
-                     const Expr *MovedExpr,
-                     const llvm::SmallVector<AssignmentPair> AliasList,
-                     SourceLocation FreeLoc) {}
+  virtual void reportUseAfterFree(
+      const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation FreeLoc) {}
 
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 041fad8249115..8e1a2485ce8b8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11010,7 +11010,7 @@ def note_lifetime_safety_dangling_static_here: Note<"this static storage dangles
 def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
 def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global storage">;
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">;
-def note_lifetime_safety_note_alias_chain : Note<"variable `%0` is now an alias of `%1`">;
+def note_lifetime_safety_note_alias_chain : Note<"%0 aliases the storage of %1">;
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index ed19e06a12323..c3506f20f57d9 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -24,54 +24,54 @@ using namespace clang;
 using namespace clang::lifetimes;
 using namespace clang::lifetimes::internal;
 
-std::optional<OriginSrcExpr> GetPureSrcExpr(const Expr *TargetExpr) {
+std::optional<const Expr *> GetPureSrcExpr(const Expr *TargetExpr) {
   if (!TargetExpr)
     return std::nullopt;
   const Expr *SExpr = TargetExpr->IgnoreParenCasts();
   if (!SExpr)
     return std::nullopt;
 
-  if (const auto *SDRExpr = llvm::dyn_cast<DeclRefExpr>(SExpr)) {
-    return SDRExpr;
-  }
-  if (const auto *STMExpr = llvm::dyn_cast<CXXTemporaryObjectExpr>(SExpr)) {
-    return STMExpr;
-  }
-  if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr)) {
+  if (llvm::isa<DeclRefExpr, CXXTemporaryObjectExpr, ConditionalOperator,
+                CXXConstructExpr>(SExpr) &&
+      !SExpr->getExprLoc().isInvalid())
+    return SExpr;
+
+  if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr);
+      SCExpr && !SCExpr->getExprLoc().isInvalid() &&
+      !SCExpr->getCallee()->IgnoreParenCasts()->getExprLoc().isInvalid())
     return SCExpr;
-  }
 
-  if (const auto *SCCExpr = llvm::dyn_cast<CXXConstructExpr>(SExpr)) {
-    if (SCCExpr->getNumArgs() > 0)
-      return GetPureSrcExpr(SCCExpr->getArg(0));
-  }
-  if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr)) {
+  if (const auto *SMExpr = llvm::dyn_cast<MemberExpr>(SExpr))
+    return GetPureSrcExpr(SMExpr->getBase());
+  if (const auto *SCExpr = llvm::dyn_cast<CXXMemberCallExpr>(SExpr))
+    return GetPureSrcExpr(SCExpr->getCallee());
+  if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr))
     return GetPureSrcExpr(SUOExpr->getSubExpr());
-  }
-  if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr)) {
+  if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr))
     return GetPureSrcExpr(SCBExpr->getSubExpr());
-  }
 
   return std::nullopt;
 }
 
-AliasAssignmentSearchResult
-getAliasListCore(const AssignmentQueryContext &Context, const CFGBlock *Block,
-                 const LoanID EndLoanID, OriginID *TargetOID,
-                 const ValueDecl *LastDestDecl = nullptr,
-                 const std::optional<OriginID> LastOriginID = std::nullopt) {
+AliasAssignmentSearchResult getAliasListCore(
+    const AssignmentQueryContext &Context, const CFGBlock *Block,
+    const LoanID EndLoanID, OriginID *TargetOID,
+    const std::optional<OriginDestExpr> LastDestDecl = std::nullopt,
+    const std::optional<OriginID> LastOriginID = std::nullopt) {
+  std::optional<OriginID> CurrOrigin = std::nullopt;
+  std::optional<OriginDestExpr> DestDecl = LastDestDecl;
+  std::optional<const Expr *> SrcExpr = std::nullopt;
   llvm::SmallVector<AssignmentPair> AliasStmts;
-  const ValueDecl *DestDecl = LastDestDecl;
   const auto Facts = Context.FactMgr.getFacts(Block);
   bool FetchLoan = false;
   auto IssueOriginID = LastOriginID;
 
-  for (auto F = Facts.rbegin(); F != Facts.rend(); ++F) {
-    if (const auto *OFF = (*F)->getAs<OriginFlowFact>()) {
+  for (const auto &F : llvm::reverse(Facts)) {
+    if (const auto *OFF = F->getAs<OriginFlowFact>()) {
       if (IssueOriginID.has_value() &&
-          OFF->getDestOriginID() == IssueOriginID.value()) {
+          OFF->getDestOriginID() == IssueOriginID.value())
         FetchLoan = true;
-      }
+
       if (OFF->getDestOriginID() == *TargetOID) {
         const auto HeldLoans =
             Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
@@ -80,9 +80,11 @@ getAliasListCore(const AssignmentQueryContext &Context, const CFGBlock *Block,
           const auto TargetOrigin =
               Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
 
-          if (DestDecl == nullptr) {
-            if (const ValueDecl *DDecl = TargetOrigin.getDecl()) {
-              DestDecl = DDecl;
+          if (!DestDecl.has_value()) {
+            if (const ValueDecl *DVecl = TargetOrigin.getDecl();
+                DVecl && !DVecl->getLocation().isInvalid()) {
+              CurrOrigin = *TargetOID;
+              DestDecl = DVecl;
             }
           } else {
             auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
@@ -93,17 +95,36 @@ getAliasListCore(const AssignmentQueryContext &Context, const CFGBlock *Block,
             }
 
             if (SExpr.has_value()) {
-              AliasStmts.push_back({SExpr.value(), DestDecl});
-              DestDecl = nullptr;
+              AliasStmts.push_back({DestDecl.value(), SExpr.value()});
+              SrcExpr = SExpr.value();
+              DestDecl = std::nullopt;
+              CurrOrigin = std::nullopt;
             }
           }
           *TargetOID = OFF->getSrcOriginID();
         }
       }
-    } else if (const auto *IF = (*F)->getAs<IssueFact>()) {
+    } else if (const auto *IF = F->getAs<IssueFact>()) {
       if (IF->getLoanID() == EndLoanID) {
         IssueOriginID = IF->getOriginID();
       }
+    } else if (const auto *UF = F->getAs<UseFact>()) {
+      if (CurrOrigin.has_value()) {
+        for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+             Cur = Cur->peelOuterOrigin()) {
+          if (Cur->getOuterOriginID() == CurrOrigin.value() &&
+              UF->isWritten()) {
+            const auto UExpr = GetPureSrcExpr(UF->getUseExpr());
+            if (UExpr.has_value()) {
+              if (const auto *UDExpr =
+                      llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
+                DestDecl = UDExpr;
+                break;
+              }
+            }
+          }
+        }
+      }
     }
 
     if (FetchLoan) {
@@ -117,7 +138,7 @@ std::optional<llvm::SmallVector<AssignmentPair>>
 getAliasListInMultiBlock(const AssignmentQueryContext &Context,
                          const CFGBlock *StartBlock, const LoanID EndLoanID,
                          OriginID *StartOID) {
-  const ValueDecl *LastDestDecl = nullptr;
+  std::optional<OriginDestExpr> LastDestDecl = std::nullopt;
   llvm::SmallVector<const CFGBlock *> PendingBlocks;
   std::optional<AssignmentPair> StartStmt = std::nullopt;
   std::optional<AssignmentPair> EndStmt = std::nullopt;
@@ -128,9 +149,8 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
   const auto AliasStmtFilter = [&VistedExprs](const AssignmentPair StartStmt,
                                               const AssignmentPair EndStmt) {
     llvm::SmallVector<AssignmentPair> AliasStmts;
-    for (auto Stmt = StartStmt; Stmt != EndStmt; Stmt = VistedExprs.at(Stmt)) {
+    for (auto Stmt = StartStmt; Stmt != EndStmt; Stmt = VistedExprs.at(Stmt))
       AliasStmts.push_back(Stmt);
-    }
     AliasStmts.push_back(EndStmt);
     return AliasStmts;
   };
@@ -149,13 +169,11 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
       LastOriginID = CurrLastOriginID;
 
     if (!BlockAliasList.empty()) {
-      if (VistedExprs.empty()) {
+      if (VistedExprs.empty())
         StartStmt = BlockAliasList[0];
-      }
 
-      for (size_t i = 0; i < BlockAliasList.size() - 1; ++i) {
+      for (size_t i = 0; i < BlockAliasList.size() - 1; ++i)
         VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]});
-      }
 
       if (EndStmt.has_value())
         VistedExprs.insert({EndStmt.value(), BlockAliasList[0]});
@@ -163,49 +181,129 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
       EndStmt = BlockAliasList[BlockAliasList.size() - 1];
     }
 
-    if (Success && StartStmt.has_value() && EndStmt.has_value()) {
+    if (Success && StartStmt.has_value() && EndStmt.has_value())
       return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-    }
 
-    for (const auto Block : CurrBlock->preds()) {
+    for (const auto &Block : CurrBlock->preds())
       if (Block && VistedBlocks.insert(Block).second)
         PendingBlocks.push_back(Block);
-    }
 
     if (VistedBlocks.size() >= 32 && StartStmt.has_value() &&
-        EndStmt.has_value()) {
+        EndStmt.has_value())
       return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-    }
   }
 
-  if (StartStmt.has_value() && EndStmt.has_value()) {
+  if (StartStmt.has_value() && EndStmt.has_value())
     return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-  }
 
   return std::nullopt;
 }
 } // namespace
 
+namespace clang::lifetimes {
+
+ExprPrintingResult FormatIssueExprForSema(const Expr *IssueExpr) {
+  if (!IssueExpr)
+    return {};
+  const auto *PureExpr = IssueExpr->IgnoreParenCasts();
+  if (!PureExpr)
+    return {};
+
+  if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr))
+    return {FormatValueDeclForSema(IDeclExpr->getDecl()), IssueExpr};
+  return {{"the temporary"}, IssueExpr};
+}
+
+llvm::SmallVector<ExprPrintingResult>
+FormatSrcExprForSema(const Expr *SrcExpr) {
+  if (!SrcExpr)
+    return {};
+  const auto *PureExpr = SrcExpr->IgnoreParenCasts();
+  if (!PureExpr)
+    return {};
+
+  if (const auto *IOpCallExpr = llvm::dyn_cast<CXXOperatorCallExpr>(PureExpr);
+      IOpCallExpr && !IOpCallExpr->getExprLoc().isInvalid())
+    return {{{"expression"}, IOpCallExpr->getArg(0)}};
+  if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr);
+      IDeclExpr && !IDeclExpr->getExprLoc().isInvalid())
+    return {{{}, IDeclExpr}};
+
+  if (const auto *ICXXCallExpr = llvm::dyn_cast<CXXMemberCallExpr>(PureExpr);
+      ICXXCallExpr && !ICXXCallExpr->getExprLoc().isInvalid()) {
+    llvm::SmallVector<ExprPrintingResult> Result;
+    if (!ICXXCallExpr->getCallee()->getExprLoc().isInvalid())
+      Result.push_back({{"function call result"}, ICXXCallExpr});
+    if (const auto *SubExpr = ICXXCallExpr->getImplicitObjectArgument();
+        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
+      Result.append(FormatSrcExprForSema(SubExpr));
+    }
+    return Result;
+  }
+  if (const auto *ICallExpr = llvm::dyn_cast<CallExpr>(PureExpr);
+      ICallExpr && !ICallExpr->getExprLoc().isInvalid()) {
+    llvm::SmallVector<ExprPrintingResult> Result;
+    if (!ICallExpr->getCallee()->getExprLoc().isInvalid())
+      Result.push_back({{"function call result"}, ICallExpr});
+    if (const auto *SubExpr = ICallExpr->getCallee();
+        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
+      Result.append(FormatSrcExprForSema(SubExpr));
+    }
+    return Result;
+  }
+  if (const auto *IMemberExpr = llvm::dyn_cast<MemberExpr>(PureExpr);
+      IMemberExpr && !IMemberExpr->getExprLoc().isInvalid()) {
+    llvm::SmallVector<ExprPrintingResult> Result;
+    Result.push_back({{"member access"}, IMemberExpr});
+    if (const auto *SubExpr = IMemberExpr->getBase();
+        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
+      Result.append(FormatSrcExprForSema(SubExpr));
+    }
+    return Result;
+  }
+
+  if (const auto *ICCExpr = llvm::dyn_cast<CXXConstructExpr>(PureExpr)) {
+    if (ICCExpr->getNumArgs() > 0) {
+      if (const auto *SubExpr = ICCExpr->getArg(0);
+          SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
+        return FormatSrcExprForSema(SubExpr);
+      }
+    }
+  }
+  if (const auto *ITempExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(PureExpr)) {
+    if (const auto *SubExpr = ITempExpr->getSubExpr();
+        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
+      return FormatSrcExprForSema(SubExpr);
+    }
+  }
+
+  return {};
+}
+} // namespace clang::lifetimes
+
 namespace clang::lifetimes::internal {
 
 std::optional<llvm::SmallVector<AssignmentPair>>
-getAliasList(const AssignmentQueryContext &Context, const UseFact *UF,
-             const LoanID End, const bool InOneBlock) {
-  const CFGBlock *IssueBlock =
+getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
+             const LoanID End, const Expr *IssueExpr) {
+  const auto *UF = llvm::dyn_cast<UseFact>(CausingFact);
+  const CFGBlock *StartBlock =
       Context.ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
-  assert(IssueBlock && "Searching CFGBlock failed");
+  assert(StartBlock && "Searching CFGBlock failed");
+  const CFGBlock *EndBlock = Context.ADC.getCFGStmtMap()->getBlock(IssueExpr);
+  assert(EndBlock && "Searching CFGBlock failed");
 
   for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
        Cur = Cur->peelOuterOrigin()) {
     auto TargetOID = Cur->getOuterOriginID();
-    if (InOneBlock) {
+    if (StartBlock == EndBlock) {
       AliasAssignmentSearchResult Result =
-          getAliasListCore(Context, IssueBlock, End, &TargetOID);
+          getAliasListCore(Context, StartBlock, End, &TargetOID);
       if (!Result.Payload.empty())
         return Result.Payload;
     } else {
       auto Result =
-          getAliasListInMultiBlock(Context, IssueBlock, End, &TargetOID);
+          getAliasListInMultiBlock(Context, StartBlock, End, &TargetOID);
       if (Result.has_value())
         return Result.value();
     }
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 802bdb25cd325..a17f1da16be75 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -254,17 +254,13 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           const struct AssignmentQueryContext Context = {
               LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
-          const CFGStmtMap *CurrCFGStmtMap = ADC.getCFGStmtMap();
-          const auto AliasExprs =
-              getAliasList(Context, UF, LID,
-                           CurrCFGStmtMap->getBlock(UF->getUseExpr()) ==
-                               CurrCFGStmtMap->getBlock(IssueExpr));
+          const auto AliasExprs = getAliasList(Context, UF, LID, IssueExpr);
           if (!AliasExprs.has_value()) {
             llvm::dbgs() << "Search variable assignment chain failed\n";
           }
 
           SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
-                                         AliasExprs.value_or({}), ExpiryLoc);
+                                         AliasExprs, ExpiryLoc);
         }
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 5d4872f3a38f8..d7132fbfe345f 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -38,15 +38,47 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) {
                           D->getBeginLoc());
 }
 
+inline void reportAssignmentImpl(Sema &S, const Expr *IssueExpr,
+                                 const ValueDecl *LHS, const Expr *RHS,
+                                 const SourceLocation LHSExploc) {
+  const auto [IssueMsg, _] = FormatIssueExprForSema(IssueExpr);
+  const auto SrcMsgList = FormatSrcExprForSema(RHS);
+  if (SrcMsgList.size() == 1 &&
+      llvm::isa<DeclRefExpr>(SrcMsgList[0].CurrExpr)) {
+    S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
+        << FormatValueDeclForSema(LHS) << IssueMsg;
+  } else {
+    for (const auto &SrcMsg : llvm::reverse(SrcMsgList))
+      S.Diag(RHS->getBeginLoc(), diag::note_lifetime_safety_note_alias_chain)
+          << SrcMsg.CurrExpr->getSourceRange() << SrcMsg.Value << IssueMsg;
+    S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
+        << FormatValueDeclForSema(LHS) << IssueMsg;
+  }
+}
+
+inline void reportAssignment(Sema &S, const Expr *IssueExpr,
+                             const OriginDestExpr &LHS, const Expr *RHS) {
+  if (!LHS || !RHS) {
+    return;
+  }
+
+  if (const DeclRefExpr *LDExpr = llvm::dyn_cast<const DeclRefExpr *>(LHS)) {
+    reportAssignmentImpl(S, IssueExpr, LDExpr->getDecl(), RHS,
+                         LDExpr->getExprLoc());
+  } else if (const ValueDecl *LVDecl = llvm::dyn_cast<const ValueDecl *>(LHS)) {
+    reportAssignmentImpl(S, IssueExpr, LVDecl, RHS, LVDecl->getLocation());
+  }
+}
+
 class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
 
 public:
   LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
 
-  void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
-                          const Expr *MovedExpr,
-                          const llvm::SmallVector<AssignmentPair> AliasList,
-                          SourceLocation FreeLoc) override {
+  void reportUseAfterFree(
+      const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation FreeLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved
                      : diag::warn_lifetime_safety_use_after_scope)
@@ -56,50 +88,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
           << MovedExpr->getSourceRange();
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
 
-    for (auto AliasStmt = AliasList.rbegin(); AliasStmt != AliasList.rend();
-         ++AliasStmt) {
-      if (const auto *CurrDeclExpr =
-              llvm::dyn_cast<const DeclRefExpr *>((*AliasStmt).first)) {
-        S.Diag(CurrDeclExpr->getExprLoc(),
-               diag::note_lifetime_safety_note_alias_chain)
-            << (*AliasStmt).second->getNameAsString()
-            << CurrDeclExpr->getDecl()->getNameAsString();
-      } else if (const auto *CurrDeclExpr =
-                     llvm::dyn_cast<const CXXTemporaryObjectExpr *>(
-                         (*AliasStmt).first)) {
-        S.Diag(CurrDeclExpr->getExprLoc(),
-               diag::note_lifetime_safety_note_alias_chain)
-            << (*AliasStmt).second->getNameAsString()
-            << CurrDeclExpr->getConstructor()->getNameAsString() + "()";
-      } else if (const auto *CurrCallExpr =
-                     llvm::dyn_cast<const CallExpr *>((*AliasStmt).first)) {
-        std::string OutStr;
-        llvm::raw_string_ostream OutStream(OutStr);
-        LangOptions Lo;
-        PrintingPolicy Policy(Lo);
-
-        if (const Expr *Callee = CurrCallExpr->getCallee()) {
-          Callee->IgnoreParenCasts()->printPretty(OutStream, nullptr, Policy);
-        }
-
-        OutStream << "(";
-        for (size_t i = 0; i < CurrCallExpr->getNumArgs(); ++i) {
-          const Expr *CurrArg = CurrCallExpr->getArg(i);
-          if (CurrArg) {
-            CurrArg->printPretty(OutStream, nullptr, Policy);
-          }
-
-          if (i < CurrCallExpr->getNumArgs() - 1) {
-            OutStream << ", ";
-          }
-        }
-        OutStream << ")";
-
-        S.Diag(CurrCallExpr->getExprLoc(),
-               diag::note_lifetime_safety_note_alias_chain)
-            << (*AliasStmt).second->getNameAsString() << OutStream.str();
-      }
-    }
+    if (AliasList.has_value())
+      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
+        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
         << UseExpr->getSourceRange();
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index acef9844a91c0..2a7884e98c660 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -76,6 +76,8 @@ struct Y {
 
 void dangligGslPtrFromTemporary() {
   MyIntPointer p = Y{}.a; // cfg-warning {{object whose reference is captured does not live long enough}} \
+                          // cfg-note {{member access aliases the storage of the temporary}} \
+                          // cfg-note {{variable 'p' aliases the storage of the temporary}} \
                           // cfg-note {{destroyed here}}
   (void)p;                // cfg-note {{later used here}}
 }
@@ -146,15 +148,15 @@ MyLongPointerFromConversion global2;
 void initLocalGslPtrWithTempOwner() {
   MyIntPointer p = MyIntOwner{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
                                  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                 // cfg-note {{variable `p` is now an alias of `MyIntOwner()`}}
+                                 // cfg-note {{variable 'p' aliases the storage of the temporary}}
   use(p);                        // cfg-note {{later used here}}
 
   MyIntPointer pp = p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' will be}} \
                                       // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                      // cfg-note {{variable `p` is now an alias of `MyIntOwner()`}}
+                                      // cfg-note {{variable 'p' aliases the storage of the temporary}}
   use(p, pp);                         // cfg-note {{later used here}}
 
-  p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} cfg-note {{variable `p` is now an alias of `MyIntOwner()`}} \
+  p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} cfg-note {{variable 'p' aliases the storage of the temporary}} \
                     // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(p);           // cfg-note {{later used here}}
 
@@ -162,20 +164,20 @@ void initLocalGslPtrWithTempOwner() {
   use(p, pp);
 
   global = MyIntOwner{}; // expected-warning {{object backing the pointer 'global' }} \
-                         // cfg-note {{variable `global` is now an alias of `MyIntOwner()`}} \
+                         // cfg-note {{variable 'global' aliases the storage of the temporary}} \
                          // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(global);           // cfg-note {{later used here}}
 
   MyLongPointerFromConversion p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
                                                                 // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                                // cfg-note {{variable `p2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}}
+                                                                // cfg-note {{variable 'p2' aliases the storage of the temporary}}
   use(p2);                                                      // cfg-note {{later used here}}
 
   p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer 'p2' }} \
-                                    // cfg-note {{variable `p2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} \
+                                    // cfg-note {{variable 'p2' aliases the storage of the temporary}} \
                                     // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   global2 = MyLongOwnerWithConversion{};  // expected-warning {{object backing the pointer 'global2' }} \
-                                          // cfg-note {{variable `global2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} \
+                                          // cfg-note {{variable 'global2' aliases the storage of the temporary}} \
                                           // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(global2, p2);                       // cfg-note 2 {{later used here}}
 }
@@ -189,7 +191,8 @@ struct Unannotated {
 
 void modelIterators() {
   std::vector<int>::iterator it = std::vector<int>().begin(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                                              // cfg-note {{variable `it` is now an alias of `std::vector<int>().begin()`}} \
+                                                              // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                              // cfg-note {{variable 'it' aliases the storage of the temporary}} \
                                                               // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   (void)it; // cfg-note {{later used here}}
 }
@@ -238,13 +241,15 @@ int &danglingRawPtrFromLocal3() {
 // GH100384
 std::string_view containerWithAnnotatedElements() {
   std::string_view c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                                          // cfg-note {{variable `c1` is now an alias of `std::vector<std::string>().at(0).operator basic_string_view()`}} \
+                                                          // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                          // cfg-note {{variable 'c1' aliases the storage of the temporary}} \
                                                           // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(c1);                                                // cfg-note {{later used here}}
 
   c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}} \
                                          // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                         // cfg-note {{variable `c1` is now an alias of `std::vector<std::string>().at(0).operator basic_string_view()`}}
+                                         // cfg-note {{function call result aliases the storage of the temporary}} \
+                                         // cfg-note {{variable 'c1' aliases the storage of the temporary}}
   use(c1);                               // cfg-note {{later used here}}
 
   // no warning on constructing from gsl-pointer
@@ -305,28 +310,34 @@ std::string_view danglingRefToOptionalFromTemp4() {
 
 void danglingReferenceFromTempOwner() {
   int &&r = *std::optional<int>();          // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                            // cfg-note {{variable `r` is now an alias of `operator*(std::optional<int>())`}} \
+                                            // cfg-note {{expression aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 'r' aliases the storage of the temporary}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   // https://github.com/llvm/llvm-project/issues/175893
   int &&r2 = *std::optional<int>(5);        // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                            // cfg-note {{variable `r2` is now an alias of `operator*(std::optional<int>(5))`}} \
+                                            // cfg-note {{expression aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 'r2' aliases the storage of the temporary}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   // https://github.com/llvm/llvm-project/issues/175893
   int &&r3 = std::optional<int>(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                            // cfg-note {{variable `r3` is now an alias of `std::optional<int>(5).value()`}} \
+                                            // cfg-note {{function call result aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 'r3' aliases the storage of the temporary}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   const int &r4 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                            // cfg-note {{variable `r4` is now an alias of `std::vector<int>().at(3)`}} \
+                                            // cfg-note {{function call result aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 'r4' aliases the storage of the temporary}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   int &&r5 = std::vector<int>().at(3);      // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                            // cfg-note {{variable `r5` is now an alias of `std::vector<int>().at(3)`}} \
+                                            // cfg-note {{function call result aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 'r5' aliases the storage of the temporary}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(r, r2, r3, r4, r5);                   // cfg-note 5 {{later used here}}
 
   std::string_view sv = *getTempOptStr();  // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                           // cfg-note {{variable `sv` is now an alias of `* getTempOptStr().operator basic_string_view()`}} \
+                                           // cfg-note {{expression aliases the storage of the temporary}} \
+                                           // cfg-note {{variable 'sv' aliases the storage of the temporary}} \
                                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(sv);                                 // cfg-note {{later used here}}
 }
@@ -338,8 +349,10 @@ void testLoops() {
   for (auto i : getTempVec()) // ok
     ;
   for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                  // cfg-note {{variable `__range1` is now an alias of `operator*(getTempOptVec())`}} \
-                                  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}}
+                                  // cfg-note {{expression aliases the storage of the temporary}} \
+                                  // cfg-note {{variable '__range1' aliases the storage of the temporary}} \
+                                  // cfg-warning {{object whose reference is captured does not live long enough}} \
+                                  // cfg-note {{destroyed here}} cfg-note {{later used here}}
     ;
 }
 
@@ -401,7 +414,7 @@ void handleGslPtrInitsThroughReference2() {
 void handleTernaryOperator(bool cond) {
     std::basic_string<char> def;
     std::basic_string_view<char> v = cond ? def : ""; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
-                                                      // cfg-note {{variable `v` is now an alias of `cond ? def : "".operator basic_string_view()`}} \
+                                                      // cfg-note {{variable 'v' aliases the storage of the temporary}} \
                                                       // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
     use(v); // cfg-note {{later used here}}
 }
@@ -410,12 +423,13 @@ std::string operator+(std::string_view s1, std::string_view s2);
 void danglingStringviewAssignment(std::string_view a1, std::string_view a2) {
   a1 = std::string(); // expected-warning {{object backing}} \
                       // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                      // cfg-note {{variable `a1` is now an alias of `std::string().operator basic_string_view()`}}
+                      // cfg-note {{variable 'a1' aliases the storage of the temporary}}
   use(a1);            // cfg-note {{later used here}}
 
   a2 = a1 + a1; // expected-warning {{object backing}} \
                 // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                // cfg-note {{variable `a2` is now an alias of `a1 + a1.operator basic_string_view()`}}
+                // cfg-note {{expression aliases the storage of the temporary}} \
+                // cfg-note {{variable 'a2' aliases the storage of the temporary}}
   use(a2);      // cfg-note {{later used here}}
 }
 
@@ -611,6 +625,8 @@ std::string StrCat(std::string_view, std::string_view);
 void test1() {
   UrlAnalyzed url(StrCat("abc", "bcd")); // expected-warning {{object backing the pointer will be destroyed}} \
                                          // cfg-warning {{object whose reference is captured does not live long enough}} \
+                                         // cfg-note {{function call result aliases the storage of the temporary}} \
+                                         // cfg-note {{variable 'url' aliases the storage of the temporary}} \
                                          // cfg-note {{destroyed here}}
   use(url);                              // cfg-note {{later used here}}
 }
@@ -620,7 +636,8 @@ std::string_view ReturnStringView(std::string_view abc [[clang::lifetimebound]])
 void test() {
   std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
                                                                   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                                  // cfg-note {{variable `svjkk1` is now an alias of `ReturnStringView(StrCat("bar", "x"))`}}
+                                                                  // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                                  // cfg-note {{variable 'svjkk1' aliases the storage of the temporary}}
   use(svjkk1);                                                    // cfg-note {{later used here}}
 }
 } // namespace GH100549
@@ -855,7 +872,8 @@ namespace GH118064{
 void test() {
   auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}} \
   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-  // cfg-note {{variable `y` is now an alias of `std::set<int>{}.begin()`}}
+  // cfg-note {{function call result aliases the storage of the temporary}} \
+  // cfg-note {{variable 'y' aliases the storage of the temporary}}
   use(y); // cfg-note {{later used here}}
 }
 } // namespace GH118064
@@ -871,11 +889,13 @@ std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
 std::string_view test1_1() {
   std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                            // cfg-note {{variable `t1` is now an alias of `Ref(std::string()).operator basic_string_view()`}}
+                                            // cfg-note {{function call result aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 't1' aliases the storage of the temporary}}
   use(t1);                                  // cfg-note {{later used here}}
   t1 = Ref(std::string()); // expected-warning {{object backing}} \
                            // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                           // cfg-note {{variable `t1` is now an alias of `Ref(std::string()).operator basic_string_view()`}}
+                           // cfg-note {{function call result aliases the storage of the temporary}} \
+                           // cfg-note {{variable 't1' aliases the storage of the temporary}}
   use(t1);                 // cfg-note {{later used here}}
   return Ref(std::string()); // expected-warning {{returning address}} \
                              // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
@@ -884,11 +904,13 @@ std::string_view test1_1() {
 std::string_view test1_2() {
   std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                            // cfg-note {{variable `t2` is now an alias of `TakeSv(std::string())`}}
+                                            // cfg-note {{function call result aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 't2' aliases the storage of the temporary}}
   use(t2);                                  // cfg-note {{later used here}}
   t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
                               // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                              // cfg-note {{variable `t2` is now an alias of `TakeSv(std::string())`}}
+                              // cfg-note {{function call result aliases the storage of the temporary}} \
+                              // cfg-note {{variable 't2' aliases the storage of the temporary}}
   use(t2);                    // cfg-note {{later used here}}
 
   return TakeSv(std::string()); // expected-warning {{returning address}} \
@@ -897,11 +919,13 @@ std::string_view test1_2() {
 
 std::string_view test1_3() {
   std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \
-                                                   // cfg-note {{variable `t3` is now an alias of `TakeStrRef(std::string())`}} \
+                                                   // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                   // cfg-note {{variable 't3' aliases the storage of the temporary}} \
                                                    // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(t3);                                         // cfg-note {{later used here}}
   t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \
-                                  // cfg-note {{variable `t3` is now an alias of `TakeStrRef(std::string())`}} \
+                                  // cfg-note {{function call result aliases the storage of the temporary}} \
+                                  // cfg-note {{variable 't3' aliases the storage of the temporary}} \
                                   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(t3);                        // cfg-note {{later used here}}
   return TakeStrRef(std::string()); // expected-warning {{returning address}} \
@@ -924,12 +948,14 @@ struct Foo {
 };
 std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
   std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
-                                                  // cfg-note {{variable `t1` is now an alias of `Foo<std::string>().get().operator basic_string_view()`}} \
+                                                  // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                  // cfg-note {{variable 't1' aliases the storage of the temporary}} \
                                                   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   use(t1);                                        // cfg-note {{later used here}}
   t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
                                  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                 // cfg-note {{variable `t1` is now an alias of `Foo<std::string>().get().operator basic_string_view()`}}
+                                 // cfg-note {{function call result aliases the storage of the temporary}} \
+                                 // cfg-note {{variable 't1' aliases the storage of the temporary}}
   use(t1);                       // cfg-note {{later used here}}
   return r1.get(); // expected-warning {{address of stack}} \
                    // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
@@ -1048,11 +1074,16 @@ void operator_star_arrow_reference() {
 
   auto temporary = []() { return std::vector<std::string>{{"1"}}; };
   const char* x = temporary().begin()->data();    // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                  // cfg-note {{variable `x` is now an alias of `temporary().begin()->data()`}}
+                                                  // cfg-note {{expression aliases the storage of the temporary}} \
+                                                  // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                  // cfg-note {{variable 'x' aliases the storage of the temporary}}
   const char* y = (*temporary().begin()).data();  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                  // cfg-note {{variable `y` is now an alias of `(* temporary().begin()).data()`}}
+                                                  // cfg-note {{expression aliases the storage of the temporary}} \
+                                                  // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                  // cfg-note {{variable 'y' aliases the storage of the temporary}}
   const std::string& z = (*temporary().begin());  // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                  // cfg-note {{variable `z` is now an alias of `operator*(temporary().begin())`}}
+                                                  // cfg-note {{expression aliases the storage of the temporary}} \
+                                                  // cfg-note {{variable 'z' aliases the storage of the temporary}}
 
   use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
 }
@@ -1065,11 +1096,18 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
 
   auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, "1"}}; };
   const char* x = temporary().begin()->second.data();   // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                        // cfg-note {{variable `x` is now an alias of `temporary().begin()->second.data()`}}
+                                                        // cfg-note {{expression aliases the storage of the temporary}} \
+                                                        // cfg-note {{member access aliases the storage of the temporary}} \
+                                                        // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                        // cfg-note {{variable 'x' aliases the storage of the temporary}}
   const char* y = (*temporary().begin()).second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                        // cfg-note {{variable `y` is now an alias of `(* temporary().begin()).second.data()`}}
+                                                        // cfg-note {{expression aliases the storage of the temporary}} \
+                                                        // cfg-note {{member access aliases the storage of the temporary}} \
+                                                        // cfg-note {{function call result aliases the storage of the temporary}} \
+                                                        // cfg-note {{variable 'y' aliases the storage of the temporary}}
   const std::string& z = (*temporary().begin()).second; // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                                        // cfg-note {{variable `z` is now an alias of `operator*(temporary().begin())`}}
+                                                        // cfg-note {{expression aliases the storage of the temporary}} \
+                                                        // cfg-note {{variable 'z' aliases the storage of the temporary}}
 
   use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
 }
@@ -1119,20 +1157,23 @@ std::string_view foo(std::string_view sv [[clang::lifetimebound]]);
 void test1() {
   std::string_view k1 = S().sv; // OK
   std::string_view k2 = S().s; // expected-warning {{object backing the pointer will}} \
-                               // cfg-note {{variable `k2` is now an alias of `S().s.operator basic_string_view()`}} \
+                               // cfg-note {{variable 'k2' aliases the storage of the temporary}} \
                                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   std::string_view k3 = Q().get()->sv; // OK
   std::string_view k4  = Q().get()->s; // expected-warning {{object backing the pointer will}} \
                                        // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
-                                       // cfg-note {{variable `k4` is now an alias of `Q().get()->s.operator basic_string_view()`}}
+                                       // cfg-note {{function call result aliases the storage of the temporary}} \
+                                       // cfg-note {{variable 'k4' aliases the storage of the temporary}}
 
 
   std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \
-                                     // cfg-note {{variable `lb1` is now an alias of `foo(S().s)`}} \
+                                     // cfg-note {{function call result aliases the storage of the temporary}} \
+                                     // cfg-note {{variable 'lb1' aliases the storage of the temporary}} \
                                      // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
   std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \
-                                            // cfg-note {{variable `lb2` is now an alias of `foo(Q().get()->s)`}} \
+                                            // cfg-note {{function call result aliases the storage of the temporary}} \
+                                            // cfg-note {{variable 'lb2' aliases the storage of the temporary}} \
                                             // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   use(k1, k2, k3, k4, lb1, lb2);  // cfg-note 4 {{later used here}}
diff --git a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
index 51cd8a6e5b349..2b047878b21cb 100644
--- a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
@@ -27,7 +27,8 @@ void single_block_cfg() {
   MyObj* p;
   {
     MyObj s;
-    p = &s;     // bailout-warning {{object whose reference is captured does not live long enough}} bailout-note {{variable `p` is now an alias of `s`}}
+    p = &s;     // bailout-warning {{object whose reference is captured does not live long enough}} \
+                // bailout-note {{variable 'p' aliases the storage of variable 's'}}
   }             // bailout-note {{destroyed here}}
   (void)*p;     // bailout-note {{later used here}}
 }
@@ -39,7 +40,8 @@ void multiple_block_cfg() {
   {
     if (a > 5) {
       MyObj s;
-      p = &s;    // nobailout-warning {{object whose reference is captured does not live long enough}} nobailout-note {{variable `p` is now an alias of `s`}}
+      p = &s;    // nobailout-warning {{object whose reference is captured does not live long enough}} \
+                 // nobailout-note {{variable 'p' aliases the storage of variable 's'}}
     } else {     // nobailout-note {{destroyed here}}
       p = &safe;
     }     
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 14d04c63215db..4753339309445 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -222,21 +222,24 @@ View return_view_field(const ViewProvider& v) {    // expected-warning {{paramet
 void test_get_on_temporary_pointer() {
   const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{object whose reference is captured does not live long enough}}.
                                                    // expected-note at -1 {{destroyed here}}.
-                                                   // expected-note at -2 {{variable `s_ref` is now an alias of `ReturnsSelf().get()`}}
+                                                   // expected-note at -2 {{function call result aliases the storage of the temporary}}
+                                                   // expected-note at -3 {{variable 's_ref' aliases the storage of the temporary}}
   (void)s_ref;                                     // expected-note {{later used here}}
 }
 
 void test_get_on_temporary_ref() {
   const ReturnsSelf& s_ref = ReturnsSelf().get();  // expected-warning {{object whose reference is captured does not live long enough}}.
                                                    // expected-note at -1 {{destroyed here}}.
-                                                   // expected-note at -2 {{variable `s_ref` is now an alias of `ReturnsSelf().get()`}}
+                                                   // expected-note at -2 {{function call result aliases the storage of the temporary}}
+                                                   // expected-note at -3 {{variable 's_ref' aliases the storage of the temporary}}
   (void)s_ref;                                     // expected-note {{later used here}}
 }
 
 void test_getView_on_temporary() {
   View sv = ViewProvider{1}.getView();      // expected-warning {{object whose reference is captured does not live long enough}}.
                                             // expected-note at -1 {{destroyed here}}.
-                                            // expected-note at -2 {{variable `sv` is now an alias of `ViewProvider{1}.getView()`}}
+                                            // expected-note at -2 {{function call result aliases the storage of the temporary}}
+                                            // expected-note at -3 {{variable 'sv' aliases the storage of the temporary}}
   (void)sv;                                 // expected-note {{later used here}}
 }
 
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 38744e08f49db..2e432515ac70c 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -55,7 +55,7 @@ void simple_case() {
   {
     MyObj s;
     p = &s;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `s`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -65,7 +65,7 @@ void simple_case_gsl() {
   {
     MyObj s;
     v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `s`}}
+                // expected-note {{variable 'v' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -94,8 +94,8 @@ void pointer_chain() {
   {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `s`}}
-    q = p;      // expected-note {{variable `q` is now an alias of `p`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+    q = p;      // expected-note {{variable 'q' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   (void)*q;     // expected-note {{later used here}}
 }
@@ -105,8 +105,8 @@ void propagation_gsl() {
   {
     MyObj s;
     v1 = s;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v1` is now an alias of `s`}}
-    v2 = v1;    // expected-note {{`v2` is now an alias of `v1`}}
+                // expected-note {{variable 'v1' aliases the storage of variable 's'}}
+    v2 = v1;    // expected-note {{variable 'v2' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   v2.use();     // expected-note {{later used here}}
 }
@@ -116,7 +116,7 @@ void multiple_uses_one_warning() {
   {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `s`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   // No second warning for the same loan.
@@ -130,11 +130,11 @@ void multiple_pointers() {
   {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `s`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
     q = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `q` is now an alias of `s`}}
+                // expected-note {{variable 'q' aliases the storage of variable 's'}}
     r = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `r` is now an alias of `s`}}
+                // expected-note {{variable 'r' aliases the storage of variable 's'}}
   }             // expected-note 3 {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   (void)*q;     // expected-note {{later used here}}
@@ -146,12 +146,12 @@ void single_pointer_multiple_loans(bool cond) {
   if (cond){
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{`p` is now an alias of `s`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   else {
     MyObj t;
     p = &t;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `t`}}
+                // expected-note {{variable 'p' aliases the storage of variable 't'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note 2  {{later used here}}
 }
@@ -161,12 +161,12 @@ void single_pointer_multiple_loans_gsl(bool cond) {
   if (cond){
     MyObj s;
     v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `s`}}
+                // expected-note {{variable 'v' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   else {
     MyObj t;
     v = t;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `t`}}
+                // expected-note {{variable 'v' aliases the storage of variable 't'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note 2 {{later used here}}
 }
@@ -177,7 +177,7 @@ void if_branch(bool cond) {
   if (cond) {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `temp`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -188,7 +188,7 @@ void if_branch_potential(bool cond) {
   if (cond) {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `temp`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
   }             // expected-note {{destroyed here}}
   if (!cond)
     (void)*p;   // expected-note {{later used here}}
@@ -202,7 +202,7 @@ void if_branch_gsl(bool cond) {
   if (cond) {
     MyObj temp;
     v = temp;   // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `temp`}}
+                // expected-note {{variable 'v' aliases the storage of variable 'temp'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -216,10 +216,10 @@ void potential_together(bool cond) {
     MyObj s;
     if (cond)
       p_definite = &s;  // expected-warning {{does not live long enough}} \
-                        // expected-note {{variable `p_definite` is now an alias of `s`}}
+                        // expected-note {{variable 'p_definite' aliases the storage of variable 's'}}
     if (cond)
       p_maybe = &s;     // expected-warning {{does not live long enough}} \
-                        // expected-note {{variable `p_maybe` is now an alias of `s`}}
+                        // expected-note {{variable 'p_maybe' aliases the storage of variable 's'}}
   }                     // expected-note 2 {{destroyed here}}
   (void)*p_definite;    // expected-note {{later used here}}
   if (!cond)
@@ -233,8 +233,8 @@ void overrides_potential(bool cond) {
   {
     MyObj s;
     q = &s;       // expected-warning {{does not live long enough}} \
-                  // expected-note {{variable `q` is now an alias of `s`}}
-    p = q;        // expected-note {{variable `p` is now an alias of `q`}}
+                  // expected-note {{variable 'q' aliases the storage of variable 's'}}
+    p = q;        // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }               // expected-note {{destroyed here}}
 
   if (cond) {
@@ -254,7 +254,7 @@ void due_to_conditional_killing(bool cond) {
   {
     MyObj s;
     q = &s;       // expected-warning {{does not live long enough}} \
-                  // expected-note {{variable `q` is now an alias of `s`}}
+                  // expected-note {{variable 'q' aliases the storage of variable 's'}}
   }               // expected-note {{destroyed here}}
   if (cond) {
     // 'q' is conditionally "rescued". 'p' is not.
@@ -268,7 +268,7 @@ void for_loop_use_after_loop_body(MyObj safe) {
   for (int i = 0; i < 1; ++i) {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `s`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -289,7 +289,7 @@ void for_loop_gsl() {
   for (int i = 0; i < 1; ++i) {
     MyObj s;
     v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `s`}}
+                // expected-note {{variable 'v' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -301,7 +301,7 @@ void for_loop_use_before_loop_body(MyObj safe) {
     (void)*p;   // expected-note {{later used here}}
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `s`}}
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;
 }
@@ -313,7 +313,7 @@ void loop_with_break(bool cond) {
     if (cond) {
       MyObj temp;
       p = &temp; // expected-warning {{does not live long enough}} \
-                 // expected-note {{variable `p` is now an alias of `temp`}}
+                 // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
       break;     // expected-note {{destroyed here}}
     }           
   } 
@@ -327,7 +327,7 @@ void loop_with_break_gsl(bool cond) {
     if (cond) {
       MyObj temp;
       v = temp;   // expected-warning {{object whose reference is captured does not live long enough}} \
-                  // expected-note {{variable `v` is now an alias of `temp`}}
+                  // expected-note {{variable 'v' aliases the storage of variable 'temp'}}
       break;      // expected-note {{destroyed here}}
     }
   }
@@ -342,7 +342,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     MyObj unsafe;
     if (cond) {
       p = &unsafe; // expected-warning {{does not live long enough}} \
-                   // expected-note {{variable `p` is now an alias of `unsafe`}}
+                   // expected-note {{variable 'p' aliases the storage of variable 'unsafe'}}
       break;       // expected-note {{destroyed here}}
     }
   }
@@ -353,7 +353,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     MyObj unsafe;
     if (cond) {
       p = &unsafe;    // expected-warning {{does not live long enough}} \
-                      // expected-note {{variable `p` is now an alias of `unsafe`}}
+                      // expected-note {{variable 'p' aliases the storage of variable 'unsafe'}}
       if (cond)
         break;        // expected-note {{destroyed here}}
     }
@@ -365,7 +365,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     if (cond) {
       MyObj unsafe2;
       p = &unsafe2;   // expected-warning {{does not live long enough}} \
-                      // expected-note {{variable `p` is now an alias of `unsafe2`}}
+                      // expected-note {{variable 'p' aliases the storage of variable 'unsafe2'}}
       break;          // expected-note {{destroyed here}}
     }
   }
@@ -376,7 +376,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     MyObj unsafe;
     if (cond)
       p = &unsafe;    // expected-warning {{does not live long enough}} \
-                      // expected-note {{variable `p` is now an alias of `unsafe`}}
+                      // expected-note {{variable 'p' aliases the storage of variable 'unsafe'}}
     if (cond)
       break;          // expected-note {{destroyed here}}
   }
@@ -390,7 +390,7 @@ void switch_potential(int mode) {
   case 1: {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `temp`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
@@ -410,19 +410,19 @@ void switch_uaf(int mode) {
   case 1: {
     MyObj temp1;
     p = &temp1; // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `temp1`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'temp1'}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
     MyObj temp2;
     p = &temp2; // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `temp2`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'temp2'}}
     break;      // expected-note {{destroyed here}}
   }
   default: {
     MyObj temp2;
     p = &temp2; // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `temp2`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'temp2'}}
     break;      // expected-note {{destroyed here}}
   }
   }
@@ -435,19 +435,19 @@ void switch_gsl(int mode) {
   case 1: {
     MyObj temp1;
     v = temp1;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `temp1`}}
+                // expected-note {{variable 'v' aliases the storage of variable 'temp1'}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
     MyObj temp2;
     v = temp2;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `temp2`}}
+                // expected-note {{variable 'v' aliases the storage of variable 'temp2'}}
     break;      // expected-note {{destroyed here}}
   }
   default: {
     MyObj temp3;
     v = temp3;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `v` is now an alias of `temp3`}}
+                // expected-note {{variable 'v' aliases the storage of variable 'temp3'}}
     break;      // expected-note {{destroyed here}}
   }
   }
@@ -461,7 +461,7 @@ void loan_from_previous_iteration(MyObj safe, bool condition) {
   while (condition) {
     MyObj x;
     p = &x;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `x`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'x'}}
 
     if (condition)
       q = p;
@@ -475,7 +475,7 @@ void trivial_int_uaf() {
   {
       int b = 1;
       a = &b;  // expected-warning {{object whose reference is captured does not live long enough}} \
-               // expected-note {{variable `a` is now an alias of `b`}}
+               // expected-note {{variable 'a' aliases the storage of variable 'b'}}
   }            // expected-note {{destroyed here}}
   (void)*a;    // expected-note {{later used here}}
 }
@@ -485,7 +485,7 @@ void trivial_class_uaf() {
   {
       TriviallyDestructedClass s;
       ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `ptr` is now an alias of `s`}}
+                // expected-note {{variable 'ptr' aliases the storage of variable 's'}}
   }             // expected-note {{destroyed here}}
   (void)ptr;    // expected-note {{later used here}}
 }
@@ -678,7 +678,7 @@ void test_view_pointer() {
   {
     View v;
     vp = &v;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable `vp` is now an alias of `v`}}
+                 // expected-note {{variable 'vp' aliases the storage of variable 'v'}}
   }              // expected-note {{destroyed here}}
   vp->use();     // expected-note {{later used here}}
 }
@@ -688,7 +688,7 @@ void test_view_double_pointer() {
   {
     View* vp = nullptr;
     vpp = &vp;   // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable `vpp` is now an alias of `vp`}}
+                 // expected-note {{variable 'vpp' aliases the storage of variable 'vp'}}
   }              // expected-note {{destroyed here}}
   (**vpp).use(); // expected-note {{later used here}}
 }
@@ -716,8 +716,9 @@ void test_lifetimebound_multi_level() {
     int* p = nullptr;
     int** pp = &p;
     int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{variable `ppp` is now an alias of `pp`}}
-    result = return_inner_ptr_addr(ppp); // expected-note {{variable `result` is now an alias of `return_inner_ptr_addr(ppp)`}}
+                      // expected-note {{variable 'ppp' aliases the storage of variable 'pp'}}
+    result = return_inner_ptr_addr(ppp); // expected-note {{function call result aliases the storage of variable 'pp'}} \
+                                         // expected-note {{variable 'result' aliases the storage of variable 'pp'}}
   }                   // expected-note {{destroyed here}}
   (void)**result;     // expected-note {{used here}}
 }
@@ -751,7 +752,7 @@ MyObj* uaf_before_uar() {
   {
     MyObj local_obj;
     p = &local_obj;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable `p` is now an alias of `local_obj`}}
+                     // expected-note {{variable 'p' aliases the storage of variable 'local_obj'}}
   }                  // expected-note {{destroyed here}}
   return p;          // expected-note {{later used here}}
 }
@@ -842,7 +843,8 @@ void lifetimebound_simple_function() {
   {
     MyObj obj;
     v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}} \
-                       // expected-note {{variable `v` is now an alias of `Identity(obj)`}}
+                       // expected-note {{function call result aliases the storage of variable 'obj'}} \
+                       // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   }                    // expected-note {{destroyed here}}
   v.use();             // expected-note {{later used here}}
 }
@@ -851,8 +853,10 @@ void lifetimebound_multiple_args_definite() {
   View v;
   {
     MyObj obj1, obj2;
-    v = Choose(true,  // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} \
-                      // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}}
+    v = Choose(true,  // expected-note {{function call result aliases the storage of variable 'obj1'}} \
+                      // expected-note {{variable 'v' aliases the storage of variable 'obj1'}} \
+                      // expected-note {{function call result aliases the storage of variable 'obj2'}} \
+                      // expected-note {{variable 'v' aliases the storage of variable 'obj2'}}
                obj1,  // expected-warning {{object whose reference is captured does not live long enough}}
                obj2); // expected-warning {{object whose reference is captured does not live long enough}}
   }                              // expected-note 2 {{destroyed here}}
@@ -866,8 +870,10 @@ void lifetimebound_multiple_args_potential(bool cond) {
     MyObj obj1;
     if (cond) {
       MyObj obj2;
-      v = Choose(true,             // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} \
-                                   // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}}
+      v = Choose(true,             // expected-note {{function call result aliases the storage of variable 'obj1'}} \
+                                   // expected-note {{variable 'v' aliases the storage of variable 'obj1'}} \
+                                   // expected-note {{function call result aliases the storage of variable 'obj2'}} \
+                                   // expected-note {{variable 'v' aliases the storage of variable 'obj2'}}
                  obj1,             // expected-warning {{object whose reference is captured does not live long enough}}
                  obj2);            // expected-warning {{object whose reference is captured does not live long enough}}
     }                              // expected-note {{destroyed here}}
@@ -881,7 +887,8 @@ void lifetimebound_mixed_args() {
   {
     MyObj obj1, obj2;
     v = SelectFirst(obj1,        // expected-warning {{object whose reference is captured does not live long enough}} \
-                                 // expected-note {{variable `v` is now an alias of `SelectFirst(obj1, obj2)`}}
+                                 // expected-note {{function call result aliases the storage of variable 'obj1'}} \
+                                 // expected-note {{variable 'v' aliases the storage of variable 'obj1'}}
                     obj2);
   }                              // expected-note {{destroyed here}}
   v.use();                       // expected-note {{later used here}}
@@ -898,7 +905,8 @@ void lifetimebound_member_function() {
   {
     MyObj obj;
     v  = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                        // expected-note {{variable `v` is now an alias of `obj.getView()`}}
+                        // expected-note {{function call result aliases the storage of variable 'obj'}} \
+                        // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   }                     // expected-note {{destroyed here}}
   v.use();              // expected-note {{later used here}}
 }
@@ -914,7 +922,7 @@ void lifetimebound_conversion_operator() {
   {
     LifetimeBoundConversionView obj;
     v = obj;  // expected-warning {{object whose reference is captured does not live long enough}} \
-              // expected-note {{variable `v` is now an alias of `obj.operator View()`}}
+              // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   }           // expected-note {{destroyed here}}
   v.use();    // expected-note {{later used here}}
 }
@@ -924,7 +932,8 @@ void lifetimebound_chained_calls() {
   {
     MyObj obj;
     v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}} \
-                                           // expected-note {{variable `v` is now an alias of `Identity(Identity(Identity(obj)))`}}
+                                           // expected-note {{function call result aliases the storage of variable 'obj'}} \
+                                           // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   }                                        // expected-note {{destroyed here}}
   v.use();                                 // expected-note {{later used here}}
 }
@@ -934,7 +943,8 @@ void lifetimebound_with_pointers() {
   {
     MyObj obj;
     ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}} \
-                           // expected-note {{variable `ptr` is now an alias of `GetPointer(obj)`}}
+                           // expected-note {{function call result aliases the storage of variable 'obj'}} \
+                           // expected-note {{variable 'ptr' aliases the storage of variable 'obj'}}
   }                        // expected-note {{destroyed here}}
   (void)*ptr;              // expected-note {{later used here}}
 }
@@ -952,7 +962,8 @@ void lifetimebound_partial_safety(bool cond) {
   
   if (cond) {
     MyObj temp_obj;
-    v = Choose(true,       // expected-note {{variable `v` is now an alias of `Choose(true, safe_obj, temp_obj)`}}
+    v = Choose(true,      // expected-note {{function call result aliases the storage of variable 'temp_obj'}} \
+                          // expected-note {{variable 'v' aliases the storage of variable 'temp_obj'}}
                safe_obj,
                temp_obj); // expected-warning {{object whose reference is captured does not live long enough}}
   }                       // expected-note {{destroyed here}}
@@ -966,9 +977,10 @@ void lifetimebound_return_reference() {
   {
     MyObj obj;
     View temp_v = obj;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                           // expected-note {{variable `temp_v` is now an alias of `obj`}}
-    const MyObj& ref = GetObject(temp_v); // expected-note {{variable `ref` is now an alias of `GetObject(temp_v)`}}
-    ptr = &ref;           // expected-note {{variable `ptr` is now an alias of `ref`}}
+                           // expected-note {{variable 'temp_v' aliases the storage of variable 'obj'}}
+    const MyObj& ref = GetObject(temp_v); // expected-note {{function call result aliases the storage of variable 'obj'}} \
+                                          // expected-note {{variable 'ref' aliases the storage of variable 'obj'}}
+    ptr = &ref;           // expected-note {{variable 'ptr' aliases the storage of variable 'obj'}}
   }                       // expected-note {{destroyed here}}
   (void)*ptr;             // expected-note {{later used here}}
 }
@@ -982,7 +994,8 @@ void lifetimebound_ctor() {
   LifetimeBoundCtor v;
   {
     MyObj obj;
-    v = obj; // expected-warning {{object whose reference is captured does not live long enough}}
+    v = obj; // expected-warning {{object whose reference is captured does not live long enough}} \
+             // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   }          // expected-note {{destroyed here}}
   (void)v;   // expected-note {{later used here}}
 }
@@ -1059,7 +1072,7 @@ void conditional_operator_one_unsafe_branch(bool cond) {
   {
     MyObj temp;
     p = cond ? &temp  // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{variable `p` is now an alias of `temp`}}
+                      // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
              : &safe;
   }  // expected-note {{destroyed here}}
 
@@ -1076,9 +1089,9 @@ void conditional_operator_two_unsafe_branches(bool cond) {
   {
     MyObj a, b;
     p = cond ? &a   // expected-warning {{object whose reference is captured does not live long enough}} \
-                    // expected-note {{variable `p` is now an alias of `a`}}
-             : &b;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                    // expected-note {{variable `p` is now an alias of `b`}}
+                    // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
+                    // expected-note {{variable 'p' aliases the storage of variable 'b'}}
+             : &b;  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
 }
@@ -1088,13 +1101,13 @@ void conditional_operator_nested(bool cond) {
   {
     MyObj a, b, c, d;
     p = cond ? cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}}. \
-                            // expected-note {{variable `p` is now an alias of `a`}}
-                    : &b    // expected-warning {{object whose reference is captured does not live long enough}}. \
-                            // expected-note {{variable `p` is now an alias of `b`}}
-             : cond ? &c    // expected-warning {{object whose reference is captured does not live long enough}}. \
-                            // expected-note {{variable `p` is now an alias of `c`}}
-                    : &d;   // expected-warning {{object whose reference is captured does not live long enough}}. \
-                            // expected-note {{variable `p` is now an alias of `d`}}
+                            // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
+                            // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
+                            // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
+                            // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+                    : &b    // expected-warning {{object whose reference is captured does not live long enough}}.
+             : cond ? &c    // expected-warning {{object whose reference is captured does not live long enough}}.
+                    : &d;   // expected-warning {{object whose reference is captured does not live long enough}}.
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 }
@@ -1104,8 +1117,10 @@ void conditional_operator_lifetimebound(bool cond) {
   {
     MyObj a, b;
     p = Identity(cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}} \
-                              // expected-note {{variable `p` is now an alias of `Identity(cond ? &a : &b)`}} \
-                              // expected-note {{variable `p` is now an alias of `Identity(cond ? &a : &b)`}}
+                              // expected-note {{function call result aliases the storage of variable 'b'}} \
+                              // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
+                              // expected-note {{function call result aliases the storage of variable 'a'}} \
+                              // expected-note {{variable 'p' aliases the storage of variable 'a'}}
                       : &b);  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1116,8 +1131,10 @@ void conditional_operator_lifetimebound_nested(bool cond) {
   {
     MyObj a, b;
     p = Identity(cond ? Identity(&a)    // expected-warning {{object whose reference is captured does not live long enough}} \
-                                        // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(&a) : Identity(&b))`}} \
-                                        // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(&a) : Identity(&b))`}}
+                                        // expected-note {{function call result aliases the storage of variable 'b'}} \
+                                        // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
+                                        // expected-note {{function call result aliases the storage of variable 'a'}} \
+                                        // expected-note {{variable 'p' aliases the storage of variable 'a'}}
                       : Identity(&b));  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1128,10 +1145,14 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
   {
     MyObj a, b, c, d;
     p = Identity(cond ? Identity(cond ? &a     // expected-warning {{object whose reference is captured does not live long enough}} \
-                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \
-                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \
-                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \
-                                               // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}}
+                                               // expected-note {{function call result aliases the storage of variable 'a'}} \
+                                               // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
+                                               // expected-note {{function call result aliases the storage of variable 'b'}} \
+                                               // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
+                                               // expected-note {{function call result aliases the storage of variable 'c'}} \
+                                               // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
+                                               // expected-note {{function call result aliases the storage of variable 'd'}} \
+                                               // expected-note {{variable 'p' aliases the storage of variable 'd'}}
                                       : &b)    // expected-warning {{object whose reference is captured does not live long enough}}
                       : Identity(cond ? &c     // expected-warning {{object whose reference is captured does not live long enough}}
                                       : &d));  // expected-warning {{object whose reference is captured does not live long enough}}
@@ -1144,30 +1165,39 @@ void parentheses(bool cond) {
   {
     MyObj a;
     p = &((((a))));  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable `p` is now an alias of `a`}}
+                     // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 
   {
     MyObj a;
     p = ((GetPointer((a))));  // expected-warning {{object whose reference is captured does not live long enough}} \
-                              // expected-note {{variable `p` is now an alias of `GetPointer((a))`}}
+                              // expected-note {{function call result aliases the storage of variable 'a'}} \
+                              // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }                           // expected-note {{destroyed here}}
   (void)*p;                   // expected-note {{later used here}}
 
   {
     MyObj a, b, c, d;
-    p = &(cond ? (cond ? a     // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `a`}}
-                       : b)    // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `b`}}
-               : (cond ? c     // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `c`}}
-                       : d));  // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `d`}}
+    p = &(cond ? (cond ? a     // expected-warning {{object whose reference is captured does not live long enough}}. \
+                               // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
+                               // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
+                               // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
+                               // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+                       : b)    // expected-warning {{object whose reference is captured does not live long enough}}.
+               : (cond ? c     // expected-warning {{object whose reference is captured does not live long enough}}.
+                       : d));  // expected-warning {{object whose reference is captured does not live long enough}}.
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 
   {
     MyObj a, b, c, d;
-    p = ((cond ? (((cond ? &a : &b)))   // expected-warning 2 {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `b`}} expected-note {{variable `p` is now an alias of `a`}}
-              : &(((cond ? c : d)))));  // expected-warning 2 {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `d`}} expected-note {{variable `p` is now an alias of `c`}}
+    p = ((cond ? (((cond ? &a : &b)))   // expected-warning 2 {{object whose reference is captured does not live long enough}}. \
+                                        // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
+                                        // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
+                                        // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
+                                        // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+              : &(((cond ? c : d)))));  // expected-warning 2 {{object whose reference is captured does not live long enough}}.
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
 
@@ -1176,20 +1206,26 @@ void parentheses(bool cond) {
 void use_temporary_after_destruction() {
   View a;
   a = non_trivially_destructed_temporary(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                  expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `non_trivially_destructed_temporary()`}}
+                                            // expected-note {{destroyed here}} \
+                                            // expected-note {{function call result aliases the storage of the temporary}} \
+                                            // expected-note {{variable 'a' aliases the storage of the temporary}}
   use(a); // expected-note {{later used here}}
 }
 
 void passing_temporary_to_lifetime_bound_function() {
   View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{object whose reference is captured does not live long enough}} \
-                expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `construct_view(non_trivially_destructed_temporary())`}}
+                                                                 // expected-note {{destroyed here}} \
+                                                                 // expected-note {{function call result aliases the storage of the temporary}} \
+                                                                 // expected-note {{variable 'a' aliases the storage of the temporary}}
   use(a); // expected-note {{later used here}}
 }
 
 void use_trivial_temporary_after_destruction() {
   View a;
   a = trivially_destructed_temporary(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `trivially_destructed_temporary()`}}
+                                        // expected-note {{destroyed here}} \
+                                        // expected-note {{function call result aliases the storage of the temporary}} \
+                                        // expected-note {{variable 'a' aliases the storage of the temporary}}
   use(a); // expected-note {{later used here}}
 }
 
@@ -1230,8 +1266,10 @@ void foobar() {
   View view;
   {
     StatusOr<MyObj> string_or = getStringOr();
-    view = string_or. // expected-warning {{object whose reference is captured does not live long enough}}
-            value();  // expected-note {{variable `view` is now an alias of `string_or.value()`}}
+    view = string_or. // expected-warning {{object whose reference is captured does not live long enough}} \\
+                      // expected-note {{function call result aliases the storage of variable 'string_or'}} \\
+                      // expected-note {{variable 'view' aliases the storage of variable 'string_or'}}
+            value();
   }                     // expected-note {{destroyed here}}
   (void)view;           // expected-note {{later used here}}
 }
@@ -1251,8 +1289,8 @@ void range_based_for_use_after_scope() {
   {
     MyObjStorage s;
     for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} \
-                               // expected-note {{variable `o` is now an alias of `__begin2`}}
-      v = o;                  // expected-note {{variable `v` is now an alias of `o`}}
+                               // expected-note {{variable 'o' aliases the storage of variable 's'}}
+      v = o;                   // expected-note {{variable 'v' aliases the storage of variable 's'}}
     }
   } // expected-note {{destroyed here}}
   v.use(); // expected-note {{later used here}}
@@ -1273,7 +1311,7 @@ void range_based_for_not_reference() {
     MyObjStorage s;
     for (MyObj o : s) { // expected-note {{destroyed here}}
       v = o; // expected-warning {{object whose reference is captured does not live long enough}} \
-             // expected-note {{variable `v` is now an alias of `o`}}
+             // expected-note {{variable 'v' aliases the storage of variable 'o'}}
     }
   }
   v.use();  // expected-note {{later used here}}
@@ -1307,7 +1345,8 @@ void test_user_defined_deref_uaf() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = &(*smart_ptr);  // expected-warning {{object whose reference is captured does not live long enough}} \
-                        // expected-note {{variable `p` is now an alias of `operator*(smart_ptr)`}}
+                        // expected-note {{expression aliases the storage of variable 'smart_ptr'}} \
+                        // expected-note {{variable 'p' aliases the storage of variable 'smart_ptr'}}
   }                     // expected-note {{destroyed here}}
   (void)*p;             // expected-note {{later used here}}
 }
@@ -1325,7 +1364,8 @@ void test_user_defined_deref_with_view() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     v = *smart_ptr;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable `v` is now an alias of `operator*(smart_ptr)`}}
+                     // expected-note {{expression aliases the storage of variable 'smart_ptr'}} \
+                     // expected-note {{variable 'v' aliases the storage of variable 'smart_ptr'}}
   }                  // expected-note {{destroyed here}}
   v.use();           // expected-note {{later used here}}
 }
@@ -1336,7 +1376,8 @@ void test_user_defined_deref_arrow() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = smart_ptr.operator->();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                                 // expected-note {{variable `p` is now an alias of `smart_ptr.operator->()`}}
+                                 // expected-note {{function call result aliases the storage of variable 'smart_ptr'}} \
+                                 // expected-note {{variable 'p' aliases the storage of variable 'smart_ptr'}}
   }                              // expected-note {{destroyed here}}
   (void)*p;                      // expected-note {{later used here}}
 }
@@ -1347,7 +1388,8 @@ void test_user_defined_deref_chained() {
     MyObj obj;
     SmartPtr<SmartPtr<MyObj>> double_ptr;
     p = &(**double_ptr);  // expected-warning {{object whose reference is captured does not live long enough}} \
-                          // expected-note {{variable `p` is now an alias of `operator*(* double_ptr)`}}
+                          // expected-note {{expression aliases the storage of variable 'double_ptr'}} \
+                          // expected-note {{variable 'p' aliases the storage of variable 'double_ptr'}}
   }                       // expected-note {{destroyed here}}
   (void)*p;               // expected-note {{later used here}}
 }
@@ -1499,7 +1541,7 @@ void strict_warn_on_move() {
   {
     MyObj a;
     v = a;            // expected-warning-re {{object whose reference {{.*}} may have been moved}} \
-                      // expected-note {{variable `v` is now an alias of `a`}}
+                      // expected-note {{variable 'v' aliases the storage of variable 'a'}}
     b = std::move(a); // expected-note {{potentially moved here}}
   }                   // expected-note {{destroyed here}}
   (void)v;            // expected-note {{later used here}}
@@ -1513,7 +1555,7 @@ void flow_sensitive(bool c) {
       MyObj b = std::move(a);
       return;
     }
-    v = a;  // expected-warning {{object whose reference}} expected-note {{variable `v` is now an alias of `a`}}
+    v = a;  // expected-warning {{object whose reference}} expected-note {{variable 'v' aliases the storage of variable 'a'}}
   }         // expected-note {{destroyed here}}
   (void)v;  // expected-note {{later used here}}
 }
@@ -1524,8 +1566,8 @@ void detect_conditional(bool cond) {
   {
     MyObj a, b;
     v = cond ? a : b; // expected-warning-re 2 {{object whose reference {{.*}} may have been moved}} \
-                      // expected-note {{variable `v` is now an alias of `b`}} \
-                      // expected-note {{variable `v` is now an alias of `a`}}
+                      // expected-note {{variable 'v' aliases the storage of variable 'b'}} \
+                      // expected-note {{variable 'v' aliases the storage of variable 'a'}}
     take(std::move(cond ? a : b)); // expected-note 2 {{potentially moved here}}
   }         // expected-note 2 {{destroyed here}}
   (void)v;  // expected-note 2 {{later used here}}
@@ -1536,14 +1578,16 @@ void wrong_use_of_move_is_permissive() {
   {
     MyObj a;
     v = std::move(a); // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{variable `v` is now an alias of `std::move(a)`}}
+                      // expected-note {{function call result aliases the storage of variable 'a'}} \
+                      // expected-note {{variable 'v' aliases the storage of variable 'a'}}
   }         // expected-note {{destroyed here}}
   (void)v;  // expected-note {{later used here}}
   const int* p;
   {
     MyObj a;
     p = std::move(a).getData(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                                // expected-note {{variable `p` is now an alias of `std::move(a).getData()`}}
+                                // expected-note 2 {{function call result aliases the storage of variable 'a'}} \
+                                // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }         // expected-note {{destroyed here}}
   (void)p;  // expected-note {{later used here}}
 }
@@ -1555,7 +1599,8 @@ void test_release_no_uaf() {
   {
     std::unique_ptr<int> p;
     r = p.get();        // expected-warning-re {{object whose reference {{.*}} may have been moved}} \
-                        // expected-note {{variable `r` is now an alias of `p.get()`}}
+                        // expected-note {{function call result aliases the storage of variable 'p'}} \
+                        // expected-note {{variable 'r' aliases the storage of variable 'p'}}
     take(p.release());  // expected-note {{potentially moved here}}
   }                     // expected-note {{destroyed here}}
   (void)*r;             // expected-note {{later used here}}
@@ -1578,9 +1623,12 @@ void bar() {
     {
         S s;
         x = s.x();        // expected-warning {{object whose reference is captured does not live long enough}} \
-                          // expected-note {{variable `x` is now an alias of `s.x()`}}
+                          // expected-note {{function call result aliases the storage of variable 's'}} \
+                          // expected-note {{variable 'x' aliases the storage of variable 's'}}
         View y = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                             expected-note {{destroyed here}} expected-note {{variable `y` is now an alias of `S().x()`}}
+                          // expected-note {{destroyed here}} \
+                          // expected-note {{function call result aliases the storage of the temporary}} \
+                          // expected-note {{variable 'y' aliases the storage of the temporary}}
         (void)y; // expected-note {{used here}}
     } // expected-note {{destroyed here}}
     (void)x; // expected-note {{used here}}
@@ -1670,20 +1718,23 @@ const S& identity(const S& in [[clang::lifetimebound]]);
 void test_temporary() {
   const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
                                   // expected-note {{destroyed here}} \
-                                  // expected-note {{variable `x` is now an alias of `S().x()`}}
+                                  // expected-note {{function call result aliases the storage of the temporary}} \
+                                  // expected-note {{variable 'x' aliases the storage of the temporary}}
   (void)x; // expected-note {{later used here}}
 
   const std::string& y = identity(S().x()); // expected-warning {{object whose reference is captured does not live long enough}} \
                                             // expected-note {{destroyed here}} \
-                                            // expected-note {{variable `y` is now an alias of `identity(S().x())`}}
+                                            // expected-note {{function call result aliases the storage of the temporary}} \
+                                            // expected-note {{variable 'y' aliases the storage of the temporary}}
   (void)y; // expected-note {{later used here}}
 
   std::string_view z;
   {
     S s;
     const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                                   // expected-note {{variable `zz` is now an alias of `s.x()`}}
-    z = zz;                        // expected-note {{variable `z` is now an alias of `zz.operator basic_string_view()`}}
+                                   // expected-note {{function call result aliases the storage of variable 's'}} \
+                                   // expected-note {{variable 'zz' aliases the storage of variable 's'}}
+    z = zz;                        // expected-note {{variable 'z' aliases the storage of variable 's'}}
   } // expected-note {{destroyed here}}
   (void)z; // expected-note {{later used here}}
 }
@@ -1693,14 +1744,16 @@ void test_lifetime_extension_ok() {
   (void)x;
   const S& y = identity(S()); // expected-warning {{object whose reference is captured does not live long enough}} \
                               // expected-note {{destroyed here}} \
-                              // expected-note {{variable `y` is now an alias of `identity(S())`}}
+                              // expected-note {{function call result aliases the storage of the temporary}} \
+                              // expected-note {{variable 'y' aliases the storage of the temporary}}
   (void)y; // expected-note {{later used here}}
 }
 
 const std::string& test_return() {
   const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
                                   // expected-note {{destroyed here}} \
-                                  // expected-note {{variable `x` is now an alias of `S().x()`}}
+                                  // expected-note {{function call result aliases the storage of the temporary}} \
+                                  // expected-note {{variable 'x' aliases the storage of the temporary}}
   return x; // expected-note {{later used here}}
 }
 } // namespace reference_type_decl_ref_expr
@@ -1717,8 +1770,8 @@ void uaf() {
   {
     S str;
     S* p = &str;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                  // expected-note {{variable `p` is now an alias of `str`}}
-    view = p->s;  // expected-note {{variable `view` is now an alias of `p->s.operator basic_string_view()`}}
+                  // expected-note {{variable 'p' aliases the storage of variable 'str'}}
+    view = p->s;  // expected-note {{variable 'view' aliases the storage of variable 'str'}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1744,8 +1797,8 @@ void uaf_union() {
   {
     U u = U{"hello"};
     U* up = &u;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable `up` is now an alias of `u`}}
-    view = up->s; // expected-note {{variable `view` is now an alias of `up->s.operator basic_string_view()`}}
+                 // expected-note {{variable 'up' aliases the storage of variable 'u'}}
+    view = up->s; // expected-note {{variable 'view' aliases the storage of variable 'u'}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1761,8 +1814,9 @@ void uaf_anonymous_union() {
   int* ip;
   {
     AnonymousUnion au;
-    AnonymousUnion* up = &au;  // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{variable `up` is now an alias of `au`}}
-    ip = &up->x; // expected-note {{variable `ip` is now an alias of `up`}}
+    AnonymousUnion* up = &au;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                               // expected-note {{variable 'up' aliases the storage of variable 'au'}}
+    ip = &up->x; // expected-note {{variable 'ip' aliases the storage of variable 'au'}}
   } // expected-note {{destroyed here}}
   (void)ip;  // expected-note {{later used here}}
 }
@@ -1822,13 +1876,16 @@ void test() {
   MemberFuncsTpl<MyObj> mtf;
   const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} \
                                             // expected-note {{destroyed here}} \
-                                            // expected-note {{variable `pTMA` is now an alias of `mtf.memberA(MyObj())`}}
+                                            // expected-note {{function call result aliases the storage of the temporary}} \
+                                            // expected-note {{variable 'pTMA' aliases the storage of the temporary}}
   const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{object whose reference is captured does not live long enough}} \
                                             // tu-note {{destroyed here}} \
-                                            // tu-note {{variable `pTMB` is now an alias of `mtf.memberB(MyObj())`}}
+                                            // tu-note {{function call result aliases the storage of the temporary}} \
+                                            // tu-note {{variable 'pTMB' aliases the storage of the temporary}}
   const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} \
                                             // expected-note {{destroyed here}} \
-                                            // expected-note {{variable `pTMC` is now an alias of `mtf.memberC(MyObj())`}}
+                                            // expected-note {{function call result aliases the storage of the temporary}} \
+                                            // expected-note {{variable 'pTMC' aliases the storage of the temporary}}
   (void)pTMA; // expected-note {{later used here}}
   (void)pTMB; // tu-note {{later used here}}
   (void)pTMC; // expected-note {{later used here}}
@@ -1864,7 +1921,9 @@ void test_optional_arrow() {
   {
     std::optional<std::string> opt;
     p = opt->data();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{variable `p` is now an alias of `opt->data()`}}
+                      // expected-note {{expression aliases the storage of variable 'opt'}} \
+                      // expected-note {{function call result aliases the storage of variable 'opt'}} \
+                      // expected-note {{variable 'p' aliases the storage of variable 'opt'}}
   }                   // expected-note {{destroyed here}}
   (void)*p;           // expected-note {{later used here}}
 }
@@ -1874,7 +1933,9 @@ void test_optional_arrow_lifetimebound() {
   {
     std::optional<MyObj> opt;
     v = opt->getView();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                         // expected-note {{variable `v` is now an alias of `opt->getView()`}}
+                         // expected-note {{expression aliases the storage of variable 'opt'}} \
+                         // expected-note {{function call result aliases the storage of variable 'opt'}} \
+                         // expected-note {{variable 'v' aliases the storage of variable 'opt'}}
   }                      // expected-note {{destroyed here}}
   v.use();               // expected-note {{later used here}}
 }
@@ -1884,7 +1945,9 @@ void test_unique_ptr_arrow() {
   {
     std::unique_ptr<std::string> up;
     p = up->data();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable `p` is now an alias of `up->data()`}}
+                     // expected-note {{expression aliases the storage of variable 'up'}} \
+                     // expected-note {{function call result aliases the storage of variable 'up'}} \
+                     // expected-note {{variable 'p' aliases the storage of variable 'up'}}
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 }
@@ -2076,8 +2139,8 @@ void multi_level_pointer_in_loop() {
     MyObj** pp;
     if (i > 5) {
       p = &obj; // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable `p` is now an alias of `obj`}}
-      pp = &p;  // expected-note {{variable `pp` is now an alias of `p`}}
+                // expected-note {{variable 'p' aliases the storage of variable 'obj'}}
+      pp = &p;  // expected-note {{variable 'pp' aliases the storage of variable 'obj'}}
     }
     (void)**pp; // expected-note {{later used here}}
   }             // expected-note {{destroyed here}}
@@ -2089,7 +2152,7 @@ void outer_pointer_outlives_inner_pointee() {
   for (int i = 0; i < 10; ++i) {
     MyObj obj;
     view = &obj;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable `view` is now an alias of `obj`}}
+                     // expected-note {{variable 'view' aliases the storage of variable 'obj'}}
   }                  // expected-note {{destroyed here}}
   (void)*view;       // expected-note {{later used here}}
 }
@@ -2103,7 +2166,7 @@ void element_use_after_scope() {
   {
     int a[10]{};
     p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} \
-               // expected-note {{variable `p` is now an alias of `a`}}
+               // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }            // expected-note {{destroyed here}}
   (void)*p;    // expected-note {{later used here}}
 }
@@ -2136,7 +2199,7 @@ void multidimensional_use_after_scope() {
   {
     int a[3][4]{};
     p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} \
-                  // expected-note {{variable `p` is now an alias of `a`}}
+                  // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }               // expected-note {{destroyed here}}
   (void)*p;       // expected-note {{later used here}}
 }
@@ -2150,7 +2213,7 @@ void member_array_element_use_after_scope() {
   {
     S s;
     p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} \
-                   // expected-note {{variable `p` is now an alias of `s`}}
+                   // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }                // expected-note {{destroyed here}}
   (void)*p;        // expected-note {{later used here}}
 }
@@ -2160,7 +2223,7 @@ void array_of_pointers_use_after_scope() {
   {
     int* a[10]{};
     p = a;  // expected-warning {{object whose reference is captured does not live long enough}} \
-            // expected-note {{variable `p` is now an alias of `a`}}
+            // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }         // expected-note {{destroyed here}}
   (void)*p; // expected-note {{later used here}}
 }
@@ -2170,7 +2233,7 @@ void reversed_subscript_use_after_scope() {
   {
     int a[10]{};
     p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable `p` is now an alias of `a`}}
+                 // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   }              // expected-note {{destroyed here}}
   (void)*p;      // expected-note {{later used here}}
 }
@@ -2243,8 +2306,8 @@ struct S {
 
 void indexing_with_static_operator() {
   S()(1, 2);
-  S& x = S()("1", //expected-note {{variable `x` is now an alias of `operator()(S(), "1", 2, 3)`}} \
-                  //expected-note {{variable `x` is now an alias of `operator()(S(), "1", 2, 3)`}}
+  S& x = S()("1", //expected-note 2 {{expression aliases the storage of the temporary}} \
+                  //expected-note 2 {{variable 'x' aliases the storage of the temporary}}
              2,   // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
              3);  // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
 
@@ -2269,12 +2332,15 @@ S getS(const std::string &s [[clang::lifetimebound]]);
 
 void from_free_function() {
   S s = getS(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                   // expected-note {{function call result aliases the storage of the temporary}} \
+                                   // expected-note {{variable 's' aliases the storage of the temporary}} \
                                    // expected-note {{destroyed here}}
   use(s);                          // expected-note {{later used here}}
 }
 
 void from_constructor() {
   S s(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                            // expected-note {{variable 's' aliases the storage of the temporary}} \
                             // expected-note {{destroyed here}}
   use(s);                   // expected-note {{later used here}}
 }
@@ -2288,12 +2354,16 @@ struct Factory {
 void from_method() {
   Factory f;
   S s = f.make(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                     // expected-note {{function call result aliases the storage of the temporary}} \
+                                     // expected-note {{variable 's' aliases the storage of the temporary}} \
                                      // expected-note {{destroyed here}}
   use(s);                            // expected-note {{later used here}}
 }
 
 void from_static_method() {
   S s = Factory::create(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                              // expected-note {{function call result aliases the storage of the temporary}} \
+                                              // expected-note {{variable 's' aliases the storage of the temporary}} \
                                               // expected-note {{destroyed here}}
   use(s);                                     // expected-note {{later used here}}
 }
@@ -2302,7 +2372,9 @@ void from_lifetimebound_this_method() {
   S value;
   {
     Factory f;
-    value = f.makeThis(); // expected-warning {{object whose reference is captured does not live long enough}}
+    value = f.makeThis(); // expected-warning {{object whose reference is captured does not live long enough}} \
+                          // expected-note {{function call result aliases the storage of variable 'f'}} \
+                          // expected-note {{variable 'value' aliases the storage of variable 'f'}}
   }                       // expected-note {{destroyed here}}
   use(value);             // expected-note {{later used here}}
 }
@@ -2311,7 +2383,9 @@ void across_scope() {
   S s{};
   {
     std::string str{"abc"};
-    s = getS(str); // expected-warning {{object whose reference is captured does not live long enough}}
+    s = getS(str); // expected-warning {{object whose reference is captured does not live long enough}} \
+                   // expected-note {{function call result aliases the storage of variable 'str'}} \
+                   // expected-note {{variable 's' aliases the storage of variable 'str'}}
   }                // expected-note {{destroyed here}}
   use(s);          // expected-note {{later used here}}
 }
@@ -2333,8 +2407,10 @@ void assignment_propagation() {
   S a, b;
   {
     std::string str{"abc"};
-    a = getS(str); // expected-warning {{object whose reference is captured does not live long enough}}
-    b = a;
+    a = getS(str); // expected-warning {{object whose reference is captured does not live long enough}} \
+                   // expected-note {{function call result aliases the storage of variable 'str'}} \
+                   // expected-note {{variable 'a' aliases the storage of variable 'str'}}
+    b = a;         // expected-note {{variable 'b' aliases the storage of variable 'str'}}
   }                // expected-note {{destroyed here}}
   use(b);          // expected-note {{later used here}}
 }
@@ -2348,6 +2424,8 @@ void no_annotation() {
 
 void mix_annotated_and_not() {
   S s1 = getS(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                    // expected-note {{function call result aliases the storage of the temporary}} \
+                                    // expected-note {{variable 's1' aliases the storage of the temporary}} \
                                     // expected-note {{destroyed here}}
   S s2 = getSNoAnnotation(std::string("temp"));
   use(s1); // expected-note {{later used here}}
@@ -2360,6 +2438,8 @@ S multiple_lifetimebound_params() {
   std::string str{"abc"};
   S s = getS2(str, std::string("temp")); // expected-warning {{address of stack memory is returned later}} \
                                          // expected-warning {{object whose reference is captured does not live long enough}} \
+                                         // expected-note {{function call result aliases the storage of the temporary}} \
+                                         // expected-note {{variable 's' aliases the storage of the temporary}} \
                                          // expected-note {{destroyed here}}
   return s;                              // expected-note {{returned here}} \
                                          // expected-note {{later used here}}
@@ -2379,6 +2459,8 @@ T make(const std::string &s [[clang::lifetimebound]]);
 
 void from_template_instantiation() {
   S s = make<S>(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                      // expected-note {{function call result aliases the storage of the temporary}} \
+                                      // expected-note {{variable 's' aliases the storage of the temporary}} \
                                       // expected-note {{destroyed here}}
   use(s);                             // expected-note {{later used here}}
 }
@@ -2442,6 +2524,8 @@ SAlias getSAlias(const std::string &s [[clang::lifetimebound]]);
 
 void from_typedef_return() {
   SAlias s = getSAlias(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                             // expected-note {{function call result aliases the storage of the temporary}} \
+                                             // expected-note {{variable 's' aliases the storage of the temporary}} \
                                              // expected-note {{destroyed here}}
   use(s);                                    // expected-note {{later used here}}
 }
@@ -2516,6 +2600,8 @@ std::unique_ptr<S> getUniqueS(const std::string &s [[clang::lifetimebound]]);
 
 void owner_return_unique_ptr_s() {
   auto ptr = getUniqueS(std::string("temp")); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                              // expected-note {{function call result aliases the storage of the temporary}} \
+                                              // expected-note {{variable 'ptr' aliases the storage of the temporar}} \
                                               // expected-note {{destroyed here}}
   (void)ptr;                                  // expected-note {{later used here}}
 }

>From dc41835c670067cb88f4fd254651e344641ed546 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 31 Mar 2026 17:53:06 +0800
Subject: [PATCH 04/22] fix ci error

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index c3506f20f57d9..2b26f7aee024b 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
 #include "clang/AST/Decl.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/AnalysisDeclContext.h"

>From 3edabc22468fee42f59762233c2fb92bb8de1c62 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 31 Mar 2026 17:57:32 +0800
Subject: [PATCH 05/22] fix clang-format error and my comment

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp | 2 +-
 clang/lib/Sema/SemaLifetimeSafety.h                   | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 2b26f7aee024b..eed32cc065506 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -13,10 +13,10 @@
 
 #include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
 #include "clang/AST/Decl.h"
-#include "llvm/ADT/SmallPtrSet.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include <cstddef>
 
 namespace {
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index d7132fbfe345f..d8ef9cdc92982 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -58,9 +58,8 @@ inline void reportAssignmentImpl(Sema &S, const Expr *IssueExpr,
 
 inline void reportAssignment(Sema &S, const Expr *IssueExpr,
                              const OriginDestExpr &LHS, const Expr *RHS) {
-  if (!LHS || !RHS) {
+  if (!LHS || !RHS)
     return;
-  }
 
   if (const DeclRefExpr *LDExpr = llvm::dyn_cast<const DeclRefExpr *>(LHS)) {
     reportAssignmentImpl(S, IssueExpr, LDExpr->getDecl(), RHS,

>From 863872c274ff383873679731a3401b7c3258a1e9 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Wed, 1 Apr 2026 13:37:05 +0800
Subject: [PATCH 06/22] [LifetimeSafety]: using AssignmentQuery in
 reportUseAfterReturn

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   8 +-
 .../LifetimeSafety/AssignmentQuery.cpp        |  29 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  18 +-
 clang/lib/Sema/SemaLifetimeSafety.h           |  12 +-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |  21 +-
 clang/test/Sema/warn-lifetime-safety.cpp      | 256 ++++++++++++------
 6 files changed, 234 insertions(+), 110 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index fac48afa9860b..e9eb7570a8e5b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -67,10 +67,10 @@ class LifetimeSafetySemaHelper {
       const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
       SourceLocation FreeLoc) {}
 
-  virtual void reportUseAfterReturn(const Expr *IssueExpr,
-                                    const Expr *ReturnExpr,
-                                    const Expr *MovedExpr,
-                                    SourceLocation ExpiryLoc) {}
+  virtual void reportUseAfterReturn(
+      const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation ExpiryLoc) {}
 
   virtual void reportDanglingField(const Expr *IssueExpr,
                                    const FieldDecl *Field,
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index eed32cc065506..33ae7b45ea5fa 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -287,28 +287,43 @@ namespace clang::lifetimes::internal {
 std::optional<llvm::SmallVector<AssignmentPair>>
 getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
              const LoanID End, const Expr *IssueExpr) {
-  const auto *UF = llvm::dyn_cast<UseFact>(CausingFact);
+  llvm::SmallVector<OriginID, 4> TargetOIDList;
+  const Expr *WarnningFact = nullptr;
+
+  if (const auto *UF = llvm::dyn_cast<UseFact>(CausingFact)) {
+    WarnningFact = UF->getUseExpr();
+    for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+         Cur = Cur->peelOuterOrigin())
+      TargetOIDList.push_back(Cur->getOuterOriginID());
+  } else if (const auto *RetEscapeF =
+                 llvm::dyn_cast<ReturnEscapeFact>(CausingFact)) {
+    WarnningFact = RetEscapeF->getReturnExpr();
+    TargetOIDList.push_back(RetEscapeF->getEscapedOriginID());
+  } else {
+    llvm_unreachable("Without a corresponding Fact handler, assignment history "
+                     "traceback will fail.");
+  }
+
   const CFGBlock *StartBlock =
-      Context.ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
+      Context.ADC.getCFGStmtMap()->getBlock(WarnningFact);
   assert(StartBlock && "Searching CFGBlock failed");
   const CFGBlock *EndBlock = Context.ADC.getCFGStmtMap()->getBlock(IssueExpr);
   assert(EndBlock && "Searching CFGBlock failed");
 
-  for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
-       Cur = Cur->peelOuterOrigin()) {
-    auto TargetOID = Cur->getOuterOriginID();
+  for (auto TargetOID : TargetOIDList) {
     if (StartBlock == EndBlock) {
-      AliasAssignmentSearchResult Result =
+      const AliasAssignmentSearchResult Result =
           getAliasListCore(Context, StartBlock, End, &TargetOID);
       if (!Result.Payload.empty())
         return Result.Payload;
     } else {
-      auto Result =
+      const auto Result =
           getAliasListInMultiBlock(Context, StartBlock, End, &TargetOID);
       if (Result.has_value())
         return Result.value();
     }
   }
+
   return std::nullopt;
 }
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index a17f1da16be75..fa420c0976dc2 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -255,20 +255,20 @@ class LifetimeChecker {
           const struct AssignmentQueryContext Context = {
               LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
           const auto AliasExprs = getAliasList(Context, UF, LID, IssueExpr);
-          if (!AliasExprs.has_value()) {
-            llvm::dbgs() << "Search variable assignment chain failed\n";
-          }
-
           SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
                                          AliasExprs, ExpiryLoc);
         }
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
-        if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
-          // Return stack address.
-          SemaHelper->reportUseAfterReturn(
-              IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
-        else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
+        if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
+          const struct AssignmentQueryContext Context = {
+              LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
+          const auto AliasExprs =
+              getAliasList(Context, RetEscape, LID, IssueExpr);
+          SemaHelper->reportUseAfterReturn(IssueExpr,
+                                           RetEscape->getReturnExpr(),
+                                           MovedExpr, AliasExprs, ExpiryLoc);
+        } else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
           // Dangling field.
           SemaHelper->reportDanglingField(
               IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index d8ef9cdc92982..5352d1595eac1 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -95,9 +95,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << UseExpr->getSourceRange();
   }
 
-  void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
-                            const Expr *MovedExpr,
-                            SourceLocation ExpiryLoc) override {
+  void reportUseAfterReturn(
+      const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation ExpiryLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved
                      : diag::warn_lifetime_safety_return_stack_addr)
@@ -105,6 +106,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     if (MovedExpr)
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
+
+    if (AliasList.has_value())
+      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
+        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+
     S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
         << ReturnExpr->getSourceRange();
   }
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 2a7884e98c660..495839952449e 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -365,7 +365,9 @@ int &usedToBeFalsePositive(std::vector<int> &v) {
 int &doNotFollowReferencesForLocalOwner() {
 // Warning caught by CFG analysis.
   std::unique_ptr<int> localOwner;
-  int &p = *localOwner // cfg-warning {{address of stack memory is returned later}}
+  int &p = *localOwner // cfg-warning {{address of stack memory is returned later}} \
+                       // cfg-note {{function call result aliases the storage of variable 'localOwner'}} \
+                       // cfg-note {{variable 'p' aliases the storage of variable 'localOwner'}}
             .get();
   return p; // cfg-note {{returned here}}
 }
@@ -1025,14 +1027,18 @@ void test4() {
 
 namespace range_based_for_loop_variables {
 std::string_view test_view_loop_var(std::vector<std::string> strings) {
-  for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} 
+  for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
+                                        // cfg-note {{function call result aliases the storage of variable 'strings'}} \
+                                        // cfg-note {{variable '__begin1' aliases the storage of variable 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
 }
 
 const char* test_view_loop_var_with_data(std::vector<std::string> strings) {
-  for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} 
+  for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
+                                        // cfg-note {{function call result aliases the storage of variable 'strings'}} \
+                                        // cfg-note {{variable '__begin1' aliases the storage of variable 'strings'}}
     return s.data(); //cfg-note {{returned here}}
   }
   return "";
@@ -1046,14 +1052,19 @@ std::string_view test_no_error_for_views(std::vector<std::string_view> views) {
 }
 
 std::string_view test_string_ref_var(std::vector<std::string> strings) {
-  for (const std::string& s : strings) {  // cfg-warning {{address of stack memory is returned later}} 
+  for (const std::string& s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
+                                          // cfg-note {{function call result aliases the storage of variable 'strings'}} \
+                                          // cfg-note {{variable '__begin1' aliases the storage of variable 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
 }
 
 std::string_view test_opt_strings(std::optional<std::vector<std::string>> strings_or) {
-  for (const std::string& s : *strings_or) {  // cfg-warning {{address of stack memory is returned later}} 
+  for (const std::string& s : *strings_or) {  // cfg-warning {{address of stack memory is returned later}} \
+                                              // cfg-note {{variable '__range1' aliases the storage of variable 'strings_or'}} \
+                                              // cfg-note {{function call result aliases the storage of variable 'strings_or'}} \
+                                              // cfg-note {{variable '__begin1' aliases the storage of variable 'strings_or'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 2e432515ac70c..d2a02ed73fd4d 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -506,7 +506,8 @@ void small_scope_reference_var_no_error() {
 
 MyObj* simple_return_stack_address() {
   MyObj s;      
-  MyObj* p = &s; // expected-warning {{address of stack memory is returned later}}
+  MyObj* p = &s; // expected-warning {{address of stack memory is returned later}} \
+                 // expected-note {{variable 'p' aliases the storage of variable 's'}}
   return p;      // expected-note {{returned here}}
 }
 
@@ -535,7 +536,8 @@ const MyObj* conditional_assign_unconditional_return(const MyObj& safe, bool c)
   MyObj s; 
   const MyObj* p = &safe;
   if (c) {
-    p = &s;       // expected-warning {{address of stack memory is returned later}}
+    p = &s;       // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }     
   return p;      // expected-note {{returned here}}
 }
@@ -544,7 +546,8 @@ View conditional_assign_both_branches(const MyObj& safe, bool c) {
   MyObj s;
   View p;
   if (c) {
-    p = s;      // expected-warning {{address of stack memory is returned later}}
+    p = s;      // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'p' aliases the storage of variable 's'}}
   } 
   else {
     p = safe;
@@ -556,15 +559,17 @@ View conditional_assign_both_branches(const MyObj& safe, bool c) {
 View reassign_safe_to_local(const MyObj& safe) {
   MyObj local;
   View p = safe;
-  p = local;    // expected-warning {{address of stack memory is returned later}}
+  p = local;    // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'p' aliases the storage of variable 'local'}}
   return p;     // expected-note {{returned here}}
 }
 
 View pointer_chain_to_local() {
   MyObj local;
-  View p1 = local;     // expected-warning {{address of stack memory is returned later}}
-  View p2 = p1; 
-  return p2;          // expected-note {{returned here}}
+  View p1 = local;     // expected-warning {{address of stack memory is returned later}} \
+                       // expected-note {{variable 'p1' aliases the storage of variable 'local'}}
+  View p2 = p1;        // expected-note {{variable 'p2' aliases the storage of variable 'local'}}
+  return p2;           // expected-note {{returned here}}
 }
 
 View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) {
@@ -572,11 +577,13 @@ View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) {
   MyObj local2;
   View p;
   if (c1) {
-    p = local1;       // expected-warning {{address of stack memory is returned later}}
+    p = local1;       // expected-warning {{address of stack memory is returned later}} \
+                      // expected-note {{variable 'p' aliases the storage of variable 'local1'}}
     return p;         // expected-note {{returned here}}
   }
   else if (c2) {
-    p = local2;       // expected-warning {{address of stack memory is returned later}}
+    p = local2;       // expected-warning {{address of stack memory is returned later}} \
+                      // expected-note {{variable 'p' aliases the storage of variable 'local2'}}
     return p;         // expected-note {{returned here}}
   }
   p = safe;
@@ -588,15 +595,17 @@ View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) {
   MyObj local2;
   View p;
   if (c1) {
-    p = local1;      // expected-warning {{address of stack memory is returned later}}
+    p = local1;      // expected-warning {{address of stack memory is returned later}} \
+                     // expected-note {{variable 'p' aliases the storage of variable 'local1'}}
   }
   else if (c2) {
-    p = local2;      // expected-warning {{address of stack memory is returned later}}
+    p = local2;      // expected-warning {{address of stack memory is returned later}} \
+                     // expected-note {{variable 'p' aliases the storage of variable 'local2'}}
   }
   else {
     p = safe;
   }
-  return p;         // expected-note 2 {{returned here}}
+  return p;          // expected-note 2 {{returned here}}
 }
 
 View direct_return_of_local() {
@@ -614,14 +623,16 @@ MyObj& reference_return_of_local() {
 int* trivial_int_uar() {
   int *a;
   int b = 1;
-  a = &b;          // expected-warning {{address of stack memory is returned later}}
+  a = &b;          // expected-warning {{address of stack memory is returned later}} \
+                   // expected-note {{variable 'a' aliases the storage of variable 'b'}}
   return a;        // expected-note {{returned here}}
 }
 
 TriviallyDestructedClass* trivial_class_uar () {
   TriviallyDestructedClass *ptr;
   TriviallyDestructedClass s;
-  ptr = &s;       // expected-warning {{address of stack memory is returned later}}
+  ptr = &s;       // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'ptr' aliases the storage of variable 's'}}
   return ptr;     // expected-note {{returned here}}
 }
 
@@ -636,7 +647,8 @@ int* return_pointer_to_parameter(int a) {
 }
 
 const int& return_reference_to_parameter(int a) {
-    const int &b = a;   // expected-warning {{address of stack memory is returned later}}
+    const int &b = a;   // expected-warning {{address of stack memory is returned later}} \
+                        // expected-note {{variable 'b' aliases the storage of variable 'a'}}
     return b;           // expected-note {{returned here}}
 }
 int return_reference_to_parameter_no_error(int a) {
@@ -646,24 +658,31 @@ int return_reference_to_parameter_no_error(int a) {
 
 MyObj*& return_ref_to_local_ptr_pointing_to_local() {
   MyObj local;
-  MyObj* p = &local; // expected-warning {{address of stack memory is returned later}}
+  MyObj* p = &local; // expected-warning {{address of stack memory is returned later}} \
+                     // expected-note {{variable 'p' aliases the storage of variable 'local'}}
   return p;          // expected-note {{returned here}} \
                      // expected-warning {{address of stack memory is returned later}} \
                      // expected-note {{returned here}}
 }
 
 const int& reference_via_conditional(int a, int b, bool cond) {
-    const int &c = (cond ? ((a)) : (b));  // expected-warning 2 {{address of stack memory is returned later}}
+    const int &c = (cond ? ((a)) : (b));  // expected-warning 2 {{address of stack memory is returned later}} \
+                                          // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
+                                          // expected-note {{variable 'c' aliases the storage of variable 'b'}}
     return c;                             // expected-note 2 {{returned here}}
 }
 const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
-    const int &c = cond ? a : b;  // expected-warning 2 {{address of stack memory is returned later}}
-    const int* d = &c;
+    const int &c = cond ? a : b;  // expected-warning 2 {{address of stack memory is returned later}} \
+                                  // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
+                                  // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+    const int* d = &c;            // expected-note {{variable 'd' aliases the storage of variable 'a'}} \
+                                  // expected-note {{variable 'd' aliases the storage of variable 'b'}}
     return d;                     // expected-note 2 {{returned here}}
 }
 
 const int& return_pointer_to_parameter_via_reference_1(int a) {
-    const int* d = &a; // expected-warning {{address of stack memory is returned later}}
+    const int* d = &a; // expected-warning {{address of stack memory is returned later}} \
+                       // expected-note {{variable 'd' aliases the storage of variable 'a'}}
     return *d;    // expected-note {{returned here}}
 }
 
@@ -700,7 +719,9 @@ struct PtrHolder {
 
 int* const& test_ref_to_ptr() {
   PtrHolder a;
-  int *const &ref = a.getRef();  // expected-warning {{address of stack memory is returned later}}
+  int *const &ref = a.getRef();  // expected-warning {{address of stack memory is returned later}} \
+                                 // expected-note {{function call result aliases the storage of variable 'a'}} \
+                                 // expected-note {{variable 'ref' aliases the storage of variable 'a'}}
   return ref;  // expected-note {{returned here}}
 }
 int* const test_ref_to_ptr_no_error() {
@@ -737,9 +758,13 @@ void test_assign_through_double_ptr() {
 
 int** test_ternary_double_ptr(bool cond) {
   int a = 1, b = 2;
-  int* pa = &a;  // expected-warning {{address of stack memory is returned later}}
-  int* pb = &b;  // expected-warning {{address of stack memory is returned later}}
-  int** result = cond ? &pa : &pb;  // expected-warning 2 {{address of stack memory is returned later}}
+  int* pa = &a;  // expected-warning {{address of stack memory is returned later}} \
+                 // expected-note {{variable 'pa' aliases the storage of variable 'a'}}
+  int* pb = &b;  // expected-warning {{address of stack memory is returned later}} \
+                 // expected-note {{variable 'pb' aliases the storage of variable 'b'}}
+  int** result = cond ? &pa : &pb;  // expected-warning 2 {{address of stack memory is returned later}} \
+                                    // expected-note {{variable 'result' aliases the storage of variable 'pa'}} \
+                                    // expected-note {{variable 'result' aliases the storage of variable 'pb'}}
   return result; // expected-note 4 {{returned here}}
 }
 //===----------------------------------------------------------------------===//
@@ -761,7 +786,8 @@ View uar_before_uaf(const MyObj& safe, bool c) {
   View p;
   {
     MyObj local_obj; 
-    p = local_obj;  // expected-warning {{ddress of stack memory is returned later}}
+    p = local_obj;  // expected-warning {{ddress of stack memory is returned later}} \
+                    // expected-note {{variable 'p' aliases the storage of variable 'local_obj'}}
     if (c) {
       return p;     // expected-note {{returned here}}
     }
@@ -1298,7 +1324,9 @@ void range_based_for_use_after_scope() {
 
 View range_based_for_use_after_return() {
   MyObjStorage s;
-  for (const MyObj &o : s) { // expected-warning {{address of stack memory is returned later}}
+  for (const MyObj &o : s) { // expected-warning {{address of stack memory is returned later}} \
+                             // expected-note {{function call result aliases the storage of variable 's'}} \
+                             // expected-note {{variable '__begin1' aliases the storage of variable 's'}}
     return o;  // expected-note {{returned here}}
   }
   return *s.begin();  // expected-warning {{address of stack memory is returned later}}
@@ -1438,14 +1466,18 @@ MyObj* call_max_with_obj_error() {
 
 const MyObj* call_max_with_ref_obj_error() {
   MyObj oa, ob;
-  const MyObj& refa = oa;     // expected-warning {{address of stack memory is returned later}}
-  const MyObj& refb = ob;     // expected-warning {{address of stack memory is returned later}}
+  const MyObj& refa = oa;     // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{variable 'refa' aliases the storage of variable 'oa'}}
+  const MyObj& refb = ob;     // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{variable 'refb' aliases the storage of variable 'ob'}}
   return  &MaxT(refa, refb);  // expected-note 2 {{returned here}}
 }
 const MyObj& call_max_with_ref_obj_return_ref_error() {
   MyObj oa, ob;
-  const MyObj& refa = oa;     // expected-warning {{address of stack memory is returned later}}
-  const MyObj& refb = ob;     // expected-warning {{address of stack memory is returned later}}
+  const MyObj& refa = oa;     // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{variable 'refa' aliases the storage of variable 'oa'}}
+  const MyObj& refb = ob;     // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{variable 'refb' aliases the storage of variable 'ob'}}
   return  MaxT(refa, refb);   // expected-note 2 {{returned here}}
 }
 
@@ -1477,30 +1509,46 @@ const NonTrivialPointer& call_max_with_non_trivial_view_with_error() {
 namespace MultiPointerTypes {
 int** return_2p() {
   int a = 1;
-  int* b = &a;  // expected-warning {{address of stack memory is returned later}}
-  int** c = &b; // expected-warning {{address of stack memory is returned later}}
+  int* b = &a;  // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+  int** c = &b; // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
+                // expected-note {{variable 'c' aliases the storage of variable 'b'}}
   return c;     // expected-note 2 {{returned here}}
 }
 
 int** return_2p_one_is_safe(int& a) {
   int* b = &a;
-  int** c = &b; // expected-warning {{address of stack memory is returned later}}
+  int** c = &b; // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'c' aliases the storage of variable 'b'}}
   return c;     // expected-note {{returned here}}
 }
 
 int*** return_3p() {
   int a = 1;
-  int* b = &a;    // expected-warning {{address of stack memory is returned later}}
-  int** c = &b;   // expected-warning {{address of stack memory is returned later}}
-  int*** d = &c;  // expected-warning {{address of stack memory is returned later}}
+  int* b = &a;    // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+  int** c = &b;   // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
+                  // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+  int*** d = &c;  // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'd' aliases the storage of variable 'a'}} \
+                  // expected-note {{variable 'd' aliases the storage of variable 'b'}} \
+                  // expected-note {{variable 'd' aliases the storage of variable 'c'}}
   return d;       // expected-note 3 {{returned here}}
 }
 
 View** return_view_p() {
   MyObj a;
-  View b = a;     // expected-warning {{address of stack memory is returned later}}
-  View* c = &b;   // expected-warning {{address of stack memory is returned later}}
-  View** d = &c;  // expected-warning {{address of stack memory is returned later}}
+  View b = a;     // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+  View* c = &b;   // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
+                  // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+  View** d = &c;  // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'd' aliases the storage of variable 'a'}} \
+                  // expected-note {{variable 'd' aliases the storage of variable 'b'}} \
+                  // expected-note {{variable 'd' aliases the storage of variable 'c'}}
   return d;       // expected-note 3 {{returned here}}
 }
 
@@ -1637,24 +1685,30 @@ void bar() {
 
 namespace DereferenceViews {
 const MyObj& testDeref(MyObj obj) {
-  View v = obj; // expected-warning {{address of stack memory is returned later}}
+  View v = obj; // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   return *v;    // expected-note {{returned here}}
 }
 const MyObj* testDerefAddr(MyObj obj) {
-  View v = obj; // expected-warning {{address of stack memory is returned later}}
+  View v = obj; // expected-warning {{address of stack memory is returned later}} \
+                // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   return &*v;   // expected-note {{returned here}}
 }
 const MyObj* testData(MyObj obj) {
-  View v = obj;     // expected-warning {{address of stack memory is returned later}}
+  View v = obj;     // expected-warning {{address of stack memory is returned later}} \
+                    // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   return v.data();  // expected-note {{returned here}}
 }
 const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
-  View v = obj;           // expected-warning {{address of stack memory is returned later}}
-  const MyObj* ptr = v.data();
+  View v = obj;           // expected-warning {{address of stack memory is returned later}} \
+                          // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+  const MyObj* ptr = v.data(); // expected-note {{function call result aliases the storage of variable 'obj'}} \
+                               // expected-note {{variable 'ptr' aliases the storage of variable 'obj'}}
   return ptr->getData();  // expected-note {{returned here}}
 }
 const int* testLifetimeboundAccessorOfMyObjThroughDeref(MyObj obj) {
-  View v = obj;         // expected-warning {{address of stack memory is returned later}}
+  View v = obj;         // expected-warning {{address of stack memory is returned later}} \
+                        // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
   return v->getData();  // expected-note {{returned here}}
 }
 } // namespace DereferenceViews
@@ -1678,17 +1732,23 @@ It end() const [[clang::lifetimebound]];
 MyObj Global;
 
 const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
-  for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}}
+  for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{function call result aliases the storage of variable 'c'}} \
+                              // expected-note {{variable '__begin1' aliases the storage of variable 'c'}}
     return x;                 // expected-note {{returned here}}
   }
   return Global;
 }
 
 View ContainerMyObjReturnView(Container<MyObj> c) {
-  for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}}
+  for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{function call result aliases the storage of variable 'c'}} \
+                              // expected-note {{variable '__begin1' aliases the storage of variable 'c'}}
     return x;                 // expected-note {{returned here}}
   }
-  for (View x : c) {  // expected-warning {{address of stack memory is returned later}}
+  for (View x : c) {  // expected-warning {{address of stack memory is returned later}} \
+                      // expected-note {{function call result aliases the storage of variable 'c'}} \
+                      // expected-note {{variable '__begin1' aliases the storage of variable 'c'}}
     return x;         // expected-note {{returned here}}
   }
   return Global;
@@ -1905,12 +1965,14 @@ View test1(std::string a) {
 }
 
 View test2(std::string a) {
-  View b = View(a); // expected-warning {{address of stack memory is returned later}}
+  View b = View(a); // expected-warning {{address of stack memory is returned later}} \
+                    // expected-note {{variable 'b' aliases the storage of variable 'a'}}
   return b;         // expected-note {{returned here}}
 }
 
 View test3(std::string a) {
-  const View& b = View(a);  // expected-warning {{address of stack memory is returned later}}
+  const View& b = View(a);  // expected-warning {{address of stack memory is returned later}} \
+                            // expected-note {{variable 'b' aliases the storage of variable 'a'}}
   return b;                 // expected-note {{returned here}}
 }
 } // namespace non_trivial_views
@@ -1965,7 +2027,8 @@ void test_optional_view_arrow() {
 namespace lambda_captures {
 auto return_ref_capture() {
   int local = 1;
-  auto lambda = [&local]() { return local; }; // expected-warning {{address of stack memory is returned later}}
+  auto lambda = [&local]() { return local; }; // expected-warning {{address of stack memory is returned later}} \
+                                              // expected-note {{variable 'lambda' aliases the storage of variable 'local'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -1983,8 +2046,9 @@ auto capture_int_by_value() {
 
 auto capture_view_by_value() {
   MyObj obj;
-  View v(obj); // expected-warning {{address of stack memory is returned later}}
-  auto lambda = [v]() { return v; };
+  View v(obj); // expected-warning {{address of stack memory is returned later}} \
+               // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+  auto lambda = [v]() { return v; }; // expected-note {{variable 'lambda' aliases the storage of variable 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -1998,13 +2062,15 @@ void capture_view_by_value_safe() {
 auto capture_pointer_by_ref() {
   MyObj obj;
   MyObj* p = &obj;
-  auto lambda = [&p]() { return p; }; // expected-warning {{address of stack memory is returned later}}
+  auto lambda = [&p]() { return p; }; // expected-warning {{address of stack memory is returned later}} \\
+                                      // expected-note {{variable 'lambda' aliases the storage of variable 'p'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_multiple() {
   int a, b;
-  auto lambda = [
+  auto lambda = [ // expected-note {{variable 'lambda' aliases the storage of variable 'b'}} \
+                  // expected-note {{variable 'lambda' aliases the storage of variable 'a'}}
     &a,  // expected-warning {{address of stack memory is returned later}}
     &b   // expected-warning {{address of stack memory is returned later}}
   ]() { return a + b; };
@@ -2013,42 +2079,48 @@ auto capture_multiple() {
 
 auto capture_raw_pointer_by_value() {
   int x;
-  int* p = &x; // expected-warning {{address of stack memory is returned later}}
-  auto lambda = [p]() { return p; };
+  int* p = &x; // expected-warning {{address of stack memory is returned later}} \
+               // expected-note {{variable 'p' aliases the storage of variable 'x'}}
+  auto lambda = [p]() { return p; }; // expected-note {{variable 'lambda' aliases the storage of variable 'x'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_raw_pointer_init_capture() {
   int x;
-  int* p = &x; // expected-warning {{address of stack memory is returned later}}
-  auto lambda = [q = p]() { return q; };
+  int* p = &x; // expected-warning {{address of stack memory is returned later}} \
+               // expected-note {{variable 'p' aliases the storage of variable 'x'}}
+  auto lambda = [q = p]() { return q; }; // expected-note {{variable 'lambda' aliases the storage of variable 'x'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_view_init_capture() {
   MyObj obj;
-  View v(obj); // expected-warning {{address of stack memory is returned later}}
-  auto lambda = [w = v]() { return w; };
+  View v(obj); // expected-warning {{address of stack memory is returned later}} \
+               // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+  auto lambda = [w = v]() { return w; }; // expected-note {{variable 'lambda' aliases the storage of variable 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_lambda() {
   int x;
-  auto inner = [&x]() { return x; }; // expected-warning {{address of stack memory is returned later}}
-  auto outer = [inner]() { return inner(); };
+  auto inner = [&x]() { return x; }; // expected-warning {{address of stack memory is returned later}} \
+                                     // expected-note {{variable 'inner' aliases the storage of variable 'x'}}
+  auto outer = [inner]() { return inner(); }; // expected-note {{variable 'outer' aliases the storage of variable 'x'}}
   return outer; // expected-note {{returned here}}
 }
 
 auto return_copied_lambda() {
   int local = 1;
-  auto lambda = [&local]() { return local; }; // expected-warning {{address of stack memory is returned later}}
-  auto lambda_copy = lambda;
+  auto lambda = [&local]() { return local; }; // expected-warning {{address of stack memory is returned later}} \
+                                              // expected-note {{variable 'lambda' aliases the storage of variable 'local'}}
+  auto lambda_copy = lambda;                  // expected-note {{variable 'lambda_copy' aliases the storage of variable 'local'}}
   return lambda_copy; // expected-note {{returned here}}
 }
 
 auto implicit_ref_capture() {
   int local = 1;
-  auto lambda = [&]() { return local; }; // expected-warning {{address of stack memory is returned later}}
+  auto lambda = [&]() { return local; }; // expected-warning {{address of stack memory is returned later}} \
+                                         // expected-note {{variable 'lambda' aliases the storage of variable 'local'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2057,14 +2129,17 @@ auto implicit_ref_capture() {
 // can point to the same source location.
 auto implicit_ref_capture_multiple() {
   int local = 1, local2 = 2;
-  auto lambda = [&]() { return local + local2; }; // expected-warning 2 {{address of stack memory is returned later}}
+  auto lambda = [&]() { return local + local2; }; // expected-warning 2 {{address of stack memory is returned later}} \
+                                                  // expected-note {{variable 'lambda' aliases the storage of variable 'local'}} \
+                                                  // expected-note {{variable 'lambda' aliases the storage of variable 'local2'}}
   return lambda; // expected-note 2 {{returned here}}
 }
 
 auto implicit_value_capture() {
   MyObj obj;
-  View v(obj); // expected-warning {{address of stack memory is returned later}}
-  auto lambda = [=]() { return v; };
+  View v(obj); // expected-warning {{address of stack memory is returned later}} \
+               // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+  auto lambda = [=]() { return v; }; // expected-note {{variable 'lambda' aliases the storage of variable 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2095,16 +2170,22 @@ auto capture_static_address_by_value() {
 auto capture_static_address_by_ref() {
   static int local = 1;
   int* p = &local;
-  auto lambda = [&p]() { return p; }; // expected-warning {{address of stack memory is returned later}}
+  auto lambda = [&p]() { return p; }; // expected-warning {{address of stack memory is returned later}} \
+                                      // expected-note {{variable 'lambda' aliases the storage of variable 'p'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_multilevel_pointer() {
   int x;
-  int *p = &x; // expected-warning {{address of stack memory is returned later}}
-  int **q = &p; // expected-warning {{address of stack memory is returned later}}
-  int ***r = &q; // expected-warning {{address of stack memory is returned later}}
-  auto lambda = [=]() { return *p + **q + ***r; };
+  int *p = &x;   // expected-warning {{address of stack memory is returned later}} \
+                 // expected-note {{variable 'p' aliases the storage of variable 'x'}}
+  int **q = &p;  // expected-warning {{address of stack memory is returned later}} \
+                 // expected-note {{variable 'q' aliases the storage of variable 'p'}}
+  int ***r = &q; // expected-warning {{address of stack memory is returned later}} \
+                 // expected-note {{variable 'r' aliases the storage of variable 'q'}}
+  auto lambda = [=]() { return *p + **q + ***r; }; // expected-note {{variable 'lambda' aliases the storage of variable 'x'}} \
+                                                   // expected-note {{variable 'lambda' aliases the storage of variable 'q'}} \
+                                                   // expected-note {{variable 'lambda' aliases the storage of variable 'p'}}
   return lambda; // expected-note 3 {{returned here}}
 }
 } // namespace lambda_captures
@@ -2173,7 +2254,8 @@ void element_use_after_scope() {
 
 int* element_use_after_return() {
   int a[10]{};
-  int* p = &a[0]; // expected-warning {{address of stack memory is returned later}}
+  int* p = &a[0]; // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   return p;       // expected-note {{returned here}}
 }
 
@@ -2240,7 +2322,8 @@ void reversed_subscript_use_after_scope() {
 
 int* return_decayed_array() {
   int a[10]{};
-  int *p = a; // expected-warning {{address of stack memory is returned later}}
+  int *p = a; // expected-warning {{address of stack memory is returned later}} \
+              // expected-note {{variable 'p' aliases the storage of variable 'a'}}
   return p;   // expected-note {{returned here}}
 }
 
@@ -2398,8 +2481,10 @@ void same_scope() {
 
 S copy_propagation() {
   std::string str{"abc"};
-  S a = getS(str); // expected-warning {{address of stack memory is returned later}}
-  S b = a;
+  S a = getS(str); // expected-warning {{address of stack memory is returned later}} \
+                   // expected-note {{function call result aliases the storage of variable 'str'}} \
+                   // expected-note {{variable 'a' aliases the storage of variable 'str'}}
+  S b = a;         // expected-note {{variable 'b' aliases the storage of variable 'str'}}
   return b; // expected-note {{returned here}}
 }
 
@@ -2440,6 +2525,8 @@ S multiple_lifetimebound_params() {
                                          // expected-warning {{object whose reference is captured does not live long enough}} \
                                          // expected-note {{function call result aliases the storage of the temporary}} \
                                          // expected-note {{variable 's' aliases the storage of the temporary}} \
+                                         // expected-note {{function call result aliases the storage of variable 'str'}} \
+                                         // expected-note {{variable 's' aliases the storage of variable 'str'}} \
                                          // expected-note {{destroyed here}}
   return s;                              // expected-note {{returned here}} \
                                          // expected-note {{later used here}}
@@ -2560,8 +2647,10 @@ DefaultedOuter getDefaultedOuter(const std::string &s [[clang::lifetimebound]]);
 // pattern does not fit the ownership model this analysis supports.
 DefaultedOuter nested_defaulted_outer_with_user_defined_inner() {
   std::string str{"abc"};
-  DefaultedOuter o = getDefaultedOuter(str); // expected-warning {{address of stack memory is returned later}}
-  DefaultedOuter copy = o;
+  DefaultedOuter o = getDefaultedOuter(str); // expected-warning {{address of stack memory is returned later}} \
+                                             // expected-note {{function call result aliases the storage of variable 'str'}} \
+                                             // expected-note {{variable 'o' aliases the storage of variable 'str'}}
+  DefaultedOuter copy = o;                   // expected-note {{variable 'copy' aliases the storage of variable 'str'}}
   return copy; // expected-note {{returned here}}
 }
 
@@ -2609,8 +2698,11 @@ void owner_return_unique_ptr_s() {
 std::string_view return_dangling_view_through_owner() {
   std::string local;
   auto ups = getUniqueS(local);
-  S* s = ups.get(); // expected-warning {{address of stack memory is returned later}}
-  std::string_view sv = s->getData();
+  S* s = ups.get(); // expected-warning {{address of stack memory is returned later}} \
+                    // expected-note {{function call result aliases the storage of variable 'ups'}} \
+                    // expected-note {{variable 's' aliases the storage of variable 'ups'}}
+  std::string_view sv = s->getData(); // expected-note {{function call result aliases the storage of variable 'ups'}} \
+                                      // expected-note {{variable 'sv' aliases the storage of variable 'ups'}}
   return sv; // expected-note {{returned here}}
 }
 

>From cb08ec65665794ad3a6c73bd65a8ffc19d30b61d Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Wed, 1 Apr 2026 21:23:01 +0800
Subject: [PATCH 07/22] [LifetimeSafety] using AssignmentQuery in
 reportDanglingGlobal

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  3 +-
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 10 ++++++
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  9 ++---
 .../LifetimeSafety/AssignmentQuery.cpp        | 12 +++----
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 35 ++++++++++++++-----
 clang/lib/Sema/SemaLifetimeSafety.h           | 14 +++++---
 .../warn-lifetime-safety-dangling-global.cpp  | 13 ++++---
 7 files changed, 66 insertions(+), 30 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index b0f99a8b094ce..097beb1682bab 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -72,7 +72,8 @@ struct AssignmentQueryContext {
 /// address originated.
 std::optional<llvm::SmallVector<AssignmentPair>>
 getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
-             const LoanID End, const Expr *IssueExpr);
+             const LoanID End, const CFGBlock *StartBlock,
+             const Expr *IssueExpr);
 } // namespace clang::lifetimes::internal
 
 #endif
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index b70fecd5ab1d1..87d41dffa5b88 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -346,6 +346,16 @@ class FactManager {
     return BlockToFacts[B->getBlockID()];
   }
 
+  std::optional<size_t> getBlockID(const Fact *TargetFact) const {
+    for (size_t i = 0; i < BlockToFacts.size(); ++i) {
+      for (const auto &F : BlockToFacts[i]) {
+        if (F == TargetFact)
+          return i;
+      }
+    }
+    return std::nullopt;
+  }
+
   void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {
     if (!NewFacts.empty())
       BlockToFacts[B->getBlockID()].assign(NewFacts.begin(), NewFacts.end());
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index e9eb7570a8e5b..f4aa5c66f7e34 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -77,10 +77,11 @@ class LifetimeSafetySemaHelper {
                                    const Expr *MovedExpr,
                                    SourceLocation ExpiryLoc) {}
 
-  virtual void reportDanglingGlobal(const Expr *IssueExpr,
-                                    const VarDecl *DanglingGlobal,
-                                    const Expr *MovedExpr,
-                                    SourceLocation ExpiryLoc) {}
+  virtual void reportDanglingGlobal(
+      const Expr *IssueExpr, const VarDecl *DanglingGlobal,
+      const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation ExpiryLoc) {}
 
   // Reports when a reference/iterator is used after the container operation
   // that invalidated it.
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 33ae7b45ea5fa..f3e007b375de1 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -286,27 +286,25 @@ namespace clang::lifetimes::internal {
 
 std::optional<llvm::SmallVector<AssignmentPair>>
 getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
-             const LoanID End, const Expr *IssueExpr) {
+             const LoanID End, const CFGBlock *StartBlock,
+             const Expr *IssueExpr) {
   llvm::SmallVector<OriginID, 4> TargetOIDList;
-  const Expr *WarnningFact = nullptr;
 
   if (const auto *UF = llvm::dyn_cast<UseFact>(CausingFact)) {
-    WarnningFact = UF->getUseExpr();
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       TargetOIDList.push_back(Cur->getOuterOriginID());
   } else if (const auto *RetEscapeF =
                  llvm::dyn_cast<ReturnEscapeFact>(CausingFact)) {
-    WarnningFact = RetEscapeF->getReturnExpr();
     TargetOIDList.push_back(RetEscapeF->getEscapedOriginID());
+  } else if (const auto *GlobalEscapeF =
+                 llvm::dyn_cast<GlobalEscapeFact>(CausingFact)) {
+    TargetOIDList.push_back(GlobalEscapeF->getEscapedOriginID());
   } else {
     llvm_unreachable("Without a corresponding Fact handler, assignment history "
                      "traceback will fail.");
   }
 
-  const CFGBlock *StartBlock =
-      Context.ADC.getCFGStmtMap()->getBlock(WarnningFact);
-  assert(StartBlock && "Searching CFGBlock failed");
   const CFGBlock *EndBlock = Context.ADC.getCFGStmtMap()->getBlock(IssueExpr);
   assert(EndBlock && "Searching CFGBlock failed");
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index fa420c0976dc2..dcbe8f8af3f75 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -22,6 +22,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/DenseMap.h"
@@ -47,6 +48,7 @@ namespace {
 struct PendingWarning {
   SourceLocation ExpiryLoc; // Where the loan expired.
   llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;
+  const ExpireFact *ExpiryExpr;
   const Expr *MovedExpr;
   const Expr *InvalidatedByExpr;
   bool CausingFactDominatesExpiry;
@@ -179,6 +181,7 @@ class LifetimeChecker {
           continue;
         if (causingFactDominatesExpiry(LiveInfo.Kind))
           CurWarning.CausingFactDominatesExpiry = true;
+        CurWarning.ExpiryExpr = EF;
         CurWarning.CausingFact = LiveInfo.CausingFact;
         CurWarning.ExpiryLoc = EF->getExpiryLoc();
         CurWarning.MovedExpr = MovedExpr;
@@ -218,6 +221,7 @@ class LifetimeChecker {
             FinalWarningsMap[LiveLoanID] = {
                 /*ExpiryLoc=*/{},
                 /*CausingFact=*/LiveInfo.CausingFact,
+                /*ExpiryExpr=*/nullptr,
                 /*MovedExpr=*/nullptr,
                 /*InvalidatedByExpr=*/IOF->getInvalidationExpr(),
                 /*CausingFactDominatesExpiry=*/CurDomination};
@@ -238,6 +242,8 @@ class LifetimeChecker {
           L->getAccessPath().getAsPlaceholderParam();
       const Expr *MovedExpr = Warning.MovedExpr;
       SourceLocation ExpiryLoc = Warning.ExpiryLoc;
+      const struct AssignmentQueryContext Context = {
+          LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
 
       if (const auto *UF = CausingFact.dyn_cast<const UseFact *>()) {
         if (Warning.InvalidatedByExpr) {
@@ -252,19 +258,20 @@ class LifetimeChecker {
 
         } else {
           // Scope-based expiry (use-after-scope).
-          const struct AssignmentQueryContext Context = {
-              LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
-          const auto AliasExprs = getAliasList(Context, UF, LID, IssueExpr);
+          const auto *StartBlock =
+              ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
+          const auto AliasExprs =
+              getAliasList(Context, UF, LID, StartBlock, IssueExpr);
           SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
                                          AliasExprs, ExpiryLoc);
         }
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
         if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
-          const struct AssignmentQueryContext Context = {
-              LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
+          const auto *StartBlock =
+              ADC.getCFGStmtMap()->getBlock(RetEscape->getReturnExpr());
           const auto AliasExprs =
-              getAliasList(Context, RetEscape, LID, IssueExpr);
+              getAliasList(Context, RetEscape, LID, StartBlock, IssueExpr);
           SemaHelper->reportUseAfterReturn(IssueExpr,
                                            RetEscape->getReturnExpr(),
                                            MovedExpr, AliasExprs, ExpiryLoc);
@@ -272,11 +279,21 @@ class LifetimeChecker {
           // Dangling field.
           SemaHelper->reportDanglingField(
               IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
-        else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF))
+        else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
           // Global escape.
+          const auto BlockID = FactMgr.getBlockID(Warning.ExpiryExpr).value();
+          const CFGBlock *StartBlock = nullptr;
+          for (auto *const CFGBlock : *ADC.getCFG()) {
+            if (CFGBlock->getBlockID() == BlockID) {
+              StartBlock = CFGBlock;
+              break;
+            }
+          }
+          const auto AliasExprs =
+              getAliasList(Context, GlobalEscape, LID, StartBlock, IssueExpr);
           SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
-                                           MovedExpr, ExpiryLoc);
-        else
+                                           MovedExpr, AliasExprs, ExpiryLoc);
+        } else
           llvm_unreachable("Unhandled OriginEscapesFact type");
       } else
         llvm_unreachable("Unhandled CausingFact type");
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 5352d1595eac1..fe7e5c29fdf8c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -131,10 +131,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << DanglingField->getEndLoc();
   }
 
-  void reportDanglingGlobal(const Expr *IssueExpr,
-                            const VarDecl *DanglingGlobal,
-                            const Expr *MovedExpr,
-                            SourceLocation ExpiryLoc) override {
+  void reportDanglingGlobal(
+      const Expr *IssueExpr, const VarDecl *DanglingGlobal,
+      const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation ExpiryLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved
                      : diag::warn_lifetime_safety_dangling_global)
@@ -142,6 +143,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     if (MovedExpr)
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
+
+    if (AliasList.has_value())
+      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
+        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+
     if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
       S.Diag(DanglingGlobal->getLocation(),
              diag::note_lifetime_safety_dangling_static_here)
diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp
index ac40c7df5a8f5..98b627c96641b 100644
--- a/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp
@@ -22,14 +22,16 @@ void invoke_function_with_side_effects() {
 // We can however catch the inlined one of course!
 void inlined() {
   int local;
-  global = &local; // expected-warning {{address of stack memory escapes to global or static storage}}
-  global_backup = global; 
+  global = &local; // expected-warning {{address of stack memory escapes to global or static storage}} \
+                   // expected-note {{variable 'global' aliases the storage of variable 'local'}}
+  global_backup = global; // expected-note {{variable 'global_backup' aliases the storage of variable 'local'}}
   global = nullptr;
 }
 
 void store_local_in_global() {
   int local;
-  global = &local; // expected-warning {{address of stack memory escapes to global or static storage}}
+  global = &local; // expected-warning {{address of stack memory escapes to global or static storage}} \
+                   // expected-note {{variable 'global' aliases the storage of variable 'local'}}
 }
 
 void store_then_clear() {
@@ -40,5 +42,6 @@ void store_then_clear() {
 
 void dangling_static_field() {
   int local;
-  ObjWithStaticField::static_field = &local; // expected-warning {{address of stack memory escapes to global or static storage}}
-}
\ No newline at end of file
+  ObjWithStaticField::static_field = &local; // expected-warning {{address of stack memory escapes to global or static storage}} \
+                                             // expected-note {{variable 'static_field' aliases the storage of variable 'local'}}
+}

>From d3dcfb92b871aa412d9c733f9edb95978c0c72c2 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Thu, 2 Apr 2026 20:24:19 +0800
Subject: [PATCH 08/22] [LifetimeSafety] using AssignmentQuery in
 reportDanglingField

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  3 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  8 ++--
 .../LifetimeSafety/AssignmentQuery.cpp        | 36 ++++++++++++++-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 19 ++++++--
 clang/lib/Sema/SemaLifetimeSafety.h           | 18 ++++++--
 .../warn-lifetime-safety-dangling-field.cpp   | 45 +++++++++++++------
 6 files changed, 101 insertions(+), 28 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 097beb1682bab..6824081fd458b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -25,7 +25,8 @@
 namespace clang::lifetimes {
 
 using OriginDestExpr =
-    llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *>;
+    llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *,
+                       const MemberExpr *>;
 
 using AssignmentPair = std::pair<OriginDestExpr, const Expr *>;
 
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index f4aa5c66f7e34..a2441eaf5f742 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -72,10 +72,10 @@ class LifetimeSafetySemaHelper {
       const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
       SourceLocation ExpiryLoc) {}
 
-  virtual void reportDanglingField(const Expr *IssueExpr,
-                                   const FieldDecl *Field,
-                                   const Expr *MovedExpr,
-                                   SourceLocation ExpiryLoc) {}
+  virtual void reportDanglingField(
+      const Expr *IssueExpr, const FieldDecl *Field, const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation ExpiryLoc) {}
 
   virtual void reportDanglingGlobal(
       const Expr *IssueExpr, const VarDecl *DanglingGlobal,
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index f3e007b375de1..85fa25fb49784 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/ParentMap.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
@@ -54,6 +55,25 @@ std::optional<const Expr *> GetPureSrcExpr(const Expr *TargetExpr) {
   return std::nullopt;
 }
 
+/// Specifically handles assignments involving a FieldDecl.
+///
+/// Since we currently only store the FieldDecl without its corresponding
+/// LHS expression, this function attempts to recover or resolve the LHS
+/// context by analyzing the RHS.
+const MemberExpr *getFieldFromAssignmentExpr(const Expr *RHS,
+                                             const ParentMap &CurrParentMap) {
+
+  const Stmt *CurrStmt = CurrParentMap.getParent(RHS);
+  if (!CurrStmt)
+    return nullptr;
+  if (const auto *BinaryOp = llvm::dyn_cast<BinaryOperator>(CurrStmt))
+    return llvm::dyn_cast<MemberExpr>(BinaryOp->getLHS());
+  if (const auto *CXXOp = llvm::dyn_cast<CXXOperatorCallExpr>(CurrStmt);
+      CXXOp && CXXOp->getOperator() == OO_Equal && CXXOp->getNumArgs() == 2)
+    return llvm::dyn_cast<MemberExpr>(CXXOp->getArg(0));
+  return nullptr;
+}
+
 AliasAssignmentSearchResult getAliasListCore(
     const AssignmentQueryContext &Context, const CFGBlock *Block,
     const LoanID EndLoanID, OriginID *TargetOID,
@@ -84,8 +104,17 @@ AliasAssignmentSearchResult getAliasListCore(
           if (!DestDecl.has_value()) {
             if (const ValueDecl *DVecl = TargetOrigin.getDecl();
                 DVecl && !DVecl->getLocation().isInvalid()) {
-              CurrOrigin = *TargetOID;
-              DestDecl = DVecl;
+              if (llvm::isa<FieldDecl>(DVecl)) {
+                const auto *CurrExpr = Context.FactMgr.getOriginMgr()
+                                           .getOrigin(OFF->getSrcOriginID())
+                                           .getExpr();
+                if (CurrExpr)
+                  DestDecl = getFieldFromAssignmentExpr(
+                      CurrExpr, Context.ADC.getParentMap());
+              } else {
+                CurrOrigin = *TargetOID;
+                DestDecl = DVecl;
+              }
             }
           } else {
             auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
@@ -297,6 +326,9 @@ getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
   } else if (const auto *RetEscapeF =
                  llvm::dyn_cast<ReturnEscapeFact>(CausingFact)) {
     TargetOIDList.push_back(RetEscapeF->getEscapedOriginID());
+  } else if (const auto *FieldEscapeF =
+                 llvm::dyn_cast<FieldEscapeFact>(CausingFact)) {
+    TargetOIDList.push_back(FieldEscapeF->getEscapedOriginID());
   } else if (const auto *GlobalEscapeF =
                  llvm::dyn_cast<GlobalEscapeFact>(CausingFact)) {
     TargetOIDList.push_back(GlobalEscapeF->getEscapedOriginID());
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index dcbe8f8af3f75..b1177252ed3fd 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -275,11 +275,22 @@ class LifetimeChecker {
           SemaHelper->reportUseAfterReturn(IssueExpr,
                                            RetEscape->getReturnExpr(),
                                            MovedExpr, AliasExprs, ExpiryLoc);
-        } else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF))
+        } else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
           // Dangling field.
-          SemaHelper->reportDanglingField(
-              IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc);
-        else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
+          const auto BlockID = FactMgr.getBlockID(Warning.ExpiryExpr).value();
+          const CFGBlock *StartBlock = nullptr;
+          for (auto *const CFGBlock : *ADC.getCFG()) {
+            if (CFGBlock->getBlockID() == BlockID) {
+              StartBlock = CFGBlock;
+              break;
+            }
+          }
+          const auto AliasExprs =
+              getAliasList(Context, FieldEscape, LID, StartBlock, IssueExpr);
+          SemaHelper->reportDanglingField(IssueExpr,
+                                          FieldEscape->getFieldDecl(),
+                                          MovedExpr, AliasExprs, ExpiryLoc);
+        } else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
           // Global escape.
           const auto BlockID = FactMgr.getBlockID(Warning.ExpiryExpr).value();
           const CFGBlock *StartBlock = nullptr;
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index fe7e5c29fdf8c..29fe079844d42 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -66,6 +66,10 @@ inline void reportAssignment(Sema &S, const Expr *IssueExpr,
                          LDExpr->getExprLoc());
   } else if (const ValueDecl *LVDecl = llvm::dyn_cast<const ValueDecl *>(LHS)) {
     reportAssignmentImpl(S, IssueExpr, LVDecl, RHS, LVDecl->getLocation());
+  } else if (const MemberExpr *LVDecl =
+                 llvm::dyn_cast<const MemberExpr *>(LHS)) {
+    reportAssignmentImpl(S, IssueExpr, LVDecl->getMemberDecl(), RHS,
+                         LVDecl->getExprLoc());
   }
 }
 
@@ -115,10 +119,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << ReturnExpr->getSourceRange();
   }
 
-  void reportDanglingField(const Expr *IssueExpr,
-                           const FieldDecl *DanglingField,
-                           const Expr *MovedExpr,
-                           SourceLocation ExpiryLoc) override {
+  void reportDanglingField(
+      const Expr *IssueExpr, const FieldDecl *DanglingField,
+      const Expr *MovedExpr,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      SourceLocation ExpiryLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved
                      : diag::warn_lifetime_safety_dangling_field)
@@ -126,6 +131,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     if (MovedExpr)
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
+
+    if (AliasList.has_value())
+      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
+        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+
     S.Diag(DanglingField->getLocation(),
            diag::note_lifetime_safety_dangling_field_here)
         << DanglingField->getEndLoc();
diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
index 2afcd4a3dd97b..901511f5904d6 100644
--- a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
@@ -15,7 +15,8 @@ struct CtorInit {
 
 struct CtorSet {
   std::string_view view;  // expected-note {{this field dangles}}
-  CtorSet(std::string s) { view = s; } // expected-warning {{address of stack memory escapes to a field}}
+  CtorSet(std::string s) { view = s; } // expected-warning {{address of stack memory escapes to a field}} \
+                                       // expected-note {{variable 'view' aliases the storage of variable 's'}}
 };
 
 struct CtorInitLifetimeBound {
@@ -80,13 +81,19 @@ struct MemberSetters {
   const char* p;          // expected-note 6 {{this field dangles}}
 
   void setWithParam(std::string s) {
-    view = s;     // expected-warning {{address of stack memory escapes to a field}}
-    p = s.data(); // expected-warning {{address of stack memory escapes to a field}}
+    view = s;     // expected-warning {{address of stack memory escapes to a field}} \
+                  // expected-note {{variable 'view' aliases the storage of variable 's'}}
+    p = s.data(); // expected-warning {{address of stack memory escapes to a field}} \
+                  // expected-note {{function call result aliases the storage of variable 's'}} \
+                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }
 
   void setWithParamAndReturn(std::string s) {
-    view = s;     // expected-warning {{address of stack memory escapes to a field}}
-    p = s.data(); // expected-warning {{address of stack memory escapes to a field}}
+    view = s;     // expected-warning {{address of stack memory escapes to a field}} \
+                  // expected-note {{variable 'view' aliases the storage of variable 's'}}
+    p = s.data(); // expected-warning {{address of stack memory escapes to a field}} \
+                  // expected-note {{function call result aliases the storage of variable 's'}} \
+                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
     return;
   }
 
@@ -103,14 +110,20 @@ struct MemberSetters {
 
   void setWithLocal() {
     std::string s;
-    view = s;     // expected-warning {{address of stack memory escapes to a field}}
-    p = s.data(); // expected-warning {{address of stack memory escapes to a field}}
+    view = s;     // expected-warning {{address of stack memory escapes to a field}} \
+                  // expected-note {{variable 'view' aliases the storage of variable 's'}}
+    p = s.data(); // expected-warning {{address of stack memory escapes to a field}} \
+                  // expected-note {{function call result aliases the storage of variable 's'}} \
+                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
   }
   
   void setWithLocalButMoved() {
     std::string s;
-    view = s;                 // expected-warning-re {{address of stack memory escapes to a field. {{.*}} may have been moved}}
-    p = s.data();             // expected-warning-re {{address of stack memory escapes to a field. {{.*}} may have been moved}}
+    view = s;                 // expected-warning-re {{address of stack memory escapes to a field. {{.*}} may have been moved}} \
+                              // expected-note {{variable 'view' aliases the storage of variable 's'}}
+    p = s.data();             // expected-warning-re {{address of stack memory escapes to a field. {{.*}} may have been moved}} \
+                              // expected-note {{function call result aliases the storage of variable 's'}} \
+                              // expected-note {{variable 'p' aliases the storage of variable 's'}}
     takeString(std::move(s)); // expected-note 2 {{potentially moved here}}
   }
 
@@ -133,15 +146,21 @@ struct MemberSetters {
     p = kGlobal.data();
 
     std::string local;
-    view = local;     // expected-warning {{address of stack memory escapes to a field}}
-    p = local.data(); // expected-warning {{address of stack memory escapes to a field}}
+    view = local;     // expected-warning {{address of stack memory escapes to a field}} \
+                      // expected-note {{variable 'view' aliases the storage of variable 'local'}}
+    p = local.data(); // expected-warning {{address of stack memory escapes to a field}} \
+                      // expected-note {{function call result aliases the storage of variable 'local'}} \
+                      // expected-note {{variable 'p' aliases the storage of variable 'local'}}
   }
 
   void use_after_scope() {
     {
       std::string local;
-      view = local;     // expected-warning {{address of stack memory escapes to a field}}
-      p = local.data(); // expected-warning {{address of stack memory escapes to a field}}
+      view = local;     // expected-warning {{address of stack memory escapes to a field}} \
+                        // expected-note {{variable 'view' aliases the storage of variable 'local'}}
+      p = local.data(); // expected-warning {{address of stack memory escapes to a field}} \
+                        // expected-note {{function call result aliases the storage of variable 'local'}} \
+                        // expected-note {{variable 'p' aliases the storage of variable 'local'}}
     }
     (void)view;
     (void)p;

>From f15840c15fb8a6329d2709f2a46ebe57db632ec8 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 3 Apr 2026 11:23:01 +0800
Subject: [PATCH 09/22] [LifetimeSafety] using AssignmentQuery in
 suggestWithScopeForParmVar

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  3 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  8 ++---
 .../LifetimeSafety/AssignmentQuery.cpp        | 29 ++++++++++++-------
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 10 +++----
 clang/lib/Sema/SemaLifetimeSafety.h           | 15 +++++-----
 .../Sema/warn-lifetime-safety-suggestions.cpp |  6 ++--
 6 files changed, 41 insertions(+), 30 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 6824081fd458b..6ebea68a8a427 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -27,6 +27,7 @@ namespace clang::lifetimes {
 using OriginDestExpr =
     llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *,
                        const MemberExpr *>;
+using LoanEntity = llvm::PointerUnion<const Expr *, const ParmVarDecl *>;
 
 using AssignmentPair = std::pair<OriginDestExpr, const Expr *>;
 
@@ -35,7 +36,7 @@ struct ExprPrintingResult {
   const Expr *CurrExpr;
 };
 
-ExprPrintingResult FormatIssueExprForSema(const Expr *IssueExpr);
+llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity);
 llvm::SmallVector<ExprPrintingResult> FormatSrcExprForSema(const Expr *SrcExpr);
 
 inline __attribute__((always_inline)) llvm::SmallString<32>
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index a2441eaf5f742..bf76e5075b7c2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -95,10 +95,10 @@ class LifetimeSafetySemaHelper {
   using EscapingTarget =
       llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
 
-  // Suggests lifetime bound annotations for function parameters.
-  virtual void suggestLifetimeboundToParmVar(SuggestionScope Scope,
-                                             const ParmVarDecl *ParmToAnnotate,
-                                             EscapingTarget Target) {}
+  // Suggests lifetime bound annotations for function paramters.
+  virtual void suggestLifetimeboundToParmVar(
+      SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate,
+      EscapingTarget Target) {}
 
   // Reports misuse of [[clang::noescape]] when parameter escapes through return
   virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 85fa25fb49784..472f7302b6a12 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -232,16 +232,25 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
 
 namespace clang::lifetimes {
 
-ExprPrintingResult FormatIssueExprForSema(const Expr *IssueExpr) {
-  if (!IssueExpr)
-    return {};
-  const auto *PureExpr = IssueExpr->IgnoreParenCasts();
-  if (!PureExpr)
+llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity) {
+  if (!IssueEntity)
     return {};
 
-  if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr))
-    return {FormatValueDeclForSema(IDeclExpr->getDecl()), IssueExpr};
-  return {{"the temporary"}, IssueExpr};
+  if (const auto *IssueExpr = llvm::dyn_cast<const Expr *>(IssueEntity)) {
+    const auto *PureExpr = IssueExpr->IgnoreParenCasts();
+    if (!PureExpr)
+      return {};
+
+    if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr))
+      return FormatValueDeclForSema(IDeclExpr->getDecl());
+    return {"the temporary"};
+  }
+  if (const auto *IssueParmDecl =
+          llvm::dyn_cast<const ParmVarDecl *>(IssueEntity)) {
+    return FormatValueDeclForSema(IssueParmDecl);
+  }
+
+  return {"the temporary"};
 }
 
 llvm::SmallVector<ExprPrintingResult>
@@ -337,8 +346,8 @@ getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
                      "traceback will fail.");
   }
 
-  const CFGBlock *EndBlock = Context.ADC.getCFGStmtMap()->getBlock(IssueExpr);
-  assert(EndBlock && "Searching CFGBlock failed");
+  const CFGBlock *EndBlock =
+      IssueExpr ? Context.ADC.getCFGStmtMap()->getBlock(IssueExpr) : nullptr;
 
   for (auto TargetOID : TargetOIDList) {
     if (StartBlock == EndBlock) {
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index b1177252ed3fd..7d9b09dc59549 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -332,7 +332,7 @@ class LifetimeChecker {
     return nullptr;
   }
 
-  static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
+  void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
                                          const ParmVarDecl *PVD,
                                          SourceManager &SM,
                                          EscapingTarget EscapeTarget) {
@@ -352,14 +352,14 @@ class LifetimeChecker {
   static void
   suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
                                   const CXXMethodDecl *MD, SourceManager &SM,
-                                  const Expr *EscapeExpr) {
+                                  const Expr *EscapeFact) {
     if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM))
       SemaHelper->suggestLifetimeboundToImplicitThis(
           SuggestionScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl),
-          EscapeExpr);
+          EscapeFact);
     else
-      SemaHelper->suggestLifetimeboundToImplicitThis(SuggestionScope::IntraTU,
-                                                     MD, EscapeExpr);
+      SemaHelper->suggestLifetimeboundToImplicitThis(
+          SuggestionScope::IntraTU, MD, EscapeFact);
   }
 
   void suggestAnnotations() {
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 29fe079844d42..de9953b6d864c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -38,11 +38,12 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) {
                           D->getBeginLoc());
 }
 
-inline void reportAssignmentImpl(Sema &S, const Expr *IssueExpr,
+inline void reportAssignmentImpl(Sema &S, LoanEntity IssueEntity,
                                  const ValueDecl *LHS, const Expr *RHS,
                                  const SourceLocation LHSExploc) {
-  const auto [IssueMsg, _] = FormatIssueExprForSema(IssueExpr);
-  const auto SrcMsgList = FormatSrcExprForSema(RHS);
+  const llvm::SmallString<32> IssueMsg = FormatLoanEntityForSema(IssueEntity);
+  const llvm::SmallVector<ExprPrintingResult> SrcMsgList =
+      FormatSrcExprForSema(RHS);
   if (SrcMsgList.size() == 1 &&
       llvm::isa<DeclRefExpr>(SrcMsgList[0].CurrExpr)) {
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
@@ -56,19 +57,19 @@ inline void reportAssignmentImpl(Sema &S, const Expr *IssueExpr,
   }
 }
 
-inline void reportAssignment(Sema &S, const Expr *IssueExpr,
+inline void reportAssignment(Sema &S, LoanEntity IssueEntity,
                              const OriginDestExpr &LHS, const Expr *RHS) {
   if (!LHS || !RHS)
     return;
 
   if (const DeclRefExpr *LDExpr = llvm::dyn_cast<const DeclRefExpr *>(LHS)) {
-    reportAssignmentImpl(S, IssueExpr, LDExpr->getDecl(), RHS,
+    reportAssignmentImpl(S, IssueEntity, LDExpr->getDecl(), RHS,
                          LDExpr->getExprLoc());
   } else if (const ValueDecl *LVDecl = llvm::dyn_cast<const ValueDecl *>(LHS)) {
-    reportAssignmentImpl(S, IssueExpr, LVDecl, RHS, LVDecl->getLocation());
+    reportAssignmentImpl(S, IssueEntity, LVDecl, RHS, LVDecl->getLocation());
   } else if (const MemberExpr *LVDecl =
                  llvm::dyn_cast<const MemberExpr *>(LHS)) {
-    reportAssignmentImpl(S, IssueExpr, LVDecl->getMemberDecl(), RHS,
+    reportAssignmentImpl(S, IssueEntity, LVDecl->getMemberDecl(), RHS,
                          LVDecl->getExprLoc());
   }
 }
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 4753339309445..d6d7ffd5288c4 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -80,9 +80,9 @@ View return_view_directly(View a) {
 View conditional_return_view(View a, View b, bool c) {
   View res;
   if (c)  
-    res = a;                    
+    res = a;   // expected-note {{variable 'res' aliases the storage of variable 'a'}}
   else
-    res = b;          
+    res = b;   // expected-note {{variable 'res' aliases the storage of variable 'b'}}
   return res;  // expected-note 2 {{param returned here}} 
 }
 
@@ -172,7 +172,7 @@ View only_one_paramter_annotated(View a [[clang::lifetimebound]],
 View reassigned_to_another_parameter(
     View a,
     View b) {     // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
-  a = b;
+  a = b;          // expected-note {{variable 'a' aliases the storage of variable 'b'}}
   return a;       // expected-note {{param returned here}} 
 }
 

>From 7d532a8895c8d21e7226e25e9fc8045c19f2b198 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 3 Apr 2026 11:39:41 +0800
Subject: [PATCH 10/22] using AssignmentQuery in
 suggestWithScopeForImplicitThis

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h        |  3 ++-
 .../Analyses/LifetimeSafety/LifetimeSafety.h         |  7 ++++---
 .../lib/Analysis/LifetimeSafety/AssignmentQuery.cpp  |  6 ++++--
 clang/lib/Sema/SemaLifetimeSafety.h                  | 12 +++++++++---
 4 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 6ebea68a8a427..a68ffe3cc03d3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -27,7 +27,8 @@ namespace clang::lifetimes {
 using OriginDestExpr =
     llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *,
                        const MemberExpr *>;
-using LoanEntity = llvm::PointerUnion<const Expr *, const ParmVarDecl *>;
+using LoanEntity = llvm::PointerUnion<const Expr *, const ParmVarDecl *,
+                                      const CXXMethodDecl *>;
 
 using AssignmentPair = std::pair<OriginDestExpr, const Expr *>;
 
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index bf76e5075b7c2..a69ee3e8826dc 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -112,9 +112,10 @@ class LifetimeSafetySemaHelper {
                                        const VarDecl *EscapeGlobal) {}
 
   // Suggests lifetime bound annotations for implicit this.
-  virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
-                                                  const CXXMethodDecl *MD,
-                                                  const Expr *EscapeExpr) {}
+  virtual void suggestLifetimeboundToImplicitThis(
+      SuggestionScope Scope, const CXXMethodDecl *MD,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      const Expr *EscapeExpr) {}
 
   // Adds inferred lifetime bound attribute for implicit this to its
   // TypeSourceInfo.
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 472f7302b6a12..894c13556c373 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -246,9 +246,11 @@ llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity) {
     return {"the temporary"};
   }
   if (const auto *IssueParmDecl =
-          llvm::dyn_cast<const ParmVarDecl *>(IssueEntity)) {
+          llvm::dyn_cast<const ParmVarDecl *>(IssueEntity))
     return FormatValueDeclForSema(IssueParmDecl);
-  }
+  if (const auto *IssueCXXMD =
+          llvm::dyn_cast<const CXXMethodDecl *>(IssueEntity))
+    return FormatValueDeclForSema(IssueCXXMD);
 
   return {"the temporary"};
 }
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index de9953b6d864c..efc9811999c06 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -221,9 +221,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
           << EscapeField->getSourceRange();
   }
 
-  void suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
-                                          const CXXMethodDecl *MD,
-                                          const Expr *EscapeExpr) override {
+  void suggestLifetimeboundToImplicitThis(
+      SuggestionScope Scope, const CXXMethodDecl *MD,
+      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
+      const Expr *EscapeExpr) override {
     unsigned DiagID = (Scope == SuggestionScope::CrossTU)
                           ? diag::warn_lifetime_safety_cross_tu_this_suggestion
                           : diag::warn_lifetime_safety_intra_tu_this_suggestion;
@@ -249,6 +250,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << MD->getNameInfo().getSourceRange()
         << FixItHint::CreateInsertion(InsertionPoint,
                                       " [[clang::lifetimebound]]");
+
+    if (AliasList.has_value())
+      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
+        reportAssignment(S, MD, AliasStmt.first, AliasStmt.second);
+
     S.Diag(EscapeExpr->getBeginLoc(),
            diag::note_lifetime_safety_suggestion_returned_here)
         << EscapeExpr->getSourceRange();

>From 7e54b152157fe1a0c14f6a93009057936ed8891f Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 3 Apr 2026 14:09:09 +0800
Subject: [PATCH 11/22] fix test error

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 clang/test/Sema/warn-lifetime-safety.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index d2a02ed73fd4d..f0e529cf409a0 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2342,9 +2342,12 @@ void pointer_arithmetic_use_after_scope() {
   int* p3;
   {
     int a[10]{};
-    p = a + 5;  // expected-warning {{object whose reference is captured does not live long enough}}
-    p2 = a - 5; // expected-warning {{object whose reference is captured does not live long enough}}
-    p3 = 5 + a; // expected-warning {{object whose reference is captured does not live long enough}}
+    p = a + 5;  // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+    p2 = a - 5; // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable 'p2' aliases the storage of variable 'a'}}
+    p3 = 5 + a; // expected-warning {{object whose reference is captured does not live long enough}} \
+                // expected-note {{variable 'p3' aliases the storage of variable 'a'}}
   }             // expected-note 3 {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   (void)*p2;    // expected-note {{later used here}}

>From 5b592afcb46368f9672d213bcdb6eebf3379333c Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 3 Apr 2026 14:15:33 +0800
Subject: [PATCH 12/22] fix RHS format error

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  11 -
 .../LifetimeSafety/AssignmentQuery.cpp        |  18 +-
 clang/lib/Sema/SemaLifetimeSafety.h           |  15 +-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |  22 +-
 .../Sema/warn-lifetime-safety-cfg-bailout.cpp |   4 +-
 .../warn-lifetime-safety-dangling-field.cpp   |  38 +-
 .../warn-lifetime-safety-dangling-global.cpp  |   8 +-
 .../Sema/warn-lifetime-safety-suggestions.cpp |   6 +-
 clang/test/Sema/warn-lifetime-safety.cpp      | 550 +++++++++---------
 9 files changed, 342 insertions(+), 330 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index a68ffe3cc03d3..3af1908662140 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -39,17 +39,6 @@ struct ExprPrintingResult {
 
 llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity);
 llvm::SmallVector<ExprPrintingResult> FormatSrcExprForSema(const Expr *SrcExpr);
-
-inline __attribute__((always_inline)) llvm::SmallString<32>
-FormatValueDeclForSema(const ValueDecl *TargetValue) {
-  llvm::SmallString<32> Result;
-  if (TargetValue) {
-    Result += "variable '";
-    Result += TargetValue->getName();
-    Result += "'";
-  }
-  return Result;
-}
 } // namespace clang::lifetimes
 
 namespace clang::lifetimes::internal {
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 894c13556c373..f685072fb58bf 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -228,6 +228,18 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
 
   return std::nullopt;
 }
+
+
+llvm::SmallString<32>
+FormatRHSValueDeclForSema(const ValueDecl *TargetValue) {
+  llvm::SmallString<32> Result;
+  if (TargetValue) {
+    Result += "'";
+    Result += TargetValue->getName();
+    Result += "'";
+  }
+  return Result;
+}
 } // namespace
 
 namespace clang::lifetimes {
@@ -242,15 +254,15 @@ llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity) {
       return {};
 
     if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr))
-      return FormatValueDeclForSema(IDeclExpr->getDecl());
+      return FormatRHSValueDeclForSema(IDeclExpr->getDecl());
     return {"the temporary"};
   }
   if (const auto *IssueParmDecl =
           llvm::dyn_cast<const ParmVarDecl *>(IssueEntity))
-    return FormatValueDeclForSema(IssueParmDecl);
+    return FormatRHSValueDeclForSema(IssueParmDecl);
   if (const auto *IssueCXXMD =
           llvm::dyn_cast<const CXXMethodDecl *>(IssueEntity))
-    return FormatValueDeclForSema(IssueCXXMD);
+    return FormatRHSValueDeclForSema(IssueCXXMD);
 
   return {"the temporary"};
 }
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index efc9811999c06..1497c7633a1c4 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -38,6 +38,17 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) {
                           D->getBeginLoc());
 }
 
+inline __attribute__((always_inline)) llvm::SmallString<32>
+FormatLHSValueDeclForSema(const ValueDecl *TargetValue) {
+  llvm::SmallString<32> Result;
+  if (TargetValue) {
+    Result += "variable '";
+    Result += TargetValue->getName();
+    Result += "'";
+  }
+  return Result;
+}
+
 inline void reportAssignmentImpl(Sema &S, LoanEntity IssueEntity,
                                  const ValueDecl *LHS, const Expr *RHS,
                                  const SourceLocation LHSExploc) {
@@ -47,13 +58,13 @@ inline void reportAssignmentImpl(Sema &S, LoanEntity IssueEntity,
   if (SrcMsgList.size() == 1 &&
       llvm::isa<DeclRefExpr>(SrcMsgList[0].CurrExpr)) {
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
-        << FormatValueDeclForSema(LHS) << IssueMsg;
+        << FormatLHSValueDeclForSema(LHS) << IssueMsg;
   } else {
     for (const auto &SrcMsg : llvm::reverse(SrcMsgList))
       S.Diag(RHS->getBeginLoc(), diag::note_lifetime_safety_note_alias_chain)
           << SrcMsg.CurrExpr->getSourceRange() << SrcMsg.Value << IssueMsg;
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
-        << FormatValueDeclForSema(LHS) << IssueMsg;
+        << FormatLHSValueDeclForSema(LHS) << IssueMsg;
   }
 }
 
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 495839952449e..e35b6ff3dcf53 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -366,8 +366,8 @@ int &doNotFollowReferencesForLocalOwner() {
 // Warning caught by CFG analysis.
   std::unique_ptr<int> localOwner;
   int &p = *localOwner // cfg-warning {{address of stack memory is returned later}} \
-                       // cfg-note {{function call result aliases the storage of variable 'localOwner'}} \
-                       // cfg-note {{variable 'p' aliases the storage of variable 'localOwner'}}
+                       // cfg-note {{function call result aliases the storage of 'localOwner'}} \
+                       // cfg-note {{variable 'p' aliases the storage of 'localOwner'}}
             .get();
   return p; // cfg-note {{returned here}}
 }
@@ -1028,8 +1028,8 @@ void test4() {
 namespace range_based_for_loop_variables {
 std::string_view test_view_loop_var(std::vector<std::string> strings) {
   for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
-                                        // cfg-note {{function call result aliases the storage of variable 'strings'}} \
-                                        // cfg-note {{variable '__begin1' aliases the storage of variable 'strings'}}
+                                        // cfg-note {{function call result aliases the storage of 'strings'}} \
+                                        // cfg-note {{variable '__begin1' aliases the storage of 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
@@ -1037,8 +1037,8 @@ std::string_view test_view_loop_var(std::vector<std::string> strings) {
 
 const char* test_view_loop_var_with_data(std::vector<std::string> strings) {
   for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
-                                        // cfg-note {{function call result aliases the storage of variable 'strings'}} \
-                                        // cfg-note {{variable '__begin1' aliases the storage of variable 'strings'}}
+                                        // cfg-note {{function call result aliases the storage of 'strings'}} \
+                                        // cfg-note {{variable '__begin1' aliases the storage of 'strings'}}
     return s.data(); //cfg-note {{returned here}}
   }
   return "";
@@ -1053,8 +1053,8 @@ std::string_view test_no_error_for_views(std::vector<std::string_view> views) {
 
 std::string_view test_string_ref_var(std::vector<std::string> strings) {
   for (const std::string& s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
-                                          // cfg-note {{function call result aliases the storage of variable 'strings'}} \
-                                          // cfg-note {{variable '__begin1' aliases the storage of variable 'strings'}}
+                                          // cfg-note {{function call result aliases the storage of 'strings'}} \
+                                          // cfg-note {{variable '__begin1' aliases the storage of 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
@@ -1062,9 +1062,9 @@ std::string_view test_string_ref_var(std::vector<std::string> strings) {
 
 std::string_view test_opt_strings(std::optional<std::vector<std::string>> strings_or) {
   for (const std::string& s : *strings_or) {  // cfg-warning {{address of stack memory is returned later}} \
-                                              // cfg-note {{variable '__range1' aliases the storage of variable 'strings_or'}} \
-                                              // cfg-note {{function call result aliases the storage of variable 'strings_or'}} \
-                                              // cfg-note {{variable '__begin1' aliases the storage of variable 'strings_or'}}
+                                              // cfg-note {{variable '__range1' aliases the storage of 'strings_or'}} \
+                                              // cfg-note {{function call result aliases the storage of 'strings_or'}} \
+                                              // cfg-note {{variable '__begin1' aliases the storage of 'strings_or'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
diff --git a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
index 2b047878b21cb..bf6a1712bc297 100644
--- a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
@@ -28,7 +28,7 @@ void single_block_cfg() {
   {
     MyObj s;
     p = &s;     // bailout-warning {{object whose reference is captured does not live long enough}} \
-                // bailout-note {{variable 'p' aliases the storage of variable 's'}}
+                // bailout-note {{variable 'p' aliases the storage of 's'}}
   }             // bailout-note {{destroyed here}}
   (void)*p;     // bailout-note {{later used here}}
 }
@@ -41,7 +41,7 @@ void multiple_block_cfg() {
     if (a > 5) {
       MyObj s;
       p = &s;    // nobailout-warning {{object whose reference is captured does not live long enough}} \
-                 // nobailout-note {{variable 'p' aliases the storage of variable 's'}}
+                 // nobailout-note {{variable 'p' aliases the storage of 's'}}
     } else {     // nobailout-note {{destroyed here}}
       p = &safe;
     }     
diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
index 901511f5904d6..e1216df6e666f 100644
--- a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
@@ -16,7 +16,7 @@ struct CtorInit {
 struct CtorSet {
   std::string_view view;  // expected-note {{this field dangles}}
   CtorSet(std::string s) { view = s; } // expected-warning {{address of stack memory escapes to a field}} \
-                                       // expected-note {{variable 'view' aliases the storage of variable 's'}}
+                                       // expected-note {{variable 'view' aliases the storage of 's'}}
 };
 
 struct CtorInitLifetimeBound {
@@ -82,18 +82,18 @@ struct MemberSetters {
 
   void setWithParam(std::string s) {
     view = s;     // expected-warning {{address of stack memory escapes to a field}} \
-                  // expected-note {{variable 'view' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'view' aliases the storage of 's'}}
     p = s.data(); // expected-warning {{address of stack memory escapes to a field}} \
-                  // expected-note {{function call result aliases the storage of variable 's'}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                  // expected-note {{function call result aliases the storage of 's'}} \
+                  // expected-note {{variable 'p' aliases the storage of 's'}}
   }
 
   void setWithParamAndReturn(std::string s) {
     view = s;     // expected-warning {{address of stack memory escapes to a field}} \
-                  // expected-note {{variable 'view' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'view' aliases the storage of 's'}}
     p = s.data(); // expected-warning {{address of stack memory escapes to a field}} \
-                  // expected-note {{function call result aliases the storage of variable 's'}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                  // expected-note {{function call result aliases the storage of 's'}} \
+                  // expected-note {{variable 'p' aliases the storage of 's'}}
     return;
   }
 
@@ -111,19 +111,19 @@ struct MemberSetters {
   void setWithLocal() {
     std::string s;
     view = s;     // expected-warning {{address of stack memory escapes to a field}} \
-                  // expected-note {{variable 'view' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'view' aliases the storage of 's'}}
     p = s.data(); // expected-warning {{address of stack memory escapes to a field}} \
-                  // expected-note {{function call result aliases the storage of variable 's'}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                  // expected-note {{function call result aliases the storage of 's'}} \
+                  // expected-note {{variable 'p' aliases the storage of 's'}}
   }
   
   void setWithLocalButMoved() {
     std::string s;
     view = s;                 // expected-warning-re {{address of stack memory escapes to a field. {{.*}} may have been moved}} \
-                              // expected-note {{variable 'view' aliases the storage of variable 's'}}
+                              // expected-note {{variable 'view' aliases the storage of 's'}}
     p = s.data();             // expected-warning-re {{address of stack memory escapes to a field. {{.*}} may have been moved}} \
-                              // expected-note {{function call result aliases the storage of variable 's'}} \
-                              // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                              // expected-note {{function call result aliases the storage of 's'}} \
+                              // expected-note {{variable 'p' aliases the storage of 's'}}
     takeString(std::move(s)); // expected-note 2 {{potentially moved here}}
   }
 
@@ -147,20 +147,20 @@ struct MemberSetters {
 
     std::string local;
     view = local;     // expected-warning {{address of stack memory escapes to a field}} \
-                      // expected-note {{variable 'view' aliases the storage of variable 'local'}}
+                      // expected-note {{variable 'view' aliases the storage of 'local'}}
     p = local.data(); // expected-warning {{address of stack memory escapes to a field}} \
-                      // expected-note {{function call result aliases the storage of variable 'local'}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'local'}}
+                      // expected-note {{function call result aliases the storage of 'local'}} \
+                      // expected-note {{variable 'p' aliases the storage of 'local'}}
   }
 
   void use_after_scope() {
     {
       std::string local;
       view = local;     // expected-warning {{address of stack memory escapes to a field}} \
-                        // expected-note {{variable 'view' aliases the storage of variable 'local'}}
+                        // expected-note {{variable 'view' aliases the storage of 'local'}}
       p = local.data(); // expected-warning {{address of stack memory escapes to a field}} \
-                        // expected-note {{function call result aliases the storage of variable 'local'}} \
-                        // expected-note {{variable 'p' aliases the storage of variable 'local'}}
+                        // expected-note {{function call result aliases the storage of 'local'}} \
+                        // expected-note {{variable 'p' aliases the storage of 'local'}}
     }
     (void)view;
     (void)p;
diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp
index 98b627c96641b..600930389e75c 100644
--- a/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-global.cpp
@@ -23,15 +23,15 @@ void invoke_function_with_side_effects() {
 void inlined() {
   int local;
   global = &local; // expected-warning {{address of stack memory escapes to global or static storage}} \
-                   // expected-note {{variable 'global' aliases the storage of variable 'local'}}
-  global_backup = global; // expected-note {{variable 'global_backup' aliases the storage of variable 'local'}}
+                   // expected-note {{variable 'global' aliases the storage of 'local'}}
+  global_backup = global; // expected-note {{variable 'global_backup' aliases the storage of 'local'}}
   global = nullptr;
 }
 
 void store_local_in_global() {
   int local;
   global = &local; // expected-warning {{address of stack memory escapes to global or static storage}} \
-                   // expected-note {{variable 'global' aliases the storage of variable 'local'}}
+                   // expected-note {{variable 'global' aliases the storage of 'local'}}
 }
 
 void store_then_clear() {
@@ -43,5 +43,5 @@ void store_then_clear() {
 void dangling_static_field() {
   int local;
   ObjWithStaticField::static_field = &local; // expected-warning {{address of stack memory escapes to global or static storage}} \
-                                             // expected-note {{variable 'static_field' aliases the storage of variable 'local'}}
+                                             // expected-note {{variable 'static_field' aliases the storage of 'local'}}
 }
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index d6d7ffd5288c4..4bc7a9b6bbbcf 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -80,9 +80,9 @@ View return_view_directly(View a) {
 View conditional_return_view(View a, View b, bool c) {
   View res;
   if (c)  
-    res = a;   // expected-note {{variable 'res' aliases the storage of variable 'a'}}
+    res = a;   // expected-note {{variable 'res' aliases the storage of 'a'}}
   else
-    res = b;   // expected-note {{variable 'res' aliases the storage of variable 'b'}}
+    res = b;   // expected-note {{variable 'res' aliases the storage of 'b'}}
   return res;  // expected-note 2 {{param returned here}} 
 }
 
@@ -172,7 +172,7 @@ View only_one_paramter_annotated(View a [[clang::lifetimebound]],
 View reassigned_to_another_parameter(
     View a,
     View b) {     // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
-  a = b;          // expected-note {{variable 'a' aliases the storage of variable 'b'}}
+  a = b;          // expected-note {{variable 'a' aliases the storage of 'b'}}
   return a;       // expected-note {{param returned here}} 
 }
 
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index f0e529cf409a0..47183629a9605 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -55,7 +55,7 @@ void simple_case() {
   {
     MyObj s;
     p = &s;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -65,7 +65,7 @@ void simple_case_gsl() {
   {
     MyObj s;
     v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 's'}}
+                // expected-note {{variable 'v' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -94,8 +94,8 @@ void pointer_chain() {
   {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
-    q = p;      // expected-note {{variable 'q' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
+    q = p;      // expected-note {{variable 'q' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   (void)*q;     // expected-note {{later used here}}
 }
@@ -105,8 +105,8 @@ void propagation_gsl() {
   {
     MyObj s;
     v1 = s;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v1' aliases the storage of variable 's'}}
-    v2 = v1;    // expected-note {{variable 'v2' aliases the storage of variable 's'}}
+                // expected-note {{variable 'v1' aliases the storage of 's'}}
+    v2 = v1;    // expected-note {{variable 'v2' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   v2.use();     // expected-note {{later used here}}
 }
@@ -116,7 +116,7 @@ void multiple_uses_one_warning() {
   {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   // No second warning for the same loan.
@@ -130,11 +130,11 @@ void multiple_pointers() {
   {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
     q = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'q' aliases the storage of variable 's'}}
+                // expected-note {{variable 'q' aliases the storage of 's'}}
     r = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'r' aliases the storage of variable 's'}}
+                // expected-note {{variable 'r' aliases the storage of 's'}}
   }             // expected-note 3 {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   (void)*q;     // expected-note {{later used here}}
@@ -146,12 +146,12 @@ void single_pointer_multiple_loans(bool cond) {
   if (cond){
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   else {
     MyObj t;
     p = &t;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 't'}}
+                // expected-note {{variable 'p' aliases the storage of 't'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note 2  {{later used here}}
 }
@@ -161,12 +161,12 @@ void single_pointer_multiple_loans_gsl(bool cond) {
   if (cond){
     MyObj s;
     v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 's'}}
+                // expected-note {{variable 'v' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   else {
     MyObj t;
     v = t;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 't'}}
+                // expected-note {{variable 'v' aliases the storage of 't'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note 2 {{later used here}}
 }
@@ -177,7 +177,7 @@ void if_branch(bool cond) {
   if (cond) {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
+                // expected-note {{variable 'p' aliases the storage of 'temp'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -188,7 +188,7 @@ void if_branch_potential(bool cond) {
   if (cond) {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
+                // expected-note {{variable 'p' aliases the storage of 'temp'}}
   }             // expected-note {{destroyed here}}
   if (!cond)
     (void)*p;   // expected-note {{later used here}}
@@ -202,7 +202,7 @@ void if_branch_gsl(bool cond) {
   if (cond) {
     MyObj temp;
     v = temp;   // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 'temp'}}
+                // expected-note {{variable 'v' aliases the storage of 'temp'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -216,10 +216,10 @@ void potential_together(bool cond) {
     MyObj s;
     if (cond)
       p_definite = &s;  // expected-warning {{does not live long enough}} \
-                        // expected-note {{variable 'p_definite' aliases the storage of variable 's'}}
+                        // expected-note {{variable 'p_definite' aliases the storage of 's'}}
     if (cond)
       p_maybe = &s;     // expected-warning {{does not live long enough}} \
-                        // expected-note {{variable 'p_maybe' aliases the storage of variable 's'}}
+                        // expected-note {{variable 'p_maybe' aliases the storage of 's'}}
   }                     // expected-note 2 {{destroyed here}}
   (void)*p_definite;    // expected-note {{later used here}}
   if (!cond)
@@ -233,8 +233,8 @@ void overrides_potential(bool cond) {
   {
     MyObj s;
     q = &s;       // expected-warning {{does not live long enough}} \
-                  // expected-note {{variable 'q' aliases the storage of variable 's'}}
-    p = q;        // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'q' aliases the storage of 's'}}
+    p = q;        // expected-note {{variable 'p' aliases the storage of 's'}}
   }               // expected-note {{destroyed here}}
 
   if (cond) {
@@ -254,7 +254,7 @@ void due_to_conditional_killing(bool cond) {
   {
     MyObj s;
     q = &s;       // expected-warning {{does not live long enough}} \
-                  // expected-note {{variable 'q' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'q' aliases the storage of 's'}}
   }               // expected-note {{destroyed here}}
   if (cond) {
     // 'q' is conditionally "rescued". 'p' is not.
@@ -268,7 +268,7 @@ void for_loop_use_after_loop_body(MyObj safe) {
   for (int i = 0; i < 1; ++i) {
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
 }
@@ -289,7 +289,7 @@ void for_loop_gsl() {
   for (int i = 0; i < 1; ++i) {
     MyObj s;
     v = s;      // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 's'}}
+                // expected-note {{variable 'v' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   v.use();      // expected-note {{later used here}}
 }
@@ -301,7 +301,7 @@ void for_loop_use_before_loop_body(MyObj safe) {
     (void)*p;   // expected-note {{later used here}}
     MyObj s;
     p = &s;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   (void)*p;
 }
@@ -313,7 +313,7 @@ void loop_with_break(bool cond) {
     if (cond) {
       MyObj temp;
       p = &temp; // expected-warning {{does not live long enough}} \
-                 // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
+                 // expected-note {{variable 'p' aliases the storage of 'temp'}}
       break;     // expected-note {{destroyed here}}
     }           
   } 
@@ -327,7 +327,7 @@ void loop_with_break_gsl(bool cond) {
     if (cond) {
       MyObj temp;
       v = temp;   // expected-warning {{object whose reference is captured does not live long enough}} \
-                  // expected-note {{variable 'v' aliases the storage of variable 'temp'}}
+                  // expected-note {{variable 'v' aliases the storage of 'temp'}}
       break;      // expected-note {{destroyed here}}
     }
   }
@@ -342,7 +342,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     MyObj unsafe;
     if (cond) {
       p = &unsafe; // expected-warning {{does not live long enough}} \
-                   // expected-note {{variable 'p' aliases the storage of variable 'unsafe'}}
+                   // expected-note {{variable 'p' aliases the storage of 'unsafe'}}
       break;       // expected-note {{destroyed here}}
     }
   }
@@ -353,7 +353,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     MyObj unsafe;
     if (cond) {
       p = &unsafe;    // expected-warning {{does not live long enough}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'unsafe'}}
+                      // expected-note {{variable 'p' aliases the storage of 'unsafe'}}
       if (cond)
         break;        // expected-note {{destroyed here}}
     }
@@ -365,7 +365,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     if (cond) {
       MyObj unsafe2;
       p = &unsafe2;   // expected-warning {{does not live long enough}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'unsafe2'}}
+                      // expected-note {{variable 'p' aliases the storage of 'unsafe2'}}
       break;          // expected-note {{destroyed here}}
     }
   }
@@ -376,7 +376,7 @@ void multiple_expiry_of_same_loan(bool cond) {
     MyObj unsafe;
     if (cond)
       p = &unsafe;    // expected-warning {{does not live long enough}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'unsafe'}}
+                      // expected-note {{variable 'p' aliases the storage of 'unsafe'}}
     if (cond)
       break;          // expected-note {{destroyed here}}
   }
@@ -390,7 +390,7 @@ void switch_potential(int mode) {
   case 1: {
     MyObj temp;
     p = &temp;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
+                // expected-note {{variable 'p' aliases the storage of 'temp'}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
@@ -410,19 +410,19 @@ void switch_uaf(int mode) {
   case 1: {
     MyObj temp1;
     p = &temp1; // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'temp1'}}
+                // expected-note {{variable 'p' aliases the storage of 'temp1'}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
     MyObj temp2;
     p = &temp2; // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'temp2'}}
+                // expected-note {{variable 'p' aliases the storage of 'temp2'}}
     break;      // expected-note {{destroyed here}}
   }
   default: {
     MyObj temp2;
     p = &temp2; // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'temp2'}}
+                // expected-note {{variable 'p' aliases the storage of 'temp2'}}
     break;      // expected-note {{destroyed here}}
   }
   }
@@ -435,19 +435,19 @@ void switch_gsl(int mode) {
   case 1: {
     MyObj temp1;
     v = temp1;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 'temp1'}}
+                // expected-note {{variable 'v' aliases the storage of 'temp1'}}
     break;      // expected-note {{destroyed here}}
   }
   case 2: {
     MyObj temp2;
     v = temp2;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 'temp2'}}
+                // expected-note {{variable 'v' aliases the storage of 'temp2'}}
     break;      // expected-note {{destroyed here}}
   }
   default: {
     MyObj temp3;
     v = temp3;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'v' aliases the storage of variable 'temp3'}}
+                // expected-note {{variable 'v' aliases the storage of 'temp3'}}
     break;      // expected-note {{destroyed here}}
   }
   }
@@ -461,7 +461,7 @@ void loan_from_previous_iteration(MyObj safe, bool condition) {
   while (condition) {
     MyObj x;
     p = &x;     // expected-warning {{does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'x'}}
+                // expected-note {{variable 'p' aliases the storage of 'x'}}
 
     if (condition)
       q = p;
@@ -475,7 +475,7 @@ void trivial_int_uaf() {
   {
       int b = 1;
       a = &b;  // expected-warning {{object whose reference is captured does not live long enough}} \
-               // expected-note {{variable 'a' aliases the storage of variable 'b'}}
+               // expected-note {{variable 'a' aliases the storage of 'b'}}
   }            // expected-note {{destroyed here}}
   (void)*a;    // expected-note {{later used here}}
 }
@@ -485,7 +485,7 @@ void trivial_class_uaf() {
   {
       TriviallyDestructedClass s;
       ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'ptr' aliases the storage of variable 's'}}
+                // expected-note {{variable 'ptr' aliases the storage of 's'}}
   }             // expected-note {{destroyed here}}
   (void)ptr;    // expected-note {{later used here}}
 }
@@ -507,7 +507,7 @@ void small_scope_reference_var_no_error() {
 MyObj* simple_return_stack_address() {
   MyObj s;      
   MyObj* p = &s; // expected-warning {{address of stack memory is returned later}} \
-                 // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                 // expected-note {{variable 'p' aliases the storage of 's'}}
   return p;      // expected-note {{returned here}}
 }
 
@@ -537,7 +537,7 @@ const MyObj* conditional_assign_unconditional_return(const MyObj& safe, bool c)
   const MyObj* p = &safe;
   if (c) {
     p = &s;       // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'p' aliases the storage of 's'}}
   }     
   return p;      // expected-note {{returned here}}
 }
@@ -547,7 +547,7 @@ View conditional_assign_both_branches(const MyObj& safe, bool c) {
   View p;
   if (c) {
     p = s;      // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                // expected-note {{variable 'p' aliases the storage of 's'}}
   } 
   else {
     p = safe;
@@ -560,15 +560,15 @@ View reassign_safe_to_local(const MyObj& safe) {
   MyObj local;
   View p = safe;
   p = local;    // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'local'}}
+                // expected-note {{variable 'p' aliases the storage of 'local'}}
   return p;     // expected-note {{returned here}}
 }
 
 View pointer_chain_to_local() {
   MyObj local;
   View p1 = local;     // expected-warning {{address of stack memory is returned later}} \
-                       // expected-note {{variable 'p1' aliases the storage of variable 'local'}}
-  View p2 = p1;        // expected-note {{variable 'p2' aliases the storage of variable 'local'}}
+                       // expected-note {{variable 'p1' aliases the storage of 'local'}}
+  View p2 = p1;        // expected-note {{variable 'p2' aliases the storage of 'local'}}
   return p2;           // expected-note {{returned here}}
 }
 
@@ -578,12 +578,12 @@ View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) {
   View p;
   if (c1) {
     p = local1;       // expected-warning {{address of stack memory is returned later}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'local1'}}
+                      // expected-note {{variable 'p' aliases the storage of 'local1'}}
     return p;         // expected-note {{returned here}}
   }
   else if (c2) {
     p = local2;       // expected-warning {{address of stack memory is returned later}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'local2'}}
+                      // expected-note {{variable 'p' aliases the storage of 'local2'}}
     return p;         // expected-note {{returned here}}
   }
   p = safe;
@@ -596,11 +596,11 @@ View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) {
   View p;
   if (c1) {
     p = local1;      // expected-warning {{address of stack memory is returned later}} \
-                     // expected-note {{variable 'p' aliases the storage of variable 'local1'}}
+                     // expected-note {{variable 'p' aliases the storage of 'local1'}}
   }
   else if (c2) {
     p = local2;      // expected-warning {{address of stack memory is returned later}} \
-                     // expected-note {{variable 'p' aliases the storage of variable 'local2'}}
+                     // expected-note {{variable 'p' aliases the storage of 'local2'}}
   }
   else {
     p = safe;
@@ -624,7 +624,7 @@ int* trivial_int_uar() {
   int *a;
   int b = 1;
   a = &b;          // expected-warning {{address of stack memory is returned later}} \
-                   // expected-note {{variable 'a' aliases the storage of variable 'b'}}
+                   // expected-note {{variable 'a' aliases the storage of 'b'}}
   return a;        // expected-note {{returned here}}
 }
 
@@ -632,7 +632,7 @@ TriviallyDestructedClass* trivial_class_uar () {
   TriviallyDestructedClass *ptr;
   TriviallyDestructedClass s;
   ptr = &s;       // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'ptr' aliases the storage of variable 's'}}
+                  // expected-note {{variable 'ptr' aliases the storage of 's'}}
   return ptr;     // expected-note {{returned here}}
 }
 
@@ -648,7 +648,7 @@ int* return_pointer_to_parameter(int a) {
 
 const int& return_reference_to_parameter(int a) {
     const int &b = a;   // expected-warning {{address of stack memory is returned later}} \
-                        // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+                        // expected-note {{variable 'b' aliases the storage of 'a'}}
     return b;           // expected-note {{returned here}}
 }
 int return_reference_to_parameter_no_error(int a) {
@@ -659,7 +659,7 @@ int return_reference_to_parameter_no_error(int a) {
 MyObj*& return_ref_to_local_ptr_pointing_to_local() {
   MyObj local;
   MyObj* p = &local; // expected-warning {{address of stack memory is returned later}} \
-                     // expected-note {{variable 'p' aliases the storage of variable 'local'}}
+                     // expected-note {{variable 'p' aliases the storage of 'local'}}
   return p;          // expected-note {{returned here}} \
                      // expected-warning {{address of stack memory is returned later}} \
                      // expected-note {{returned here}}
@@ -667,22 +667,22 @@ MyObj*& return_ref_to_local_ptr_pointing_to_local() {
 
 const int& reference_via_conditional(int a, int b, bool cond) {
     const int &c = (cond ? ((a)) : (b));  // expected-warning 2 {{address of stack memory is returned later}} \
-                                          // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
-                                          // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+                                          // expected-note {{variable 'c' aliases the storage of 'a'}} \
+                                          // expected-note {{variable 'c' aliases the storage of 'b'}}
     return c;                             // expected-note 2 {{returned here}}
 }
 const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
     const int &c = cond ? a : b;  // expected-warning 2 {{address of stack memory is returned later}} \
-                                  // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
-                                  // expected-note {{variable 'c' aliases the storage of variable 'b'}}
-    const int* d = &c;            // expected-note {{variable 'd' aliases the storage of variable 'a'}} \
-                                  // expected-note {{variable 'd' aliases the storage of variable 'b'}}
+                                  // expected-note {{variable 'c' aliases the storage of 'a'}} \
+                                  // expected-note {{variable 'c' aliases the storage of 'b'}}
+    const int* d = &c;            // expected-note {{variable 'd' aliases the storage of 'a'}} \
+                                  // expected-note {{variable 'd' aliases the storage of 'b'}}
     return d;                     // expected-note 2 {{returned here}}
 }
 
 const int& return_pointer_to_parameter_via_reference_1(int a) {
     const int* d = &a; // expected-warning {{address of stack memory is returned later}} \
-                       // expected-note {{variable 'd' aliases the storage of variable 'a'}}
+                       // expected-note {{variable 'd' aliases the storage of 'a'}}
     return *d;    // expected-note {{returned here}}
 }
 
@@ -697,7 +697,7 @@ void test_view_pointer() {
   {
     View v;
     vp = &v;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable 'vp' aliases the storage of variable 'v'}}
+                 // expected-note {{variable 'vp' aliases the storage of 'v'}}
   }              // expected-note {{destroyed here}}
   vp->use();     // expected-note {{later used here}}
 }
@@ -707,7 +707,7 @@ void test_view_double_pointer() {
   {
     View* vp = nullptr;
     vpp = &vp;   // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable 'vpp' aliases the storage of variable 'vp'}}
+                 // expected-note {{variable 'vpp' aliases the storage of 'vp'}}
   }              // expected-note {{destroyed here}}
   (**vpp).use(); // expected-note {{later used here}}
 }
@@ -720,8 +720,8 @@ struct PtrHolder {
 int* const& test_ref_to_ptr() {
   PtrHolder a;
   int *const &ref = a.getRef();  // expected-warning {{address of stack memory is returned later}} \
-                                 // expected-note {{function call result aliases the storage of variable 'a'}} \
-                                 // expected-note {{variable 'ref' aliases the storage of variable 'a'}}
+                                 // expected-note {{function call result aliases the storage of 'a'}} \
+                                 // expected-note {{variable 'ref' aliases the storage of 'a'}}
   return ref;  // expected-note {{returned here}}
 }
 int* const test_ref_to_ptr_no_error() {
@@ -737,9 +737,9 @@ void test_lifetimebound_multi_level() {
     int* p = nullptr;
     int** pp = &p;
     int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{variable 'ppp' aliases the storage of variable 'pp'}}
-    result = return_inner_ptr_addr(ppp); // expected-note {{function call result aliases the storage of variable 'pp'}} \
-                                         // expected-note {{variable 'result' aliases the storage of variable 'pp'}}
+                      // expected-note {{variable 'ppp' aliases the storage of 'pp'}}
+    result = return_inner_ptr_addr(ppp); // expected-note {{function call result aliases the storage of 'pp'}} \
+                                         // expected-note {{variable 'result' aliases the storage of 'pp'}}
   }                   // expected-note {{destroyed here}}
   (void)**result;     // expected-note {{used here}}
 }
@@ -759,12 +759,12 @@ void test_assign_through_double_ptr() {
 int** test_ternary_double_ptr(bool cond) {
   int a = 1, b = 2;
   int* pa = &a;  // expected-warning {{address of stack memory is returned later}} \
-                 // expected-note {{variable 'pa' aliases the storage of variable 'a'}}
+                 // expected-note {{variable 'pa' aliases the storage of 'a'}}
   int* pb = &b;  // expected-warning {{address of stack memory is returned later}} \
-                 // expected-note {{variable 'pb' aliases the storage of variable 'b'}}
+                 // expected-note {{variable 'pb' aliases the storage of 'b'}}
   int** result = cond ? &pa : &pb;  // expected-warning 2 {{address of stack memory is returned later}} \
-                                    // expected-note {{variable 'result' aliases the storage of variable 'pa'}} \
-                                    // expected-note {{variable 'result' aliases the storage of variable 'pb'}}
+                                    // expected-note {{variable 'result' aliases the storage of 'pa'}} \
+                                    // expected-note {{variable 'result' aliases the storage of 'pb'}}
   return result; // expected-note 4 {{returned here}}
 }
 //===----------------------------------------------------------------------===//
@@ -777,7 +777,7 @@ MyObj* uaf_before_uar() {
   {
     MyObj local_obj;
     p = &local_obj;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable 'p' aliases the storage of variable 'local_obj'}}
+                     // expected-note {{variable 'p' aliases the storage of 'local_obj'}}
   }                  // expected-note {{destroyed here}}
   return p;          // expected-note {{later used here}}
 }
@@ -787,7 +787,7 @@ View uar_before_uaf(const MyObj& safe, bool c) {
   {
     MyObj local_obj; 
     p = local_obj;  // expected-warning {{ddress of stack memory is returned later}} \
-                    // expected-note {{variable 'p' aliases the storage of variable 'local_obj'}}
+                    // expected-note {{variable 'p' aliases the storage of 'local_obj'}}
     if (c) {
       return p;     // expected-note {{returned here}}
     }
@@ -869,8 +869,8 @@ void lifetimebound_simple_function() {
   {
     MyObj obj;
     v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}} \
-                       // expected-note {{function call result aliases the storage of variable 'obj'}} \
-                       // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                       // expected-note {{function call result aliases the storage of 'obj'}} \
+                       // expected-note {{variable 'v' aliases the storage of 'obj'}}
   }                    // expected-note {{destroyed here}}
   v.use();             // expected-note {{later used here}}
 }
@@ -879,10 +879,10 @@ void lifetimebound_multiple_args_definite() {
   View v;
   {
     MyObj obj1, obj2;
-    v = Choose(true,  // expected-note {{function call result aliases the storage of variable 'obj1'}} \
-                      // expected-note {{variable 'v' aliases the storage of variable 'obj1'}} \
-                      // expected-note {{function call result aliases the storage of variable 'obj2'}} \
-                      // expected-note {{variable 'v' aliases the storage of variable 'obj2'}}
+    v = Choose(true,  // expected-note {{function call result aliases the storage of 'obj1'}} \
+                      // expected-note {{variable 'v' aliases the storage of 'obj1'}} \
+                      // expected-note {{function call result aliases the storage of 'obj2'}} \
+                      // expected-note {{variable 'v' aliases the storage of 'obj2'}}
                obj1,  // expected-warning {{object whose reference is captured does not live long enough}}
                obj2); // expected-warning {{object whose reference is captured does not live long enough}}
   }                              // expected-note 2 {{destroyed here}}
@@ -896,10 +896,10 @@ void lifetimebound_multiple_args_potential(bool cond) {
     MyObj obj1;
     if (cond) {
       MyObj obj2;
-      v = Choose(true,             // expected-note {{function call result aliases the storage of variable 'obj1'}} \
-                                   // expected-note {{variable 'v' aliases the storage of variable 'obj1'}} \
-                                   // expected-note {{function call result aliases the storage of variable 'obj2'}} \
-                                   // expected-note {{variable 'v' aliases the storage of variable 'obj2'}}
+      v = Choose(true,             // expected-note {{function call result aliases the storage of 'obj1'}} \
+                                   // expected-note {{variable 'v' aliases the storage of 'obj1'}} \
+                                   // expected-note {{function call result aliases the storage of 'obj2'}} \
+                                   // expected-note {{variable 'v' aliases the storage of 'obj2'}}
                  obj1,             // expected-warning {{object whose reference is captured does not live long enough}}
                  obj2);            // expected-warning {{object whose reference is captured does not live long enough}}
     }                              // expected-note {{destroyed here}}
@@ -913,8 +913,8 @@ void lifetimebound_mixed_args() {
   {
     MyObj obj1, obj2;
     v = SelectFirst(obj1,        // expected-warning {{object whose reference is captured does not live long enough}} \
-                                 // expected-note {{function call result aliases the storage of variable 'obj1'}} \
-                                 // expected-note {{variable 'v' aliases the storage of variable 'obj1'}}
+                                 // expected-note {{function call result aliases the storage of 'obj1'}} \
+                                 // expected-note {{variable 'v' aliases the storage of 'obj1'}}
                     obj2);
   }                              // expected-note {{destroyed here}}
   v.use();                       // expected-note {{later used here}}
@@ -931,8 +931,8 @@ void lifetimebound_member_function() {
   {
     MyObj obj;
     v  = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                        // expected-note {{function call result aliases the storage of variable 'obj'}} \
-                        // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                        // expected-note {{function call result aliases the storage of 'obj'}} \
+                        // expected-note {{variable 'v' aliases the storage of 'obj'}}
   }                     // expected-note {{destroyed here}}
   v.use();              // expected-note {{later used here}}
 }
@@ -948,7 +948,7 @@ void lifetimebound_conversion_operator() {
   {
     LifetimeBoundConversionView obj;
     v = obj;  // expected-warning {{object whose reference is captured does not live long enough}} \
-              // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+              // expected-note {{variable 'v' aliases the storage of 'obj'}}
   }           // expected-note {{destroyed here}}
   v.use();    // expected-note {{later used here}}
 }
@@ -958,8 +958,8 @@ void lifetimebound_chained_calls() {
   {
     MyObj obj;
     v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}} \
-                                           // expected-note {{function call result aliases the storage of variable 'obj'}} \
-                                           // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                                           // expected-note {{function call result aliases the storage of 'obj'}} \
+                                           // expected-note {{variable 'v' aliases the storage of 'obj'}}
   }                                        // expected-note {{destroyed here}}
   v.use();                                 // expected-note {{later used here}}
 }
@@ -969,8 +969,8 @@ void lifetimebound_with_pointers() {
   {
     MyObj obj;
     ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}} \
-                           // expected-note {{function call result aliases the storage of variable 'obj'}} \
-                           // expected-note {{variable 'ptr' aliases the storage of variable 'obj'}}
+                           // expected-note {{function call result aliases the storage of 'obj'}} \
+                           // expected-note {{variable 'ptr' aliases the storage of 'obj'}}
   }                        // expected-note {{destroyed here}}
   (void)*ptr;              // expected-note {{later used here}}
 }
@@ -988,8 +988,8 @@ void lifetimebound_partial_safety(bool cond) {
   
   if (cond) {
     MyObj temp_obj;
-    v = Choose(true,      // expected-note {{function call result aliases the storage of variable 'temp_obj'}} \
-                          // expected-note {{variable 'v' aliases the storage of variable 'temp_obj'}}
+    v = Choose(true,      // expected-note {{function call result aliases the storage of 'temp_obj'}} \
+                          // expected-note {{variable 'v' aliases the storage of 'temp_obj'}}
                safe_obj,
                temp_obj); // expected-warning {{object whose reference is captured does not live long enough}}
   }                       // expected-note {{destroyed here}}
@@ -1003,10 +1003,10 @@ void lifetimebound_return_reference() {
   {
     MyObj obj;
     View temp_v = obj;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                           // expected-note {{variable 'temp_v' aliases the storage of variable 'obj'}}
-    const MyObj& ref = GetObject(temp_v); // expected-note {{function call result aliases the storage of variable 'obj'}} \
-                                          // expected-note {{variable 'ref' aliases the storage of variable 'obj'}}
-    ptr = &ref;           // expected-note {{variable 'ptr' aliases the storage of variable 'obj'}}
+                           // expected-note {{variable 'temp_v' aliases the storage of 'obj'}}
+    const MyObj& ref = GetObject(temp_v); // expected-note {{function call result aliases the storage of 'obj'}} \
+                                          // expected-note {{variable 'ref' aliases the storage of 'obj'}}
+    ptr = &ref;           // expected-note {{variable 'ptr' aliases the storage of 'obj'}}
   }                       // expected-note {{destroyed here}}
   (void)*ptr;             // expected-note {{later used here}}
 }
@@ -1021,7 +1021,7 @@ void lifetimebound_ctor() {
   {
     MyObj obj;
     v = obj; // expected-warning {{object whose reference is captured does not live long enough}} \
-             // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+             // expected-note {{variable 'v' aliases the storage of 'obj'}}
   }          // expected-note {{destroyed here}}
   (void)v;   // expected-note {{later used here}}
 }
@@ -1098,7 +1098,7 @@ void conditional_operator_one_unsafe_branch(bool cond) {
   {
     MyObj temp;
     p = cond ? &temp  // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'temp'}}
+                      // expected-note {{variable 'p' aliases the storage of 'temp'}}
              : &safe;
   }  // expected-note {{destroyed here}}
 
@@ -1115,8 +1115,8 @@ void conditional_operator_two_unsafe_branches(bool cond) {
   {
     MyObj a, b;
     p = cond ? &a   // expected-warning {{object whose reference is captured does not live long enough}} \
-                    // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
-                    // expected-note {{variable 'p' aliases the storage of variable 'b'}}
+                    // expected-note {{variable 'p' aliases the storage of 'a'}} \
+                    // expected-note {{variable 'p' aliases the storage of 'b'}}
              : &b;  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1127,10 +1127,10 @@ void conditional_operator_nested(bool cond) {
   {
     MyObj a, b, c, d;
     p = cond ? cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}}. \
-                            // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
-                            // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
-                            // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
-                            // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+                            // expected-note {{variable 'p' aliases the storage of 'a'}} \
+                            // expected-note {{variable 'p' aliases the storage of 'b'}} \
+                            // expected-note {{variable 'p' aliases the storage of 'c'}} \
+                            // expected-note {{variable 'p' aliases the storage of 'd'}}
                     : &b    // expected-warning {{object whose reference is captured does not live long enough}}.
              : cond ? &c    // expected-warning {{object whose reference is captured does not live long enough}}.
                     : &d;   // expected-warning {{object whose reference is captured does not live long enough}}.
@@ -1143,10 +1143,10 @@ void conditional_operator_lifetimebound(bool cond) {
   {
     MyObj a, b;
     p = Identity(cond ? &a    // expected-warning {{object whose reference is captured does not live long enough}} \
-                              // expected-note {{function call result aliases the storage of variable 'b'}} \
-                              // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
-                              // expected-note {{function call result aliases the storage of variable 'a'}} \
-                              // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                              // expected-note {{function call result aliases the storage of 'b'}} \
+                              // expected-note {{variable 'p' aliases the storage of 'b'}} \
+                              // expected-note {{function call result aliases the storage of 'a'}} \
+                              // expected-note {{variable 'p' aliases the storage of 'a'}}
                       : &b);  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1157,10 +1157,10 @@ void conditional_operator_lifetimebound_nested(bool cond) {
   {
     MyObj a, b;
     p = Identity(cond ? Identity(&a)    // expected-warning {{object whose reference is captured does not live long enough}} \
-                                        // expected-note {{function call result aliases the storage of variable 'b'}} \
-                                        // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
-                                        // expected-note {{function call result aliases the storage of variable 'a'}} \
-                                        // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                                        // expected-note {{function call result aliases the storage of 'b'}} \
+                                        // expected-note {{variable 'p' aliases the storage of 'b'}} \
+                                        // expected-note {{function call result aliases the storage of 'a'}} \
+                                        // expected-note {{variable 'p' aliases the storage of 'a'}}
                       : Identity(&b));  // expected-warning {{object whose reference is captured does not live long enough}}
   }  // expected-note 2 {{destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1171,14 +1171,14 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
   {
     MyObj a, b, c, d;
     p = Identity(cond ? Identity(cond ? &a     // expected-warning {{object whose reference is captured does not live long enough}} \
-                                               // expected-note {{function call result aliases the storage of variable 'a'}} \
-                                               // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
-                                               // expected-note {{function call result aliases the storage of variable 'b'}} \
-                                               // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
-                                               // expected-note {{function call result aliases the storage of variable 'c'}} \
-                                               // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
-                                               // expected-note {{function call result aliases the storage of variable 'd'}} \
-                                               // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+                                               // expected-note {{function call result aliases the storage of 'a'}} \
+                                               // expected-note {{variable 'p' aliases the storage of 'a'}} \
+                                               // expected-note {{function call result aliases the storage of 'b'}} \
+                                               // expected-note {{variable 'p' aliases the storage of 'b'}} \
+                                               // expected-note {{function call result aliases the storage of 'c'}} \
+                                               // expected-note {{variable 'p' aliases the storage of 'c'}} \
+                                               // expected-note {{function call result aliases the storage of 'd'}} \
+                                               // expected-note {{variable 'p' aliases the storage of 'd'}}
                                       : &b)    // expected-warning {{object whose reference is captured does not live long enough}}
                       : Identity(cond ? &c     // expected-warning {{object whose reference is captured does not live long enough}}
                                       : &d));  // expected-warning {{object whose reference is captured does not live long enough}}
@@ -1191,25 +1191,25 @@ void parentheses(bool cond) {
   {
     MyObj a;
     p = &((((a))));  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                     // expected-note {{variable 'p' aliases the storage of 'a'}}
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 
   {
     MyObj a;
     p = ((GetPointer((a))));  // expected-warning {{object whose reference is captured does not live long enough}} \
-                              // expected-note {{function call result aliases the storage of variable 'a'}} \
-                              // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                              // expected-note {{function call result aliases the storage of 'a'}} \
+                              // expected-note {{variable 'p' aliases the storage of 'a'}}
   }                           // expected-note {{destroyed here}}
   (void)*p;                   // expected-note {{later used here}}
 
   {
     MyObj a, b, c, d;
     p = &(cond ? (cond ? a     // expected-warning {{object whose reference is captured does not live long enough}}. \
-                               // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
-                               // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
-                               // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
-                               // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+                               // expected-note {{variable 'p' aliases the storage of 'a'}} \
+                               // expected-note {{variable 'p' aliases the storage of 'b'}} \
+                               // expected-note {{variable 'p' aliases the storage of 'c'}} \
+                               // expected-note {{variable 'p' aliases the storage of 'd'}}
                        : b)    // expected-warning {{object whose reference is captured does not live long enough}}.
                : (cond ? c     // expected-warning {{object whose reference is captured does not live long enough}}.
                        : d));  // expected-warning {{object whose reference is captured does not live long enough}}.
@@ -1219,10 +1219,10 @@ void parentheses(bool cond) {
   {
     MyObj a, b, c, d;
     p = ((cond ? (((cond ? &a : &b)))   // expected-warning 2 {{object whose reference is captured does not live long enough}}. \
-                                        // expected-note {{variable 'p' aliases the storage of variable 'a'}} \
-                                        // expected-note {{variable 'p' aliases the storage of variable 'b'}} \
-                                        // expected-note {{variable 'p' aliases the storage of variable 'c'}} \
-                                        // expected-note {{variable 'p' aliases the storage of variable 'd'}}
+                                        // expected-note {{variable 'p' aliases the storage of 'a'}} \
+                                        // expected-note {{variable 'p' aliases the storage of 'b'}} \
+                                        // expected-note {{variable 'p' aliases the storage of 'c'}} \
+                                        // expected-note {{variable 'p' aliases the storage of 'd'}}
               : &(((cond ? c : d)))));  // expected-warning 2 {{object whose reference is captured does not live long enough}}.
   }  // expected-note 4 {{destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
@@ -1293,8 +1293,8 @@ void foobar() {
   {
     StatusOr<MyObj> string_or = getStringOr();
     view = string_or. // expected-warning {{object whose reference is captured does not live long enough}} \\
-                      // expected-note {{function call result aliases the storage of variable 'string_or'}} \\
-                      // expected-note {{variable 'view' aliases the storage of variable 'string_or'}}
+                      // expected-note {{function call result aliases the storage of 'string_or'}} \\
+                      // expected-note {{variable 'view' aliases the storage of 'string_or'}}
             value();
   }                     // expected-note {{destroyed here}}
   (void)view;           // expected-note {{later used here}}
@@ -1315,8 +1315,8 @@ void range_based_for_use_after_scope() {
   {
     MyObjStorage s;
     for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} \
-                               // expected-note {{variable 'o' aliases the storage of variable 's'}}
-      v = o;                   // expected-note {{variable 'v' aliases the storage of variable 's'}}
+                               // expected-note {{variable 'o' aliases the storage of 's'}}
+      v = o;                   // expected-note {{variable 'v' aliases the storage of 's'}}
     }
   } // expected-note {{destroyed here}}
   v.use(); // expected-note {{later used here}}
@@ -1325,8 +1325,8 @@ void range_based_for_use_after_scope() {
 View range_based_for_use_after_return() {
   MyObjStorage s;
   for (const MyObj &o : s) { // expected-warning {{address of stack memory is returned later}} \
-                             // expected-note {{function call result aliases the storage of variable 's'}} \
-                             // expected-note {{variable '__begin1' aliases the storage of variable 's'}}
+                             // expected-note {{function call result aliases the storage of 's'}} \
+                             // expected-note {{variable '__begin1' aliases the storage of 's'}}
     return o;  // expected-note {{returned here}}
   }
   return *s.begin();  // expected-warning {{address of stack memory is returned later}}
@@ -1339,7 +1339,7 @@ void range_based_for_not_reference() {
     MyObjStorage s;
     for (MyObj o : s) { // expected-note {{destroyed here}}
       v = o; // expected-warning {{object whose reference is captured does not live long enough}} \
-             // expected-note {{variable 'v' aliases the storage of variable 'o'}}
+             // expected-note {{variable 'v' aliases the storage of 'o'}}
     }
   }
   v.use();  // expected-note {{later used here}}
@@ -1373,8 +1373,8 @@ void test_user_defined_deref_uaf() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = &(*smart_ptr);  // expected-warning {{object whose reference is captured does not live long enough}} \
-                        // expected-note {{expression aliases the storage of variable 'smart_ptr'}} \
-                        // expected-note {{variable 'p' aliases the storage of variable 'smart_ptr'}}
+                        // expected-note {{expression aliases the storage of 'smart_ptr'}} \
+                        // expected-note {{variable 'p' aliases the storage of 'smart_ptr'}}
   }                     // expected-note {{destroyed here}}
   (void)*p;             // expected-note {{later used here}}
 }
@@ -1392,8 +1392,8 @@ void test_user_defined_deref_with_view() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     v = *smart_ptr;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{expression aliases the storage of variable 'smart_ptr'}} \
-                     // expected-note {{variable 'v' aliases the storage of variable 'smart_ptr'}}
+                     // expected-note {{expression aliases the storage of 'smart_ptr'}} \
+                     // expected-note {{variable 'v' aliases the storage of 'smart_ptr'}}
   }                  // expected-note {{destroyed here}}
   v.use();           // expected-note {{later used here}}
 }
@@ -1404,8 +1404,8 @@ void test_user_defined_deref_arrow() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = smart_ptr.operator->();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                                 // expected-note {{function call result aliases the storage of variable 'smart_ptr'}} \
-                                 // expected-note {{variable 'p' aliases the storage of variable 'smart_ptr'}}
+                                 // expected-note {{function call result aliases the storage of 'smart_ptr'}} \
+                                 // expected-note {{variable 'p' aliases the storage of 'smart_ptr'}}
   }                              // expected-note {{destroyed here}}
   (void)*p;                      // expected-note {{later used here}}
 }
@@ -1416,8 +1416,8 @@ void test_user_defined_deref_chained() {
     MyObj obj;
     SmartPtr<SmartPtr<MyObj>> double_ptr;
     p = &(**double_ptr);  // expected-warning {{object whose reference is captured does not live long enough}} \
-                          // expected-note {{expression aliases the storage of variable 'double_ptr'}} \
-                          // expected-note {{variable 'p' aliases the storage of variable 'double_ptr'}}
+                          // expected-note {{expression aliases the storage of 'double_ptr'}} \
+                          // expected-note {{variable 'p' aliases the storage of 'double_ptr'}}
   }                       // expected-note {{destroyed here}}
   (void)*p;               // expected-note {{later used here}}
 }
@@ -1467,17 +1467,17 @@ MyObj* call_max_with_obj_error() {
 const MyObj* call_max_with_ref_obj_error() {
   MyObj oa, ob;
   const MyObj& refa = oa;     // expected-warning {{address of stack memory is returned later}} \
-                              // expected-note {{variable 'refa' aliases the storage of variable 'oa'}}
+                              // expected-note {{variable 'refa' aliases the storage of 'oa'}}
   const MyObj& refb = ob;     // expected-warning {{address of stack memory is returned later}} \
-                              // expected-note {{variable 'refb' aliases the storage of variable 'ob'}}
+                              // expected-note {{variable 'refb' aliases the storage of 'ob'}}
   return  &MaxT(refa, refb);  // expected-note 2 {{returned here}}
 }
 const MyObj& call_max_with_ref_obj_return_ref_error() {
   MyObj oa, ob;
   const MyObj& refa = oa;     // expected-warning {{address of stack memory is returned later}} \
-                              // expected-note {{variable 'refa' aliases the storage of variable 'oa'}}
+                              // expected-note {{variable 'refa' aliases the storage of 'oa'}}
   const MyObj& refb = ob;     // expected-warning {{address of stack memory is returned later}} \
-                              // expected-note {{variable 'refb' aliases the storage of variable 'ob'}}
+                              // expected-note {{variable 'refb' aliases the storage of 'ob'}}
   return  MaxT(refa, refb);   // expected-note 2 {{returned here}}
 }
 
@@ -1510,45 +1510,45 @@ namespace MultiPointerTypes {
 int** return_2p() {
   int a = 1;
   int* b = &a;  // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+                // expected-note {{variable 'b' aliases the storage of 'a'}}
   int** c = &b; // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
-                // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+                // expected-note {{variable 'c' aliases the storage of 'a'}} \
+                // expected-note {{variable 'c' aliases the storage of 'b'}}
   return c;     // expected-note 2 {{returned here}}
 }
 
 int** return_2p_one_is_safe(int& a) {
   int* b = &a;
   int** c = &b; // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+                // expected-note {{variable 'c' aliases the storage of 'b'}}
   return c;     // expected-note {{returned here}}
 }
 
 int*** return_3p() {
   int a = 1;
   int* b = &a;    // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+                  // expected-note {{variable 'b' aliases the storage of 'a'}}
   int** c = &b;   // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
-                  // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+                  // expected-note {{variable 'c' aliases the storage of 'a'}} \
+                  // expected-note {{variable 'c' aliases the storage of 'b'}}
   int*** d = &c;  // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'd' aliases the storage of variable 'a'}} \
-                  // expected-note {{variable 'd' aliases the storage of variable 'b'}} \
-                  // expected-note {{variable 'd' aliases the storage of variable 'c'}}
+                  // expected-note {{variable 'd' aliases the storage of 'a'}} \
+                  // expected-note {{variable 'd' aliases the storage of 'b'}} \
+                  // expected-note {{variable 'd' aliases the storage of 'c'}}
   return d;       // expected-note 3 {{returned here}}
 }
 
 View** return_view_p() {
   MyObj a;
   View b = a;     // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+                  // expected-note {{variable 'b' aliases the storage of 'a'}}
   View* c = &b;   // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'c' aliases the storage of variable 'a'}} \
-                  // expected-note {{variable 'c' aliases the storage of variable 'b'}}
+                  // expected-note {{variable 'c' aliases the storage of 'a'}} \
+                  // expected-note {{variable 'c' aliases the storage of 'b'}}
   View** d = &c;  // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'd' aliases the storage of variable 'a'}} \
-                  // expected-note {{variable 'd' aliases the storage of variable 'b'}} \
-                  // expected-note {{variable 'd' aliases the storage of variable 'c'}}
+                  // expected-note {{variable 'd' aliases the storage of 'a'}} \
+                  // expected-note {{variable 'd' aliases the storage of 'b'}} \
+                  // expected-note {{variable 'd' aliases the storage of 'c'}}
   return d;       // expected-note 3 {{returned here}}
 }
 
@@ -1589,7 +1589,7 @@ void strict_warn_on_move() {
   {
     MyObj a;
     v = a;            // expected-warning-re {{object whose reference {{.*}} may have been moved}} \
-                      // expected-note {{variable 'v' aliases the storage of variable 'a'}}
+                      // expected-note {{variable 'v' aliases the storage of 'a'}}
     b = std::move(a); // expected-note {{potentially moved here}}
   }                   // expected-note {{destroyed here}}
   (void)v;            // expected-note {{later used here}}
@@ -1603,7 +1603,7 @@ void flow_sensitive(bool c) {
       MyObj b = std::move(a);
       return;
     }
-    v = a;  // expected-warning {{object whose reference}} expected-note {{variable 'v' aliases the storage of variable 'a'}}
+    v = a;  // expected-warning {{object whose reference}} expected-note {{variable 'v' aliases the storage of 'a'}}
   }         // expected-note {{destroyed here}}
   (void)v;  // expected-note {{later used here}}
 }
@@ -1614,8 +1614,8 @@ void detect_conditional(bool cond) {
   {
     MyObj a, b;
     v = cond ? a : b; // expected-warning-re 2 {{object whose reference {{.*}} may have been moved}} \
-                      // expected-note {{variable 'v' aliases the storage of variable 'b'}} \
-                      // expected-note {{variable 'v' aliases the storage of variable 'a'}}
+                      // expected-note {{variable 'v' aliases the storage of 'b'}} \
+                      // expected-note {{variable 'v' aliases the storage of 'a'}}
     take(std::move(cond ? a : b)); // expected-note 2 {{potentially moved here}}
   }         // expected-note 2 {{destroyed here}}
   (void)v;  // expected-note 2 {{later used here}}
@@ -1626,16 +1626,16 @@ void wrong_use_of_move_is_permissive() {
   {
     MyObj a;
     v = std::move(a); // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{function call result aliases the storage of variable 'a'}} \
-                      // expected-note {{variable 'v' aliases the storage of variable 'a'}}
+                      // expected-note {{function call result aliases the storage of 'a'}} \
+                      // expected-note {{variable 'v' aliases the storage of 'a'}}
   }         // expected-note {{destroyed here}}
   (void)v;  // expected-note {{later used here}}
   const int* p;
   {
     MyObj a;
     p = std::move(a).getData(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                                // expected-note 2 {{function call result aliases the storage of variable 'a'}} \
-                                // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                                // expected-note 2 {{function call result aliases the storage of 'a'}} \
+                                // expected-note {{variable 'p' aliases the storage of 'a'}}
   }         // expected-note {{destroyed here}}
   (void)p;  // expected-note {{later used here}}
 }
@@ -1647,8 +1647,8 @@ void test_release_no_uaf() {
   {
     std::unique_ptr<int> p;
     r = p.get();        // expected-warning-re {{object whose reference {{.*}} may have been moved}} \
-                        // expected-note {{function call result aliases the storage of variable 'p'}} \
-                        // expected-note {{variable 'r' aliases the storage of variable 'p'}}
+                        // expected-note {{function call result aliases the storage of 'p'}} \
+                        // expected-note {{variable 'r' aliases the storage of 'p'}}
     take(p.release());  // expected-note {{potentially moved here}}
   }                     // expected-note {{destroyed here}}
   (void)*r;             // expected-note {{later used here}}
@@ -1671,8 +1671,8 @@ void bar() {
     {
         S s;
         x = s.x();        // expected-warning {{object whose reference is captured does not live long enough}} \
-                          // expected-note {{function call result aliases the storage of variable 's'}} \
-                          // expected-note {{variable 'x' aliases the storage of variable 's'}}
+                          // expected-note {{function call result aliases the storage of 's'}} \
+                          // expected-note {{variable 'x' aliases the storage of 's'}}
         View y = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \
                           // expected-note {{destroyed here}} \
                           // expected-note {{function call result aliases the storage of the temporary}} \
@@ -1686,29 +1686,29 @@ void bar() {
 namespace DereferenceViews {
 const MyObj& testDeref(MyObj obj) {
   View v = obj; // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                // expected-note {{variable 'v' aliases the storage of 'obj'}}
   return *v;    // expected-note {{returned here}}
 }
 const MyObj* testDerefAddr(MyObj obj) {
   View v = obj; // expected-warning {{address of stack memory is returned later}} \
-                // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                // expected-note {{variable 'v' aliases the storage of 'obj'}}
   return &*v;   // expected-note {{returned here}}
 }
 const MyObj* testData(MyObj obj) {
   View v = obj;     // expected-warning {{address of stack memory is returned later}} \
-                    // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                    // expected-note {{variable 'v' aliases the storage of 'obj'}}
   return v.data();  // expected-note {{returned here}}
 }
 const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
   View v = obj;           // expected-warning {{address of stack memory is returned later}} \
-                          // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
-  const MyObj* ptr = v.data(); // expected-note {{function call result aliases the storage of variable 'obj'}} \
-                               // expected-note {{variable 'ptr' aliases the storage of variable 'obj'}}
+                          // expected-note {{variable 'v' aliases the storage of 'obj'}}
+  const MyObj* ptr = v.data(); // expected-note {{function call result aliases the storage of 'obj'}} \
+                               // expected-note {{variable 'ptr' aliases the storage of 'obj'}}
   return ptr->getData();  // expected-note {{returned here}}
 }
 const int* testLifetimeboundAccessorOfMyObjThroughDeref(MyObj obj) {
   View v = obj;         // expected-warning {{address of stack memory is returned later}} \
-                        // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
+                        // expected-note {{variable 'v' aliases the storage of 'obj'}}
   return v->getData();  // expected-note {{returned here}}
 }
 } // namespace DereferenceViews
@@ -1733,8 +1733,8 @@ MyObj Global;
 
 const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
   for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}} \
-                              // expected-note {{function call result aliases the storage of variable 'c'}} \
-                              // expected-note {{variable '__begin1' aliases the storage of variable 'c'}}
+                              // expected-note {{function call result aliases the storage of 'c'}} \
+                              // expected-note {{variable '__begin1' aliases the storage of 'c'}}
     return x;                 // expected-note {{returned here}}
   }
   return Global;
@@ -1742,13 +1742,13 @@ const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
 
 View ContainerMyObjReturnView(Container<MyObj> c) {
   for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}} \
-                              // expected-note {{function call result aliases the storage of variable 'c'}} \
-                              // expected-note {{variable '__begin1' aliases the storage of variable 'c'}}
+                              // expected-note {{function call result aliases the storage of 'c'}} \
+                              // expected-note {{variable '__begin1' aliases the storage of 'c'}}
     return x;                 // expected-note {{returned here}}
   }
   for (View x : c) {  // expected-warning {{address of stack memory is returned later}} \
-                      // expected-note {{function call result aliases the storage of variable 'c'}} \
-                      // expected-note {{variable '__begin1' aliases the storage of variable 'c'}}
+                      // expected-note {{function call result aliases the storage of 'c'}} \
+                      // expected-note {{variable '__begin1' aliases the storage of 'c'}}
     return x;         // expected-note {{returned here}}
   }
   return Global;
@@ -1792,9 +1792,9 @@ void test_temporary() {
   {
     S s;
     const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                                   // expected-note {{function call result aliases the storage of variable 's'}} \
-                                   // expected-note {{variable 'zz' aliases the storage of variable 's'}}
-    z = zz;                        // expected-note {{variable 'z' aliases the storage of variable 's'}}
+                                   // expected-note {{function call result aliases the storage of 's'}} \
+                                   // expected-note {{variable 'zz' aliases the storage of 's'}}
+    z = zz;                        // expected-note {{variable 'z' aliases the storage of 's'}}
   } // expected-note {{destroyed here}}
   (void)z; // expected-note {{later used here}}
 }
@@ -1830,8 +1830,8 @@ void uaf() {
   {
     S str;
     S* p = &str;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 'str'}}
-    view = p->s;  // expected-note {{variable 'view' aliases the storage of variable 'str'}}
+                  // expected-note {{variable 'p' aliases the storage of 'str'}}
+    view = p->s;  // expected-note {{variable 'view' aliases the storage of 'str'}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1857,8 +1857,8 @@ void uaf_union() {
   {
     U u = U{"hello"};
     U* up = &u;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable 'up' aliases the storage of variable 'u'}}
-    view = up->s; // expected-note {{variable 'view' aliases the storage of variable 'u'}}
+                 // expected-note {{variable 'up' aliases the storage of 'u'}}
+    view = up->s; // expected-note {{variable 'view' aliases the storage of 'u'}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1875,8 +1875,8 @@ void uaf_anonymous_union() {
   {
     AnonymousUnion au;
     AnonymousUnion* up = &au;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                               // expected-note {{variable 'up' aliases the storage of variable 'au'}}
-    ip = &up->x; // expected-note {{variable 'ip' aliases the storage of variable 'au'}}
+                               // expected-note {{variable 'up' aliases the storage of 'au'}}
+    ip = &up->x; // expected-note {{variable 'ip' aliases the storage of 'au'}}
   } // expected-note {{destroyed here}}
   (void)ip;  // expected-note {{later used here}}
 }
@@ -1966,13 +1966,13 @@ View test1(std::string a) {
 
 View test2(std::string a) {
   View b = View(a); // expected-warning {{address of stack memory is returned later}} \
-                    // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+                    // expected-note {{variable 'b' aliases the storage of 'a'}}
   return b;         // expected-note {{returned here}}
 }
 
 View test3(std::string a) {
   const View& b = View(a);  // expected-warning {{address of stack memory is returned later}} \
-                            // expected-note {{variable 'b' aliases the storage of variable 'a'}}
+                            // expected-note {{variable 'b' aliases the storage of 'a'}}
   return b;                 // expected-note {{returned here}}
 }
 } // namespace non_trivial_views
@@ -1983,9 +1983,9 @@ void test_optional_arrow() {
   {
     std::optional<std::string> opt;
     p = opt->data();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                      // expected-note {{expression aliases the storage of variable 'opt'}} \
-                      // expected-note {{function call result aliases the storage of variable 'opt'}} \
-                      // expected-note {{variable 'p' aliases the storage of variable 'opt'}}
+                      // expected-note {{expression aliases the storage of 'opt'}} \
+                      // expected-note {{function call result aliases the storage of 'opt'}} \
+                      // expected-note {{variable 'p' aliases the storage of 'opt'}}
   }                   // expected-note {{destroyed here}}
   (void)*p;           // expected-note {{later used here}}
 }
@@ -1995,9 +1995,9 @@ void test_optional_arrow_lifetimebound() {
   {
     std::optional<MyObj> opt;
     v = opt->getView();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                         // expected-note {{expression aliases the storage of variable 'opt'}} \
-                         // expected-note {{function call result aliases the storage of variable 'opt'}} \
-                         // expected-note {{variable 'v' aliases the storage of variable 'opt'}}
+                         // expected-note {{expression aliases the storage of 'opt'}} \
+                         // expected-note {{function call result aliases the storage of 'opt'}} \
+                         // expected-note {{variable 'v' aliases the storage of 'opt'}}
   }                      // expected-note {{destroyed here}}
   v.use();               // expected-note {{later used here}}
 }
@@ -2007,9 +2007,9 @@ void test_unique_ptr_arrow() {
   {
     std::unique_ptr<std::string> up;
     p = up->data();  // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{expression aliases the storage of variable 'up'}} \
-                     // expected-note {{function call result aliases the storage of variable 'up'}} \
-                     // expected-note {{variable 'p' aliases the storage of variable 'up'}}
+                     // expected-note {{expression aliases the storage of 'up'}} \
+                     // expected-note {{function call result aliases the storage of 'up'}} \
+                     // expected-note {{variable 'p' aliases the storage of 'up'}}
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 }
@@ -2028,7 +2028,7 @@ namespace lambda_captures {
 auto return_ref_capture() {
   int local = 1;
   auto lambda = [&local]() { return local; }; // expected-warning {{address of stack memory is returned later}} \
-                                              // expected-note {{variable 'lambda' aliases the storage of variable 'local'}}
+                                              // expected-note {{variable 'lambda' aliases the storage of 'local'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2047,8 +2047,8 @@ auto capture_int_by_value() {
 auto capture_view_by_value() {
   MyObj obj;
   View v(obj); // expected-warning {{address of stack memory is returned later}} \
-               // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
-  auto lambda = [v]() { return v; }; // expected-note {{variable 'lambda' aliases the storage of variable 'obj'}}
+               // expected-note {{variable 'v' aliases the storage of 'obj'}}
+  auto lambda = [v]() { return v; }; // expected-note {{variable 'lambda' aliases the storage of 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2063,14 +2063,14 @@ auto capture_pointer_by_ref() {
   MyObj obj;
   MyObj* p = &obj;
   auto lambda = [&p]() { return p; }; // expected-warning {{address of stack memory is returned later}} \\
-                                      // expected-note {{variable 'lambda' aliases the storage of variable 'p'}}
+                                      // expected-note {{variable 'lambda' aliases the storage of 'p'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_multiple() {
   int a, b;
-  auto lambda = [ // expected-note {{variable 'lambda' aliases the storage of variable 'b'}} \
-                  // expected-note {{variable 'lambda' aliases the storage of variable 'a'}}
+  auto lambda = [ // expected-note {{variable 'lambda' aliases the storage of 'b'}} \
+                  // expected-note {{variable 'lambda' aliases the storage of 'a'}}
     &a,  // expected-warning {{address of stack memory is returned later}}
     &b   // expected-warning {{address of stack memory is returned later}}
   ]() { return a + b; };
@@ -2080,47 +2080,47 @@ auto capture_multiple() {
 auto capture_raw_pointer_by_value() {
   int x;
   int* p = &x; // expected-warning {{address of stack memory is returned later}} \
-               // expected-note {{variable 'p' aliases the storage of variable 'x'}}
-  auto lambda = [p]() { return p; }; // expected-note {{variable 'lambda' aliases the storage of variable 'x'}}
+               // expected-note {{variable 'p' aliases the storage of 'x'}}
+  auto lambda = [p]() { return p; }; // expected-note {{variable 'lambda' aliases the storage of 'x'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_raw_pointer_init_capture() {
   int x;
   int* p = &x; // expected-warning {{address of stack memory is returned later}} \
-               // expected-note {{variable 'p' aliases the storage of variable 'x'}}
-  auto lambda = [q = p]() { return q; }; // expected-note {{variable 'lambda' aliases the storage of variable 'x'}}
+               // expected-note {{variable 'p' aliases the storage of 'x'}}
+  auto lambda = [q = p]() { return q; }; // expected-note {{variable 'lambda' aliases the storage of 'x'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_view_init_capture() {
   MyObj obj;
   View v(obj); // expected-warning {{address of stack memory is returned later}} \
-               // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
-  auto lambda = [w = v]() { return w; }; // expected-note {{variable 'lambda' aliases the storage of variable 'obj'}}
+               // expected-note {{variable 'v' aliases the storage of 'obj'}}
+  auto lambda = [w = v]() { return w; }; // expected-note {{variable 'lambda' aliases the storage of 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_lambda() {
   int x;
   auto inner = [&x]() { return x; }; // expected-warning {{address of stack memory is returned later}} \
-                                     // expected-note {{variable 'inner' aliases the storage of variable 'x'}}
-  auto outer = [inner]() { return inner(); }; // expected-note {{variable 'outer' aliases the storage of variable 'x'}}
+                                     // expected-note {{variable 'inner' aliases the storage of 'x'}}
+  auto outer = [inner]() { return inner(); }; // expected-note {{variable 'outer' aliases the storage of 'x'}}
   return outer; // expected-note {{returned here}}
 }
 
 auto return_copied_lambda() {
   int local = 1;
   auto lambda = [&local]() { return local; }; // expected-warning {{address of stack memory is returned later}} \
-                                              // expected-note {{variable 'lambda' aliases the storage of variable 'local'}}
-  auto lambda_copy = lambda;                  // expected-note {{variable 'lambda_copy' aliases the storage of variable 'local'}}
+                                              // expected-note {{variable 'lambda' aliases the storage of 'local'}}
+  auto lambda_copy = lambda;                  // expected-note {{variable 'lambda_copy' aliases the storage of 'local'}}
   return lambda_copy; // expected-note {{returned here}}
 }
 
 auto implicit_ref_capture() {
   int local = 1;
   auto lambda = [&]() { return local; }; // expected-warning {{address of stack memory is returned later}} \
-                                         // expected-note {{variable 'lambda' aliases the storage of variable 'local'}}
+                                         // expected-note {{variable 'lambda' aliases the storage of 'local'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2130,16 +2130,16 @@ auto implicit_ref_capture() {
 auto implicit_ref_capture_multiple() {
   int local = 1, local2 = 2;
   auto lambda = [&]() { return local + local2; }; // expected-warning 2 {{address of stack memory is returned later}} \
-                                                  // expected-note {{variable 'lambda' aliases the storage of variable 'local'}} \
-                                                  // expected-note {{variable 'lambda' aliases the storage of variable 'local2'}}
+                                                  // expected-note {{variable 'lambda' aliases the storage of 'local'}} \
+                                                  // expected-note {{variable 'lambda' aliases the storage of 'local2'}}
   return lambda; // expected-note 2 {{returned here}}
 }
 
 auto implicit_value_capture() {
   MyObj obj;
   View v(obj); // expected-warning {{address of stack memory is returned later}} \
-               // expected-note {{variable 'v' aliases the storage of variable 'obj'}}
-  auto lambda = [=]() { return v; }; // expected-note {{variable 'lambda' aliases the storage of variable 'obj'}}
+               // expected-note {{variable 'v' aliases the storage of 'obj'}}
+  auto lambda = [=]() { return v; }; // expected-note {{variable 'lambda' aliases the storage of 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2171,21 +2171,21 @@ auto capture_static_address_by_ref() {
   static int local = 1;
   int* p = &local;
   auto lambda = [&p]() { return p; }; // expected-warning {{address of stack memory is returned later}} \
-                                      // expected-note {{variable 'lambda' aliases the storage of variable 'p'}}
+                                      // expected-note {{variable 'lambda' aliases the storage of 'p'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_multilevel_pointer() {
   int x;
   int *p = &x;   // expected-warning {{address of stack memory is returned later}} \
-                 // expected-note {{variable 'p' aliases the storage of variable 'x'}}
+                 // expected-note {{variable 'p' aliases the storage of 'x'}}
   int **q = &p;  // expected-warning {{address of stack memory is returned later}} \
-                 // expected-note {{variable 'q' aliases the storage of variable 'p'}}
+                 // expected-note {{variable 'q' aliases the storage of 'p'}}
   int ***r = &q; // expected-warning {{address of stack memory is returned later}} \
-                 // expected-note {{variable 'r' aliases the storage of variable 'q'}}
-  auto lambda = [=]() { return *p + **q + ***r; }; // expected-note {{variable 'lambda' aliases the storage of variable 'x'}} \
-                                                   // expected-note {{variable 'lambda' aliases the storage of variable 'q'}} \
-                                                   // expected-note {{variable 'lambda' aliases the storage of variable 'p'}}
+                 // expected-note {{variable 'r' aliases the storage of 'q'}}
+  auto lambda = [=]() { return *p + **q + ***r; }; // expected-note {{variable 'lambda' aliases the storage of 'x'}} \
+                                                   // expected-note {{variable 'lambda' aliases the storage of 'q'}} \
+                                                   // expected-note {{variable 'lambda' aliases the storage of 'p'}}
   return lambda; // expected-note 3 {{returned here}}
 }
 } // namespace lambda_captures
@@ -2220,8 +2220,8 @@ void multi_level_pointer_in_loop() {
     MyObj** pp;
     if (i > 5) {
       p = &obj; // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'obj'}}
-      pp = &p;  // expected-note {{variable 'pp' aliases the storage of variable 'obj'}}
+                // expected-note {{variable 'p' aliases the storage of 'obj'}}
+      pp = &p;  // expected-note {{variable 'pp' aliases the storage of 'obj'}}
     }
     (void)**pp; // expected-note {{later used here}}
   }             // expected-note {{destroyed here}}
@@ -2233,7 +2233,7 @@ void outer_pointer_outlives_inner_pointee() {
   for (int i = 0; i < 10; ++i) {
     MyObj obj;
     view = &obj;     // expected-warning {{object whose reference is captured does not live long enough}} \
-                     // expected-note {{variable 'view' aliases the storage of variable 'obj'}}
+                     // expected-note {{variable 'view' aliases the storage of 'obj'}}
   }                  // expected-note {{destroyed here}}
   (void)*view;       // expected-note {{later used here}}
 }
@@ -2247,7 +2247,7 @@ void element_use_after_scope() {
   {
     int a[10]{};
     p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} \
-               // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+               // expected-note {{variable 'p' aliases the storage of 'a'}}
   }            // expected-note {{destroyed here}}
   (void)*p;    // expected-note {{later used here}}
 }
@@ -2255,7 +2255,7 @@ void element_use_after_scope() {
 int* element_use_after_return() {
   int a[10]{};
   int* p = &a[0]; // expected-warning {{address of stack memory is returned later}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                  // expected-note {{variable 'p' aliases the storage of 'a'}}
   return p;       // expected-note {{returned here}}
 }
 
@@ -2281,7 +2281,7 @@ void multidimensional_use_after_scope() {
   {
     int a[3][4]{};
     p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} \
-                  // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                  // expected-note {{variable 'p' aliases the storage of 'a'}}
   }               // expected-note {{destroyed here}}
   (void)*p;       // expected-note {{later used here}}
 }
@@ -2295,7 +2295,7 @@ void member_array_element_use_after_scope() {
   {
     S s;
     p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} \
-                   // expected-note {{variable 'p' aliases the storage of variable 's'}}
+                   // expected-note {{variable 'p' aliases the storage of 's'}}
   }                // expected-note {{destroyed here}}
   (void)*p;        // expected-note {{later used here}}
 }
@@ -2305,7 +2305,7 @@ void array_of_pointers_use_after_scope() {
   {
     int* a[10]{};
     p = a;  // expected-warning {{object whose reference is captured does not live long enough}} \
-            // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+            // expected-note {{variable 'p' aliases the storage of 'a'}}
   }         // expected-note {{destroyed here}}
   (void)*p; // expected-note {{later used here}}
 }
@@ -2315,7 +2315,7 @@ void reversed_subscript_use_after_scope() {
   {
     int a[10]{};
     p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} \
-                 // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                 // expected-note {{variable 'p' aliases the storage of 'a'}}
   }              // expected-note {{destroyed here}}
   (void)*p;      // expected-note {{later used here}}
 }
@@ -2323,7 +2323,7 @@ void reversed_subscript_use_after_scope() {
 int* return_decayed_array() {
   int a[10]{};
   int *p = a; // expected-warning {{address of stack memory is returned later}} \
-              // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+              // expected-note {{variable 'p' aliases the storage of 'a'}}
   return p;   // expected-note {{returned here}}
 }
 
@@ -2343,11 +2343,11 @@ void pointer_arithmetic_use_after_scope() {
   {
     int a[10]{};
     p = a + 5;  // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p' aliases the storage of variable 'a'}}
+                // expected-note {{variable 'p' aliases the storage of 'a'}}
     p2 = a - 5; // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p2' aliases the storage of variable 'a'}}
+                // expected-note {{variable 'p2' aliases the storage of 'a'}}
     p3 = 5 + a; // expected-warning {{object whose reference is captured does not live long enough}} \
-                // expected-note {{variable 'p3' aliases the storage of variable 'a'}}
+                // expected-note {{variable 'p3' aliases the storage of 'a'}}
   }             // expected-note 3 {{destroyed here}}
   (void)*p;     // expected-note {{later used here}}
   (void)*p2;    // expected-note {{later used here}}
@@ -2459,8 +2459,8 @@ void from_lifetimebound_this_method() {
   {
     Factory f;
     value = f.makeThis(); // expected-warning {{object whose reference is captured does not live long enough}} \
-                          // expected-note {{function call result aliases the storage of variable 'f'}} \
-                          // expected-note {{variable 'value' aliases the storage of variable 'f'}}
+                          // expected-note {{function call result aliases the storage of 'f'}} \
+                          // expected-note {{variable 'value' aliases the storage of 'f'}}
   }                       // expected-note {{destroyed here}}
   use(value);             // expected-note {{later used here}}
 }
@@ -2470,8 +2470,8 @@ void across_scope() {
   {
     std::string str{"abc"};
     s = getS(str); // expected-warning {{object whose reference is captured does not live long enough}} \
-                   // expected-note {{function call result aliases the storage of variable 'str'}} \
-                   // expected-note {{variable 's' aliases the storage of variable 'str'}}
+                   // expected-note {{function call result aliases the storage of 'str'}} \
+                   // expected-note {{variable 's' aliases the storage of 'str'}}
   }                // expected-note {{destroyed here}}
   use(s);          // expected-note {{later used here}}
 }
@@ -2485,9 +2485,9 @@ void same_scope() {
 S copy_propagation() {
   std::string str{"abc"};
   S a = getS(str); // expected-warning {{address of stack memory is returned later}} \
-                   // expected-note {{function call result aliases the storage of variable 'str'}} \
-                   // expected-note {{variable 'a' aliases the storage of variable 'str'}}
-  S b = a;         // expected-note {{variable 'b' aliases the storage of variable 'str'}}
+                   // expected-note {{function call result aliases the storage of 'str'}} \
+                   // expected-note {{variable 'a' aliases the storage of 'str'}}
+  S b = a;         // expected-note {{variable 'b' aliases the storage of 'str'}}
   return b; // expected-note {{returned here}}
 }
 
@@ -2496,9 +2496,9 @@ void assignment_propagation() {
   {
     std::string str{"abc"};
     a = getS(str); // expected-warning {{object whose reference is captured does not live long enough}} \
-                   // expected-note {{function call result aliases the storage of variable 'str'}} \
-                   // expected-note {{variable 'a' aliases the storage of variable 'str'}}
-    b = a;         // expected-note {{variable 'b' aliases the storage of variable 'str'}}
+                   // expected-note {{function call result aliases the storage of 'str'}} \
+                   // expected-note {{variable 'a' aliases the storage of 'str'}}
+    b = a;         // expected-note {{variable 'b' aliases the storage of 'str'}}
   }                // expected-note {{destroyed here}}
   use(b);          // expected-note {{later used here}}
 }
@@ -2528,8 +2528,8 @@ S multiple_lifetimebound_params() {
                                          // expected-warning {{object whose reference is captured does not live long enough}} \
                                          // expected-note {{function call result aliases the storage of the temporary}} \
                                          // expected-note {{variable 's' aliases the storage of the temporary}} \
-                                         // expected-note {{function call result aliases the storage of variable 'str'}} \
-                                         // expected-note {{variable 's' aliases the storage of variable 'str'}} \
+                                         // expected-note {{function call result aliases the storage of 'str'}} \
+                                         // expected-note {{variable 's' aliases the storage of 'str'}} \
                                          // expected-note {{destroyed here}}
   return s;                              // expected-note {{returned here}} \
                                          // expected-note {{later used here}}
@@ -2651,9 +2651,9 @@ DefaultedOuter getDefaultedOuter(const std::string &s [[clang::lifetimebound]]);
 DefaultedOuter nested_defaulted_outer_with_user_defined_inner() {
   std::string str{"abc"};
   DefaultedOuter o = getDefaultedOuter(str); // expected-warning {{address of stack memory is returned later}} \
-                                             // expected-note {{function call result aliases the storage of variable 'str'}} \
-                                             // expected-note {{variable 'o' aliases the storage of variable 'str'}}
-  DefaultedOuter copy = o;                   // expected-note {{variable 'copy' aliases the storage of variable 'str'}}
+                                             // expected-note {{function call result aliases the storage of 'str'}} \
+                                             // expected-note {{variable 'o' aliases the storage of 'str'}}
+  DefaultedOuter copy = o;                   // expected-note {{variable 'copy' aliases the storage of 'str'}}
   return copy; // expected-note {{returned here}}
 }
 
@@ -2702,10 +2702,10 @@ std::string_view return_dangling_view_through_owner() {
   std::string local;
   auto ups = getUniqueS(local);
   S* s = ups.get(); // expected-warning {{address of stack memory is returned later}} \
-                    // expected-note {{function call result aliases the storage of variable 'ups'}} \
-                    // expected-note {{variable 's' aliases the storage of variable 'ups'}}
-  std::string_view sv = s->getData(); // expected-note {{function call result aliases the storage of variable 'ups'}} \
-                                      // expected-note {{variable 'sv' aliases the storage of variable 'ups'}}
+                    // expected-note {{function call result aliases the storage of 'ups'}} \
+                    // expected-note {{variable 's' aliases the storage of 'ups'}}
+  std::string_view sv = s->getData(); // expected-note {{function call result aliases the storage of 'ups'}} \
+                                      // expected-note {{variable 'sv' aliases the storage of 'ups'}}
   return sv; // expected-note {{returned here}}
 }
 

>From 4eacf2d3e92b4df0371a88c1b14e2387ee9cebbc Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 3 Apr 2026 14:32:46 +0800
Subject: [PATCH 13/22] fix clang-format error

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index f685072fb58bf..b87e52971dd6b 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -229,9 +229,7 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
   return std::nullopt;
 }
 
-
-llvm::SmallString<32>
-FormatRHSValueDeclForSema(const ValueDecl *TargetValue) {
+llvm::SmallString<32> FormatRHSValueDeclForSema(const ValueDecl *TargetValue) {
   llvm::SmallString<32> Result;
   if (TargetValue) {
     Result += "'";

>From ff787df4233e36db0361eb5078ba38c24b11f606 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Thu, 9 Apr 2026 20:21:51 +0800
Subject: [PATCH 14/22] fix for code style

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  16 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  45 ++--
 .../LifetimeSafety/AssignmentQuery.cpp        | 234 ++++++++----------
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  29 +--
 clang/lib/Sema/SemaLifetimeSafety.h           | 101 ++++----
 5 files changed, 197 insertions(+), 228 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 3af1908662140..2b763fea8d62b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -33,12 +33,14 @@ using LoanEntity = llvm::PointerUnion<const Expr *, const ParmVarDecl *,
 using AssignmentPair = std::pair<OriginDestExpr, const Expr *>;
 
 struct ExprPrintingResult {
-  llvm::SmallString<32> Value;
+  llvm::StringRef Str;
   const Expr *CurrExpr;
 };
 
-llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity);
-llvm::SmallVector<ExprPrintingResult> FormatSrcExprForSema(const Expr *SrcExpr);
+void FormatLoanEntityForSema(LoanEntity IssueEntity,
+                             llvm::SmallVectorImpl<char> &IssueMsg);
+void FormatSrcExprForSema(
+    const Expr *SrcExpr, llvm::SmallVectorImpl<ExprPrintingResult> &SrcMsgList);
 } // namespace clang::lifetimes
 
 namespace clang::lifetimes::internal {
@@ -62,10 +64,10 @@ struct AssignmentQueryContext {
 ///
 /// To help users understand the data flow, we track where the problematic
 /// address originated.
-std::optional<llvm::SmallVector<AssignmentPair>>
-getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
-             const LoanID End, const CFGBlock *StartBlock,
-             const Expr *IssueExpr);
+void getAliasList(const AssignmentQueryContext &Context,
+                  llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
+                  const Fact *CausingFact, const LoanID End,
+                  const CFGBlock *StartBlock, const Expr *IssueExpr);
 } // namespace clang::lifetimes::internal
 
 #endif
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index a69ee3e8826dc..4310ada231439 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -62,26 +62,28 @@ class LifetimeSafetySemaHelper {
   LifetimeSafetySemaHelper() = default;
   virtual ~LifetimeSafetySemaHelper() = default;
 
-  virtual void reportUseAfterFree(
-      const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation FreeLoc) {}
-
-  virtual void reportUseAfterReturn(
-      const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation ExpiryLoc) {}
-
-  virtual void reportDanglingField(
-      const Expr *IssueExpr, const FieldDecl *Field, const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation ExpiryLoc) {}
-
-  virtual void reportDanglingGlobal(
-      const Expr *IssueExpr, const VarDecl *DanglingGlobal,
-      const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation ExpiryLoc) {}
+  virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
+                                  const Expr *MovedExpr,
+                                  llvm::ArrayRef<AssignmentPair> AliasList,
+                                  SourceLocation FreeLoc) {}
+
+  virtual void reportUseAfterReturn(const Expr *IssueExpr,
+                                    const Expr *ReturnExpr,
+                                    const Expr *MovedExpr,
+                                    llvm::ArrayRef<AssignmentPair> AliasList,
+                                    SourceLocation ExpiryLoc) {}
+
+  virtual void reportDanglingField(const Expr *IssueExpr,
+                                   const FieldDecl *Field,
+                                   const Expr *MovedExpr,
+                                   llvm::ArrayRef<AssignmentPair> AliasList,
+                                   SourceLocation ExpiryLoc) {}
+
+  virtual void reportDanglingGlobal(const Expr *IssueExpr,
+                                    const VarDecl *DanglingGlobal,
+                                    const Expr *MovedExpr,
+                                    llvm::ArrayRef<AssignmentPair> AliasList,
+                                    SourceLocation ExpiryLoc) {}
 
   // Reports when a reference/iterator is used after the container operation
   // that invalidated it.
@@ -114,8 +116,7 @@ class LifetimeSafetySemaHelper {
   // Suggests lifetime bound annotations for implicit this.
   virtual void suggestLifetimeboundToImplicitThis(
       SuggestionScope Scope, const CXXMethodDecl *MD,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      const Expr *EscapeExpr) {}
+      llvm::ArrayRef<AssignmentPair> AliasList, const Expr *EscapeExpr) {}
 
   // Adds inferred lifetime bound attribute for implicit this to its
   // TypeSourceInfo.
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index b87e52971dd6b..8e37c7dfd823f 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -77,31 +77,28 @@ const MemberExpr *getFieldFromAssignmentExpr(const Expr *RHS,
 AliasAssignmentSearchResult getAliasListCore(
     const AssignmentQueryContext &Context, const CFGBlock *Block,
     const LoanID EndLoanID, OriginID *TargetOID,
-    const std::optional<OriginDestExpr> LastDestDecl = std::nullopt,
+    const std::optional<OriginDestExpr> LastDestExpr = std::nullopt,
     const std::optional<OriginID> LastOriginID = std::nullopt) {
+  llvm::SmallVector<AssignmentPair> AliasStmts;
+  llvm::ArrayRef<const Fact *> Facts = Context.FactMgr.getFacts(Block);
+  std::optional<OriginID> IssueOriginID = LastOriginID;
+  std::optional<OriginDestExpr> DestExpr = LastDestExpr;
   std::optional<OriginID> CurrOrigin = std::nullopt;
-  std::optional<OriginDestExpr> DestDecl = LastDestDecl;
   std::optional<const Expr *> SrcExpr = std::nullopt;
-  llvm::SmallVector<AssignmentPair> AliasStmts;
-  const auto Facts = Context.FactMgr.getFacts(Block);
-  bool FetchLoan = false;
-  auto IssueOriginID = LastOriginID;
 
-  for (const auto &F : llvm::reverse(Facts)) {
+  for (const Fact *F : llvm::reverse(Facts)) {
     if (const auto *OFF = F->getAs<OriginFlowFact>()) {
-      if (IssueOriginID.has_value() &&
-          OFF->getDestOriginID() == IssueOriginID.value())
-        FetchLoan = true;
-
+      if (IssueOriginID && OFF->getDestOriginID() == IssueOriginID.value())
+        return {AliasStmts, true, DestExpr, IssueOriginID};
       if (OFF->getDestOriginID() == *TargetOID) {
-        const auto HeldLoans =
+        const LoanSet HeldLoans =
             Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
 
         if (HeldLoans.contains(EndLoanID)) {
-          const auto TargetOrigin =
+          const Origin TargetOrigin =
               Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
 
-          if (!DestDecl.has_value()) {
+          if (!DestExpr.has_value()) {
             if (const ValueDecl *DVecl = TargetOrigin.getDecl();
                 DVecl && !DVecl->getLocation().isInvalid()) {
               if (llvm::isa<FieldDecl>(DVecl)) {
@@ -109,25 +106,25 @@ AliasAssignmentSearchResult getAliasListCore(
                                            .getOrigin(OFF->getSrcOriginID())
                                            .getExpr();
                 if (CurrExpr)
-                  DestDecl = getFieldFromAssignmentExpr(
+                  DestExpr = getFieldFromAssignmentExpr(
                       CurrExpr, Context.ADC.getParentMap());
               } else {
                 CurrOrigin = *TargetOID;
-                DestDecl = DVecl;
+                DestExpr = DVecl;
               }
             }
           } else {
             auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
-            if (!SExpr.has_value()) {
+            if (!SExpr) {
               const auto SrcOrigin = Context.FactMgr.getOriginMgr().getOrigin(
                   OFF->getSrcOriginID());
               SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
             }
 
-            if (SExpr.has_value()) {
-              AliasStmts.push_back({DestDecl.value(), SExpr.value()});
+            if (SExpr) {
+              AliasStmts.push_back({DestExpr.value(), SExpr.value()});
               SrcExpr = SExpr.value();
-              DestDecl = std::nullopt;
+              DestExpr = std::nullopt;
               CurrOrigin = std::nullopt;
             }
           }
@@ -135,33 +132,27 @@ AliasAssignmentSearchResult getAliasListCore(
         }
       }
     } else if (const auto *IF = F->getAs<IssueFact>()) {
-      if (IF->getLoanID() == EndLoanID) {
+      if (IF->getLoanID() == EndLoanID)
         IssueOriginID = IF->getOriginID();
-      }
     } else if (const auto *UF = F->getAs<UseFact>()) {
       if (CurrOrigin.has_value()) {
         for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
              Cur = Cur->peelOuterOrigin()) {
-          if (Cur->getOuterOriginID() == CurrOrigin.value() &&
-              UF->isWritten()) {
-            const auto UExpr = GetPureSrcExpr(UF->getUseExpr());
-            if (UExpr.has_value()) {
-              if (const auto *UDExpr =
-                      llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
-                DestDecl = UDExpr;
-                break;
-              }
+          if (Cur->getOuterOriginID() != CurrOrigin.value() || !UF->isWritten())
+            continue;
+          std::optional<const Expr *> UExpr = GetPureSrcExpr(UF->getUseExpr());
+          if (UExpr.has_value()) {
+            if (const auto *UDExpr =
+                    llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
+              DestExpr = UDExpr;
+              break;
             }
           }
         }
       }
     }
-
-    if (FetchLoan) {
-      return {AliasStmts, true, DestDecl, IssueOriginID};
-    }
   }
-  return {AliasStmts, false, DestDecl, IssueOriginID};
+  return {AliasStmts, false, DestExpr, IssueOriginID};
 }
 
 std::optional<llvm::SmallVector<AssignmentPair>>
@@ -229,152 +220,125 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
   return std::nullopt;
 }
 
-llvm::SmallString<32> FormatRHSValueDeclForSema(const ValueDecl *TargetValue) {
-  llvm::SmallString<32> Result;
+void FormatRHSValueDeclForSema(const ValueDecl *TargetValue,
+                               llvm::SmallVectorImpl<char> &IssueMsg) {
   if (TargetValue) {
-    Result += "'";
-    Result += TargetValue->getName();
-    Result += "'";
+    const StringRef TargetName = TargetValue->getName();
+    IssueMsg.push_back('\'');
+    IssueMsg.append(TargetName.begin(), TargetName.end());
+    IssueMsg.push_back('\'');
   }
-  return Result;
 }
 } // namespace
 
 namespace clang::lifetimes {
 
-llvm::SmallString<32> FormatLoanEntityForSema(LoanEntity IssueEntity) {
-  if (!IssueEntity)
-    return {};
+void FormatLoanEntityForSema(LoanEntity IssueEntity,
+                             llvm::SmallVectorImpl<char> &IssueMsg) {
+  llvm::StringRef Temp = "the temporary";
 
   if (const auto *IssueExpr = llvm::dyn_cast<const Expr *>(IssueEntity)) {
-    const auto *PureExpr = IssueExpr->IgnoreParenCasts();
-    if (!PureExpr)
-      return {};
-
-    if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr))
-      return FormatRHSValueDeclForSema(IDeclExpr->getDecl());
-    return {"the temporary"};
+    if (const auto *IDeclExpr =
+            llvm::dyn_cast<DeclRefExpr>(IssueExpr->IgnoreParenCasts()))
+      FormatRHSValueDeclForSema(IDeclExpr->getDecl(), IssueMsg);
   }
+
   if (const auto *IssueParmDecl =
           llvm::dyn_cast<const ParmVarDecl *>(IssueEntity))
-    return FormatRHSValueDeclForSema(IssueParmDecl);
-  if (const auto *IssueCXXMD =
-          llvm::dyn_cast<const CXXMethodDecl *>(IssueEntity))
-    return FormatRHSValueDeclForSema(IssueCXXMD);
-
-  return {"the temporary"};
+    FormatRHSValueDeclForSema(IssueParmDecl, IssueMsg);
+  else if (const auto *IssueCXXMD =
+               llvm::dyn_cast<const CXXMethodDecl *>(IssueEntity))
+    FormatRHSValueDeclForSema(IssueCXXMD, IssueMsg);
+  else
+    IssueMsg.append(Temp.begin(), Temp.end());
 }
 
-llvm::SmallVector<ExprPrintingResult>
-FormatSrcExprForSema(const Expr *SrcExpr) {
+void FormatSrcExprForSema(
+    const Expr *SrcExpr,
+    llvm::SmallVectorImpl<ExprPrintingResult> &SrcMsgList) {
   if (!SrcExpr)
-    return {};
+    return;
   const auto *PureExpr = SrcExpr->IgnoreParenCasts();
   if (!PureExpr)
-    return {};
+    return;
+
+  const auto AppendSubExprIfNeeded = [&SrcMsgList](const Expr *SubExpr) {
+    if (SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
+      FormatSrcExprForSema(SubExpr, SrcMsgList);
+    }
+  };
 
   if (const auto *IOpCallExpr = llvm::dyn_cast<CXXOperatorCallExpr>(PureExpr);
-      IOpCallExpr && !IOpCallExpr->getExprLoc().isInvalid())
-    return {{{"expression"}, IOpCallExpr->getArg(0)}};
-  if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr);
-      IDeclExpr && !IDeclExpr->getExprLoc().isInvalid())
-    return {{{}, IDeclExpr}};
-
-  if (const auto *ICXXCallExpr = llvm::dyn_cast<CXXMemberCallExpr>(PureExpr);
-      ICXXCallExpr && !ICXXCallExpr->getExprLoc().isInvalid()) {
-    llvm::SmallVector<ExprPrintingResult> Result;
+      IOpCallExpr && !IOpCallExpr->getExprLoc().isInvalid()) {
+    SrcMsgList.push_back({"expression", IOpCallExpr->getArg(0)});
+  } else if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr);
+             IDeclExpr && !IDeclExpr->getExprLoc().isInvalid()) {
+    SrcMsgList.push_back({"", IDeclExpr});
+  } else if (const auto *ICXXCallExpr =
+                 llvm::dyn_cast<CXXMemberCallExpr>(PureExpr);
+             ICXXCallExpr && !ICXXCallExpr->getExprLoc().isInvalid()) {
     if (!ICXXCallExpr->getCallee()->getExprLoc().isInvalid())
-      Result.push_back({{"function call result"}, ICXXCallExpr});
-    if (const auto *SubExpr = ICXXCallExpr->getImplicitObjectArgument();
-        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
-      Result.append(FormatSrcExprForSema(SubExpr));
-    }
-    return Result;
-  }
-  if (const auto *ICallExpr = llvm::dyn_cast<CallExpr>(PureExpr);
-      ICallExpr && !ICallExpr->getExprLoc().isInvalid()) {
-    llvm::SmallVector<ExprPrintingResult> Result;
+      SrcMsgList.push_back({"function call result", ICXXCallExpr});
+    AppendSubExprIfNeeded(ICXXCallExpr->getImplicitObjectArgument());
+  } else if (const auto *ICallExpr = llvm::dyn_cast<CallExpr>(PureExpr);
+             ICallExpr && !ICallExpr->getExprLoc().isInvalid()) {
     if (!ICallExpr->getCallee()->getExprLoc().isInvalid())
-      Result.push_back({{"function call result"}, ICallExpr});
-    if (const auto *SubExpr = ICallExpr->getCallee();
-        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
-      Result.append(FormatSrcExprForSema(SubExpr));
-    }
-    return Result;
-  }
-  if (const auto *IMemberExpr = llvm::dyn_cast<MemberExpr>(PureExpr);
-      IMemberExpr && !IMemberExpr->getExprLoc().isInvalid()) {
-    llvm::SmallVector<ExprPrintingResult> Result;
-    Result.push_back({{"member access"}, IMemberExpr});
-    if (const auto *SubExpr = IMemberExpr->getBase();
-        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
-      Result.append(FormatSrcExprForSema(SubExpr));
-    }
-    return Result;
-  }
-
-  if (const auto *ICCExpr = llvm::dyn_cast<CXXConstructExpr>(PureExpr)) {
-    if (ICCExpr->getNumArgs() > 0) {
-      if (const auto *SubExpr = ICCExpr->getArg(0);
-          SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
-        return FormatSrcExprForSema(SubExpr);
-      }
-    }
+      SrcMsgList.push_back({"function call result", ICallExpr});
+    AppendSubExprIfNeeded(ICallExpr->getCallee());
+  } else if (const auto *IMemberExpr = llvm::dyn_cast<MemberExpr>(PureExpr);
+             IMemberExpr && !IMemberExpr->getExprLoc().isInvalid()) {
+    SrcMsgList.push_back({"member access", IMemberExpr});
+    AppendSubExprIfNeeded(IMemberExpr->getBase());
+  } else if (const auto *ICCExpr = llvm::dyn_cast<CXXConstructExpr>(PureExpr);
+             ICCExpr && ICCExpr->getNumArgs() > 0) {
+    AppendSubExprIfNeeded(ICCExpr->getArg(0));
+  } else if (const auto *ITempExpr =
+                 llvm::dyn_cast<CXXBindTemporaryExpr>(PureExpr)) {
+    AppendSubExprIfNeeded(ITempExpr->getSubExpr());
   }
-  if (const auto *ITempExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(PureExpr)) {
-    if (const auto *SubExpr = ITempExpr->getSubExpr();
-        SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
-      return FormatSrcExprForSema(SubExpr);
-    }
-  }
-
-  return {};
 }
 } // namespace clang::lifetimes
 
 namespace clang::lifetimes::internal {
 
-std::optional<llvm::SmallVector<AssignmentPair>>
-getAliasList(const AssignmentQueryContext &Context, const Fact *CausingFact,
-             const LoanID End, const CFGBlock *StartBlock,
-             const Expr *IssueExpr) {
+void getAliasList(const AssignmentQueryContext &Context,
+                  llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
+                  const Fact *CausingFact, const LoanID End,
+                  const CFGBlock *StartBlock, const Expr *IssueExpr) {
   llvm::SmallVector<OriginID, 4> TargetOIDList;
 
-  if (const auto *UF = llvm::dyn_cast<UseFact>(CausingFact)) {
+  if (const auto *UF = llvm::dyn_cast<UseFact>(CausingFact))
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       TargetOIDList.push_back(Cur->getOuterOriginID());
-  } else if (const auto *RetEscapeF =
-                 llvm::dyn_cast<ReturnEscapeFact>(CausingFact)) {
+  else if (const auto *RetEscapeF =
+               llvm::dyn_cast<ReturnEscapeFact>(CausingFact))
     TargetOIDList.push_back(RetEscapeF->getEscapedOriginID());
-  } else if (const auto *FieldEscapeF =
-                 llvm::dyn_cast<FieldEscapeFact>(CausingFact)) {
+  else if (const auto *FieldEscapeF =
+               llvm::dyn_cast<FieldEscapeFact>(CausingFact))
     TargetOIDList.push_back(FieldEscapeF->getEscapedOriginID());
-  } else if (const auto *GlobalEscapeF =
-                 llvm::dyn_cast<GlobalEscapeFact>(CausingFact)) {
+  else if (const auto *GlobalEscapeF =
+               llvm::dyn_cast<GlobalEscapeFact>(CausingFact))
     TargetOIDList.push_back(GlobalEscapeF->getEscapedOriginID());
-  } else {
+  else
     llvm_unreachable("Without a corresponding Fact handler, assignment history "
                      "traceback will fail.");
-  }
 
   const CFGBlock *EndBlock =
       IssueExpr ? Context.ADC.getCFGStmtMap()->getBlock(IssueExpr) : nullptr;
 
-  for (auto TargetOID : TargetOIDList) {
+  for (OriginID TargetOID : TargetOIDList) {
     if (StartBlock == EndBlock) {
-      const AliasAssignmentSearchResult Result =
+      AliasAssignmentSearchResult Result =
           getAliasListCore(Context, StartBlock, End, &TargetOID);
       if (!Result.Payload.empty())
-        return Result.Payload;
+        AssignmentList = Result.Payload;
     } else {
-      const auto Result =
+      std::optional<llvm::SmallVector<AssignmentPair>> Result =
           getAliasListInMultiBlock(Context, StartBlock, End, &TargetOID);
-      if (Result.has_value())
-        return Result.value();
+      if (Result)
+        AssignmentList = Result.value();
     }
   }
-
-  return std::nullopt;
 }
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 7d9b09dc59549..6a1424c4eac47 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -244,6 +244,7 @@ class LifetimeChecker {
       SourceLocation ExpiryLoc = Warning.ExpiryLoc;
       const struct AssignmentQueryContext Context = {
           LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
+      llvm::SmallVector<AssignmentPair> AssignmentList;
 
       if (const auto *UF = CausingFact.dyn_cast<const UseFact *>()) {
         if (Warning.InvalidatedByExpr) {
@@ -260,21 +261,20 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           const auto *StartBlock =
               ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
-          const auto AliasExprs =
-              getAliasList(Context, UF, LID, StartBlock, IssueExpr);
+          getAliasList(Context, AssignmentList, UF, LID, StartBlock, IssueExpr);
           SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr,
-                                         AliasExprs, ExpiryLoc);
+                                         AssignmentList, ExpiryLoc);
         }
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
         if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
           const auto *StartBlock =
               ADC.getCFGStmtMap()->getBlock(RetEscape->getReturnExpr());
-          const auto AliasExprs =
-              getAliasList(Context, RetEscape, LID, StartBlock, IssueExpr);
-          SemaHelper->reportUseAfterReturn(IssueExpr,
-                                           RetEscape->getReturnExpr(),
-                                           MovedExpr, AliasExprs, ExpiryLoc);
+          getAliasList(Context, AssignmentList, RetEscape, LID, StartBlock,
+                       IssueExpr);
+          SemaHelper->reportUseAfterReturn(
+              IssueExpr, RetEscape->getReturnExpr(), MovedExpr, AssignmentList,
+              ExpiryLoc);
         } else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
           // Dangling field.
           const auto BlockID = FactMgr.getBlockID(Warning.ExpiryExpr).value();
@@ -285,11 +285,11 @@ class LifetimeChecker {
               break;
             }
           }
-          const auto AliasExprs =
-              getAliasList(Context, FieldEscape, LID, StartBlock, IssueExpr);
+          getAliasList(Context, AssignmentList, FieldEscape, LID, StartBlock,
+                       IssueExpr);
           SemaHelper->reportDanglingField(IssueExpr,
                                           FieldEscape->getFieldDecl(),
-                                          MovedExpr, AliasExprs, ExpiryLoc);
+                                          MovedExpr, AssignmentList, ExpiryLoc);
         } else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
           // Global escape.
           const auto BlockID = FactMgr.getBlockID(Warning.ExpiryExpr).value();
@@ -300,10 +300,11 @@ class LifetimeChecker {
               break;
             }
           }
-          const auto AliasExprs =
-              getAliasList(Context, GlobalEscape, LID, StartBlock, IssueExpr);
+          getAliasList(Context, AssignmentList, GlobalEscape, LID, StartBlock,
+                       IssueExpr);
           SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
-                                           MovedExpr, AliasExprs, ExpiryLoc);
+                                           MovedExpr, AssignmentList,
+                                           ExpiryLoc);
         } else
           llvm_unreachable("Unhandled OriginEscapesFact type");
       } else
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 1497c7633a1c4..09eddd9f21888 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -38,33 +38,38 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) {
                           D->getBeginLoc());
 }
 
-inline __attribute__((always_inline)) llvm::SmallString<32>
-FormatLHSValueDeclForSema(const ValueDecl *TargetValue) {
-  llvm::SmallString<32> Result;
+inline __attribute__((always_inline)) void
+FormatLHSValueDeclForSema(const ValueDecl *TargetValue,
+                          llvm::SmallVectorImpl<char> &LHSMsg) {
   if (TargetValue) {
-    Result += "variable '";
-    Result += TargetValue->getName();
-    Result += "'";
+    const llvm::StringRef PrefixStr = "variable '";
+    const llvm::StringRef TargetName = TargetValue->getName();
+    LHSMsg.append(PrefixStr.begin(), PrefixStr.end());
+    LHSMsg.append(TargetName.begin(), TargetName.end());
+    LHSMsg.push_back('\'');
   }
-  return Result;
 }
 
 inline void reportAssignmentImpl(Sema &S, LoanEntity IssueEntity,
                                  const ValueDecl *LHS, const Expr *RHS,
                                  const SourceLocation LHSExploc) {
-  const llvm::SmallString<32> IssueMsg = FormatLoanEntityForSema(IssueEntity);
-  const llvm::SmallVector<ExprPrintingResult> SrcMsgList =
-      FormatSrcExprForSema(RHS);
+  llvm::SmallString<32> IssueMsg;
+  llvm::SmallString<32> LHSMsg;
+  llvm::SmallVector<ExprPrintingResult> SrcMsgList;
+  FormatLoanEntityForSema(IssueEntity, IssueMsg);
+  FormatLHSValueDeclForSema(LHS, LHSMsg);
+  FormatSrcExprForSema(RHS, SrcMsgList);
+
   if (SrcMsgList.size() == 1 &&
       llvm::isa<DeclRefExpr>(SrcMsgList[0].CurrExpr)) {
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
-        << FormatLHSValueDeclForSema(LHS) << IssueMsg;
+        << LHSMsg << IssueMsg;
   } else {
     for (const auto &SrcMsg : llvm::reverse(SrcMsgList))
       S.Diag(RHS->getBeginLoc(), diag::note_lifetime_safety_note_alias_chain)
-          << SrcMsg.CurrExpr->getSourceRange() << SrcMsg.Value << IssueMsg;
+          << SrcMsg.CurrExpr->getSourceRange() << SrcMsg.Str << IssueMsg;
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
-        << FormatLHSValueDeclForSema(LHS) << IssueMsg;
+        << LHSMsg << IssueMsg;
   }
 }
 
@@ -90,10 +95,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
 public:
   LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
 
-  void reportUseAfterFree(
-      const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation FreeLoc) override {
+  void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
+                          const Expr *MovedExpr,
+                          llvm::ArrayRef<AssignmentPair> AliasList,
+                          SourceLocation FreeLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved
                      : diag::warn_lifetime_safety_use_after_scope)
@@ -103,18 +108,17 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
           << MovedExpr->getSourceRange();
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
 
-    if (AliasList.has_value())
-      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
-        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+      reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
         << UseExpr->getSourceRange();
   }
 
-  void reportUseAfterReturn(
-      const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation ExpiryLoc) override {
+  void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
+                            const Expr *MovedExpr,
+                            llvm::ArrayRef<AssignmentPair> AliasList,
+                            SourceLocation ExpiryLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved
                      : diag::warn_lifetime_safety_return_stack_addr)
@@ -123,19 +127,18 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
 
-    if (AliasList.has_value())
-      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
-        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+      reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
         << ReturnExpr->getSourceRange();
   }
 
-  void reportDanglingField(
-      const Expr *IssueExpr, const FieldDecl *DanglingField,
-      const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation ExpiryLoc) override {
+  void reportDanglingField(const Expr *IssueExpr,
+                           const FieldDecl *DanglingField,
+                           const Expr *MovedExpr,
+                           llvm::ArrayRef<AssignmentPair> AliasList,
+                           SourceLocation ExpiryLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved
                      : diag::warn_lifetime_safety_dangling_field)
@@ -144,20 +147,19 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
 
-    if (AliasList.has_value())
-      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
-        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+      reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(DanglingField->getLocation(),
            diag::note_lifetime_safety_dangling_field_here)
         << DanglingField->getEndLoc();
   }
 
-  void reportDanglingGlobal(
-      const Expr *IssueExpr, const VarDecl *DanglingGlobal,
-      const Expr *MovedExpr,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      SourceLocation ExpiryLoc) override {
+  void reportDanglingGlobal(const Expr *IssueExpr,
+                            const VarDecl *DanglingGlobal,
+                            const Expr *MovedExpr,
+                            llvm::ArrayRef<AssignmentPair> AliasList,
+                            SourceLocation ExpiryLoc) override {
     S.Diag(IssueExpr->getExprLoc(),
            MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved
                      : diag::warn_lifetime_safety_dangling_global)
@@ -166,9 +168,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
 
-    if (AliasList.has_value())
-      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
-        reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
+    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+      reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
       S.Diag(DanglingGlobal->getLocation(),
@@ -232,10 +233,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
           << EscapeField->getSourceRange();
   }
 
-  void suggestLifetimeboundToImplicitThis(
-      SuggestionScope Scope, const CXXMethodDecl *MD,
-      const std::optional<llvm::SmallVector<AssignmentPair>> AliasList,
-      const Expr *EscapeExpr) override {
+  void
+  suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
+                                     const CXXMethodDecl *MD,
+                                     llvm::ArrayRef<AssignmentPair> AliasList,
+                                     const Expr *EscapeExpr) override {
     unsigned DiagID = (Scope == SuggestionScope::CrossTU)
                           ? diag::warn_lifetime_safety_cross_tu_this_suggestion
                           : diag::warn_lifetime_safety_intra_tu_this_suggestion;
@@ -262,9 +264,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << FixItHint::CreateInsertion(InsertionPoint,
                                       " [[clang::lifetimebound]]");
 
-    if (AliasList.has_value())
-      for (const auto &AliasStmt : llvm::reverse(AliasList.value()))
-        reportAssignment(S, MD, AliasStmt.first, AliasStmt.second);
+    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+      reportAssignment(S, MD, AliasStmt.first, AliasStmt.second);
 
     S.Diag(EscapeExpr->getBeginLoc(),
            diag::note_lifetime_safety_suggestion_returned_here)

>From 994111526e0a1a4f9f5b7fb965928fc9d9c7835e Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 10 Apr 2026 11:50:35 +0800
Subject: [PATCH 15/22] refactor getAliasListCore and some code style fix

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |   1 -
 .../LifetimeSafety/AssignmentQuery.cpp        | 217 ++++++++++--------
 2 files changed, 119 insertions(+), 99 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 2b763fea8d62b..8e3c2017eb5a1 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -46,7 +46,6 @@ void FormatSrcExprForSema(
 namespace clang::lifetimes::internal {
 
 struct AliasAssignmentSearchResult {
-  const llvm::SmallVector<AssignmentPair> Payload;
   bool SearchComplete;
   const std::optional<OriginDestExpr> LastDestDecl;
   std::optional<OriginID> LastOrigin;
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 8e37c7dfd823f..20690b24524a5 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -17,6 +17,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include <cstddef>
 
@@ -74,91 +75,114 @@ const MemberExpr *getFieldFromAssignmentExpr(const Expr *RHS,
   return nullptr;
 }
 
+const DeclRefExpr *getLHSExpr(const UseFact *UF, const OriginID OID) {
+  for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+       Cur = Cur->peelOuterOrigin()) {
+    if (Cur->getOuterOriginID() != OID || !UF->isWritten())
+      continue;
+    std::optional<const Expr *> UExpr = GetPureSrcExpr(UF->getUseExpr());
+    if (UExpr) {
+      if (const auto *UDExpr = llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
+        return UDExpr;
+      }
+    }
+  }
+  return nullptr;
+}
+
+std::optional<OriginDestExpr>
+getLHSDeclOrExpr(const AssignmentQueryContext &Context,
+                 const OriginFlowFact *OFF) {
+  const Origin TargetOrigin =
+      Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
+  if (const ValueDecl *DVecl = TargetOrigin.getDecl();
+      DVecl && !DVecl->getLocation().isInvalid()) {
+    if (llvm::isa<FieldDecl>(DVecl)) {
+      const Expr *CurrExpr = Context.FactMgr.getOriginMgr()
+                                 .getOrigin(OFF->getSrcOriginID())
+                                 .getExpr();
+      if (CurrExpr)
+        return getFieldFromAssignmentExpr(CurrExpr, Context.ADC.getParentMap());
+    } else {
+      return DVecl;
+    }
+  }
+  return std::nullopt;
+}
+
+std::optional<const Expr *>
+getRHSDeclOrExpr(const AssignmentQueryContext &Context,
+                 const OriginFlowFact *OFF) {
+  const Origin TargetOrigin =
+      Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
+  std::optional<const Expr *> SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
+  if (!SExpr) {
+    const Origin SrcOrigin =
+        Context.FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID());
+    SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
+  }
+
+  return SExpr;
+}
+
 AliasAssignmentSearchResult getAliasListCore(
-    const AssignmentQueryContext &Context, const CFGBlock *Block,
-    const LoanID EndLoanID, OriginID *TargetOID,
+    const AssignmentQueryContext &Context,
+    llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
+    const CFGBlock *Block, const LoanID EndLoanID, OriginID *TargetOID,
     const std::optional<OriginDestExpr> LastDestExpr = std::nullopt,
     const std::optional<OriginID> LastOriginID = std::nullopt) {
-  llvm::SmallVector<AssignmentPair> AliasStmts;
   llvm::ArrayRef<const Fact *> Facts = Context.FactMgr.getFacts(Block);
   std::optional<OriginID> IssueOriginID = LastOriginID;
-  std::optional<OriginDestExpr> DestExpr = LastDestExpr;
+  std::optional<OriginDestExpr> CurrDestExpr = LastDestExpr;
   std::optional<OriginID> CurrOrigin = std::nullopt;
-  std::optional<const Expr *> SrcExpr = std::nullopt;
+
+  const auto InsertAssignmentList = [&](const OriginFlowFact *OFF) {
+    if (!CurrDestExpr) {
+      std::optional<OriginDestExpr> DestExpr = getLHSDeclOrExpr(Context, OFF);
+      if (DestExpr) {
+        if (llvm::isa<const ValueDecl *>(DestExpr.value()))
+          CurrOrigin = *TargetOID;
+        CurrDestExpr = DestExpr;
+      }
+    } else {
+      std::optional<const Expr *> CurrSrcExpr = getRHSDeclOrExpr(Context, OFF);
+      if (CurrSrcExpr) {
+        AssignmentList.push_back({CurrDestExpr.value(), CurrSrcExpr.value()});
+        CurrDestExpr = std::nullopt;
+        CurrOrigin = std::nullopt;
+      }
+    }
+  };
 
   for (const Fact *F : llvm::reverse(Facts)) {
     if (const auto *OFF = F->getAs<OriginFlowFact>()) {
       if (IssueOriginID && OFF->getDestOriginID() == IssueOriginID.value())
-        return {AliasStmts, true, DestExpr, IssueOriginID};
-      if (OFF->getDestOriginID() == *TargetOID) {
-        const LoanSet HeldLoans =
-            Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
-
-        if (HeldLoans.contains(EndLoanID)) {
-          const Origin TargetOrigin =
-              Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
-
-          if (!DestExpr.has_value()) {
-            if (const ValueDecl *DVecl = TargetOrigin.getDecl();
-                DVecl && !DVecl->getLocation().isInvalid()) {
-              if (llvm::isa<FieldDecl>(DVecl)) {
-                const auto *CurrExpr = Context.FactMgr.getOriginMgr()
-                                           .getOrigin(OFF->getSrcOriginID())
-                                           .getExpr();
-                if (CurrExpr)
-                  DestExpr = getFieldFromAssignmentExpr(
-                      CurrExpr, Context.ADC.getParentMap());
-              } else {
-                CurrOrigin = *TargetOID;
-                DestExpr = DVecl;
-              }
-            }
-          } else {
-            auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
-            if (!SExpr) {
-              const auto SrcOrigin = Context.FactMgr.getOriginMgr().getOrigin(
-                  OFF->getSrcOriginID());
-              SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
-            }
-
-            if (SExpr) {
-              AliasStmts.push_back({DestExpr.value(), SExpr.value()});
-              SrcExpr = SExpr.value();
-              DestExpr = std::nullopt;
-              CurrOrigin = std::nullopt;
-            }
-          }
-          *TargetOID = OFF->getSrcOriginID();
-        }
+        return {true, CurrDestExpr, IssueOriginID};
+      if (OFF->getDestOriginID() == *TargetOID &&
+          Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF)
+              .contains(EndLoanID)) {
+        InsertAssignmentList(OFF);
+        *TargetOID = OFF->getSrcOriginID();
       }
     } else if (const auto *IF = F->getAs<IssueFact>()) {
       if (IF->getLoanID() == EndLoanID)
         IssueOriginID = IF->getOriginID();
     } else if (const auto *UF = F->getAs<UseFact>()) {
-      if (CurrOrigin.has_value()) {
-        for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
-             Cur = Cur->peelOuterOrigin()) {
-          if (Cur->getOuterOriginID() != CurrOrigin.value() || !UF->isWritten())
-            continue;
-          std::optional<const Expr *> UExpr = GetPureSrcExpr(UF->getUseExpr());
-          if (UExpr.has_value()) {
-            if (const auto *UDExpr =
-                    llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
-              DestExpr = UDExpr;
-              break;
-            }
-          }
-        }
+      if (CurrOrigin) {
+        const DeclRefExpr *LHSExpr = getLHSExpr(UF, CurrOrigin.value());
+        if (LHSExpr)
+          CurrDestExpr = LHSExpr;
       }
     }
   }
-  return {AliasStmts, false, DestExpr, IssueOriginID};
+
+  return {false, CurrDestExpr, IssueOriginID};
 }
 
-std::optional<llvm::SmallVector<AssignmentPair>>
-getAliasListInMultiBlock(const AssignmentQueryContext &Context,
-                         const CFGBlock *StartBlock, const LoanID EndLoanID,
-                         OriginID *StartOID) {
+void getAliasListInMultiBlock(
+    const AssignmentQueryContext &Context,
+    llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
+    const CFGBlock *StartBlock, const LoanID EndLoanID, OriginID *StartOID) {
   std::optional<OriginDestExpr> LastDestDecl = std::nullopt;
   llvm::SmallVector<const CFGBlock *> PendingBlocks;
   std::optional<AssignmentPair> StartStmt = std::nullopt;
@@ -167,12 +191,14 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
   llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
   llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs;
 
-  const auto AliasStmtFilter = [&VistedExprs](const AssignmentPair StartStmt,
-                                              const AssignmentPair EndStmt) {
+  const auto AliasStmtFilter = [&VistedExprs,
+                                &AssignmentList](const AssignmentPair StartStmt,
+                                                 const AssignmentPair EndStmt) {
     llvm::SmallVector<AssignmentPair> AliasStmts;
-    for (auto Stmt = StartStmt; Stmt != EndStmt; Stmt = VistedExprs.at(Stmt))
-      AliasStmts.push_back(Stmt);
-    AliasStmts.push_back(EndStmt);
+    for (AssignmentPair Stmt = StartStmt; Stmt != EndStmt;
+         Stmt = VistedExprs.at(Stmt))
+      AssignmentList.push_back(Stmt);
+    AssignmentList.push_back(EndStmt);
     return AliasStmts;
   };
 
@@ -180,14 +206,15 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
 
   for (size_t i = 0; i < PendingBlocks.size(); ++i) {
     const CFGBlock *CurrBlock = PendingBlocks[i];
+    llvm::SmallVector<AssignmentPair> BlockAliasList;
 
-    const auto [BlockAliasList, Success, CurrLastDestDecl, CurrLastOriginID] =
-        getAliasListCore(Context, CurrBlock, EndLoanID, StartOID, LastDestDecl,
-                         LastOriginID);
-    if (CurrLastDestDecl)
-      LastDestDecl = CurrLastDestDecl;
-    if (CurrLastOriginID.has_value())
-      LastOriginID = CurrLastOriginID;
+    const AliasAssignmentSearchResult Result =
+        getAliasListCore(Context, BlockAliasList, CurrBlock, EndLoanID,
+                         StartOID, LastDestDecl, LastOriginID);
+    if (Result.LastDestDecl)
+      LastDestDecl = Result.LastDestDecl;
+    if (Result.LastOrigin)
+      LastOriginID = Result.LastOrigin;
 
     if (!BlockAliasList.empty()) {
       if (VistedExprs.empty())
@@ -196,28 +223,27 @@ getAliasListInMultiBlock(const AssignmentQueryContext &Context,
       for (size_t i = 0; i < BlockAliasList.size() - 1; ++i)
         VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]});
 
-      if (EndStmt.has_value())
+      if (EndStmt)
         VistedExprs.insert({EndStmt.value(), BlockAliasList[0]});
 
       EndStmt = BlockAliasList[BlockAliasList.size() - 1];
     }
 
-    if (Success && StartStmt.has_value() && EndStmt.has_value())
-      return AliasStmtFilter(StartStmt.value(), EndStmt.value());
+    // TODO: The number of CFGBlocks is limited to 32 to minmize performance
+    // impact. Note that is not a magic number derived from extensive
+    // engineering practice. If this limit proves unnecessary or overly
+    // restrictive, the boundary conditions should be adjusted.
+    // https://github.com/llvm/llvm-project/pull/188467#discussion_r3050068533
+    if (Result.SearchComplete || VistedBlocks.size() >= 32)
+      break;
 
-    for (const auto &Block : CurrBlock->preds())
+    for (const CFGBlock *Block : CurrBlock->preds())
       if (Block && VistedBlocks.insert(Block).second)
         PendingBlocks.push_back(Block);
-
-    if (VistedBlocks.size() >= 32 && StartStmt.has_value() &&
-        EndStmt.has_value())
-      return AliasStmtFilter(StartStmt.value(), EndStmt.value());
   }
 
-  if (StartStmt.has_value() && EndStmt.has_value())
-    return AliasStmtFilter(StartStmt.value(), EndStmt.value());
-
-  return std::nullopt;
+  if (StartStmt && EndStmt)
+    AliasStmtFilter(StartStmt.value(), EndStmt.value());
 }
 
 void FormatRHSValueDeclForSema(const ValueDecl *TargetValue,
@@ -258,7 +284,7 @@ void FormatSrcExprForSema(
     llvm::SmallVectorImpl<ExprPrintingResult> &SrcMsgList) {
   if (!SrcExpr)
     return;
-  const auto *PureExpr = SrcExpr->IgnoreParenCasts();
+  const Expr *PureExpr = SrcExpr->IgnoreParenCasts();
   if (!PureExpr)
     return;
 
@@ -329,15 +355,10 @@ void getAliasList(const AssignmentQueryContext &Context,
 
   for (OriginID TargetOID : TargetOIDList) {
     if (StartBlock == EndBlock) {
-      AliasAssignmentSearchResult Result =
-          getAliasListCore(Context, StartBlock, End, &TargetOID);
-      if (!Result.Payload.empty())
-        AssignmentList = Result.Payload;
+      getAliasListCore(Context, AssignmentList, StartBlock, End, &TargetOID);
     } else {
-      std::optional<llvm::SmallVector<AssignmentPair>> Result =
-          getAliasListInMultiBlock(Context, StartBlock, End, &TargetOID);
-      if (Result)
-        AssignmentList = Result.value();
+      getAliasListInMultiBlock(Context, AssignmentList, StartBlock, End,
+                               &TargetOID);
     }
   }
 }

>From 0c6fd21723b86aeb34c79eab9d8a788ac4115179 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 10 Apr 2026 13:16:18 +0800
Subject: [PATCH 16/22] simplify getBlockContaining

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 clang/lib/Analysis/LifetimeSafety/Facts.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..8a71fb8404cfe 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -145,11 +145,9 @@ void FactManager::dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
 
 llvm::ArrayRef<const Fact *>
 FactManager::getBlockContaining(ProgramPoint P) const {
-  for (const auto &BlockToFactsVec : BlockToFacts) {
-    for (const Fact *F : BlockToFactsVec)
-      if (F == P)
-        return BlockToFactsVec;
-  }
+  std::optional<size_t> BlockIndex = getBlockID(P);
+  if (BlockIndex)
+    return BlockToFacts[BlockIndex.value()];
   return {};
 }
 

>From 66a248e40e6a14b2a6f14ae06018affa4c7fe202 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Sat, 11 Apr 2026 10:19:20 +0800
Subject: [PATCH 17/22] fix code style

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |  24 +-
 .../Analysis/Analyses/LifetimeSafety/Loans.h  |   6 +
 .../Analyses/LifetimeSafety/Origins.h         |   6 +
 .../LifetimeSafety/AssignmentQuery.cpp        | 350 ++++++++++--------
 clang/lib/Sema/SemaLifetimeSafety.h           |  13 +-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |   6 +-
 clang/test/Sema/warn-lifetime-safety.cpp      |  20 +-
 7 files changed, 238 insertions(+), 187 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 8e3c2017eb5a1..61a5bcfdbdfb7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -24,12 +24,6 @@
 
 namespace clang::lifetimes {
 
-using OriginDestExpr =
-    llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *,
-                       const MemberExpr *>;
-using LoanEntity = llvm::PointerUnion<const Expr *, const ParmVarDecl *,
-                                      const CXXMethodDecl *>;
-
 using AssignmentPair = std::pair<OriginDestExpr, const Expr *>;
 
 struct ExprPrintingResult {
@@ -37,18 +31,18 @@ struct ExprPrintingResult {
   const Expr *CurrExpr;
 };
 
-void FormatLoanEntityForSema(LoanEntity IssueEntity,
+void formatLoanEntityForSema(LoanEntity IssueEntity,
                              llvm::SmallVectorImpl<char> &IssueMsg);
-void FormatSrcExprForSema(
+void formatSrcExprForSema(
     const Expr *SrcExpr, llvm::SmallVectorImpl<ExprPrintingResult> &SrcMsgList);
 } // namespace clang::lifetimes
 
 namespace clang::lifetimes::internal {
 
 struct AliasAssignmentSearchResult {
-  bool SearchComplete;
-  const std::optional<OriginDestExpr> LastDestDecl;
-  std::optional<OriginID> LastOrigin;
+  const bool SearchComplete;
+  const OriginDestExpr LastDestDecl;
+  const std::optional<OriginID> LastOrigin;
 };
 
 struct AssignmentQueryContext {
@@ -59,10 +53,16 @@ struct AssignmentQueryContext {
   AnalysisDeclContext &ADC;
 };
 
-/// Retrieves a list of assignment chains for use-after-scope analysis.
+/// Get assignment history when an error is detected.
 ///
 /// To help users understand the data flow, we track where the problematic
 /// address originated.
+///
+/// `StartBlock` denotes the beginning block of the search, while `IssueExpr`
+/// represents the ending block.
+/// Note that `IssueExpr` can be null, as certain errors may not provide a
+/// specific expression. The search termination condition is independent of
+/// `IssueExpr`; it serves only as a hint to assist specific search strategies.
 void getAliasList(const AssignmentQueryContext &Context,
                   llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
                   const Fact *CausingFact, const LoanID End,
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
index aee6bf9eb69c9..6dc95a6360053 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
@@ -20,6 +20,12 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "llvm/Support/raw_ostream.h"
 
+namespace clang::lifetimes {
+using LoanEntity = llvm::PointerUnion<const Expr *, const ParmVarDecl *,
+                                      const CXXMethodDecl *>;
+
+} // namespace clang::lifetimes
+
 namespace clang::lifetimes::internal {
 
 using LoanID = utils::ID<struct LoanTag>;
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index c2db59c579060..d513757803bfb 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -23,6 +23,12 @@
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "llvm/Support/raw_ostream.h"
 
+namespace clang::lifetimes {
+using OriginDestExpr =
+    llvm::PointerUnion<const DeclRefExpr *, const ValueDecl *,
+                       const MemberExpr *>;
+} // namespace clang::lifetimes
+
 namespace clang::lifetimes::internal {
 
 using OriginID = utils::ID<struct OriginTag>;
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 20690b24524a5..304cd4d36651a 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -20,6 +20,7 @@
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include <cstddef>
+#include <queue>
 
 namespace {
 
@@ -27,130 +28,144 @@ using namespace clang;
 using namespace clang::lifetimes;
 using namespace clang::lifetimes::internal;
 
-std::optional<const Expr *> GetPureSrcExpr(const Expr *TargetExpr) {
-  if (!TargetExpr)
-    return std::nullopt;
+/// Locate the rightmost sub expression of the RHS, given that the LHS is
+/// already known. To ensure printability, we invoke `Explorc->isValid()`.
+///
+/// `PureSrcExpr` refers to the root expression representing the RHS.
+/// Generally, we aim for the rightmost sub expression because it will be
+/// recursively parsed in `formatSrcExprForSema`.
+///
+/// Most expressions accepted by this function have corresponding handling logic
+/// in `formatSrcExprForSema`.
+///
+/// Note that the returned `DeclRefExpr` acts purely as a placeholder.
+/// For simple assignments like `a = b`, we do not need to provide source
+/// location highlights for the RHS, so `formatSrcExprForSema` will not perform
+/// any meaningful parsing on this `DeclRefExpr`.
+const Expr *getPureSrcExpr(const Expr *TargetExpr) {
+  assert(TargetExpr);
   const Expr *SExpr = TargetExpr->IgnoreParenCasts();
-  if (!SExpr)
-    return std::nullopt;
 
-  if (llvm::isa<DeclRefExpr, CXXTemporaryObjectExpr, ConditionalOperator,
-                CXXConstructExpr>(SExpr) &&
-      !SExpr->getExprLoc().isInvalid())
+  if (isa_and_nonnull<DeclRefExpr, CXXTemporaryObjectExpr, CXXConstructExpr,
+                      MemberExpr, CXXMemberCallExpr, UnaryOperator,
+                      CXXBindTemporaryExpr>(SExpr) &&
+      SExpr->getExprLoc().isValid())
     return SExpr;
 
-  if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr);
-      SCExpr && !SCExpr->getExprLoc().isInvalid() &&
-      !SCExpr->getCallee()->IgnoreParenCasts()->getExprLoc().isInvalid())
+  if (const auto *SCExpr = dyn_cast_or_null<CallExpr>(SExpr);
+      SCExpr && SCExpr->getExprLoc().isValid() &&
+      SCExpr->getCallee()->IgnoreParenCasts()->getExprLoc().isValid())
     return SCExpr;
 
-  if (const auto *SMExpr = llvm::dyn_cast<MemberExpr>(SExpr))
-    return GetPureSrcExpr(SMExpr->getBase());
-  if (const auto *SCExpr = llvm::dyn_cast<CXXMemberCallExpr>(SExpr))
-    return GetPureSrcExpr(SCExpr->getCallee());
-  if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr))
-    return GetPureSrcExpr(SUOExpr->getSubExpr());
-  if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr))
-    return GetPureSrcExpr(SCBExpr->getSubExpr());
-
-  return std::nullopt;
-}
-
-/// Specifically handles assignments involving a FieldDecl.
-///
-/// Since we currently only store the FieldDecl without its corresponding
-/// LHS expression, this function attempts to recover or resolve the LHS
-/// context by analyzing the RHS.
-const MemberExpr *getFieldFromAssignmentExpr(const Expr *RHS,
-                                             const ParentMap &CurrParentMap) {
-
-  const Stmt *CurrStmt = CurrParentMap.getParent(RHS);
-  if (!CurrStmt)
-    return nullptr;
-  if (const auto *BinaryOp = llvm::dyn_cast<BinaryOperator>(CurrStmt))
-    return llvm::dyn_cast<MemberExpr>(BinaryOp->getLHS());
-  if (const auto *CXXOp = llvm::dyn_cast<CXXOperatorCallExpr>(CurrStmt);
-      CXXOp && CXXOp->getOperator() == OO_Equal && CXXOp->getNumArgs() == 2)
-    return llvm::dyn_cast<MemberExpr>(CXXOp->getArg(0));
   return nullptr;
 }
 
+/// Obtain the actual LHS Expr from `WriteUF->getUseExpr()` based on the Decl
+/// retrieved from `DestOrigin->getDecl()` in the `OriginFlowFact`
 const DeclRefExpr *getLHSExpr(const UseFact *UF, const OriginID OID) {
   for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
        Cur = Cur->peelOuterOrigin()) {
     if (Cur->getOuterOriginID() != OID || !UF->isWritten())
       continue;
-    std::optional<const Expr *> UExpr = GetPureSrcExpr(UF->getUseExpr());
-    if (UExpr) {
-      if (const auto *UDExpr = llvm::dyn_cast<DeclRefExpr>(UExpr.value())) {
-        return UDExpr;
-      }
+    if (const auto *UDestExpr =
+            dyn_cast_or_null<DeclRefExpr>(getPureSrcExpr(UF->getUseExpr()))) {
+      return UDestExpr;
     }
   }
   return nullptr;
 }
 
-std::optional<OriginDestExpr>
-getLHSDeclOrExpr(const AssignmentQueryContext &Context,
-                 const OriginFlowFact *OFF) {
-  const Origin TargetOrigin =
+OriginDestExpr getLHSDeclOrExpr(const AssignmentQueryContext &Context,
+                                const OriginFlowFact *OFF) {
+  const Origin &DestOrigin =
       Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
-  if (const ValueDecl *DVecl = TargetOrigin.getDecl();
-      DVecl && !DVecl->getLocation().isInvalid()) {
-    if (llvm::isa<FieldDecl>(DVecl)) {
-      const Expr *CurrExpr = Context.FactMgr.getOriginMgr()
-                                 .getOrigin(OFF->getSrcOriginID())
-                                 .getExpr();
-      if (CurrExpr)
-        return getFieldFromAssignmentExpr(CurrExpr, Context.ADC.getParentMap());
-    } else {
-      return DVecl;
-    }
+  const Origin &SrcOrigin =
+      Context.FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID());
+
+  if (const ValueDecl *DestDecl = DestOrigin.getDecl();
+      DestDecl && DestDecl->getLocation().isValid()) {
+    return DestDecl;
   }
-  return std::nullopt;
+
+  // This logic specifically handles the isa<FieldDecl>(DestOrigin->getDecl())
+  // case. In `OriginFlowFact`, we store the Decl of the corresponding variable
+  // as the Origin rather than the LHS Origin itself.
+  //
+  // For a general `ValueDecl`, we typically find the corresponding `UseFact`
+  // following the `OriginFlowFact`. However, for a `FieldDecl`, the subsequent
+  // `OriginFlowFact` is associated with a `MemberExpr`. In this scenario,
+  // `DestOrigin` represents the `MemberExpr`, while SrcOrigin represents the
+  // Origin of the `CXXThisExpr` (CXXMethodDecl).
+  const Expr *DestExpr = DestOrigin.getExpr();
+  const ValueDecl *SrcDecl = SrcOrigin.getDecl();
+  if (isa_and_nonnull<MemberExpr>(DestExpr) &&
+      isa_and_nonnull<CXXMethodDecl>(SrcDecl))
+    return dyn_cast<MemberExpr>(DestExpr);
+
+  return nullptr;
 }
 
-std::optional<const Expr *>
-getRHSDeclOrExpr(const AssignmentQueryContext &Context,
-                 const OriginFlowFact *OFF) {
-  const Origin TargetOrigin =
+const Expr *getRHSExpr(const AssignmentQueryContext &Context,
+                       const OriginFlowFact *OFF) {
+  const Origin &DestOrigin =
       Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
-  std::optional<const Expr *> SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
+
+  const Expr *SExpr = getPureSrcExpr(DestOrigin.getExpr());
   if (!SExpr) {
-    const Origin SrcOrigin =
+    const Origin &SrcOrigin =
         Context.FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID());
-    SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
+    SExpr = getPureSrcExpr(SrcOrigin.getExpr());
   }
 
   return SExpr;
 }
 
-AliasAssignmentSearchResult getAliasListCore(
-    const AssignmentQueryContext &Context,
-    llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
-    const CFGBlock *Block, const LoanID EndLoanID, OriginID *TargetOID,
-    const std::optional<OriginDestExpr> LastDestExpr = std::nullopt,
-    const std::optional<OriginID> LastOriginID = std::nullopt) {
-  llvm::ArrayRef<const Fact *> Facts = Context.FactMgr.getFacts(Block);
+AliasAssignmentSearchResult
+getAliasListCore(const AssignmentQueryContext &Context,
+                 llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
+                 const CFGBlock *Block, const LoanID EndLoanID,
+                 OriginID *TargetOID,
+                 const OriginDestExpr LastDestExpr = nullptr,
+                 const std::optional<OriginID> LastOriginID = std::nullopt) {
+  OriginDestExpr CurrDestExpr = LastDestExpr;
   std::optional<OriginID> IssueOriginID = LastOriginID;
-  std::optional<OriginDestExpr> CurrDestExpr = LastDestExpr;
   std::optional<OriginID> CurrOrigin = std::nullopt;
+  llvm::ArrayRef<const Fact *> Facts = Context.FactMgr.getFacts(Block);
+  bool NeedSearchOriginDestWithoutLoan = false;
 
-  const auto InsertAssignmentList = [&](const OriginFlowFact *OFF) {
-    if (!CurrDestExpr) {
-      std::optional<OriginDestExpr> DestExpr = getLHSDeclOrExpr(Context, OFF);
-      if (DestExpr) {
-        if (llvm::isa<const ValueDecl *>(DestExpr.value()))
-          CurrOrigin = *TargetOID;
-        CurrDestExpr = DestExpr;
+  const auto TryInsertAssignmentList = [&](const OriginFlowFact *OFF) {
+    if (NeedSearchOriginDestWithoutLoan) {
+      if (const MemberExpr *DestMemberExpr =
+              dyn_cast_or_null<const MemberExpr *>(
+                  getLHSDeclOrExpr(Context, OFF))) {
+
+        CurrDestExpr = DestMemberExpr;
+        NeedSearchOriginDestWithoutLoan = false;
       }
-    } else {
-      std::optional<const Expr *> CurrSrcExpr = getRHSDeclOrExpr(Context, OFF);
-      if (CurrSrcExpr) {
-        AssignmentList.push_back({CurrDestExpr.value(), CurrSrcExpr.value()});
-        CurrDestExpr = std::nullopt;
-        CurrOrigin = std::nullopt;
+    }
+    if (OFF->getDestOriginID() == *TargetOID &&
+        Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF)
+            .contains(EndLoanID)) {
+      if (!CurrDestExpr) {
+        OriginDestExpr DestExpr = getLHSDeclOrExpr(Context, OFF);
+        const ValueDecl *DestValueDecl =
+            dyn_cast_or_null<const ValueDecl *>(DestExpr);
+        if (DestValueDecl)
+          CurrOrigin = *TargetOID;
+
+        if (llvm::isa_and_nonnull<FieldDecl>(DestValueDecl))
+          NeedSearchOriginDestWithoutLoan = true;
+        else
+          CurrDestExpr = DestExpr;
+      } else {
+        const Expr *CurrSrcExpr = getRHSExpr(Context, OFF);
+        if (CurrSrcExpr) {
+          AssignmentList.push_back({CurrDestExpr, CurrSrcExpr});
+          CurrDestExpr = nullptr;
+          CurrOrigin = std::nullopt;
+        }
       }
+      *TargetOID = OFF->getSrcOriginID();
     }
   };
 
@@ -158,12 +173,7 @@ AliasAssignmentSearchResult getAliasListCore(
     if (const auto *OFF = F->getAs<OriginFlowFact>()) {
       if (IssueOriginID && OFF->getDestOriginID() == IssueOriginID.value())
         return {true, CurrDestExpr, IssueOriginID};
-      if (OFF->getDestOriginID() == *TargetOID &&
-          Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF)
-              .contains(EndLoanID)) {
-        InsertAssignmentList(OFF);
-        *TargetOID = OFF->getSrcOriginID();
-      }
+      TryInsertAssignmentList(OFF);
     } else if (const auto *IF = F->getAs<IssueFact>()) {
       if (IF->getLoanID() == EndLoanID)
         IssueOriginID = IF->getOriginID();
@@ -183,48 +193,48 @@ void getAliasListInMultiBlock(
     const AssignmentQueryContext &Context,
     llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
     const CFGBlock *StartBlock, const LoanID EndLoanID, OriginID *StartOID) {
-  std::optional<OriginDestExpr> LastDestDecl = std::nullopt;
-  llvm::SmallVector<const CFGBlock *> PendingBlocks;
+  OriginDestExpr LastDestDeclOrExpr = nullptr;
+  std::queue<const CFGBlock *> PendingBlocks;
+  std::optional<OriginID> LastOriginID = std::nullopt;
   std::optional<AssignmentPair> StartStmt = std::nullopt;
   std::optional<AssignmentPair> EndStmt = std::nullopt;
-  std::optional<OriginID> LastOriginID = std::nullopt;
   llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
-  llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs;
+  llvm::DenseMap<AssignmentPair, AssignmentPair> VistedAssignmentExprs;
 
-  const auto AliasStmtFilter = [&VistedExprs,
+  const auto AliasStmtFilter = [&VistedAssignmentExprs,
                                 &AssignmentList](const AssignmentPair StartStmt,
                                                  const AssignmentPair EndStmt) {
-    llvm::SmallVector<AssignmentPair> AliasStmts;
     for (AssignmentPair Stmt = StartStmt; Stmt != EndStmt;
-         Stmt = VistedExprs.at(Stmt))
+         Stmt = VistedAssignmentExprs.at(Stmt))
       AssignmentList.push_back(Stmt);
     AssignmentList.push_back(EndStmt);
-    return AliasStmts;
   };
 
-  PendingBlocks.push_back(StartBlock);
+  PendingBlocks.push(StartBlock);
 
-  for (size_t i = 0; i < PendingBlocks.size(); ++i) {
-    const CFGBlock *CurrBlock = PendingBlocks[i];
+  while (!PendingBlocks.empty()) {
+    const CFGBlock *CurrBlock = PendingBlocks.front();
+    PendingBlocks.pop();
     llvm::SmallVector<AssignmentPair> BlockAliasList;
 
     const AliasAssignmentSearchResult Result =
         getAliasListCore(Context, BlockAliasList, CurrBlock, EndLoanID,
-                         StartOID, LastDestDecl, LastOriginID);
+                         StartOID, LastDestDeclOrExpr, LastOriginID);
     if (Result.LastDestDecl)
-      LastDestDecl = Result.LastDestDecl;
+      LastDestDeclOrExpr = Result.LastDestDecl;
     if (Result.LastOrigin)
       LastOriginID = Result.LastOrigin;
 
     if (!BlockAliasList.empty()) {
-      if (VistedExprs.empty())
+      if (VistedAssignmentExprs.empty())
         StartStmt = BlockAliasList[0];
 
       for (size_t i = 0; i < BlockAliasList.size() - 1; ++i)
-        VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]});
+        VistedAssignmentExprs.insert(
+            {BlockAliasList[i], BlockAliasList[i + 1]});
 
       if (EndStmt)
-        VistedExprs.insert({EndStmt.value(), BlockAliasList[0]});
+        VistedAssignmentExprs.insert({EndStmt.value(), BlockAliasList[0]});
 
       EndStmt = BlockAliasList[BlockAliasList.size() - 1];
     }
@@ -237,16 +247,16 @@ void getAliasListInMultiBlock(
     if (Result.SearchComplete || VistedBlocks.size() >= 32)
       break;
 
-    for (const CFGBlock *Block : CurrBlock->preds())
-      if (Block && VistedBlocks.insert(Block).second)
-        PendingBlocks.push_back(Block);
+    for (const CFGBlock *NextBlock : CurrBlock->preds())
+      if (NextBlock && VistedBlocks.insert(NextBlock).second)
+        PendingBlocks.push(NextBlock);
   }
 
   if (StartStmt && EndStmt)
     AliasStmtFilter(StartStmt.value(), EndStmt.value());
 }
 
-void FormatRHSValueDeclForSema(const ValueDecl *TargetValue,
+void formatRHSValueDeclForSema(const ValueDecl *TargetValue,
                                llvm::SmallVectorImpl<char> &IssueMsg) {
   if (TargetValue) {
     const StringRef TargetName = TargetValue->getName();
@@ -259,68 +269,89 @@ void FormatRHSValueDeclForSema(const ValueDecl *TargetValue,
 
 namespace clang::lifetimes {
 
-void FormatLoanEntityForSema(LoanEntity IssueEntity,
+void formatLoanEntityForSema(LoanEntity IssueEntity,
                              llvm::SmallVectorImpl<char> &IssueMsg) {
   llvm::StringRef Temp = "the temporary";
 
-  if (const auto *IssueExpr = llvm::dyn_cast<const Expr *>(IssueEntity)) {
+  if (const auto *IssueExpr = dyn_cast<const Expr *>(IssueEntity)) {
     if (const auto *IDeclExpr =
-            llvm::dyn_cast<DeclRefExpr>(IssueExpr->IgnoreParenCasts()))
-      FormatRHSValueDeclForSema(IDeclExpr->getDecl(), IssueMsg);
+            dyn_cast<DeclRefExpr>(IssueExpr->IgnoreParenCasts())) {
+      formatRHSValueDeclForSema(IDeclExpr->getDecl(), IssueMsg);
+      return;
+    }
   }
 
-  if (const auto *IssueParmDecl =
-          llvm::dyn_cast<const ParmVarDecl *>(IssueEntity))
-    FormatRHSValueDeclForSema(IssueParmDecl, IssueMsg);
+  if (const auto *IssueParmDecl = dyn_cast<const ParmVarDecl *>(IssueEntity))
+    formatRHSValueDeclForSema(IssueParmDecl, IssueMsg);
   else if (const auto *IssueCXXMD =
-               llvm::dyn_cast<const CXXMethodDecl *>(IssueEntity))
-    FormatRHSValueDeclForSema(IssueCXXMD, IssueMsg);
+               dyn_cast<const CXXMethodDecl *>(IssueEntity))
+    formatRHSValueDeclForSema(IssueCXXMD, IssueMsg);
   else
     IssueMsg.append(Temp.begin(), Temp.end());
 }
 
-void FormatSrcExprForSema(
+/// Recursively parse the `SrcExpr` and issue corresponding warnings based on
+/// its type.
+///
+/// Most expressions handled here are obtained from `getPureSrcExpr`, but we
+/// must account for additional expression types encountered during recursion.
+///
+/// Certain implicit expressions, such as `CXXBindTemporaryExpr` and
+/// `CXXConstructExpr`, are allowed and should be skipped without performing
+/// `Expr->getExprLoc().isValid()` validation.
+void formatSrcExprForSema(
     const Expr *SrcExpr,
     llvm::SmallVectorImpl<ExprPrintingResult> &SrcMsgList) {
   if (!SrcExpr)
     return;
   const Expr *PureExpr = SrcExpr->IgnoreParenCasts();
-  if (!PureExpr)
-    return;
 
   const auto AppendSubExprIfNeeded = [&SrcMsgList](const Expr *SubExpr) {
     if (SubExpr && !llvm::isa<DeclRefExpr>(SubExpr->IgnoreParenCasts())) {
-      FormatSrcExprForSema(SubExpr, SrcMsgList);
+      formatSrcExprForSema(SubExpr, SrcMsgList);
     }
   };
 
-  if (const auto *IOpCallExpr = llvm::dyn_cast<CXXOperatorCallExpr>(PureExpr);
-      IOpCallExpr && !IOpCallExpr->getExprLoc().isInvalid()) {
-    SrcMsgList.push_back({"expression", IOpCallExpr->getArg(0)});
-  } else if (const auto *IDeclExpr = llvm::dyn_cast<DeclRefExpr>(PureExpr);
-             IDeclExpr && !IDeclExpr->getExprLoc().isInvalid()) {
-    SrcMsgList.push_back({"", IDeclExpr});
-  } else if (const auto *ICXXCallExpr =
-                 llvm::dyn_cast<CXXMemberCallExpr>(PureExpr);
-             ICXXCallExpr && !ICXXCallExpr->getExprLoc().isInvalid()) {
-    if (!ICXXCallExpr->getCallee()->getExprLoc().isInvalid())
-      SrcMsgList.push_back({"function call result", ICXXCallExpr});
-    AppendSubExprIfNeeded(ICXXCallExpr->getImplicitObjectArgument());
-  } else if (const auto *ICallExpr = llvm::dyn_cast<CallExpr>(PureExpr);
-             ICallExpr && !ICallExpr->getExprLoc().isInvalid()) {
-    if (!ICallExpr->getCallee()->getExprLoc().isInvalid())
-      SrcMsgList.push_back({"function call result", ICallExpr});
-    AppendSubExprIfNeeded(ICallExpr->getCallee());
-  } else if (const auto *IMemberExpr = llvm::dyn_cast<MemberExpr>(PureExpr);
-             IMemberExpr && !IMemberExpr->getExprLoc().isInvalid()) {
-    SrcMsgList.push_back({"member access", IMemberExpr});
-    AppendSubExprIfNeeded(IMemberExpr->getBase());
-  } else if (const auto *ICCExpr = llvm::dyn_cast<CXXConstructExpr>(PureExpr);
-             ICCExpr && ICCExpr->getNumArgs() > 0) {
-    AppendSubExprIfNeeded(ICCExpr->getArg(0));
-  } else if (const auto *ITempExpr =
-                 llvm::dyn_cast<CXXBindTemporaryExpr>(PureExpr)) {
-    AppendSubExprIfNeeded(ITempExpr->getSubExpr());
+  if (const auto *SrcCXXOpCallExpr =
+          dyn_cast_or_null<CXXOperatorCallExpr>(PureExpr);
+      SrcCXXOpCallExpr && SrcCXXOpCallExpr->getExprLoc().isValid()) {
+    SrcMsgList.push_back({"expression", SrcCXXOpCallExpr->getArg(0)});
+  } else if (const auto *SrcDeclRefExpr =
+                 dyn_cast_or_null<DeclRefExpr>(PureExpr);
+             SrcDeclRefExpr && SrcDeclRefExpr->getExprLoc().isValid()) {
+    SrcMsgList.push_back({"", SrcDeclRefExpr});
+  } else if (const auto *SrcCXXMemberCallExpr =
+                 dyn_cast_or_null<CXXMemberCallExpr>(PureExpr);
+             SrcCXXMemberCallExpr &&
+             SrcCXXMemberCallExpr->getExprLoc().isValid()) {
+    if (SrcCXXMemberCallExpr->getCallee()->getExprLoc().isValid())
+      SrcMsgList.push_back({"function call result", SrcCXXMemberCallExpr});
+    AppendSubExprIfNeeded(SrcCXXMemberCallExpr->getImplicitObjectArgument());
+  } else if (const auto *SrcCallExpr = dyn_cast_or_null<CallExpr>(PureExpr);
+             SrcCallExpr && SrcCallExpr->getExprLoc().isValid()) {
+    if (SrcCallExpr->getCallee()->getExprLoc().isValid())
+      SrcMsgList.push_back({"function call result", SrcCallExpr});
+    AppendSubExprIfNeeded(SrcCallExpr->getCallee());
+  } else if (const auto *SrcMemberExpr = dyn_cast_or_null<MemberExpr>(PureExpr);
+             SrcMemberExpr && SrcMemberExpr->getExprLoc().isValid() &&
+             !SrcMemberExpr->getMemberDecl()->isImplicit()) {
+    SrcMsgList.push_back({"member access", SrcMemberExpr});
+    AppendSubExprIfNeeded(SrcMemberExpr->getBase());
+  } else if (const auto *SrcCXXConExpr =
+                 dyn_cast_or_null<CXXConstructExpr>(PureExpr);
+             SrcCXXConExpr && SrcCXXConExpr->getNumArgs() > 0) {
+    AppendSubExprIfNeeded(SrcCXXConExpr->getArg(0));
+  } else if (const auto *SrcCXXBindTempExpr =
+                 dyn_cast_or_null<CXXBindTemporaryExpr>(PureExpr)) {
+    AppendSubExprIfNeeded(SrcCXXBindTempExpr->getSubExpr());
+  } else if (const auto *SrcArraySubExpr =
+                 dyn_cast_or_null<ArraySubscriptExpr>(PureExpr);
+             SrcArraySubExpr && SrcArraySubExpr->getExprLoc().isValid()) {
+    SrcMsgList.push_back({"array subscript access", SrcArraySubExpr});
+    AppendSubExprIfNeeded(SrcArraySubExpr->getBase());
+  } else if (const auto *SrcUnaryOpExpr =
+                 dyn_cast_or_null<UnaryOperator>(PureExpr)) {
+    AppendSubExprIfNeeded(SrcUnaryOpExpr->getSubExpr());
   }
 }
 } // namespace clang::lifetimes
@@ -333,18 +364,15 @@ void getAliasList(const AssignmentQueryContext &Context,
                   const CFGBlock *StartBlock, const Expr *IssueExpr) {
   llvm::SmallVector<OriginID, 4> TargetOIDList;
 
-  if (const auto *UF = llvm::dyn_cast<UseFact>(CausingFact))
+  if (const auto *UF = dyn_cast<UseFact>(CausingFact))
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       TargetOIDList.push_back(Cur->getOuterOriginID());
-  else if (const auto *RetEscapeF =
-               llvm::dyn_cast<ReturnEscapeFact>(CausingFact))
+  else if (const auto *RetEscapeF = dyn_cast<ReturnEscapeFact>(CausingFact))
     TargetOIDList.push_back(RetEscapeF->getEscapedOriginID());
-  else if (const auto *FieldEscapeF =
-               llvm::dyn_cast<FieldEscapeFact>(CausingFact))
+  else if (const auto *FieldEscapeF = dyn_cast<FieldEscapeFact>(CausingFact))
     TargetOIDList.push_back(FieldEscapeF->getEscapedOriginID());
-  else if (const auto *GlobalEscapeF =
-               llvm::dyn_cast<GlobalEscapeFact>(CausingFact))
+  else if (const auto *GlobalEscapeF = dyn_cast<GlobalEscapeFact>(CausingFact))
     TargetOIDList.push_back(GlobalEscapeF->getEscapedOriginID());
   else
     llvm_unreachable("Without a corresponding Fact handler, assignment history "
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 09eddd9f21888..ec0663eb861f9 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -39,7 +39,7 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) {
 }
 
 inline __attribute__((always_inline)) void
-FormatLHSValueDeclForSema(const ValueDecl *TargetValue,
+formatLHSValueDeclForSema(const ValueDecl *TargetValue,
                           llvm::SmallVectorImpl<char> &LHSMsg) {
   if (TargetValue) {
     const llvm::StringRef PrefixStr = "variable '";
@@ -56,17 +56,18 @@ inline void reportAssignmentImpl(Sema &S, LoanEntity IssueEntity,
   llvm::SmallString<32> IssueMsg;
   llvm::SmallString<32> LHSMsg;
   llvm::SmallVector<ExprPrintingResult> SrcMsgList;
-  FormatLoanEntityForSema(IssueEntity, IssueMsg);
-  FormatLHSValueDeclForSema(LHS, LHSMsg);
-  FormatSrcExprForSema(RHS, SrcMsgList);
+  formatLoanEntityForSema(IssueEntity, IssueMsg);
+  formatLHSValueDeclForSema(LHS, LHSMsg);
+  formatSrcExprForSema(RHS, SrcMsgList);
 
   if (SrcMsgList.size() == 1 &&
       llvm::isa<DeclRefExpr>(SrcMsgList[0].CurrExpr)) {
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
         << LHSMsg << IssueMsg;
   } else {
-    for (const auto &SrcMsg : llvm::reverse(SrcMsgList))
-      S.Diag(RHS->getBeginLoc(), diag::note_lifetime_safety_note_alias_chain)
+    for (const ExprPrintingResult &SrcMsg : llvm::reverse(SrcMsgList))
+      S.Diag(SrcMsg.CurrExpr->getExprLoc(),
+             diag::note_lifetime_safety_note_alias_chain)
           << SrcMsg.CurrExpr->getSourceRange() << SrcMsg.Str << IssueMsg;
     S.Diag(LHSExploc, diag::note_lifetime_safety_note_alias_chain)
         << LHSMsg << IssueMsg;
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index e35b6ff3dcf53..23540584e80f0 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -366,9 +366,8 @@ int &doNotFollowReferencesForLocalOwner() {
 // Warning caught by CFG analysis.
   std::unique_ptr<int> localOwner;
   int &p = *localOwner // cfg-warning {{address of stack memory is returned later}} \
-                       // cfg-note {{function call result aliases the storage of 'localOwner'}} \
                        // cfg-note {{variable 'p' aliases the storage of 'localOwner'}}
-            .get();
+            .get();    // cfg-note {{function call result aliases the storage of 'localOwner'}}
   return p; // cfg-note {{returned here}}
 }
 
@@ -1117,6 +1116,7 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
                                                         // cfg-note {{function call result aliases the storage of the temporary}} \
                                                         // cfg-note {{variable 'y' aliases the storage of the temporary}}
   const std::string& z = (*temporary().begin()).second; // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                                        // cfg-note {{member access aliases the storage of the temporary}} \
                                                         // cfg-note {{expression aliases the storage of the temporary}} \
                                                         // cfg-note {{variable 'z' aliases the storage of the temporary}}
 
@@ -1168,12 +1168,14 @@ std::string_view foo(std::string_view sv [[clang::lifetimebound]]);
 void test1() {
   std::string_view k1 = S().sv; // OK
   std::string_view k2 = S().s; // expected-warning {{object backing the pointer will}} \
+                               // cfg-note {{member access aliases the storage of the temporary}} \
                                // cfg-note {{variable 'k2' aliases the storage of the temporary}} \
                                // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
 
   std::string_view k3 = Q().get()->sv; // OK
   std::string_view k4  = Q().get()->s; // expected-warning {{object backing the pointer will}} \
                                        // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                                       // cfg-note {{member access aliases the storage of the temporary}} \
                                        // cfg-note {{function call result aliases the storage of the temporary}} \
                                        // cfg-note {{variable 'k4' aliases the storage of the temporary}}
 
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 47183629a9605..9c775e179502d 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1292,10 +1292,9 @@ void foobar() {
   View view;
   {
     StatusOr<MyObj> string_or = getStringOr();
-    view = string_or. // expected-warning {{object whose reference is captured does not live long enough}} \\
-                      // expected-note {{function call result aliases the storage of 'string_or'}} \\
+    view = string_or. // expected-warning {{object whose reference is captured does not live long enough}} \
                       // expected-note {{variable 'view' aliases the storage of 'string_or'}}
-            value();
+            value();  // expected-note {{function call result aliases the storage of 'string_or'}}
   }                     // expected-note {{destroyed here}}
   (void)view;           // expected-note {{later used here}}
 }
@@ -1831,7 +1830,8 @@ void uaf() {
     S str;
     S* p = &str;  // expected-warning {{object whose reference is captured does not live long enough}} \
                   // expected-note {{variable 'p' aliases the storage of 'str'}}
-    view = p->s;  // expected-note {{variable 'view' aliases the storage of 'str'}}
+    view = p->s;  // expected-note {{member access aliases the storage of 'str'}} \
+                  // expected-note {{variable 'view' aliases the storage of 'str'}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1858,7 +1858,8 @@ void uaf_union() {
     U u = U{"hello"};
     U* up = &u;  // expected-warning {{object whose reference is captured does not live long enough}} \
                  // expected-note {{variable 'up' aliases the storage of 'u'}}
-    view = up->s; // expected-note {{variable 'view' aliases the storage of 'u'}}
+    view = up->s; // expected-note {{member access aliases the storage of 'u'}} \
+                  // expected-note {{variable 'view' aliases the storage of 'u'}}
   } // expected-note {{destroyed here}}
   (void)view;  // expected-note {{later used here}}
 }
@@ -1876,7 +1877,8 @@ void uaf_anonymous_union() {
     AnonymousUnion au;
     AnonymousUnion* up = &au;  // expected-warning {{object whose reference is captured does not live long enough}} \
                                // expected-note {{variable 'up' aliases the storage of 'au'}}
-    ip = &up->x; // expected-note {{variable 'ip' aliases the storage of 'au'}}
+    ip = &up->x;               // expected-note {{member access aliases the storage of 'au'}} \
+                               // expected-note {{variable 'ip' aliases the storage of 'au'}}
   } // expected-note {{destroyed here}}
   (void)ip;  // expected-note {{later used here}}
 }
@@ -2247,6 +2249,7 @@ void element_use_after_scope() {
   {
     int a[10]{};
     p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} \
+               // expected-note {{array subscript access aliases the storage of 'a'}} \
                // expected-note {{variable 'p' aliases the storage of 'a'}}
   }            // expected-note {{destroyed here}}
   (void)*p;    // expected-note {{later used here}}
@@ -2255,6 +2258,7 @@ void element_use_after_scope() {
 int* element_use_after_return() {
   int a[10]{};
   int* p = &a[0]; // expected-warning {{address of stack memory is returned later}} \
+                  // expected-note {{array subscript access aliases the storage of 'a'}} \
                   // expected-note {{variable 'p' aliases the storage of 'a'}}
   return p;       // expected-note {{returned here}}
 }
@@ -2281,6 +2285,7 @@ void multidimensional_use_after_scope() {
   {
     int a[3][4]{};
     p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} \
+                  // expected-note 2 {{array subscript access aliases the storage of 'a'}} \
                   // expected-note {{variable 'p' aliases the storage of 'a'}}
   }               // expected-note {{destroyed here}}
   (void)*p;       // expected-note {{later used here}}
@@ -2295,6 +2300,8 @@ void member_array_element_use_after_scope() {
   {
     S s;
     p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} \
+                   // expected-note {{array subscript access aliases the storage of 's'}} \
+                   // expected-note {{member access aliases the storage of 's'}} \
                    // expected-note {{variable 'p' aliases the storage of 's'}}
   }                // expected-note {{destroyed here}}
   (void)*p;        // expected-note {{later used here}}
@@ -2315,6 +2322,7 @@ void reversed_subscript_use_after_scope() {
   {
     int a[10]{};
     p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} \
+                 // expected-note {{array subscript access aliases the storage of 'a'}} \
                  // expected-note {{variable 'p' aliases the storage of 'a'}}
   }              // expected-note {{destroyed here}}
   (void)*p;      // expected-note {{later used here}}

>From 5b0183ec6d23d556774f774bd4dc4e77c8ff2de9 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 14 Apr 2026 14:37:48 +0800
Subject: [PATCH 18/22] fix merge conflicts

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   2 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 113 ++++++++++++++----
 clang/lib/Sema/SemaLifetimeSafety.h           |   5 +
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |   6 +-
 .../Sema/warn-lifetime-safety-suggestions.cpp |   8 +-
 clang/test/Sema/warn-lifetime-safety.cpp      |  10 +-
 6 files changed, 110 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 4310ada231439..3708f52f4378f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -100,7 +100,7 @@ class LifetimeSafetySemaHelper {
   // Suggests lifetime bound annotations for function paramters.
   virtual void suggestLifetimeboundToParmVar(
       SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate,
-      EscapingTarget Target) {}
+      llvm::ArrayRef<AssignmentPair> AliasList, EscapingTarget Target) {}
 
   // Reports misuse of [[clang::noescape]] when parameter escapes through return
   virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 6a1424c4eac47..d29bce5495cf1 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -57,12 +57,15 @@ struct PendingWarning {
 using AnnotationTarget =
     llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
 using EscapingTarget = LifetimeSafetySemaHelper::EscapingTarget;
+using EscapingFact =
+    llvm::PointerUnion<const ReturnEscapeFact *, const FieldEscapeFact *,
+                       const GlobalEscapeFact *>;
 
 class LifetimeChecker {
 private:
   llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
-  llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
-  llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
+  llvm::DenseMap<AnnotationTarget, EscapingFact> AnnotationWarningsMap;
+  llvm::DenseMap<const ParmVarDecl *, EscapingFact> NoescapeWarningsMap;
   const LoanPropagationAnalysis &LoanPropagation;
   const MovedLoansAnalysis &MovedLoans;
   const LiveOriginsAnalysis &LiveOrigins;
@@ -122,21 +125,21 @@ class LifetimeChecker {
       // NoEscape param should not escape.
       if (PVD->hasAttr<NoEscapeAttr>()) {
         if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
-          NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
+          NoescapeWarningsMap.try_emplace(PVD, ReturnEsc);
         if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
-          NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
+          NoescapeWarningsMap.try_emplace(PVD, FieldEsc);
         if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
-          NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
+          NoescapeWarningsMap.try_emplace(PVD, GlobalEsc);
         return;
       }
       // Suggest lifetimebound for parameter escaping through return or a field
       // in constructor.
       if (!PVD->hasAttr<LifetimeBoundAttr>()) {
         if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
-          AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
+          AnnotationWarningsMap.try_emplace(PVD, ReturnEsc);
         else if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF);
                  FieldEsc && isa<CXXConstructorDecl>(FD))
-          AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
+          AnnotationWarningsMap.try_emplace(PVD, FieldEsc);
       }
       // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a
       // field!
@@ -144,7 +147,7 @@ class LifetimeChecker {
     auto CheckImplicitThis = [&](const CXXMethodDecl *MD) {
       if (!implicitObjectParamIsLifetimeBound(MD))
         if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
-          AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
+          AnnotationWarningsMap.try_emplace(MD, ReturnEsc);
     };
     for (LoanID LID : EscapedLoans) {
       const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
@@ -333,46 +336,102 @@ class LifetimeChecker {
     return nullptr;
   }
 
+  EscapingTarget formatEscapingFactToEscapingTarget(EscapingFact TargetFact) {
+    if (const ReturnEscapeFact *ReturnFact =
+            dyn_cast<const ReturnEscapeFact *>(TargetFact))
+      return ReturnFact->getReturnExpr();
+    if (const FieldEscapeFact *ReturnFact =
+            dyn_cast<const FieldEscapeFact *>(TargetFact))
+      return ReturnFact->getFieldDecl();
+    if (const GlobalEscapeFact *ReturnFact =
+            dyn_cast<const GlobalEscapeFact *>(TargetFact))
+      return ReturnFact->getGlobal();
+    llvm_unreachable("No corresponding processing logic");
+  }
+
   void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
-                                         const ParmVarDecl *PVD,
-                                         SourceManager &SM,
-                                         EscapingTarget EscapeTarget) {
-    if (llvm::isa<const VarDecl *>(EscapeTarget))
+                                  const ParmVarDecl *PVD, SourceManager &SM,
+                                  EscapingFact EscapeTarget) {
+    if (isa<const GlobalEscapeFact *>(EscapeTarget))
       return;
 
+    llvm::SmallVector<AssignmentPair> AssignmentList;
+
+    if (const auto *ReturnFact =
+            dyn_cast<const ReturnEscapeFact *>(EscapeTarget)) {
+      const struct AssignmentQueryContext Context = {
+          LoanPropagation, MovedLoans, LiveOrigins, FactMgr, ADC};
+      const auto *StartBlock =
+          ADC.getCFGStmtMap()->getBlock(ReturnFact->getReturnExpr());
+      std::optional<LoanID> TargetLoanID;
+
+      for (const LoanID &CurrLoanID : LoanPropagation.getLoans(
+               ReturnFact->getEscapedOriginID(), ReturnFact)) {
+        const Loan *CurrLoan = FactMgr.getLoanMgr().getLoan(CurrLoanID);
+        if (CurrLoan->getAccessPath().getAsPlaceholderParam() == PVD) {
+          TargetLoanID = CurrLoanID;
+          break;
+        }
+      }
+      getAliasList(Context, AssignmentList, ReturnFact, TargetLoanID.value(),
+                   StartBlock, nullptr);
+    }
+
     if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM))
       SemaHelper->suggestLifetimeboundToParmVar(
           SuggestionScope::CrossTU,
           CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()),
-          EscapeTarget);
+          AssignmentList, formatEscapingFactToEscapingTarget(EscapeTarget));
     else
-      SemaHelper->suggestLifetimeboundToParmVar(SuggestionScope::IntraTU, PVD,
-                                                EscapeTarget);
+      SemaHelper->suggestLifetimeboundToParmVar(
+          SuggestionScope::IntraTU, PVD, AssignmentList,
+          formatEscapingFactToEscapingTarget(EscapeTarget));
   }
 
-  static void
-  suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
-                                  const CXXMethodDecl *MD, SourceManager &SM,
-                                  const Expr *EscapeFact) {
+  void suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
+                                       const CXXMethodDecl *MD,
+                                       SourceManager &SM,
+                                       const ReturnEscapeFact *EscapeFact) {
+    const struct AssignmentQueryContext Context = {LoanPropagation, MovedLoans,
+                                                   LiveOrigins, FactMgr, ADC};
+    const auto *StartBlock =
+        ADC.getCFGStmtMap()->getBlock(EscapeFact->getReturnExpr());
+    std::optional<LoanID> TargetLoanID;
+
+    for (const LoanID &CurrLoanID : LoanPropagation.getLoans(
+             EscapeFact->getEscapedOriginID(), EscapeFact)) {
+      const Loan *CurrLoan = FactMgr.getLoanMgr().getLoan(CurrLoanID);
+      if (CurrLoan->getAccessPath().getAsPlaceholderThis() == MD) {
+        TargetLoanID = CurrLoanID;
+        break;
+      }
+    }
+
+    llvm::SmallVector<AssignmentPair> AssignmentList;
+    getAliasList(Context, AssignmentList, EscapeFact, TargetLoanID.value(),
+                 StartBlock, nullptr);
+
     if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM))
       SemaHelper->suggestLifetimeboundToImplicitThis(
           SuggestionScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl),
-          EscapeFact);
+          AssignmentList, EscapeFact->getReturnExpr());
     else
       SemaHelper->suggestLifetimeboundToImplicitThis(
-          SuggestionScope::IntraTU, MD, EscapeFact);
+          SuggestionScope::IntraTU, MD, AssignmentList,
+          EscapeFact->getReturnExpr());
   }
 
   void suggestAnnotations() {
     if (!SemaHelper)
       return;
     SourceManager &SM = AST.getSourceManager();
-    for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
+    for (auto [Target, EscapeFact] : AnnotationWarningsMap) {
       if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>())
-        suggestWithScopeForParmVar(SemaHelper, PVD, SM, EscapeTarget);
+        suggestWithScopeForParmVar(SemaHelper, PVD, SM, EscapeFact);
       else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
-        if (const auto *EscapeExpr = EscapeTarget.dyn_cast<const Expr *>())
-          suggestWithScopeForImplicitThis(SemaHelper, MD, SM, EscapeExpr);
+        if (const auto *EscapeF =
+                EscapeFact.dyn_cast<const ReturnEscapeFact *>())
+          suggestWithScopeForImplicitThis(SemaHelper, MD, SM, EscapeF);
         else
           llvm_unreachable("Implicit this can only escape via Expr (return)");
       }
@@ -380,7 +439,9 @@ class LifetimeChecker {
   }
 
   void reportNoescapeViolations() {
-    for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
+    for (auto [PVD, EscapeFact] : NoescapeWarningsMap) {
+      EscapingTarget EscapeTarget =
+          formatEscapingFactToEscapingTarget(EscapeFact);
       if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
         SemaHelper->reportNoescapeViolation(PVD, E);
       else if (const auto *FD = EscapeTarget.dyn_cast<const FieldDecl *>())
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index ec0663eb861f9..672deb7ec5b42 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H
 
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Sema/Sema.h"
@@ -206,6 +207,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
 
   void suggestLifetimeboundToParmVar(SuggestionScope Scope,
                                      const ParmVarDecl *ParmToAnnotate,
+                                     llvm::ArrayRef<AssignmentPair> AliasList,
                                      EscapingTarget Target) override {
     unsigned DiagID =
         (Scope == SuggestionScope::CrossTU)
@@ -224,6 +226,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << ParmToAnnotate->getSourceRange()
         << FixItHint::CreateInsertion(InsertionPoint, FixItText);
 
+    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+      reportAssignment(S, ParmToAnnotate, AliasStmt.first, AliasStmt.second);
+
     if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
       S.Diag(EscapeExpr->getBeginLoc(),
              diag::note_lifetime_safety_suggestion_returned_here)
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 23540584e80f0..e368c973f3eda 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -985,9 +985,11 @@ struct [[gsl::Pointer]] Pointer {
   Pointer(const Bar & bar [[clang::lifetimebound]]);
 };
 Pointer test3(Bar bar) {
-  Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                              // cfg-note {{variable 'p' aliases the storage of the temporary}}
   use(p);                     // cfg-note {{later used here}}
-  p = Pointer(Bar());         // expected-warning {{object backing}} cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  p = Pointer(Bar());         // expected-warning {{object backing}} cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \
+                              // cfg-note {{variable 'p' aliases the storage of the temporary}}
   use(p);                     // cfg-note {{later used here}}
   return bar;                 // expected-warning {{address of stack}} cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
 }
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 4bc7a9b6bbbcf..18d7d8a27b4ec 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -473,6 +473,7 @@ struct CaptureRefToView {
 CaptureRefToView test_ref_to_view() {
   MyObj obj;
   CaptureRefToView x(obj); // expected-warning {{address of stack memory is returned later}}
+                           // expected-note at -1 {{variable 'x' aliases the storage of 'obj'}}
   return x; // expected-note {{returned here}}
 }
 
@@ -484,6 +485,7 @@ struct CaptureRefToPtr {
 CaptureRefToPtr test_ref_to_ptr() {
   MyObj obj;
   CaptureRefToPtr x(obj); // expected-warning {{address of stack memory is returned later}}
+                          // expected-note at -1 {{variable 'x' aliases the storage of 'obj'}}
   return x; // expected-note {{returned here}}
 }
 
@@ -495,7 +497,8 @@ struct CaptureViewToView {
 CaptureViewToView test_view_to_view() {
   MyObj obj;
   View v(obj); // expected-warning {{address of stack memory is returned later}}
-  CaptureViewToView x(v);
+               // expected-note at -1 {{variable 'v' aliases the storage of 'obj'}}
+  CaptureViewToView x(v); // expected-note {{variable 'x' aliases the storage of 'obj'}}
   return x; // expected-note {{returned here}}
 }
 
@@ -507,6 +510,7 @@ struct CapturePtrToPtr {
 CapturePtrToPtr test_ptr_to_ptr() {
   MyObj obj;
   CapturePtrToPtr x(&obj); // expected-warning {{address of stack memory is returned later}}
+                           // expected-note at -1 {{variable 'x' aliases the storage of 'obj'}}
   return x; // expected-note {{returned here}}
 }
 
@@ -518,6 +522,7 @@ struct CaptureRefToRef {
 CaptureRefToRef test_ref_to_ref() {
   MyObj obj;
   CaptureRefToRef x(obj); // expected-warning {{address of stack memory is returned later}}
+                          // expected-note at -1 {{variable 'x' aliases the storage of 'obj'}}
   return x; // expected-note {{returned here}}
 }
 
@@ -533,6 +538,7 @@ struct CaptureRefToBaseView : BaseWithView {
 CaptureRefToBaseView test_ref_to_base_view() {
   MyObj obj;
   CaptureRefToBaseView x(obj); // expected-warning {{address of stack memory is returned later}}
+                               // expected-note at -1 {{variable 'x' aliases the storage of 'obj'}}
   return x; // expected-note {{returned here}}
 }
 } // namespace capturing_constructor
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 9c775e179502d..99ea552da67d9 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2746,7 +2746,8 @@ void local_pointer() {
   Pointer<int> p;
   {
     int v;
-    p = Pointer(v); // expected-warning {{object whose reference is captured does not live long enough}}
+    p = Pointer(v); // expected-warning {{object whose reference is captured does not live long enough}} \
+                    // expected-note {{variable 'p' aliases the storage of 'v'}}
   }                 // expected-note {{destroyed here}}
   use(*p);          // expected-note {{later used here}}
 }
@@ -2757,9 +2758,10 @@ void nested_local_pointer() {
   Pointer<Bar> p;
   {
     Bar v;
-    p = Pointer(v);     // expected-warning {{object whose reference is captured does not live long enough}}
-    pp = Pointer(p);
-    ppp = Pointer(pp);
+    p = Pointer(v);     // expected-warning {{object whose reference is captured does not live long enough}} \
+                        // expected-note {{variable 'p' aliases the storage of 'v'}}
+    pp = Pointer(p);    // expected-note {{variable 'pp' aliases the storage of 'v'}}
+    ppp = Pointer(pp);  // expected-note {{variable 'ppp' aliases the storage of 'v'}}
   }                     // expected-note {{destroyed here}}
   use(***ppp);          // expected-note {{later used here}}
 }

>From 1b2b2d350b75bc21951fcb375210702908dffa36 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 14 Apr 2026 15:26:58 +0800
Subject: [PATCH 19/22] rebase to fix ci fail

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 clang/test/Sema/warn-lifetime-safety.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 99ea552da67d9..8a19afe4a941e 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2842,7 +2842,8 @@ struct S {
   void foo() {
     {
       int num;
-      this->p_ = # // expected-warning {{object whose reference is captured does not live long enough}}
+      this->p_ = # // expected-warning {{object whose reference is captured does not live long enough}} \
+                       // expected-note {{variable 'p_' aliases the storage of 'num'}}
     }                  // expected-note {{destroyed here}}
     bar();             // expected-note {{later used here}}
     this->p_ = &GLOBAL_INT;
@@ -2861,7 +2862,8 @@ struct T {
   std::string_view v;
   void bar();
   void foo() {
-    v = std::string("tmp"); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}}
+    v = std::string("tmp"); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} \
+                            // expected-note {{variable 'v' aliases the storage of the temporary}}
     bar();                  // expected-note {{later used here}}
   }
 };

>From 73c081c21dc606b0e25497e7e97223a3c39f813b Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 14 Apr 2026 16:11:24 +0800
Subject: [PATCH 20/22] reafctor getAliasListInMultiBlock to use correct BFS

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../Analyses/LifetimeSafety/AssignmentQuery.h |   4 +-
 .../LifetimeSafety/AssignmentQuery.cpp        | 103 +++++++++++-------
 clang/lib/Sema/SemaLifetimeSafety.h           |  12 +-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |  16 ++-
 clang/test/Sema/warn-lifetime-safety.cpp      |  23 +++-
 5 files changed, 102 insertions(+), 56 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
index 61a5bcfdbdfb7..3232ff1ef25ed 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h
@@ -41,8 +41,8 @@ namespace clang::lifetimes::internal {
 
 struct AliasAssignmentSearchResult {
   const bool SearchComplete;
-  const OriginDestExpr LastDestDecl;
-  const std::optional<OriginID> LastOrigin;
+  const OriginDestExpr LastDestDeclOrExpr;
+  const std::optional<OriginID> IssueOriginID;
 };
 
 struct AssignmentQueryContext {
diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 304cd4d36651a..9311a566912e8 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -18,7 +18,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
-#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
 #include <cstddef>
 #include <queue>
 
@@ -28,6 +28,27 @@ using namespace clang;
 using namespace clang::lifetimes;
 using namespace clang::lifetimes::internal;
 
+struct AssignmentSearchToken {
+  const CFGBlock *Block;
+  const OriginID OID;
+
+  bool operator==(const AssignmentSearchToken& Other) const {
+    return Block == Other.Block && OID == Other.OID;
+  }
+  bool operator<(const AssignmentSearchToken& Other) const {
+    if (Block == Other.Block)
+      return OID < Other.OID;
+    return Block < Other.Block;
+  }
+};
+
+struct AssignmentSearchContext {
+  const AssignmentSearchToken CurrToken;
+  const OriginDestExpr LastDestDeclOrExpr;
+  const std::optional<AssignmentPair> LastEndAssignment;
+  const std::optional<OriginID> LastOriginID;
+};
+
 /// Locate the rightmost sub expression of the RHS, given that the LHS is
 /// already known. To ensure printability, we invoke `Explorc->isValid()`.
 ///
@@ -126,9 +147,9 @@ getAliasListCore(const AssignmentQueryContext &Context,
                  const CFGBlock *Block, const LoanID EndLoanID,
                  OriginID *TargetOID,
                  const OriginDestExpr LastDestExpr = nullptr,
-                 const std::optional<OriginID> LastOriginID = std::nullopt) {
+                 const std::optional<OriginID> LastIssueOriginID = std::nullopt) {
   OriginDestExpr CurrDestExpr = LastDestExpr;
-  std::optional<OriginID> IssueOriginID = LastOriginID;
+  std::optional<OriginID> IssueOriginID = LastIssueOriginID;
   std::optional<OriginID> CurrOrigin = std::nullopt;
   llvm::ArrayRef<const Fact *> Facts = Context.FactMgr.getFacts(Block);
   bool NeedSearchOriginDestWithoutLoan = false;
@@ -172,7 +193,7 @@ getAliasListCore(const AssignmentQueryContext &Context,
   for (const Fact *F : llvm::reverse(Facts)) {
     if (const auto *OFF = F->getAs<OriginFlowFact>()) {
       if (IssueOriginID && OFF->getDestOriginID() == IssueOriginID.value())
-        return {true, CurrDestExpr, IssueOriginID};
+        return {true, CurrDestExpr, std::nullopt};
       TryInsertAssignmentList(OFF);
     } else if (const auto *IF = F->getAs<IssueFact>()) {
       if (IF->getLoanID() == EndLoanID)
@@ -192,51 +213,52 @@ getAliasListCore(const AssignmentQueryContext &Context,
 void getAliasListInMultiBlock(
     const AssignmentQueryContext &Context,
     llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
-    const CFGBlock *StartBlock, const LoanID EndLoanID, OriginID *StartOID) {
-  OriginDestExpr LastDestDeclOrExpr = nullptr;
-  std::queue<const CFGBlock *> PendingBlocks;
-  std::optional<OriginID> LastOriginID = std::nullopt;
-  std::optional<AssignmentPair> StartStmt = std::nullopt;
-  std::optional<AssignmentPair> EndStmt = std::nullopt;
-  llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
+    const CFGBlock *StartBlock, const LoanID EndLoanID, const OriginID StartOID) {
+  std::queue<AssignmentSearchContext> PendingBlocks;
+  std::optional<AssignmentPair> FinalAssignment = std::nullopt;
+  llvm::SmallSet<AssignmentSearchToken, 32> VistedBlocks;
   llvm::DenseMap<AssignmentPair, AssignmentPair> VistedAssignmentExprs;
 
   const auto AliasStmtFilter = [&VistedAssignmentExprs,
-                                &AssignmentList](const AssignmentPair StartStmt,
-                                                 const AssignmentPair EndStmt) {
-    for (AssignmentPair Stmt = StartStmt; Stmt != EndStmt;
-         Stmt = VistedAssignmentExprs.at(Stmt))
-      AssignmentList.push_back(Stmt);
-    AssignmentList.push_back(EndStmt);
+                                &AssignmentList](const AssignmentPair EndAssignment) {
+    AssignmentPair CurrAssignment = EndAssignment;
+    while (true) {
+      AssignmentList.push_back(CurrAssignment);
+      const auto NextAssignment = VistedAssignmentExprs.find(CurrAssignment);
+      if (NextAssignment == VistedAssignmentExprs.end())
+          break;
+      CurrAssignment = NextAssignment->second;
+    }
   };
 
-  PendingBlocks.push(StartBlock);
+  AssignmentSearchToken StartToken = {StartBlock, StartOID};
+  AssignmentSearchContext StartContext = {StartToken, nullptr, std::nullopt, std::nullopt};
+  PendingBlocks.push(StartContext);
 
   while (!PendingBlocks.empty()) {
-    const CFGBlock *CurrBlock = PendingBlocks.front();
+    const AssignmentSearchContext CurrContext = PendingBlocks.front();
     PendingBlocks.pop();
+
+    std::optional<AssignmentPair> EndAssignment = std::nullopt;
     llvm::SmallVector<AssignmentPair> BlockAliasList;
+    OriginID CurrOID = CurrContext.CurrToken.OID;
 
     const AliasAssignmentSearchResult Result =
-        getAliasListCore(Context, BlockAliasList, CurrBlock, EndLoanID,
-                         StartOID, LastDestDeclOrExpr, LastOriginID);
-    if (Result.LastDestDecl)
-      LastDestDeclOrExpr = Result.LastDestDecl;
-    if (Result.LastOrigin)
-      LastOriginID = Result.LastOrigin;
+        getAliasListCore(Context, BlockAliasList, CurrContext.CurrToken.Block, EndLoanID,
+                         &CurrOID, CurrContext.LastDestDeclOrExpr, CurrContext.LastOriginID);
 
     if (!BlockAliasList.empty()) {
-      if (VistedAssignmentExprs.empty())
-        StartStmt = BlockAliasList[0];
-
       for (size_t i = 0; i < BlockAliasList.size() - 1; ++i)
         VistedAssignmentExprs.insert(
-            {BlockAliasList[i], BlockAliasList[i + 1]});
+            {BlockAliasList[i + 1], BlockAliasList[i]});
 
-      if (EndStmt)
-        VistedAssignmentExprs.insert({EndStmt.value(), BlockAliasList[0]});
+      if (CurrContext.LastEndAssignment)
+        VistedAssignmentExprs.insert({BlockAliasList[0], CurrContext.LastEndAssignment.value()});
 
-      EndStmt = BlockAliasList[BlockAliasList.size() - 1];
+      EndAssignment = BlockAliasList.back();
+      FinalAssignment = BlockAliasList.back();
+    } else {
+      EndAssignment = CurrContext.LastEndAssignment;
     }
 
     // TODO: The number of CFGBlocks is limited to 32 to minmize performance
@@ -247,13 +269,17 @@ void getAliasListInMultiBlock(
     if (Result.SearchComplete || VistedBlocks.size() >= 32)
       break;
 
-    for (const CFGBlock *NextBlock : CurrBlock->preds())
-      if (NextBlock && VistedBlocks.insert(NextBlock).second)
-        PendingBlocks.push(NextBlock);
+    for (const CFGBlock *NextBlock : CurrContext.CurrToken.Block->preds()) {
+      AssignmentSearchToken NextToken = {NextBlock, CurrOID};
+      if (NextBlock && VistedBlocks.insert(NextToken).second) {
+        AssignmentSearchContext NextContext = {NextToken, Result.LastDestDeclOrExpr, EndAssignment, Result.IssueOriginID};
+        PendingBlocks.push(NextContext);
+      }
+    }
   }
 
-  if (StartStmt && EndStmt)
-    AliasStmtFilter(StartStmt.value(), EndStmt.value());
+  if (FinalAssignment)
+    AliasStmtFilter(FinalAssignment.value());
 }
 
 void formatRHSValueDeclForSema(const ValueDecl *TargetValue,
@@ -384,9 +410,10 @@ void getAliasList(const AssignmentQueryContext &Context,
   for (OriginID TargetOID : TargetOIDList) {
     if (StartBlock == EndBlock) {
       getAliasListCore(Context, AssignmentList, StartBlock, End, &TargetOID);
+      std::reverse(AssignmentList.begin(), AssignmentList.end());
     } else {
       getAliasListInMultiBlock(Context, AssignmentList, StartBlock, End,
-                               &TargetOID);
+                               TargetOID);
     }
   }
 }
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 672deb7ec5b42..4b14058d9e73c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -110,7 +110,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
           << MovedExpr->getSourceRange();
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
 
-    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+    for (const AssignmentPair &AliasStmt : AliasList)
       reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
@@ -129,7 +129,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
 
-    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+    for (const AssignmentPair &AliasStmt : AliasList)
       reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
@@ -149,7 +149,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
 
-    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+    for (const AssignmentPair &AliasStmt : AliasList)
       reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     S.Diag(DanglingField->getLocation(),
@@ -170,7 +170,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
 
-    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+    for (const AssignmentPair &AliasStmt : AliasList)
       reportAssignment(S, IssueExpr, AliasStmt.first, AliasStmt.second);
 
     if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
@@ -226,7 +226,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << ParmToAnnotate->getSourceRange()
         << FixItHint::CreateInsertion(InsertionPoint, FixItText);
 
-    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+    for (const AssignmentPair &AliasStmt : AliasList)
       reportAssignment(S, ParmToAnnotate, AliasStmt.first, AliasStmt.second);
 
     if (const auto *EscapeExpr = Target.dyn_cast<const Expr *>())
@@ -270,7 +270,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         << FixItHint::CreateInsertion(InsertionPoint,
                                       " [[clang::lifetimebound]]");
 
-    for (const AssignmentPair &AliasStmt : llvm::reverse(AliasList))
+    for (const AssignmentPair &AliasStmt : AliasList)
       reportAssignment(S, MD, AliasStmt.first, AliasStmt.second);
 
     S.Diag(EscapeExpr->getBeginLoc(),
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index e368c973f3eda..51ef795e826bc 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1029,8 +1029,10 @@ void test4() {
 namespace range_based_for_loop_variables {
 std::string_view test_view_loop_var(std::vector<std::string> strings) {
   for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
+                                        // cfg-note {{expression aliases the storage of 'strings'}} \
                                         // cfg-note {{function call result aliases the storage of 'strings'}} \
-                                        // cfg-note {{variable '__begin1' aliases the storage of 'strings'}}
+                                        // cfg-note {{variable '__begin1' aliases the storage of 'strings'}} \
+                                        // cfg-note {{variable 's' aliases the storage of 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
@@ -1038,8 +1040,10 @@ std::string_view test_view_loop_var(std::vector<std::string> strings) {
 
 const char* test_view_loop_var_with_data(std::vector<std::string> strings) {
   for (std::string_view s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
+                                        // cfg-note {{expression aliases the storage of 'strings'}} \
                                         // cfg-note {{function call result aliases the storage of 'strings'}} \
-                                        // cfg-note {{variable '__begin1' aliases the storage of 'strings'}}
+                                        // cfg-note {{variable '__begin1' aliases the storage of 'strings'}} \
+                                        // cfg-note {{variable 's' aliases the storage of 'strings'}}
     return s.data(); //cfg-note {{returned here}}
   }
   return "";
@@ -1054,8 +1058,10 @@ std::string_view test_no_error_for_views(std::vector<std::string_view> views) {
 
 std::string_view test_string_ref_var(std::vector<std::string> strings) {
   for (const std::string& s : strings) {  // cfg-warning {{address of stack memory is returned later}} \
+                                          // cfg-note {{expression aliases the storage of 'strings'}} \
                                           // cfg-note {{function call result aliases the storage of 'strings'}} \
-                                          // cfg-note {{variable '__begin1' aliases the storage of 'strings'}}
+                                          // cfg-note {{variable '__begin1' aliases the storage of 'strings'}} \
+                                          // cfg-note {{variable 's' aliases the storage of 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
@@ -1063,9 +1069,11 @@ std::string_view test_string_ref_var(std::vector<std::string> strings) {
 
 std::string_view test_opt_strings(std::optional<std::vector<std::string>> strings_or) {
   for (const std::string& s : *strings_or) {  // cfg-warning {{address of stack memory is returned later}} \
+                                              // cfg-note {{expression aliases the storage of 'strings_or'}} \
                                               // cfg-note {{variable '__range1' aliases the storage of 'strings_or'}} \
                                               // cfg-note {{function call result aliases the storage of 'strings_or'}} \
-                                              // cfg-note {{variable '__begin1' aliases the storage of 'strings_or'}}
+                                              // cfg-note {{variable '__begin1' aliases the storage of 'strings_or'}} \
+                                              // cfg-note {{variable 's' aliases the storage of 'strings_or'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 8a19afe4a941e..f922ca2e09a0b 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -464,7 +464,7 @@ void loan_from_previous_iteration(MyObj safe, bool condition) {
                 // expected-note {{variable 'p' aliases the storage of 'x'}}
 
     if (condition)
-      q = p;
+      q = p;    // expected-note {{variable 'q' aliases the storage of 'x'}}
     (void)*p;
     (void)*q;   // expected-note {{later used here}}
   }             // expected-note {{destroyed here}}
@@ -764,7 +764,9 @@ int** test_ternary_double_ptr(bool cond) {
                  // expected-note {{variable 'pb' aliases the storage of 'b'}}
   int** result = cond ? &pa : &pb;  // expected-warning 2 {{address of stack memory is returned later}} \
                                     // expected-note {{variable 'result' aliases the storage of 'pa'}} \
-                                    // expected-note {{variable 'result' aliases the storage of 'pb'}}
+                                    // expected-note {{variable 'result' aliases the storage of 'pb'}} \
+                                    // expected-note {{variable 'result' aliases the storage of 'a'}} \
+                                    // expected-note {{variable 'result' aliases the storage of 'b'}}
   return result; // expected-note 4 {{returned here}}
 }
 //===----------------------------------------------------------------------===//
@@ -1314,6 +1316,8 @@ void range_based_for_use_after_scope() {
   {
     MyObjStorage s;
     for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} \
+                               // expected-note {{function call result aliases the storage of 's'}} \
+                               // expected-note {{variable '__begin2' aliases the storage of 's'}} \
                                // expected-note {{variable 'o' aliases the storage of 's'}}
       v = o;                   // expected-note {{variable 'v' aliases the storage of 's'}}
     }
@@ -1325,7 +1329,8 @@ View range_based_for_use_after_return() {
   MyObjStorage s;
   for (const MyObj &o : s) { // expected-warning {{address of stack memory is returned later}} \
                              // expected-note {{function call result aliases the storage of 's'}} \
-                             // expected-note {{variable '__begin1' aliases the storage of 's'}}
+                             // expected-note {{variable '__begin1' aliases the storage of 's'}} \
+                             // expected-note {{variable 'o' aliases the storage of 's'}}
     return o;  // expected-note {{returned here}}
   }
   return *s.begin();  // expected-warning {{address of stack memory is returned later}}
@@ -1732,8 +1737,10 @@ MyObj Global;
 
 const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
   for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{expression aliases the storage of 'c'}} \
                               // expected-note {{function call result aliases the storage of 'c'}} \
-                              // expected-note {{variable '__begin1' aliases the storage of 'c'}}
+                              // expected-note {{variable '__begin1' aliases the storage of 'c'}} \
+                              // expected-note {{variable 'x' aliases the storage of 'c'}}
     return x;                 // expected-note {{returned here}}
   }
   return Global;
@@ -1741,13 +1748,17 @@ const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
 
 View ContainerMyObjReturnView(Container<MyObj> c) {
   for (const MyObj& x : c) {  // expected-warning {{address of stack memory is returned later}} \
+                              // expected-note {{expression aliases the storage of 'c'}} \
                               // expected-note {{function call result aliases the storage of 'c'}} \
-                              // expected-note {{variable '__begin1' aliases the storage of 'c'}}
+                              // expected-note {{variable '__begin1' aliases the storage of 'c'}} \
+                              // expected-note {{variable 'x' aliases the storage of 'c'}}
     return x;                 // expected-note {{returned here}}
   }
   for (View x : c) {  // expected-warning {{address of stack memory is returned later}} \
+                      // expected-note {{expression aliases the storage of 'c'}} \
                       // expected-note {{function call result aliases the storage of 'c'}} \
-                      // expected-note {{variable '__begin1' aliases the storage of 'c'}}
+                      // expected-note {{variable '__begin1' aliases the storage of 'c'}} \
+                      // expected-note {{variable 'x' aliases the storage of 'c'}}
     return x;         // expected-note {{returned here}}
   }
   return Global;

>From 7b4d295186f657ad995addef738a510c542b97de Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 14 Apr 2026 19:22:49 +0800
Subject: [PATCH 21/22] fix clang-format error

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../LifetimeSafety/AssignmentQuery.cpp        | 42 ++++++++++---------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
index 9311a566912e8..f299b6d1aff10 100644
--- a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp
@@ -32,10 +32,10 @@ struct AssignmentSearchToken {
   const CFGBlock *Block;
   const OriginID OID;
 
-  bool operator==(const AssignmentSearchToken& Other) const {
+  bool operator==(const AssignmentSearchToken &Other) const {
     return Block == Other.Block && OID == Other.OID;
   }
-  bool operator<(const AssignmentSearchToken& Other) const {
+  bool operator<(const AssignmentSearchToken &Other) const {
     if (Block == Other.Block)
       return OID < Other.OID;
     return Block < Other.Block;
@@ -141,13 +141,12 @@ const Expr *getRHSExpr(const AssignmentQueryContext &Context,
   return SExpr;
 }
 
-AliasAssignmentSearchResult
-getAliasListCore(const AssignmentQueryContext &Context,
-                 llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
-                 const CFGBlock *Block, const LoanID EndLoanID,
-                 OriginID *TargetOID,
-                 const OriginDestExpr LastDestExpr = nullptr,
-                 const std::optional<OriginID> LastIssueOriginID = std::nullopt) {
+AliasAssignmentSearchResult getAliasListCore(
+    const AssignmentQueryContext &Context,
+    llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
+    const CFGBlock *Block, const LoanID EndLoanID, OriginID *TargetOID,
+    const OriginDestExpr LastDestExpr = nullptr,
+    const std::optional<OriginID> LastIssueOriginID = std::nullopt) {
   OriginDestExpr CurrDestExpr = LastDestExpr;
   std::optional<OriginID> IssueOriginID = LastIssueOriginID;
   std::optional<OriginID> CurrOrigin = std::nullopt;
@@ -213,26 +212,28 @@ getAliasListCore(const AssignmentQueryContext &Context,
 void getAliasListInMultiBlock(
     const AssignmentQueryContext &Context,
     llvm::SmallVectorImpl<AssignmentPair> &AssignmentList,
-    const CFGBlock *StartBlock, const LoanID EndLoanID, const OriginID StartOID) {
+    const CFGBlock *StartBlock, const LoanID EndLoanID,
+    const OriginID StartOID) {
   std::queue<AssignmentSearchContext> PendingBlocks;
   std::optional<AssignmentPair> FinalAssignment = std::nullopt;
   llvm::SmallSet<AssignmentSearchToken, 32> VistedBlocks;
   llvm::DenseMap<AssignmentPair, AssignmentPair> VistedAssignmentExprs;
 
-  const auto AliasStmtFilter = [&VistedAssignmentExprs,
-                                &AssignmentList](const AssignmentPair EndAssignment) {
+  const auto AliasStmtFilter = [&VistedAssignmentExprs, &AssignmentList](
+                                   const AssignmentPair EndAssignment) {
     AssignmentPair CurrAssignment = EndAssignment;
     while (true) {
       AssignmentList.push_back(CurrAssignment);
       const auto NextAssignment = VistedAssignmentExprs.find(CurrAssignment);
       if (NextAssignment == VistedAssignmentExprs.end())
-          break;
+        break;
       CurrAssignment = NextAssignment->second;
     }
   };
 
   AssignmentSearchToken StartToken = {StartBlock, StartOID};
-  AssignmentSearchContext StartContext = {StartToken, nullptr, std::nullopt, std::nullopt};
+  AssignmentSearchContext StartContext = {StartToken, nullptr, std::nullopt,
+                                          std::nullopt};
   PendingBlocks.push(StartContext);
 
   while (!PendingBlocks.empty()) {
@@ -243,9 +244,9 @@ void getAliasListInMultiBlock(
     llvm::SmallVector<AssignmentPair> BlockAliasList;
     OriginID CurrOID = CurrContext.CurrToken.OID;
 
-    const AliasAssignmentSearchResult Result =
-        getAliasListCore(Context, BlockAliasList, CurrContext.CurrToken.Block, EndLoanID,
-                         &CurrOID, CurrContext.LastDestDeclOrExpr, CurrContext.LastOriginID);
+    const AliasAssignmentSearchResult Result = getAliasListCore(
+        Context, BlockAliasList, CurrContext.CurrToken.Block, EndLoanID,
+        &CurrOID, CurrContext.LastDestDeclOrExpr, CurrContext.LastOriginID);
 
     if (!BlockAliasList.empty()) {
       for (size_t i = 0; i < BlockAliasList.size() - 1; ++i)
@@ -253,7 +254,8 @@ void getAliasListInMultiBlock(
             {BlockAliasList[i + 1], BlockAliasList[i]});
 
       if (CurrContext.LastEndAssignment)
-        VistedAssignmentExprs.insert({BlockAliasList[0], CurrContext.LastEndAssignment.value()});
+        VistedAssignmentExprs.insert(
+            {BlockAliasList[0], CurrContext.LastEndAssignment.value()});
 
       EndAssignment = BlockAliasList.back();
       FinalAssignment = BlockAliasList.back();
@@ -272,7 +274,9 @@ void getAliasListInMultiBlock(
     for (const CFGBlock *NextBlock : CurrContext.CurrToken.Block->preds()) {
       AssignmentSearchToken NextToken = {NextBlock, CurrOID};
       if (NextBlock && VistedBlocks.insert(NextToken).second) {
-        AssignmentSearchContext NextContext = {NextToken, Result.LastDestDeclOrExpr, EndAssignment, Result.IssueOriginID};
+        AssignmentSearchContext NextContext = {
+            NextToken, Result.LastDestDeclOrExpr, EndAssignment,
+            Result.IssueOriginID};
         PendingBlocks.push(NextContext);
       }
     }

>From 731e932256abb197d92645c7bb3fa92b1a5453fc Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 14 Apr 2026 21:02:06 +0800
Subject: [PATCH 22/22] rebase to fix ci fail

Signed-off-by: suoyuan666 <suoyuan666 at s5n.xyz>
---
 .../warn-lifetime-safety-dangling-field.cpp    |  3 ++-
 .../Sema/warn-lifetime-safety-suggestions.cpp  |  4 +++-
 clang/test/Sema/warn-lifetime-safety.cpp       | 18 ++++++++++++------
 3 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
index e1216df6e666f..2a7b24ed47711 100644
--- a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
@@ -223,7 +223,8 @@ struct HasCallback {
 
   void set_callback() {
     int local;
-    callback = [&local]() { (void)local; }; // expected-warning {{address of stack memory escapes to a field}}
+    callback = [&local]() { (void)local; }; // expected-warning {{address of stack memory escapes to a field}} \
+                                            // expected-note {{variable 'callback' aliases the storage of 'local'}}
   }
 };
 
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 18d7d8a27b4ec..417a31d7048ef 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -553,7 +553,9 @@ void uaf_via_inferred_lifetimebound() {
   std::function<void()> f = []() {};
   {
     int local;
-    f = return_lambda_capturing_param(local); // expected-warning {{object whose reference is captured does not live long enough}}
+    f = return_lambda_capturing_param(local); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                              // expected-note {{function call result aliases the storage of 'local'}} \
+                                              // expected-note {{variable 'f' aliases the storage of 'local'}}
   } // expected-note {{destroyed here}}
   (void)f; // expected-note {{later used here}}
 }
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index f922ca2e09a0b..8fd2012e26e00 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2943,16 +2943,18 @@ std::function<void()> direct_return() {
 
 std::function<void()> copy_function() {
   int x;
-  std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}}
-  std::function<void()> f2 = f;
+  std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}} \
+                                                 // expected-note {{variable 'f' aliases the storage of 'x'}}
+  std::function<void()> f2 = f;                  // expected-note {{variable 'f2' aliases the storage of 'x'}}
   return f2; // expected-note {{returned here}}
 }
 
 std::function<void()> copy_assign() {
   int x;
-  std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}}
+  std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}} \
+                                                 // expected-note {{variable 'f' aliases the storage of 'x'}}
   std::function<void()> f2 = []() {};
-  f2 = f;
+  f2 = f;                                        // expected-note {{variable 'f2' aliases the storage of 'x'}}
   return f2; // expected-note {{returned here}}
 }
 
@@ -2971,7 +2973,8 @@ std::function<void()> reassign_safe_then_unsafe() {
   static int safe = 1;
   int local = 2;
   std::function<void()> f = []() { (void)safe; };
-  f = [&local]() { (void)local; }; // expected-warning {{address of stack memory is returned later}}
+  f = [&local]() { (void)local; }; // expected-warning {{address of stack memory is returned later}} \
+                                   // expected-note {{variable 'f' aliases the storage of 'local'}}
   return f; // expected-note {{returned here}}
 }
 
@@ -3014,7 +3017,9 @@ void uaf_via_lifetimebound() {
   std::function<void()> f = []() {};
   {
     int local;
-    f = capture_lifetimebound_param(local); // expected-warning {{object whose reference is captured does not live long enough}}
+    f = capture_lifetimebound_param(local); // expected-warning {{object whose reference is captured does not live long enough}} \
+                                            // expected-note {{function call result aliases the storage of 'local'}} \
+                                            // expected-note {{variable 'f' aliases the storage of 'local'}}
   } // expected-note {{destroyed here}}
   (void)f; // expected-note {{later used here}}
 }
@@ -3034,6 +3039,7 @@ struct [[gsl::Pointer]] function_ref {
 // avoid this warning for non-capturing lambdas.
 void assign_non_capturing_to_function_ref(function_ref &r) {
   r = []() {}; // expected-warning {{object whose reference is captured does not live long enough}} \
+               // expected-note {{variable 'r' aliases the storage of the temporary}} \
                // expected-note {{destroyed here}}
   (void)r; // expected-note {{later used here}}
 }



More information about the cfe-commits mailing list