[clang] [LifetimeSafety] Introduce buildOriginFlowChain for use-after-scope (PR #199345)
Yuan Suo via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 12 06:54:43 PDT 2026
https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/199345
>From ec4461c3e4417227b2a9ea57886a50f10c7a40cd 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/8] [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/LifetimeSafety/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 a3b575b7ee63a..9333af70ebd81 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11056,6 +11056,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 2a71b5eb9a934..31d3d4981388e 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -263,10 +263,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) {
@@ -490,6 +505,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/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index f6dab33f8476c..67dd2b1d4ca56 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/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}}
}
@@ -1774,8 +1774,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 b8cc5af2c9ed5d25e2567b84a3e365c75dba860d 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/8] 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 31d3d4981388e..ef973711901e5 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -506,40 +506,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 1eec8705748102875188987793d7bdc0743a625f 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/8] 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 +++++++-------
.../LifetimeSafety/annotation-suggestions.cpp | 6 +-
clang/test/Sema/LifetimeSafety/nocfg.cpp | 32 +++--
clang/test/Sema/LifetimeSafety/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 9333af70ebd81..756c11c4a39d1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11056,7 +11056,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 ef973711901e5..4e160c7204b0e 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -271,9 +271,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/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index f5a3cf89e4c8d..85b80a7af811b 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-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/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index 41b07771c52c1..61e162f01947f 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/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/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 67dd2b1d4ca56..6e4c9d187dd0c 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/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;
{
@@ -1339,7 +1364,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}}
@@ -1430,7 +1456,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}}
}
@@ -1636,7 +1663,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;
@@ -1675,7 +1703,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}}
@@ -1774,8 +1803,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}}
}
@@ -1805,7 +1834,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}}
}
@@ -1830,8 +1860,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}}
}
@@ -1848,7 +1879,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}}
}
@@ -1952,7 +1983,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}}
}
@@ -2371,7 +2403,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}}
}
@@ -2380,7 +2413,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}}
}
@@ -2402,8 +2436,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}}
}
@@ -2413,7 +2448,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}}
}
@@ -2611,7 +2646,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}}
}
@@ -2634,7 +2670,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}}
}
@@ -2645,9 +2682,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}}
}
@@ -2792,7 +2832,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}}
}
@@ -3387,7 +3427,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 6380bbd48a2be810e8acbaaf574a3983db9547ef 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/8] 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 ++--
.../LifetimeSafety/annotation-suggestions.cpp | 10 +-
.../test/Sema/LifetimeSafety/cfg-bailout.cpp | 3 +-
clang/test/Sema/LifetimeSafety/nocfg.cpp | 90 ++++--
clang/test/Sema/LifetimeSafety/safety.cpp | 285 +++++++++++-------
7 files changed, 276 insertions(+), 188 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 4e160c7204b0e..d8746d1f6a6c4 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -277,11 +277,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 =
@@ -514,7 +513,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/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 85b80a7af811b..750c26876efcc 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-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/LifetimeSafety/cfg-bailout.cpp b/clang/test/Sema/LifetimeSafety/cfg-bailout.cpp
index 6f2c06fdf6a72..d9161d950abdc 100644
--- a/clang/test/Sema/LifetimeSafety/cfg-bailout.cpp
+++ b/clang/test/Sema/LifetimeSafety/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/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index 61e162f01947f..d224e5226541d 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/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/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 6e4c9d187dd0c..ac9f19a22c278 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/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}}
}
@@ -1365,7 +1387,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}}
@@ -1439,7 +1461,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}}
}
@@ -1457,7 +1480,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}}
}
@@ -1467,7 +1490,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}}
}
@@ -1477,7 +1501,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}}
}
@@ -1664,13 +1689,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}}
}
@@ -1681,7 +1707,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}}
@@ -1704,9 +1731,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}}
@@ -1794,17 +1822,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}}
}
@@ -1812,12 +1843,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
@@ -1833,9 +1866,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}}
}
@@ -1860,9 +1894,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}}
}
@@ -1878,8 +1913,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}}
}
@@ -1937,9 +1974,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}}
@@ -1974,7 +2014,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}}
}
@@ -1984,7 +2025,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}}
}
@@ -1993,7 +2034,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}}
}
@@ -2209,7 +2251,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}}
}
@@ -2241,7 +2284,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}}
}
@@ -2254,7 +2298,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}}
}
@@ -2272,7 +2317,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}}
}
@@ -2298,9 +2344,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}}
@@ -2370,13 +2419,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}}
}
@@ -2389,13 +2440,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}}
}
@@ -2404,7 +2457,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}}
}
@@ -2414,7 +2467,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}}
}
@@ -2437,7 +2490,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}}
@@ -2447,8 +2500,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}}
}
@@ -2462,7 +2517,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);
@@ -2474,6 +2530,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}}
@@ -2493,7 +2550,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}}
}
@@ -2556,7 +2614,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}}
}
@@ -2630,7 +2689,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}}
}
@@ -2647,7 +2707,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}}
}
@@ -2671,7 +2731,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}}
}
@@ -2683,11 +2743,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}}
}
@@ -2768,7 +2828,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}}
}
@@ -2831,8 +2892,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}}
}
@@ -2841,7 +2904,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}}
}
@@ -2855,7 +2919,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}}
}
@@ -3018,7 +3083,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}}
}
@@ -3028,7 +3094,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}}
}
@@ -3038,7 +3105,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}}
}
@@ -3047,7 +3115,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}}
}
@@ -3056,7 +3125,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}}
}
@@ -3262,7 +3332,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;
@@ -3302,7 +3373,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;
@@ -3310,7 +3382,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;
@@ -3428,7 +3501,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}}
}
@@ -3491,7 +3564,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}}
}
>From 6a95a0b92918579c7c15eb96c83095b4f8d38fbb Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Tue, 2 Jun 2026 12:11:12 +0800
Subject: [PATCH 5/8] add shouldShowInAliasChain and
buildOriginFlowChain(FactMgr, UF, LID)
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LoanPropagation.h | 4 +++
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 13 ++-----
.../LifetimeSafety/LoanPropagation.cpp | 17 ++++++++++
clang/lib/Sema/SemaLifetimeSafety.h | 34 +++++++++++--------
clang/test/Sema/LifetimeSafety/nocfg.cpp | 4 +--
clang/test/Sema/LifetimeSafety/safety.cpp | 16 ++++-----
6 files changed, 52 insertions(+), 36 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index e46912bedeb0f..9c35d7b3f1726 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -48,6 +48,10 @@ class LoanPropagationAnalysis {
buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
const LoanID TargetLoan) const;
+ llvm::SmallVector<OriginID>
+ buildOriginFlowChain(const FactManager &FactMgr, const UseFact *UF,
+ const LoanID TargetLoan) const;
+
private:
class Impl;
std::unique_ptr<Impl> PImpl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index d8746d1f6a6c4..30f9c995391e4 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -266,17 +266,8 @@ class LifetimeChecker {
} 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);
- break;
- }
-
+ llvm::SmallVector<OriginID> OriginFlowChain =
+ LoanPropagation.buildOriginFlowChain(FactMgr, UF, LID);
const llvm::SmallVector<const Expr *> ExprChain =
getExprChain(OriginFlowChain);
SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 8c570cd29673b..cfc1c4eb81cb1 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -295,4 +295,21 @@ LoanPropagationAnalysis::buildOriginFlowChain(ProgramPoint StartPoint,
const LoanID TargetLoan) const {
return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan);
}
+
+llvm::SmallVector<OriginID>
+LoanPropagationAnalysis::buildOriginFlowChain(const FactManager &FactMgr,
+ const UseFact *UF,
+ const LoanID TargetLoan) const {
+ llvm::SmallVector<OriginID> OriginFlowChain;
+
+ for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+ Cur = Cur->peelOuterOrigin())
+ if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan)) {
+ OriginFlowChain = buildOriginFlowChain(
+ FactMgr, UF, Cur->getOuterOriginID(), TargetLoan);
+ break;
+ }
+
+ return OriginFlowChain;
+}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index fe584a5328134..b4881ff2fb512 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -489,25 +489,29 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
return "expression";
}
+ bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
+ CurrExpr = CurrExpr->IgnoreImpCasts()->IgnoreParens();
+ LastExpr = LastExpr->IgnoreImpCasts()->IgnoreParens();
+
+ if (CurrExpr->getSourceRange() == LastExpr->getSourceRange())
+ return false;
+ if (isa<MaterializeTemporaryExpr>(CurrExpr) &&
+ isa<MaterializeTemporaryExpr>(LastExpr))
+ return false;
+
+ return true;
+ }
+
void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
- std::string IssueStr;
- const Expr *IssueExpr = nullptr;
- const Expr *LastExpr = nullptr;
+ if (OriginExprChain.empty())
+ return;
- for (const Expr *CurrExpr : reverse(OriginExprChain)) {
- if (!IssueExpr) {
- IssueStr = getDiagSubjectDescription(CurrExpr);
- IssueExpr = CurrExpr;
- LastExpr = CurrExpr;
- continue;
- }
+ const Expr *LastExpr = OriginExprChain.back();
+ std::string IssueStr = getDiagSubjectDescription(LastExpr);
- if (CurrExpr->getSourceRange() == LastExpr->getSourceRange())
- continue;
- if (isa<MaterializeTemporaryExpr>(CurrExpr) &&
- isa<MaterializeTemporaryExpr>(IssueExpr))
+ for (const Expr *CurrExpr : reverse(OriginExprChain)) {
+ if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
-
S.Diag(CurrExpr->getBeginLoc(),
diag::note_lifetime_safety_aliases_storage)
<< CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index d224e5226541d..0e1f18d446a8c 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -1055,9 +1055,9 @@ void operator_star_arrow_reference() {
const char* x = temporary().begin()->data(); // 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}}
const char* y = (*temporary().begin()).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()); // 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}}
+ const std::string& z = (*temporary().begin()); // 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}}
use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index ac9f19a22c278..200391b177fcc 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -1005,7 +1005,6 @@ void lifetimebound_ctor_c_style_cast() {
{
MyObj obj;
v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // 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}}
@@ -1290,7 +1289,6 @@ void parentheses(bool cond) {
{
MyObj a;
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}}
@@ -1336,14 +1334,16 @@ void use_trivial_temporary_after_destruction() {
namespace FullExprCleanupLoc {
void var_initializer() {
- View v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}}
+ View v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
.getView(); // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void expr_statement() {
View v;
- v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}}
+ v = non_trivially_destructed_temporary() // expected-warning {{local temporary object does not live long enough}} \
+ // expected-note {{expression aliases the storage of local temporary object}}
.getView(); // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -2394,9 +2394,9 @@ struct S {
void indexing_with_static_operator() {
S()(1, 2);
- S& x = S()("1",
- 2, // expected-warning {{local temporary object does not live long enough}}
- 3); // expected-warning {{local temporary object does not live long enough}} expected-note 2 {{destroyed here}}
+ 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}}
+ 3); // expected-warning {{local temporary object does not live long enough}} expected-note 2 {{destroyed here}}
(void)x; // expected-note 2 {{later used here}}
@@ -3636,7 +3636,7 @@ void transitive_capture() {
{
MyObj local;
setCaptureBy(v1, local); // expected-warning {{local variable 'local' does not live long enough}}
- setCaptureBy(v2, v1);
+ setCaptureBy(v2, v1); // expected-note {{local variable 'v1' aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)v2; // expected-note {{later used here}}
}
>From 720cbce27ac3bb0169b3c83c0ab92886e65fd52d Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Tue, 2 Jun 2026 19:45:03 +0800
Subject: [PATCH 6/8] Improve code quality of the previous commit
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../LifetimeSafety/LoanPropagation.cpp | 12 ++++-------
clang/lib/Sema/SemaLifetimeSafety.h | 21 ++++++++-----------
clang/test/Sema/LifetimeSafety/safety.cpp | 11 ----------
3 files changed, 13 insertions(+), 31 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index cfc1c4eb81cb1..a70916be9118e 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -300,16 +300,12 @@ llvm::SmallVector<OriginID>
LoanPropagationAnalysis::buildOriginFlowChain(const FactManager &FactMgr,
const UseFact *UF,
const LoanID TargetLoan) const {
- llvm::SmallVector<OriginID> OriginFlowChain;
-
for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
Cur = Cur->peelOuterOrigin())
- if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan)) {
- OriginFlowChain = buildOriginFlowChain(
- FactMgr, UF, Cur->getOuterOriginID(), TargetLoan);
- break;
- }
+ if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
+ return buildOriginFlowChain(FactMgr, UF, Cur->getOuterOriginID(),
+ TargetLoan);
- return OriginFlowChain;
+ return {};
}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index b4881ff2fb512..a6238fb35e81e 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -474,12 +474,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
}
std::string getDiagSubjectDescription(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().
- E = E->IgnoreImpCasts()->IgnoreParens();
+ E = E->IgnoreImpCasts();
if (isa<MaterializeTemporaryExpr>(E))
return "local temporary object";
@@ -490,13 +485,15 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
}
bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
- CurrExpr = CurrExpr->IgnoreImpCasts()->IgnoreParens();
- LastExpr = LastExpr->IgnoreImpCasts()->IgnoreParens();
+ CurrExpr = CurrExpr->IgnoreImpCasts();
+ LastExpr = LastExpr->IgnoreImpCasts();
- if (CurrExpr->getSourceRange() == LastExpr->getSourceRange())
+ if (isa<MaterializeTemporaryExpr, ParenExpr>(CurrExpr))
return false;
- if (isa<MaterializeTemporaryExpr>(CurrExpr) &&
- isa<MaterializeTemporaryExpr>(LastExpr))
+ // Source ranges can be used to filter out many implicit expressions,
+ // because operations between class objects often involve numerous implicit
+ // conversions, yet they share the same source range.
+ if (CurrExpr->getSourceRange() == LastExpr->getSourceRange())
return false;
return true;
@@ -509,7 +506,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
const Expr *LastExpr = OriginExprChain.back();
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : reverse(OriginExprChain)) {
+ for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
if (!shouldShowInAliasChain(CurrExpr, LastExpr))
continue;
S.Diag(CurrExpr->getBeginLoc(),
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 200391b177fcc..1d9b513e5e4de 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -1266,17 +1266,6 @@ 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}} \
- // 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}}
-}
-
void parentheses(bool cond) {
MyObj* p;
{
>From fa0ef9b5d839185dae263f6bc221e49ebb6552e0 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Wed, 3 Jun 2026 15:47:04 +0800
Subject: [PATCH 7/8] code cleanup
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LoanPropagation.h | 3 +--
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 13 ++++--------
.../LifetimeSafety/LoanPropagation.cpp | 21 +++++++++++--------
clang/lib/Sema/SemaLifetimeSafety.h | 5 +----
4 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 9c35d7b3f1726..724c6eee7d3c2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -49,8 +49,7 @@ class LoanPropagationAnalysis {
const LoanID TargetLoan) const;
llvm::SmallVector<OriginID>
- buildOriginFlowChain(const FactManager &FactMgr, const UseFact *UF,
- const LoanID TargetLoan) const;
+ buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
private:
class Impl;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 30f9c995391e4..d35452549bcfe 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -263,16 +263,11 @@ class LifetimeChecker {
SemaHelper->reportUseAfterInvalidation(
InvalidatedPVD, UF->getUseExpr(), Warning.InvalidatedByExpr);
- } else {
+ } else
// Scope-based expiry (use-after-scope).
-
- llvm::SmallVector<OriginID> OriginFlowChain =
- LoanPropagation.buildOriginFlowChain(FactMgr, UF, LID);
- const llvm::SmallVector<const Expr *> ExprChain =
- getExprChain(OriginFlowChain);
- SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
- MovedExpr, ExpiryLoc, ExprChain);
- }
+ SemaHelper->reportUseAfterScope(
+ IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
+ getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
} else if (const auto *OEF =
CausingFact.dyn_cast<const OriginEscapesFact *>()) {
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a70916be9118e..f3bb85f08e965 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -239,6 +239,16 @@ class AnalysisImpl
return {};
}
+ llvm::SmallVector<OriginID>
+ buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const {
+ for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
+ Cur = Cur->peelOuterOrigin())
+ if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
+ return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan);
+
+ return {};
+ }
+
private:
/// Returns true if the origin is persistent (referenced in multiple blocks).
bool isPersistent(OriginID OID) const {
@@ -297,15 +307,8 @@ LoanPropagationAnalysis::buildOriginFlowChain(ProgramPoint StartPoint,
}
llvm::SmallVector<OriginID>
-LoanPropagationAnalysis::buildOriginFlowChain(const FactManager &FactMgr,
- const UseFact *UF,
+LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF,
const LoanID TargetLoan) const {
- for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
- Cur = Cur->peelOuterOrigin())
- if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
- return buildOriginFlowChain(FactMgr, UF, Cur->getOuterOriginID(),
- TargetLoan);
-
- return {};
+ return PImpl->buildOriginFlowChain(UF, TargetLoan);
}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index a6238fb35e81e..2eca8f83bb337 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -493,10 +493,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
// Source ranges can be used to filter out many implicit expressions,
// because operations between class objects often involve numerous implicit
// conversions, yet they share the same source range.
- if (CurrExpr->getSourceRange() == LastExpr->getSourceRange())
- return false;
-
- return true;
+ return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
}
void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
>From 80da5b469c1f62d6fd52280114a638781c804012 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Fri, 12 Jun 2026 21:33:47 +0800
Subject: [PATCH 8/8] Use a small subset of expressions to make judgements in
shouldShowInAliasChain
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Sema/SemaLifetimeSafety.h | 2 +-
.../LifetimeSafety/annotation-suggestions.cpp | 2 +-
.../test/Sema/LifetimeSafety/cfg-bailout.cpp | 3 +-
clang/test/Sema/LifetimeSafety/nocfg.cpp | 14 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 147 ++++++------------
5 files changed, 58 insertions(+), 110 deletions(-)
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 2eca8f83bb337..69b2b159e01ae 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -488,7 +488,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
CurrExpr = CurrExpr->IgnoreImpCasts();
LastExpr = LastExpr->IgnoreImpCasts();
- if (isa<MaterializeTemporaryExpr, ParenExpr>(CurrExpr))
+ if (!isa<CallExpr, DeclRefExpr>(CurrExpr))
return false;
// Source ranges can be used to filter out many implicit expressions,
// because operations between class objects often involve numerous implicit
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 750c26876efcc..6bb9e9a715aab 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -278,7 +278,7 @@ 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}}
+ // expected-note at -2 {{expression aliases the storage of local temporary object}}
(void)s_ref; // expected-note {{later used here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/cfg-bailout.cpp b/clang/test/Sema/LifetimeSafety/cfg-bailout.cpp
index d9161d950abdc..6f2c06fdf6a72 100644
--- a/clang/test/Sema/LifetimeSafety/cfg-bailout.cpp
+++ b/clang/test/Sema/LifetimeSafety/cfg-bailout.cpp
@@ -27,8 +27,7 @@ void single_block_cfg() {
MyObj* p;
{
MyObj s;
- p = &s; // bailout-warning {{local variable 's' does not live long enough}} \
- // bailout-note {{expression aliases the storage of local variable 's'}}
+ p = &s; // bailout-warning {{local variable 's' does not live long enough}}
} // bailout-note {{destroyed here}}
(void)*p; // bailout-note {{later used here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index 0e1f18d446a8c..fd3d8faf96c6a 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -76,7 +76,6 @@ 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}}
}
@@ -613,7 +612,6 @@ 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}}
}
@@ -961,11 +959,9 @@ 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}} \
- // cfg-note {{expression aliases the storage of local temporary object}}
+ 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}} \
- // cfg-note {{expression 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}}
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}}
}
@@ -1070,11 +1066,11 @@ void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
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 4 {{expression aliases the storage of local temporary object}}
+ // cfg-note 3 {{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}}
+ const std::string& z = (*temporary().begin()).second; // 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}}
use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 1d9b513e5e4de..0a5679c6092df 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -54,8 +54,7 @@ void simple_case() {
MyObj* p;
{
MyObj s;
- p = &s; // expected-warning {{local variable 's' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 's'}}
+ p = &s; // expected-warning {{local variable 's' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -92,8 +91,7 @@ void pointer_chain() {
MyObj* q;
{
MyObj s;
- p = &s; // expected-warning {{does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 's'}}
+ p = &s; // expected-warning {{does not live long enough}}
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}}
@@ -113,8 +111,7 @@ void multiple_uses_one_warning() {
MyObj* p;
{
MyObj s;
- p = &s; // expected-warning {{does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 's'}}
+ p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
// No second warning for the same loan.
@@ -127,12 +124,9 @@ void multiple_pointers() {
MyObj *p, *q, *r;
{
MyObj s;
- 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'}}
+ 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}}
} // expected-note 3 {{destroyed here}}
(void)*p; // expected-note {{later used here}}
(void)*q; // expected-note {{later used here}}
@@ -144,8 +138,7 @@ void multiple_pointers_chained() {
{
MyObj s;
MyObj* obj1, *obj2;
- p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}} \
- // expected-note 3 {{expression aliases the storage of local variable 's'}}
+ p = obj1 = obj2 = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -465,8 +458,7 @@ void trivial_int_uaf() {
int * a;
{
int b = 1;
- a = &b; // expected-warning {{local variable 'b' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'b'}}
+ a = &b; // expected-warning {{local variable 'b' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*a; // expected-note {{later used here}}
}
@@ -475,8 +467,7 @@ void trivial_class_uaf() {
TriviallyDestructedClass* ptr;
{
TriviallyDestructedClass s;
- ptr = &s; // expected-warning {{local variable 's' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 's'}}
+ ptr = &s; // expected-warning {{local variable 's' does not live long enough}}
} // expected-note {{destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
@@ -668,8 +659,7 @@ void test_view_pointer() {
View* vp;
{
View v;
- vp = &v; // expected-warning {{local variable 'v' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'v'}}
+ vp = &v; // expected-warning {{local variable 'v' does not live long enough}}
} // expected-note {{destroyed here}}
vp->use(); // expected-note {{later used here}}
}
@@ -678,8 +668,7 @@ void test_view_double_pointer() {
View** vpp;
{
View* vp = nullptr;
- vpp = &vp; // expected-warning {{local variable 'vp' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'vp'}}
+ vpp = &vp; // expected-warning {{local variable 'vp' does not live long enough}}
} // expected-note {{destroyed here}}
(**vpp).use(); // expected-note {{later used here}}
}
@@ -706,8 +695,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}} \
- // expected-note {{expression aliases the storage of local variable 'pp'}}
+ 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'}} \
// expected-note {{expression aliases the storage of local variable 'pp'}}
} // expected-note {{destroyed here}}
@@ -742,8 +730,7 @@ MyObj* uaf_before_uar() {
MyObj* p;
{
MyObj local_obj;
- 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'}}
+ p = &local_obj; // expected-warning {{local variable 'local_obj' does not live long enough}}
} // expected-note {{destroyed here}}
return p; // expected-note {{later used here}}
}
@@ -934,7 +921,7 @@ void chained_assignment_lifetimebound_call() {
{
MyObj s;
p = Identity(obj = &s); // expected-warning {{does not live long enough}} \
- // expected-note 3 {{expression aliases the storage of local variable 's'}}
+ // expected-note {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -968,7 +955,7 @@ void lifetimebound_return_reference() {
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'}} \
// expected-note {{expression aliases the storage of local variable 'obj'}}
- ptr = &ref; // expected-note {{expression aliases the storage of local variable 'obj'}}
+ ptr = &ref;
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
@@ -994,8 +981,7 @@ void lifetimebound_ctor_functional_cast() {
LifetimeBoundCtor v;
{
MyObj obj;
- v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'obj'}}
+ v = LifetimeBoundCtor(obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -1004,8 +990,7 @@ void lifetimebound_ctor_c_style_cast() {
LifetimeBoundCtor v;
{
MyObj obj;
- v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'obj'}}
+ v = (LifetimeBoundCtor)(obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -1014,8 +999,7 @@ void lifetimebound_ctor_static_cast() {
LifetimeBoundCtor v;
{
MyObj obj;
- v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'obj'}}
+ v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
(void)v; // expected-note {{later used here}}
}
@@ -1270,8 +1254,7 @@ void parentheses(bool cond) {
MyObj* p;
{
MyObj a;
- p = &((((a)))); // expected-warning {{local variable 'a' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'a'}}
+ p = &((((a)))); // expected-warning {{local variable 'a' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
@@ -1451,7 +1434,7 @@ 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}} \
- // expected-note 2 {{expression aliases the storage of local variable 'smart_ptr'}}
+ // expected-note {{expression aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1491,7 +1474,7 @@ 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}} \
- // expected-note 3 {{expression aliases the storage of local variable 'double_ptr'}}
+ // expected-note 2 {{expression aliases the storage of local variable 'double_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1855,8 +1838,7 @@ void uaf() {
std::string_view view;
{
S str;
- S* p = &str; // expected-warning {{local variable 'str' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'str'}}
+ S* p = &str; // expected-warning {{local variable 'str' does not live long enough}}
view = p->s; // expected-note {{local variable 'p' aliases the storage of local variable 'str'}} \
// expected-note {{expression aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
@@ -1883,8 +1865,7 @@ void uaf_union() {
std::string_view view;
{
U u = U{"hello"};
- U* up = &u; // expected-warning {{local variable 'u' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'u'}}
+ 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 {{expression aliases the storage of local variable 'u'}}
} // expected-note {{destroyed here}}
@@ -1902,10 +1883,8 @@ void uaf_anonymous_union() {
int* ip;
{
AnonymousUnion 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'}}
+ 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'}}
} // expected-note {{destroyed here}}
(void)ip; // expected-note {{later used here}}
}
@@ -2240,8 +2219,7 @@ void element_use_after_scope() {
int* p;
{
int a[10]{};
- p = &a[2]; // expected-warning {{local variable 'a' does not live long enough}} \
- // expected-note 2 {{expression aliases the storage of local variable 'a'}}
+ p = &a[2]; // expected-warning {{local variable 'a' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2273,8 +2251,7 @@ 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}} \
- // expected-note 3 {{expression aliases the storage of local variable 'a'}}
+ p = &a[1][2]; // expected-warning {{local variable 'a' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2287,8 +2264,7 @@ 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}} \
- // expected-note 3 {{expression aliases the storage of local variable 's'}}
+ p = &s.arr[0]; // expected-warning {{local variable 's' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2306,8 +2282,7 @@ void reversed_subscript_use_after_scope() {
int* p;
{
int a[10]{};
- p = &(0[a]); // expected-warning {{local variable 'a' does not live long enough}} \
- // expected-note 2 {{expression aliases the storage of local variable 'a'}}
+ p = &(0[a]); // expected-warning {{local variable 'a' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -2333,12 +2308,9 @@ void pointer_arithmetic_use_after_scope() {
int* p3;
{
int a[10]{};
- 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'}}
+ 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}}
} // expected-note 3 {{destroyed here}}
(void)*p; // expected-note {{later used here}}
(void)*p2; // expected-note {{later used here}}
@@ -2415,8 +2387,7 @@ void from_free_function() {
void from_constructor() {
S s(std::string("temp")); // expected-warning {{local temporary object does not live long enough}} \
- // expected-note {{destroyed here}} \
- // expected-note {{expression aliases the storage of local temporary object}}
+ // expected-note {{destroyed here}}
use(s); // expected-note {{later used here}}
}
@@ -2719,8 +2690,7 @@ void local_pointer() {
Pointer<int> p;
{
int v;
- p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'v'}}
+ p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}}
} // expected-note {{destroyed here}}
use(*p); // expected-note {{later used here}}
}
@@ -2731,12 +2701,9 @@ void nested_local_pointer() {
Pointer<Bar> p;
{
Bar v;
- p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}} \
- // 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 {{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 {{expression aliases the storage of local variable 'v'}}
+ p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}}
+ pp = Pointer(p); // expected-note {{local variable 'p' aliases the storage of local variable 'v'}}
+ ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}}
} // expected-note {{destroyed here}}
use(***ppp); // expected-note {{later used here}}
}
@@ -2817,8 +2784,7 @@ void new_view_from_dead_scope() {
View *p;
{
MyObj obj;
- 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'}}
+ p = new View(obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
p->use(); // expected-note {{later used here}}
}
@@ -2881,10 +2847,8 @@ void new_pointer_from_pointer() {
MyObj **p;
{
MyObj 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'}}
+ 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'}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -2893,8 +2857,7 @@ void new_pointer_from_dead_object() {
MyObj **p;
{
MyObj obj;
- 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'}}
+ p = new MyObj *(&obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -2908,8 +2871,7 @@ 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}} \
- // expected-note 2 {{expression aliases the storage of local variable 'obj2'}}
+ p = new MultiView(obj1, obj2); // expected-warning {{local variable 'obj2' does not live long enough}}
} // expected-note {{destroyed here}}
(void)p; // expected-note {{later used here}}
}
@@ -3072,8 +3034,7 @@ void placement_new_int_basic() {
int *p;
{
int storage;
- 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'}}
+ p = new (&storage) int; // expected-warning {{local variable 'storage' does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -3083,8 +3044,7 @@ 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}} \
- // expected-note 2 {{expression aliases the storage of local variable 'obj'}}
+ p = new (&storage) View(obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
p->use(); // expected-note {{later used here}}
}
@@ -3094,8 +3054,7 @@ 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}} \
- // expected-note 2 {{expression aliases the storage of local variable 'obj'}}
+ p = new (&slot) MyObj *(&obj); // expected-warning {{local variable 'obj' does not live long enough}}
} // expected-note {{destroyed here}}
(void)**p; // expected-note {{later used here}}
}
@@ -3104,8 +3063,7 @@ 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}} \
- // expected-note 2 {{expression aliases the storage of local variable 'storage'}}
+ p = new (&storage) int[2]; // expected-warning {{local variable 'storage' does not live long enough}}
} // expected-note {{destroyed here}}
(void)p[0]; // expected-note {{later used here}}
}
@@ -3114,8 +3072,7 @@ 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}} \
- // expected-note 2 {{expression aliases the storage of local variable 'storage'}}
+ p = new (&storage) int[2]{}; // expected-warning {{local variable 'storage' does not live long enough}}
} // expected-note {{destroyed here}}
(void)p[0]; // expected-note {{later used here}}
}
@@ -3321,8 +3278,7 @@ struct S {
void foo() {
{
int num;
- this->p_ = # // expected-warning {{local variable 'num' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'num'}}
+ this->p_ = # // expected-warning {{local variable 'num' does not live long enough}}
} // expected-note {{destroyed here}}
bar(); // expected-note {{later used here}}
this->p_ = &GLOBAL_INT;
@@ -3362,8 +3318,7 @@ struct S2 : S {
void foo2() {
{
int num;
- this->p_ = # // expected-warning {{local variable 'num' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'num'}}
+ this->p_ = # // expected-warning {{local variable 'num' does not live long enough}}
} // expected-note {{destroyed here}}
bar(); // expected-note {{later used here}}
this->p_ = &GLOBAL_INT;
@@ -3371,8 +3326,7 @@ struct S2 : S {
void baz2() {
{
int num;
- this->p_ = # // expected-warning {{local variable 'num' does not live long enough}} \
- // expected-note {{expression aliases the storage of local variable 'num'}}
+ this->p_ = # // expected-warning {{local variable 'num' does not live long enough}}
} // expected-note {{destroyed here}}
bar2(); // expected-note {{later used here}}
this->p_ = nullptr;
@@ -3554,7 +3508,6 @@ void deref_use_after_scope() {
{
optional<MyObj> opt;
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