[clang] [LifetimeSafety] Introduce buildOriginFlowChain for use-after-scope (PR #199345)
Yuan Suo via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 1 01:57:18 PDT 2026
https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/199345
>From 02bbdc2c415f3364c7b27057758c10a9f594f03d Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 22 May 2026 15:10:28 +0800
Subject: [PATCH 1/4] [LifetimeSafety] Introduce buildOriginFlowChain for
use-after-scope
After adding buildOriginFlowChain, we need to choose a diagnostic type that
is as simple as possible to verify its feasibility during Sema diagnostics.
I did not choose the annotation suggestions described in
https://github.com/llvm/llvm-project/pull/188467/#issuecomment-4359071778 as the
first target to implement, because it does not seem to occur within a single
CFG block. The IssueFact always resides in the block preceding the
OriginEscapesFact, which causes me to always get an empty OriginFlowChain.
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 7 ++-
.../clang/Basic/DiagnosticSemaKinds.td | 1 +
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 56 ++++++++++++++++++-
clang/lib/Sema/SemaLifetimeSafety.h | 25 ++++++++-
clang/test/Sema/warn-lifetime-safety.cpp | 6 +-
5 files changed, 86 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 398cce1395854..42f7d80f90a02 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -61,9 +61,10 @@ class LifetimeSafetySemaHelper {
LifetimeSafetySemaHelper() = default;
virtual ~LifetimeSafetySemaHelper() = default;
- virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
- const Expr *MovedExpr,
- SourceLocation FreeLoc) {}
+ virtual void
+ reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
+ const Expr *MovedExpr, SourceLocation FreeLoc,
+ llvm::SmallVector<const Expr *> OriginExprChain) {}
virtual void reportUseAfterReturn(const Expr *IssueExpr,
const Expr *ReturnExpr,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 077aace321264..54fb4c2fbc6c8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11047,6 +11047,7 @@ def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global storage">;
def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">;
def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">;
+def note_lifetime_safety_note_alias_chain : Note<"%0 aliases the storage of %1">;
def warn_lifetime_safety_intra_tu_param_suggestion
: Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index d6d4ec6b5617e..2947c70301a44 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -260,10 +260,25 @@ class LifetimeChecker {
SemaHelper->reportUseAfterInvalidation(
InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr);
- } else
+ } else {
// Scope-based expiry (use-after-scope).
+
+ llvm::SmallVector<OriginID> OriginFlowChain;
+
+ for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+ Cur = Cur->peelOuterOrigin())
+ if (LoanPropagation.getLoans(Cur->getOuterOriginID(), UF)
+ .contains(LID))
+ OriginFlowChain = LoanPropagation.buildOriginFlowChain(
+ FactMgr, UF, Cur->getOuterOriginID(), LID);
+
+ const llvm::SmallVector<const Expr *> OriginExprChain =
+ buildExprOrDeclChain(OriginFlowChain);
SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
- MovedExpr, ExpiryLoc);
+ MovedExpr, ExpiryLoc,
+ OriginExprChain);
+ }
+
} else if (const auto *OEF =
CausingFact.dyn_cast<const OriginEscapesFact *>()) {
if (Warning.InvalidatedByExpr) {
@@ -487,6 +502,43 @@ class LifetimeChecker {
}
}
}
+
+ /// Retrieve a list of reliable expressions from OriginIFlowChain that
+ /// can be used for Sema warnings.
+ ///
+ /// Although the AST node corresponding to Origin can be either a
+ /// `const Expr *` or a `const ValueDecl *`, `buildOriginFlowChain` only
+ /// collects Origins from RHS expressions. Therefore, we do not need to
+ /// handle non-expression cases here.
+ llvm::SmallVector<const Expr *>
+ buildExprOrDeclChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
+ llvm::SmallVector<const Expr *> rs;
+ const SourceManager &SM = AST.getSourceManager();
+
+ auto InsertOrReplace = [&rs, &SM](const Expr *NewNode) {
+ if (!NewNode)
+ return;
+ SourceLocation NewLocation = NewNode->getExprLoc();
+ if (NewLocation.isInvalid())
+ return;
+
+ const Expr *LastNode = rs.back();
+ SourceLocation LastLocation = LastNode->getExprLoc();
+ if (SM.getSpellingLineNumber(LastLocation) ==
+ SM.getSpellingLineNumber(NewLocation))
+ rs.back() = NewNode;
+ else
+ rs.push_back(NewNode);
+ };
+
+ for (const OriginID CurrOID : OriginFlowChain)
+ if (!rs.empty())
+ InsertOrReplace(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr());
+ else
+ rs.push_back(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr());
+
+ return rs;
+ }
};
} // namespace
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 6da4953dea56d..1ce4ddea838b7 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -55,6 +55,15 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
return false;
}
+inline StringRef formatExpr(const Expr *E) {
+ const Expr *PureExpr = E->IgnoreImpCasts();
+ if (const DeclRefExpr *DRExpr = dyn_cast<DeclRefExpr>(PureExpr))
+ return DRExpr->getDecl()->getName();
+
+ // TODO: Add support for more expression types.
+ return "";
+}
+
class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
public:
@@ -62,17 +71,31 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr,
- SourceLocation FreeLoc) override {
+ SourceLocation FreeLoc, llvm::SmallVector<const Expr *> OriginExprChain) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_use_after_scope_moved
: diag::warn_lifetime_safety_use_after_scope;
S.Diag(IssueExpr->getExprLoc(), DiagID)
<< getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange();
+
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
+
+ StringRef IssueStr;
+ for (const Expr *CurrExpr : reverse(OriginExprChain)) {
+ if (IssueStr.empty()) {
+ IssueStr = formatExpr(CurrExpr);
+ continue;
+ }
+
+ S.Diag(CurrExpr->getBeginLoc(),
+ diag::note_lifetime_safety_aliases_storage)
+ << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr;
+ }
+
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
}
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 06097d4600af5..90708bd4c3e15 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -92,7 +92,7 @@ void pointer_chain() {
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
- q = p;
+ q = p; // expected-note {{p aliases the storage of s}}
} // expected-note {{destroyed here}}
(void)*q; // expected-note {{later used here}}
}
@@ -1759,8 +1759,8 @@ void test_temporary() {
std::string_view z;
{
S s;
- const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}}
- z = zz;
+ const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}}
+ z = zz; // expected-note {{aliases the storage of s}}
} // expected-note {{destroyed here}}
(void)z; // expected-note {{later used here}}
}
>From d6a2df274463e6490f17bf17617334328bb86c8b Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Thu, 28 May 2026 09:58:12 +0800
Subject: [PATCH 2/4] Reduce the side effects of buildExprOrDeclChain and apply
some minor cleanups
Previously, `buildExprOrDeclChain` was responsible not only for constructing an expression list,
but also for filtering out expressions that should not be emitted.
However, this filtering logic does not need to be handled there.
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 5 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 36 +++-----------
clang/lib/Sema/SemaLifetimeSafety.h | 48 ++++++++++++++-----
3 files changed, 48 insertions(+), 41 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 42f7d80f90a02..d635105043b26 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -61,10 +61,13 @@ class LifetimeSafetySemaHelper {
LifetimeSafetySemaHelper() = default;
virtual ~LifetimeSafetySemaHelper() = default;
+ virtual void
+ reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {}
+
virtual void
reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr, SourceLocation FreeLoc,
- llvm::SmallVector<const Expr *> OriginExprChain) {}
+ llvm::ArrayRef<const Expr *> OriginExprChain) {}
virtual void reportUseAfterReturn(const Expr *IssueExpr,
const Expr *ReturnExpr,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 2947c70301a44..3abdf92e2989a 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -503,40 +503,18 @@ class LifetimeChecker {
}
}
- /// Retrieve a list of reliable expressions from OriginIFlowChain that
- /// can be used for Sema warnings.
+ /// Extract expressions from the origin flow chain for diagnostic purposes.
///
- /// Although the AST node corresponding to Origin can be either a
- /// `const Expr *` or a `const ValueDecl *`, `buildOriginFlowChain` only
- /// collects Origins from RHS expressions. Therefore, we do not need to
- /// handle non-expression cases here.
+ /// Given a chain of origins that shows how a loan propagates, this function
+ /// extracts the corresponding expressions for each origin. Origins that refer
+ /// to declarations (rather than expressions) are skipped.
llvm::SmallVector<const Expr *>
buildExprOrDeclChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
llvm::SmallVector<const Expr *> rs;
- const SourceManager &SM = AST.getSourceManager();
-
- auto InsertOrReplace = [&rs, &SM](const Expr *NewNode) {
- if (!NewNode)
- return;
- SourceLocation NewLocation = NewNode->getExprLoc();
- if (NewLocation.isInvalid())
- return;
-
- const Expr *LastNode = rs.back();
- SourceLocation LastLocation = LastNode->getExprLoc();
- if (SM.getSpellingLineNumber(LastLocation) ==
- SM.getSpellingLineNumber(NewLocation))
- rs.back() = NewNode;
- else
- rs.push_back(NewNode);
- };
-
for (const OriginID CurrOID : OriginFlowChain)
- if (!rs.empty())
- InsertOrReplace(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr());
- else
- rs.push_back(FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr());
-
+ if (const Expr *CurrExpr =
+ FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
+ rs.push_back(CurrExpr);
return rs;
}
};
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 1ce4ddea838b7..a4f0b7db9705c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -64,11 +64,47 @@ inline StringRef formatExpr(const Expr *E) {
return "";
}
+inline bool shouldShowInAliasingChain(const Expr *CurrExpr,
+ const Expr *LastExpr) {
+ if (isa<ImplicitCastExpr>(CurrExpr))
+ return false;
+ if (isa<ParenExpr>(CurrExpr))
+ return false;
+ if (isa<CastExpr>(CurrExpr))
+ return false;
+ if (CurrExpr->getSourceRange().isInvalid())
+ return false;
+
+ if (LastExpr && CurrExpr->getSourceRange() == LastExpr->getSourceRange())
+ return false;
+
+ return true;
+}
+
class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
public:
LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
+ void
+ reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) override {
+ StringRef IssueStr;
+ const Expr *LastExpr = nullptr;
+ for (const Expr *CurrExpr : reverse(OriginExprChain)) {
+ if (IssueStr.empty()) {
+ IssueStr = formatExpr(CurrExpr);
+ continue;
+ }
+
+ if (shouldShowInAliasingChain(CurrExpr, LastExpr)) {
+ S.Diag(CurrExpr->getBeginLoc(),
+ diag::note_lifetime_safety_note_alias_chain)
+ << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr;
+ LastExpr = CurrExpr;
+ }
+ }
+ }
+
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr,
SourceLocation FreeLoc, llvm::SmallVector<const Expr *> OriginExprChain) override {
@@ -84,17 +120,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
<< MovedExpr->getSourceRange();
S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
- StringRef IssueStr;
- for (const Expr *CurrExpr : reverse(OriginExprChain)) {
- if (IssueStr.empty()) {
- IssueStr = formatExpr(CurrExpr);
- continue;
- }
-
- S.Diag(CurrExpr->getBeginLoc(),
- diag::note_lifetime_safety_aliases_storage)
- << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr;
- }
+ reportAliasingChain(OriginExprChain);
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
>From e7ecc7869cb12e4de594e7c07f61585bd60e249d Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Thu, 28 May 2026 21:08:16 +0800
Subject: [PATCH 3/4] rebase to use getDiagSubjectDescription
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 3 -
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 4 +-
clang/lib/Sema/SemaLifetimeSafety.h | 98 +++++++-------
.../Sema/warn-lifetime-analysis-nocfg.cpp | 32 +++--
.../Sema/warn-lifetime-safety-suggestions.cpp | 6 +-
clang/test/Sema/warn-lifetime-safety.cpp | 127 ++++++++++++------
7 files changed, 163 insertions(+), 109 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index d635105043b26..78f39f4609358 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -61,9 +61,6 @@ class LifetimeSafetySemaHelper {
LifetimeSafetySemaHelper() = default;
virtual ~LifetimeSafetySemaHelper() = default;
- virtual void
- reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {}
-
virtual void
reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr, SourceLocation FreeLoc,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 54fb4c2fbc6c8..0e1d13653a169 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11047,7 +11047,7 @@ def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global storage">;
def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">;
def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">;
-def note_lifetime_safety_note_alias_chain : Note<"%0 aliases the storage of %1">;
+def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of %1">;
def warn_lifetime_safety_intra_tu_param_suggestion
: Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 3abdf92e2989a..2f5db5bd109c8 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -268,9 +268,11 @@ class LifetimeChecker {
for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
Cur = Cur->peelOuterOrigin())
if (LoanPropagation.getLoans(Cur->getOuterOriginID(), UF)
- .contains(LID))
+ .contains(LID)) {
OriginFlowChain = LoanPropagation.buildOriginFlowChain(
FactMgr, UF, Cur->getOuterOriginID(), LID);
+ break;
+ }
const llvm::SmallVector<const Expr *> OriginExprChain =
buildExprOrDeclChain(OriginFlowChain);
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index a4f0b7db9705c..0fe439b3a8569 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -55,59 +55,15 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
return false;
}
-inline StringRef formatExpr(const Expr *E) {
- const Expr *PureExpr = E->IgnoreImpCasts();
- if (const DeclRefExpr *DRExpr = dyn_cast<DeclRefExpr>(PureExpr))
- return DRExpr->getDecl()->getName();
-
- // TODO: Add support for more expression types.
- return "";
-}
-
-inline bool shouldShowInAliasingChain(const Expr *CurrExpr,
- const Expr *LastExpr) {
- if (isa<ImplicitCastExpr>(CurrExpr))
- return false;
- if (isa<ParenExpr>(CurrExpr))
- return false;
- if (isa<CastExpr>(CurrExpr))
- return false;
- if (CurrExpr->getSourceRange().isInvalid())
- return false;
-
- if (LastExpr && CurrExpr->getSourceRange() == LastExpr->getSourceRange())
- return false;
-
- return true;
-}
-
class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
public:
LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
void
- reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) override {
- StringRef IssueStr;
- const Expr *LastExpr = nullptr;
- for (const Expr *CurrExpr : reverse(OriginExprChain)) {
- if (IssueStr.empty()) {
- IssueStr = formatExpr(CurrExpr);
- continue;
- }
-
- if (shouldShowInAliasingChain(CurrExpr, LastExpr)) {
- S.Diag(CurrExpr->getBeginLoc(),
- diag::note_lifetime_safety_note_alias_chain)
- << CurrExpr->getSourceRange() << formatExpr(CurrExpr) << IssueStr;
- LastExpr = CurrExpr;
- }
- }
- }
-
- void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
- const Expr *MovedExpr,
- SourceLocation FreeLoc, llvm::SmallVector<const Expr *> OriginExprChain) override {
+ reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
+ const Expr *MovedExpr, SourceLocation FreeLoc,
+ llvm::ArrayRef<const Expr *> OriginExprChain) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_use_after_scope_moved
: diag::warn_lifetime_safety_use_after_scope;
@@ -529,6 +485,54 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
return "";
}
+ const Expr *extractExpr(const Expr *E) {
+ // FIXME: Ideally, this should use IgnoreParenImpCasts().
+ // However, according to the comment on IgnoreParenImpCasts(),
+ // it is not fully equivalent to IgnoreImpCasts() + IgnoreParens().
+ // Once the FIXME in IgnoreParenImpCasts() is resolved,
+ // this can be switched to use IgnoreParenImpCasts().
+ const Expr *PureExpr = E->IgnoreImpCasts()->IgnoreParens();
+
+ if (const auto *UO = dyn_cast<UnaryOperator>(PureExpr))
+ return UO->getSubExpr();
+ // For a BinaryOperator, there is only one relevant case: assignment
+ // chains. Therefore, we only need to consider the LHS expression.
+ if (const auto *BO = dyn_cast<BinaryOperator>(PureExpr))
+ return BO->getLHS();
+
+ // TODO: Handle other expression types.
+ return PureExpr;
+ }
+
+ void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
+ std::string IssueStr;
+ const Expr *LastExpr = nullptr;
+ for (const Expr *CurrExpr : reverse(OriginExprChain)) {
+ if (IssueStr.empty()) {
+ IssueStr = getDiagSubjectDescription(CurrExpr);
+ LastExpr = CurrExpr;
+ continue;
+ }
+
+ const Expr *ExtractedExpr = extractExpr(CurrExpr);
+ if (LastExpr &&
+ ExtractedExpr->getSourceRange() == LastExpr->getSourceRange())
+ continue;
+
+ // FIXME: Because getDiagSubjectDescription and extractExpr is not fully
+ // implemented yet, some diagnostic that should have been issued are
+ // currently being skipped here.
+ std::string ExprName = getDiagSubjectDescription(ExtractedExpr);
+ if (ExprName.empty())
+ continue;
+
+ S.Diag(ExtractedExpr->getBeginLoc(),
+ diag::note_lifetime_safety_aliases_storage)
+ << ExtractedExpr->getSourceRange() << ExprName << IssueStr;
+ LastExpr = ExtractedExpr;
+ }
+ }
+
Sema &S;
};
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 41b07771c52c1..61e162f01947f 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -245,7 +245,8 @@ std::string_view containerWithAnnotatedElements() {
use(c1); // cfg-note {{later used here}}
c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
use(c1); // cfg-note {{later used here}}
// no warning on constructing from gsl-pointer
@@ -862,7 +863,8 @@ std::string_view test1_1() {
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
use(t1); // cfg-note {{later used here}}
t1 = Ref(std::string()); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
use(t1); // cfg-note {{later used here}}
return Ref(std::string()); // expected-warning {{returning address}} \
// cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}}
@@ -873,7 +875,8 @@ std::string_view test1_2() {
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
use(t2); // cfg-note {{later used here}}
t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
use(t2); // cfg-note {{later used here}}
return TakeSv(std::string()); // expected-warning {{returning address}} \
@@ -885,7 +888,8 @@ std::string_view test1_3() {
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
use(t3); // cfg-note {{later used here}}
t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
use(t3); // cfg-note {{later used here}}
return TakeStrRef(std::string()); // expected-warning {{returning address}} \
// cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}}
@@ -910,7 +914,8 @@ std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
use(t1); // cfg-note {{later used here}}
t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
use(t1); // cfg-note {{later used here}}
return r1.get(); // expected-warning {{address of stack}} \
// cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}}
@@ -941,7 +946,7 @@ struct [[gsl::Pointer]] Pointer {
Pointer test3(Bar bar) {
Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
use(p); // cfg-note {{later used here}}
- p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
use(p); // cfg-note {{later used here}}
return bar; // expected-warning {{address of stack}} cfg-warning {{stack memory associated with parameter 'bar' is returned}} cfg-note {{returned here}}
}
@@ -1028,9 +1033,12 @@ void operator_star_arrow_reference() {
const std::string& r = *v.begin();
auto temporary = []() { return std::vector<std::string>{{"1"}}; };
- const char* x = temporary().begin()->data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
- const char* y = (*temporary().begin()).data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
- const std::string& z = (*temporary().begin()); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ const char* x = temporary().begin()->data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ const char* y = (*temporary().begin()).data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ const std::string& z = (*temporary().begin()); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{local temporary object aliases the storage of local temporary object}}
use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
}
@@ -1042,9 +1050,9 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
const std::string& r = (*v.begin()).second;
auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, "1"}}; };
- const char* x = temporary().begin()->second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
- const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
- const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ const char* x = temporary().begin()->second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
+ const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
+ const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
}
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index f5a3cf89e4c8d..85b80a7af811b 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -599,7 +599,8 @@ void uaf_via_inferred_lifetimebound() {
std::function<void()> f = []() {};
{
int local;
- f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}}
+ f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)f; // expected-note {{later used here}}
}
@@ -622,7 +623,8 @@ void test_inference() {
std::unique_ptr<LifetimeBoundCtor> ptr;
{
MyObj obj;
- ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index 90708bd4c3e15..d9fb06154c67e 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -92,7 +92,7 @@ void pointer_chain() {
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
- q = p; // expected-note {{p aliases the storage of s}}
+ q = p; // expected-note {{local variable 'p' aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*q; // expected-note {{later used here}}
}
@@ -102,7 +102,7 @@ void propagation_gsl() {
{
MyObj s;
v1 = s; // expected-warning {{local variable 's' does not live long enough}}
- v2 = v1;
+ v2 = v1; // expected-note {{local variable 'v1' aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
v2.use(); // expected-note {{later used here}}
}
@@ -138,7 +138,8 @@ void multiple_pointers_chained() {
{
MyObj s;
MyObj* obj1, *obj2;
- p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}}
+ p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}} \
+ // expected-note 2 {{aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -696,7 +697,7 @@ void test_lifetimebound_multi_level() {
int* p = nullptr;
int** pp = &p;
int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live long enough}}
- result = return_inner_ptr_addr(ppp);
+ result = return_inner_ptr_addr(ppp); // expected-note {{local variable 'ppp' aliases the storage of local variable 'pp'}}
} // expected-note {{destroyed here}}
(void)**result; // expected-note {{used here}}
}
@@ -819,7 +820,8 @@ void lifetimebound_simple_function() {
View v;
{
MyObj obj;
- v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -828,7 +830,8 @@ void lifetimebound_multiple_args_definite() {
View v;
{
MyObj obj1, obj2;
- v = Choose(true,
+ v = Choose(true, // expected-note {{local temporary object aliases the storage of local variable 'obj1'}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj2'}}
obj1, // expected-warning {{local variable 'obj1' does not live long enough}}
obj2); // expected-warning {{local variable 'obj2' does not live long enough}}
} // expected-note 2 {{destroyed here}}
@@ -855,7 +858,8 @@ void lifetimebound_mixed_args() {
View v;
{
MyObj obj1, obj2;
- v = SelectFirst(obj1, // expected-warning {{local variable 'obj1' does not live long enough}}
+ v = SelectFirst(obj1, // expected-warning {{local variable 'obj1' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj1'}}
obj2);
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
@@ -871,7 +875,8 @@ void lifetimebound_member_function() {
View v;
{
MyObj obj;
- v = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}}
+ v = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -895,7 +900,8 @@ void lifetimebound_chained_calls() {
View v;
{
MyObj obj;
- v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}}
+ v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -913,7 +919,8 @@ void chained_assignment_lifetimebound_call() {
MyObj *p, *obj;
{
MyObj s;
- p = Identity(obj = &s); // expected-warning {{does not live long enough}}
+ p = Identity(obj = &s); // expected-warning {{does not live long enough}} \
+ // expected-note {{local variable 'obj' aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -945,8 +952,8 @@ void lifetimebound_return_reference() {
{
MyObj obj;
View temp_v = obj; // expected-warning {{local variable 'obj' does not live long enough}}
- const MyObj& ref = GetObject(temp_v);
- ptr = &ref;
+ const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}}
+ ptr = &ref; // expected-note {{local variable 'ref' aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
@@ -972,7 +979,8 @@ void lifetimebound_ctor_functional_cast() {
LifetimeBoundCtor v;
{
MyObj obj;
- v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -981,7 +989,8 @@ void lifetimebound_ctor_c_style_cast() {
LifetimeBoundCtor v;
{
MyObj obj;
- v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -990,7 +999,8 @@ void lifetimebound_ctor_static_cast() {
LifetimeBoundCtor v;
{
MyObj obj;
- v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -999,7 +1009,8 @@ void lifetimebound_make_unique() {
std::unique_ptr<LifetimeBoundCtor> ptr;
{
MyObj obj;
- ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}}
+ ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}} \
+ // tu-note {{local temporary object aliases the storage of local variable 'obj'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1053,7 +1064,8 @@ void lifetimebound_make_unique_multi_params() {
MyObj obj_long;
{
MyObj obj_short;
- ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}}
+ ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}} \
+ // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1063,7 +1075,8 @@ void lifetimebound_make_unique_multi_params2() {
MyObj obj_long;
{
MyObj obj_short;
- ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}}
+ ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}} \
+ // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1083,7 +1096,8 @@ void lifetimebound_make_unique_multi_params3_1() {
MyObj obj_long;
{
MyObj obj_short;
- ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}}
+ ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \
+ // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1093,7 +1107,8 @@ void lifetimebound_make_unique_multi_params3_2() {
MyObj obj_long;
{
MyObj obj_short;
- ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}}
+ ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \
+ // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1235,6 +1250,16 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
(void)*p; // expected-note 4 {{later used here}}
}
+void simpleparen() {
+ MyObj* p;
+ {
+ MyObj a;
+ MyObj* b = &a; // expected-warning {{local variable 'a' does not live long enough}}
+ p = (((b))); // expected-note {{local variable 'b' aliases the storage of local variable 'a'}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
+
void parentheses(bool cond) {
MyObj* p;
{
@@ -1324,7 +1349,8 @@ void foobar() {
View view;
{
StatusOr<MyObj> string_or = getStringOr();
- view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}}
+ view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'string_or'}}
value();
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
@@ -1415,7 +1441,8 @@ void test_user_defined_deref_with_view() {
{
MyObj obj;
SmartPtr<MyObj> smart_ptr(&obj);
- v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not live long enough}}
+ v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1621,7 +1648,8 @@ void wrong_use_of_move_is_permissive() {
View v;
{
MyObj a;
- v = std::move(a); // expected-warning {{local variable 'a' does not live long enough}}
+ v = std::move(a); // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
const int* p;
@@ -1660,7 +1688,8 @@ void bar() {
View x;
{
S s;
- x = s.x(); // expected-warning {{local variable 's' does not live long enough}}
+ x = s.x(); // expected-warning {{local variable 's' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 's'}}
View y = S().x(); // expected-warning {{local temporary object does not live long enough}} \
expected-note {{destroyed here}}
(void)y; // expected-note {{used here}}
@@ -1759,8 +1788,8 @@ void test_temporary() {
std::string_view z;
{
S s;
- const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}}
- z = zz; // expected-note {{aliases the storage of s}}
+ const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}}
+ z = zz; // expected-note {{local temporary object aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)z; // expected-note {{later used here}}
}
@@ -1790,7 +1819,8 @@ void uaf() {
{
S str;
S* p = &str; // expected-warning {{local variable 'str' does not live long enough}}
- view = p->s;
+ view = p->s; // expected-note {{local variable 'p' aliases the storage of local variable 'str'}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
@@ -1815,8 +1845,9 @@ void uaf_union() {
std::string_view view;
{
U u = U{"hello"};
- U* up = &u; // expected-warning {{local variable 'u' does not live long enough}}
- view = up->s;
+ U* up = &u; // expected-warning {{local variable 'u' does not live long enough}}
+ view = up->s; // expected-note {{local variable 'up' aliases the storage of local variable 'u'}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'u'}}
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
@@ -1833,7 +1864,7 @@ void uaf_anonymous_union() {
{
AnonymousUnion au;
AnonymousUnion* up = &au; // expected-warning {{local variable 'au' does not live long enough}}
- ip = &up->x;
+ ip = &up->x; // expected-note {{local variable 'up' aliases the storage of local variable 'au'}}
} // expected-note {{destroyed here}}
(void)ip; // expected-note {{later used here}}
}
@@ -1937,7 +1968,8 @@ void test_optional_arrow_lifetimebound() {
View v;
{
std::optional<MyObj> opt;
- v = opt->getView(); // expected-warning {{local variable 'opt' does not live long enough}}
+ v = opt->getView(); // expected-warning {{local variable 'opt' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'opt'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -2356,7 +2388,8 @@ void from_lifetimebound_this_method() {
S value;
{
Factory f;
- value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}}
+ value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'f'}}
} // expected-note {{destroyed here}}
use(value); // expected-note {{later used here}}
}
@@ -2365,7 +2398,8 @@ void across_scope() {
S s{};
{
std::string str{"abc"};
- s = getS(str); // expected-warning {{local variable 'str' does not live long enough}}
+ s = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(s); // expected-note {{later used here}}
}
@@ -2387,8 +2421,9 @@ void assignment_propagation() {
S a, b;
{
std::string str{"abc"};
- a = getS(str); // expected-warning {{local variable 'str' does not live long enough}}
- b = a;
+ a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'str'}}
+ b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(b); // expected-note {{later used here}}
}
@@ -2398,7 +2433,7 @@ void chained_defaulted_assignment_propagation() {
{
std::string str{"abc"};
S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}}
- c = b = a;
+ c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(c); // expected-note {{later used here}}
}
@@ -2596,7 +2631,8 @@ void owner_outlives_lifetimebound_source() {
std::unique_ptr<S> ups;
{
std::string local;
- ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}}
+ ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)ups; // expected-note {{later used here}}
}
@@ -2619,7 +2655,8 @@ void local_pointer() {
Pointer<int> p;
{
int v;
- p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}}
+ p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'v'}}
} // expected-note {{destroyed here}}
use(*p); // expected-note {{later used here}}
}
@@ -2630,9 +2667,12 @@ void nested_local_pointer() {
Pointer<Bar> p;
{
Bar v;
- p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}}
- pp = Pointer(p);
- ppp = Pointer(pp);
+ p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'v'}}
+ pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v'}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'v'}}
+ ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'v'}}
} // expected-note {{destroyed here}}
use(***ppp); // expected-note {{later used here}}
}
@@ -2759,7 +2799,7 @@ void new_pointer_from_pointer() {
{
MyObj obj;
MyObj *q = &obj; // expected-warning {{local variable 'obj' does not live long enough}}
- p = new MyObj *(q);
+ p = new MyObj *(q); // expected-note {{local variable 'q' aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -3316,7 +3356,8 @@ void uaf_via_lifetimebound() {
std::function<void()> f = []() {};
{
int local;
- f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}}
+ f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}} \
+ // expected-note {{local temporary object aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)f; // expected-note {{later used here}}
}
>From 5eb2afc3c56744404aed2f6fb4ba861d5654fec1 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Mon, 1 Jun 2026 16:50:19 +0800
Subject: [PATCH 4/4] remove extractExpr and fix some naming
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 8 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 9 +-
clang/lib/Sema/SemaLifetimeSafety.h | 59 ++--
.../Sema/warn-lifetime-analysis-nocfg.cpp | 90 ++++--
.../Sema/warn-lifetime-safety-cfg-bailout.cpp | 3 +-
.../Sema/warn-lifetime-safety-suggestions.cpp | 10 +-
clang/test/Sema/warn-lifetime-safety.cpp | 287 +++++++++++-------
7 files changed, 277 insertions(+), 189 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 78f39f4609358..29e51946e31f0 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -61,10 +61,10 @@ class LifetimeSafetySemaHelper {
LifetimeSafetySemaHelper() = default;
virtual ~LifetimeSafetySemaHelper() = default;
- virtual void
- reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
- const Expr *MovedExpr, SourceLocation FreeLoc,
- llvm::ArrayRef<const Expr *> OriginExprChain) {}
+ virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
+ const Expr *MovedExpr,
+ SourceLocation FreeLoc,
+ llvm::ArrayRef<const Expr *> ExprChain) {}
virtual void reportUseAfterReturn(const Expr *IssueExpr,
const Expr *ReturnExpr,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 2f5db5bd109c8..4b60aacbd19fd 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -274,11 +274,10 @@ class LifetimeChecker {
break;
}
- const llvm::SmallVector<const Expr *> OriginExprChain =
- buildExprOrDeclChain(OriginFlowChain);
+ const llvm::SmallVector<const Expr *> ExprChain =
+ getExprChain(OriginFlowChain);
SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
- MovedExpr, ExpiryLoc,
- OriginExprChain);
+ MovedExpr, ExpiryLoc, ExprChain);
}
} else if (const auto *OEF =
@@ -511,7 +510,7 @@ class LifetimeChecker {
/// extracts the corresponding expressions for each origin. Origins that refer
/// to declarations (rather than expressions) are skipped.
llvm::SmallVector<const Expr *>
- buildExprOrDeclChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
+ getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
llvm::SmallVector<const Expr *> rs;
for (const OriginID CurrOID : OriginFlowChain)
if (const Expr *CurrExpr =
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 0fe439b3a8569..fe584a5328134 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -60,23 +60,21 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
public:
LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
- void
- reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
- const Expr *MovedExpr, SourceLocation FreeLoc,
- llvm::ArrayRef<const Expr *> OriginExprChain) override {
+ void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
+ const Expr *MovedExpr, SourceLocation FreeLoc,
+ llvm::ArrayRef<const Expr *> ExprChain) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_use_after_scope_moved
: diag::warn_lifetime_safety_use_after_scope;
S.Diag(IssueExpr->getExprLoc(), DiagID)
<< getDiagSubjectDescription(IssueExpr) << IssueExpr->getSourceRange();
-
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
- reportAliasingChain(OriginExprChain);
+ reportAliasingChain(ExprChain);
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
@@ -476,60 +474,45 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
}
std::string getDiagSubjectDescription(const Expr *E) {
- if (isa<MaterializeTemporaryExpr>(E))
- return "local temporary object";
-
- if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
- return getDiagSubjectDescription(DRE->getDecl());
- // TODO: Handle other expression types.
- return "";
- }
-
- const Expr *extractExpr(const Expr *E) {
// FIXME: Ideally, this should use IgnoreParenImpCasts().
// However, according to the comment on IgnoreParenImpCasts(),
// it is not fully equivalent to IgnoreImpCasts() + IgnoreParens().
// Once the FIXME in IgnoreParenImpCasts() is resolved,
// this can be switched to use IgnoreParenImpCasts().
- const Expr *PureExpr = E->IgnoreImpCasts()->IgnoreParens();
-
- if (const auto *UO = dyn_cast<UnaryOperator>(PureExpr))
- return UO->getSubExpr();
- // For a BinaryOperator, there is only one relevant case: assignment
- // chains. Therefore, we only need to consider the LHS expression.
- if (const auto *BO = dyn_cast<BinaryOperator>(PureExpr))
- return BO->getLHS();
+ E = E->IgnoreImpCasts()->IgnoreParens();
+ if (isa<MaterializeTemporaryExpr>(E))
+ return "local temporary object";
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+ return getDiagSubjectDescription(DRE->getDecl());
// TODO: Handle other expression types.
- return PureExpr;
+ return "expression";
}
void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
std::string IssueStr;
+ const Expr *IssueExpr = nullptr;
const Expr *LastExpr = nullptr;
+
for (const Expr *CurrExpr : reverse(OriginExprChain)) {
- if (IssueStr.empty()) {
+ if (!IssueExpr) {
IssueStr = getDiagSubjectDescription(CurrExpr);
+ IssueExpr = CurrExpr;
LastExpr = CurrExpr;
continue;
}
- const Expr *ExtractedExpr = extractExpr(CurrExpr);
- if (LastExpr &&
- ExtractedExpr->getSourceRange() == LastExpr->getSourceRange())
+ if (CurrExpr->getSourceRange() == LastExpr->getSourceRange())
continue;
-
- // FIXME: Because getDiagSubjectDescription and extractExpr is not fully
- // implemented yet, some diagnostic that should have been issued are
- // currently being skipped here.
- std::string ExprName = getDiagSubjectDescription(ExtractedExpr);
- if (ExprName.empty())
+ if (isa<MaterializeTemporaryExpr>(CurrExpr) &&
+ isa<MaterializeTemporaryExpr>(IssueExpr))
continue;
- S.Diag(ExtractedExpr->getBeginLoc(),
+ S.Diag(CurrExpr->getBeginLoc(),
diag::note_lifetime_safety_aliases_storage)
- << ExtractedExpr->getSourceRange() << ExprName << IssueStr;
- LastExpr = ExtractedExpr;
+ << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
+ << IssueStr;
+ LastExpr = CurrExpr;
}
}
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 61e162f01947f..d224e5226541d 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -76,6 +76,7 @@ struct Y {
void dangligGslPtrFromTemporary() {
MyIntPointer p = Y{}.a; // cfg-warning {{local temporary object does not live long enough}} \
+ // cfg-note {{expression aliases the storage of local temporary object}} \
// cfg-note {{destroyed here}}
(void)p; // cfg-note {{later used here}}
}
@@ -193,7 +194,8 @@ struct Unannotated {
void modelIterators() {
std::vector<int>::iterator it = std::vector<int>().begin(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
(void)it; // cfg-note {{later used here}}
}
@@ -241,12 +243,13 @@ int &danglingRawPtrFromLocal3() {
// GH100384
std::string_view containerWithAnnotatedElements() {
std::string_view c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(c1); // cfg-note {{later used here}}
c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}} \
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(c1); // cfg-note {{later used here}}
// no warning on constructing from gsl-pointer
@@ -307,23 +310,29 @@ std::string_view danglingRefToOptionalFromTemp4() {
void danglingReferenceFromTempOwner() {
int &&r = *std::optional<int>(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
// https://github.com/llvm/llvm-project/issues/175893
int &&r2 = *std::optional<int>(5); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
// https://github.com/llvm/llvm-project/issues/175893
int &&r3 = std::optional<int>(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
const int &r4 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
int &&r5 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(r, r2, r3, r4, r5); // cfg-note 5 {{later used here}}
std::string_view sv = *getTempOptStr(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(sv); // cfg-note {{later used here}}
}
@@ -334,7 +343,8 @@ void testLoops() {
for (auto i : getTempVec()) // ok
;
for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
;
}
@@ -603,6 +613,7 @@ std::string StrCat(std::string_view, std::string_view);
void test1() {
UrlAnalyzed url(StrCat("abc", "bcd")); // expected-warning {{object backing the pointer will be destroyed}} \
// cfg-warning {{local temporary object does not live long enough}} \
+ // cfg-note {{expression aliases the storage of local temporary object}} \
// cfg-note {{destroyed here}}
use(url); // cfg-note {{later used here}}
}
@@ -611,7 +622,8 @@ std::string_view ReturnStringView(std::string_view abc [[clang::lifetimebound]])
void test() {
std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(svjkk1); // cfg-note {{later used here}}
}
} // namespace GH100549
@@ -845,7 +857,8 @@ namespace GH118064{
void test() {
auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(y); // cfg-note {{later used here}}
}
} // namespace GH118064
@@ -860,11 +873,12 @@ std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
std::string_view test1_1() {
std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t1); // cfg-note {{later used here}}
t1 = Ref(std::string()); // expected-warning {{object backing}} \
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t1); // cfg-note {{later used here}}
return Ref(std::string()); // expected-warning {{returning address}} \
// cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}}
@@ -872,11 +886,12 @@ std::string_view test1_1() {
std::string_view test1_2() {
std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t2); // cfg-note {{later used here}}
t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t2); // cfg-note {{later used here}}
return TakeSv(std::string()); // expected-warning {{returning address}} \
@@ -885,11 +900,12 @@ std::string_view test1_2() {
std::string_view test1_3() {
std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t3); // cfg-note {{later used here}}
t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t3); // cfg-note {{later used here}}
return TakeStrRef(std::string()); // expected-warning {{returning address}} \
// cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}}
@@ -911,11 +927,12 @@ struct Foo {
};
std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t1); // cfg-note {{later used here}}
t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
// cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(t1); // cfg-note {{later used here}}
return r1.get(); // expected-warning {{address of stack}} \
// cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}}
@@ -944,9 +961,11 @@ struct [[gsl::Pointer]] Pointer {
Pointer(const Bar & bar [[clang::lifetimebound]]);
};
Pointer test3(Bar bar) {
- Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(p); // cfg-note {{later used here}}
- p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
+ p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
use(p); // cfg-note {{later used here}}
return bar; // expected-warning {{address of stack}} cfg-warning {{stack memory associated with parameter 'bar' is returned}} cfg-note {{returned here}}
}
@@ -1034,11 +1053,11 @@ void operator_star_arrow_reference() {
auto temporary = []() { return std::vector<std::string>{{"1"}}; };
const char* x = temporary().begin()->data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note 3 {{expression aliases the storage of local temporary object}}
const char* y = (*temporary().begin()).data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note 4 {{expression aliases the storage of local temporary object}}
const std::string& z = (*temporary().begin()); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
- // cfg-note {{local temporary object aliases the storage of local temporary object}}
+ // cfg-note 3 {{expression aliases the storage of local temporary object}}
use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
}
@@ -1050,9 +1069,12 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
const std::string& r = (*v.begin()).second;
auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, "1"}}; };
- const char* x = temporary().begin()->second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
- const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
- const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{local temporary object aliases the storage of local temporary object}}
+ const char* x = temporary().begin()->second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note 4 {{expression aliases the storage of local temporary object}}
+ const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note 4 {{expression aliases the storage of local temporary object}}
+ const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note 3 {{expression aliases the storage of local temporary object}}
use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
}
@@ -1102,17 +1124,21 @@ std::string_view foo(std::string_view sv [[clang::lifetimebound]]);
void test1() {
std::string_view k1 = S().sv; // OK
std::string_view k2 = S().s; // expected-warning {{object backing the pointer will}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note {{expression aliases the storage of local temporary object}}
std::string_view k3 = Q().get()->sv; // OK
std::string_view k4 = Q().get()->s; // expected-warning {{object backing the pointer will}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note 2 {{expression aliases the storage of local temporary object}}
std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note 2 {{expression aliases the storage of local temporary object}}
std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \
- // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}}
+ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} \
+ // cfg-note 3 {{expression aliases the storage of local temporary object}}
use(k1, k2, k3, k4, lb1, lb2); // cfg-note 4 {{later used here}}
}
diff --git a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
index 6f2c06fdf6a72..d9161d950abdc 100644
--- a/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-cfg-bailout.cpp
@@ -27,7 +27,8 @@ void single_block_cfg() {
MyObj* p;
{
MyObj s;
- p = &s; // bailout-warning {{local variable 's' does not live long enough}}
+ p = &s; // bailout-warning {{local variable 's' does not live long enough}} \
+ // bailout-note {{expression aliases the storage of local variable 's'}}
} // bailout-note {{destroyed here}}
(void)*p; // bailout-note {{later used here}}
}
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 85b80a7af811b..750c26876efcc 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -278,18 +278,21 @@ View return_view_field(const ViewProvider& v) { // expected-warning {{paramet
void test_get_on_temporary_pointer() {
const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{local temporary object does not live long enough}}.
// expected-note at -1 {{destroyed here}}
+ // expected-note at -2 2 {{expression aliases the storage of local temporary object}}
(void)s_ref; // expected-note {{later used here}}
}
void test_get_on_temporary_ref() {
const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning {{local temporary object does not live long enough}}.
// expected-note at -1 {{destroyed here}}
+ // expected-note at -2 {{expression aliases the storage of local temporary object}}
(void)s_ref; // expected-note {{later used here}}
}
void test_getView_on_temporary() {
View sv = ViewProvider{1}.getView(); // expected-warning {{local temporary object does not live long enough}}.
// expected-note at -1 {{destroyed here}}
+ // expected-note at -2 {{expression aliases the storage of local temporary object}}
(void)sv; // expected-note {{later used here}}
}
@@ -600,7 +603,7 @@ void uaf_via_inferred_lifetimebound() {
{
int local;
f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'local'}}
+ // expected-note {{expression aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)f; // expected-note {{later used here}}
}
@@ -624,7 +627,7 @@ void test_inference() {
{
MyObj obj;
ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
@@ -638,7 +641,8 @@ View* MakeView(const MyObj& in) { // expected-warning {{parameter in intra-TU fu
void test_new_allocation() {
View* v = MakeView(MyObj{}); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
(void)v; // expected-note {{later used here}}
}
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index d9fb06154c67e..ebbff07733bd3 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -54,7 +54,8 @@ void simple_case() {
MyObj* p;
{
MyObj s;
- p = &s; // expected-warning {{local variable 's' does not live long enough}}
+ p = &s; // expected-warning {{local variable 's' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -91,7 +92,8 @@ void pointer_chain() {
MyObj* q;
{
MyObj s;
- p = &s; // expected-warning {{does not live long enough}}
+ p = &s; // expected-warning {{does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 's'}}
q = p; // expected-note {{local variable 'p' aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*q; // expected-note {{later used here}}
@@ -111,7 +113,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 {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
// No second warning for the same loan.
@@ -124,9 +127,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 {{expression aliases the storage of local variable 's'}}
+ q = &s; // expected-warning {{does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 's'}}
+ r = &s; // expected-warning {{does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 's'}}
} // expected-note 3 {{destroyed here}}
(void)*p; // expected-note {{later used here}}
(void)*q; // expected-note {{later used here}}
@@ -139,7 +145,7 @@ void multiple_pointers_chained() {
MyObj s;
MyObj* obj1, *obj2;
p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}} \
- // expected-note 2 {{aliases the storage of local variable 's'}}
+ // expected-note 3 {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -459,7 +465,8 @@ void trivial_int_uaf() {
int * a;
{
int b = 1;
- a = &b; // expected-warning {{local variable 'b' does not live long enough}}
+ a = &b; // expected-warning {{local variable 'b' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'b'}}
} // expected-note {{destroyed here}}
(void)*a; // expected-note {{later used here}}
}
@@ -468,7 +475,8 @@ void trivial_class_uaf() {
TriviallyDestructedClass* ptr;
{
TriviallyDestructedClass s;
- ptr = &s; // expected-warning {{local variable 's' does not live long enough}}
+ ptr = &s; // expected-warning {{local variable 's' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
@@ -660,7 +668,8 @@ void test_view_pointer() {
View* vp;
{
View v;
- vp = &v; // expected-warning {{local variable 'v' does not live long enough}}
+ vp = &v; // expected-warning {{local variable 'v' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'v'}}
} // expected-note {{destroyed here}}
vp->use(); // expected-note {{later used here}}
}
@@ -669,7 +678,8 @@ void test_view_double_pointer() {
View** vpp;
{
View* vp = nullptr;
- vpp = &vp; // expected-warning {{local variable 'vp' does not live long enough}}
+ vpp = &vp; // expected-warning {{local variable 'vp' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'vp'}}
} // expected-note {{destroyed here}}
(**vpp).use(); // expected-note {{later used here}}
}
@@ -696,8 +706,10 @@ void test_lifetimebound_multi_level() {
{
int* p = nullptr;
int** pp = &p;
- int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live long enough}}
- result = return_inner_ptr_addr(ppp); // expected-note {{local variable 'ppp' aliases the storage of local variable 'pp'}}
+ int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'pp'}}
+ result = return_inner_ptr_addr(ppp); // expected-note {{local variable 'ppp' aliases the storage of local variable 'pp'}} \
+ // expected-note {{expression aliases the storage of local variable 'pp'}}
} // expected-note {{destroyed here}}
(void)**result; // expected-note {{used here}}
}
@@ -730,7 +742,8 @@ MyObj* uaf_before_uar() {
MyObj* p;
{
MyObj local_obj;
- p = &local_obj; // expected-warning {{local variable 'local_obj' does not live long enough}}
+ p = &local_obj; // expected-warning {{local variable 'local_obj' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'local_obj'}}
} // expected-note {{destroyed here}}
return p; // expected-note {{later used here}}
}
@@ -821,7 +834,7 @@ void lifetimebound_simple_function() {
{
MyObj obj;
v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -830,8 +843,8 @@ void lifetimebound_multiple_args_definite() {
View v;
{
MyObj obj1, obj2;
- v = Choose(true, // expected-note {{local temporary object aliases the storage of local variable 'obj1'}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj2'}}
+ v = Choose(true, // expected-note {{expression aliases the storage of local variable 'obj1'}} \
+ // expected-note {{expression aliases the storage of local variable 'obj2'}}
obj1, // expected-warning {{local variable 'obj1' does not live long enough}}
obj2); // expected-warning {{local variable 'obj2' does not live long enough}}
} // expected-note 2 {{destroyed here}}
@@ -859,7 +872,7 @@ void lifetimebound_mixed_args() {
{
MyObj obj1, obj2;
v = SelectFirst(obj1, // expected-warning {{local variable 'obj1' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj1'}}
+ // expected-note {{expression aliases the storage of local variable 'obj1'}}
obj2);
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
@@ -876,7 +889,7 @@ void lifetimebound_member_function() {
{
MyObj obj;
v = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -901,7 +914,7 @@ void lifetimebound_chained_calls() {
{
MyObj obj;
v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note 3 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -910,7 +923,8 @@ void lifetimebound_with_pointers() {
MyObj* ptr;
{
MyObj obj;
- ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
@@ -920,7 +934,7 @@ void chained_assignment_lifetimebound_call() {
{
MyObj s;
p = Identity(obj = &s); // expected-warning {{does not live long enough}} \
- // expected-note {{local variable 'obj' aliases the storage of local variable 's'}}
+ // expected-note 3 {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -952,8 +966,9 @@ void lifetimebound_return_reference() {
{
MyObj obj;
View temp_v = obj; // expected-warning {{local variable 'obj' does not live long enough}}
- const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}}
- ptr = &ref; // expected-note {{local variable 'ref' aliases the storage of local variable 'obj'}}
+ const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}} \
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
+ ptr = &ref; // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
@@ -980,7 +995,7 @@ void lifetimebound_ctor_functional_cast() {
{
MyObj obj;
v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -990,7 +1005,8 @@ void lifetimebound_ctor_c_style_cast() {
{
MyObj obj;
v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note {{local variable 'obj' aliases the storage of local variable 'obj'}} \
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -1000,7 +1016,7 @@ void lifetimebound_ctor_static_cast() {
{
MyObj obj;
v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -1010,7 +1026,7 @@ void lifetimebound_make_unique() {
{
MyObj obj;
ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}} \
- // tu-note {{local temporary object aliases the storage of local variable 'obj'}}
+ // tu-note {{expression aliases the storage of local variable 'obj'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1027,7 +1043,8 @@ void non_lifetimebound_make_unique() {
void lifetimebound_make_unique_temp() {
std::unique_ptr<LifetimeBoundCtor> ptr = std::make_unique<LifetimeBoundCtor>(MyObj()); // tu-warning {{local temporary object does not live long enough}} \
- // tu-note {{destroyed here}}
+ // tu-note {{destroyed here}} \
+ // tu-note {{expression aliases the storage of local temporary object}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1065,7 +1082,7 @@ void lifetimebound_make_unique_multi_params() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}} \
- // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
+ // tu-note {{expression aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1076,7 +1093,7 @@ void lifetimebound_make_unique_multi_params2() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}} \
- // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
+ // tu-note {{expression aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1097,7 +1114,7 @@ void lifetimebound_make_unique_multi_params3_1() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \
- // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
+ // tu-note {{expression aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1108,7 +1125,7 @@ void lifetimebound_make_unique_multi_params3_2() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \
- // tu-note {{local temporary object aliases the storage of local variable 'obj_short'}}
+ // tu-note {{expression aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1254,7 +1271,8 @@ void simpleparen() {
MyObj* p;
{
MyObj a;
- MyObj* b = &a; // expected-warning {{local variable 'a' does not live long enough}}
+ MyObj* b = &a; // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'a'}}
p = (((b))); // expected-note {{local variable 'b' aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
@@ -1264,13 +1282,16 @@ void parentheses(bool cond) {
MyObj* p;
{
MyObj a;
- p = &((((a)))); // expected-warning {{local variable 'a' does not live long enough}}
+ p = &((((a)))); // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
{
MyObj a;
- p = ((GetPointer((a)))); // expected-warning {{local variable 'a' does not live long enough}}
+ p = ((GetPointer((a)))); // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{local variable 'a' aliases the storage of local variable 'a'}} \
+ // expected-note {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
@@ -1301,7 +1322,8 @@ void use_temporary_after_destruction() {
void passing_temporary_to_lifetime_bound_function() {
View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{local temporary object does not live long enough}} \
- expected-note {{destroyed here}}
+ expected-note {{destroyed here}} \
+ expected-note {{expression aliases the storage of local temporary object}}
use(a); // expected-note {{later used here}}
}
@@ -1350,7 +1372,7 @@ void foobar() {
{
StatusOr<MyObj> string_or = getStringOr();
view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'string_or'}}
+ // expected-note {{expression aliases the storage of local variable 'string_or'}}
value();
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
@@ -1424,7 +1446,8 @@ void test_user_defined_deref_uaf() {
{
MyObj obj;
SmartPtr<MyObj> smart_ptr(&obj);
- p = &(*smart_ptr); // expected-warning {{local variable 'smart_ptr' does not live long enough}}
+ p = &(*smart_ptr); // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1442,7 +1465,7 @@ void test_user_defined_deref_with_view() {
MyObj obj;
SmartPtr<MyObj> smart_ptr(&obj);
v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'smart_ptr'}}
+ // expected-note {{expression aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1452,7 +1475,8 @@ void test_user_defined_deref_arrow() {
{
MyObj obj;
SmartPtr<MyObj> smart_ptr(&obj);
- p = smart_ptr.operator->(); // expected-warning {{local variable 'smart_ptr' does not live long enough}}
+ p = smart_ptr.operator->(); // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1462,7 +1486,8 @@ void test_user_defined_deref_chained() {
{
MyObj obj;
SmartPtr<SmartPtr<MyObj>> double_ptr;
- p = &(**double_ptr); // expected-warning {{local variable 'double_ptr' does not live long enough}}
+ p = &(**double_ptr); // expected-warning {{local variable 'double_ptr' does not live long enough}} \
+ // expected-note 3 {{expression aliases the storage of local variable 'double_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1649,13 +1674,14 @@ void wrong_use_of_move_is_permissive() {
{
MyObj a;
v = std::move(a); // expected-warning {{local variable 'a' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'a'}}
+ // expected-note {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
const int* p;
{
MyObj a;
- p = std::move(a).getData(); // expected-warning {{local variable 'a' does not live long enough}}
+ p = std::move(a).getData(); // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)p; // expected-note {{later used here}}
}
@@ -1666,7 +1692,8 @@ void test_release_no_uaf() {
// Calling release() marks p as moved from, so its destruction doesn't invalidate r.
{
std::unique_ptr<int> p;
- r = p.get(); // expected-warning {{local variable 'p' may not live long enough. This could be a false positive as the storage may have been moved later}}
+ r = p.get(); // expected-warning {{local variable 'p' may not live long enough. This could be a false positive as the storage may have been moved later}} \
+ // expected-note {{expression aliases the storage of local variable 'p'}}
take(p.release()); // expected-note {{potentially moved here}}
} // expected-note {{destroyed here}}
(void)*r; // expected-note {{later used here}}
@@ -1689,9 +1716,10 @@ void bar() {
{
S s;
x = s.x(); // expected-warning {{local variable 's' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 's'}}
+ // expected-note {{expression aliases the storage of local variable 's'}}
View y = S().x(); // expected-warning {{local temporary object does not live long enough}} \
- expected-note {{destroyed here}}
+ expected-note {{destroyed here}} \
+ expected-note {{expression aliases the storage of local temporary object}}
(void)y; // expected-note {{used here}}
} // expected-note {{destroyed here}}
(void)x; // expected-note {{used here}}
@@ -1779,17 +1807,20 @@ const std::string& identity(const std::string& in [[clang::lifetimebound]]);
const S& identity(const S& in [[clang::lifetimebound]]);
void test_temporary() {
- const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
+ const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
(void)x; // expected-note {{later used here}}
- const std::string& y = identity(S().x()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
+ const std::string& y = identity(S().x()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \
+ // expected-note 2 {{expression aliases the storage of local temporary object}}
(void)y; // expected-note {{later used here}}
std::string_view z;
{
S s;
- const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}}
- z = zz; // expected-note {{local temporary object aliases the storage of local variable 's'}}
+ const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 's'}}
+ z = zz; // expected-note {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)z; // expected-note {{later used here}}
}
@@ -1797,12 +1828,14 @@ void test_temporary() {
void test_lifetime_extension_ok() {
const S& x = S();
(void)x;
- const S& y = identity(S()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
+ const S& y = identity(S()); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
(void)y; // expected-note {{later used here}}
}
const std::string& test_return() {
- const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
+ const std::string& x = S().x(); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
return x; // expected-note {{later used here}}
}
} // namespace reference_type_decl_ref_expr
@@ -1818,9 +1851,10 @@ void uaf() {
std::string_view view;
{
S str;
- S* p = &str; // expected-warning {{local variable 'str' does not live long enough}}
+ S* p = &str; // expected-warning {{local variable 'str' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'str'}}
view = p->s; // expected-note {{local variable 'p' aliases the storage of local variable 'str'}} \
- // expected-note {{local temporary object aliases the storage of local variable 'str'}}
+ // expected-note {{expression aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
@@ -1845,9 +1879,10 @@ void uaf_union() {
std::string_view view;
{
U u = U{"hello"};
- U* up = &u; // expected-warning {{local variable 'u' does not live long enough}}
+ U* up = &u; // expected-warning {{local variable 'u' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'u'}}
view = up->s; // expected-note {{local variable 'up' aliases the storage of local variable 'u'}} \
- // expected-note {{local temporary object aliases the storage of local variable 'u'}}
+ // expected-note {{expression aliases the storage of local variable 'u'}}
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
@@ -1863,8 +1898,10 @@ void uaf_anonymous_union() {
int* ip;
{
AnonymousUnion au;
- AnonymousUnion* up = &au; // expected-warning {{local variable 'au' does not live long enough}}
- ip = &up->x; // expected-note {{local variable 'up' aliases the storage of local variable 'au'}}
+ AnonymousUnion* up = &au; // expected-warning {{local variable 'au' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'au'}}
+ ip = &up->x; // expected-note {{local variable 'up' aliases the storage of local variable 'au'}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'au'}}
} // expected-note {{destroyed here}}
(void)ip; // expected-note {{later used here}}
}
@@ -1922,9 +1959,12 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) {
void test() {
MemberFuncsTpl<MyObj> mtf;
- const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}}
- const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{local temporary object does not live long enough}} // tu-note {{destroyed here}}
- const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}}
+ const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
+ const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{local temporary object does not live long enough}} // tu-note {{destroyed here}} \
+ // tu-note {{expression aliases the storage of local temporary object}}
+ const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{local temporary object does not live long enough}} // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
(void)pTMA; // expected-note {{later used here}}
(void)pTMB; // tu-note {{later used here}}
(void)pTMC; // expected-note {{later used here}}
@@ -1959,7 +1999,8 @@ void test_optional_arrow() {
const char* p;
{
std::optional<std::string> opt;
- p = opt->data(); // expected-warning {{local variable 'opt' does not live long enough}}
+ p = opt->data(); // expected-warning {{local variable 'opt' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'opt'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1969,7 +2010,7 @@ void test_optional_arrow_lifetimebound() {
{
std::optional<MyObj> opt;
v = opt->getView(); // expected-warning {{local variable 'opt' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'opt'}}
+ // expected-note 2 {{expression aliases the storage of local variable 'opt'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1978,7 +2019,8 @@ void test_unique_ptr_arrow() {
const char* p;
{
std::unique_ptr<std::string> up;
- p = up->data(); // expected-warning {{local variable 'up' does not live long enough}}
+ p = up->data(); // expected-warning {{local variable 'up' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'up'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2194,7 +2236,8 @@ void element_use_after_scope() {
int* p;
{
int a[10]{};
- p = &a[2]; // expected-warning {{local variable 'a' does not live long enough}}
+ p = &a[2]; // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2226,7 +2269,8 @@ void multidimensional_use_after_scope() {
int* p;
{
int a[3][4]{};
- p = &a[1][2]; // expected-warning {{local variable 'a' does not live long enough}}
+ p = &a[1][2]; // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note 3 {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2239,7 +2283,8 @@ void member_array_element_use_after_scope() {
int* p;
{
S s;
- p = &s.arr[0]; // expected-warning {{local variable 's' does not live long enough}}
+ p = &s.arr[0]; // expected-warning {{local variable 's' does not live long enough}} \
+ // expected-note 3 {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2257,7 +2302,8 @@ void reversed_subscript_use_after_scope() {
int* p;
{
int a[10]{};
- p = &(0[a]); // expected-warning {{local variable 'a' does not live long enough}}
+ p = &(0[a]); // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2283,9 +2329,12 @@ void pointer_arithmetic_use_after_scope() {
int* p3;
{
int a[10]{};
- p = a + 5; // expected-warning {{local variable 'a' does not live long enough}}
- p2 = a - 5; // expected-warning {{local variable 'a' does not live long enough}}
- p3 = 5 + a; // expected-warning {{local variable 'a' does not live long enough}}
+ p = a + 5; // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'a'}}
+ p2 = a - 5; // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'a'}}
+ p3 = 5 + a; // expected-warning {{local variable 'a' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'a'}}
} // expected-note 3 {{destroyed here}}
(void)*p; // expected-note {{later used here}}
(void)*p2; // expected-note {{later used here}}
@@ -2330,7 +2379,7 @@ struct S {
void indexing_with_static_operator() {
S()(1, 2);
- S& x = S()("1",
+ S& x = S()("1", // expected-note 2 {{expression aliases the storage of local temporary object}}
2, // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
3); // expected-warning {{local temporary object does not live long enough}} expected-note {{destroyed here}}
@@ -2355,13 +2404,15 @@ S getS(const std::string &s [[clang::lifetimebound]]);
void from_free_function() {
S s = getS(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
use(s); // expected-note {{later used here}}
}
void from_constructor() {
S s(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
use(s); // expected-note {{later used here}}
}
@@ -2374,13 +2425,15 @@ struct Factory {
void from_method() {
Factory f;
S s = f.make(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
use(s); // expected-note {{later used here}}
}
void from_static_method() {
S s = Factory::create(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
use(s); // expected-note {{later used here}}
}
@@ -2389,7 +2442,7 @@ void from_lifetimebound_this_method() {
{
Factory f;
value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'f'}}
+ // expected-note {{expression aliases the storage of local variable 'f'}}
} // expected-note {{destroyed here}}
use(value); // expected-note {{later used here}}
}
@@ -2399,7 +2452,7 @@ void across_scope() {
{
std::string str{"abc"};
s = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'str'}}
+ // expected-note {{expression aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(s); // expected-note {{later used here}}
}
@@ -2422,7 +2475,7 @@ void assignment_propagation() {
{
std::string str{"abc"};
a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'str'}}
+ // expected-note {{expression aliases the storage of local variable 'str'}}
b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(b); // expected-note {{later used here}}
@@ -2432,8 +2485,10 @@ void chained_defaulted_assignment_propagation() {
S b, c;
{
std::string str{"abc"};
- S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}}
- c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
+ S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'str'}}
+ c = b = a; // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}\
+ // expected-note {{expression aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(c); // expected-note {{later used here}}
}
@@ -2447,7 +2502,8 @@ void no_annotation() {
void mix_annotated_and_not() {
S s1 = getS(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
S s2 = getSNoAnnotation(std::string("temp"));
use(s1); // expected-note {{later used here}}
use(s2);
@@ -2459,6 +2515,7 @@ S multiple_lifetimebound_params() {
std::string str{"abc"};
S s = getS2(str, std::string("temp")); // expected-warning {{stack memory associated with local variable 'str' is returned}} \
// expected-warning {{local temporary object does not live long enough}} \
+ // expected-note {{expression aliases the storage of local temporary object}} \
// expected-note {{destroyed here}}
return s; // expected-note {{returned here}} \
// expected-note {{later used here}}
@@ -2478,7 +2535,8 @@ T make(const std::string &s [[clang::lifetimebound]]);
void from_template_instantiation() {
S s = make<S>(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
use(s); // expected-note {{later used here}}
}
@@ -2541,7 +2599,8 @@ SAlias getSAlias(const std::string &s [[clang::lifetimebound]]);
void from_typedef_return() {
SAlias s = getSAlias(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
use(s); // expected-note {{later used here}}
}
@@ -2615,7 +2674,8 @@ std::unique_ptr<S> getUniqueS(const std::string &s [[clang::lifetimebound]]);
void owner_return_unique_ptr_s() {
auto ptr = getUniqueS(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}}
+ // expected-note {{destroyed here}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
(void)ptr; // expected-note {{later used here}}
}
@@ -2632,7 +2692,7 @@ void owner_outlives_lifetimebound_source() {
{
std::string local;
ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'local'}}
+ // expected-note {{expression aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)ups; // expected-note {{later used here}}
}
@@ -2656,7 +2716,7 @@ void local_pointer() {
{
int v;
p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'v'}}
+ // expected-note {{expression aliases the storage of local variable 'v'}}
} // expected-note {{destroyed here}}
use(*p); // expected-note {{later used here}}
}
@@ -2668,11 +2728,11 @@ void nested_local_pointer() {
{
Bar v;
p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'v'}}
+ // expected-note {{expression aliases the storage of local variable 'v'}}
pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v'}} \
- // expected-note {{local temporary object aliases the storage of local variable 'v'}}
+ // expected-note {{expression aliases the storage of local variable 'v'}}
ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}} \
- // expected-note {{local temporary object aliases the storage of local variable 'v'}}
+ // expected-note {{expression aliases the storage of local variable 'v'}}
} // expected-note {{destroyed here}}
use(***ppp); // expected-note {{later used here}}
}
@@ -2753,7 +2813,8 @@ void new_view_from_dead_scope() {
View *p;
{
MyObj obj;
- p = new View(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ p = new View(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
p->use(); // expected-note {{later used here}}
}
@@ -2798,8 +2859,10 @@ void new_pointer_from_pointer() {
MyObj **p;
{
MyObj obj;
- MyObj *q = &obj; // expected-warning {{local variable 'obj' does not live long enough}}
- p = new MyObj *(q); // expected-note {{local variable 'q' aliases the storage of local variable 'obj'}}
+ MyObj *q = &obj; // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
+ p = new MyObj *(q); // expected-note {{local variable 'q' aliases the storage of local variable 'obj'}} \
+ // expected-note {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -2808,7 +2871,8 @@ void new_pointer_from_dead_object() {
MyObj **p;
{
MyObj obj;
- p = new MyObj *(&obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ p = new MyObj *(&obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -2822,7 +2886,8 @@ void new_multiview_from_mixed_scope() {
MultiView *p;
{
MyObj obj2;
- p = new MultiView(obj1, obj2); // expected-warning {{local variable 'obj2' does not live long enough}}
+ p = new MultiView(obj1, obj2); // expected-warning {{local variable 'obj2' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'obj2'}}
} // expected-note {{destroyed here}}
(void)p; // expected-note {{later used here}}
}
@@ -2973,7 +3038,8 @@ void placement_new_int_basic() {
int *p;
{
int storage;
- p = new (&storage) int; // expected-warning {{local variable 'storage' does not live long enough}}
+ p = new (&storage) int; // expected-warning {{local variable 'storage' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'storage'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2983,7 +3049,8 @@ void placement_new_view_from_dead_scope() {
View *p = &storage;
{
MyObj obj;
- p = new (&storage) View(obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ p = new (&storage) View(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
p->use(); // expected-note {{later used here}}
}
@@ -2993,7 +3060,8 @@ void placement_new_pointer_from_dead_object() {
MyObj **p = &slot;
{
MyObj obj;
- p = new (&slot) MyObj *(&obj); // expected-warning {{local variable 'obj' does not live long enough}}
+ p = new (&slot) MyObj *(&obj); // expected-warning {{local variable 'obj' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -3002,7 +3070,8 @@ void placement_new_array_basic() {
int *p;
{
int storage[2];
- p = new (&storage) int[2]; // expected-warning {{local variable 'storage' does not live long enough}}
+ p = new (&storage) int[2]; // expected-warning {{local variable 'storage' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'storage'}}
} // expected-note {{destroyed here}}
(void)p[0]; // expected-note {{later used here}}
}
@@ -3011,7 +3080,8 @@ void placement_new_array_braces() {
int *p;
{
int storage[2];
- p = new (&storage) int[2]{}; // expected-warning {{local variable 'storage' does not live long enough}}
+ p = new (&storage) int[2]{}; // expected-warning {{local variable 'storage' does not live long enough}} \
+ // expected-note 2 {{expression aliases the storage of local variable 'storage'}}
} // expected-note {{destroyed here}}
(void)p[0]; // expected-note {{later used here}}
}
@@ -3191,7 +3261,8 @@ struct S {
void foo() {
{
int num;
- this->p_ = # // expected-warning {{local variable 'num' does not live long enough}}
+ this->p_ = # // expected-warning {{local variable 'num' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'num'}}
} // expected-note {{destroyed here}}
bar(); // expected-note {{later used here}}
this->p_ = &GLOBAL_INT;
@@ -3231,7 +3302,8 @@ struct S2 : S {
void foo2() {
{
int num;
- this->p_ = # // expected-warning {{local variable 'num' does not live long enough}}
+ this->p_ = # // expected-warning {{local variable 'num' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'num'}}
} // expected-note {{destroyed here}}
bar(); // expected-note {{later used here}}
this->p_ = &GLOBAL_INT;
@@ -3239,7 +3311,8 @@ struct S2 : S {
void baz2() {
{
int num;
- this->p_ = # // expected-warning {{local variable 'num' does not live long enough}}
+ this->p_ = # // expected-warning {{local variable 'num' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'num'}}
} // expected-note {{destroyed here}}
bar2(); // expected-note {{later used here}}
this->p_ = nullptr;
@@ -3357,7 +3430,7 @@ void uaf_via_lifetimebound() {
{
int local;
f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}} \
- // expected-note {{local temporary object aliases the storage of local variable 'local'}}
+ // expected-note {{expression aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)f; // expected-note {{later used here}}
}
@@ -3420,7 +3493,9 @@ void deref_use_after_scope() {
const MyObj* p;
{
optional<MyObj> opt;
- p = &*opt; // expected-warning {{local variable 'opt' does not live long enough}}
+ p = &*opt; // expected-warning {{local variable 'opt' does not live long enough}} \
+ // expected-note {{expression aliases the storage of local variable 'opt'}} \
+ // expected-note {{expression aliases the storage of local variable 'opt'}}
} // expected-note {{destroyed here}}
(void)p->id; // expected-note {{later used here}}
}
More information about the cfe-commits
mailing list