Author: Yuan Suo Date: 2026-06-12T17:37:59+02:00 New Revision: c5654d53c008522d2da165f05b682aafa36d7189
URL: https://github.com/llvm/llvm-project/commit/c5654d53c008522d2da165f05b682aafa36d7189 DIFF: https://github.com/llvm/llvm-project/commit/c5654d53c008522d2da165f05b682aafa36d7189.diff LOG: [LifetimeSafety] Introduce buildOriginFlowChain for use-after-scope (#199345) After adding `buildOriginFlowChain`, we need to choose a diagnostic type that is as simple as possible to verify its feasibility during `Sema` diagnostics. I did not choose the annotation suggestions described in https://github.com/llvm/llvm-project/pull/188467/#issuecomment-4359071778 as the first target to implement, because it does not seem to occur within a single CFG block. The `IssueFact` always resides in the block preceding the `OriginEscapesFact`, which causes me to always get an empty `OriginFlowChain`. Since we use `buildOriginFlowChain`, we can directly trace distinct assignment steps that occur within a single source-level expression. For example: ```cpp #include <vector> #include <string> template<class... T> void use(T... arg); void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() { std::vector<std::pair<int, std::string>> v; const char* p = v.begin()->second.data(); const char* q = (*v.begin()).second.data(); 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(); const char* y = (*temporary().begin()).second.data(); const std::string& z = (*temporary().begin()).second; use(p, q, r, x, y, z); } ``` The code above produces the following diagnostic output: ```txt pr.cpp:13:19: warning: local temporary object does not live long enough [-Wlifetime-safety-use-after-scope] 13 | const char* x = temporary().begin()->second.data(); | ^~~~~~~~~~~ pr.cpp:13:52: note: destroyed here 13 | const char* x = temporary().begin()->second.data(); | ^ pr.cpp:13:19: note: expression aliases the storage of local temporary object 13 | const char* x = temporary().begin()->second.data(); | ^~~~~~~~~~~~~~~~~~~ pr.cpp:13:19: note: expression aliases the storage of local temporary object 13 | const char* x = temporary().begin()->second.data(); | ^~~~~~~~~~~~~~~~~~~~~ pr.cpp:13:19: note: expression aliases the storage of local temporary object 13 | const char* x = temporary().begin()->second.data(); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ pr.cpp:16:16: note: later used here 16 | use(p, q, r, x, y, z); | ^ pr.cpp:15:28: warning: local temporary object does not live long enough [-Wlifetime-safety-use-after-scope] 15 | const std::string& z = (*temporary().begin()).second; | ^~~~~~~~~~~ pr.cpp:15:49: note: destroyed here 15 | const std::string& z = (*temporary().begin()).second; | ^ pr.cpp:15:28: note: expression aliases the storage of local temporary object 15 | const std::string& z = (*temporary().begin()).second; | ^~~~~~~~~~~~~~~~~~~ pr.cpp:15:27: note: expression aliases the storage of local temporary object 15 | const std::string& z = (*temporary().begin()).second; | ^~~~~~~~~~~~~~~~~~~~ pr.cpp:16:22: note: later used here 16 | use(p, q, r, x, y, z); | ^ pr.cpp:14:21: warning: local temporary object does not live long enough [-Wlifetime-safety-use-after-scope] 14 | const char* y = (*temporary().begin()).second.data(); | ^~~~~~~~~~~ pr.cpp:14:54: note: destroyed here 14 | const char* y = (*temporary().begin()).second.data(); | ^ pr.cpp:14:21: note: expression aliases the storage of local temporary object 14 | const char* y = (*temporary().begin()).second.data(); | ^~~~~~~~~~~~~~~~~~~ pr.cpp:14:20: note: expression aliases the storage of local temporary object 14 | const char* y = (*temporary().begin()).second.data(); | ^~~~~~~~~~~~~~~~~~~~ pr.cpp:14:19: note: expression aliases the storage of local temporary object 14 | const char* y = (*temporary().begin()).second.data(); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ pr.cpp:16:19: note: later used here 16 | use(p, q, r, x, y, z); | ^ 3 warnings generated. ``` --------- Signed-off-by: Yuan Suo <[email protected]> Added: Modified: clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Analysis/LifetimeSafety/Checker.cpp clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp clang/lib/Sema/SemaLifetimeSafety.h clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp clang/test/Sema/LifetimeSafety/nocfg.cpp clang/test/Sema/LifetimeSafety/safety.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index b89e1bfe4b43e..28886b826f72f 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -63,7 +63,8 @@ class LifetimeSafetySemaHelper { virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, - SourceLocation FreeLoc) {} + SourceLocation FreeLoc, + llvm::ArrayRef<const Expr *> ExprChain) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h index e46912bedeb0f..724c6eee7d3c2 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h @@ -48,6 +48,9 @@ class LoanPropagationAnalysis { buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan) const; + llvm::SmallVector<OriginID> + buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const; + private: class Impl; std::unique_ptr<Impl> PImpl; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 583f3f9d2eff5..9691963d4fe7e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11061,6 +11061,7 @@ 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_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">; +def note_lifetime_safety_aliases_storage : 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/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 71cf2d2d5a674..d41d6f43f837b 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -266,8 +266,10 @@ class LifetimeChecker { } else // Scope-based expiry (use-after-scope). - SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(), - MovedExpr, ExpiryLoc); + SemaHelper->reportUseAfterScope( + IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc, + getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID))); + } else if (const auto *OEF = CausingFact.dyn_cast<const OriginEscapesFact *>()) { if (Warning.InvalidatedByExpr) { @@ -509,6 +511,21 @@ class LifetimeChecker { } } } + + /// Extract expressions from the origin flow chain for diagnostic purposes. + /// + /// Given a chain of origins that shows how a loan propagates, this function + /// extracts the corresponding expressions for each origin. Origins that refer + /// to declarations (rather than expressions) are skipped. + llvm::SmallVector<const Expr *> + getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) { + llvm::SmallVector<const Expr *> rs; + for (const OriginID CurrOID : OriginFlowChain) + if (const Expr *CurrExpr = + FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()) + rs.push_back(CurrExpr); + return rs; + } }; } // namespace diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp index 8c570cd29673b..f3bb85f08e965 100644 --- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp @@ -239,6 +239,16 @@ class AnalysisImpl return {}; } + llvm::SmallVector<OriginID> + buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const { + for (const OriginList *Cur = UF->getUsedOrigins(); Cur; + Cur = Cur->peelOuterOrigin()) + if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan)) + return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan); + + return {}; + } + private: /// Returns true if the origin is persistent (referenced in multiple blocks). bool isPersistent(OriginID OID) const { @@ -295,4 +305,10 @@ LoanPropagationAnalysis::buildOriginFlowChain(ProgramPoint StartPoint, const LoanID TargetLoan) const { return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan); } + +llvm::SmallVector<OriginID> +LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF, + const LoanID TargetLoan) const { + return PImpl->buildOriginFlowChain(UF, TargetLoan); +} } // namespace clang::lifetimes::internal diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 462a4ed168e24..a7c628f315d78 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -62,8 +62,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc) override { + const Expr *MovedExpr, SourceLocation FreeLoc, + llvm::ArrayRef<const Expr *> ExprChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved : diag::warn_lifetime_safety_use_after_scope; @@ -74,6 +74,9 @@ 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); + + reportAliasingChain(ExprChain); + S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); } @@ -481,13 +484,44 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { } std::string getDiagSubjectDescription(const Expr *E) { + E = E->IgnoreImpCasts(); if (isa<MaterializeTemporaryExpr>(E)) return "local temporary object"; if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) return getDiagSubjectDescription(DRE->getDecl()); // TODO: Handle other expression types. - return ""; + return "expression"; + } + + bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) { + CurrExpr = CurrExpr->IgnoreImpCasts(); + LastExpr = LastExpr->IgnoreImpCasts(); + + if (!isa<CallExpr, DeclRefExpr>(CurrExpr)) + return false; + // Source ranges can be used to filter out many implicit expressions, + // because operations between class objects often involve numerous implicit + // conversions, yet they share the same source range. + return CurrExpr->getSourceRange() != LastExpr->getSourceRange(); + } + + void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) { + if (OriginExprChain.empty()) + return; + + const Expr *LastExpr = OriginExprChain.back(); + std::string IssueStr = getDiagSubjectDescription(LastExpr); + + for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) { + if (!shouldShowInAliasChain(CurrExpr, LastExpr)) + continue; + S.Diag(CurrExpr->getBeginLoc(), + diag::note_lifetime_safety_aliases_storage) + << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr) + << IssueStr; + LastExpr = CurrExpr; + } } Sema &S; diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp index 1f6832e824e37..1f94e101fb9ef 100644 --- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp +++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp @@ -278,18 +278,21 @@ View return_view_field(const ViewProvider& v) { // expected-warning {{paramet void test_get_on_temporary_pointer() { const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{local temporary object does not live long enough}}. // expected-note@-1 {{destroyed here}} + // expected-note@-2 {{expression aliases the storage of local temporary object}} (void)s_ref; // expected-note {{later used here}} } void test_get_on_temporary_ref() { const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning {{local temporary object does not live long enough}}. // expected-note@-1 {{destroyed here}} + // expected-note@-2 {{expression aliases the storage of local temporary object}} (void)s_ref; // expected-note {{later used here}} } void test_getView_on_temporary() { View sv = ViewProvider{1}.getView(); // expected-warning {{local temporary object does not live long enough}}. // expected-note@-1 {{destroyed here}} + // expected-note@-2 {{expression aliases the storage of local temporary object}} (void)sv; // expected-note {{later used here}} } @@ -599,7 +602,8 @@ void uaf_via_inferred_lifetimebound() { std::function<void()> f = []() {}; { int local; - f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} + f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)f; // expected-note {{later used here}} } @@ -622,7 +626,8 @@ void test_inference() { std::unique_ptr<LifetimeBoundCtor> ptr; { MyObj obj; - ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} + ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)ptr; // expected-note {{later used here}} } @@ -636,7 +641,8 @@ View* MakeView(const MyObj& in) { // expected-warning {{parameter in intra-TU fu void test_new_allocation() { View* v = MakeView(MyObj{}); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} (void)v; // expected-note {{later used here}} } diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp index e2a340401ac87..49b6175d378ae 100644 --- a/clang/test/Sema/LifetimeSafety/nocfg.cpp +++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp @@ -193,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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} (void)it; // cfg-note {{later used here}} } @@ -241,11 +242,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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(c1); // cfg-note {{later used here}} c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(c1); // cfg-note {{later used here}} // no warning on constructing from gsl-pointer @@ -306,23 +309,29 @@ 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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} // 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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} // 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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} 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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} int &&r5 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} 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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(sv); // cfg-note {{later used here}} } @@ -333,7 +342,8 @@ 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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} ; } @@ -610,7 +620,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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(svjkk1); // cfg-note {{later used here}} } } // namespace GH100549 @@ -844,7 +855,8 @@ namespace GH118064{ void test() { auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(y); // cfg-note {{later used here}} } } // namespace GH118064 @@ -859,10 +871,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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t1); // cfg-note {{later used here}} t1 = Ref(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t1); // cfg-note {{later used here}} return Ref(std::string()); // expected-warning {{returning address}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} @@ -870,10 +884,12 @@ std::string_view test1_1() { std::string_view test1_2() { std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t2); // cfg-note {{later used here}} t2 = TakeSv(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t2); // cfg-note {{later used here}} return TakeSv(std::string()); // expected-warning {{returning address}} \ @@ -882,10 +898,12 @@ std::string_view test1_2() { std::string_view test1_3() { std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t3); // cfg-note {{later used here}} t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t3); // cfg-note {{later used here}} return TakeStrRef(std::string()); // expected-warning {{returning address}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} @@ -907,10 +925,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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t1); // cfg-note {{later used here}} t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} use(t1); // cfg-note {{later used here}} return r1.get(); // expected-warning {{address of stack}} \ // cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}} @@ -1028,9 +1048,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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} - const char* y = (*temporary().begin()).data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} - const std::string& z = (*temporary().begin()); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + const char* x = temporary().begin()->data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 3 {{expression aliases the storage of local temporary object}} + const char* y = (*temporary().begin()).data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 3 {{expression aliases the storage of local temporary object}} + const std::string& z = (*temporary().begin()); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 2 {{expression aliases the storage of local temporary object}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } @@ -1042,9 +1065,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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} - const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} - const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + const char* x = temporary().begin()->second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 3 {{expression aliases the storage of local temporary object}} + const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 3 {{expression aliases the storage of local temporary object}} + const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 2 {{expression aliases the storage of local temporary object}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } @@ -1094,17 +1120,21 @@ 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-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{expression aliases the storage of local temporary object}} std::string_view k3 = Q().get()->sv; // OK std::string_view k4 = Q().get()->s; // expected-warning {{object backing the pointer will}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 2 {{expression aliases the storage of local temporary object}} std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 2 {{expression aliases the storage of local temporary object}} std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \ - // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note 3 {{expression aliases the storage of local temporary object}} use(k1, k2, k3, k4, lb1, lb2); // cfg-note 4 {{later used here}} } diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp index 580e1b2639ee1..2aa5ff43ec3bc 100644 --- a/clang/test/Sema/LifetimeSafety/safety.cpp +++ b/clang/test/Sema/LifetimeSafety/safety.cpp @@ -92,7 +92,7 @@ void pointer_chain() { { MyObj s; p = &s; // expected-warning {{does not live long enough}} - q = p; + q = p; // expected-note {{local variable 'p' aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} (void)*q; // expected-note {{later used here}} } @@ -102,7 +102,7 @@ void propagation_gsl() { { MyObj s; v1 = s; // expected-warning {{local variable 's' does not live long enough}} - v2 = v1; + v2 = v1; // expected-note {{local variable 'v1' aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} v2.use(); // expected-note {{later used here}} } @@ -696,7 +696,8 @@ void test_lifetimebound_multi_level() { int* p = nullptr; int** pp = &p; int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live long enough}} - result = return_inner_ptr_addr(ppp); + result = return_inner_ptr_addr(ppp); // expected-note {{local variable 'ppp' aliases the storage of local variable 'pp'}} \ + // expected-note {{expression aliases the storage of local variable 'pp'}} } // expected-note {{destroyed here}} (void)**result; // expected-note {{used here}} } @@ -819,7 +820,8 @@ void lifetimebound_simple_function() { View v; { MyObj obj; - v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}} + v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -828,7 +830,8 @@ void lifetimebound_multiple_args_definite() { View v; { MyObj obj1, obj2; - v = Choose(true, + v = Choose(true, // expected-note {{expression aliases the storage of local variable 'obj1'}} \ + // expected-note {{expression aliases the storage of local variable 'obj2'}} obj1, // expected-warning {{local variable 'obj1' does not live long enough}} obj2); // expected-warning {{local variable 'obj2' does not live long enough}} } // expected-note 2 {{destroyed here}} @@ -855,7 +858,8 @@ void lifetimebound_mixed_args() { View v; { MyObj obj1, obj2; - v = SelectFirst(obj1, // expected-warning {{local variable 'obj1' does not live long enough}} + v = SelectFirst(obj1, // expected-warning {{local variable 'obj1' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'obj1'}} obj2); } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} @@ -871,7 +875,8 @@ void lifetimebound_member_function() { View v; { MyObj obj; - v = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}} + v = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -895,7 +900,8 @@ void lifetimebound_chained_calls() { View v; { MyObj obj; - v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}} + v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note 3 {{expression aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -904,7 +910,8 @@ void lifetimebound_with_pointers() { MyObj* ptr; { MyObj obj; - ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not live long enough}} + ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)*ptr; // expected-note {{later used here}} } @@ -913,7 +920,8 @@ void chained_assignment_lifetimebound_call() { MyObj *p, *obj; { MyObj s; - p = Identity(obj = &s); // expected-warning {{does not live long enough}} + p = Identity(obj = &s); // expected-warning {{does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -945,7 +953,8 @@ void lifetimebound_return_reference() { { MyObj obj; View temp_v = obj; // expected-warning {{local variable 'obj' does not live long enough}} - const MyObj& ref = GetObject(temp_v); + const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}} \ + // expected-note {{expression aliases the storage of local variable 'obj'}} ptr = &ref; } // expected-note {{destroyed here}} (void)*ptr; // expected-note {{later used here}} @@ -999,7 +1008,8 @@ void lifetimebound_make_unique() { std::unique_ptr<LifetimeBoundCtor> ptr; { MyObj obj; - ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}} + ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}} \ + // tu-note {{expression aliases the storage of local variable 'obj'}} } // tu-note {{destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1016,7 +1026,8 @@ void non_lifetimebound_make_unique() { void lifetimebound_make_unique_temp() { std::unique_ptr<LifetimeBoundCtor> ptr = std::make_unique<LifetimeBoundCtor>(MyObj()); // tu-warning {{local temporary object does not live long enough}} \ - // tu-note {{destroyed here}} + // tu-note {{destroyed here}} \ + // tu-note {{expression aliases the storage of local temporary object}} (void)ptr; // tu-note {{later used here}} } @@ -1053,7 +1064,8 @@ void lifetimebound_make_unique_multi_params() { MyObj obj_long; { MyObj obj_short; - ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}} + ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}} \ + // tu-note {{expression aliases the storage of local variable 'obj_short'}} } // tu-note {{destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1063,7 +1075,8 @@ void lifetimebound_make_unique_multi_params2() { MyObj obj_long; { MyObj obj_short; - ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}} + ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}} \ + // tu-note {{expression aliases the storage of local variable 'obj_short'}} } // tu-note {{destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1083,7 +1096,8 @@ void lifetimebound_make_unique_multi_params3_1() { MyObj obj_long; { MyObj obj_short; - ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} + ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \ + // tu-note {{expression aliases the storage of local variable 'obj_short'}} } // tu-note {{destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1093,7 +1107,8 @@ void lifetimebound_make_unique_multi_params3_2() { MyObj obj_long; { MyObj obj_short; - ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} + ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \ + // tu-note {{expression aliases the storage of local variable 'obj_short'}} } // tu-note {{destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1245,7 +1260,8 @@ void parentheses(bool cond) { { MyObj a; - p = ((GetPointer((a)))); // expected-warning {{local variable 'a' does not live long enough}} + p = ((GetPointer((a)))); // expected-warning {{local variable 'a' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'a'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} @@ -1276,7 +1292,8 @@ void use_temporary_after_destruction() { void passing_temporary_to_lifetime_bound_function() { View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{local temporary object does not live long enough}} \ - expected-note {{destroyed here}} + expected-note {{destroyed here}} \ + expected-note {{expression aliases the storage of local temporary object}} use(a); // expected-note {{later used here}} } @@ -1289,14 +1306,16 @@ void use_trivial_temporary_after_destruction() { namespace FullExprCleanupLoc { void var_initializer() { - View v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}} + View v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}} \ + // expected-note {{expression aliases the storage of local temporary object}} .getView(); // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } void expr_statement() { View v; - v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}} + v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}} \ + // expected-note {{expression aliases the storage of local temporary object}} .getView(); // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1339,7 +1358,8 @@ void foobar() { View view; { StatusOr<MyObj> string_or = getStringOr(); - view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}} + view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'string_or'}} value(); } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} @@ -1413,7 +1433,8 @@ void test_user_defined_deref_uaf() { { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); - p = &(*smart_ptr); // expected-warning {{local variable 'smart_ptr' does not live long enough}} + p = &(*smart_ptr); // expected-warning {{local variable 'smart_ptr' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'smart_ptr'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1430,7 +1451,8 @@ void test_user_defined_deref_with_view() { { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); - v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not live long enough}} + v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'smart_ptr'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1440,7 +1462,8 @@ void test_user_defined_deref_arrow() { { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); - p = smart_ptr.operator->(); // expected-warning {{local variable 'smart_ptr' does not live long enough}} + p = smart_ptr.operator->(); // expected-warning {{local variable 'smart_ptr' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'smart_ptr'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1450,7 +1473,8 @@ void test_user_defined_deref_chained() { { MyObj obj; SmartPtr<SmartPtr<MyObj>> double_ptr; - p = &(**double_ptr); // expected-warning {{local variable 'double_ptr' does not live long enough}} + p = &(**double_ptr); // expected-warning {{local variable 'double_ptr' does not live long enough}} \ + // expected-note 2 {{expression aliases the storage of local variable 'double_ptr'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1636,13 +1660,15 @@ void wrong_use_of_move_is_permissive() { View v; { MyObj a; - v = std::move(a); // expected-warning {{local variable 'a' does not live long enough}} + v = std::move(a); // expected-warning {{local variable 'a' does not live long enough}} \ + // expected-note {{expression aliases the storage of local 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 {{local variable 'a' does not live long enough}} + p = std::move(a).getData(); // expected-warning {{local variable 'a' does not live long enough}} \ + // expected-note 2 {{expression aliases the storage of local variable 'a'}} } // expected-note {{destroyed here}} (void)p; // expected-note {{later used here}} } @@ -1653,7 +1679,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 {{local variable 'p' may not live long enough. This could be a false positive as the storage may have been moved later}} + r = p.get(); // expected-warning {{local variable 'p' may not live long enough. This could be a false positive as the storage may have been moved later}} \ + // expected-note {{expression aliases the storage of local variable 'p'}} take(p.release()); // expected-note {{potentially moved here}} } // expected-note {{destroyed here}} (void)*r; // expected-note {{later used here}} @@ -1675,9 +1702,11 @@ void bar() { View x; { S s; - x = s.x(); // expected-warning {{local variable 's' does not live long enough}} + x = s.x(); // expected-warning {{local variable 's' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 's'}} View y = S().x(); // expected-warning {{local temporary object does not live long enough}} \ - expected-note {{destroyed here}} + expected-note {{destroyed here}} \ + expected-note {{expression aliases the storage of local temporary object}} (void)y; // expected-note {{used here}} } // expected-note {{destroyed here}} (void)x; // expected-note {{used here}} @@ -1765,17 +1794,20 @@ 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 {{local temporary object does not live long enough}} expected-note {{destroyed here}} + const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} (void)x; // expected-note {{later used here}} - const std::string& y = identity(S().x()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} + const std::string& y = identity(S().x()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \ + // expected-note 2 {{expression aliases the storage of local temporary object}} (void)y; // expected-note {{later used here}} std::string_view z; { S s; - const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}} - z = zz; + const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 's'}} + z = zz; // expected-note {{expression aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} (void)z; // expected-note {{later used here}} } @@ -1783,12 +1815,14 @@ void test_temporary() { void test_lifetime_extension_ok() { const S& x = S(); (void)x; - const S& y = identity(S()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} + const S& y = identity(S()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} (void)y; // expected-note {{later used here}} } const std::string& test_return() { - const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} + const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} return x; // expected-note {{later used here}} } } // namespace reference_type_decl_ref_expr @@ -1805,7 +1839,8 @@ void uaf() { { S str; S* p = &str; // expected-warning {{local variable 'str' does not live long enough}} - view = p->s; + view = p->s; // expected-note {{local variable 'p' aliases the storage of local variable 'str'}} \ + // expected-note {{expression aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1830,8 +1865,9 @@ void uaf_union() { std::string_view view; { U u = U{"hello"}; - U* up = &u; // expected-warning {{local variable 'u' does not live long enough}} - view = up->s; + U* up = &u; // expected-warning {{local variable 'u' does not live long enough}} + view = up->s; // expected-note {{local variable 'up' aliases the storage of local variable 'u'}} \ + // expected-note {{expression aliases the storage of local variable 'u'}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1848,7 +1884,7 @@ void uaf_anonymous_union() { { AnonymousUnion au; AnonymousUnion* up = &au; // expected-warning {{local variable 'au' does not live long enough}} - ip = &up->x; + ip = &up->x; // expected-note {{local variable 'up' aliases the storage of local variable 'au'}} } // expected-note {{destroyed here}} (void)ip; // expected-note {{later used here}} } @@ -1906,9 +1942,12 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { void test() { MemberFuncsTpl<MyObj> mtf; - const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}} - const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{local temporary object does not live long enough}} // tu-note {{destroyed here}} - const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}} + const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} + const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{local temporary object does not live long enough}} // tu-note {{destroyed here}} \ + // tu-note {{expression aliases the storage of local temporary object}} + const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} (void)pTMA; // expected-note {{later used here}} (void)pTMB; // tu-note {{later used here}} (void)pTMC; // expected-note {{later used here}} @@ -1943,7 +1982,8 @@ void test_optional_arrow() { const char* p; { std::optional<std::string> opt; - p = opt->data(); // expected-warning {{local variable 'opt' does not live long enough}} + p = opt->data(); // expected-warning {{local variable 'opt' does not live long enough}} \ + // expected-note 2 {{expression aliases the storage of local variable 'opt'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1952,7 +1992,8 @@ void test_optional_arrow_lifetimebound() { View v; { std::optional<MyObj> opt; - v = opt->getView(); // expected-warning {{local variable 'opt' does not live long enough}} + v = opt->getView(); // expected-warning {{local variable 'opt' does not live long enough}} \ + // expected-note 2 {{expression aliases the storage of local variable 'opt'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1961,7 +2002,8 @@ void test_unique_ptr_arrow() { const char* p; { std::unique_ptr<std::string> up; - p = up->data(); // expected-warning {{local variable 'up' does not live long enough}} + p = up->data(); // expected-warning {{local variable 'up' does not live long enough}} \ + // expected-note 2 {{expression aliases the storage of local variable 'up'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -2313,9 +2355,9 @@ struct S { void indexing_with_static_operator() { S()(1, 2); - S& x = S()("1", - 2, // expected-warning {{local temporary object does not live long enough}} - 3); // expected-warning {{local temporary object does not live long enough}} expected-note 2 {{destroyed here}} + S& x = S()("1", // expected-note 2 {{expression aliases the storage of local temporary object}} + 2, // expected-warning {{local temporary object does not live long enough}} + 3); // expected-warning {{local temporary object does not live long enough}} expected-note 2 {{destroyed here}} (void)x; // expected-note 2 {{later used here}} @@ -2338,7 +2380,8 @@ S getS(const std::string &s [[clang::lifetimebound]]); void from_free_function() { S s = getS(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} use(s); // expected-note {{later used here}} } @@ -2357,13 +2400,15 @@ struct Factory { void from_method() { Factory f; S s = f.make(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} use(s); // expected-note {{later used here}} } void from_static_method() { S s = Factory::create(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} use(s); // expected-note {{later used here}} } @@ -2371,7 +2416,8 @@ void from_lifetimebound_this_method() { S value; { Factory f; - value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}} + value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'f'}} } // expected-note {{destroyed here}} use(value); // expected-note {{later used here}} } @@ -2380,7 +2426,8 @@ void across_scope() { S s{}; { std::string str{"abc"}; - s = getS(str); // expected-warning {{local variable 'str' does not live long enough}} + s = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} use(s); // expected-note {{later used here}} } @@ -2402,8 +2449,9 @@ void assignment_propagation() { S a, b; { std::string str{"abc"}; - a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} - b = a; + a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'str'}} + b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} use(b); // expected-note {{later used here}} } @@ -2412,8 +2460,10 @@ void chained_defaulted_assignment_propagation() { S b, c; { std::string str{"abc"}; - S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} - c = b = a; + S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'str'}} + c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}\ + // expected-note {{expression aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} use(c); // expected-note {{later used here}} } @@ -2427,7 +2477,8 @@ void no_annotation() { void mix_annotated_and_not() { S s1 = getS(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} S s2 = getSNoAnnotation(std::string("temp")); use(s1); // expected-note {{later used here}} use(s2); @@ -2439,6 +2490,7 @@ 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}} \ // expected-warning {{local temporary object does not live long enough}} \ + // expected-note {{expression aliases the storage of local temporary object}} \ // expected-note {{destroyed here}} return s; // expected-note {{returned here}} \ // expected-note {{later used here}} @@ -2458,7 +2510,8 @@ T make(const std::string &s [[clang::lifetimebound]]); void from_template_instantiation() { S s = make<S>(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} use(s); // expected-note {{later used here}} } @@ -2521,7 +2574,8 @@ SAlias getSAlias(const std::string &s [[clang::lifetimebound]]); void from_typedef_return() { SAlias s = getSAlias(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} use(s); // expected-note {{later used here}} } @@ -2595,7 +2649,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 {{local temporary object does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} \ + // expected-note {{expression aliases the storage of local temporary object}} (void)ptr; // expected-note {{later used here}} } @@ -2611,7 +2666,8 @@ void owner_outlives_lifetimebound_source() { std::unique_ptr<S> ups; { std::string local; - ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}} + ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)ups; // expected-note {{later used here}} } @@ -2646,8 +2702,8 @@ void nested_local_pointer() { { Bar v; p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} - pp = Pointer(p); - ppp = Pointer(pp); + pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v'}} + ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}} } // expected-note {{destroyed here}} use(***ppp); // expected-note {{later used here}} } @@ -2792,7 +2848,7 @@ void new_pointer_from_pointer() { { MyObj obj; MyObj *q = &obj; // expected-warning {{local variable 'obj' does not live long enough}} - p = new MyObj *(q); + p = new MyObj *(q); // expected-note {{local variable 'q' aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)**p; // expected-note {{later used here}} } @@ -3387,7 +3443,8 @@ void uaf_via_lifetimebound() { std::function<void()> f = []() {}; { int local; - f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}} + f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)f; // expected-note {{later used here}} } @@ -3450,7 +3507,8 @@ void deref_use_after_scope() { const MyObj* p; { optional<MyObj> opt; - p = &*opt; // expected-warning {{local variable 'opt' does not live long enough}} + p = &*opt; // expected-warning {{local variable 'opt' does not live long enough}} \ + // expected-note {{expression aliases the storage of local variable 'opt'}} } // expected-note {{destroyed here}} (void)p->id; // expected-note {{later used here}} } @@ -3520,7 +3578,7 @@ void transitive_capture() { { MyObj local; setCaptureBy(v1, local); // expected-warning {{local variable 'local' does not live long enough}} - setCaptureBy(v2, v1); + setCaptureBy(v2, v1); // expected-note {{local variable 'v1' aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)v2; // expected-note {{later used here}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
