[clang] c5654d5 - [LifetimeSafety] Introduce buildOriginFlowChain for use-after-scope (#199345)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 12 08:38:04 PDT 2026
Author: Yuan Suo
Date: 2026-06-12T17:37:59+02:00
New Revision: c5654d53c008522d2da165f05b682aafa36d7189
URL: https://github.com/llvm/llvm-project/commit/c5654d53c008522d2da165f05b682aafa36d7189
DIFF: https://github.com/llvm/llvm-project/commit/c5654d53c008522d2da165f05b682aafa36d7189.diff
LOG: [LifetimeSafety] Introduce buildOriginFlowChain for use-after-scope (#199345)
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`.
Since we use `buildOriginFlowChain`, we can directly trace distinct
assignment steps that occur within a single source-level expression. For
example:
```cpp
#include <vector>
#include <string>
template<class... T> void use(T... arg);
void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
std::vector<std::pair<int, std::string>> v;
const char* p = v.begin()->second.data();
const char* q = (*v.begin()).second.data();
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();
const char* y = (*temporary().begin()).second.data();
const std::string& z = (*temporary().begin()).second;
use(p, q, r, x, y, z);
}
```
The code above produces the following diagnostic output:
```txt
pr.cpp:13:19: warning: local temporary object does not live long enough [-Wlifetime-safety-use-after-scope]
13 | const char* x = temporary().begin()->second.data();
| ^~~~~~~~~~~
pr.cpp:13:52: note: destroyed here
13 | const char* x = temporary().begin()->second.data();
| ^
pr.cpp:13:19: note: expression aliases the storage of local temporary object
13 | const char* x = temporary().begin()->second.data();
| ^~~~~~~~~~~~~~~~~~~
pr.cpp:13:19: note: expression aliases the storage of local temporary object
13 | const char* x = temporary().begin()->second.data();
| ^~~~~~~~~~~~~~~~~~~~~
pr.cpp:13:19: note: expression aliases the storage of local temporary object
13 | const char* x = temporary().begin()->second.data();
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pr.cpp:16:16: note: later used here
16 | use(p, q, r, x, y, z);
| ^
pr.cpp:15:28: warning: local temporary object does not live long enough [-Wlifetime-safety-use-after-scope]
15 | const std::string& z = (*temporary().begin()).second;
| ^~~~~~~~~~~
pr.cpp:15:49: note: destroyed here
15 | const std::string& z = (*temporary().begin()).second;
| ^
pr.cpp:15:28: note: expression aliases the storage of local temporary object
15 | const std::string& z = (*temporary().begin()).second;
| ^~~~~~~~~~~~~~~~~~~
pr.cpp:15:27: note: expression aliases the storage of local temporary object
15 | const std::string& z = (*temporary().begin()).second;
| ^~~~~~~~~~~~~~~~~~~~
pr.cpp:16:22: note: later used here
16 | use(p, q, r, x, y, z);
| ^
pr.cpp:14:21: warning: local temporary object does not live long enough [-Wlifetime-safety-use-after-scope]
14 | const char* y = (*temporary().begin()).second.data();
| ^~~~~~~~~~~
pr.cpp:14:54: note: destroyed here
14 | const char* y = (*temporary().begin()).second.data();
| ^
pr.cpp:14:21: note: expression aliases the storage of local temporary object
14 | const char* y = (*temporary().begin()).second.data();
| ^~~~~~~~~~~~~~~~~~~
pr.cpp:14:20: note: expression aliases the storage of local temporary object
14 | const char* y = (*temporary().begin()).second.data();
| ^~~~~~~~~~~~~~~~~~~~
pr.cpp:14:19: note: expression aliases the storage of local temporary object
14 | const char* y = (*temporary().begin()).second.data();
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pr.cpp:16:19: note: later used here
16 | use(p, q, r, x, y, z);
| ^
3 warnings generated.
```
---------
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
Added:
Modified:
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Analysis/LifetimeSafety/Checker.cpp
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
clang/lib/Sema/SemaLifetimeSafety.h
clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
clang/test/Sema/LifetimeSafety/nocfg.cpp
clang/test/Sema/LifetimeSafety/safety.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index b89e1bfe4b43e..28886b826f72f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -63,7 +63,8 @@ class LifetimeSafetySemaHelper {
virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr,
- SourceLocation FreeLoc) {}
+ SourceLocation FreeLoc,
+ llvm::ArrayRef<const Expr *> ExprChain) {}
virtual void reportUseAfterReturn(const Expr *IssueExpr,
const Expr *ReturnExpr,
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index e46912bedeb0f..724c6eee7d3c2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -48,6 +48,9 @@ class LoanPropagationAnalysis {
buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
const LoanID TargetLoan) const;
+ llvm::SmallVector<OriginID>
+ buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
+
private:
class Impl;
std::unique_ptr<Impl> PImpl;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 583f3f9d2eff5..9691963d4fe7e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11061,6 +11061,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_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 71cf2d2d5a674..d41d6f43f837b 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -266,8 +266,10 @@ class LifetimeChecker {
} else
// Scope-based expiry (use-after-scope).
- SemaHelper->reportUseAfterScope(IssueExpr, UF->getUseExpr(),
- MovedExpr, ExpiryLoc);
+ SemaHelper->reportUseAfterScope(
+ IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
+ getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
+
} else if (const auto *OEF =
CausingFact.dyn_cast<const OriginEscapesFact *>()) {
if (Warning.InvalidatedByExpr) {
@@ -509,6 +511,21 @@ class LifetimeChecker {
}
}
}
+
+ /// Extract expressions from the origin flow chain for diagnostic purposes.
+ ///
+ /// 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 *>
+ getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
+ llvm::SmallVector<const Expr *> rs;
+ for (const OriginID CurrOID : OriginFlowChain)
+ if (const Expr *CurrExpr =
+ FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
+ rs.push_back(CurrExpr);
+ return rs;
+ }
};
} // namespace
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 8c570cd29673b..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 {
@@ -295,4 +305,10 @@ LoanPropagationAnalysis::buildOriginFlowChain(ProgramPoint StartPoint,
const LoanID TargetLoan) const {
return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan);
}
+
+llvm::SmallVector<OriginID>
+LoanPropagationAnalysis::buildOriginFlowChain(const UseFact *UF,
+ const LoanID TargetLoan) const {
+ return PImpl->buildOriginFlowChain(UF, TargetLoan);
+}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 462a4ed168e24..a7c628f315d78 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -62,8 +62,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
- const Expr *MovedExpr,
- SourceLocation FreeLoc) override {
+ 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;
@@ -74,6 +74,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here);
+
+ reportAliasingChain(ExprChain);
+
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
}
@@ -481,13 +484,44 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
}
std::string getDiagSubjectDescription(const Expr *E) {
+ E = E->IgnoreImpCasts();
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 "";
+ return "expression";
+ }
+
+ bool shouldShowInAliasChain(const Expr *CurrExpr, const Expr *LastExpr) {
+ CurrExpr = CurrExpr->IgnoreImpCasts();
+ LastExpr = LastExpr->IgnoreImpCasts();
+
+ 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
+ // conversions, yet they share the same source range.
+ return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
+ }
+
+ void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
+ if (OriginExprChain.empty())
+ return;
+
+ const Expr *LastExpr = OriginExprChain.back();
+ std::string IssueStr = getDiagSubjectDescription(LastExpr);
+
+ for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
+ if (!shouldShowInAliasChain(CurrExpr, LastExpr))
+ continue;
+ S.Diag(CurrExpr->getBeginLoc(),
+ diag::note_lifetime_safety_aliases_storage)
+ << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
+ << IssueStr;
+ LastExpr = CurrExpr;
+ }
}
Sema &S;
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 1f6832e824e37..1f94e101fb9ef 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 {{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}}
}
@@ -599,7 +602,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 {{expression aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)f; // expected-note {{later used here}}
}
@@ -622,7 +626,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 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
@@ -636,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/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index e2a340401ac87..49b6175d378ae 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -193,7 +193,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,11 +242,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-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}}
// no warning on constructing from gsl-pointer
@@ -306,23 +309,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}}
}
@@ -333,7 +342,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}}
;
}
@@ -610,7 +620,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
@@ -844,7 +855,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
@@ -859,10 +871,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-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}}
return Ref(std::string()); // expected-warning {{returning address}} \
// cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}}
@@ -870,10 +884,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-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}}
return TakeSv(std::string()); // expected-warning {{returning address}} \
@@ -882,10 +898,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-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}}
return TakeStrRef(std::string()); // expected-warning {{returning address}} \
// cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}}
@@ -907,10 +925,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-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}}
return r1.get(); // expected-warning {{address of stack}} \
// cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}}
@@ -1028,9 +1048,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 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 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}}
}
@@ -1042,9 +1065,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}}
- 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 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 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}}
}
@@ -1094,17 +1120,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 580e1b2639ee1..2aa5ff43ec3bc 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 {{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}}
}
@@ -696,7 +696,8 @@ 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 {{expression 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 {{expression 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 {{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}}
@@ -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 {{expression 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 {{expression 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 3 {{expression aliases the storage of local variable 'obj'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -904,7 +910,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}}
}
@@ -913,7 +920,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 {{expression aliases the storage of local variable 's'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -945,7 +953,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);
+ 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 {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
@@ -999,7 +1008,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 {{expression aliases the storage of local variable 'obj'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1016,7 +1026,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}}
}
@@ -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 {{expression 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 {{expression 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 {{expression 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 {{expression aliases the storage of local variable 'obj_short'}}
} // tu-note {{destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1245,7 +1260,8 @@ void parentheses(bool cond) {
{
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 {{expression aliases the storage of local variable 'a'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
@@ -1276,7 +1292,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}}
}
@@ -1289,14 +1306,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}}
}
@@ -1339,7 +1358,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 {{expression aliases the storage of local variable 'string_or'}}
value();
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
@@ -1413,7 +1433,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 {{expression aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1430,7 +1451,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 {{expression aliases the storage of local variable 'smart_ptr'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1440,7 +1462,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}}
}
@@ -1450,7 +1473,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 2 {{expression aliases the storage of local variable 'double_ptr'}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1636,13 +1660,15 @@ 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 {{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}}
}
@@ -1653,7 +1679,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}}
@@ -1675,9 +1702,11 @@ 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 {{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}}
@@ -1765,17 +1794,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;
+ 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}}
}
@@ -1783,12 +1815,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
@@ -1805,7 +1839,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 {{expression aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
@@ -1830,8 +1865,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 {{expression aliases the storage of local variable 'u'}}
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
@@ -1848,7 +1884,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}}
}
@@ -1906,9 +1942,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}}
@@ -1943,7 +1982,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}}
}
@@ -1952,7 +1992,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 2 {{expression aliases the storage of local variable 'opt'}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1961,7 +2002,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}}
}
@@ -2313,9 +2355,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}}
@@ -2338,7 +2380,8 @@ 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}}
}
@@ -2357,13 +2400,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}}
}
@@ -2371,7 +2416,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 {{expression aliases the storage of local variable 'f'}}
} // expected-note {{destroyed here}}
use(value); // expected-note {{later used here}}
}
@@ -2380,7 +2426,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 {{expression aliases the storage of local variable 'str'}}
} // expected-note {{destroyed here}}
use(s); // expected-note {{later used here}}
}
@@ -2402,8 +2449,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 {{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}}
}
@@ -2412,8 +2460,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;
+ 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}}
}
@@ -2427,7 +2477,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);
@@ -2439,6 +2490,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}}
@@ -2458,7 +2510,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}}
}
@@ -2521,7 +2574,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}}
}
@@ -2595,7 +2649,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}}
}
@@ -2611,7 +2666,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 {{expression aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)ups; // expected-note {{later used here}}
}
@@ -2646,8 +2702,8 @@ void nested_local_pointer() {
{
Bar v;
p = Pointer(v); // expected-warning {{local variable 'v' does not live long enough}}
- pp = Pointer(p);
- ppp = Pointer(pp);
+ 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}}
}
@@ -2792,7 +2848,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 +3443,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 {{expression aliases the storage of local variable 'local'}}
} // expected-note {{destroyed here}}
(void)f; // expected-note {{later used here}}
}
@@ -3450,7 +3507,8 @@ 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 {{destroyed here}}
(void)p->id; // expected-note {{later used here}}
}
@@ -3520,7 +3578,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}}
}
More information about the cfe-commits
mailing list