[clang] [LifetimeSafety] Report the alias chain for returned stack memory (PR #226395)

Gábor Horváth via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 25 01:30:19 PDT 2026


https://github.com/Xazax-hun created https://github.com/llvm/llvm-project/pull/226395

reportUseAfterReturn did not receive the aliasing chain, so a returned dangling value was reported without saying which calls or variables carried the borrow, unlike use-after-scope. Build the chain from the escaping origin with a new buildOriginFlowChain overload for escape facts. Like the UseFact overload, it drops the casts that just load the returned variable, which would otherwise repeat the "returned here" note.

Assisted by: Opus 5.5

>From 4be6faba889dd4f6f541c160fc2d0647a2450405 Mon Sep 17 00:00:00 2001
From: Gabor Horvath <gaborh at apple.com>
Date: Fri, 25 Sep 2026 09:23:32 +0100
Subject: [PATCH] [LifetimeSafety] Report the alias chain for returned stack
 memory

reportUseAfterReturn did not receive the aliasing chain, so a returned
dangling value was reported without saying which calls or variables carried
the borrow, unlike use-after-scope. Build the chain from the escaping origin
with a new buildOriginFlowChain overload for escape facts. Like the UseFact
overload, it drops the casts that just load the returned variable, which would
otherwise repeat the "returned here" note.

Assisted by: Opus 5.5
---
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  7 +-
 .../Analyses/LifetimeSafety/LoanPropagation.h |  6 ++
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  8 +-
 .../LifetimeSafety/LoanPropagation.cpp        | 23 ++++-
 clang/lib/Sema/SemaLifetimeSafety.h           |  6 +-
 .../LifetimeSafety/annotation-suggestions.cpp |  2 +-
 .../explicit-object-param-no-crash.cpp        |  4 +-
 clang/test/Sema/LifetimeSafety/nocfg.cpp      | 48 +++++-----
 clang/test/Sema/LifetimeSafety/safety.cpp     | 90 +++++++++----------
 9 files changed, 111 insertions(+), 83 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 18e5e8473e4144..6854902059e501 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -74,11 +74,12 @@ class LifetimeSafetySemaHelper {
                                    SourceLocation FreeLoc,
                                    llvm::ArrayRef<const Expr *> ExprChain) {}
 
-  // TODO: Pass the expiry location and aliasing chain like
-  // reportUseAfterScope.
+  // TODO: Report where the object was destroyed when that happens before the
+  // return (inner scopes, temporaries).
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
-                                    const Expr *MovedExpr) {}
+                                    const Expr *MovedExpr,
+                                    llvm::ArrayRef<const Expr *> ExprChain) {}
 
   virtual void reportDanglingField(const Expr *IssueExpr,
                                    const FieldDecl *Field,
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index d46712ce2b1a56..de7eca39228ca3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -57,6 +57,12 @@ class LoanPropagationAnalysis {
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const;
 
+  /// Like the above, starting from the origin \p OEF lets escape. Empty if it
+  /// does not hold \p TargetLoan.
+  llvm::SmallVector<OriginID> buildOriginFlowChain(const OriginEscapesFact *OEF,
+                                                   const LoanID TargetLoan,
+                                                   const CFG *Cfg) const;
+
 private:
   class Impl;
   std::unique_ptr<Impl> PImpl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index c4cc872dcd568d..30ae9961781c89 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -330,11 +330,13 @@ class LifetimeChecker {
             // FIXME: Diagnose invalidated return escapes separately.
           } else
             llvm_unreachable("Unhandled OriginEscapesFact type");
-        } else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF))
+        } else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
           // Return stack address.
           SemaHelper->reportUseAfterReturn(
-              IssueExpr, RetEscape->getReturnExpr(), MovedExpr);
-        else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
+              IssueExpr, RetEscape->getReturnExpr(), MovedExpr,
+              getExprChain(
+                  LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
+        } else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
           // Dangling field.
           bool IsCapturedByLambda =
               FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 80ba7d08a3103b..df66f029a5400d 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -230,18 +230,27 @@ class AnalysisImpl
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
-        return dropLoadsInUse(
+        return dropLeadingLoads(
             buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan, Cfg));
 
     return {};
   }
 
+  llvm::SmallVector<OriginID> buildOriginFlowChain(const OriginEscapesFact *OEF,
+                                                   const LoanID TargetLoan,
+                                                   const CFG *Cfg) const {
+    OriginID OID = OEF->getEscapedOriginID();
+    if (!getLoans(OID, OEF).contains(TargetLoan))
+      return {};
+    return dropLeadingLoads(buildOriginFlowChain(OEF, OID, TargetLoan, Cfg));
+  }
+
 private:
   /// An expression's origin only receives loans from its subexpressions, so
-  /// until the chain reaches a declaration it is inside the use expression.
-  /// Casts there just load the used variable, so drop them.
+  /// until the chain reaches a declaration it is inside the expression the
+  /// chain starts from. Casts there just load its variable, so drop them.
   llvm::SmallVector<OriginID>
