[clang] [LifetimeSafety] Use buildOriginFlowChain for remaining diagnostics in issuePendingWarnings (PR #212083)
Yuan Suo via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 17 18:48:24 PDT 2026
https://github.com/suoyuan666 updated https://github.com/llvm/llvm-project/pull/212083
>From fc0f25bc4769204d0fdfad065bc35708d31300c4 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 18 Jul 2026 15:06:23 +0800
Subject: [PATCH 1/6] Use buildOriginFlowChain for remaining diagnostics in
issuePendingWarnings
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 18 ++++--
.../Analyses/LifetimeSafety/LoanPropagation.h | 2 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 34 +++++-----
.../LifetimeSafety/LoanPropagation.cpp | 9 ++-
clang/lib/Sema/SemaLifetimeSafety.h | 34 +++++++---
.../LifetimeSafety/annotation-suggestions.cpp | 18 ++++--
.../Sema/LifetimeSafety/dangling-field.cpp | 27 +++++---
.../Sema/LifetimeSafety/dangling-global.cpp | 2 +-
.../Sema/LifetimeSafety/invalidations.cpp | 19 ++++--
clang/test/Sema/LifetimeSafety/nocfg.cpp | 14 +++--
clang/test/Sema/LifetimeSafety/safety-c.c | 6 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 63 +++++++++++++------
12 files changed, 164 insertions(+), 82 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index f8b7d2008bd789..84fbfde19dbb80 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -86,12 +86,14 @@ class LifetimeSafetySemaHelper {
const FieldDecl *Field,
const Expr *MovedExpr,
bool IsCapturedByLambda,
- SourceLocation ExpiryLoc) {}
+ SourceLocation ExpiryLoc,
+ llvm::ArrayRef<const Expr *> ExprChain) {}
virtual void reportDanglingGlobal(const Expr *IssueExpr,
const VarDecl *DanglingGlobal,
const Expr *MovedExpr,
SourceLocation ExpiryLoc,
+ llvm::ArrayRef<const Expr *> ExprChain,
bool IsMain = false) {}
// Reports when a reference/iterator is used after the container operation
@@ -106,16 +108,22 @@ class LifetimeSafetySemaHelper {
llvm::ArrayRef<const Expr *> ExprChain) {}
virtual void reportInvalidatedField(const Expr *IssueExpr,
const FieldDecl *Field,
- const Expr *InvalidationExpr) {}
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) {}
virtual void reportInvalidatedField(const ParmVarDecl *PVD,
const FieldDecl *Field,
- const Expr *InvalidationExpr) {}
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) {}
virtual void reportInvalidatedGlobal(const Expr *IssueExpr,
const VarDecl *Global,
- const Expr *InvalidationExpr) {}
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) {
+ }
virtual void reportInvalidatedGlobal(const ParmVarDecl *PVD,
const VarDecl *Global,
- const Expr *InvalidationExpr) {}
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) {
+ }
using EscapingTarget =
llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index de7eca39228ca3..d8dffb4fc1a268 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -53,7 +53,7 @@ class LoanPropagationAnalysis {
/// Like the above, starting from the used origin holding \p TargetLoan. The
/// casts that load the used variable itself are left out.
- llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
+ llvm::SmallVector<OriginID> buildOriginFlowChain(const Fact *F,
const LoanID TargetLoan,
const CFG *Cfg) const;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 3d2cab92b384bf..a5fdcf677484cd 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -322,33 +322,35 @@ class LifetimeChecker {
} else if (const auto *OEF =
dyn_cast<const OriginEscapesFact *>(CausingFact)) {
+ llvm::SmallVector<const Expr *> ExprChain =
+ getExprChain(LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg));
if (Warning.InvalidatedByExpr) {
if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
// Invalidated object escapes to a field.
if (IssueExpr)
// Invalidated object on stack escapes to a field.
- SemaHelper->reportInvalidatedField(IssueExpr,
- FieldEscape->getFieldDecl(),
- Warning.InvalidatedByExpr);
+ SemaHelper->reportInvalidatedField(
+ IssueExpr, FieldEscape->getFieldDecl(),
+ Warning.InvalidatedByExpr, ExprChain);
else if (InvalidatedPVD)
// Invalidated parameter escapes to a field.
- SemaHelper->reportInvalidatedField(InvalidatedPVD,
- FieldEscape->getFieldDecl(),
- Warning.InvalidatedByExpr);
+ SemaHelper->reportInvalidatedField(
+ InvalidatedPVD, FieldEscape->getFieldDecl(),
+ Warning.InvalidatedByExpr, ExprChain);
} else if (const auto *GlobalEscape =
dyn_cast<GlobalEscapeFact>(OEF)) {
// Invalidated object escapes to global or static storage.
if (IssueExpr)
// Invalidated object on stack escapes to global or static
// storage.
- SemaHelper->reportInvalidatedGlobal(IssueExpr,
- GlobalEscape->getGlobal(),
- Warning.InvalidatedByExpr);
+ SemaHelper->reportInvalidatedGlobal(
+ IssueExpr, GlobalEscape->getGlobal(),
+ Warning.InvalidatedByExpr, ExprChain);
else if (InvalidatedPVD)
// Invalidated parameter escapes to global or static storage.
- SemaHelper->reportInvalidatedGlobal(InvalidatedPVD,
- GlobalEscape->getGlobal(),
- Warning.InvalidatedByExpr);
+ SemaHelper->reportInvalidatedGlobal(
+ InvalidatedPVD, GlobalEscape->getGlobal(),
+ Warning.InvalidatedByExpr, ExprChain);
} else if (isa<ReturnEscapeFact>(OEF)) {
// FIXME: Diagnose invalidated return escapes separately.
} else
@@ -356,23 +358,21 @@ class LifetimeChecker {
} else if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) {
// Return stack address.
SemaHelper->reportUseAfterReturn(
- IssueExpr, RetEscape->getReturnExpr(), MovedExpr,
- getExprChain(
- LoanPropagation.buildOriginFlowChain(OEF, LID, Cfg)));
+ IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExprChain);
} else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) {
// Dangling field.
bool IsCapturedByLambda =
FactMgr.isFieldCapturedByLambda(FieldEscape->getFieldDecl());
SemaHelper->reportDanglingField(
IssueExpr, FieldEscape->getFieldDecl(), MovedExpr,
- IsCapturedByLambda, ExpiryLoc);
+ IsCapturedByLambda, ExpiryLoc, ExprChain);
} else if (const auto *GlobalEscape = dyn_cast<GlobalEscapeFact>(OEF)) {
// Global escape.
bool IsMain = false;
if (const auto *Func = dyn_cast_if_present<FunctionDecl>(FD))
IsMain = Func->isMain();
SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
- MovedExpr, ExpiryLoc, IsMain);
+ MovedExpr, ExpiryLoc, ExprChain, IsMain);
} else
llvm_unreachable("Unhandled OriginEscapesFact type");
} else
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index df66f029a5400d..7db184a09a41e0 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -352,8 +352,13 @@ llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
}
llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
- const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
- return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
+ const Fact *F, const LoanID TargetLoan, const CFG *Cfg) const {
+ if (const auto *UF = llvm::dyn_cast<UseFact>(F))
+ return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
+ if (const auto *OEF = llvm::dyn_cast<OriginEscapesFact>(F))
+ return PImpl->buildOriginFlowChain(OEF, OEF->getEscapedOriginID(),
+ TargetLoan, Cfg);
+ llvm_unreachable("Unhandled Fact type");
}
llvm::SmallVector<OriginID>
LoanPropagationAnalysis::buildOriginFlowChain(const OriginEscapesFact *OEF,
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 3db298b6fec85c..57570043610a0d 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -187,7 +187,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
void reportDanglingField(const Expr *IssueExpr,
const FieldDecl *DanglingField,
const Expr *MovedExpr, bool IsCapturedByLambda,
- SourceLocation ExpiryLoc) override {
+ SourceLocation ExpiryLoc,
+ llvm::ArrayRef<const Expr *> ExprChain) override {
unsigned DiagID =
IsCapturedByLambda
? diag::warn_lifetime_safety_dangling_field_lambda_capture
@@ -201,6 +202,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
+
+ reportAliasingChain(ExprChain);
+
S.Diag(DanglingField->getLocation(),
diag::note_lifetime_safety_dangling_field_here)
<< DanglingField->getEndLoc();
@@ -209,6 +213,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
void reportDanglingGlobal(const Expr *IssueExpr,
const VarDecl *DanglingGlobal,
const Expr *MovedExpr, SourceLocation ExpiryLoc,
+ llvm::ArrayRef<const Expr *> ExprChain,
bool IsMain = false) override {
unsigned DiagID;
if (IsMain) {
@@ -226,6 +231,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
if (MovedExpr)
S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here)
<< MovedExpr->getSourceRange();
+
+ reportAliasingChain(ExprChain);
+
if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_static_here)
@@ -271,13 +279,15 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
void reportInvalidatedField(const Expr *IssueExpr,
const FieldDecl *DanglingField,
- const Expr *InvalidationExpr) override {
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) override {
std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
S.Diag(IssueExpr->getExprLoc(),
diag::warn_lifetime_safety_invalidated_field)
<< InvalidatedSubject << getDiagSubjectDescription(DanglingField)
<< IssueExpr->getSourceRange();
reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
+ reportAliasingChain(ExprChain);
S.Diag(DanglingField->getLocation(),
diag::note_lifetime_safety_dangling_field_here)
<< DanglingField->getEndLoc();
@@ -285,27 +295,31 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
void reportInvalidatedField(const ParmVarDecl *PVD,
const FieldDecl *DanglingField,
- const Expr *InvalidationExpr) override {
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) override {
std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
S.Diag(PVD->getSourceRange().getBegin(),
diag::warn_lifetime_safety_invalidated_field)
<< InvalidatedSubject << getDiagSubjectDescription(DanglingField)
<< PVD->getSourceRange();
reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
+ reportAliasingChain(ExprChain);
S.Diag(DanglingField->getLocation(),
diag::note_lifetime_safety_dangling_field_here)
<< DanglingField->getEndLoc();
}
- void reportInvalidatedGlobal(const Expr *IssueExpr,
- const VarDecl *DanglingGlobal,
- const Expr *InvalidationExpr) override {
+ void
+ reportInvalidatedGlobal(const Expr *IssueExpr, const VarDecl *DanglingGlobal,
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) override {
std::string InvalidatedSubject = getDiagSubjectDescription(IssueExpr);
S.Diag(IssueExpr->getExprLoc(),
diag::warn_lifetime_safety_invalidated_global)
<< InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
<< IssueExpr->getSourceRange();
reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
+ reportAliasingChain(ExprChain);
if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_static_here)
@@ -316,15 +330,17 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
<< DanglingGlobal->getEndLoc();
}
- void reportInvalidatedGlobal(const ParmVarDecl *PVD,
- const VarDecl *DanglingGlobal,
- const Expr *InvalidationExpr) override {
+ void
+ reportInvalidatedGlobal(const ParmVarDecl *PVD, const VarDecl *DanglingGlobal,
+ const Expr *InvalidationExpr,
+ llvm::ArrayRef<const Expr *> ExprChain) override {
std::string InvalidatedSubject = getDiagSubjectDescription(PVD);
S.Diag(PVD->getSourceRange().getBegin(),
diag::warn_lifetime_safety_invalidated_global)
<< InvalidatedSubject << getDiagSubjectDescription(DanglingGlobal)
<< PVD->getSourceRange();
reportInvalidationSite(InvalidationExpr, InvalidatedSubject);
+ reportAliasingChain(ExprChain);
if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember())
S.Diag(DanglingGlobal->getLocation(),
diag::note_lifetime_safety_dangling_static_here)
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 34f3ad4f45de1a..3769710c489886 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -528,7 +528,8 @@ struct CaptureRefToView {
CaptureRefToView test_ref_to_view() {
MyObj obj;
CaptureRefToView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct CaptureRefToPtr {
@@ -539,7 +540,8 @@ struct CaptureRefToPtr {
CaptureRefToPtr test_ref_to_ptr() {
MyObj obj;
CaptureRefToPtr x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct CaptureViewToView {
@@ -562,7 +564,8 @@ struct CapturePtrToPtr {
CapturePtrToPtr test_ptr_to_ptr() {
MyObj obj;
CapturePtrToPtr x(&obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct CaptureRefToRef {
@@ -573,7 +576,8 @@ struct CaptureRefToRef {
CaptureRefToRef test_ref_to_ref() {
MyObj obj;
CaptureRefToRef x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct BaseWithView {
@@ -588,7 +592,8 @@ struct CaptureRefToBaseView : BaseWithView {
CaptureRefToBaseView test_ref_to_base_view() {
MyObj obj;
CaptureRefToBaseView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
} // namespace capturing_constructor
@@ -660,7 +665,8 @@ struct HasCtorField {
HasCtorField test_dangling_field_ctor() {
MyObj obj;
HasCtorField x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct HasSetterField {
diff --git a/clang/test/Sema/LifetimeSafety/dangling-field.cpp b/clang/test/Sema/LifetimeSafety/dangling-field.cpp
index 68ed47687b29ae..06db94d81f02c5 100644
--- a/clang/test/Sema/LifetimeSafety/dangling-field.cpp
+++ b/clang/test/Sema/LifetimeSafety/dangling-field.cpp
@@ -40,7 +40,8 @@ struct CtorSetInLoop {
struct CtorInitLifetimeBound {
std::string_view view; // expected-note {{this field dangles}}
- CtorInitLifetimeBound(std::string s) : view(construct_view(s)) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}}
+ CtorInitLifetimeBound(std::string s) : view(construct_view(s)) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}} \
+ // expected-note {{result of call to 'construct_view' aliases the storage of parameter 's'}}
};
struct CtorInitButMoved {
@@ -90,7 +91,8 @@ struct CtorRefField {
struct CtorPointerField {
const char* ptr; // expected-note {{this field dangles}}
- CtorPointerField(std::string s) : ptr(s.data()) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'ptr' which will dangle}}
+ CtorPointerField(std::string s) : ptr(s.data()) {} // expected-warning {{stack memory associated with parameter 's' escapes to the field 'ptr' which will dangle}} \
+ // expected-note {{result of call to 'data' aliases the storage of parameter 's'}}
CtorPointerField(Dummy<1> ok, const std::string& s) : ptr(s.data()) {}
CtorPointerField(Dummy<2> ok, std::string_view view) : ptr(view.data()) {}
};
@@ -101,12 +103,14 @@ struct MemberSetters {
void setWithParam(std::string s) {
view = s; // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}}
- p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}}
+ p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}} \
+ // expected-note {{result of call to 'data' aliases the storage of parameter 's'}}
}
void setWithParamAndReturn(std::string s) {
view = s; // expected-warning {{stack memory associated with parameter 's' escapes to the field 'view' which will dangle}}
- p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}}
+ p = s.data(); // expected-warning {{stack memory associated with parameter 's' escapes to the field 'p' which will dangle}} \
+ // expected-note {{result of call to 'data' aliases the storage of parameter 's'}}
return;
}
@@ -124,13 +128,15 @@ struct MemberSetters {
void setWithLocal() {
std::string s;
view = s; // expected-warning {{stack memory associated with local variable 's' escapes to the field 'view' which will dangle}}
- p = s.data(); // expected-warning {{stack memory associated with local variable 's' escapes to the field 'p' which will dangle}}
+ p = s.data(); // expected-warning {{stack memory associated with local variable 's' escapes to the field 'p' which will dangle}} \
+ // expected-note {{result of call to 'data' aliases the storage of local variable 's'}}
}
void setWithLocalButMoved() {
std::string s;
view = s; // expected-warning-re {{stack memory associated with local variable 's' may escape to the field 'view' which will dangle. {{.*}} may have been moved}}
- p = s.data(); // expected-warning-re {{stack memory associated with local variable 's' may escape to the field 'p' which will dangle. {{.*}} may have been moved}}
+ p = s.data(); // expected-warning-re {{stack memory associated with local variable 's' may escape to the field 'p' which will dangle. {{.*}} may have been moved}} \
+ // expected-note {{result of call to 'data' aliases the storage of local variable 's'}}
takeString(std::move(s)); // expected-note 2 {{potentially moved here}}
}
@@ -154,14 +160,16 @@ struct MemberSetters {
std::string local;
view = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'view' which will dangle}}
- p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}}
+ p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}} \
+ // expected-note {{result of call to 'data' aliases the storage of local variable 'local'}}
}
void escape_to_field() {
{
std::string local;
view = local; // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'view' which will dangle}}
- p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}}
+ p = local.data(); // expected-warning {{stack memory associated with local variable 'local' escapes to the field 'p' which will dangle}} \
+ // expected-note {{result of call to 'data' aliases the storage of local variable 'local'}}
}
(void)view; // Discarding the value is not a use.
(void)p;
@@ -256,7 +264,8 @@ struct HasUniquePtrField {
std::unique_ptr<LifetimeBoundCtor> field; // tu-note {{this field dangles}}
void setWithParam(MyObj obj) {
- field = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{stack memory associated with parameter 'obj' escapes to the field 'field' which will dangle}}
+ field = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{stack memory associated with parameter 'obj' escapes to the field 'field' which will dangle}} \
+ // tu-note {{result of call to 'make_unique<MakeUnique::LifetimeBoundCtor, MakeUnique::MyObj &>' aliases the storage of parameter 'obj'}}
}
};
} // namespace MakeUnique
diff --git a/clang/test/Sema/LifetimeSafety/dangling-global.cpp b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
index ef00582fb9c122..7a0e03594fff64 100644
--- a/clang/test/Sema/LifetimeSafety/dangling-global.cpp
+++ b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
@@ -27,7 +27,7 @@ void invoke_function_with_side_effects() {
void inlined() {
int local;
global = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global_backup' which will dangle}}
- global_backup = global;
+ global_backup = global; // expected-note {{global variable 'global' aliases the storage of local variable 'local'}}
global = nullptr;
}
diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp
index a7c62852c69892..95d33ad0a9c64e 100644
--- a/clang/test/Sema/LifetimeSafety/invalidations.cpp
+++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp
@@ -578,7 +578,8 @@ struct SinkOwnerBorrow {
struct SinkInteriorBorrow {
const char *dest_; // expected-note {{this field dangles}}
- SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter 'dest' escapes to the field 'dest_' and is later invalidated}}
+ SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter 'dest' escapes to the field 'dest_' and is later invalidated}} \
+ // expected-note {{result of call to 'data' aliases the storage of parameter 'dest'}}
if (n > 0)
dest->clear(); // expected-note {{parameter 'dest' is invalidated here}}
}
@@ -596,12 +597,16 @@ struct S {
void InvalidatedFieldLocalVector() {
std::vector<std::string> strings;
- FieldFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the field 'FieldFromLocalVector' and is later invalidated}}
+ FieldFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the field 'FieldFromLocalVector' and is later invalidated}} \
+ // expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \
+ // expected-note {{expression aliases the storage of local variable 'strings'}}
strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
}
void InvalidatedFieldByValueParamVector(std::vector<std::string> strings) {
- FieldFromByValueParamVector = *strings.begin(); // expected-warning {{parameter 'strings' escapes to the field 'FieldFromByValueParamVector' and is later invalidated}}
+ FieldFromByValueParamVector = *strings.begin(); // expected-warning {{parameter 'strings' escapes to the field 'FieldFromByValueParamVector' and is later invalidated}} \
+ // expected-note {{result of call to 'begin' aliases the storage of parameter 'strings'}} \
+ // expected-note {{expression aliases the storage of parameter 'strings'}}
strings.push_back("1"); // expected-note {{parameter 'strings' is invalidated here}}
}
@@ -623,7 +628,7 @@ struct S {
void InvalidatedFieldDelete() {
int *p = new int; // expected-warning {{allocated object escapes to the field 'FieldFromNew' and is later invalidated}}
- FieldFromNew = p;
+ FieldFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
delete p; // expected-note {{allocated object is freed here}}
}
@@ -656,7 +661,9 @@ struct S {
void InvalidatedGlobalLocalVector() {
std::vector<std::string> strings;
- GlobalFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the global variable 'GlobalFromLocalVector' and is later invalidated}}
+ GlobalFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the global variable 'GlobalFromLocalVector' and is later invalidated}} \
+ // expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \
+ // expected-note {{expression aliases the storage of local variable 'strings'}}
strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
}
@@ -672,7 +679,7 @@ void InvalidatedGlobalRefParamString(std::string &s) { // expected-warning {{par
void InvalidatedGlobalDelete() {
int *p = new int; // expected-warning {{allocated object escapes to the global variable 'GlobalFromNew' and is later invalidated}}
- GlobalFromNew = p;
+ GlobalFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
delete p; // expected-note {{allocated object is freed here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index addbe9213a0ac2..405312b35eff45 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -374,8 +374,10 @@ const char *trackThroughMultiplePointer() {
struct X {
X(std::unique_ptr<int> up) :
- pointee(*up), // cfg-warning {{may have been moved.}}
- pointee2(up.get()), // cfg-warning {{may have been moved.}}
+ pointee(*up), // cfg-warning {{may have been moved.}} \
+ // cfg-note {{expression aliases the storage of parameter 'up'}}
+ pointee2(up.get()), // cfg-warning {{may have been moved.}} \
+ // cfg-note {{result of call to 'get' aliases the storage of parameter 'up'}}
pointer(std::move(up)) {} // cfg-note 2 {{potentially moved here}}
int &pointee; // cfg-note {{this field dangles}}
int *pointee2; // cfg-note {{this field dangles}}
@@ -389,7 +391,8 @@ struct X2 {
// A common usage that moves the passing owner to the class.
// verify a strict warning on this case.
X2(XOwner owner) :
- pointee(owner.get()), // cfg-warning {{may have been moved.}}
+ pointee(owner.get()), // cfg-warning {{may have been moved.}} \
+ // cfg-note {{result of call to 'get' aliases the storage of parameter 'owner'}}
owner(std::move(owner)) {} // cfg-note {{potentially moved here}}
int* pointee; // cfg-note {{this field dangles}}
XOwner owner;
@@ -610,10 +613,12 @@ FooView test3(int i, std::optional<Foo> a) {
return *a; // expected-warning {{address of stack memory}} \
// cfg-note {{result of call to 'operator*' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}} \
// cfg-warning {{stack memory associated with parameter 'a' is returned}} \
+ // cfg-note {{expression aliases the storage of parameter 'a'}} \
// cfg-note {{returned here}}
return a.value(); // expected-warning {{address of stack memory}} \
// cfg-note {{result of call to 'value' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}} \
// cfg-warning {{stack memory associated with parameter 'a' is returned}} \
+ // cfg-note {{result of call to 'value' aliases the storage of parameter 'a'}} \
// cfg-note {{returned here}}
}
} // namespace GH93386
@@ -1197,7 +1202,8 @@ struct Foo2 {
};
struct Test {
- Test(Foo2 foo) : bar(foo.bar.get()), // cfg-warning-re {{stack memory associated with parameter 'foo' may escape to the field 'bar' which will dangle. {{.*}} may have been moved}}
+ Test(Foo2 foo) : bar(foo.bar.get()), // cfg-warning-re {{stack memory associated with parameter 'foo' may escape to the field 'bar' which will dangle. {{.*}} may have been moved}} \
+ // cfg-note {{result of call to 'get' aliases the storage of parameter 'foo'}}
storage(std::move(foo.bar)) {}; // cfg-note {{potentially moved here}}
Bar* bar; // cfg-note {{this field dangles}}
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c
index 8e6ca521c18046..ed0b9e21eb53ee 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -181,7 +181,8 @@ void *void_pointer_dereference(void) {
int *atomic_pointer_declref(void) {
int value;
_Atomic(int *) p = &value; // expected-warning {{stack memory associated with local variable 'value' is returned}}
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'value'}}
}
int *atomic_pointer_static(void) {
@@ -193,7 +194,8 @@ int *atomic_pointer_static(void) {
int **atomic_pointer_multilevel(void) {
int *inner;
_Atomic(int **) p = &inner; // expected-warning {{stack memory associated with local variable 'inner' is returned}}
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'inner'}}
}
// In C, a pointer compound assignment is a prvalue; its result still carries
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 18800830cb59f4..9ded44f6a720f6 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -530,7 +530,8 @@ View conditional_assign_both_branches(const MyObj& safe, bool c) {
else {
p = safe;
}
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 's'}}
}
@@ -538,7 +539,8 @@ View reassign_safe_to_local(const MyObj& safe) {
MyObj local;
View p = safe;
p = local; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'local'}}
}
View pointer_chain_to_local() {
@@ -554,11 +556,13 @@ View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) {
View p;
if (c1) {
p = local1; // expected-warning {{stack memory associated with local variable 'local1' is returned}}
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'local1'}}
}
else if (c2) {
p = local2; // expected-warning {{stack memory associated with local variable 'local2' is returned}}
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'local2'}}
}
p = safe;
return p;
@@ -577,7 +581,9 @@ View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) {
else {
p = safe;
}
- return p; // expected-note 2 {{returned here}}
+ return p; // expected-note 2 {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'local1'}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'local2'}}
}
View direct_return_of_local() {
@@ -645,7 +651,8 @@ const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
const int& return_pointer_to_parameter_via_reference_1(int a) {
const int* d = &a; // expected-warning {{stack memory associated with parameter 'a' is returned}}
- return *d; // expected-note {{returned here}}
+ return *d; // expected-note {{returned here}} \
+ // expected-note {{local variable 'd' aliases the storage of parameter 'a'}}
}
const int& get_ref_to_local() {
@@ -743,7 +750,8 @@ View uar_before_uaf(const MyObj& safe, bool c) {
MyObj local_obj;
p = local_obj; // expected-warning {{stack memory associated with local variable 'local_obj' is returned}}
if (c) {
- return p; // expected-note {{returned here}}
+ return p; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'local_obj'}}
}
}
p.use();
@@ -1973,7 +1981,8 @@ void bar() {
namespace DereferenceViews {
const MyObj& testDeref(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
- return *v; // expected-note {{returned here}}
+ return *v; // expected-note {{returned here}} \
+ // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}}
}
const MyObj* testDerefAddr(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
@@ -1982,7 +1991,8 @@ const MyObj* testDerefAddr(MyObj obj) {
}
const MyObj* testData(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
- return v.data(); // expected-note {{returned here}}
+ return v.data(); // expected-note {{returned here}} \
+ // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}}
}
const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
@@ -2293,7 +2303,8 @@ View test1(std::string a) {
View test2(std::string a) {
View b = View(a); // expected-warning {{stack memory associated with parameter 'a' is returned}}
- return b; // expected-note {{returned here}}
+ return b; // expected-note {{returned here}} \
+ // expected-note {{local variable 'b' aliases the storage of parameter 'a'}}
}
View test3(std::string a) {
@@ -2350,7 +2361,8 @@ namespace lambda_captures {
auto return_ref_capture() {
int local = 1;
auto lambda = [&local]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note {{returned here}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
}
void safe_ref_capture() {
@@ -2383,7 +2395,8 @@ auto capture_pointer_by_ref() {
MyObj obj;
MyObj* p = &obj;
auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- return lambda; // expected-note {{returned here}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
}
auto capture_multiple() {
@@ -2392,7 +2405,9 @@ auto capture_multiple() {
&a, // expected-warning {{stack memory associated with local variable 'a' is returned}}
&b // expected-warning {{stack memory associated with local variable 'b' is returned}}
]() { return a + b; };
- return lambda; // expected-note 2 {{returned here}}
+ return lambda; // expected-note 2 {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'a'}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'b'}}
}
auto capture_raw_pointer_by_value() {
@@ -2433,7 +2448,8 @@ auto return_copied_lambda() {
auto implicit_ref_capture() {
int local = 1;
auto lambda = [&]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note {{returned here}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
}
// TODO: Include the name of the variable in the diagnostic to improve
@@ -2442,7 +2458,9 @@ auto implicit_ref_capture() {
auto implicit_ref_capture_multiple() {
int local = 1, local2 = 2;
auto lambda = [&]() { return local + local2; }; // expected-warning {{stack memory associated with local variable 'local2' is returned}} expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note 2 {{returned here}}
+ return lambda; // expected-note 2 {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local2'}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
}
auto implicit_value_capture() {
@@ -2480,7 +2498,8 @@ auto capture_static_address_by_ref() {
static int local = 1;
int* p = &local;
auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- return lambda; // expected-note {{returned here}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
}
auto capture_multilevel_pointer() {
@@ -2885,7 +2904,8 @@ void from_template_instantiation() {
struct FieldInitFromLifetimebound {
S value; // expected-note {{this field dangles}}
- FieldInitFromLifetimebound() : value(getS(std::string("temp"))) {} // expected-warning {{stack memory associated with temporary object escapes to the field 'value' which will dangle}}
+ FieldInitFromLifetimebound() : value(getS(std::string("temp"))) {} // expected-warning {{stack memory associated with temporary object escapes to the field 'value' which will dangle}} \
+ // expected-note {{result of call to 'getS' aliases the storage of temporary object}}
};
S S::return_self_after_registration() const {
@@ -3117,7 +3137,8 @@ void binary_conditional_throw_void(int *in) {
int *binary_conditional_throw_dangling() {
int x;
int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
- return p ?: throw 1; // expected-note {{returned here}}
+ return p ?: throw 1; // expected-note {{returned here}} \
+ // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
}
#endif
@@ -3800,7 +3821,8 @@ std::function<void()> reassign_safe_then_unsafe() {
int local = 2;
std::function<void()> f = []() { (void)safe; };
f = [&local]() { (void)local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return f; // expected-note {{returned here}}
+ return f; // expected-note {{returned here}} \
+ // expected-note {{local variable 'f' aliases the storage of local variable 'local'}}
}
std::function<void()> reassign_unsafe_then_safe() {
@@ -3973,7 +3995,8 @@ View use_after_return_capture_by() {
MyObj a;
View res;
setCaptureBy(res, a); // expected-warning {{stack memory associated with local variable 'a' is returned}}
- return res; // expected-note {{returned here}}
+ return res; // expected-note {{returned here}} \
+ // expected-note {{local variable 'res' aliases the storage of local variable 'a'}}
}
>From 9ab795024831150873d8fe9c1aac2aa327fc1b43 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 1 Aug 2026 16:35:44 +0800
Subject: [PATCH 2/6] Align continuation comments
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
Assisted-by: deepseek-v4-flash
---
clang/test/Sema/LifetimeSafety/dangling-global.cpp | 2 +-
clang/test/Sema/LifetimeSafety/invalidations.cpp | 2 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang/test/Sema/LifetimeSafety/dangling-global.cpp b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
index 7a0e03594fff64..3681ae9fcab669 100644
--- a/clang/test/Sema/LifetimeSafety/dangling-global.cpp
+++ b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
@@ -26,7 +26,7 @@ void invoke_function_with_side_effects() {
// We can however catch the inlined one of course!
void inlined() {
int local;
- global = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global_backup' which will dangle}}
+ global = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global_backup' which will dangle}}
global_backup = global; // expected-note {{global variable 'global' aliases the storage of local variable 'local'}}
global = nullptr;
}
diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp
index 95d33ad0a9c64e..6affbed1958322 100644
--- a/clang/test/Sema/LifetimeSafety/invalidations.cpp
+++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp
@@ -678,7 +678,7 @@ void InvalidatedGlobalRefParamString(std::string &s) { // expected-warning {{par
}
void InvalidatedGlobalDelete() {
- int *p = new int; // expected-warning {{allocated object escapes to the global variable 'GlobalFromNew' and is later invalidated}}
+ int *p = new int; // expected-warning {{allocated object escapes to the global variable 'GlobalFromNew' and is later invalidated}}
GlobalFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
delete p; // expected-note {{allocated object is freed here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 9ded44f6a720f6..bfbe1e66f61190 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -651,8 +651,8 @@ const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
const int& return_pointer_to_parameter_via_reference_1(int a) {
const int* d = &a; // expected-warning {{stack memory associated with parameter 'a' is returned}}
- return *d; // expected-note {{returned here}} \
- // expected-note {{local variable 'd' aliases the storage of parameter 'a'}}
+ return *d; // expected-note {{returned here}} \
+ // expected-note {{local variable 'd' aliases the storage of parameter 'a'}}
}
const int& get_ref_to_local() {
@@ -3942,7 +3942,7 @@ void deref_use_after_scope() {
namespace GH191954 {
int* return_moved_pointer() {
int x;
- int* f = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ int* f = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
int* a;
a = std::move(f); // expected-note {{result of call to 'move<int *&>' aliases the storage of local variable 'x'}}
return a; // expected-note {{returned here}}
>From 312b2f5b135b860ee9795eddeba282bd252072a8 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sat, 22 Aug 2026 16:34:54 +0800
Subject: [PATCH 3/6] Rebase and resolve some conflicts
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/test/Sema/LifetimeSafety/capture-by.cpp | 2 +-
.../Sema/LifetimeSafety/explicit-object-param-no-crash.cpp | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/clang/test/Sema/LifetimeSafety/capture-by.cpp b/clang/test/Sema/LifetimeSafety/capture-by.cpp
index 99924bed821457..8331c6a5437d4a 100644
--- a/clang/test/Sema/LifetimeSafety/capture-by.cpp
+++ b/clang/test/Sema/LifetimeSafety/capture-by.cpp
@@ -196,7 +196,7 @@ void temporary_pointer_lifetimebound_capture() {
}
void temporary_nested_lifetimebound_capture() {
- capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer(
+ capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer( // cfg-note 2 {{result of call to 'getLifetimeBoundPointer' aliases the storage of temporary object because parameter 's' is lifetimebound}}
std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} \
// cfg-warning {{stack memory associated with temporary object escapes to the global variable 'x' which will dangle}}
}
diff --git a/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp b/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
index 31c07d9d7f1028..4437ba773ffffc 100644
--- a/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
+++ b/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
@@ -78,14 +78,16 @@ const int *t1(Holder h) {
const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}} \
// expected-note {{result of call to 'borrow' aliases the storage of parameter 'h' because the implicit object parameter is marked as lifetimebound}}
static_cast<Holder &&>(h).consume();
- return ptr; // expected-note {{returned here}}
+ return ptr; // expected-note {{returned here}} \
+ // expected-note {{local variable 'ptr' aliases the storage of parameter 'h'}}
}
const int *t2(Holder h) {
const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}} \
// expected-note {{result of call to 'borrow' aliases the storage of parameter 'h' because the implicit object parameter is marked as lifetimebound}}
std::move(h).consume();
- return ptr; // expected-note {{returned here}}
+ return ptr; // expected-note {{returned here}} \
+ // expected-note {{local variable 'ptr' aliases the storage of parameter 'h'}}
}
struct Set {
>From 54a3e2e7317cf56e6aae9254dea33ff6c8086893 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sun, 30 Aug 2026 10:55:18 +0800
Subject: [PATCH 4/6] Align something
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../LifetimeSafety/annotation-suggestions.cpp | 22 +++----
.../Sema/LifetimeSafety/invalidations.cpp | 10 ++--
clang/test/Sema/LifetimeSafety/safety.cpp | 60 +++++++++----------
3 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 3769710c489886..2ca3a94f41b7ac 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -528,8 +528,8 @@ struct CaptureRefToView {
CaptureRefToView test_ref_to_view() {
MyObj obj;
CaptureRefToView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct CaptureRefToPtr {
@@ -540,8 +540,8 @@ struct CaptureRefToPtr {
CaptureRefToPtr test_ref_to_ptr() {
MyObj obj;
CaptureRefToPtr x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct CaptureViewToView {
@@ -551,7 +551,7 @@ struct CaptureViewToView {
CaptureViewToView test_view_to_view() {
MyObj obj;
- View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
+ View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
CaptureViewToView x(v); // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
return x; // expected-note {{returned here}}
}
@@ -564,8 +564,8 @@ struct CapturePtrToPtr {
CapturePtrToPtr test_ptr_to_ptr() {
MyObj obj;
CapturePtrToPtr x(&obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct CaptureRefToRef {
@@ -576,8 +576,8 @@ struct CaptureRefToRef {
CaptureRefToRef test_ref_to_ref() {
MyObj obj;
CaptureRefToRef x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
struct BaseWithView {
@@ -592,8 +592,8 @@ struct CaptureRefToBaseView : BaseWithView {
CaptureRefToBaseView test_ref_to_base_view() {
MyObj obj;
CaptureRefToBaseView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}} \
+ // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
}
} // namespace capturing_constructor
diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp
index 6affbed1958322..af09676981d0db 100644
--- a/clang/test/Sema/LifetimeSafety/invalidations.cpp
+++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp
@@ -600,14 +600,14 @@ struct S {
FieldFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the field 'FieldFromLocalVector' and is later invalidated}} \
// expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \
// expected-note {{expression aliases the storage of local variable 'strings'}}
- strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
+ strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
}
void InvalidatedFieldByValueParamVector(std::vector<std::string> strings) {
FieldFromByValueParamVector = *strings.begin(); // expected-warning {{parameter 'strings' escapes to the field 'FieldFromByValueParamVector' and is later invalidated}} \
// expected-note {{result of call to 'begin' aliases the storage of parameter 'strings'}} \
// expected-note {{expression aliases the storage of parameter 'strings'}}
- strings.push_back("1"); // expected-note {{parameter 'strings' is invalidated here}}
+ strings.push_back("1"); // expected-note {{parameter 'strings' is invalidated here}}
}
void InvalidatedFieldLocalString() {
@@ -629,7 +629,7 @@ struct S {
void InvalidatedFieldDelete() {
int *p = new int; // expected-warning {{allocated object escapes to the field 'FieldFromNew' and is later invalidated}}
FieldFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
- delete p; // expected-note {{allocated object is freed here}}
+ delete p; // expected-note {{allocated object is freed here}}
}
void InvalidatedFieldDeleteParam(int *p) { // expected-warning {{parameter 'p' escapes to the field 'FieldFromPointerParam' and is later invalidated}}
@@ -664,7 +664,7 @@ void InvalidatedGlobalLocalVector() {
GlobalFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the global variable 'GlobalFromLocalVector' and is later invalidated}} \
// expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \
// expected-note {{expression aliases the storage of local variable 'strings'}}
- strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
+ strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
}
void InvalidatedGlobalByValueParamString(std::string s) {
@@ -680,7 +680,7 @@ void InvalidatedGlobalRefParamString(std::string &s) { // expected-warning {{par
void InvalidatedGlobalDelete() {
int *p = new int; // expected-warning {{allocated object escapes to the global variable 'GlobalFromNew' and is later invalidated}}
GlobalFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
- delete p; // expected-note {{allocated object is freed here}}
+ delete p; // expected-note {{allocated object is freed here}}
}
void InvalidatedGlobalDeleteParam(int *p) { // expected-warning {{parameter 'p' escapes to the global variable 'GlobalFromPointerParam' and is later invalidated}}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index bfbe1e66f61190..2abd81eb2b0bc8 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -1642,7 +1642,7 @@ View range_based_for_use_after_return() {
MyObjStorage s;
for (const MyObj &o : s) { // expected-warning {{stack memory associated with local variable 's' is returned}} \
// expected-note {{local variable '__range1' aliases the storage of local variable 's'}}
- return o; // expected-note {{returned here}}
+ return o; // expected-note {{returned here}}
}
return *s.begin(); // expected-warning {{stack memory associated with local variable 's' is returned}} \
// expected-note {{result of call to 'begin' aliases the storage of local variable 's' because the implicit object parameter is marked as lifetimebound}} \
@@ -1995,7 +1995,7 @@ const MyObj* testData(MyObj obj) {
// expected-note {{local variable 'v' aliases the storage of parameter 'obj'}}
}
const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
- View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
+ View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
const MyObj* ptr = v.data(); // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}} \
// expected-note {{result of call to 'data' aliases the storage of parameter 'obj' because the implicit object parameter is inferred as lifetimebound}}
return ptr->getData(); // expected-note {{returned here}}
@@ -2361,8 +2361,8 @@ namespace lambda_captures {
auto return_ref_capture() {
int local = 1;
auto lambda = [&local]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
}
void safe_ref_capture() {
@@ -2379,7 +2379,7 @@ auto capture_int_by_value() {
auto capture_view_by_value() {
MyObj obj;
- View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
+ View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
auto lambda = [v]() { return v; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
return lambda; // expected-note {{returned here}}
}
@@ -2395,8 +2395,8 @@ auto capture_pointer_by_ref() {
MyObj obj;
MyObj* p = &obj;
auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
}
auto capture_multiple() {
@@ -2412,28 +2412,28 @@ auto capture_multiple() {
auto capture_raw_pointer_by_value() {
int x;
- int* p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ int* p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
auto lambda = [p]() { return p; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
return lambda; // expected-note {{returned here}}
}
auto capture_raw_pointer_init_capture() {
int x;
- int* p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ int* p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
auto lambda = [q = p]() { return q; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
return lambda; // expected-note {{returned here}}
}
auto capture_view_init_capture() {
MyObj obj;
- View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
+ View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
auto lambda = [w = v]() { return w; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
return lambda; // expected-note {{returned here}}
}
auto capture_lambda() {
int x;
- auto inner = [&x]() { return x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ auto inner = [&x]() { return x; }; // expected-warning {{stack memory associated with local variable 'x' is returned}}
auto outer = [inner]() { return inner(); }; // expected-note {{local variable 'inner' aliases the storage of local variable 'x'}}
return outer; // expected-note {{returned here}}
}
@@ -2448,8 +2448,8 @@ auto return_copied_lambda() {
auto implicit_ref_capture() {
int local = 1;
auto lambda = [&]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
}
// TODO: Include the name of the variable in the diagnostic to improve
@@ -2458,14 +2458,14 @@ auto implicit_ref_capture() {
auto implicit_ref_capture_multiple() {
int local = 1, local2 = 2;
auto lambda = [&]() { return local + local2; }; // expected-warning {{stack memory associated with local variable 'local2' is returned}} expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note 2 {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local2'}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
+ return lambda; // expected-note 2 {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local2'}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
}
auto implicit_value_capture() {
MyObj obj;
- View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
+ View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
auto lambda = [=]() { return v; }; // expected-note {{local variable 'v' aliases the storage of local variable 'obj'}}
return lambda; // expected-note {{returned here}}
}
@@ -2498,15 +2498,15 @@ auto capture_static_address_by_ref() {
static int local = 1;
int* p = &local;
auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
+ return lambda; // expected-note {{returned here}} \
+ // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
}
auto capture_multilevel_pointer() {
int x;
- int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
- int **q = &p; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- int ***r = &q; // expected-warning {{stack memory associated with local variable 'q' is returned}}
+ int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ int **q = &p; // expected-warning {{stack memory associated with local variable 'p' is returned}}
+ int ***r = &q; // expected-warning {{stack memory associated with local variable 'q' is returned}}
auto lambda = [=]() { return *p + **q + ***r; }; // expected-note {{local variable 'p' aliases the storage of local variable 'x'}} \
// expected-note {{local variable 'r' aliases the storage of local variable 'q'}} \
// expected-note {{local variable 'q' aliases the storage of local variable 'p'}}
@@ -3136,7 +3136,7 @@ void binary_conditional_throw_void(int *in) {
int *binary_conditional_throw_dangling() {
int x;
- int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
return p ?: throw 1; // expected-note {{returned here}} \
// expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
}
@@ -3821,8 +3821,8 @@ std::function<void()> reassign_safe_then_unsafe() {
int local = 2;
std::function<void()> f = []() { (void)safe; };
f = [&local]() { (void)local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return f; // expected-note {{returned here}} \
- // expected-note {{local variable 'f' aliases the storage of local variable 'local'}}
+ return f; // expected-note {{returned here}} \
+ // expected-note {{local variable 'f' aliases the storage of local variable 'local'}}
}
std::function<void()> reassign_unsafe_then_safe() {
@@ -3942,10 +3942,10 @@ void deref_use_after_scope() {
namespace GH191954 {
int* return_moved_pointer() {
int x;
- int* f = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
+ int* f = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
int* a;
a = std::move(f); // expected-note {{result of call to 'move<int *&>' aliases the storage of local variable 'x'}}
- return a; // expected-note {{returned here}}
+ return a; // expected-note {{returned here}}
}
int* return_moved_pointer2() {
@@ -3957,7 +3957,7 @@ namespace GH191954 {
View return_moved_view() {
MyObj o;
- View v(o); // expected-warning {{stack memory associated with local variable 'o' is returned}}
+ View v(o); // expected-warning {{stack memory associated with local variable 'o' is returned}}
View v2 = std::move(v); // expected-note {{result of call to 'move<View &>' aliases the storage of local variable 'o'}}
return v2; // expected-note {{returned here}}
}
@@ -3995,8 +3995,8 @@ View use_after_return_capture_by() {
MyObj a;
View res;
setCaptureBy(res, a); // expected-warning {{stack memory associated with local variable 'a' is returned}}
- return res; // expected-note {{returned here}} \
- // expected-note {{local variable 'res' aliases the storage of local variable 'a'}}
+ return res; // expected-note {{returned here}} \
+ // expected-note {{local variable 'res' aliases the storage of local variable 'a'}}
}
>From 84f66d030285c2301eece2217f02119cfee52638 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sun, 18 Oct 2026 09:35:54 +0800
Subject: [PATCH 5/6] Rebase and resolve some conflicts
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
.../Analyses/LifetimeSafety/LoanPropagation.h | 6 --
.../LifetimeSafety/LoanPropagation.cpp | 9 +--
.../LifetimeSafety/annotation-suggestions.cpp | 18 ++----
clang/test/Sema/LifetimeSafety/capture-by.cpp | 2 +-
.../Sema/LifetimeSafety/dangling-global.cpp | 2 +-
.../explicit-object-param-no-crash.cpp | 6 +-
.../Sema/LifetimeSafety/invalidations.cpp | 13 ++--
clang/test/Sema/LifetimeSafety/nocfg.cpp | 4 +-
clang/test/Sema/LifetimeSafety/safety-c.c | 6 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 60 ++++++-------------
10 files changed, 39 insertions(+), 87 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index d8dffb4fc1a268..d410adb1caef03 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -57,12 +57,6 @@ class LoanPropagationAnalysis {
const LoanID TargetLoan,
const CFG *Cfg) const;
- /// Like the above, starting from the origin \p OEF lets escape. Empty if it
- /// does not hold \p TargetLoan.
- llvm::SmallVector<OriginID> buildOriginFlowChain(const OriginEscapesFact *OEF,
- const LoanID TargetLoan,
- const CFG *Cfg) const;
-
private:
class Impl;
std::unique_ptr<Impl> PImpl;
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 7db184a09a41e0..4f3ee8c7dab8a7 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -356,14 +356,7 @@ llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
if (const auto *UF = llvm::dyn_cast<UseFact>(F))
return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
if (const auto *OEF = llvm::dyn_cast<OriginEscapesFact>(F))
- return PImpl->buildOriginFlowChain(OEF, OEF->getEscapedOriginID(),
- TargetLoan, Cfg);
+ return PImpl->buildOriginFlowChain(OEF, TargetLoan, Cfg);
llvm_unreachable("Unhandled Fact type");
}
-llvm::SmallVector<OriginID>
-LoanPropagationAnalysis::buildOriginFlowChain(const OriginEscapesFact *OEF,
- const LoanID TargetLoan,
- const CFG *Cfg) const {
- return PImpl->buildOriginFlowChain(OEF, TargetLoan, Cfg);
-}
} // namespace clang::lifetimes::internal
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 2ca3a94f41b7ac..70ef67ceacd916 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -528,8 +528,7 @@ struct CaptureRefToView {
CaptureRefToView test_ref_to_view() {
MyObj obj;
CaptureRefToView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}}
}
struct CaptureRefToPtr {
@@ -540,8 +539,7 @@ struct CaptureRefToPtr {
CaptureRefToPtr test_ref_to_ptr() {
MyObj obj;
CaptureRefToPtr x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}}
}
struct CaptureViewToView {
@@ -564,8 +562,7 @@ struct CapturePtrToPtr {
CapturePtrToPtr test_ptr_to_ptr() {
MyObj obj;
CapturePtrToPtr x(&obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}}
}
struct CaptureRefToRef {
@@ -576,8 +573,7 @@ struct CaptureRefToRef {
CaptureRefToRef test_ref_to_ref() {
MyObj obj;
CaptureRefToRef x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}}
}
struct BaseWithView {
@@ -592,8 +588,7 @@ struct CaptureRefToBaseView : BaseWithView {
CaptureRefToBaseView test_ref_to_base_view() {
MyObj obj;
CaptureRefToBaseView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}}
}
} // namespace capturing_constructor
@@ -665,8 +660,7 @@ struct HasCtorField {
HasCtorField test_dangling_field_ctor() {
MyObj obj;
HasCtorField x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}}
- return x; // expected-note {{returned here}} \
- // expected-note {{local variable 'x' aliases the storage of local variable 'obj'}}
+ return x; // expected-note {{returned here}}
}
struct HasSetterField {
diff --git a/clang/test/Sema/LifetimeSafety/capture-by.cpp b/clang/test/Sema/LifetimeSafety/capture-by.cpp
index 8331c6a5437d4a..f6bf9d71d4c903 100644
--- a/clang/test/Sema/LifetimeSafety/capture-by.cpp
+++ b/clang/test/Sema/LifetimeSafety/capture-by.cpp
@@ -196,7 +196,7 @@ void temporary_pointer_lifetimebound_capture() {
}
void temporary_nested_lifetimebound_capture() {
- capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer( // cfg-note 2 {{result of call to 'getLifetimeBoundPointer' aliases the storage of temporary object because parameter 's' is lifetimebound}}
+ capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer( // cfg-note 2 {{result of call to 'getLifetimeBoundPointer' aliases the storage of temporary object because parameter 's' is marked as lifetimebound}}
std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} \
// cfg-warning {{stack memory associated with temporary object escapes to the global variable 'x' which will dangle}}
}
diff --git a/clang/test/Sema/LifetimeSafety/dangling-global.cpp b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
index 3681ae9fcab669..2013f9479a7842 100644
--- a/clang/test/Sema/LifetimeSafety/dangling-global.cpp
+++ b/clang/test/Sema/LifetimeSafety/dangling-global.cpp
@@ -27,7 +27,7 @@ void invoke_function_with_side_effects() {
void inlined() {
int local;
global = &local; // expected-warning {{stack memory associated with local variable 'local' escapes to the global variable 'global_backup' which will dangle}}
- global_backup = global; // expected-note {{global variable 'global' aliases the storage of local variable 'local'}}
+ global_backup = global;
global = nullptr;
}
diff --git a/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp b/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
index 4437ba773ffffc..31c07d9d7f1028 100644
--- a/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
+++ b/clang/test/Sema/LifetimeSafety/explicit-object-param-no-crash.cpp
@@ -78,16 +78,14 @@ const int *t1(Holder h) {
const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}} \
// expected-note {{result of call to 'borrow' aliases the storage of parameter 'h' because the implicit object parameter is marked as lifetimebound}}
static_cast<Holder &&>(h).consume();
- return ptr; // expected-note {{returned here}} \
- // expected-note {{local variable 'ptr' aliases the storage of parameter 'h'}}
+ return ptr; // expected-note {{returned here}}
}
const int *t2(Holder h) {
const int *ptr = h.borrow(); // expected-warning {{stack memory associated with parameter 'h' is returned}} \
// expected-note {{result of call to 'borrow' aliases the storage of parameter 'h' because the implicit object parameter is marked as lifetimebound}}
std::move(h).consume();
- return ptr; // expected-note {{returned here}} \
- // expected-note {{local variable 'ptr' aliases the storage of parameter 'h'}}
+ return ptr; // expected-note {{returned here}}
}
struct Set {
diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp
index af09676981d0db..3d9eb493cfdb43 100644
--- a/clang/test/Sema/LifetimeSafety/invalidations.cpp
+++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp
@@ -578,8 +578,7 @@ struct SinkOwnerBorrow {
struct SinkInteriorBorrow {
const char *dest_; // expected-note {{this field dangles}}
- SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter 'dest' escapes to the field 'dest_' and is later invalidated}} \
- // expected-note {{result of call to 'data' aliases the storage of parameter 'dest'}}
+ SinkInteriorBorrow(std::string *dest, int n) : dest_(dest->data()) { // expected-warning {{parameter 'dest' escapes to the field 'dest_' and is later invalidated}}
if (n > 0)
dest->clear(); // expected-note {{parameter 'dest' is invalidated here}}
}
@@ -599,14 +598,14 @@ struct S {
std::vector<std::string> strings;
FieldFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the field 'FieldFromLocalVector' and is later invalidated}} \
// expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \
- // expected-note {{expression aliases the storage of local variable 'strings'}}
+ // expected-note {{result of call to 'operator*' aliases the storage of local variable 'strings' because the implicit object parameter is inferred as lifetimebound}}
strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
}
void InvalidatedFieldByValueParamVector(std::vector<std::string> strings) {
FieldFromByValueParamVector = *strings.begin(); // expected-warning {{parameter 'strings' escapes to the field 'FieldFromByValueParamVector' and is later invalidated}} \
// expected-note {{result of call to 'begin' aliases the storage of parameter 'strings'}} \
- // expected-note {{expression aliases the storage of parameter 'strings'}}
+ // expected-note {{result of call to 'operator*' aliases the storage of parameter 'strings' because the implicit object parameter is inferred as lifetimebound}}
strings.push_back("1"); // expected-note {{parameter 'strings' is invalidated here}}
}
@@ -628,7 +627,7 @@ struct S {
void InvalidatedFieldDelete() {
int *p = new int; // expected-warning {{allocated object escapes to the field 'FieldFromNew' and is later invalidated}}
- FieldFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
+ FieldFromNew = p;
delete p; // expected-note {{allocated object is freed here}}
}
@@ -663,7 +662,7 @@ void InvalidatedGlobalLocalVector() {
std::vector<std::string> strings;
GlobalFromLocalVector = *strings.begin(); // expected-warning {{local variable 'strings' escapes to the global variable 'GlobalFromLocalVector' and is later invalidated}} \
// expected-note {{result of call to 'begin' aliases the storage of local variable 'strings'}} \
- // expected-note {{expression aliases the storage of local variable 'strings'}}
+ // expected-note {{result of call to 'operator*' aliases the storage of local variable 'strings' because the implicit object parameter is inferred as lifetimebound}}
strings.push_back("1"); // expected-note {{local variable 'strings' is invalidated here}}
}
@@ -679,7 +678,7 @@ void InvalidatedGlobalRefParamString(std::string &s) { // expected-warning {{par
void InvalidatedGlobalDelete() {
int *p = new int; // expected-warning {{allocated object escapes to the global variable 'GlobalFromNew' and is later invalidated}}
- GlobalFromNew = p; // expected-note {{local variable 'p' aliases the storage of allocated object}}
+ GlobalFromNew = p;
delete p; // expected-note {{allocated object is freed here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index 405312b35eff45..82aec5d3b279df 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -375,7 +375,7 @@ const char *trackThroughMultiplePointer() {
struct X {
X(std::unique_ptr<int> up) :
pointee(*up), // cfg-warning {{may have been moved.}} \
- // cfg-note {{expression aliases the storage of parameter 'up'}}
+ // cfg-note {{result of call to 'operator*' aliases the storage of parameter 'up' because the implicit object parameter is inferred as lifetimebound}}
pointee2(up.get()), // cfg-warning {{may have been moved.}} \
// cfg-note {{result of call to 'get' aliases the storage of parameter 'up'}}
pointer(std::move(up)) {} // cfg-note 2 {{potentially moved here}}
@@ -613,12 +613,10 @@ FooView test3(int i, std::optional<Foo> a) {
return *a; // expected-warning {{address of stack memory}} \
// cfg-note {{result of call to 'operator*' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}} \
// cfg-warning {{stack memory associated with parameter 'a' is returned}} \
- // cfg-note {{expression aliases the storage of parameter 'a'}} \
// cfg-note {{returned here}}
return a.value(); // expected-warning {{address of stack memory}} \
// cfg-note {{result of call to 'value' aliases the storage of parameter 'a' because the implicit object parameter is inferred as lifetimebound}} \
// cfg-warning {{stack memory associated with parameter 'a' is returned}} \
- // cfg-note {{result of call to 'value' aliases the storage of parameter 'a'}} \
// cfg-note {{returned here}}
}
} // namespace GH93386
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c
index ed0b9e21eb53ee..8e6ca521c18046 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -181,8 +181,7 @@ void *void_pointer_dereference(void) {
int *atomic_pointer_declref(void) {
int value;
_Atomic(int *) p = &value; // expected-warning {{stack memory associated with local variable 'value' is returned}}
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'value'}}
+ return p; // expected-note {{returned here}}
}
int *atomic_pointer_static(void) {
@@ -194,8 +193,7 @@ int *atomic_pointer_static(void) {
int **atomic_pointer_multilevel(void) {
int *inner;
_Atomic(int **) p = &inner; // expected-warning {{stack memory associated with local variable 'inner' is returned}}
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'inner'}}
+ return p; // expected-note {{returned here}}
}
// In C, a pointer compound assignment is a prvalue; its result still carries
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 2abd81eb2b0bc8..63f79324cd8696 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -530,8 +530,7 @@ View conditional_assign_both_branches(const MyObj& safe, bool c) {
else {
p = safe;
}
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 's'}}
+ return p; // expected-note {{returned here}}
}
@@ -539,8 +538,7 @@ View reassign_safe_to_local(const MyObj& safe) {
MyObj local;
View p = safe;
p = local; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'local'}}
+ return p; // expected-note {{returned here}}
}
View pointer_chain_to_local() {
@@ -556,13 +554,11 @@ View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) {
View p;
if (c1) {
p = local1; // expected-warning {{stack memory associated with local variable 'local1' is returned}}
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'local1'}}
+ return p; // expected-note {{returned here}}
}
else if (c2) {
p = local2; // expected-warning {{stack memory associated with local variable 'local2' is returned}}
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'local2'}}
+ return p; // expected-note {{returned here}}
}
p = safe;
return p;
@@ -581,9 +577,7 @@ View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) {
else {
p = safe;
}
- return p; // expected-note 2 {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'local1'}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'local2'}}
+ return p; // expected-note 2 {{returned here}}
}
View direct_return_of_local() {
@@ -651,8 +645,7 @@ const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
const int& return_pointer_to_parameter_via_reference_1(int a) {
const int* d = &a; // expected-warning {{stack memory associated with parameter 'a' is returned}}
- return *d; // expected-note {{returned here}} \
- // expected-note {{local variable 'd' aliases the storage of parameter 'a'}}
+ return *d; // expected-note {{returned here}}
}
const int& get_ref_to_local() {
@@ -750,8 +743,7 @@ View uar_before_uaf(const MyObj& safe, bool c) {
MyObj local_obj;
p = local_obj; // expected-warning {{stack memory associated with local variable 'local_obj' is returned}}
if (c) {
- return p; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'local_obj'}}
+ return p; // expected-note {{returned here}}
}
}
p.use();
@@ -1981,8 +1973,7 @@ void bar() {
namespace DereferenceViews {
const MyObj& testDeref(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
- return *v; // expected-note {{returned here}} \
- // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}}
+ return *v; // expected-note {{returned here}}
}
const MyObj* testDerefAddr(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
@@ -1991,8 +1982,7 @@ const MyObj* testDerefAddr(MyObj obj) {
}
const MyObj* testData(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
- return v.data(); // expected-note {{returned here}} \
- // expected-note {{local variable 'v' aliases the storage of parameter 'obj'}}
+ return v.data(); // expected-note {{returned here}}
}
const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
View v = obj; // expected-warning {{stack memory associated with parameter 'obj' is returned}}
@@ -2303,8 +2293,7 @@ View test1(std::string a) {
View test2(std::string a) {
View b = View(a); // expected-warning {{stack memory associated with parameter 'a' is returned}}
- return b; // expected-note {{returned here}} \
- // expected-note {{local variable 'b' aliases the storage of parameter 'a'}}
+ return b; // expected-note {{returned here}}
}
View test3(std::string a) {
@@ -2361,8 +2350,7 @@ namespace lambda_captures {
auto return_ref_capture() {
int local = 1;
auto lambda = [&local]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
+ return lambda; // expected-note {{returned here}}
}
void safe_ref_capture() {
@@ -2395,8 +2383,7 @@ auto capture_pointer_by_ref() {
MyObj obj;
MyObj* p = &obj;
auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
+ return lambda; // expected-note {{returned here}}
}
auto capture_multiple() {
@@ -2405,9 +2392,7 @@ auto capture_multiple() {
&a, // expected-warning {{stack memory associated with local variable 'a' is returned}}
&b // expected-warning {{stack memory associated with local variable 'b' is returned}}
]() { return a + b; };
- return lambda; // expected-note 2 {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'a'}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'b'}}
+ return lambda; // expected-note 2 {{returned here}}
}
auto capture_raw_pointer_by_value() {
@@ -2448,8 +2433,7 @@ auto return_copied_lambda() {
auto implicit_ref_capture() {
int local = 1;
auto lambda = [&]() { return local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
+ return lambda; // expected-note {{returned here}}
}
// TODO: Include the name of the variable in the diagnostic to improve
@@ -2458,9 +2442,7 @@ auto implicit_ref_capture() {
auto implicit_ref_capture_multiple() {
int local = 1, local2 = 2;
auto lambda = [&]() { return local + local2; }; // expected-warning {{stack memory associated with local variable 'local2' is returned}} expected-warning {{stack memory associated with local variable 'local' is returned}}
- return lambda; // expected-note 2 {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local2'}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'local'}}
+ return lambda; // expected-note 2 {{returned here}}
}
auto implicit_value_capture() {
@@ -2498,8 +2480,7 @@ auto capture_static_address_by_ref() {
static int local = 1;
int* p = &local;
auto lambda = [&p]() { return p; }; // expected-warning {{stack memory associated with local variable 'p' is returned}}
- return lambda; // expected-note {{returned here}} \
- // expected-note {{local variable 'lambda' aliases the storage of local variable 'p'}}
+ return lambda; // expected-note {{returned here}}
}
auto capture_multilevel_pointer() {
@@ -3137,8 +3118,7 @@ void binary_conditional_throw_void(int *in) {
int *binary_conditional_throw_dangling() {
int x;
int *p = &x; // expected-warning {{stack memory associated with local variable 'x' is returned}}
- return p ?: throw 1; // expected-note {{returned here}} \
- // expected-note {{local variable 'p' aliases the storage of local variable 'x'}}
+ return p ?: throw 1; // expected-note {{returned here}}
}
#endif
@@ -3821,8 +3801,7 @@ std::function<void()> reassign_safe_then_unsafe() {
int local = 2;
std::function<void()> f = []() { (void)safe; };
f = [&local]() { (void)local; }; // expected-warning {{stack memory associated with local variable 'local' is returned}}
- return f; // expected-note {{returned here}} \
- // expected-note {{local variable 'f' aliases the storage of local variable 'local'}}
+ return f; // expected-note {{returned here}}
}
std::function<void()> reassign_unsafe_then_safe() {
@@ -3995,8 +3974,7 @@ View use_after_return_capture_by() {
MyObj a;
View res;
setCaptureBy(res, a); // expected-warning {{stack memory associated with local variable 'a' is returned}}
- return res; // expected-note {{returned here}} \
- // expected-note {{local variable 'res' aliases the storage of local variable 'a'}}
+ return res; // expected-note {{returned here}}
}
>From ccfc7be69345c00d4d12cda70fa73fb4fee85a16 Mon Sep 17 00:00:00 2001
From: Yuan Suo <suoyuan666 at s5n.xyz>
Date: Sun, 18 Oct 2026 09:46:17 +0800
Subject: [PATCH 6/6] clang-format
Signed-off-by: Yuan Suo <suoyuan666 at s5n.xyz>
---
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index a5fdcf677484cd..9d8899eb5372a6 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -372,7 +372,8 @@ class LifetimeChecker {
if (const auto *Func = dyn_cast_if_present<FunctionDecl>(FD))
IsMain = Func->isMain();
SemaHelper->reportDanglingGlobal(IssueExpr, GlobalEscape->getGlobal(),
- MovedExpr, ExpiryLoc, ExprChain, IsMain);
+ MovedExpr, ExpiryLoc, ExprChain,
+ IsMain);
} else
llvm_unreachable("Unhandled OriginEscapesFact type");
} else
More information about the cfe-commits
mailing list