https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/188467
>From bfa790315f83314e8cb7eb226f55e0503aee5cef Mon Sep 17 00:00:00 2001 From: suoyuan666 <[email protected]> Date: Mon, 16 Mar 2026 09:55:36 +0800 Subject: [PATCH] [LifetimeSafety] Trace assignment history for use-after-scope errors Currently, when the compiler detects a use-after-scope error, it only emits notes for the point of use, the destruction point, and the initial declaration of the destroyed variable. In complex control flows, it can be difficult for users to figure out how the dangling pointer/reference reached the use point. This patch introduces reverse tracing at the CFGBlock corresponding to UseFact->getUseExpr, using BFS to trace the assignment history of dangling variables. It adds extra comments to the assignment statements, clearly showing the event chain that led to the use-after-scope error. Fixes #177986 Signed-off-by: suoyuan666 <[email protected]> --- .../Analyses/LifetimeSafety/AssignmentQuery.h | 48 +++ .../Analyses/LifetimeSafety/LifetimeSafety.h | 14 +- .../clang/Basic/DiagnosticSemaKinds.td | 1 + .../LifetimeSafety/AssignmentQuery.cpp | 226 +++++++++++ .../Analysis/LifetimeSafety/CMakeLists.txt | 1 + clang/lib/Analysis/LifetimeSafety/Checker.cpp | 16 +- clang/lib/Sema/SemaLifetimeSafety.h | 27 +- .../Sema/warn-lifetime-analysis-nocfg.cpp | 85 ++-- .../Sema/warn-lifetime-safety-cfg-bailout.cpp | 4 +- .../Sema/warn-lifetime-safety-suggestions.cpp | 9 +- clang/test/Sema/warn-lifetime-safety.cpp | 369 ++++++++++++------ 11 files changed, 633 insertions(+), 167 deletions(-) create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h create mode 100644 clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h new file mode 100644 index 0000000000000..dd36b3e8f1bcb --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h @@ -0,0 +1,48 @@ +//===- AssignmentQuery.cpp - C++ Lifetime Safety Analysis -*- C++-*-==========// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines and enforces the lifetime safety policy. It detects +// use-after-free errors by examining loan expiration points and checking if +// any live origins hold the expired loans. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_ASSIGNMENTQUERY_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_ASSIGNMENTQUERY_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h" +#include "clang/Analysis/Analyses/LifetimeSafety/MovedLoans.h" + +namespace clang::lifetimes::internal { + +using DestOriginExpr = + llvm::PointerUnion<const DeclRefExpr *, const CXXTemporaryObjectExpr *, + const CallExpr *>; + +struct AssignmentQueryContext { + FactManager &FactMgr; + AnalysisDeclContext &ADC; + const LoanPropagationAnalysis &LoanPropagation; + const LiveOriginsAnalysis &LiveOrigins; + const MovedLoansAnalysis &MovedLoans; +}; + +/// Retrieves a list of assignment chains for use-after-scope analysis. +/// +/// To help users understand the data flow, we track where the problematic +/// address originated. +const std::optional<llvm::SmallVector<DestOriginExpr>> +getAssignmentChain(const AssignmentQueryContext &Context, + const Fact *CausingFact, const LoanID &EndLoanID); +} // namespace clang::lifetimes::internal + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_ASSIGNMENTQUERY_H diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 08038dd096685..803f04407d385 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -21,6 +21,7 @@ #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H #include "clang/AST/Decl.h" +#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h" #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h" @@ -45,6 +46,11 @@ enum class SuggestionScope { IntraTU // For suggestions on definitions local to a Translation Unit. }; +using OriginSrcExpr = + llvm::PointerUnion<const DeclRefExpr *, const CXXTemporaryObjectExpr *, + const CallExpr *>; +using AssignmentPair = std::pair<OriginSrcExpr, const ValueDecl *>; + /// Abstract interface for operations requiring Sema access. /// /// This class exists to break a circular dependency: the LifetimeSafety @@ -60,9 +66,11 @@ class LifetimeSafetySemaHelper { LifetimeSafetySemaHelper() = default; virtual ~LifetimeSafetySemaHelper() = default; - virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc) {} + virtual void reportUseAfterFree( + const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, + const std::optional<llvm::SmallVector<internal::DestOriginExpr>> + AliasList, + SourceLocation FreeLoc) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index db1e3630435d0..16c64ecadc1d9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10999,6 +10999,7 @@ def note_lifetime_safety_dangling_static_here: Note<"this static storage dangles def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">; def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global storage">; def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">; +def note_lifetime_safety_note_alias_chain : Note<"variable `%0` is now an alias of `%1`">; def warn_lifetime_safety_intra_tu_param_suggestion : Warning<"parameter in intra-TU function should be marked " diff --git a/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp new file mode 100644 index 0000000000000..cac534de6cd42 --- /dev/null +++ b/clang/lib/Analysis/LifetimeSafety/AssignmentQuery.cpp @@ -0,0 +1,226 @@ +//===- AssignmentQuery.cpp - C++ Lifetime Safety Analysis -*- C++-*-==========// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines and enforces the lifetime safety policy. It detects +// use-after-free errors by examining loan expiration points and checking if +// any live origins hold the expired loans. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h" +#include "clang/AST/Expr.h" +#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +using namespace clang; +using namespace clang::lifetimes::internal; + +struct AliasAssignmentSearchResult { + const llvm::SmallVector<DestOriginExpr> Payload; + const bool SearchComplete; + const std::optional<OriginID> LastOrigin; +}; + +const std::optional<DestOriginExpr> getPureSrcExpr(const Expr *TargetExpr) { + if (!TargetExpr) + return std::nullopt; + const Expr *SExpr = TargetExpr->IgnoreParenCasts(); + if (!SExpr) + return std::nullopt; + + if (const auto *SDRExpr = llvm::dyn_cast<DeclRefExpr>(SExpr)) + return SDRExpr; + if (const auto *STMExpr = llvm::dyn_cast<CXXTemporaryObjectExpr>(SExpr)) + return STMExpr; + if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr)) + return SCExpr; + + if (const auto *SCCExpr = llvm::dyn_cast<CXXConstructExpr>(SExpr)) + if (SCCExpr->getNumArgs() > 0) + return getPureSrcExpr(SCCExpr->getArg(0)); + if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr)) + return getPureSrcExpr(SUOExpr->getSubExpr()); + if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr)) + return getPureSrcExpr(SCBExpr->getSubExpr()); + + return std::nullopt; +} + +const AliasAssignmentSearchResult getAssignmentChainCore( + const AssignmentQueryContext &Context, const CFGBlock *Block, + const LoanID EndLoanID, OriginID *TargetOID, + const std::optional<OriginID> LastOriginID = std::nullopt) { + llvm::SmallVector<DestOriginExpr> AliasStmts; + const auto Facts = Context.FactMgr.getFacts(Block); + bool FetchLoan = false; + auto IssueOriginID = LastOriginID; + const SourceManager &SM = Context.ADC.getASTContext().getSourceManager(); + + const auto DestOriginExpr2Expr = + [](const DestOriginExpr &TargetExpr) -> const Expr * { + if (const auto *DExpr = llvm::dyn_cast<const DeclRefExpr *>(TargetExpr)) + return DExpr; + if (const auto *DCTExpr = + llvm::dyn_cast<const CXXTemporaryObjectExpr *>(TargetExpr)) + return DCTExpr; + if (const auto *DCExpr = llvm::dyn_cast<const CallExpr *>(TargetExpr)) + return DCExpr; + return nullptr; + }; + + for (auto F = Facts.rbegin(), FEnd = Facts.rend(); F != FEnd; ++F) { + if (const auto *OFF = (*F)->getAs<OriginFlowFact>()) { + OFF->dump(llvm::outs(), Context.FactMgr.getLoanMgr(), + Context.FactMgr.getOriginMgr()); + if (IssueOriginID.has_value() && + OFF->getDestOriginID() == IssueOriginID.value()) + FetchLoan = true; + + if (OFF->getDestOriginID() == *TargetOID) { + const auto HeldLoans = + Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF); + + if (HeldLoans.contains(EndLoanID)) { + const auto TargetOrigin = + Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID()); + + const auto DExpr = getPureSrcExpr(TargetOrigin.getExpr()); + + if (DExpr.has_value()) { + if (AliasStmts.empty()) { + llvm::outs() << "Empty Insert!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; + AliasStmts.push_back(DExpr.value()); + } else { + const Expr *LastExpr = + DestOriginExpr2Expr(AliasStmts[AliasStmts.size() - 1]); + const Expr *CurrExpr = DestOriginExpr2Expr(DExpr.value()); + if (SM.getSpellingLineNumber(LastExpr->getBeginLoc()) == + SM.getSpellingLineNumber(CurrExpr->getBeginLoc())) { + if (llvm::isa<const DeclRefExpr *>( + AliasStmts[AliasStmts.size() - 1]) && + !llvm::isa<const DeclRefExpr *>(DExpr.value())) { + llvm::outs() << "Replace!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; + AliasStmts[AliasStmts.size() - 1] = DExpr.value(); + } + } else { + llvm::outs() << "Insert!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; + AliasStmts.push_back(DExpr.value()); + } + } + } + *TargetOID = OFF->getSrcOriginID(); + } + } + } else if (const auto *IF = (*F)->getAs<IssueFact>()) + if (IF->getLoanID() == EndLoanID) + IssueOriginID = IF->getOriginID(); + + if (FetchLoan) + return {AliasStmts, true, IssueOriginID}; + } + return {AliasStmts, false, IssueOriginID}; +} + +const std::optional<llvm::SmallVector<DestOriginExpr>> +getAssignmentChainInMultiBlock(const AssignmentQueryContext &Context, + const CFGBlock *StartBlock, + const LoanID EndLoanID, OriginID *StartOID) { + llvm::SmallVector<const CFGBlock *> PendingBlocks; + std::optional<DestOriginExpr> StartStmt = std::nullopt; + std::optional<DestOriginExpr> EndStmt = std::nullopt; + std::optional<OriginID> LastOriginID = std::nullopt; + llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks; + llvm::DenseMap<DestOriginExpr, DestOriginExpr> VistedExprs; + + const auto AliasStmtFilter = [&VistedExprs](const DestOriginExpr StartStmt, + const DestOriginExpr EndStmt) { + llvm::SmallVector<DestOriginExpr> AliasStmts; + for (auto Stmt = StartStmt; Stmt != EndStmt; Stmt = VistedExprs.at(Stmt)) { + AliasStmts.push_back(Stmt); + } + AliasStmts.push_back(EndStmt); + return AliasStmts; + }; + + PendingBlocks.push_back(StartBlock); + + for (size_t i = 0; i < PendingBlocks.size(); ++i) { + const CFGBlock *CurrBlock = PendingBlocks[i]; + const auto [BlockAliasList, Success, CurrLastOriginID] = + getAssignmentChainCore(Context, CurrBlock, EndLoanID, StartOID, + LastOriginID); + if (CurrLastOriginID.has_value()) + LastOriginID = CurrLastOriginID; + + if (!BlockAliasList.empty()) { + if (VistedExprs.empty()) + StartStmt = BlockAliasList[0]; + + for (size_t i = 0; i < BlockAliasList.size() - 1; ++i) + VistedExprs.insert({BlockAliasList[i], BlockAliasList[i + 1]}); + + if (EndStmt.has_value()) + VistedExprs.insert({EndStmt.value(), BlockAliasList[0]}); + + EndStmt = BlockAliasList[BlockAliasList.size() - 1]; + } + + if (Success && StartStmt.has_value() && EndStmt.has_value()) + return AliasStmtFilter(StartStmt.value(), EndStmt.value()); + + for (const auto Block : CurrBlock->preds()) + if (Block && VistedBlocks.insert(Block).second) + PendingBlocks.push_back(Block); + + if (VistedBlocks.size() >= 32 && StartStmt.has_value() && + EndStmt.has_value()) + return AliasStmtFilter(StartStmt.value(), EndStmt.value()); + } + + if (StartStmt.has_value() && EndStmt.has_value()) + return AliasStmtFilter(StartStmt.value(), EndStmt.value()); + + return std::nullopt; +} + +} // namespace + +namespace clang::lifetimes::internal { +const std::optional<llvm::SmallVector<DestOriginExpr>> +getAssignmentChain(const AssignmentQueryContext &Context, + const Fact *CausingFact, const LoanID &EndLoanID) { + const auto *UF = llvm::dyn_cast<UseFact>(CausingFact); + const CFGBlock *StartBlock = + Context.ADC.getCFGStmtMap()->getBlock(UF->getUseExpr()); + assert(StartBlock && "Searching Start CFGBlock failed"); + const CFGBlock *EndBlock = Context.ADC.getCFGStmtMap()->getBlock( + Context.FactMgr.getLoanMgr().getLoan(EndLoanID)->getIssuingExpr()); + assert(StartBlock && "Searching End CFGBlock failed"); + + for (const OriginList *Cur = UF->getUsedOrigins(); Cur; + Cur = Cur->peelOuterOrigin()) { + auto TargetOID = Cur->getOuterOriginID(); + if (StartBlock == EndBlock) { + AliasAssignmentSearchResult Result = + getAssignmentChainCore(Context, StartBlock, EndLoanID, &TargetOID); + if (!Result.Payload.empty()) + return Result.Payload; + } else { + auto Result = getAssignmentChainInMultiBlock(Context, StartBlock, + EndLoanID, &TargetOID); + if (Result.has_value()) + return Result.value(); + } + } + + return std::nullopt; +} +} // namespace clang::lifetimes::internal diff --git a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt index 247377c7256d9..6c4d4e123908a 100644 --- a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt +++ b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt @@ -1,4 +1,5 @@ add_clang_library(clangAnalysisLifetimeSafety + AssignmentQuery.cpp Checker.cpp Facts.cpp FactsGenerator.cpp diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 36477c6f67b52..20342d2b80838 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -14,6 +14,7 @@ #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" +#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h" #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h" @@ -67,6 +68,7 @@ class LifetimeChecker { FactManager &FactMgr; LifetimeSafetySemaHelper *SemaHelper; ASTContext &AST; + AnalysisDeclContext &ADC; static SourceLocation GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F) { @@ -89,7 +91,7 @@ class LifetimeChecker { LifetimeSafetySemaHelper *SemaHelper) : LoanPropagation(LoanPropagation), MovedLoans(MovedLoans), LiveOrigins(LiveOrigins), FactMgr(FM), SemaHelper(SemaHelper), - AST(ADC.getASTContext()) { + AST(ADC.getASTContext()), ADC(ADC) { for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>()) for (const Fact *F : FactMgr.getFacts(B)) if (const auto *EF = F->getAs<ExpireFact>()) @@ -243,10 +245,18 @@ class LifetimeChecker { SemaHelper->reportUseAfterInvalidation( InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr); - } else + } else { // Scope-based expiry (use-after-scope). + const AssignmentQueryContext AQContext = { + FactMgr, ADC, LoanPropagation, LiveOrigins, MovedLoans}; + const auto AliasExprs = getAssignmentChain(AQContext, UF, LID); + if (!AliasExprs.has_value()) + llvm::dbgs() << "GetAssignmentChain: Search variable assignment " + "chain failed\n"; + SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(), MovedExpr, - ExpiryLoc); + AliasExprs, ExpiryLoc); + } } else if (const auto *OEF = CausingFact.dyn_cast<const OriginEscapesFact *>()) { if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index e6f7e3d929f61..ab2b03bc26cb5 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -15,10 +15,12 @@ #ifndef LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H #define LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H +#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Lex/Lexer.h" #include "clang/Sema/Sema.h" +#include "llvm/Support/Casting.h" namespace clang::lifetimes { @@ -43,9 +45,11 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} - void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc) override { + void reportUseAfterFree( + const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, + const std::optional<llvm::SmallVector<internal::DestOriginExpr>> + AliasList, + SourceLocation FreeLoc) override { S.Diag(IssueExpr->getExprLoc(), MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved : diag::warn_lifetime_safety_use_after_scope) @@ -54,6 +58,23 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) << MovedExpr->getSourceRange(); S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here); + + if (AliasList.has_value()) { + for (auto AliasStmt = AliasList.value().rbegin(), + AliasStmtEnd = AliasList.value().rend(); + AliasStmt != AliasStmtEnd; ++AliasStmt) { + if (const auto *CurrDeclExpr = + llvm::dyn_cast<const DeclRefExpr *>((*AliasStmt))) { + S.Diag(CurrDeclExpr->getBeginLoc(), + diag::note_lifetime_safety_note_alias_chain) + << CurrDeclExpr->getDecl()->getNameAsString() + << llvm::dyn_cast<DeclRefExpr>(IssueExpr) + ->getDecl() + ->getNameAsString(); + } + } + } + S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) << UseExpr->getSourceRange(); } diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index a725119444e2f..247349cdb0cfb 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -147,14 +147,16 @@ MyLongPointerFromConversion global2; void initLocalGslPtrWithTempOwner() { MyIntPointer p = MyIntOwner{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `p` is now an alias of `MyIntOwner()`}} use(p); // cfg-note {{later used here}} MyIntPointer pp = p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' will be}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `p` is now an alias of `MyIntOwner()`}} use(p, pp); // cfg-note {{later used here}} - p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} \ + p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} cfg-note {{variable `p` is now an alias of `MyIntOwner()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(p); // cfg-note {{later used here}} @@ -162,16 +164,20 @@ void initLocalGslPtrWithTempOwner() { use(p, pp); global = MyIntOwner{}; // expected-warning {{object backing the pointer 'global' }} \ + // cfg-note {{variable `global` is now an alias of `MyIntOwner()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(global); // cfg-note {{later used here}} MyLongPointerFromConversion p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `p2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} use(p2); // cfg-note {{later used here}} p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer 'p2' }} \ + // cfg-note {{variable `p2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} global2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer 'global2' }} \ + // cfg-note {{variable `global2` is now an alias of `MyLongOwnerWithConversion{}.operator MyLongPointerFromConversion()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(global2, p2); // cfg-note 2 {{later used here}} } @@ -185,6 +191,7 @@ struct Unannotated { void modelIterators() { std::vector<int>::iterator it = std::vector<int>().begin(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `it` is now an alias of `std::vector<int>().begin()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} (void)it; // cfg-note {{later used here}} } @@ -233,11 +240,13 @@ int &danglingRawPtrFromLocal3() { // GH100384 std::string_view containerWithAnnotatedElements() { std::string_view c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `c1` is now an alias of `std::vector<std::string>().at(0).operator basic_string_view()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(c1); // cfg-note {{later used here}} c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `c1` is now an alias of `std::vector<std::string>().at(0).operator basic_string_view()`}} use(c1); // cfg-note {{later used here}} // no warning on constructing from gsl-pointer @@ -298,22 +307,28 @@ std::string_view danglingRefToOptionalFromTemp4() { void danglingReferenceFromTempOwner() { int &&r = *std::optional<int>(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `r` is now an alias of `operator*(std::optional<int>())`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} // https://github.com/llvm/llvm-project/issues/175893 int &&r2 = *std::optional<int>(5); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-note {{variable `r2` is now an alias of `operator*(std::optional<int>(5))`}} \ + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} // https://github.com/llvm/llvm-project/issues/175893 int &&r3 = std::optional<int>(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-note {{variable `r3` is now an alias of `std::optional<int>(5).value()`}} \ + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} const int &r4 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `r4` is now an alias of `std::vector<int>().at(3)`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} int &&r5 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `r5` is now an alias of `std::vector<int>().at(3)`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(r, r2, r3, r4, r5); // cfg-note 5 {{later used here}} std::string_view sv = *getTempOptStr(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `sv` is now an alias of `* getTempOptStr().operator basic_string_view()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(sv); // cfg-note {{later used here}} } @@ -325,6 +340,7 @@ void testLoops() { for (auto i : getTempVec()) // ok ; for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `__range1` is now an alias of `operator*(getTempOptVec())`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} ; } @@ -387,6 +403,7 @@ void handleGslPtrInitsThroughReference2() { void handleTernaryOperator(bool cond) { std::basic_string<char> def; std::basic_string_view<char> v = cond ? def : ""; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-note {{variable `v` is now an alias of `cond ? def : "".operator basic_string_view()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(v); // cfg-note {{later used here}} } @@ -394,11 +411,13 @@ void handleTernaryOperator(bool cond) { std::string operator+(std::string_view s1, std::string_view s2); void danglingStringviewAssignment(std::string_view a1, std::string_view a2) { a1 = std::string(); // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `a1` is now an alias of `std::string().operator basic_string_view()`}} use(a1); // cfg-note {{later used here}} a2 = a1 + a1; // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `a2` is now an alias of `a1 + a1.operator basic_string_view()`}} use(a2); // cfg-note {{later used here}} } @@ -602,7 +621,8 @@ std::string_view ReturnStringView(std::string_view abc [[clang::lifetimebound]]) void test() { std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `svjkk1` is now an alias of `ReturnStringView(StrCat("bar", "x"))`}} use(svjkk1); // cfg-note {{later used here}} } } // namespace GH100549 @@ -836,7 +856,8 @@ namespace GH118064{ void test() { auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `y` is now an alias of `std::set<int>{}.begin()`}} use(y); // cfg-note {{later used here}} } } // namespace GH118064 @@ -851,10 +872,12 @@ std::string_view TakeStr(std::string abc [[clang::lifetimebound]]); std::string_view test1_1() { std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `t1` is now an alias of `Ref(std::string()).operator basic_string_view()`}} use(t1); // cfg-note {{later used here}} t1 = Ref(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `t1` is now an alias of `Ref(std::string()).operator basic_string_view()`}} use(t1); // cfg-note {{later used here}} return Ref(std::string()); // expected-warning {{returning address}} \ // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}} @@ -862,10 +885,12 @@ std::string_view test1_1() { std::string_view test1_2() { std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `t2` is now an alias of `TakeSv(std::string())`}} use(t2); // cfg-note {{later used here}} t2 = TakeSv(std::string()); // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `t2` is now an alias of `TakeSv(std::string())`}} use(t2); // cfg-note {{later used here}} return TakeSv(std::string()); // expected-warning {{returning address}} \ @@ -874,9 +899,11 @@ std::string_view test1_2() { std::string_view test1_3() { std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \ + // cfg-note {{variable `t3` is now an alias of `TakeStrRef(std::string())`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(t3); // cfg-note {{later used here}} t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \ + // cfg-note {{variable `t3` is now an alias of `TakeStrRef(std::string())`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(t3); // cfg-note {{later used here}} return TakeStrRef(std::string()); // expected-warning {{returning address}} \ @@ -899,10 +926,12 @@ struct Foo { }; std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) { std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \ + // cfg-note {{variable `t1` is now an alias of `Foo<std::string>().get().operator basic_string_view()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(t1); // cfg-note {{later used here}} t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `t1` is now an alias of `Foo<std::string>().get().operator basic_string_view()`}} use(t1); // cfg-note {{later used here}} return r1.get(); // expected-warning {{address of stack}} \ // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}} @@ -1022,9 +1051,12 @@ void operator_star_arrow_reference() { const std::string& r = *v.begin(); auto temporary = []() { return std::vector<std::string>{{"1"}}; }; - const char* x = temporary().begin()->data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} - const char* y = (*temporary().begin()).data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} - const std::string& z = (*temporary().begin()); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + const char* x = temporary().begin()->data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `x` is now an alias of `temporary().begin()->data()`}} + const char* y = (*temporary().begin()).data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `y` is now an alias of `(* temporary().begin()).data()`}} + const std::string& z = (*temporary().begin()); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `z` is now an alias of `operator*(temporary().begin())`}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } @@ -1036,9 +1068,12 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() { const std::string& r = (*v.begin()).second; auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, "1"}}; }; - const char* x = temporary().begin()->second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} - const char* y = (*temporary().begin()).second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} - const std::string& z = (*temporary().begin()).second; // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + const char* x = temporary().begin()->second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `x` is now an alias of `temporary().begin()->second.data()`}} + const char* y = (*temporary().begin()).second.data(); // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `y` is now an alias of `(* temporary().begin()).second.data()`}} + const std::string& z = (*temporary().begin()).second; // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `z` is now an alias of `operator*(temporary().begin())`}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } @@ -1088,16 +1123,20 @@ std::string_view foo(std::string_view sv [[clang::lifetimebound]]); void test1() { std::string_view k1 = S().sv; // OK std::string_view k2 = S().s; // expected-warning {{object backing the pointer will}} \ + // cfg-note {{variable `k2` is now an alias of `S().s.operator basic_string_view()`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} std::string_view k3 = Q().get()->sv; // OK std::string_view k4 = Q().get()->s; // expected-warning {{object backing the pointer will}} \ - // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} \ + // cfg-note {{variable `k4` is now an alias of `Q().get()->s.operator basic_string_view()`}} std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \ + // cfg-note {{variable `lb1` is now an alias of `foo(S().s)`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \ + // cfg-note {{variable `lb2` is now an alias of `foo(Q().get()->s)`}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} use(k1, k2, k3, k4, lb1, lb2); // cfg-note 4 {{later used here}} diff --git a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp index 7c5d61e23e710..51cd8a6e5b349 100644 --- a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp +++ b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp @@ -27,7 +27,7 @@ void single_block_cfg() { MyObj* p; { MyObj s; - p = &s; // bailout-warning {{object whose reference is captured does not live long enough}} + p = &s; // bailout-warning {{object whose reference is captured does not live long enough}} bailout-note {{variable `p` is now an alias of `s`}} } // bailout-note {{destroyed here}} (void)*p; // bailout-note {{later used here}} } @@ -39,7 +39,7 @@ void multiple_block_cfg() { { if (a > 5) { MyObj s; - p = &s; // nobailout-warning {{object whose reference is captured does not live long enough}} + p = &s; // nobailout-warning {{object whose reference is captured does not live long enough}} nobailout-note {{variable `p` is now an alias of `s`}} } else { // nobailout-note {{destroyed here}} p = &safe; } diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 22c4222022ebf..75a828e8d6f9f 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -220,19 +220,22 @@ View return_view_field(const ViewProvider& v) { // expected-warning {{paramet void test_get_on_temporary_pointer() { const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{object whose reference is captured does not live long enough}}. - // expected-note@-1 {{destroyed here}} + // expected-note@-1 {{destroyed here}}. + // expected-note@-2 {{variable `s_ref` is now an alias of `ReturnsSelf().get()`}} (void)s_ref; // expected-note {{later used here}} } void test_get_on_temporary_ref() { const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning {{object whose reference is captured does not live long enough}}. - // expected-note@-1 {{destroyed here}} + // expected-note@-1 {{destroyed here}}. + // expected-note@-2 {{variable `s_ref` is now an alias of `ReturnsSelf().get()`}} (void)s_ref; // expected-note {{later used here}} } void test_getView_on_temporary() { View sv = ViewProvider{1}.getView(); // expected-warning {{object whose reference is captured does not live long enough}}. - // expected-note@-1 {{destroyed here}} + // expected-note@-1 {{destroyed here}}. + // expected-note@-2 {{variable `sv` is now an alias of `ViewProvider{1}.getView()`}} (void)sv; // expected-note {{later used here}} } diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 76d43445f8636..2e2004e574830 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -53,7 +53,8 @@ void simple_case() { MyObj* p; { MyObj s; - p = &s; // expected-warning {{object whose reference is captured does not live long enough}} + p = &s; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -62,7 +63,8 @@ void simple_case_gsl() { View v; { MyObj s; - v = s; // expected-warning {{object whose reference is captured does not live long enough}} + v = s; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `s`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -90,8 +92,9 @@ void pointer_chain() { MyObj* q; { MyObj s; - p = &s; // expected-warning {{does not live long enough}} - q = p; + p = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} + q = p; // expected-note {{variable `q` is now an alias of `p`}} } // expected-note {{destroyed here}} (void)*q; // expected-note {{later used here}} } @@ -100,8 +103,9 @@ void propagation_gsl() { View v1, v2; { MyObj s; - v1 = s; // expected-warning {{object whose reference is captured does not live long enough}} - v2 = v1; + v1 = s; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v1` is now an alias of `s`}} + v2 = v1; // expected-note {{`v2` is now an alias of `v1`}} } // expected-note {{destroyed here}} v2.use(); // expected-note {{later used here}} } @@ -110,7 +114,8 @@ void multiple_uses_one_warning() { MyObj* p; { MyObj s; - p = &s; // expected-warning {{does not live long enough}} + p = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} // No second warning for the same loan. @@ -123,9 +128,12 @@ void multiple_pointers() { MyObj *p, *q, *r; { MyObj s; - p = &s; // expected-warning {{does not live long enough}} - q = &s; // expected-warning {{does not live long enough}} - r = &s; // expected-warning {{does not live long enough}} + p = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} + q = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `q` is now an alias of `s`}} + r = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `r` is now an alias of `s`}} } // expected-note 3 {{destroyed here}} (void)*p; // expected-note {{later used here}} (void)*q; // expected-note {{later used here}} @@ -136,11 +144,13 @@ void single_pointer_multiple_loans(bool cond) { MyObj *p; if (cond){ MyObj s; - p = &s; // expected-warning {{does not live long enough}} + p = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{`p` is now an alias of `s`}} } // expected-note {{destroyed here}} else { MyObj t; - p = &t; // expected-warning {{does not live long enough}} + p = &t; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `t`}} } // expected-note {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} } @@ -149,11 +159,13 @@ void single_pointer_multiple_loans_gsl(bool cond) { View v; if (cond){ MyObj s; - v = s; // expected-warning {{object whose reference is captured does not live long enough}} + v = s; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `s`}} } // expected-note {{destroyed here}} else { MyObj t; - v = t; // expected-warning {{object whose reference is captured does not live long enough}} + v = t; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `t`}} } // expected-note {{destroyed here}} v.use(); // expected-note 2 {{later used here}} } @@ -163,7 +175,8 @@ void if_branch(bool cond) { MyObj* p = &safe; if (cond) { MyObj temp; - p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} + p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -173,7 +186,8 @@ void if_branch_potential(bool cond) { MyObj* p = &safe; if (cond) { MyObj temp; - p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} + p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp`}} } // expected-note {{destroyed here}} if (!cond) (void)*p; // expected-note {{later used here}} @@ -186,7 +200,8 @@ void if_branch_gsl(bool cond) { View v = safe; if (cond) { MyObj temp; - v = temp; // expected-warning {{object whose reference is captured does not live long enough}} + v = temp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `temp`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -199,9 +214,11 @@ void potential_together(bool cond) { { MyObj s; if (cond) - p_definite = &s; // expected-warning {{does not live long enough}} + p_definite = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p_definite` is now an alias of `s`}} if (cond) - p_maybe = &s; // expected-warning {{does not live long enough}} + p_maybe = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p_maybe` is now an alias of `s`}} } // expected-note 2 {{destroyed here}} (void)*p_definite; // expected-note {{later used here}} if (!cond) @@ -214,8 +231,9 @@ void overrides_potential(bool cond) { MyObj* q; { MyObj s; - q = &s; // expected-warning {{does not live long enough}} - p = q; + q = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `q` is now an alias of `s`}} + p = q; // expected-note {{variable `p` is now an alias of `q`}} } // expected-note {{destroyed here}} if (cond) { @@ -234,7 +252,8 @@ void due_to_conditional_killing(bool cond) { MyObj* q; { MyObj s; - q = &s; // expected-warning {{does not live long enough}} + q = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `q` is now an alias of `s`}} } // expected-note {{destroyed here}} if (cond) { // 'q' is conditionally "rescued". 'p' is not. @@ -247,7 +266,8 @@ void for_loop_use_after_loop_body(MyObj safe) { MyObj* p = &safe; for (int i = 0; i < 1; ++i) { MyObj s; - p = &s; // expected-warning {{does not live long enough}} + p = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -267,7 +287,8 @@ void for_loop_gsl() { View v = safe; for (int i = 0; i < 1; ++i) { MyObj s; - v = s; // expected-warning {{object whose reference is captured does not live long enough}} + v = s; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `s`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -278,7 +299,8 @@ void for_loop_use_before_loop_body(MyObj safe) { for (int i = 0; i < 1; ++i) { (void)*p; // expected-note {{later used here}} MyObj s; - p = &s; // expected-warning {{does not live long enough}} + p = &s; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} } // expected-note {{destroyed here}} (void)*p; } @@ -289,7 +311,8 @@ void loop_with_break(bool cond) { for (int i = 0; i < 10; ++i) { if (cond) { MyObj temp; - p = &temp; // expected-warning {{does not live long enough}} + p = &temp; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp`}} break; // expected-note {{destroyed here}} } } @@ -302,7 +325,8 @@ void loop_with_break_gsl(bool cond) { for (int i = 0; i < 10; ++i) { if (cond) { MyObj temp; - v = temp; // expected-warning {{object whose reference is captured does not live long enough}} + v = temp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `temp`}} break; // expected-note {{destroyed here}} } } @@ -316,8 +340,9 @@ void multiple_expiry_of_same_loan(bool cond) { for (int i = 0; i < 10; ++i) { MyObj unsafe; if (cond) { - p = &unsafe; // expected-warning {{does not live long enough}} - break; // expected-note {{destroyed here}} + p = &unsafe; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `unsafe`}} + break; // expected-note {{destroyed here}} } } (void)*p; // expected-note {{later used here}} @@ -326,7 +351,8 @@ void multiple_expiry_of_same_loan(bool cond) { for (int i = 0; i < 10; ++i) { MyObj unsafe; if (cond) { - p = &unsafe; // expected-warning {{does not live long enough}} + p = &unsafe; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `unsafe`}} if (cond) break; // expected-note {{destroyed here}} } @@ -337,7 +363,8 @@ void multiple_expiry_of_same_loan(bool cond) { for (int i = 0; i < 10; ++i) { if (cond) { MyObj unsafe2; - p = &unsafe2; // expected-warning {{does not live long enough}} + p = &unsafe2; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `unsafe2`}} break; // expected-note {{destroyed here}} } } @@ -347,7 +374,8 @@ void multiple_expiry_of_same_loan(bool cond) { for (int i = 0; i < 10; ++i) { MyObj unsafe; if (cond) - p = &unsafe; // expected-warning {{does not live long enough}} + p = &unsafe; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `unsafe`}} if (cond) break; // expected-note {{destroyed here}} } @@ -360,7 +388,8 @@ void switch_potential(int mode) { switch (mode) { case 1: { MyObj temp; - p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} + p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp`}} break; // expected-note {{destroyed here}} } case 2: { @@ -379,17 +408,20 @@ void switch_uaf(int mode) { switch (mode) { case 1: { MyObj temp1; - p = &temp1; // expected-warning {{does not live long enough}} + p = &temp1; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp1`}} break; // expected-note {{destroyed here}} } case 2: { MyObj temp2; - p = &temp2; // expected-warning {{does not live long enough}} + p = &temp2; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp2`}} break; // expected-note {{destroyed here}} } default: { MyObj temp2; - p = &temp2; // expected-warning {{does not live long enough}} + p = &temp2; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp2`}} break; // expected-note {{destroyed here}} } } @@ -401,17 +433,20 @@ void switch_gsl(int mode) { switch (mode) { case 1: { MyObj temp1; - v = temp1; // expected-warning {{object whose reference is captured does not live long enough}} + v = temp1; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `temp1`}} break; // expected-note {{destroyed here}} } case 2: { MyObj temp2; - v = temp2; // expected-warning {{object whose reference is captured does not live long enough}} + v = temp2; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `temp2`}} break; // expected-note {{destroyed here}} } default: { MyObj temp3; - v = temp3; // expected-warning {{object whose reference is captured does not live long enough}} + v = temp3; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `temp3`}} break; // expected-note {{destroyed here}} } } @@ -424,7 +459,8 @@ void loan_from_previous_iteration(MyObj safe, bool condition) { while (condition) { MyObj x; - p = &x; // expected-warning {{does not live long enough}} + p = &x; // expected-warning {{does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `x`}} if (condition) q = p; @@ -437,7 +473,8 @@ void trivial_int_uaf() { int * a; { int b = 1; - a = &b; // expected-warning {{object whose reference is captured does not live long enough}} + a = &b; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `a` is now an alias of `b`}} } // expected-note {{destroyed here}} (void)*a; // expected-note {{later used here}} } @@ -446,7 +483,8 @@ void trivial_class_uaf() { TriviallyDestructedClass* ptr; { TriviallyDestructedClass s; - ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} + ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `ptr` is now an alias of `s`}} } // expected-note {{destroyed here}} (void)ptr; // expected-note {{later used here}} } @@ -638,7 +676,8 @@ void test_view_pointer() { View* vp; { View v; - vp = &v; // expected-warning {{object whose reference is captured does not live long enough}} + vp = &v; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `vp` is now an alias of `v`}} } // expected-note {{destroyed here}} vp->use(); // expected-note {{later used here}} } @@ -647,7 +686,8 @@ void test_view_double_pointer() { View** vpp; { View* vp = nullptr; - vpp = &vp; // expected-warning {{object whose reference is captured does not live long enough}} + vpp = &vp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `vpp` is now an alias of `vp`}} } // expected-note {{destroyed here}} (**vpp).use(); // expected-note {{later used here}} } @@ -673,9 +713,10 @@ void test_lifetimebound_multi_level() { int** result; { int* p = nullptr; - int** pp = &p; - int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}} - result = return_inner_ptr_addr(ppp); + int** pp = &p; + int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `ppp` is now an alias of `pp`}} + result = return_inner_ptr_addr(ppp); // expected-note {{variable `result` is now an alias of `return_inner_ptr_addr(ppp)`}} } // expected-note {{destroyed here}} (void)**result; // expected-note {{used here}} } @@ -707,8 +748,9 @@ int** test_ternary_double_ptr(bool cond) { MyObj* uaf_before_uar() { MyObj* p; { - MyObj local_obj; - p = &local_obj; // expected-warning {{object whose reference is captured does not live long enough}} + MyObj local_obj; + p = &local_obj; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `local_obj`}} } // expected-note {{destroyed here}} return p; // expected-note {{later used here}} } @@ -786,7 +828,8 @@ void lifetimebound_simple_function() { View v; { MyObj obj; - v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}} + v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `Identity(obj)`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -795,7 +838,8 @@ void lifetimebound_multiple_args_definite() { View v; { MyObj obj1, obj2; - v = Choose(true, + v = Choose(true, // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} \ + // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} obj1, // expected-warning {{object whose reference is captured does not live long enough}} obj2); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 2 {{destroyed here}} @@ -809,7 +853,8 @@ void lifetimebound_multiple_args_potential(bool cond) { MyObj obj1; if (cond) { MyObj obj2; - v = Choose(true, + v = Choose(true, // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} \ + // expected-note {{variable `v` is now an alias of `Choose(true, obj1, obj2)`}} obj1, // expected-warning {{object whose reference is captured does not live long enough}} obj2); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} @@ -822,7 +867,8 @@ void lifetimebound_mixed_args() { View v; { MyObj obj1, obj2; - v = SelectFirst(obj1, // expected-warning {{object whose reference is captured does not live long enough}} + v = SelectFirst(obj1, // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `SelectFirst(obj1, obj2)`}} obj2); } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} @@ -838,7 +884,8 @@ void lifetimebound_member_function() { View v; { MyObj obj; - v = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}} + v = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `obj.getView()`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -853,7 +900,8 @@ void lifetimebound_conversion_operator() { View v; { LifetimeBoundConversionView obj; - v = obj; // expected-warning {{object whose reference is captured does not live long enough}} + v = obj; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `obj.operator View()`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -862,7 +910,8 @@ void lifetimebound_chained_calls() { View v; { MyObj obj; - v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}} + v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `Identity(Identity(Identity(obj)))`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -871,7 +920,8 @@ void lifetimebound_with_pointers() { MyObj* ptr; { MyObj obj; - ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}} + ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `ptr` is now an alias of `GetPointer(obj)`}} } // expected-note {{destroyed here}} (void)*ptr; // expected-note {{later used here}} } @@ -889,7 +939,7 @@ void lifetimebound_partial_safety(bool cond) { if (cond) { MyObj temp_obj; - v = Choose(true, + v = Choose(true, // expected-note {{variable `v` is now an alias of `Choose(true, safe_obj, temp_obj)`}} safe_obj, temp_obj); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} @@ -902,9 +952,10 @@ void lifetimebound_return_reference() { const MyObj* ptr; { MyObj obj; - View temp_v = obj; // expected-warning {{object whose reference is captured does not live long enough}} - const MyObj& ref = GetObject(temp_v); - ptr = &ref; + View temp_v = obj; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `temp_v` is now an alias of `obj`}} + const MyObj& ref = GetObject(temp_v); // expected-note {{variable `ref` is now an alias of `GetObject(temp_v)`}} + ptr = &ref; // expected-note {{variable `ptr` is now an alias of `ref`}} } // expected-note {{destroyed here}} (void)*ptr; // expected-note {{later used here}} } @@ -995,7 +1046,8 @@ void conditional_operator_one_unsafe_branch(bool cond) { MyObj* p = &safe; { MyObj temp; - p = cond ? &temp // expected-warning {{object whose reference is captured does not live long enough}} + p = cond ? &temp // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `temp`}} : &safe; } // expected-note {{destroyed here}} @@ -1011,8 +1063,10 @@ void conditional_operator_two_unsafe_branches(bool cond) { MyObj* p; { MyObj a, b; - p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} - : &b; // expected-warning {{object whose reference is captured does not live long enough}} + p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `a`}} + : &b; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `b`}} } // expected-note 2 {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} } @@ -1021,10 +1075,14 @@ void conditional_operator_nested(bool cond) { MyObj* p; { MyObj a, b, c, d; - p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}. - : &b // expected-warning {{object whose reference is captured does not live long enough}}. - : cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}. - : &d; // expected-warning {{object whose reference is captured does not live long enough}}. + p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}. \ + // expected-note {{variable `p` is now an alias of `a`}} + : &b // expected-warning {{object whose reference is captured does not live long enough}}. \ + // expected-note {{variable `p` is now an alias of `b`}} + : cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}. \ + // expected-note {{variable `p` is now an alias of `c`}} + : &d; // expected-warning {{object whose reference is captured does not live long enough}}. \ + // expected-note {{variable `p` is now an alias of `d`}} } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} } @@ -1033,7 +1091,9 @@ void conditional_operator_lifetimebound(bool cond) { MyObj* p; { MyObj a, b; - p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} + p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? &a : &b)`}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? &a : &b)`}} : &b); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 2 {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} @@ -1043,7 +1103,9 @@ void conditional_operator_lifetimebound_nested(bool cond) { MyObj* p; { MyObj a, b; - p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}} + p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(&a) : Identity(&b))`}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(&a) : Identity(&b))`}} : Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 2 {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} @@ -1053,7 +1115,11 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) { MyObj* p; { MyObj a, b, c, d; - p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} + p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} \ + // expected-note {{variable `p` is now an alias of `Identity(cond ? Identity(cond ? &a : &b) : Identity(cond ? &c : &d))`}} : &b) // expected-warning {{object whose reference is captured does not live long enough}} : Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}} : &d)); // expected-warning {{object whose reference is captured does not live long enough}} @@ -1065,29 +1131,31 @@ void parentheses(bool cond) { MyObj* p; { MyObj a; - p = &((((a)))); // expected-warning {{object whose reference is captured does not live long enough}} + p = &((((a)))); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `a`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} { MyObj a; - p = ((GetPointer((a)))); // expected-warning {{object whose reference is captured does not live long enough}} + p = ((GetPointer((a)))); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `GetPointer((a))`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} { MyObj a, b, c, d; - p = &(cond ? (cond ? a // expected-warning {{object whose reference is captured does not live long enough}}. - : b) // expected-warning {{object whose reference is captured does not live long enough}}. - : (cond ? c // expected-warning {{object whose reference is captured does not live long enough}}. - : d)); // expected-warning {{object whose reference is captured does not live long enough}}. + p = &(cond ? (cond ? a // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `a`}} + : b) // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `b`}} + : (cond ? c // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `c`}} + : d)); // expected-warning {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `d`}} } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} { MyObj a, b, c, d; - p = ((cond ? (((cond ? &a : &b))) // expected-warning 2 {{object whose reference is captured does not live long enough}}. - : &(((cond ? c : d))))); // expected-warning 2 {{object whose reference is captured does not live long enough}}. + p = ((cond ? (((cond ? &a : &b))) // expected-warning 2 {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `b`}} expected-note {{variable `p` is now an alias of `a`}} + : &(((cond ? c : d))))); // expected-warning 2 {{object whose reference is captured does not live long enough}}. expected-note {{variable `p` is now an alias of `d`}} expected-note {{variable `p` is now an alias of `c`}} } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} @@ -1096,20 +1164,20 @@ void parentheses(bool cond) { void use_temporary_after_destruction() { View a; a = non_trivially_destructed_temporary(); // expected-warning {{object whose reference is captured does not live long enough}} \ - expected-note {{destroyed here}} + expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `non_trivially_destructed_temporary()`}} use(a); // expected-note {{later used here}} } void passing_temporary_to_lifetime_bound_function() { View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{object whose reference is captured does not live long enough}} \ - expected-note {{destroyed here}} + expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `construct_view(non_trivially_destructed_temporary())`}} use(a); // expected-note {{later used here}} } void use_trivial_temporary_after_destruction() { View a; a = trivially_destructed_temporary(); // expected-warning {{object whose reference is captured does not live long enough}} \ - expected-note {{destroyed here}} + expected-note {{destroyed here}} expected-note {{variable `a` is now an alias of `trivially_destructed_temporary()`}} use(a); // expected-note {{later used here}} } @@ -1151,7 +1219,7 @@ void foobar() { { StatusOr<MyObj> string_or = getStringOr(); view = string_or. // expected-warning {{object whose reference is captured does not live long enough}} - value(); + value(); // expected-note {{variable `view` is now an alias of `string_or.value()`}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1170,8 +1238,9 @@ void range_based_for_use_after_scope() { View v; { MyObjStorage s; - for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} - v = o; + for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `o` is now an alias of `__begin2`}} + v = o; // expected-note {{variable `v` is now an alias of `o`}} } } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} @@ -1191,7 +1260,8 @@ void range_based_for_not_reference() { { MyObjStorage s; for (MyObj o : s) { // expected-note {{destroyed here}} - v = o; // expected-warning {{object whose reference is captured does not live long enough}} + v = o; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `o`}} } } v.use(); // expected-note {{later used here}} @@ -1224,7 +1294,8 @@ void test_user_defined_deref_uaf() { { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); - p = &(*smart_ptr); // expected-warning {{object whose reference is captured does not live long enough}} + p = &(*smart_ptr); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `operator*(smart_ptr)`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1241,7 +1312,8 @@ void test_user_defined_deref_with_view() { { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); - v = *smart_ptr; // expected-warning {{object whose reference is captured does not live long enough}} + v = *smart_ptr; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `operator*(smart_ptr)`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1251,7 +1323,8 @@ void test_user_defined_deref_arrow() { { MyObj obj; SmartPtr<MyObj> smart_ptr(&obj); - p = smart_ptr.operator->(); // expected-warning {{object whose reference is captured does not live long enough}} + p = smart_ptr.operator->(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `smart_ptr.operator->()`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1261,7 +1334,8 @@ void test_user_defined_deref_chained() { { MyObj obj; SmartPtr<SmartPtr<MyObj>> double_ptr; - p = &(**double_ptr); // expected-warning {{object whose reference is captured does not live long enough}} + p = &(**double_ptr); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `operator*(* double_ptr)`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1295,15 +1369,15 @@ T&& MaxT(T&& a [[clang::lifetimebound]], T&& b [[clang::lifetimebound]]); const MyObj& call_max_with_obj() { MyObj oa, ob; - return MaxT(oa, // expected-warning {{address of stack memory is returned later}} + return MaxT(oa, // expected-warning {{address of stack memory is returned later}} // expected-note@-1 2 {{returned here}} ob); // expected-warning {{address of stack memory is returned later}} - + } MyObj* call_max_with_obj_error() { MyObj oa, ob; - return &MaxT(oa, // expected-warning {{address of stack memory is returned later}} + return &MaxT(oa, // expected-warning {{address of stack memory is returned later}} // expected-note@-1 2 {{returned here}} ob); // expected-warning {{address of stack memory is returned later}} } @@ -1412,7 +1486,8 @@ void strict_warn_on_move() { View v; { MyObj a; - v = a; // expected-warning-re {{object whose reference {{.*}} may have been moved}} + v = a; // expected-warning-re {{object whose reference {{.*}} may have been moved}} \ + // expected-note {{variable `v` is now an alias of `a`}} b = std::move(a); // expected-note {{potentially moved here}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} @@ -1426,7 +1501,7 @@ void flow_sensitive(bool c) { MyObj b = std::move(a); return; } - v = a; // expected-warning {{object whose reference}} + v = a; // expected-warning {{object whose reference}} expected-note {{variable `v` is now an alias of `a`}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} } @@ -1436,7 +1511,9 @@ void detect_conditional(bool cond) { View v; { MyObj a, b; - v = cond ? a : b; // expected-warning-re 2 {{object whose reference {{.*}} may have been moved}} + v = cond ? a : b; // expected-warning-re 2 {{object whose reference {{.*}} may have been moved}} \ + // expected-note {{variable `v` is now an alias of `b`}} \ + // expected-note {{variable `v` is now an alias of `a`}} take(std::move(cond ? a : b)); // expected-note 2 {{potentially moved here}} } // expected-note 2 {{destroyed here}} (void)v; // expected-note 2 {{later used here}} @@ -1446,13 +1523,15 @@ void wrong_use_of_move_is_permissive() { View v; { MyObj a; - v = std::move(a); // expected-warning {{object whose reference is captured does not live long enough}} + v = std::move(a); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `std::move(a)`}} } // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} const int* p; { MyObj a; - p = std::move(a).getData(); // expected-warning {{object whose reference is captured does not live long enough}} + p = std::move(a).getData(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `std::move(a).getData()`}} } // expected-note {{destroyed here}} (void)p; // expected-note {{later used here}} } @@ -1463,7 +1542,8 @@ void test_release_no_uaf() { // Calling release() marks p as moved from, so its destruction doesn't invalidate r. { std::unique_ptr<int> p; - r = p.get(); // expected-warning-re {{object whose reference {{.*}} may have been moved}} + r = p.get(); // expected-warning-re {{object whose reference {{.*}} may have been moved}} \ + // expected-note {{variable `r` is now an alias of `p.get()`}} take(p.release()); // expected-note {{potentially moved here}} } // expected-note {{destroyed here}} (void)*r; // expected-note {{later used here}} @@ -1485,9 +1565,10 @@ void bar() { View x; { S s; - x = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} + x = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `x` is now an alias of `s.x()`}} View y = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \ - expected-note {{destroyed here}} + expected-note {{destroyed here}} expected-note {{variable `y` is now an alias of `S().x()`}} (void)y; // expected-note {{used here}} } // expected-note {{destroyed here}} (void)x; // expected-note {{used here}} @@ -1575,17 +1656,22 @@ const std::string& identity(const std::string& in [[clang::lifetimebound]]); const S& identity(const S& in [[clang::lifetimebound]]); void test_temporary() { - const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{destroyed here}} \ + // expected-note {{variable `x` is now an alias of `S().x()`}} (void)x; // expected-note {{later used here}} - const std::string& y = identity(S().x()); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + const std::string& y = identity(S().x()); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{destroyed here}} \ + // expected-note {{variable `y` is now an alias of `identity(S().x())`}} (void)y; // expected-note {{later used here}} std::string_view z; { S s; - const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} - z = zz; + const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `zz` is now an alias of `s.x()`}} + z = zz; // expected-note {{variable `z` is now an alias of `zz.operator basic_string_view()`}} } // expected-note {{destroyed here}} (void)z; // expected-note {{later used here}} } @@ -1593,12 +1679,16 @@ void test_temporary() { void test_lifetime_extension_ok() { const S& x = S(); (void)x; - const S& y = identity(S()); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + const S& y = identity(S()); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{destroyed here}} \ + // expected-note {{variable `y` is now an alias of `identity(S())`}} (void)y; // expected-note {{later used here}} } const std::string& test_return() { - const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{destroyed here}} \ + // expected-note {{variable `x` is now an alias of `S().x()`}} return x; // expected-note {{later used here}} } } // namespace reference_type_decl_ref_expr @@ -1614,8 +1704,9 @@ void uaf() { std::string_view view; { S str; - S* p = &str; // expected-warning {{object whose reference is captured does not live long enough}} - view = p->s; + S* p = &str; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `str`}} + view = p->s; // expected-note {{variable `view` is now an alias of `p->s.operator basic_string_view()`}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1640,8 +1731,9 @@ void uaf_union() { std::string_view view; { U u = U{"hello"}; - U* up = &u; // expected-warning {{object whose reference is captured does not live long enough}} - view = up->s; + U* up = &u; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `up` is now an alias of `u`}} + view = up->s; // expected-note {{variable `view` is now an alias of `up->s.operator basic_string_view()`}} } // expected-note {{destroyed here}} (void)view; // expected-note {{later used here}} } @@ -1657,8 +1749,8 @@ void uaf_anonymous_union() { int* ip; { AnonymousUnion au; - AnonymousUnion* up = &au; // expected-warning {{object whose reference is captured does not live long enough}} - ip = &up->x; + AnonymousUnion* up = &au; // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{variable `up` is now an alias of `au`}} + ip = &up->x; // expected-note {{variable `ip` is now an alias of `up`}} } // expected-note {{destroyed here}} (void)ip; // expected-note {{later used here}} } @@ -1716,9 +1808,15 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) { void test() { MemberFuncsTpl<MyObj> mtf; - const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}} - const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{object whose reference is captured does not live long enough}} // tu-note {{destroyed here}} - const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} // expected-note {{destroyed here}} + const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{destroyed here}} \ + // expected-note {{variable `pTMA` is now an alias of `mtf.memberA(MyObj())`}} + const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{object whose reference is captured does not live long enough}} \ + // tu-note {{destroyed here}} \ + // tu-note {{variable `pTMB` is now an alias of `mtf.memberB(MyObj())`}} + const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{destroyed here}} \ + // expected-note {{variable `pTMC` is now an alias of `mtf.memberC(MyObj())`}} (void)pTMA; // expected-note {{later used here}} (void)pTMB; // tu-note {{later used here}} (void)pTMC; // expected-note {{later used here}} @@ -1753,7 +1851,8 @@ void test_optional_arrow() { const char* p; { std::optional<std::string> opt; - p = opt->data(); // expected-warning {{object whose reference is captured does not live long enough}} + p = opt->data(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `opt->data()`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1762,7 +1861,8 @@ void test_optional_arrow_lifetimebound() { View v; { std::optional<MyObj> opt; - v = opt->getView(); // expected-warning {{object whose reference is captured does not live long enough}} + v = opt->getView(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `v` is now an alias of `opt->getView()`}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -1771,7 +1871,8 @@ void test_unique_ptr_arrow() { const char* p; { std::unique_ptr<std::string> up; - p = up->data(); // expected-warning {{object whose reference is captured does not live long enough}} + p = up->data(); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `up->data()`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -1962,8 +2063,9 @@ void multi_level_pointer_in_loop() { MyObj* p; MyObj** pp; if (i > 5) { - p = &obj; // expected-warning {{object whose reference is captured does not live long enough}} - pp = &p; + p = &obj; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `obj`}} + pp = &p; // expected-note {{variable `pp` is now an alias of `p`}} } (void)**pp; // expected-note {{later used here}} } // expected-note {{destroyed here}} @@ -1974,7 +2076,8 @@ void outer_pointer_outlives_inner_pointee() { MyObj* view = &safe; for (int i = 0; i < 10; ++i) { MyObj obj; - view = &obj; // expected-warning {{object whose reference is captured does not live long enough}} + view = &obj; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `view` is now an alias of `obj`}} } // expected-note {{destroyed here}} (void)*view; // expected-note {{later used here}} } @@ -1987,7 +2090,8 @@ void element_use_after_scope() { int* p; { int a[10]{}; - p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} + p = &a[2]; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `a`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -2019,7 +2123,8 @@ void multidimensional_use_after_scope() { int* p; { int a[3][4]{}; - p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} + p = &a[1][2]; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `a`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -2032,7 +2137,8 @@ void member_array_element_use_after_scope() { int* p; { S s; - p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} + p = &s.arr[0]; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `s`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -2041,7 +2147,8 @@ void array_of_pointers_use_after_scope() { int** p; { int* a[10]{}; - p = a; // expected-warning {{object whose reference is captured does not live long enough}} + p = a; // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `a`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -2050,7 +2157,8 @@ void reversed_subscript_use_after_scope() { int* p; { int a[10]{}; - p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} + p = &(0[a]); // expected-warning {{object whose reference is captured does not live long enough}} \ + // expected-note {{variable `p` is now an alias of `a`}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -2118,9 +2226,10 @@ struct S { void indexing_with_static_operator() { S()(1, 2); - S& x = S()("1", - 2, // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} - 3); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + S& x = S()("1", //expected-note {{variable `x` is now an alias of `operator()(S(), "1", 2, 3)`}} \ + //expected-note {{variable `x` is now an alias of `operator()(S(), "1", 2, 3)`}} + 2, // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + 3); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} (void)x; // expected-note 2 {{later used here}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
