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

Yuan Suo via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 31 02:53:40 PDT 2026


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

>From 2785e785ed667a1a4c8460a93727d5cda3d44f96 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Mon, 16 Mar 2026 09:55:36 +0800
Subject: [PATCH 1/4] [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 08038dd096685..f1298ec528957 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -45,6 +45,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
@@ -60,9 +65,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 6b9fa4a257397..01db51d1685dc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10999,6 +10999,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 36477c6f67b52..55617469a410a 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -56,6 +56,13 @@ using AnnotationTarget =
 using EscapingTarget =
     llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
 
+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 {
   FactManager &FactMgr;
   LifetimeSafetySemaHelper *SemaHelper;
   ASTContext &AST;
+  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()) {
+        AST(ADC.getASTContext()), ADC(ADC) {
     for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
       for (const Fact *F : FactMgr.getFacts(B))
         if (const auto *EF = F->getAs<ExpireFact>())
@@ -219,6 +227,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;
@@ -243,10 +439,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 e6f7e3d929f61..5c564ea603089 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 d58f23e4b554c..f0c7306d124a5 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -147,14 +147,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}}
 
@@ -162,16 +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-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}}
 }
@@ -185,6 +191,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}}
 }
@@ -233,11 +240,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
@@ -298,22 +307,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}}
 }
@@ -325,6 +340,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}}
     ;
 }
@@ -387,6 +403,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}}
 }
@@ -394,11 +411,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}}
 }
 
@@ -602,7 +621,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
@@ -836,7 +856,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
@@ -851,10 +872,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}}
@@ -862,10 +885,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}} \
@@ -874,9 +899,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}} \
@@ -899,10 +926,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}}
@@ -1022,9 +1051,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}}
 }
@@ -1036,9 +1068,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}}
 }
@@ -1088,16 +1123,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 22e4f3d1e487b..096161bfeed7b 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -220,19 +220,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 88a502491e6c0..fbb482cc90fa8 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -53,7 +53,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}}
 }
@@ -62,7 +63,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}}
 }
@@ -90,8 +92,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}}
 }
@@ -100,8 +103,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}}
 }
@@ -110,7 +114,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.
@@ -123,9 +128,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}}
@@ -136,11 +144,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}}
 }
@@ -149,11 +159,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}}
 }
@@ -163,7 +175,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}}
 }
@@ -173,7 +186,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}}
@@ -186,7 +200,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}}
 }
@@ -199,9 +214,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)
@@ -214,8 +231,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) {
@@ -234,7 +252,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.
@@ -247,7 +266,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}}
 }
@@ -267,7 +287,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}}
 }
@@ -278,7 +299,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;
 }
@@ -289,7 +311,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}}
     }           
   } 
@@ -302,7 +325,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}}
     }
   }
@@ -316,8 +340,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}}
@@ -326,7 +351,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}}
     }
@@ -337,7 +363,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}}
     }
   }
@@ -347,7 +374,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}}
   }
@@ -360,7 +388,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: {
@@ -379,17 +408,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}}
   }
   }
@@ -401,17 +433,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}}
   }
   }
@@ -424,7 +459,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;
@@ -437,7 +473,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}}
 }
@@ -446,7 +483,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}}
 }
@@ -638,7 +676,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}}
 }
@@ -647,7 +686,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}}
 }
@@ -673,9 +713,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}}
 }
@@ -707,8 +748,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}}
 }
@@ -786,7 +828,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}}
 }
@@ -795,7 +838,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}}
@@ -809,7 +853,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}}
@@ -822,7 +867,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}}
@@ -838,7 +884,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}}
 }
@@ -853,7 +900,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}}
 }
@@ -862,7 +910,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}}
 }
@@ -871,7 +920,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}}
 }
@@ -889,7 +939,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}}
@@ -902,9 +952,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}}
 }
@@ -994,7 +1045,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}}
 
@@ -1010,8 +1062,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}}
 }
@@ -1020,10 +1074,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}}
 }
@@ -1032,7 +1090,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}}
@@ -1042,7 +1102,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}}
@@ -1052,7 +1114,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}}
@@ -1064,29 +1130,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}}
 