-  dropLoadsInUse(llvm::SmallVector<OriginID> Chain) const {
+  dropLeadingLoads(llvm::SmallVector<OriginID> Chain) const {
     const OriginManager &OM = FactMgr.getOriginMgr();
     auto FirstDecl = llvm::find_if(
         Chain, [&](OriginID OID) { return !OM.getOrigin(OID).getExpr(); });
@@ -346,4 +355,10 @@ llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
     const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
   return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
 }
+llvm::SmallVector<OriginID>
+LoanPropagationAnalysis::buildOriginFlowChain(const OriginEscapesFact *OEF,
+                                              const LoanID TargetLoan,
+                                              const CFG *Cfg) const {
+  return PImpl->buildOriginFlowChain(OEF, TargetLoan, Cfg);
+}
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 620032c27f955e..e16aea60d23195 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -158,7 +158,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
   }
 
   void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr,
-                            const Expr *MovedExpr) override {
+                            const Expr *MovedExpr,
+                            llvm::ArrayRef<const Expr *> ExprChain) override {
     unsigned DiagID = MovedExpr
                           ? diag::warn_lifetime_safety_return_stack_addr_moved
                           : diag::warn_lifetime_safety_return_stack_addr;
@@ -169,6 +170,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     if (MovedExpr)
       S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
           << MovedExpr->getSourceRange();
+
+    reportAliasingChain(ExprChain);
+
     S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here)
         << ReturnExpr->getSourceRange();
   }
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 22fe5e6bddfb59..4deb6d1f8688d6 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -550,7 +550,7 @@ struct CaptureViewToView {
 CaptureViewToView test_view_to_view() {
   MyObj obj;
   View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
-  CaptureViewToView x(v);
+  CaptureViewToView x(v); // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
   return x; // expected-note {{returned here}}
 }
 
diff --git a/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp b/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
index 71c86b9f793fb6..5fb42cd7c9258e 100644
--- a/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
+++ b/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
@@ -75,13 +75,13 @@ const int *object_arg_is_not_moved(Holder &&h [[clang::lifetimebound]]) {
 }
 
 const int *t1(Holder h) {
-  const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}}
+  const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}} expected-note {{result of call to 'borrow' aliases the storage of parameter 'h' because the implicit object parameter is marked as lifetimebound}}
   static_cast<Holder &&>(h).consume();
   return ptr; // expected-note {{returned here}}
 }
 
 const int *t2(Holder h) {
-  const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}}
+  const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}} expected-note {{result of call to 'borrow' aliases the storage of parameter 'h' because the implicit object parameter is marked as lifetimebound}}
   std::move(h).consume();
   return ptr; // expected-note {{returned here}}
 }
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index 7f4a58c1836dd9..6986d1d61e8978 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -179,7 +179,7 @@ struct LifetimeBoundCtor {
 };
 
 auto lifetimebound_make_unique_single_param() {
-  return std::make_unique<LifetimeBoundCtor>(MyIntOwner{}); // tu-warning {{stack memory associated with temporary object is returned}} tu-note {{returned here}}
+  return std::make_unique<LifetimeBoundCtor>(MyIntOwner{}); // tu-warning {{stack memory associated with temporary object is returned}} tu-note {{returned here}} tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyIntOwner>' aliases the storage of temporary object because parameter 'args' is inferred as lifetimebound}}
 }
 
 
