https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/199345
>From 02bbdc2c415f3364c7b27057758c10a9f594f03d Mon Sep 17 00:00:00 2001 From: Yuan Suo <[email protected]> Date: Fri, 22 May 2026 15:10:28 +0800 Subject: [PATCH 1/3] [LifetimeSafety] Introduce buildOriginFlowChain for use-after-scope 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. Signed-off-by: Yuan Suo <[email protected]> --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 7 ++- .../clang/Basic/DiagnosticSemaKinds.td | 1 + clang/lib/Analysis/LifetimeSafety/Checker.cpp | 56 ++++++++++++++++++- clang/lib/Sema/SemaLifetimeSafety.h | 25 ++++++++- clang/test/Sema/warn-lifetime-safety.cpp | 6 +- 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 398cce1395854..42f7d80f90a02 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -61,9 +61,10 @@ class LifetimeSafetySemaHelper { LifetimeSafetySemaHelper() = default; virtual ~LifetimeSafetySemaHelper() = default; - virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc) {} + virtual void + reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, + const Expr *MovedExpr, SourceLocation FreeLoc, + llvm::SmallVector<const Expr *> OriginExprChain) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 077aace321264..54fb4c2fbc6c8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11047,6 +11047,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_note_alias_chain : Note<"%0 aliases the storage of %1">; def warn_lifetime_safety_intra_tu_param_suggestion : Warning<"parameter in intra-TU function should be marked " diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index d6d4ec6b5617e..2947c70301a44 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -260,10 +260,25 @@ class LifetimeChecker { SemaHelper->reportUseAfterInvalidation( InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr); - } else + } else { // Scope-based expiry (use-after-scope). + + llvm::SmallVector<OriginID> OriginFlowChain; + + for (const OriginList *Cur = UF->getUsedOrigins(); Cur; + Cur = Cur->peelOuterOrigin()) + if (LoanPropagation.getLoans(Cur->getOuterOriginID(), UF) + .contains(LID)) + OriginFlowChain = LoanPropagation.buildOriginFlowChain( + FactMgr, UF, Cur->getOuterOriginID(), LID); + + const llvm::SmallVector<const Expr *> OriginExprChain = + buildExprOrDeclChain(OriginFlowChain); SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(), - MovedExpr, ExpiryLoc); + MovedExpr, ExpiryLoc, + OriginExprChain); + } + } else if (const auto *OEF = CausingFact.dyn_cast<const OriginEscapesFact *>()) { if (Warning.InvalidatedByExpr) { @@ -487,6 +502,43 @@ class LifetimeChecker { } } } + + /// Retrieve a list of reliable expressions from OriginIFlowChain that + /// can be used for Sema warnings. + /// + /// Although the AST node corresponding to Origin can be either a + /// `const Expr *` or a `const ValueDecl *`, `buildOriginFlowChain` only + /// collects Origins from RHS expressions. Therefore, we do not need to + /// handle non-expression cases here. + llvm::SmallVector<const Expr *> + buildExprOrDeclChain(llvm::ArrayRef<OriginID> OriginFlowChain) { + llvm::SmallVector<const Expr *> rs; + const SourceManager &SM = AST.getSourceManager(); + + auto InsertOrReplace = [&rs, &SM](const Expr *NewNode) { + if (!NewNode) + return; + SourceLocation NewLocation = NewNode->getExprLoc(); + if (NewLocation.isInvalid()) + return; + + const Expr *LastNode = rs.back(); + SourceLocation LastLocation = LastNode->getExprLoc(); + if (SM.getSpellingLineNumber(LastLocation) == + SM.getSpellingLineNumber(NewLocation)) + rs.back() = NewNode; + else + rs.push_back(NewNode); + }; + + for (const OriginID CurrOID : OriginFlowChain) + if (!rs.empty()) + InsertOrReplace(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()); + else + rs.push_back(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()); + + return rs; + } }; } // namespace diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 6da4953dea56d..1ce4ddea838b7 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -55,6 +55,15 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { return false; } +inline StringRef formatExpr(const Expr *E) { + const Expr *PureExpr = E->IgnoreImpCasts(); + if (const DeclRefExpr *DRExpr = dyn_cast<DeclRefExpr>(PureExpr)) + return DRExpr->getDecl()->getName(); + + // TODO: Add support for more expression types. + return ""; +} + class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: @@ -62,17 +71,31 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, - SourceLocation FreeLoc) override { + SourceLocation FreeLoc, llvm::SmallVector<const Expr *> OriginExprChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved : diag::warn_lifetime_safety_use_after_scope; S.Diag(IssueExpr->getExprLoc(), DiagID) << getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange(); + if (MovedExpr) S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) << MovedExpr->getSourceRange(); S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here); + + StringRef IssueStr; + for (const Expr *CurrExpr : reverse(OriginExprChain)) { + if (IssueStr.empty()) { + IssueStr = formatExpr(CurrExpr); + continue; + } + + S.Diag(CurrExpr->getBeginLoc(), + diag::note_lifetime_safety_aliases_storage) + << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr; + } + S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); } diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 06097d4600af5..90708bd4c3e15 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-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 {{p aliases the storage of s}} } // expected-note {{destroyed here}} (void)*q; // expected-note {{later used here}} } @@ -1759,8 +1759,8 @@ void test_temporary() { 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 {{object whose reference is captured does not live long enough}} + z = zz; // expected-note {{aliases the storage of s}} } // expected-note {{destroyed here}} (void)z; // expected-note {{later used here}} } >From d6a2df274463e6490f17bf17617334328bb86c8b Mon Sep 17 00:00:00 2001 From: Yuan Suo <[email protected]> Date: Thu, 28 May 2026 09:58:12 +0800 Subject: [PATCH 2/3] Reduce the side effects of buildExprOrDeclChain and apply some minor cleanups Previously, `buildExprOrDeclChain` was responsible not only for constructing an expression list, but also for filtering out expressions that should not be emitted. However, this filtering logic does not need to be handled there. Signed-off-by: Yuan Suo <[email protected]> --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 5 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 36 +++----------- clang/lib/Sema/SemaLifetimeSafety.h | 48 ++++++++++++++----- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 42f7d80f90a02..d635105043b26 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -61,10 +61,13 @@ class LifetimeSafetySemaHelper { LifetimeSafetySemaHelper() = default; virtual ~LifetimeSafetySemaHelper() = default; + virtual void + reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {} + virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, - llvm::SmallVector<const Expr *> OriginExprChain) {} + llvm::ArrayRef<const Expr *> OriginExprChain) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 2947c70301a44..3abdf92e2989a 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -503,40 +503,18 @@ class LifetimeChecker { } } - /// Retrieve a list of reliable expressions from OriginIFlowChain that - /// can be used for Sema warnings. + /// Extract expressions from the origin flow chain for diagnostic purposes. /// - /// Although the AST node corresponding to Origin can be either a - /// `const Expr *` or a `const ValueDecl *`, `buildOriginFlowChain` only - /// collects Origins from RHS expressions. Therefore, we do not need to - /// handle non-expression cases here. + /// 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 *> buildExprOrDeclChain(llvm::ArrayRef<OriginID> OriginFlowChain) { llvm::SmallVector<const Expr *> rs; - const SourceManager &SM = AST.getSourceManager(); - - auto InsertOrReplace = [&rs, &SM](const Expr *NewNode) { - if (!NewNode) - return; - SourceLocation NewLocation = NewNode->getExprLoc(); - if (NewLocation.isInvalid()) - return; - - const Expr *LastNode = rs.back(); - SourceLocation LastLocation = LastNode->getExprLoc(); - if (SM.getSpellingLineNumber(LastLocation) == - SM.getSpellingLineNumber(NewLocation)) - rs.back() = NewNode; - else - rs.push_back(NewNode); - }; - for (const OriginID CurrOID : OriginFlowChain) - if (!rs.empty()) - InsertOrReplace(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()); - else - rs.push_back(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()); - + if (const Expr *CurrExpr = + FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr()) + rs.push_back(CurrExpr); return rs; } }; diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 1ce4ddea838b7..a4f0b7db9705c 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -64,11 +64,47 @@ inline StringRef formatExpr(const Expr *E) { return ""; } +inline bool shouldShowInAliasingChain(const Expr *CurrExpr, + const Expr *LastExpr) { + if (isa<ImplicitCastExpr>(CurrExpr)) + return false; + if (isa<ParenExpr>(CurrExpr)) + return false; + if (isa<CastExpr>(CurrExpr)) + return false; + if (CurrExpr->getSourceRange().isInvalid()) + return false; + + if (LastExpr && CurrExpr->getSourceRange() == LastExpr->getSourceRange()) + return false; + + return true; +} + class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} + void + reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) override { + StringRef IssueStr; + const Expr *LastExpr = nullptr; + for (const Expr *CurrExpr : reverse(OriginExprChain)) { + if (IssueStr.empty()) { + IssueStr = formatExpr(CurrExpr); + continue; + } + + if (shouldShowInAliasingChain(CurrExpr, LastExpr)) { + S.Diag(CurrExpr->getBeginLoc(), + diag::note_lifetime_safety_note_alias_chain) + << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr; + LastExpr = CurrExpr; + } + } + } + void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, llvm::SmallVector<const Expr *> OriginExprChain) override { @@ -84,17 +120,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { << MovedExpr->getSourceRange(); S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here); - StringRef IssueStr; - for (const Expr *CurrExpr : reverse(OriginExprChain)) { - if (IssueStr.empty()) { - IssueStr = formatExpr(CurrExpr); - continue; - } - - S.Diag(CurrExpr->getBeginLoc(), - diag::note_lifetime_safety_aliases_storage) - << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr; - } + reportAliasingChain(OriginExprChain); S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); >From e7ecc7869cb12e4de594e7c07f61585bd60e249d Mon Sep 17 00:00:00 2001 From: Yuan Suo <[email protected]> Date: Thu, 28 May 2026 21:08:16 +0800 Subject: [PATCH 3/3] rebase to use getDiagSubjectDescription Signed-off-by: Yuan Suo <[email protected]> --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 3 - .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 4 +- clang/lib/Sema/SemaLifetimeSafety.h | 98 +++++++------- .../Sema/warn-lifetime-analysis-nocfg.cpp | 32 +++-- .../Sema/warn-lifetime-safety-suggestions.cpp | 6 +- clang/test/Sema/warn-lifetime-safety.cpp | 127 ++++++++++++------ 7 files changed, 163 insertions(+), 109 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index d635105043b26..78f39f4609358 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -61,9 +61,6 @@ class LifetimeSafetySemaHelper { LifetimeSafetySemaHelper() = default; virtual ~LifetimeSafetySemaHelper() = default; - virtual void - reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {} - virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 54fb4c2fbc6c8..0e1d13653a169 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11047,7 +11047,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_note_alias_chain : Note<"%0 aliases the storage of %1">; +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 3abdf92e2989a..2f5db5bd109c8 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -268,9 +268,11 @@ class LifetimeChecker { for (const OriginList *Cur = UF->getUsedOrigins(); Cur; Cur = Cur->peelOuterOrigin()) if (LoanPropagation.getLoans(Cur->getOuterOriginID(), UF) - .contains(LID)) + .contains(LID)) { OriginFlowChain = LoanPropagation.buildOriginFlowChain( FactMgr, UF, Cur->getOuterOriginID(), LID); + break; + } const llvm::SmallVector<const Expr *> OriginExprChain = buildExprOrDeclChain(OriginFlowChain); diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index a4f0b7db9705c..0fe439b3a8569 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -55,59 +55,15 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { return false; } -inline StringRef formatExpr(const Expr *E) { - const Expr *PureExpr = E->IgnoreImpCasts(); - if (const DeclRefExpr *DRExpr = dyn_cast<DeclRefExpr>(PureExpr)) - return DRExpr->getDecl()->getName(); - - // TODO: Add support for more expression types. - return ""; -} - -inline bool shouldShowInAliasingChain(const Expr *CurrExpr, - const Expr *LastExpr) { - if (isa<ImplicitCastExpr>(CurrExpr)) - return false; - if (isa<ParenExpr>(CurrExpr)) - return false; - if (isa<CastExpr>(CurrExpr)) - return false; - if (CurrExpr->getSourceRange().isInvalid()) - return false; - - if (LastExpr && CurrExpr->getSourceRange() == LastExpr->getSourceRange()) - return false; - - return true; -} - class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} void - reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) override { - StringRef IssueStr; - const Expr *LastExpr = nullptr; - for (const Expr *CurrExpr : reverse(OriginExprChain)) { - if (IssueStr.empty()) { - IssueStr = formatExpr(CurrExpr); - continue; - } - - if (shouldShowInAliasingChain(CurrExpr, LastExpr)) { - S.Diag(CurrExpr->getBeginLoc(), - diag::note_lifetime_safety_note_alias_chain) - << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr; - LastExpr = CurrExpr; - } - } - } - - void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc, llvm::SmallVector<const Expr *> OriginExprChain) override { + reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr, + const Expr *MovedExpr, SourceLocation FreeLoc, + llvm::ArrayRef<const Expr *> OriginExprChain) override { unsigned DiagID = MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved : diag::warn_lifetime_safety_use_after_scope; @@ -529,6 +485,54 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { return ""; } + const Expr *extractExpr(const Expr *E) { + // FIXME: Ideally, this should use IgnoreParenImpCasts(). + // However, according to the comment on IgnoreParenImpCasts(), + // it is not fully equivalent to IgnoreImpCasts() + IgnoreParens(). + // Once the FIXME in IgnoreParenImpCasts() is resolved, + // this can be switched to use IgnoreParenImpCasts(). + const Expr *PureExpr = E->IgnoreImpCasts()->IgnoreParens(); + + if (const auto *UO = dyn_cast<UnaryOperator>(PureExpr)) + return UO->getSubExpr(); + // For a BinaryOperator, there is only one relevant case: assignment + // chains. Therefore, we only need to consider the LHS expression. + if (const auto *BO = dyn_cast<BinaryOperator>(PureExpr)) + return BO->getLHS(); + + // TODO: Handle other expression types. + return PureExpr; + } + + void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) { + std::string IssueStr; + const Expr *LastExpr = nullptr; + for (const Expr *CurrExpr : reverse(OriginExprChain)) { + if (IssueStr.empty()) { + IssueStr = getDiagSubjectDescription(CurrExpr); + LastExpr = CurrExpr; + continue; + } + + const Expr *ExtractedExpr = extractExpr(CurrExpr); + if (LastExpr && + ExtractedExpr->getSourceRange() == LastExpr->getSourceRange()) + continue; + + // FIXME: Because getDiagSubjectDescription and extractExpr is not fully + // implemented yet, some diagnostic that should have been issued are + // currently being skipped here. + std::string ExprName = getDiagSubjectDescription(ExtractedExpr); + if (ExprName.empty()) + continue; + + S.Diag(ExtractedExpr->getBeginLoc(), + diag::note_lifetime_safety_aliases_storage) + << ExtractedExpr->getSourceRange() << ExprName << IssueStr; + LastExpr = ExtractedExpr; + } + } + Sema &S; }; diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 41b07771c52c1..61e162f01947f 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -245,7 +245,8 @@ std::string_view containerWithAnnotatedElements() { 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 {{local temporary object aliases the storage of local temporary object}} use(c1); // cfg-note {{later used here}} // no warning on constructing from gsl-pointer @@ -862,7 +863,8 @@ std::string_view test1_1() { // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} 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 {{local temporary object 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}} @@ -873,7 +875,8 @@ std::string_view test1_2() { // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} 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 {{local temporary object aliases the storage of local temporary object}} use(t2); // cfg-note {{later used here}} return TakeSv(std::string()); // expected-warning {{returning address}} \ @@ -885,7 +888,8 @@ std::string_view test1_3() { // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(t3); // cfg-note {{later used here}} t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \ - // cfg-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 {{local temporary object 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}} @@ -910,7 +914,8 @@ std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) { // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(t1); // cfg-note {{later used here}} t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \ - // cfg-warning {{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 {{local temporary object 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}} @@ -941,7 +946,7 @@ struct [[gsl::Pointer]] Pointer { Pointer test3(Bar bar) { Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p); // cfg-note {{later used here}} - p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} + p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}} use(p); // cfg-note {{later used here}} return bar; // expected-warning {{address of stack}} cfg-warning {{stack memory associated with parameter 'bar' is returned}} cfg-note {{returned here}} } @@ -1028,9 +1033,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 {{local temporary object 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 {{local temporary object 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 {{local temporary object aliases the storage of local temporary object}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } @@ -1042,9 +1050,9 @@ 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 {{local temporary object 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 {{local temporary object 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 {{local temporary object aliases the storage of local temporary object}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index f5a3cf89e4c8d..85b80a7af811b 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -599,7 +599,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 {{local temporary object aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)f; // expected-note {{later used here}} } @@ -622,7 +623,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 {{local temporary object aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)ptr; // expected-note {{later used here}} } diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 90708bd4c3e15..d9fb06154c67e 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -92,7 +92,7 @@ void pointer_chain() { { MyObj s; p = &s; // expected-warning {{does not live long enough}} - q = p; // expected-note {{p aliases the storage of s}} + 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}} } @@ -138,7 +138,8 @@ void multiple_pointers_chained() { { MyObj s; MyObj* obj1, *obj2; - p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}} + p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}} \ + // expected-note 2 {{aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -696,7 +697,7 @@ 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 {{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 {{local temporary object 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 {{local temporary object aliases the storage of local variable 'obj1'}} \ + // expected-note {{local temporary object 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 {{local temporary object 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 {{local temporary object 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 {{local temporary object aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -913,7 +919,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 {{local variable 'obj' aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -945,8 +952,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); - ptr = &ref; + const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}} + ptr = &ref; // expected-note {{local variable 'ref' aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)*ptr; // expected-note {{later used here}} } @@ -972,7 +979,8 @@ void lifetimebound_ctor_functional_cast() { LifetimeBoundCtor v; { MyObj obj; - v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}} + v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{local temporary object aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} } @@ -981,7 +989,8 @@ void lifetimebound_ctor_c_style_cast() { LifetimeBoundCtor v; { MyObj obj; - v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}} + v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{local temporary object aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} } @@ -990,7 +999,8 @@ void lifetimebound_ctor_static_cast() { LifetimeBoundCtor v; { MyObj obj; - v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}} + v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}} \ + // expected-note {{local temporary object aliases the storage of local variable 'obj'}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} } @@ -999,7 +1009,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 {{local temporary object aliases the storage of local variable 'obj'}} } // tu-note {{destroyed here}} (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 {{local temporary object 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 {{local temporary object 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 {{local temporary object 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 {{local temporary object aliases the storage of local variable 'obj_short'}} } // tu-note {{destroyed here}} (void)ptr; // tu-note {{later used here}} } @@ -1235,6 +1250,16 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) { (void)*p; // expected-note 4 {{later used here}} } +void simpleparen() { + MyObj* p; + { + MyObj a; + MyObj* b = &a; // expected-warning {{local variable 'a' does not live long enough}} + p = (((b))); // expected-note {{local variable 'b' aliases the storage of local variable 'a'}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + void parentheses(bool cond) { MyObj* p; { @@ -1324,7 +1349,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 {{local temporary object aliases the storage of local variable 'string_or'}} value(); } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} @@ -1415,7 +1441,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 {{local temporary object aliases the storage of local variable 'smart_ptr'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1621,7 +1648,8 @@ 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 {{local temporary object aliases the storage of local variable 'a'}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} const int* p; @@ -1660,7 +1688,8 @@ 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 {{local temporary object 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}} (void)y; // expected-note {{used here}} @@ -1759,8 +1788,8 @@ void test_temporary() { std::string_view z; { S s; - const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} - z = zz; // expected-note {{aliases the storage of s}} + const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}} + z = zz; // expected-note {{local temporary object aliases the storage of local variable 's'}} } // expected-note {{destroyed here}} (void)z; // expected-note {{later used here}} } @@ -1790,7 +1819,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 {{local temporary object aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1815,8 +1845,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 {{local temporary object aliases the storage of local variable 'u'}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1833,7 +1864,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}} } @@ -1937,7 +1968,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 {{local temporary object aliases the storage of local variable 'opt'}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -2356,7 +2388,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 {{local temporary object aliases the storage of local variable 'f'}} } // expected-note {{destroyed here}} use(value); // expected-note {{later used here}} } @@ -2365,7 +2398,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 {{local temporary object aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} use(s); // expected-note {{later used here}} } @@ -2387,8 +2421,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 {{local temporary object 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}} } @@ -2398,7 +2433,7 @@ void chained_defaulted_assignment_propagation() { { std::string str{"abc"}; S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} - c = b = a; + c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}} } // expected-note {{destroyed here}} use(c); // expected-note {{later used here}} } @@ -2596,7 +2631,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 {{local temporary object aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)ups; // expected-note {{later used here}} } @@ -2619,7 +2655,8 @@ void local_pointer() { Pointer<int> p; { int v; - p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} + p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \ + // expected-note {{local temporary object aliases the storage of local variable 'v'}} } // expected-note {{destroyed here}} use(*p); // expected-note {{later used here}} } @@ -2630,9 +2667,12 @@ void nested_local_pointer() { Pointer<Bar> p; { Bar v; - p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} - pp = Pointer(p); - ppp = Pointer(pp); + p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \ + // expected-note {{local temporary object aliases the storage of local variable 'v'}} + pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v'}} \ + // expected-note {{local temporary object aliases the storage of local variable 'v'}} + ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}} \ + // expected-note {{local temporary object aliases the storage of local variable 'v'}} } // expected-note {{destroyed here}} use(***ppp); // expected-note {{later used here}} } @@ -2759,7 +2799,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}} } @@ -3316,7 +3356,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 {{local temporary object aliases the storage of local variable 'local'}} } // expected-note {{destroyed here}} (void)f; // expected-note {{later used here}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
