https://github.com/suoyuan666 created https://github.com/llvm/llvm-project/pull/212083
Initially, I planned to make all diagnostics in Checker.cpp use `buildOriginFlowChain`. However, after thinking about it, I think the current scope is more suitable for review. >From f7a016b3ff28b78f90057f1bdd6933a2b27c914e Mon Sep 17 00:00:00 2001 From: Yuan Suo <[email protected]> Date: Sat, 18 Jul 2026 15:06:23 +0800 Subject: [PATCH] Use buildOriginFlowChain for remaining diagnostics in issuePendingWarnings Signed-off-by: Yuan Suo <[email protected]> --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 23 +- .../Analyses/LifetimeSafety/LoanPropagation.h | 2 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 35 +-- .../LifetimeSafety/LoanPropagation.cpp | 9 +- clang/lib/Sema/SemaLifetimeSafety.h | 44 ++- .../LifetimeSafety/annotation-suggestions.cpp | 23 +- .../Sema/LifetimeSafety/dangling-field.cpp | 27 +- .../Sema/LifetimeSafety/dangling-global.cpp | 2 +- .../Sema/LifetimeSafety/invalidations.cpp | 19 +- clang/test/Sema/LifetimeSafety/nocfg.cpp | 93 +++++-- clang/test/Sema/LifetimeSafety/safety-c.c | 6 +- clang/test/Sema/LifetimeSafety/safety.cpp | 252 ++++++++++++------ 12 files changed, 357 insertions(+), 178 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index a51ef2f7cc0ba..8e52363a41b4f 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -71,17 +71,20 @@ class LifetimeSafetySemaHelper { 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, const Expr *MovedExpr, - SourceLocation ExpiryLoc) {} + SourceLocation ExpiryLoc, + llvm::ArrayRef<const Expr *> ExprChain) {} virtual void reportDanglingGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, const Expr *MovedExpr, - SourceLocation ExpiryLoc) {} + SourceLocation ExpiryLoc, + llvm::ArrayRef<const Expr *> ExprChain) {} // Reports when a reference/iterator is used after the container operation // that invalidated it. @@ -95,16 +98,22 @@ class LifetimeSafetySemaHelper { llvm::ArrayRef<const Expr *> ExprChain) {} virtual void reportInvalidatedField(const Expr *IssueExpr, const FieldDecl *Field, - const Expr *InvalidationExpr) {} + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) {} virtual void reportInvalidatedField(const ParmVarDecl *PVD, const FieldDecl *Field, - const Expr *InvalidationExpr) {} + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) {} virtual void reportInvalidatedGlobal(const Expr *IssueExpr, const VarDecl *Global, - const Expr *InvalidationExpr) {} + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) { + } virtual void reportInvalidatedGlobal(const ParmVarDecl *PVD, const VarDecl *Global, - const Expr *InvalidationExpr) {} + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) { + } using EscapingTarget = llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>; diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h index e13442facd82d..36c5e4242262e 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h @@ -51,7 +51,7 @@ class LoanPropagationAnalysis { const LoanID TargetLoan, const CFG *Cfg) const; - llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF, + llvm::SmallVector<OriginID> buildOriginFlowChain(const Fact *F, const LoanID TargetLoan, const CFG *Cfg) const; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 53e5077131147..f099e400ee3b2 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -281,33 +281,35 @@ class LifetimeChecker { } else if (const auto *OEF = CausingFact.dyn_cast<const OriginEscapesFact *>()) { + llvm::SmallVector<const Expr *> ExprChain = + getExprChain(LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)); if (Warning.InvalidatedByExpr) { if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) { // Invalidated object escapes to a field. if (IssueExpr) // Invalidated object on stack escapes to a field. - SemaHelper->reportInvalidatedField(IssueExpr, - FieldEscape->getFieldDecl(), - Warning.InvalidatedByExpr); + SemaHelper->reportInvalidatedField( + IssueExpr, FieldEscape->getFieldDecl(), + Warning.InvalidatedByExpr, ExprChain); else if (InvalidatedPVD) // Invalidated parameter escapes to a field. - SemaHelper->reportInvalidatedField(InvalidatedPVD, - FieldEscape->getFieldDecl(), - Warning.InvalidatedByExpr); + SemaHelper->reportInvalidatedField( + InvalidatedPVD, FieldEscape->getFieldDecl(), + Warning.InvalidatedByExpr, ExprChain); } else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) { // Invalidated object escapes to global or static storage. if (IssueExpr) // Invalidated object on stack escapes to global or static // storage. - SemaHelper->reportInvalidatedGlobal(IssueExpr, - GlobalEscape->getGlobal(), - Warning.InvalidatedByExpr); + SemaHelper->reportInvalidatedGlobal( + IssueExpr, GlobalEscape->getGlobal(), + Warning.InvalidatedByExpr, ExprChain); else if (InvalidatedPVD) // Invalidated parameter escapes to global or static storage. - SemaHelper->reportInvalidatedGlobal(InvalidatedPVD, - GlobalEscape->getGlobal(), - Warning.InvalidatedByExpr); + SemaHelper->reportInvalidatedGlobal( + InvalidatedPVD, GlobalEscape->getGlobal(), + Warning.InvalidatedByExpr, ExprChain); } else if (isa<ReturnEscapeFact>(OEF)) { // FIXME: Diagnose invalidated return escapes separately. } else @@ -315,15 +317,16 @@ class LifetimeChecker { } else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) // Return stack address. SemaHelper->reportUseAfterReturn( - IssueExpr, RetEscape->getReturnExpr(), MovedExpr); + IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExprChain); else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) // Dangling field. - SemaHelper->reportDanglingField( - IssueExpr, FieldEscape->getFieldDecl(), MovedExpr, ExpiryLoc); + SemaHelper->reportDanglingField(IssueExpr, + FieldEscape->getFieldDecl(), + MovedExpr, ExpiryLoc, ExprChain); else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) // Global escape. SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(), - MovedExpr, ExpiryLoc); + MovedExpr, ExpiryLoc, ExprChain); else llvm_unreachable("Unhandled OriginEscapesFact type"); } else diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp index 078892bd48c10..b2787c3abc987 100644 --- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp @@ -366,7 +366,12 @@ llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain( } llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain( - const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const { - return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg); + const Fact *F, const LoanID TargetLoan, const CFG *Cfg) const { + if (const auto *UF = llvm::dyn_cast<UseFact>(F)) + return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg); + if (const auto *OEF = llvm::dyn_cast<OriginEscapesFact>(F)) + return PImpl->buildOriginFlowChain(OEF, OEF->getEscapedOriginID(), + TargetLoan, Cfg); + llvm_unreachable("Unhandled Fact type"); } } // namespace clang::lifetimes::internal diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 1d9f94be7e22d..eaa5981639e80 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -128,7 +128,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; @@ -139,14 +140,17 @@ 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(); } void reportDanglingField(const Expr *IssueExpr, const FieldDecl *DanglingField, - const Expr *MovedExpr, - SourceLocation ExpiryLoc) override { + const Expr *MovedExpr, SourceLocation ExpiryLoc, + llvm::ArrayRef<const Expr *> ExprChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved : diag::warn_lifetime_safety_dangling_field; @@ -158,6 +162,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { if (MovedExpr) S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) << MovedExpr->getSourceRange(); + + reportAliasingChain(ExprChain); + S.Diag(DanglingField->getLocation(), diag::note_lifetime_safety_dangling_field_here) << DanglingField->getEndLoc(); @@ -165,8 +172,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportDanglingGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, - const Expr *MovedExpr, - SourceLocation ExpiryLoc) override { + const Expr *MovedExpr, SourceLocation ExpiryLoc, + llvm::ArrayRef<const Expr *> ExprChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved : diag::warn_lifetime_safety_dangling_global; @@ -178,6 +185,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { if (MovedExpr) S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) << MovedExpr->getSourceRange(); + + reportAliasingChain(ExprChain); + if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember()) S.Diag(DanglingGlobal->getLocation(), diag::note_lifetime_safety_dangling_static_here) @@ -223,13 +233,15 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportInvalidatedField(const Expr *IssueExpr, const FieldDecl *DanglingField, - const Expr *InvalidationExpr) override { + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) override { std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr); S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_invalidated_field) << InvalidatedSubject << getDiagSubjectDescription(DanglingField) << IssueExpr->getSourceRange(); reportInvalidationSite(InvalidationExpr, InvalidatedSubject); + reportAliasingChain(ExprChain); S.Diag(DanglingField->getLocation(), diag::note_lifetime_safety_dangling_field_here) << DanglingField->getEndLoc(); @@ -237,27 +249,31 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportInvalidatedField(const ParmVarDecl *PVD, const FieldDecl *DanglingField, - const Expr *InvalidationExpr) override { + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) override { std::string InvalidatedSubject = getDiagSubjectDescription(PVD); S.Diag(PVD->getSourceRange().getBegin(), diag::warn_lifetime_safety_invalidated_field) << InvalidatedSubject << getDiagSubjectDescription(DanglingField) << PVD->getSourceRange(); reportInvalidationSite(InvalidationExpr, InvalidatedSubject); + reportAliasingChain(ExprChain); S.Diag(DanglingField->getLocation(), diag::note_lifetime_safety_dangling_field_here) << DanglingField->getEndLoc(); } - void reportInvalidatedGlobal(const Expr *IssueExpr, - const VarDecl *DanglingGlobal, - const Expr *InvalidationExpr) override { + void + reportInvalidatedGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal, + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) override { std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr); S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_invalidated_global) << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal) << IssueExpr->getSourceRange(); reportInvalidationSite(InvalidationExpr, InvalidatedSubject); + reportAliasingChain(ExprChain); if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember()) S.Diag(DanglingGlobal->getLocation(), diag::note_lifetime_safety_dangling_static_here) @@ -268,15 +284,17 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { << DanglingGlobal->getEndLoc(); } - void reportInvalidatedGlobal(const ParmVarDecl *PVD, - const VarDecl *DanglingGlobal, - const Expr *InvalidationExpr) override { + void + reportInvalidatedGlobal(const ParmVarDecl *PVD, const VarDecl *DanglingGlobal, + const Expr *InvalidationExpr, + llvm::ArrayRef<const Expr *> ExprChain) override { std::string InvalidatedSubject = getDiagSubjectDescription(PVD); S.Diag(PVD->getSourceRange().getBegin(), diag::warn_lifetime_safety_invalidated_global) << InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal) << PVD->getSourceRange(); reportInvalidationSite(InvalidationExpr, InvalidatedSubject); + reportAliasingChain(ExprChain); if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember()) S.Diag(DanglingGlobal->getLocation(), diag::note_lifetime_safety_dangling_static_here) diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp index 56ed4a6dc7106..3497598196ee5 100644 --- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp +++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp @@ -528,7 +528,8 @@ struct CaptureRefToView { CaptureRefToView test_ref_to_view() { MyObj obj; CaptureRefToView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - return x; // expected-note {{returned here}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } struct CaptureRefToPtr { @@ -539,7 +540,8 @@ struct CaptureRefToPtr { CaptureRefToPtr test_ref_to_ptr() { MyObj obj; CaptureRefToPtr x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - return x; // expected-note {{returned here}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } struct CaptureViewToView { @@ -550,8 +552,9 @@ 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); - return x; // expected-note {{returned here}} + CaptureViewToView x(v); // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } struct CapturePtrToPtr { @@ -562,7 +565,8 @@ struct CapturePtrToPtr { CapturePtrToPtr test_ptr_to_ptr() { MyObj obj; CapturePtrToPtr x(&obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - return x; // expected-note {{returned here}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } struct CaptureRefToRef { @@ -573,7 +577,8 @@ struct CaptureRefToRef { CaptureRefToRef test_ref_to_ref() { MyObj obj; CaptureRefToRef x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - return x; // expected-note {{returned here}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } struct BaseWithView { @@ -588,7 +593,8 @@ struct CaptureRefToBaseView : BaseWithView { CaptureRefToBaseView test_ref_to_base_view() { MyObj obj; CaptureRefToBaseView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - return x; // expected-note {{returned here}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } } // namespace capturing_constructor @@ -660,7 +666,8 @@ struct HasCtorField { HasCtorField test_dangling_field_ctor() { MyObj obj; HasCtorField x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - return x; // expected-note {{returned here}} + return x; // expected-note {{returned here}} \ + // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}} } struct HasSetterField { diff --git a/clang/test/Sema/LifetimeSafety/dangling-field.cpp b/clang/test/Sema/LifetimeSafety/dangling-field.cpp index bc73c4f7e8644..d3190d990d7a1 100644 --- a/clang/test/Sema/LifetimeSafety/dangling-field.cpp +++ b/clang/test/Sema/LifetimeSafety/dangling-field.cpp @@ -40,7 +40,8 @@ struct CtorSetInLoop { struct CtorInitLifetimeBound { std::string_view view; // expected-note {{this field dangles}} - CtorInitLifetimeBound(std::string s) : view(construct_view(s)) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}} + CtorInitLifetimeBound(std::string s) : view(construct_view(s)) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}} \ + // expected-note {{result of call to 'construct_view' aliases the storage of parameter 's'}} }; struct CtorInitButMoved { @@ -90,7 +91,8 @@ struct CtorRefField { struct CtorPointerField { const char* ptr; // expected-note {{this field dangles}} - CtorPointerField(std::string s) : ptr(s.data()) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'ptr' which will dangle}} + CtorPointerField(std::string s) : ptr(s.data()) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'ptr' which will dangle}} \ + // expected-note {{result of call to 'data' aliases the storage of parameter 's'}} CtorPointerField(Dummy<1> ok, const std::string& s) : ptr(s.data()) {} CtorPointerField(Dummy<2> ok, std::string_view view) : ptr(view.data()) {} }; @@ -101,12 +103,14 @@ struct MemberSetters { void setWithParam(std::string s) { view = s; // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}} - p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}} + p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}} \ + // expected-note {{result of call to 'data' aliases the storage of parameter 's'}} } void setWithParamAndReturn(std::string s) { view = s; // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}} - p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}} + p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}} \ + // expected-note {{result of call to 'data' aliases the storage of parameter 's'}} return; } @@ -124,13 +128,15 @@ struct MemberSetters { void setWithLocal() { std::string s; view = s; // expected-warning {{stack memory associated with local variable 's' escapes to the field 'view' which will dangle}} - p = s.data(); // expected-warning {{stack memory associated with local variable 's' escapes to the field 'p' which will dangle}} + p = s.data(); // expected-warning {{stack memory associated with local variable 's' escapes to the field 'p' which will dangle}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 's'}} } void setWithLocalButMoved() { std::string s; view = s; // expected-warning-re {{stack memory associated with local variable 's' may escape to the field 'view' which will dangle. {{.*}} may have been moved}} - p = s.data(); // expected-warning-re {{stack memory associated with local variable 's' may escape to the field 'p' which will dangle. {{.*}} may have been moved}} + p = s.data(); // expected-warning-re {{stack memory associated with local variable 's' may escape to the field 'p' which will dangle. {{.*}} may have been moved}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 's'}} takeString(std::move(s)); // expected-note 2 {{potentially moved here}} } @@ -154,14 +160,16 @@ struct MemberSetters { std::string local; view = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'view' which will dangle}} - p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}} + p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 'local'}} } void use_after_scope() { { std::string local; view = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'view' which will dangle}} - p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}} + p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}} \ + // expected-note {{result of call to 'data' aliases the storage of local variable 'local'}} } (void)view; (void)p; @@ -241,7 +249,8 @@ struct HasUniquePtrField { std::unique_ptr<LifetimeBoundCtor> field; // tu-note {{this field dangles}} void setWithParam(MyObj obj) { - field = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{stack memory associated with parameter 'obj' escapes to the field 'field' which will dangle}} + field = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{stack memory associated with parameter 'obj' escapes to the field 'field' which will dangle}} \ + // tu-note {{result of call to 'make_unique<MakeUnique::LifetimeBoundCtor, MakeUnique::MyObj &>' aliases the storage of parameter 'obj'}} } }; } // namespace MakeUnique diff --git a/clang/test/Sema/LifetimeSafety/dangling-global.cpp b/clang/test/Sema/LifetimeSafety/dangling-global.cpp index bd67a0100131f..586b595946e75 100644 --- a/clang/test/Sema/LifetimeSafety/dangling-global.cpp +++ b/clang/test/Sema/LifetimeSafety/dangling-global.cpp @@ -27,7 +27,7 @@ void invoke_function_with_side_effects() { void inlined() { int local; global = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global_backup' which will dangle}} - global_backup = global; + global_backup = global; // expected-note {{global variable 'global' aliases the storage of local variable 'local'}} global = nullptr; } diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp index 127e375bc023c..45cb7122c8859 100644 --- a/clang/test/Sema/LifetimeSafety/invalidations.cpp +++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp @@ -576,7 +576,8 @@ struct SinkOwnerBorrow { struct SinkInteriorBorrow { const char *dest_; // expected-note {{this field dangles}} - SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter 'dest' escapes to the field 'dest_' and is later invalidated}} + SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter 'dest' escapes to the field 'dest_' and is later invalidated}} \ + // expected-note {{result of call to 'data' aliases the storage of parameter 'dest'}} if (n > 0) dest->clear(); // expected-note {{parameter 'dest' is invalidated here}} } @@ -594,12 +595,16 @@ struct S { void InvalidatedFieldLocalVector() { std::vector<std::string> strings; - FieldFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the field 'FieldFromLocalVector' and is later invalidated}} + FieldFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the field 'FieldFromLocalVector' and is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \ + // expected-note {{expression aliases the storage of local variable 'strings'}} strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}} } void InvalidatedFieldByValueParamVector(std::vector<std::string> strings) { - FieldFromByValueParamVector = *strings.begin(); // expected-warning {{parameter 'strings' escapes to the field 'FieldFromByValueParamVector' and is later invalidated}} + FieldFromByValueParamVector = *strings.begin(); // expected-warning {{parameter 'strings' escapes to the field 'FieldFromByValueParamVector' and is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of parameter 'strings'}} \ + // expected-note {{expression aliases the storage of parameter 'strings'}} strings.push_back("1"); // expected-note {{parameter 'strings' is invalidated here}} } @@ -621,7 +626,7 @@ struct S { void InvalidatedFieldDelete() { int *p = new int; // expected-warning {{allocated object escapes to the field 'FieldFromNew' and is later invalidated}} - FieldFromNew = p; + FieldFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}} delete p; // expected-note {{allocated object is freed here}} } @@ -654,7 +659,9 @@ struct S { void InvalidatedGlobalLocalVector() { std::vector<std::string> strings; - GlobalFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the global variable 'GlobalFromLocalVector' and is later invalidated}} + GlobalFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the global variable 'GlobalFromLocalVector' and is later invalidated}} \ + // expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \ + // expected-note {{expression aliases the storage of local variable 'strings'}} strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}} } @@ -670,7 +677,7 @@ void InvalidatedGlobalRefParamString(std::string &s) { // expected-warning {{par void InvalidatedGlobalDelete() { int *p = new int; // expected-warning {{allocated object escapes to the global variable 'GlobalFromNew' and is later invalidated}} - GlobalFromNew = p; + GlobalFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}} delete p; // expected-note {{allocated object is freed here}} } diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp index b9a6150daf712..afab3509fd4c9 100644 --- a/clang/test/Sema/LifetimeSafety/nocfg.cpp +++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp @@ -180,7 +180,8 @@ 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}} } @@ -257,14 +258,16 @@ std::string_view containerWithAnnotatedElements() { std::vector<std::string> local; return local.at(0); // expected-warning {{address of stack memory associated with local variable}} \ - // cfg-warning {{stack memory associated with local variable 'local' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with local variable 'local' is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'at' aliases the storage of local variable 'local'}} } 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}} \ - // cfg-warning {{stack memory associated with local variable 'c1' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with local variable 'c1' is returned}} cfg-note {{returned here}} \ + // cfg-note {{expression aliases the storage of local variable 'c1'}} std::unique_ptr<std::string_view> c2; return *c2; // expect no-warning. } @@ -273,7 +276,8 @@ 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}} \ - // cfg-warning {{stack memory associated with local variable 'o' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with local variable 'o' is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'value' aliases the storage of local variable 'o'}} std::optional<std::string_view> abc; return abc.value(); // expect no warning } @@ -297,14 +301,16 @@ int *danglingUniquePtrFromTemp2() { const int& danglingRefToOptionalFromTemp3() { return std::optional<int>().value(); // expected-warning {{returning reference to local temporary object}} \ - // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'value' aliases the storage of temporary object}} } std::optional<std::string> getTempOptStr(); std::string_view danglingRefToOptionalFromTemp4() { return getTempOptStr().value(); // expected-warning {{returning address of local temporary object}} \ - // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'value' aliases the storage of temporary object}} } void danglingReferenceFromTempOwner() { @@ -356,7 +362,8 @@ 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'}} .get(); return p; // cfg-note {{returned here}} } @@ -368,8 +375,10 @@ const char *trackThroughMultiplePointer() { struct X { X(std::unique_ptr<int> up) : - pointee(*up), // cfg-warning {{may have been moved.}} - pointee2(up.get()), // cfg-warning {{may have been moved.}} + pointee(*up), // cfg-warning {{may have been moved.}} \ + // cfg-note {{expression aliases the storage of parameter 'up'}} + pointee2(up.get()), // cfg-warning {{may have been moved.}} \ + // cfg-note {{result of call to 'get' aliases the storage of parameter 'up'}} pointer(std::move(up)) {} // cfg-note 2 {{potentially moved here}} int &pointee; // cfg-note {{this field dangles}} int *pointee2; // cfg-note {{this field dangles}} @@ -383,7 +392,8 @@ struct X2 { // A common usage that moves the passing owner to the class. // verify a strict warning on this case. X2(XOwner owner) : - pointee(owner.get()), // cfg-warning {{may have been moved.}} + pointee(owner.get()), // cfg-warning {{may have been moved.}} \ + // cfg-note {{result of call to 'get' aliases the storage of parameter 'owner'}} owner(std::move(owner)) {} // cfg-note {{potentially moved here}} int* pointee; // cfg-note {{this field dangles}} XOwner owner; @@ -457,11 +467,17 @@ 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'}} \ + // cfg-note {{result of call to 'find<__gnu_cxx::basic_iterator<std::basic_string<char>>, char[3]>' aliases the storage of local variable 'v'}} \ + // cfg-note {{expression aliases the storage of local variable 'v'}} 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}} \ + // cfg-note {{result of call to 'find<__gnu_cxx::basic_iterator<std::basic_string<char>>, char[3]>' aliases the storage of temporary object}} \ + // cfg-note {{expression aliases the storage of temporary object}} GetTemporaryString().end(), "42"); } std::string_view test_view() { @@ -597,9 +613,11 @@ FooView test3(int i, std::optional<Foo> a) { if (i) return *a; // expected-warning {{address of stack memory}} \ // cfg-warning {{stack memory associated with parameter 'a' is returned}} \ + // cfg-note {{expression aliases the storage of parameter 'a'}} \ // cfg-note {{returned here}} return a.value(); // expected-warning {{address of stack memory}} \ // cfg-warning {{stack memory associated with parameter 'a' is returned}} \ + // cfg-note {{result of call to 'value' aliases the storage of parameter 'a'}} \ // cfg-note {{returned here}} } } // namespace GH93386 @@ -660,7 +678,8 @@ std::string_view test2() { 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}} \ - // cfg-warning {{stack memory associated with local variable 'k' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with local variable 'k' is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'value' aliases the storage of local variable 'k'}} } } // namespace GH108272 @@ -812,7 +831,8 @@ 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}} \ - // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'valueLB' aliases the storage of parameter 'aa'}} return aa.valueNoLB(); // OK. } @@ -821,7 +841,8 @@ 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}} \ - // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'valueLB' aliases the storage of parameter 'aa'}} return aa.valueNoLB(); // OK. } @@ -879,7 +900,8 @@ std::string_view test1_1() { // cfg-note {{result of call to 'Ref' aliases the storage of temporary object}} use(t1); // cfg-note {{later used here}} return Ref(std::string()); // expected-warning {{returning address}} \ - // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'Ref' aliases the storage of temporary object}} } std::string_view test1_2() { @@ -933,7 +955,8 @@ std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) { // cfg-note {{result of call to 'get' aliases the storage of 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}} + // cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}} \ + // cfg-note {{result of call to 'get' aliases the storage of parameter 'r1'}} } std::string_view test2_2(Foo<std::string> r1, Foo<std::string_view> r2) { std::string_view t2 = Foo<std::string_view>().get(); @@ -1000,15 +1023,19 @@ 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}} - return s; //cfg-note {{returned here}} + 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}} \ + // cfg-note {{local variable 's' aliases the storage of parameter 'strings'}} } 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}} - return s.data(); //cfg-note {{returned here}} + 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}} \ + // cfg-note {{local variable 's' aliases the storage of parameter 'strings'}} } return ""; } @@ -1021,15 +1048,20 @@ 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 {{expression 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 {{expression aliases the storage of parameter 'strings_or'}} \ + // cfg-note {{result of call to 'begin' aliases the storage of parameter 'strings_or'}} + return s; // cfg-note {{returned here}} \ + // cfg-note {{expression aliases the storage of parameter 'strings_or'}} } return ""; } @@ -1038,7 +1070,10 @@ 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'}} \ + // cfg-note {{expression aliases the storage of local variable 'strings'}} \ + // cfg-note {{result of call to 'data' aliases the storage of local variable 'strings'}} } void operator_star_arrow_reference() { @@ -1168,7 +1203,8 @@ struct Foo2 { }; struct Test { - Test(Foo2 foo) : bar(foo.bar.get()), // cfg-warning-re {{stack memory associated with parameter 'foo' may escape to the field 'bar' which will dangle. {{.*}} may have been moved}} + Test(Foo2 foo) : bar(foo.bar.get()), // cfg-warning-re {{stack memory associated with parameter 'foo' may escape to the field 'bar' which will dangle. {{.*}} may have been moved}} \ + // cfg-note {{result of call to 'get' aliases the storage of parameter 'foo'}} storage(std::move(foo.bar)) {}; // cfg-note {{potentially moved here}} Bar* bar; // cfg-note {{this field dangles}} @@ -1187,7 +1223,8 @@ struct StatusOr { const char* foo() { StatusOr<std::string> s; return s->data(); // expected-warning {{address of stack memory associated with local variable}} \ - // cfg-warning {{stack memory associated with local variable 's' is returned}} cfg-note {{returned here}} + // cfg-warning {{stack memory associated with local variable 's' is returned}} cfg-note {{returned here}} \ + // cfg-note {{expression aliases the storage of local variable 's'}} StatusOr<std::string_view> s2; return s2->data(); diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c index 42adbbb3f0628..ddab01d531ad5 100644 --- a/clang/test/Sema/LifetimeSafety/safety-c.c +++ b/clang/test/Sema/LifetimeSafety/safety-c.c @@ -181,7 +181,8 @@ void *void_pointer_dereference(void) { int *atomic_pointer_declref(void) { int value; _Atomic(int *) p = &value; // expected-warning {{stack memory associated with local variable 'value' is returned}} - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'value'}} } int *atomic_pointer_static(void) { @@ -193,7 +194,8 @@ int *atomic_pointer_static(void) { int **atomic_pointer_multilevel(void) { int *inner; _Atomic(int **) p = &inner; // expected-warning {{stack memory associated with local variable 'inner' is returned}} - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'inner'}} } // In C, a pointer compound assignment is a prvalue; its result still carries diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp index f3b042cce74b2..042138cb979d2 100644 --- a/clang/test/Sema/LifetimeSafety/safety.cpp +++ b/clang/test/Sema/LifetimeSafety/safety.cpp @@ -531,7 +531,8 @@ View conditional_assign_both_branches(const MyObj& safe, bool c) { else { p = safe; } - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 's'}} } @@ -539,14 +540,16 @@ View reassign_safe_to_local(const MyObj& safe) { MyObj local; View p = safe; p = local; // expected-warning {{stack memory associated with local variable 'local' is returned}} - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'local'}} } View pointer_chain_to_local() { MyObj local; View p1 = local; // expected-warning {{stack memory associated with local variable 'local' is returned}} - View p2 = p1; - return p2; // expected-note {{returned here}} + View p2 = p1; // expected-note {{local variable 'p1' aliases the storage of local variable 'local'}} + return p2; // expected-note {{returned here}} \ + // expected-note {{local variable 'p2' aliases the storage of local variable 'local'}} } View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) { @@ -555,11 +558,13 @@ View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) { View p; if (c1) { p = local1; // expected-warning {{stack memory associated with local variable 'local1' is returned}} - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'local1'}} } else if (c2) { p = local2; // expected-warning {{stack memory associated with local variable 'local2' is returned}} - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'local2'}} } p = safe; return p; @@ -578,7 +583,9 @@ View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) { else { p = safe; } - return p; // expected-note 2 {{returned here}} + return p; // expected-note 2 {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'local1'}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'local2'}} } View direct_return_of_local() { @@ -646,7 +653,8 @@ const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) { const int& return_pointer_to_parameter_via_reference_1(int a) { const int* d = &a; // expected-warning {{stack memory associated with parameter 'a' is returned}} - return *d; // expected-note {{returned here}} + return *d; // expected-note {{returned here}} \ + // expected-note {{local variable 'd' aliases the storage of parameter 'a'}} } const int& get_ref_to_local() { @@ -680,7 +688,8 @@ 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'}} return ref; // expected-note {{returned here}} } int* const test_ref_to_ptr_no_error() { @@ -741,7 +750,8 @@ View uar_before_uaf(const MyObj& safe, bool c) { MyObj local_obj; p = local_obj; // expected-warning {{stack memory associated with local variable 'local_obj' is returned}} if (c) { - return p; // expected-note {{returned here}} + return p; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'local_obj'}} } } p.use(); @@ -1620,11 +1630,13 @@ 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}} - // expected-note@-1 {{returned here}} + 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'}} \ + // expected-note {{returned here}} } void range_based_for_not_reference() { @@ -1740,16 +1752,20 @@ 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}} - // expected-note@-1 2 {{returned here}} + 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'}} \ + // expected-note {{result of call to 'MaxT<MyObj &>' aliases the storage of local variable 'ob'}} \ + // expected-note 2 {{returned here}} ob); // expected-warning {{stack memory associated with local variable 'ob' is returned}} } MyObj* call_max_with_obj_error() { MyObj oa, ob; - return &MaxT(oa, // expected-warning {{stack memory associated with local variable 'oa' is returned}} - // expected-note@-1 2 {{returned here}} + 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'}} \ + // expected-note {{result of call to 'MaxT<MyObj &>' aliases the storage of local variable 'ob'}} \ + // expected-note 2 {{returned here}} ob); // expected-warning {{stack memory associated with local variable 'ob' is returned}} } @@ -1757,7 +1773,9 @@ 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; @@ -1777,8 +1795,10 @@ 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}} - // expected-note@-1 2 {{returned here}} + 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'}} \ + // expected-note {{result of call to 'MaxT<View &>' aliases the storage of local variable 'vb'}} \ + // expected-note 2 {{returned here}} vb); // expected-warning {{stack memory associated with local variable 'vb' is returned}} } @@ -1786,8 +1806,10 @@ 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}} - // expected-note@-1 2 {{returned here}} + 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'}} \ + // expected-note {{result of call to 'MaxT<MaxFnLifetimeBound::NonTrivialPointer &>' aliases the storage of local variable 'vb'}} \ + // expected-note 2 {{returned here}} vb); // expected-warning {{stack memory associated with local variable 'vb' is returned}} } @@ -1949,24 +1971,32 @@ void bar() { namespace DereferenceViews { const MyObj& testDeref(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 {{local variable 'v' aliases the storage of parameter '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 {{local variable 'v' aliases the storage of parameter 'obj'}} \ + // expected-note {{expression aliases the storage of parameter 'obj'}} } const MyObj* testData(MyObj obj) { View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}} - return v.data(); // expected-note {{returned here}} + return v.data(); // expected-note {{returned here}} \ + // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}} } const int* testLifetimeboundAccessorOfMyObj(MyObj obj) { View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}} - const MyObj* ptr = v.data(); - return ptr->getData(); // expected-note {{returned here}} + 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'}} + return ptr->getData(); // expected-note {{returned here}} \ + // expected-note {{local variable 'ptr' aliases the storage of parameter 'obj'}} } 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 {{local variable 'v' aliases the storage of parameter 'obj'}} \ + // expected-note {{expression aliases the storage of parameter 'obj'}} } } // namespace DereferenceViews @@ -1989,18 +2019,22 @@ 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}} - return x; // expected-note {{returned here}} + 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}} \ + // expected-note {{local variable 'x' aliases the storage of parameter 'c'}} } return Global; } @@ -2135,7 +2169,8 @@ 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 {{expression aliases the storage of parameter 'a'}} 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}} @@ -2262,7 +2297,8 @@ View test1(std::string a) { View test2(std::string a) { View b = View(a); // expected-warning {{stack memory associated with parameter 'a' is returned}} - return b; // expected-note {{returned here}} + return b; // expected-note {{returned here}} \ + // expected-note {{local variable 'b' aliases the storage of parameter 'a'}} } View test3(std::string a) { @@ -2319,7 +2355,8 @@ namespace lambda_captures { auto return_ref_capture() { int local = 1; auto lambda = [&local]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}} - return lambda; // expected-note {{returned here}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}} } void safe_ref_capture() { @@ -2337,8 +2374,9 @@ 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; }; - return lambda; // expected-note {{returned here}} + auto lambda = [v]() { return v; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'obj'}} } void capture_view_by_value_safe() { @@ -2352,7 +2390,8 @@ auto capture_pointer_by_ref() { MyObj obj; MyObj* p = &obj; auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}} - return lambda; // expected-note {{returned here}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}} } auto capture_multiple() { @@ -2361,48 +2400,56 @@ auto capture_multiple() { &a, // expected-warning {{stack memory associated with local variable 'a' is returned}} &b // expected-warning {{stack memory associated with local variable 'b' is returned}} ]() { return a + b; }; - return lambda; // expected-note 2 {{returned here}} + return lambda; // expected-note 2 {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'a'}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'b'}} } 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; }; - return lambda; // expected-note {{returned here}} + auto lambda = [p]() { return p; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'x'}} } 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; }; - return lambda; // expected-note {{returned here}} + auto lambda = [q = p]() { return q; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'x'}} } 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; }; - return lambda; // expected-note {{returned here}} + auto lambda = [w = v]() { return w; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'obj'}} } 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(); }; - return outer; // expected-note {{returned here}} + auto outer = [inner]() { return inner(); }; // expected-note {{local variable 'inner' aliases the storage of local variable 'x'}} + return outer; // expected-note {{returned here}} \ + // expected-note {{local variable 'outer' aliases the storage of local variable 'x'}} } 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; - return lambda_copy; // expected-note {{returned here}} + auto lambda_copy = lambda; // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}} + return lambda_copy; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda_copy' aliases the storage of local variable 'local'}} } auto implicit_ref_capture() { int local = 1; auto lambda = [&]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}} - return lambda; // expected-note {{returned here}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}} } // TODO: Include the name of the variable in the diagnostic to improve @@ -2411,14 +2458,17 @@ auto implicit_ref_capture() { auto implicit_ref_capture_multiple() { int local = 1, local2 = 2; auto lambda = [&]() { return local + local2; }; // expected-warning {{stack memory associated with local variable 'local2' is returned}} expected-warning {{stack memory associated with local variable 'local' is returned}} - return lambda; // expected-note 2 {{returned here}} + return lambda; // expected-note 2 {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'local2'}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}} } auto implicit_value_capture() { MyObj obj; View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} - auto lambda = [=]() { return v; }; - return lambda; // expected-note {{returned here}} + auto lambda = [=]() { return v; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'obj'}} } auto* pointer_to_lambda_outlives() { @@ -2449,7 +2499,8 @@ auto capture_static_address_by_ref() { static int local = 1; int* p = &local; auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}} - return lambda; // expected-note {{returned here}} + return lambda; // expected-note {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}} } auto capture_multilevel_pointer() { @@ -2457,8 +2508,13 @@ 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; }; - return lambda; // expected-note 3 {{returned here}} + auto lambda = [=]() { return *p + **q + ***r; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}} \ + // expected-note {{local variable 'q' aliases the storage of local variable 'p'}} \ + // expected-note {{local variable 'r' aliases the storage of local variable 'q'}} + return lambda; // expected-note 3 {{returned here}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'x'}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}} \ + // expected-note {{local variable 'lambda' aliases the storage of local variable 'q'}} } } // namespace lambda_captures @@ -2775,9 +2831,11 @@ 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; - return b; // expected-note {{returned here}} + 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'}} + S b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}} + return b; // expected-note {{returned here}} \ + // expected-note {{local variable 'b' aliases the storage of local variable 'str'}} } void assignment_propagation() { @@ -2826,9 +2884,11 @@ S multiple_lifetimebound_params() { S s = getS2(str, std::string("temp")); // expected-warning {{stack memory associated with local variable 'str' is returned}} \ // expected-warning {{temporary object does not live long enough}} \ // expected-note {{result of call to 'getS2' aliases the storage of temporary object}} \ + // expected-note {{result of call to 'getS2' aliases the storage of local variable 'str'}} \ // expected-note {{temporary object is destroyed here}} return s; // expected-note {{returned here}} \ - // expected-note {{later used here}} + // expected-note {{later used here}} \ + // expected-note {{local variable 's' aliases the storage of local variable 'str'}} } // TODO: Diagnose [[clang::lifetimebound]] on functions whose return value @@ -2852,7 +2912,8 @@ void from_template_instantiation() { struct FieldInitFromLifetimebound { S value; // expected-note {{this field dangles}} - FieldInitFromLifetimebound() : value(getS(std::string("temp"))) {} // expected-warning {{stack memory associated with temporary object escapes to the field 'value' which will dangle}} + FieldInitFromLifetimebound() : value(getS(std::string("temp"))) {} // expected-warning {{stack memory associated with temporary object escapes to the field 'value' which will dangle}} \ + // expected-note {{result of call to 'getS' aliases the storage of temporary object}} }; S S::return_self_after_registration() const { @@ -2944,9 +3005,11 @@ 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; - return copy; // expected-note {{returned here}} + 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'}} + DefaultedOuter copy = o; // expected-note {{local variable 'o' aliases the storage of local variable 'str'}} + return copy; // expected-note {{returned here}} \ + // expected-note {{local variable 'copy' aliases the storage of local variable 'str'}} } std::string_view getSV(S s [[clang::lifetimebound]]); @@ -2992,9 +3055,12 @@ 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(); - return sv; // expected-note {{returned here}} + 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'}} + 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'}} + return sv; // expected-note {{returned here}} \ + // expected-note {{local variable 'sv' aliases the storage of local variable 'ups'}} } void owner_outlives_lifetimebound_source() { @@ -3082,7 +3148,8 @@ void binary_conditional_throw_void(int *in) { int *binary_conditional_throw_dangling() { int x; int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}} - return p ?: throw 1; // expected-note {{returned here}} + return p ?: throw 1; // expected-note {{returned here}} \ + // expected-note {{local variable 'p' aliases the storage of local variable 'x'}} } #endif @@ -3106,12 +3173,14 @@ 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'}} } 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'}} } int *noreturn_dead_false(bool cond, int *num) { int local = 0; @@ -3720,16 +3789,18 @@ 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; - return f2; // expected-note {{returned here}} + std::function<void()> f2 = f; // expected-note {{local variable 'f' aliases the storage of local variable 'x'}} + return f2; // expected-note {{returned here}} \ + // expected-note {{local variable 'f2' aliases the storage of local variable 'x'}} } 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; - return f2; // expected-note {{returned here}} + f2 = f; // expected-note {{local variable 'f' aliases the storage of local variable 'x'}} + return f2; // expected-note {{returned here}} \ + // expected-note {{local variable 'f2' aliases the storage of local variable 'x'}} } std::function<void()> chained_copy_assign() { @@ -3737,16 +3808,19 @@ 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; - return f3; // expected-note {{returned here}} + f3 = f2 = f; // expected-note {{local variable 'f' aliases the storage of local variable 'x'}} \ + // expected-note {{expression aliases the storage of local variable 'x'}} + return f3; // expected-note {{returned here}} \ + // expected-note {{local variable 'f3' aliases the storage of local variable 'x'}} } 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); - return f2; // expected-note {{returned here}} + 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}} \ + // expected-note {{local variable 'f2' aliases the storage of local variable 'x'}} } std::function<void()> reassign_safe_then_unsafe() { @@ -3754,7 +3828,8 @@ std::function<void()> reassign_safe_then_unsafe() { int local = 2; std::function<void()> f = []() { (void)safe; }; f = [&local]() { (void)local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}} - return f; // expected-note {{returned here}} + return f; // expected-note {{returned here}} \ + // expected-note {{local variable 'f' aliases the storage of local variable 'local'}} } std::function<void()> reassign_unsafe_then_safe() { @@ -3841,18 +3916,21 @@ 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}} \ + // expected-note {{expression aliases the storage of local variable 'opt'}} \ // 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}} \ + // expected-note {{result of call to 'value' aliases the storage of local variable 'opt'}} \ // 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}} \ + // expected-note {{expression aliases the storage of local variable 'opt'}} \ // expected-note {{returned here}} } @@ -3873,27 +3951,30 @@ 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); - return v2; // expected-note {{returned here}} + 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}} \ + // expected-note {{local variable 'v2' aliases the storage of local variable 'o'}} } 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; @@ -3922,7 +4003,8 @@ View use_after_return_capture_by() { MyObj a; View res; setCaptureBy(res, a); // expected-warning {{stack memory associated with local variable 'a' is returned}} - return res; // expected-note {{returned here}} + return res; // expected-note {{returned here}} \ + // expected-note {{local variable 'res' aliases the storage of local variable 'a'}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