@@ -255,14 +255,14 @@ std::string_view containerWithAnnotatedElements() {
   use(c2);
 
   std::vector<std::string> local;
-  return local.at(0); // expected-warning {{address of stack memory associated with local variable}} \
+  return local.at(0); // expected-warning {{address of stack memory associated with local variable}} cfg-note {{result of call to 'at' aliases the storage of local variable 'local' because the implicit object parameter is inferred as lifetimebound}} \
                       // cfg-warning {{stack memory associated with local variable 'local' is returned}} cfg-note {{returned here}}
 }
 
 std::string_view localUniquePtr(int i) {
   std::unique_ptr<std::string> c1;
   if (i)
-    return *c1; // expected-warning {{address of stack memory associated with local variable}} \
+    return *c1; // expected-warning {{address of stack memory associated with local variable}} cfg-note {{result of call to 'operator*' aliases the storage of local variable 'c1' because the implicit object parameter is inferred as lifetimebound}} \
                 // cfg-warning {{stack memory associated with local variable 'c1' is returned}} cfg-note {{returned here}}
   std::unique_ptr<std::string_view> c2;
   return *c2; // expect no-warning.
@@ -271,7 +271,7 @@ std::string_view localUniquePtr(int i) {
 std::string_view localOptional(int i) {
   std::optional<std::string> o;
   if (i)
-    return o.value(); // expected-warning {{address of stack memory associated with local variable}} \
+    return o.value(); // expected-warning {{address of stack memory associated with local variable}} cfg-note {{result of call to 'value' aliases the storage of local variable 'o' because the implicit object parameter is inferred as lifetimebound}} \
                       // cfg-warning {{stack memory associated with local variable 'o' is returned}} cfg-note {{returned here}}
   std::optional<std::string_view> abc;
   return abc.value(); // expect no warning
@@ -295,14 +295,14 @@ int *danglingUniquePtrFromTemp2() {
 }
 
 const int& danglingRefToOptionalFromTemp3() {
-  return std::optional<int>().value(); // expected-warning {{returning reference to local temporary object}} \
+  return std::optional<int>().value(); // expected-warning {{returning reference to local temporary object}} cfg-note {{result of call to 'value' aliases the storage of temporary object because the implicit object parameter is inferred as lifetimebound}} \
                                        // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}}
 }
 
 std::optional<std::string> getTempOptStr();
 
 std::string_view danglingRefToOptionalFromTemp4() {
-  return getTempOptStr().value(); // expected-warning {{returning address of local temporary object}} \
+  return getTempOptStr().value(); // expected-warning {{returning address of local temporary object}} cfg-note {{result of call to 'value' aliases the storage of temporary object because the implicit object parameter is inferred as lifetimebound}} \
                                   // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}}
 }
 
@@ -355,7 +355,7 @@ int &usedToBeFalsePositive(std::vector<int> &v) {
 int &doNotFollowReferencesForLocalOwner() {
 // Warning caught by CFG analysis.
   std::unique_ptr<int> localOwner;
-  int &p = *localOwner // cfg-warning {{stack memory associated with local variable 'localOwner' is returned}}
+  int &p = *localOwner // cfg-warning {{stack memory associated with local variable 'localOwner' is returned}} cfg-note {{result of call to 'get' aliases the storage of local variable 'localOwner' because the implicit object parameter is inferred as lifetimebound}}
             .get();
   return p; // cfg-note {{returned here}}
 }
@@ -456,11 +456,11 @@ std::vector<std::string_view> GetTemporaryView();
 
 std::string_view test_str_local() {
   std::vector<std::string> v;
-  return *std::find(v.begin(), // cfg-warning {{stack memory associated with local variable 'v' is returned}} cfg-note {{returned here}}
+  return *std::find(v.begin(), // cfg-warning {{stack memory associated with local variable 'v' is returned}} cfg-note {{returned here}} cfg-note {{result of call to 'begin' aliases the storage of local variable 'v' because the implicit object parameter is inferred as lifetimebound}} cfg-note {{result of call to 'find<__gnu_cxx::basic_iterator<std::basic_string<char>>, char[3]>' aliases the storage of local variable 'v' because parameter 'first' is inferred as lifetimebound}} cfg-note {{result of call to 'operator*' aliases the storage of local variable 'v' because the implicit object parameter is inferred as lifetimebound}}
                     v.end(), "42");
 }
 std::string_view test_str_temporary() {
-  return *std::find(GetTemporaryString().begin(), // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}}
+  return *std::find(GetTemporaryString().begin(), // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} cfg-note {{result of call to 'begin' aliases the storage of temporary object because the implicit object parameter is inferred as lifetimebound}} cfg-note {{result of call to 'find<__gnu_cxx::basic_iterator<std::basic_string<char>>, char[3]>' aliases the storage of temporary object because parameter 'first' is inferred as lifetimebound}} cfg-note {{result of call to 'operator*' aliases the storage of temporary object because the implicit object parameter is inferred as lifetimebound}}
                     GetTemporaryString().end(), "42");
 }
 std::string_view test_view() {
@@ -594,10 +594,10 @@ struct FooView {
 };
 FooView test3(int i, std::optional<Foo> a) {
   if (i)
-    return *a; // expected-warning {{address of stack memory}} \
+    return *a; // expected-warning {{address of stack memory}} cfg-note {{result of call to 'operator*' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}} \
                // cfg-warning {{stack memory associated with parameter 'a' is returned}} \
                // cfg-note {{returned here}}
-  return a.value(); // expected-warning {{address of stack memory}} \
+  return a.value(); // expected-warning {{address of stack memory}} cfg-note {{result of call to 'value' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}} \
                     // cfg-warning {{stack memory associated with parameter 'a' is returned}} \
                     // cfg-note {{returned here}}
 }
@@ -658,7 +658,7 @@ std::string_view test2() {
   // We expect dangling issues as the conversion operator is marked as lifetimebound。
   std::string_view bad = StatusOr<Wrapper2<std::string_view>>().value(); // expected-warning {{temporary whose address is used as value of}}
   
-  return k.value(); // expected-warning {{address of stack memory associated}} \
+  return k.value(); // expected-warning {{address of stack memory associated}} cfg-note {{result of call to 'value' aliases the storage of local variable 'k' because the implicit object parameter is marked as lifetimebound}} \
                     // cfg-warning {{stack memory associated with local variable 'k' is returned}} cfg-note {{returned here}}
 }
 } // namespace GH108272
@@ -810,7 +810,7 @@ std::vector<int*> test8(StatusOr<std::vector<int*>> aa) {
 
 // Pointer<Pointer> from Owner<Owner<Pointer>>
 Span<int*> test9(StatusOr<std::vector<int*>> aa) {
-  return aa.valueLB(); // expected-warning {{address of stack memory associated}} \
+  return aa.valueLB(); // expected-warning {{address of stack memory associated}} cfg-note {{result of call to 'valueLB' aliases the storage of parameter 'aa' because the implicit object parameter is marked as lifetimebound}} \
                        // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}}
   return aa.valueNoLB(); // OK.
 }
@@ -819,7 +819,7 @@ Span<int*> test9(StatusOr<std::vector<int*>> aa) {
 
 // Pointer<Owner>> from Owner<Owner>
 Span<std::string> test10(StatusOr<std::vector<std::string>> aa) {
-  return aa.valueLB(); // expected-warning {{address of stack memory}} \
+  return aa.valueLB(); // expected-warning {{address of stack memory}} cfg-note {{result of call to 'valueLB' aliases the storage of parameter 'aa' because the implicit object parameter is marked as lifetimebound}} \
                        // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}}
   return aa.valueNoLB(); // OK.
 }
@@ -877,7 +877,7 @@ std::string_view test1_1() {
                            // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
                            // cfg-note {{result of call to 'Ref' aliases the storage of temporary object because parameter 'abc' is marked as lifetimebound}}
   use(t1);                 // cfg-note {{later used here}}
-  return Ref(std::string()); // expected-warning {{returning address}} \
+  return Ref(std::string()); // expected-warning {{returning address}} cfg-note {{result of call to 'Ref' aliases the storage of temporary object because parameter 'abc' is marked as lifetimebound}} \
                              // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}}
 }
 
@@ -931,7 +931,7 @@ std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
                                  // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
                                  // cfg-note {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked as lifetimebound}}
   use(t1);                       // cfg-note {{later used here}}
-  return r1.get(); // expected-warning {{address of stack}} \
+  return r1.get(); // expected-warning {{address of stack}} cfg-note {{result of call to 'get' aliases the storage of parameter 'r1' because the implicit object parameter is marked as lifetimebound}} \
                    // cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}}
 }
 std::string_view test2_2(Foo<std::string> r1, Foo<std::string_view> r2) {
@@ -999,14 +999,14 @@ void test4() {
 
 namespace range_based_for_loop_variables {
 std::string_view test_view_loop_var(std::vector<std::string> strings) {
-  for (std::string_view s : strings) {  // cfg-warning {{stack memory associated with parameter 'strings' is returned}}
+  for (std::string_view s : strings) {  // cfg-warning {{stack memory associated with parameter 'strings' is returned}} cfg-note {{result of call to 'begin' aliases the storage of parameter 'strings'}}
     return s; //cfg-note {{returned here}}
   }
   return "";
 }
 
 const char* test_view_loop_var_with_data(std::vector<std::string> strings) {
-  for (std::string_view s : strings) {  // cfg-warning {{stack memory associated with parameter 'strings' is returned}}
+  for (std::string_view s : strings) {  // cfg-warning {{stack memory associated with parameter 'strings' is returned}} cfg-note {{result of call to 'begin' aliases the storage of parameter 'strings'}}
     return s.data(); //cfg-note {{returned here}}
   }
   return "";
@@ -1020,15 +1020,15 @@ std::string_view test_no_error_for_views(std::vector<std::string_view> views) {
 }
 
 std::string_view test_string_ref_var(std::vector<std::string> strings) {
-  for (const std::string& s : strings) {  // cfg-warning {{stack memory associated with parameter 'strings' is returned}}
-    return s; //cfg-note {{returned here}}
+  for (const std::string& s : strings) {  // cfg-warning {{stack memory associated with parameter 'strings' is returned}} cfg-note {{result of call to 'begin' aliases the storage of parameter 'strings'}}
+    return s; //cfg-note {{returned here}} cfg-note {{result of call to 'operator basic_string_view' aliases the storage of parameter 'strings'}}
   }
   return "";
 }
 
 std::string_view test_opt_strings(std::optional<std::vector<std::string>> strings_or) {
-  for (const std::string& s : *strings_or) {  // cfg-warning {{stack memory associated with parameter 'strings_or' is returned}}
-    return s; //cfg-note {{returned here}}
+  for (const std::string& s : *strings_or) {  // cfg-warning {{stack memory associated with parameter 'strings_or' is returned}} cfg-note {{result of call to 'operator*' aliases the storage of parameter 'strings_or' because the implicit object parameter is inferred as lifetimebound}} cfg-note {{result of call to 'begin' aliases the storage of parameter 'strings_or'}}
+    return s; //cfg-note {{returned here}} cfg-note {{result of call to 'operator basic_string_view' aliases the storage of parameter 'strings_or'}}
   }
   return "";
 }
@@ -1037,7 +1037,7 @@ std::string_view test_opt_strings(std::optional<std::vector<std::string>> string
 namespace iterator_arrow {
 std::string_view test() {
   std::vector<std::string> strings;
-  return strings.begin()->data(); // cfg-warning {{stack memory associated with local variable 'strings' is returned}} cfg-note {{returned here}}
+  return strings.begin()->data(); // cfg-warning {{stack memory associated with local variable 'strings' is returned}} cfg-note {{returned here}} cfg-note {{result of call to 'begin' aliases the storage of local variable 'strings' because the implicit object parameter is inferred as lifetimebound}} cfg-note {{result of call to 'operator->' aliases the storage of local variable 'strings' because the implicit object parameter is inferred as lifetimebound}} cfg-note {{result of call to 'data' aliases the storage of local variable 'strings' because the implicit object parameter is inferred as lifetimebound}}
 }
 
 void operator_star_arrow_reference() {
@@ -1185,7 +1185,7 @@ struct StatusOr {
 
 const char* foo() {
   StatusOr<std::string> s;
-  return s->data(); // expected-warning {{address of stack memory associated with local variable}} \
+  return s->data(); // expected-warning {{address of stack memory associated with local variable}} cfg-note {{result of call to 'operator->' aliases the storage of local variable 's' because the implicit object parameter is marked as lifetimebound}} \
                     // cfg-warning {{stack memory associated with local variable 's' is returned}} cfg-note {{returned here}}
 
   StatusOr<std::string_view> s2;
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index bf49e86577678d..c7700a387440e6 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -544,7 +544,7 @@ View reassign_safe_to_local(const MyObj& safe) {
 View pointer_chain_to_local() {
   MyObj local;
   View p1 = local;     // expected-warning {{stack memory associated with local variable 'local' is returned}}
-  View p2 = p1; 
+  View p2 = p1; // expected-note {{local variable 'p1' aliases the storage of local variable 'local'}}
   return p2;          // expected-note {{returned here}}
 }
 
@@ -679,7 +679,7 @@ struct PtrHolder {
 
 int* const& test_ref_to_ptr() {
   PtrHolder a;
-  int *const &ref = a.getRef();  // expected-warning {{stack memory associated with local variable 'a' is returned}}
+  int *const &ref = a.getRef();  // expected-warning {{stack memory associated with local variable 'a' is returned}} expected-note {{result of call to 'getRef' aliases the storage of local variable 'a' because the implicit object parameter is marked as lifetimebound}}
   return ref;  // expected-note {{returned here}}
 }
 int* const test_ref_to_ptr_no_error() {
@@ -1631,10 +1631,10 @@ void range_based_for_use_after_scope() {
 
 View range_based_for_use_after_return() {
   MyObjStorage s;
-  for (const MyObj &o : s) { // expected-warning {{stack memory associated with local variable 's' is returned}}
+  for (const MyObj &o : s) { // expected-warning {{stack memory associated with local variable 's' is returned}} expected-note {{local variable '__range1' aliases the storage of local variable 's'}}
     return o;  // expected-note {{returned here}}
   }
-  return *s.begin();  // expected-warning {{stack memory associated with local variable 's' is returned}}
+  return *s.begin();  // expected-warning {{stack memory associated with local variable 's' is returned}} expected-note {{result of call to 'begin' aliases the storage of local variable 's' because the implicit object parameter is marked as lifetimebound}}
                       // expected-note at -1 {{returned here}}
 }
 
@@ -1751,7 +1751,7 @@ T&& MaxT(T&& a [[clang::lifetimebound]], T&& b [[clang::lifetimebound]]);
 
 const MyObj& call_max_with_obj() {
   MyObj oa, ob;
-  return  MaxT(oa,    // expected-warning {{stack memory associated with local variable 'oa' is returned}}
+  return  MaxT(oa,    // expected-warning {{stack memory associated with local variable 'oa' is returned}} expected-note {{result of call to 'MaxT<MyObj &>' aliases the storage of local variable 'oa' because parameter 'a' is marked as lifetimebound}} expected-note {{result of call to 'MaxT<MyObj &>' aliases the storage of local variable 'ob' because parameter 'b' is marked as lifetimebound}}
                       // expected-note at -1 2 {{returned here}}
                ob);   // expected-warning {{stack memory associated with local variable 'ob' is returned}}
                     
@@ -1759,7 +1759,7 @@ const MyObj& call_max_with_obj() {
 
 MyObj* call_max_with_obj_error() {
   MyObj oa, ob;
-  return  &MaxT(oa,   // expected-warning {{stack memory associated with local variable 'oa' is returned}}
+  return  &MaxT(oa,   // expected-warning {{stack memory associated with local variable 'oa' is returned}} expected-note {{result of call to 'MaxT<MyObj &>' aliases the storage of local variable 'oa' because parameter 'a' is marked as lifetimebound}} expected-note {{result of call to 'MaxT<MyObj &>' aliases the storage of local variable 'ob' because parameter 'b' is marked as lifetimebound}}
                       // expected-note at -1 2 {{returned here}}
                 ob);  // expected-warning {{stack memory associated with local variable 'ob' is returned}}
 }
@@ -1768,7 +1768,7 @@ const MyObj* call_max_with_ref_obj_error() {
   MyObj oa, ob;
   const MyObj& refa = oa;     // expected-warning {{stack memory associated with local variable 'oa' is returned}}
   const MyObj& refb = ob;     // expected-warning {{stack memory associated with local variable 'ob' is returned}}
-  return  &MaxT(refa, refb);  // expected-note 2 {{returned here}}
+  return  &MaxT(refa, refb);  // expected-note 2 {{returned here}} expected-note {{result of call to 'MaxT<const MyObj &>' aliases the storage of local variable 'oa'}} expected-note {{result of call to 'MaxT<const MyObj &>' aliases the storage of local variable 'ob'}}
 }
 const MyObj& call_max_with_ref_obj_return_ref_error() {
   MyObj oa, ob;
@@ -1788,7 +1788,7 @@ const MyObj& call_max_with_ref_obj_no_error(const MyObj& a, const MyObj& b) {
 
 const View& call_max_with_view_with_error() {
   View va, vb;
-  return MaxT(va,   // expected-warning {{stack memory associated with local variable 'va' is returned}}
+  return MaxT(va,   // expected-warning {{stack memory associated with local variable 'va' is returned}} expected-note {{result of call to 'MaxT<View &>' aliases the storage of local variable 'va' because parameter 'a' is marked as lifetimebound}} expected-note {{result of call to 'MaxT<View &>' aliases the storage of local variable 'vb' because parameter 'b' is marked as lifetimebound}}
                     // expected-note at -1 2 {{returned here}}
               vb);  // expected-warning {{stack memory associated with local variable 'vb' is returned}}
 }
@@ -1797,7 +1797,7 @@ struct [[gsl::Pointer]] NonTrivialPointer  { ~NonTrivialPointer(); };
 
 const NonTrivialPointer& call_max_with_non_trivial_view_with_error() {
   NonTrivialPointer va, vb;
-  return MaxT(va,   // expected-warning {{stack memory associated with local variable 'va' is returned}}
+  return MaxT(va,   // expected-warning {{stack memory associated with local variable 'va' is returned}} expected-note {{result of call to 'MaxT<MaxFnLifetimeBound::NonTrivialPointer &>' aliases the storage of local variable 'va' because parameter 'a' is marked as lifetimebound}} expected-note {{result of call to 'MaxT<MaxFnLifetimeBound::NonTrivialPointer &>' aliases the storage of local variable 'vb' because parameter 'b' is marked as lifetimebound}}
                     // expected-note at -1 2 {{returned here}}
               vb);  // expected-warning {{stack memory associated with local variable 'vb' is returned}}
 }
@@ -1964,7 +1964,7 @@ const MyObj& testDeref(MyObj obj) {
 }
 const MyObj* testDerefAddr(MyObj obj) {
   View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
-  return &*v;   // expected-note {{returned here}}
+  return &*v;   // expected-note {{returned here}} expected-note {{result of call to 'operator*' aliases the storage of parameter 'obj'}}
 }
 const MyObj* testData(MyObj obj) {
   View v = obj;     // expected-warning {{stack memory associated with parameter 'obj' is returned}}
@@ -1972,12 +1972,12 @@ const MyObj* testData(MyObj obj) {
 }
 const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
   View v = obj;           // expected-warning {{stack memory associated with parameter 'obj' is returned}}
-  const MyObj* ptr = v.data();
+  const MyObj* ptr = v.data(); // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}} expected-note {{result of call to 'data' aliases the storage of parameter 'obj' because the implicit object parameter is inferred as lifetimebound}}
   return ptr->getData();  // expected-note {{returned here}}
 }
 const int* testLifetimeboundAccessorOfMyObjThroughDeref(MyObj obj) {
   View v = obj;         // expected-warning {{stack memory associated with parameter 'obj' is returned}}
-  return v->getData();  // expected-note {{returned here}}
+  return v->getData();  // expected-note {{returned here}} expected-note {{result of call to 'operator->' aliases the storage of parameter 'obj'}}
 }
 } // namespace DereferenceViews
 
@@ -2000,17 +2000,17 @@ It end() const [[clang::lifetimebound]];
 MyObj Global;
 
 const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
-  for (const MyObj& x : c) {  // expected-warning {{stack memory associated with parameter 'c' is returned}}
+  for (const MyObj& x : c) {  // expected-warning {{stack memory associated with parameter 'c' is returned}} expected-note {{local variable '__range1' aliases the storage of parameter 'c'}}
     return x;                 // expected-note {{returned here}}
   }
   return Global;
 }
 
 View ContainerMyObjReturnView(Container<MyObj> c) {
-  for (const MyObj& x : c) {  // expected-warning {{stack memory associated with parameter 'c' is returned}}
+  for (const MyObj& x : c) {  // expected-warning {{stack memory associated with parameter 'c' is returned}} expected-note {{local variable '__range1' aliases the storage of parameter 'c'}}
     return x;                 // expected-note {{returned here}}
   }
-  for (View x : c) {  // expected-warning {{stack memory associated with parameter 'c' is returned}}
+  for (View x : c) {  // expected-warning {{stack memory associated with parameter 'c' is returned}} expected-note {{local variable '__range1' aliases the storage of parameter 'c'}}
     return x;         // expected-note {{returned here}}
   }
   return Global;
@@ -2066,7 +2066,7 @@ void test_lifetime_extension_ok() {
 }
 
 const std::string& test_return() {
-  const std::string& x = S().x(); // expected-warning {{stack memory associated with temporary object is returned}}
+  const std::string& x = S().x(); // expected-warning {{stack memory associated with temporary object is returned}} expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked as lifetimebound}}
   return x; // expected-note {{returned here}}
 }
 } // namespace reference_type_decl_ref_expr
@@ -2145,7 +2145,7 @@ struct RefMember {
 
 std::string_view refMemberReturnView1(RefMember a) { return a.str_ref; }
 std::string_view refMemberReturnView2(RefMember a) { return *a.str_ptr; }
-std::string_view refMemberReturnView3(RefMember a) { return a.str; } // expected-warning {{stack memory associated with parameter 'a' is returned}} expected-note {{returned here}}
+std::string_view refMemberReturnView3(RefMember a) { return a.str; } // expected-warning {{stack memory associated with parameter 'a' is returned}} expected-note {{returned here}} expected-note {{result of call to 'operator basic_string_view' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}}
 std::string& refMemberReturnRef1(RefMember a) { return a.str_ref; }
 std::string& refMemberReturnRef2(RefMember a) { return *a.str_ptr; }
 std::string& refMemberReturnRef3(RefMember a) { return a.str; } // expected-warning {{stack memory associated with parameter 'a' is returned}} expected-note {{returned here}}
@@ -2347,7 +2347,7 @@ auto capture_int_by_value() {
 auto capture_view_by_value() {
   MyObj obj;
   View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
-  auto lambda = [v]() { return v; };
+  auto lambda = [v]() { return v; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2377,35 +2377,35 @@ auto capture_multiple() {
 auto capture_raw_pointer_by_value() {
   int x;
   int* p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
-  auto lambda = [p]() { return p; };
+  auto lambda = [p]() { return p; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_raw_pointer_init_capture() {
   int x;
   int* p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
-  auto lambda = [q = p]() { return q; };
+  auto lambda = [q = p]() { return q; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_view_init_capture() {
   MyObj obj;
   View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
-  auto lambda = [w = v]() { return w; };
+  auto lambda = [w = v]() { return w; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
 auto capture_lambda() {
   int x;
   auto inner = [&x]() { return x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
-  auto outer = [inner]() { return inner(); };
+  auto outer = [inner]() { return inner(); }; // expected-note {{local variable 'inner' aliases the storage of local variable 'x'}}
   return outer; // expected-note {{returned here}}
 }
 
 auto return_copied_lambda() {
   int local = 1;
   auto lambda = [&local]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
-  auto lambda_copy = lambda;
+  auto lambda_copy = lambda; // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
   return lambda_copy; // expected-note {{returned here}}
 }
 
@@ -2427,7 +2427,7 @@ auto implicit_ref_capture_multiple() {
 auto implicit_value_capture() {
   MyObj obj;
   View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
-  auto lambda = [=]() { return v; };
+  auto lambda = [=]() { return v; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
   return lambda; // expected-note {{returned here}}
 }
 
@@ -2467,7 +2467,7 @@ auto capture_multilevel_pointer() {
   int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
   int **q = &p; // expected-warning {{stack memory associated with local variable 'p' is returned}}
   int ***r = &q; // expected-warning {{stack memory associated with local variable 'q' is returned}}
-  auto lambda = [=]() { return *p + **q + ***r; };
+  auto lambda = [=]() { return *p + **q + ***r; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}} expected-note {{local variable 'r' aliases the storage of local variable 'q'}} expected-note {{local variable 'q' aliases the storage of local variable 'p'}}
   return lambda; // expected-note 3 {{returned here}}
 }
 } // namespace lambda_captures
@@ -2785,8 +2785,8 @@ void same_scope() {
 
 S copy_propagation() {
   std::string str{"abc"};
-  S a = getS(str); // expected-warning {{stack memory associated with local variable 'str' is returned}}
-  S b = a;
+  S a = getS(str); // expected-warning {{stack memory associated with local variable 'str' is returned}} expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked as lifetimebound}}
+  S b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
   return b; // expected-note {{returned here}}
 }
 
@@ -2833,7 +2833,7 @@ S getS2(const std::string &a [[clang::lifetimebound]], const std::string &b [[cl
 
 S multiple_lifetimebound_params() {
   std::string str{"abc"};
-  S s = getS2(str, std::string("temp")); // expected-warning {{stack memory associated with local variable 'str' is returned}} \
+  S s = getS2(str, std::string("temp")); // expected-warning {{stack memory associated with local variable 'str' is returned}} expected-note {{result of call to 'getS2' aliases the storage of local variable 'str' because parameter 'a' is marked as lifetimebound}} expected-note {{result of call to 'getS2' aliases the storage of temporary object because parameter 'b' is marked as lifetimebound}} \
                                          // expected-warning {{stack memory associated with temporary object is returned}}
   return s;                              // expected-note 2 {{returned here}}
 }
@@ -2951,8 +2951,8 @@ DefaultedOuter getDefaultedOuter(const std::string &s [[clang::lifetimebound]]);
 // pattern does not fit the ownership model this analysis supports.
 DefaultedOuter nested_defaulted_outer_with_user_defined_inner() {
   std::string str{"abc"};
-  DefaultedOuter o = getDefaultedOuter(str); // expected-warning {{stack memory associated with local variable 'str' is returned}}
-  DefaultedOuter copy = o;
+  DefaultedOuter o = getDefaultedOuter(str); // expected-warning {{stack memory associated with local variable 'str' is returned}} expected-note {{result of call to 'getDefaultedOuter' aliases the storage of local variable 'str' because parameter 's' is marked as lifetimebound}}
+  DefaultedOuter copy = o; // expected-note {{local variable 'o' aliases the storage of local variable 'str'}}
   return copy; // expected-note {{returned here}}
 }
 
@@ -2999,8 +2999,8 @@ void owner_return_unique_ptr_s() {
 std::string_view return_dangling_view_through_owner() {
   std::string local;
   auto ups = getUniqueS(local);
-  S* s = ups.get(); // expected-warning {{stack memory associated with local variable 'ups' is returned}}
-  std::string_view sv = s->getData();
+  S* s = ups.get(); // expected-warning {{stack memory associated with local variable 'ups' is returned}} expected-note {{result of call to 'get' aliases the storage of local variable 'ups' because the implicit object parameter is inferred as lifetimebound}}
+  std::string_view sv = s->getData(); // expected-note {{local variable 's' aliases the storage of local variable 'ups'}} expected-note {{result of call to 'getData' aliases the storage of local variable 'ups' because the implicit object parameter is marked as lifetimebound}}
   return sv; // expected-note {{returned here}}
 }
 
@@ -3112,12 +3112,12 @@ int *constexpr_dead_nested(int *num) {
 
 int *constexpr_live_false(int *num) {
   int local = 0;
-  return kFalse ? num : f(&local); // expected-warning {{stack memory associated with local variable 'local' is returned}} // expected-note {{returned here}}
+  return kFalse ? num : f(&local); // expected-warning {{stack memory associated with local variable 'local' is returned}} // expected-note {{returned here}} expected-note {{result of call to 'f' aliases the storage of local variable 'local' because parameter 'p' is marked as lifetimebound}}
 }
 
 int *constexpr_live_nested(int *num) {
   int local = 0;
-  return kTrue ? (kFalse ? num : f(&local)) : num; // expected-warning {{stack memory associated with local variable 'local' is returned}} // expected-note {{returned here}}
+  return kTrue ? (kFalse ? num : f(&local)) : num; // expected-warning {{stack memory associated with local variable 'local' is returned}} // expected-note {{returned here}} expected-note {{result of call to 'f' aliases the storage of local variable 'local' because parameter 'p' is marked as lifetimebound}}
 }
 int *noreturn_dead_false(bool cond, int *num) {
   int local = 0;
@@ -3734,7 +3734,7 @@ std::function<void()> direct_return() {
 std::function<void()> copy_function() {
   int x;
   std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
-  std::function<void()> f2 = f;
+  std::function<void()> f2 = f; // expected-note {{local variable 'f' aliases the storage of local variable 'x'}}
   return f2; // expected-note {{returned here}}
 }
 
@@ -3742,7 +3742,7 @@ std::function<void()> copy_assign() {
   int x;
   std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
   std::function<void()> f2 = []() {};
-  f2 = f;
+  f2 = f; // expected-note {{local variable 'f' aliases the storage of local variable 'x'}}
   return f2; // expected-note {{returned here}}
 }
 
@@ -3751,7 +3751,7 @@ std::function<void()> chained_copy_assign() {
   std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
   std::function<void()> f2 = []() {};
   std::function<void()> f3 = []() {};
-  f3 = f2 = f;
+  f3 = f2 = f; // expected-note {{local variable 'f' aliases the storage of local variable 'x'}} expected-note {{result of call to 'operator=' aliases the storage of local variable 'x'}}
   return f3; // expected-note {{returned here}}
 }
 
@@ -3759,7 +3759,7 @@ std::function<void()> move_assign() {
   int x;
   std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
   std::function<void()> f2 = []() {};
-  f2 = std::move(f);
+  f2 = std::move(f); // expected-note {{result of call to 'move<std::function<void ()> &>' aliases the storage of local variable 'x'}}
   return f2; // expected-note {{returned here}}
 }
 
@@ -3854,19 +3854,19 @@ struct [[gsl::Owner]] optional : __optional_storage_base<T> {
 
 const MyObj& return_optional_deref() {
   optional<MyObj> opt;
-  return *opt; // expected-warning {{stack memory associated with local variable 'opt' is returned}} \
+  return *opt; // expected-warning {{stack memory associated with local variable 'opt' is returned}} expected-note {{result of call to 'operator*' aliases the storage of local variable 'opt' because the implicit object parameter is inferred as lifetimebound}} \
                // expected-note {{returned here}}
 }
 
 const MyObj& return_optional_value() {
   optional<MyObj> opt;
-  return opt.value(); // expected-warning {{stack memory associated with local variable 'opt' is returned}} \
+  return opt.value(); // expected-warning {{stack memory associated with local variable 'opt' is returned}} expected-note {{result of call to 'value' aliases the storage of local variable 'opt' because the implicit object parameter is inferred as lifetimebound}} \
                       // expected-note {{returned here}}
 }
 
 const int* return_optional_arrow() {
   optional<MyObj> opt;
-  return &opt->id; // expected-warning {{stack memory associated with local variable 'opt' is returned}} \
+  return &opt->id; // expected-warning {{stack memory associated with local variable 'opt' is returned}} expected-note {{result of call to 'operator->' aliases the storage of local variable 'opt' because the implicit object parameter is inferred as lifetimebound}} \
                    // expected-note {{returned here}}
 }
 
@@ -3887,27 +3887,27 @@ namespace GH191954 {
     int x;
     int* f = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
     int* a;
-    a = std::move(f);
+    a = std::move(f); // expected-note {{result of call to 'move<int *&>' aliases the storage of local variable 'x'}}
     return a; // expected-note {{returned here}}
   }
 
   int* return_moved_pointer2() {
     int x;
     int* f = &x;         // expected-warning {{stack memory associated with local variable 'x' is returned}}
-    return std::move(f); // expected-note {{returned here}}
+    return std::move(f); // expected-note {{returned here}} expected-note {{result of call to 'move<int *&>' aliases the storage of local variable 'x'}}
   }
 
   View return_moved_view() {
     MyObj o;
     View v(o); // expected-warning {{stack memory associated with local variable 'o' is returned}}
-    View v2 = std::move(v);
+    View v2 = std::move(v); // expected-note {{result of call to 'move<View &>' aliases the storage of local variable 'o'}}
     return v2; // expected-note {{returned here}}
   }
 
   int* return_forwarded_pointer() {
     int x;
     int* f = &x;                  // expected-warning {{stack memory associated with local variable 'x' is returned}}
-    return std::forward<int*>(f); // expected-note {{returned here}}
+    return std::forward<int*>(f); // expected-note {{returned here}} expected-note {{result of call to 'forward<int *>' aliases the storage of local variable 'x'}}
   }
 
   int g;



More information about the cfe-commits mailing list