@@ -1095,20 +1163,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}}
 }
 
@@ -1150,7 +1218,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}}
 }
@@ -1169,8 +1237,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}}
@@ -1190,7 +1259,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}}
@@ -1223,7 +1293,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}}
 }
@@ -1240,7 +1311,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}}
 }
@@ -1250,7 +1322,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}}
 }
@@ -1260,7 +1333,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}}
 }
@@ -1294,15 +1368,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}}
 }
@@ -1411,7 +1485,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}}
@@ -1425,7 +1500,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}}
 }
@@ -1435,7 +1510,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}}
@@ -1445,13 +1522,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}}
 }
@@ -1462,7 +1541,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}}
@@ -1484,9 +1564,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}}
@@ -1574,17 +1655,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}}
 }
@@ -1592,12 +1678,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
@@ -1613,8 +1703,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}}
 }
@@ -1639,8 +1730,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}}
 }
@@ -1656,8 +1748,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}}
 }
@@ -1715,9 +1807,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}}
@@ -1752,7 +1850,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}}
 }
@@ -1761,7 +1860,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}}
 }
@@ -1770,7 +1870,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}}
 }
@@ -1961,8 +2062,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}}
@@ -1973,7 +2075,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}}
 }
@@ -1986,7 +2089,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}}
 }
@@ -2018,7 +2122,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}}
 }
@@ -2031,7 +2136,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}}
 }
@@ -2040,7 +2146,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}}
 }
@@ -2049,7 +2156,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}}
 }
@@ -2117,9 +2225,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 50f230cf3b30f940cd6eea3432e51ad3742f8906 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 27 Mar 2026 19:47:01 +0800
Subject: [PATCH 2/4] [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 55617469a410a..023496a75fc7b 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"
@@ -56,13 +57,6 @@ using AnnotationTarget =
 using EscapingTarget =
     llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
 
-struct AliasAssignmentSearchResult {
-  const llvm::SmallVector<AssignmentPair> Payload;
-  bool SearchComplete;
-  const ValueDecl *LastDestDecl;
-  std::optional<OriginID> LastOrigin;
-};
-
 class LifetimeChecker {
 private:
   llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
@@ -227,194 +221,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;
@@ -441,9 +247,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 48f2c410464ee85a093f43ac1c5e76aae7f3b9ea Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Fri, 27 Mar 2026 21:54:48 +0800
Subject: [PATCH 3/4] [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 f1298ec528957..40bc24f87b357 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"
@@ -45,11 +46,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
@@ -65,11 +61,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 01db51d1685dc..1d39acbd4e327 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10999,7 +10999,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 023496a75fc7b..9e0c2e1ce3e67 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -249,17 +249,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 5c564ea603089..2c3f3252d7b28 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 f0c7306d124a5..201a0797efdb4 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -78,6 +78,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}}
 }
@@ -148,15 +150,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}}
 
@@ -164,20 +166,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}}
 }
@@ -191,7 +193,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}}
 }
@@ -240,13 +243,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
@@ -307,28 +312,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}}
 }
@@ -340,8 +351,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}}
     ;
 }
 
@@ -403,7 +416,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}}
 }
@@ -412,12 +425,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}}
 }
 
@@ -613,6 +627,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}}
 }
@@ -622,7 +638,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
@@ -857,7 +874,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
@@ -873,11 +891,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}}
@@ -886,11 +906,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}} \
@@ -899,11 +921,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}} \
@@ -926,12 +950,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}}
@@ -1052,11 +1078,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}}
 }
@@ -1069,11 +1100,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}}
 }
@@ -1123,20 +1161,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 096161bfeed7b..f02d11278886a 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -221,21 +221,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 fbb482cc90fa8..72f3d7f79192a 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -54,7 +54,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}}
 }
@@ -64,7 +64,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}}
 }
@@ -93,8 +93,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}}
 }
@@ -104,8 +104,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}}
 }
@@ -115,7 +115,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.
@@ -129,11 +129,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}}
@@ -145,12 +145,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}}
 }
@@ -160,12 +160,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}}
 }
@@ -176,7 +176,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}}
 }
