https://github.com/suoyuan666 created
https://github.com/llvm/llvm-project/pull/188467
Fixes #177986
## Summary
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.
For the following code:
```cpp
#include <cstdio>
int foo() {
int *s;
{
int tgt = 2;
int *a = &tgt;
int *c = a;
int *b = a;
int *e = b;
s = e;
}
printf("s: %d\n", *s);
return 0;
}
```
Before this patch:
```bash
$ clang++ -Wlifetime-safety test-simple.cpp
test-simple.cpp:7:15: warning: object whose reference is captured does not live
long enough [-Wlifetime-safety-use-after-scope]
7 | int *a = &tgt;
| ^~~
test-simple.cpp:12:3: note: destroyed here
12 | }
| ^
test-simple.cpp:13:22: note: later used here
13 | printf("s: %d\n", *s);
| ^
1 warning generated.
```
After this patch:
```bash
$clang++ -Wlifetime-safety test-simple.cpp
test-simple.cpp:7:15: warning: object whose reference is captured does not live
long enough [-Wlifetime-safety-use-after-scope]
7 | int *a = &tgt;
| ^~~
test-simple.cpp:12:3: note: destroyed here
12 | }
| ^
test-simple.cpp:7:15: note: variable `a` is now an alias of `tgt`
7 | int *a = &tgt;
| ^
test-simple.cpp:9:14: note: variable `b` is now an alias of `a`
9 | int *b = a;
| ^
test-simple.cpp:10:14: note: variable `e` is now an alias of `b`
10 | int *e = b;
| ^
test-simple.cpp:11:9: note: variable `s` is now an alias of `e`
11 | s = e;
| ^
test-simple.cpp:13:22: note: later used here
13 | printf("s: %d\n", *s);
| ^
1 warning generated.
```
## Details
The implementation initiates the search from the CFG Block containing
`UF->getUseExpr()`. To facilitate this, I have cached `AnalysisDeclContext
&ADC` to retrieve the block via
`ADC.getCFGStmtMap()->getBlock(UF->getUseExpr())`.
The search process employs BFS to traverse CFG blocks in reverse order. Within
each block, Facts are traversed backward, specifically focusing on
`OriginFlowFact` and `IssueFact`:
- OriginFlowFact: Generally, I would check if the current
OFF->getDestOriginID() is what I want. If it is, then I would prepare to record
it. The matched format is similar to `ValueDecl = Expr`.
- IssueFact: When encountering `IssueFact`, it checks if it is the Loan from
the error. If so, it records `IF->getOriginID()`, and when it finds this
`OriginID` in `OriginFlowFact`, it records it as the last assignment statement
being found.
- This logic specifically addresses loop back edges, where `Block->preds()`
might point to the loop header rather than the loop body after a branch
convergence.
The logic for intra-block Fact traversal is encapsulated into a standalone
function for better modularity. This also allows the analysis to handle cases
where an `IssueFact` and `UseFact` reside within the same Block by invoking
this function directly, eliminating the assumption of multi-block spans.
I will use the following code to iterate through the `UsedOrigins` of `UseFact`
to ensure an assignment chain is found:
```cpp
for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
Cur = Cur->peelOuterOrigin())
```
## Testing
I didn't add any extra test cases; I simply used the existing ones. Below are
the results of my local test:
```bash
$ cmake --build build/ -t check-clang-sema
[79/80] Running lit suite
/var/home/suoyuan/codepjt/cpp/llvm-project/clang/test/Sema
llvm-lit:
/var/home/suoyuan/codepjt/cpp/llvm-project/llvm/utils/lit/lit/llvm/config.py:569:
note: using clang: /var/home/suoyuan/codepjt/cpp/llvm-project/build/bin/clang
llvm-lit:
/var/home/suoyuan/codepjt/cpp/llvm-project/llvm/utils/lit/lit/llvm/subst.py:130:
note: Did not find cir-opt in
/var/home/suoyuan/codepjt/cpp/llvm-project/build/bin:/var/home/suoyuan/codepjt/cpp/llvm-project/build/bin
Testing Time: 10.56s
Total Discovered Tests: 1331
Unsupported: 200 (15.03%)
Passed : 1131 (84.97%)
$ cmake --build build/ -t check-clang
[12/3016] Building native llvm-min-tblgen...
[8/8] Linking CXX executable bin/llvm-min-tblgen
[25/2964] Building native clang-tblgen...
[1/1] Linking CXX executable bin/clang-tblgen
[191/624] Building native llvm-tblgen...
[24/24] Linking CXX executable bin/llvm-tblgen
[327/328] Running the Clang regression tests
llvm-lit:
/var/home/suoyuan/codepjt/cpp/llvm-project/llvm/utils/lit/lit/llvm/config.py:569:
note: using clang: /var/home/suoyuan/codepjt/cpp/llvm-project/build/bin/clang
llvm-lit:
/var/home/suoyuan/codepjt/cpp/llvm-project/llvm/utils/lit/lit/llvm/subst.py:130:
note: Did not find cir-opt in
/var/home/suoyuan/codepjt/cpp/llvm-project/build/bin:/var/home/suoyuan/codepjt/cpp/llvm-project/build/bin
Testing Time: 1336.82s
Total Discovered Tests: 51373
Skipped : 6 (0.01%)
Unsupported : 2597 (5.06%)
Passed : 48745 (94.88%)
Expectedly Failed: 25 (0.05%)
```
>From 35940ebe476a5fc46c479ec00d87d7a72aaba064 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/LifetimeSafety.h | 13 +-
.../clang/Basic/DiagnosticSemaKinds.td | 1 +
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 213 +++++++++-
clang/lib/Sema/SemaLifetimeSafety.h | 49 +++
.../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 ++++++++++++------
8 files changed, 579 insertions(+), 164 deletions(-)
diff --git
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 08038dd096685..f1298ec528957 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -45,6 +45,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 +65,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 llvm::SmallVector<AssignmentPair> 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 e766d6e1dcf06..5a38d89e228ef 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10984,6 +10984,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/Checker.cpp
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 36477c6f67b52..cb21e11a3ca3f 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -56,6 +56,13 @@ using AnnotationTarget =
using EscapingTarget =
llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
+struct AliasAssignmentSearchResult {
+ const llvm::SmallVector<AssignmentPair> Payload;
+ bool SearchComplete;
+ const ValueDecl *LastDestDecl;
+ std::optional<OriginID> LastOrigin;
+};
+
class LifetimeChecker {
private:
llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
@@ -67,6 +74,7 @@ class LifetimeChecker {
FactManager &FactMgr;
LifetimeSafetySemaHelper *SemaHelper;
ASTContext &AST;
+ AnalysisDeclContext &ADC;
static SourceLocation
GetFactLoc(llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> F)
{
@@ -89,7 +97,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>())
@@ -219,6 +227,195 @@ class LifetimeChecker {
}
}
+ std::optional<llvm::SmallVector<AssignmentPair>>
+ getAliasListInMultiBlock(const CFGBlock *StartBlock, const LoanID EndLoanID,
+ OriginID *StartOID) {
+ const ValueDecl *LastDestDecl = nullptr;
+ std::queue<const CFGBlock *> PendingBlocks;
+ std::optional<AssignmentPair> StartStmt = std::nullopt;
+ std::optional<AssignmentPair> EndStmt = std::nullopt;
+ std::optional<OriginID> LastOriginID = std::nullopt;
+ llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks;
+ llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs;
+
+ const auto AliasStmtFilter = [&VistedExprs](const AssignmentPair StartStmt,
+ const AssignmentPair EndStmt) {
+ llvm::SmallVector<AssignmentPair> AliasStmts;
+ for (auto Stmt = StartStmt; Stmt != EndStmt;
+ Stmt = VistedExprs.at(Stmt)) {
+ AliasStmts.push_back(Stmt);
+ }
+ AliasStmts.push_back(EndStmt);
+ return AliasStmts;
+ };
+
+ PendingBlocks.push(StartBlock);
+
+ while (!PendingBlocks.empty()) {
+ const CFGBlock *CurrBlock = PendingBlocks.front();
+ PendingBlocks.pop();
+
+ const auto [BlockAliasList, Success, CurrLastDestDecl, CurrLastOriginID]
=
+ getAliasListCore(CurrBlock, EndLoanID, StartOID, LastDestDecl,
+ LastOriginID);
+ if (CurrLastDestDecl)
+ LastDestDecl = CurrLastDestDecl;
+ 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(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;
+ }
+
+ std::optional<OriginSrcExpr> 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;
+ }
+
+ /// 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.
+ AliasAssignmentSearchResult
+ getAliasListCore(const CFGBlock *Block, const LoanID EndLoanID,
+ OriginID *TargetOID, const ValueDecl *LastDestDecl =
nullptr,
+ const std::optional<OriginID> LastOriginID = std::nullopt) {
+ llvm::SmallVector<AssignmentPair> AliasStmts;
+ const ValueDecl *DestDecl = LastDestDecl;
+ const auto Facts = FactMgr.getFacts(Block);
+ bool FetchLoan = false;
+ auto IssueOriginID = LastOriginID;
+
+ for (auto F = Facts.rbegin(); F != Facts.rend(); ++F) {
+ if (const auto *OFF = (*F)->getAs<OriginFlowFact>()) {
+ if (IssueOriginID.has_value() &&
+ OFF->getDestOriginID() == IssueOriginID.value()) {
+ FetchLoan = true;
+ }
+ if (OFF->getDestOriginID() == *TargetOID) {
+ const auto HeldLoans =
+ LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF);
+
+ if (HeldLoans.contains(EndLoanID)) {
+ const auto TargetOrigin =
+ FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID());
+
+ if (DestDecl == nullptr) {
+ if (const ValueDecl *DDecl = TargetOrigin.getDecl()) {
+ DestDecl = DDecl;
+ }
+ } else {
+ auto SExpr = GetPureSrcExpr(TargetOrigin.getExpr());
+ if (!SExpr.has_value()) {
+ const auto SrcOrigin =
+ FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID());
+ SExpr = GetPureSrcExpr(SrcOrigin.getExpr());
+ }
+
+ if (SExpr.has_value()) {
+ AliasStmts.push_back({SExpr.value(), DestDecl});
+ DestDecl = nullptr;
+ }
+ }
+ *TargetOID = OFF->getSrcOriginID();
+ }
+ }
+ } else if (const auto *IF = (*F)->getAs<IssueFact>()) {
+ if (IF->getLoanID() == EndLoanID) {
+ IssueOriginID = IF->getOriginID();
+ }
+ }
+
+ if (FetchLoan) {
+ return {AliasStmts, true, DestDecl, IssueOriginID};
+ }
+ }
+ return {AliasStmts, false, DestDecl, IssueOriginID};
+ }
+
+ std::optional<llvm::SmallVector<AssignmentPair>>
+ getAliasList(const UseFact *UF, const LoanID End, const bool InOneBlock) {
+ const CFGBlock *IssueBlock =
+ ADC.getCFGStmtMap()->getBlock(UF->getUseExpr());
+ assert(IssueBlock && "Searching CFGBlock failed");
+
+ for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+ Cur = Cur->peelOuterOrigin()) {
+ auto TargetOID = Cur->getOuterOriginID();
+ if (InOneBlock) {
+ AliasAssignmentSearchResult Result =
+ getAliasListCore(IssueBlock, End, &TargetOID);
+ if (!Result.Payload.empty())
+ return Result.Payload;
+ } else {
+ auto Result = getAliasListInMultiBlock(IssueBlock, End, &TargetOID);
+ if (Result.has_value())
+ return Result.value();
+ }
+ }
+
+ return std::nullopt;
+ }
+
void issuePendingWarnings() {
if (!SemaHelper)
return;
@@ -243,10 +440,20 @@ class LifetimeChecker {
SemaHelper->reportUseAfterInvalidation(
InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr);
- } else
+ } else {
// Scope-based expiry (use-after-scope).
+ const CFGStmtMap *CurrCFGStmtMap = ADC.getCFGStmtMap();
+ const auto AliasExprs =
+ getAliasList(UF, LID,
+ CurrCFGStmtMap->getBlock(UF->getUseExpr()) ==
+ CurrCFGStmtMap->getBlock(IssueExpr));
+ if (!AliasExprs.has_value()) {
+ llvm::dbgs() << "Search variable assignment chain failed\n";
+ }
+
SemaHelper->reportUseAfterFree(IssueExpr, UF->getUseExpr(),
MovedExpr,
- ExpiryLoc);
+ AliasExprs.value_or({}), 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..0f75e52e6c120 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -45,6 +45,7 @@ class LifetimeSafetySemaHelperImpl : public
LifetimeSafetySemaHelper {
void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr,
+ const llvm::SmallVector<AssignmentPair> AliasList,
SourceLocation FreeLoc) override {
S.Diag(IssueExpr->getExprLoc(),
MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved
@@ -54,6 +55,54 @@ 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);
+
+ for (auto AliasStmt = AliasList.rbegin(); AliasStmt != AliasList.rend();
+ ++AliasStmt) {
+ if (const auto *CurrDeclExpr =
+ llvm::dyn_cast<const DeclRefExpr *>((*AliasStmt).first)) {
+ S.Diag(CurrDeclExpr->getExprLoc(),
+ diag::note_lifetime_safety_note_alias_chain)
+ << (*AliasStmt).second->getNameAsString()
+ << CurrDeclExpr->getDecl()->getNameAsString();
+ } else if (const auto *CurrDeclExpr =
+ llvm::dyn_cast<const CXXTemporaryObjectExpr *>(
+ (*AliasStmt).first)) {
+ S.Diag(CurrDeclExpr->getExprLoc(),
+ diag::note_lifetime_safety_note_alias_chain)
+ << (*AliasStmt).second->getNameAsString()
+ << CurrDeclExpr->getConstructor()->getNameAsString() + "()";
+ } else if (const auto *CurrCallExpr =
+ llvm::dyn_cast<const CallExpr *>((*AliasStmt).first)) {
+ std::string outString;
+ llvm::raw_string_ostream ss(outString);
+
+ LangOptions lo;
+ PrintingPolicy policy(lo);
+
+ if (const Expr *callee = CurrCallExpr->getCallee()) {
+ callee->IgnoreParenCasts()->printPretty(ss, nullptr, policy);
+ }
+
+ ss << "(";
+
+ for (unsigned i = 0; i < CurrCallExpr->getNumArgs(); ++i) {
+ const Expr *arg = CurrCallExpr->getArg(i);
+ if (arg) {
+ arg->printPretty(ss, nullptr, policy);
+ }
+
+ if (i < CurrCallExpr->getNumArgs() - 1) {
+ ss << ", ";
+ }
+ }
+
+ ss << ")";
+ S.Diag(CurrCallExpr->getExprLoc(),
+ diag::note_lifetime_safety_note_alias_chain)
+ << (*AliasStmt).second->getNameAsString() << ss.str();
+ }
+ }
+
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