@@ -187,7 +187,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}}
@@ -201,7 +201,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}}
 }
@@ -215,10 +215,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)
@@ -232,8 +232,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) {
@@ -253,7 +253,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.
@@ -267,7 +267,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}}
 }
@@ -288,7 +288,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}}
 }
@@ -300,7 +300,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;
 }
@@ -312,7 +312,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}}
     }           
   } 
@@ -326,7 +326,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}}
     }
   }
@@ -341,7 +341,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}}
     }
   }
@@ -352,7 +352,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}}
     }
@@ -364,7 +364,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}}
     }
   }
@@ -375,7 +375,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}}
   }
@@ -389,7 +389,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: {
@@ -409,19 +409,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}}
   }
   }
@@ -434,19 +434,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}}
   }
   }
@@ -460,7 +460,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;
@@ -474,7 +474,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}}
 }
@@ -484,7 +484,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}}
 }
@@ -677,7 +677,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}}
 }
@@ -687,7 +687,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}}
 }
@@ -715,8 +715,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}}
 }
@@ -750,7 +751,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}}
 }
@@ -829,7 +830,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}}
 }
@@ -838,8 +840,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}}
@@ -853,8 +857,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}}
@@ -868,7 +874,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}}
@@ -885,7 +892,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}}
 }
@@ -901,7 +909,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}}
 }
@@ -911,7 +919,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}}
 }
@@ -921,7 +930,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}}
 }
@@ -939,7 +949,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}}
@@ -953,9 +964,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}}
 }
@@ -969,7 +981,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}}
 }
@@ -1046,7 +1059,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}}
 
@@ -1063,9 +1076,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}}
 }
@@ -1075,13 +1088,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}}
 }
@@ -1091,8 +1104,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}}
@@ -1103,8 +1118,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}}
@@ -1115,10 +1132,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}}
@@ -1131,30 +1152,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}}
 
@@ -1163,20 +1193,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}}
 }
 
@@ -1217,8 +1253,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}}
 }
@@ -1238,8 +1276,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}}
@@ -1260,7 +1298,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}}
@@ -1294,7 +1332,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}}
 }
@@ -1312,7 +1351,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}}
 }
@@ -1323,7 +1363,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}}
 }
@@ -1334,7 +1375,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}}
 }
@@ -1486,7 +1528,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}}
@@ -1500,7 +1542,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}}
 }
@@ -1511,8 +1553,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}}
@@ -1523,14 +1565,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}}
 }
@@ -1542,7 +1586,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}}
@@ -1565,9 +1610,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}}
@@ -1657,20 +1705,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}}
 }
@@ -1680,14 +1731,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
@@ -1704,8 +1757,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}}
 }
@@ -1731,8 +1784,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}}
 }
@@ -1748,8 +1801,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}}
 }
@@ -1809,13 +1863,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}}
@@ -1851,7 +1908,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}}
 }
@@ -1861,7 +1920,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}}
 }
@@ -1871,7 +1932,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}}
 }
@@ -2063,8 +2126,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}}
@@ -2076,7 +2139,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}}
 }
@@ -2090,7 +2153,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}}
 }
@@ -2123,7 +2186,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}}
 }
@@ -2137,7 +2200,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}}
 }
@@ -2147,7 +2210,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}}
 }
@@ -2157,7 +2220,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}}
 }
@@ -2225,8 +2288,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}}
 
@@ -2251,12 +2314,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}}
 }
@@ -2270,12 +2336,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}}
 }
@@ -2284,7 +2354,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}}
 }
@@ -2293,7 +2365,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}}
 }
@@ -2315,8 +2389,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}}
 }
@@ -2330,6 +2406,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}}
@@ -2342,6 +2420,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}}
@@ -2361,6 +2441,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}}
 }
@@ -2424,6 +2506,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}}
 }
@@ -2498,6 +2582,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 4c9c1ebf55633c07fe2b5772a76d9a3107ec30a2 Mon Sep 17 00:00:00 2001
From: suoyuan666 <suoyuan666 at s5n.xyz>
Date: Tue, 31 Mar 2026 17:53:06 +0800
Subject: [PATCH 4/4] 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"



More information about the cfe-commits mailing list