https://github.com/iitianpushkar created
https://github.com/llvm/llvm-project/pull/206337
## Summary
This improves Lifetime Safety alias-chain diagnostics by explaining when an
aliasing step comes from a `[[clang::lifetimebound]]` contract.
For example:
```cpp
int *identity(int *p [[clang::lifetimebound]]) {
return p;
}
void test() {
int *q;
{
int i;
q = identity(&i);
}
(void)*q;
}
```
Instead of only saying:
```text
result of call to 'identity' aliases the storage of local variable 'i'
```
the diagnostic can now say:
```
result of call to 'identity' aliases the storage of local variable 'i' because
parameter 'p' is marked [[clang::lifetimebound]]
```
## Changes
- Track optional lifetimebound provenance on origin-flow facts.
- Preserve origin-flow facts while building the diagnostic alias chain.
- Extend alias-chain entries with optional lifetimebound information.
- Emit a more specific note when the aliasing step is caused by:a
lifetimebound function parameter
- a lifetimebound implicit object parameter
- Update Lifetime Safety tests for the new diagnostic wording.
Addresses #199667
>From 0798fec97660d9bfa086d157ded8bf070e48b7c5 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 12:04:08 +0530
Subject: [PATCH 1/4] added note for lifetimebound mark
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7e20630708312..a024e4cebfdcc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11069,6 +11069,7 @@ def note_lifetime_safety_escapes_to_global_here:
Note<"escapes to this global st
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 note_lifetime_safety_aliases_storage_lifetimebound : Note<"%0 aliases the
storage of %1" because %2 is marked [[clang::lifetimebound]]">;
def warn_lifetime_safety_intra_tu_param_suggestion
: Warning<"parameter in intra-TU function should be marked "
>From b7dc4cdf3e6f8bdb92250f00e9c9940c5947a1d4 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 13:20:31 +0530
Subject: [PATCH 2/4] added lifetimeboundinfo struct
---
.../Analysis/Analyses/LifetimeSafety/Facts.h | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 5c671a93b149c..1e0ef8680f7db 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -131,24 +131,37 @@ class ExpireFact : public Fact {
};
class OriginFlowFact : public Fact {
+public:
+ struct LifetimeBoundInfo {
+ const Expr *Call = nullptr;
+ const ParmVarDecl *Param = nullptr;
+ bool isImplicitObject = false;
+ };
+
+private:
OriginID OIDDest;
OriginID OIDSrc;
// True if the destination origin should be killed (i.e., its current loans
// cleared) before the source origin's loans are flowed into it.
bool KillDest;
+ std::optional<LifetimeBoundInfo> LifetimeBound;
public:
static bool classof(const Fact *F) {
return F->getKind() == Kind::OriginFlow;
}
- OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
+ OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest,
+ std::optional<LifetimeBoundInfo> LifetimeBound = std::nullopt)
: Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
- KillDest(KillDest) {}
+ KillDest(KillDest), LifetimeBound(LifetimeBound) {}
OriginID getDestOriginID() const { return OIDDest; }
OriginID getSrcOriginID() const { return OIDSrc; }
bool getKillDest() const { return KillDest; }
+ std::optional<LifetimeBoundInfo> getLifetimeBoundInfo() const {
+ return LifetimeBound;
+ }
void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &OM) const override;
>From 84acd32e64085ef3dfd580658f222d97a9801240 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 18:18:09 +0530
Subject: [PATCH 3/4] added full flow to include lifetimebound reason in alias
note
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 9 +++-
.../Analyses/LifetimeSafety/LoanPropagation.h | 14 ++++++
.../clang/Basic/DiagnosticSemaKinds.td | 3 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 26 ++++++----
clang/lib/Analysis/LifetimeSafety/Facts.cpp | 2 +
.../LifetimeSafety/FactsGenerator.cpp | 39 +++++++++++----
.../LifetimeSafety/LoanPropagation.cpp | 46 ++++++++++++++++--
clang/lib/Sema/SemaLifetimeSafety.h | 47 +++++++++++++++----
8 files changed, 151 insertions(+), 35 deletions(-)
diff --git
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 80a23f18baebd..c177e9e222bdc 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -49,6 +49,12 @@ enum class WarningScope {
IntraTU // For warnings on functions local to a Translation Unit.
};
+struct LifetimeSafetyAliasChainEntry {
+ const Expr *E = nullptr;
+ const ParmVarDecl *LifetimeBoundParam = nullptr;
+ bool LifetimeBoundImplicitObject = false;
+};
+
/// Abstract interface for operations requiring Sema access.
///
/// This class exists to break a circular dependency: the LifetimeSafety
@@ -67,7 +73,8 @@ class LifetimeSafetySemaHelper {
virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr,
SourceLocation FreeLoc,
- llvm::ArrayRef<const Expr *> ExprChain) {}
+
llvm::ArrayRef<LifetimeSafetyAliasChainEntry>
+ AliasChain) {}
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 724c6eee7d3c2..92d69f99c8794 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -29,6 +29,11 @@ namespace clang::lifetimes::internal {
using LoanSet = llvm::ImmutableSet<LoanID>;
using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
+struct OriginFlowChainStep {
+ OriginID OID;
+ const OriginFlowFact *FlowFact;
+};
+
class LoanPropagationAnalysis {
public:
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager
&F,
@@ -51,6 +56,15 @@ class LoanPropagationAnalysis {
llvm::SmallVector<OriginID>
buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const;
+ llvm::SmallVector<OriginFlowChainStep>
+ buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+ const OriginID StartOID,
+ const LoanID TargetLoan) const;
+
+ llvm::SmallVector<OriginFlowChainStep>
+ buildOriginFlowChainWithFacts(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 a024e4cebfdcc..63d7b54b5e735 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11069,7 +11069,8 @@ def note_lifetime_safety_escapes_to_global_here:
Note<"escapes to this global st
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 note_lifetime_safety_aliases_storage_lifetimebound : Note<"%0 aliases the
storage of %1" because %2 is marked [[clang::lifetimebound]]">;
+def note_lifetime_safety_aliases_storage_lifetimebound : Note<
+ "%0 aliases the storage of %1 because %2 is marked
[[clang::lifetimebound]]">;
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 746ebbfb15c39..335ca28861f4f 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -272,7 +272,8 @@ class LifetimeChecker {
// Scope-based expiry (use-after-scope).
SemaHelper->reportUseAfterScope(
IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
- getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID)));
+ getAliasChain(
+ LoanPropagation.buildOriginFlowChainWithFacts(UF, LID)));
} else if (const auto *OEF =
CausingFact.dyn_cast<const OriginEscapesFact *>()) {
@@ -524,13 +525,22 @@ class LifetimeChecker {
/// 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);
+ llvm::SmallVector<LifetimeSafetyAliasChainEntry>
+ getAliasChain(llvm::ArrayRef<OriginFlowChainStep> OriginFlowChain) {
+ llvm::SmallVector<LifetimeSafetyAliasChainEntry> rs;
+ for (const OriginFlowChainStep &Step : OriginFlowChain) {
+ const Expr *CurrExpr =
FactMgr.getOriginMgr().getOrigin(Step.OID).getExpr();
+ if (!CurrExpr)
+ continue;
+
+ LifetimeSafetyAliasChainEntry Entry;
+ Entry.E = CurrExpr;
+ if (auto Info = Step.FlowFact->getLifetimeBoundInfo()) {
+ Entry.LifetimeBoundParam = Info->Param;
+ Entry.LifetimeBoundImplicitObject = Info->isImplicitObject;
+ }
+ rs.push_back(Entry);
+ }
return rs;
}
};
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..6c1fb7ba4e214 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -46,6 +46,8 @@ void OriginFlowFact::dump(llvm::raw_ostream &OS, const
LoanManager &,
OS << "\tSrc: ";
OM.dump(getSrcOriginID(), OS);
OS << (getKillDest() ? "" : ", Merge");
+ if (getLifetimeBoundInfo())
+ OS << ", LifetimeBound";
OS << "\n";
}
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 8358c69a5165a..dd6838630947e 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1085,28 +1085,46 @@ void FactsGenerator::handleFunctionCall(const Expr
*Call,
flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
return;
}
- auto IsArgLifetimeBound = [FD, &Args](unsigned I) -> bool {
+ auto GetLifetimeBoundInfo = [FD, Call](unsigned I)
+ -> std::optional<OriginFlowFact::LifetimeBoundInfo> {
const ParmVarDecl *PVD = nullptr;
if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
if (I == 0)
// For the 'this' argument, the attribute is on the method itself.
- return implicitObjectParamIsLifetimeBound(Method) ||
- shouldTrackImplicitObjectArg(
- *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
+ return implicitObjectParamIsLifetimeBound(Method)
+ ? std::optional<OriginFlowFact::LifetimeBoundInfo>(
+ OriginFlowFact::LifetimeBoundInfo{
+ Call, /*Param=*/nullptr,
+ /*isImplicitObject=*/true})
+ : std::nullopt;
if ((I - 1) < Method->getNumParams())
// For explicit arguments, find the corresponding parameter
// declaration.
PVD = Method->getParamDecl(I - 1);
+ } else if (I < FD->getNumParams()) {
+ // For free functions or static methods.
+ PVD = FD->getParamDecl(I);
+ }
+ if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
+ return OriginFlowFact::LifetimeBoundInfo{Call, PVD,
+ /*isImplicitObject=*/false};
+ return std::nullopt;
+ };
+ auto IsArgLifetimeBound = [FD, &Args, &GetLifetimeBoundInfo](unsigned I) {
+ if (GetLifetimeBoundInfo(I))
+ return true;
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+ Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
+ if (I == 0)
+ return shouldTrackImplicitObjectArg(
+ *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
} else if (I == 0 && shouldTrackFirstArgument(FD)) {
return true;
} else if (I == 1 && shouldTrackSecondArgument(FD)) {
return true;
- } else if (I < FD->getNumParams()) {
- // For free functions or static methods.
- PVD = FD->getParamDecl(I);
}
- return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
+ return false;
};
auto shouldTrackPointerImplicitObjectArg = [FD, &Args](unsigned I) -> bool {
const auto *Method = dyn_cast<CXXMethodDecl>(FD);
@@ -1148,7 +1166,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
// inner origins.
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
- KillSrc));
+ KillSrc, GetLifetimeBoundInfo(I)));
KillSrc = false;
}
} else if (shouldTrackPointerImplicitObjectArg(I)) {
@@ -1164,7 +1182,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
// pointer/reference itself must not outlive the arguments. This
// only constrains the top-level origin.
CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
- CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc));
+ CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc,
+ GetLifetimeBoundInfo(I)));
KillSrc = false;
}
}
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index a67b1b3c0f826..01ef3b67e7351 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -202,14 +202,15 @@ class AnalysisImpl
return getLoans(getState(P), OID);
}
- llvm::SmallVector<OriginID>
- buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
- const LoanID TargetLoan) const {
+ llvm::SmallVector<OriginFlowChainStep>
+ buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+ const OriginID StartOID,
+ const LoanID TargetLoan) const {
assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
"TargetLoan must be present in the StartOID at the StartPoint");
OriginID CurrOID = StartOID;
- llvm::SmallVector<OriginID> OriginFlowChain;
+ llvm::SmallVector<OriginFlowChainStep> OriginFlowChain;
llvm::ArrayRef<const Fact *> Facts =
FactMgr.getBlockContaining(StartPoint);
const auto *StartIt = llvm::find(Facts, StartPoint);
assert(StartIt != Facts.end());
@@ -231,7 +232,7 @@ class AnalysisImpl
const OriginID SrcOriginID = OFF->getSrcOriginID();
if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
continue;
- OriginFlowChain.push_back(SrcOriginID);
+ OriginFlowChain.push_back({SrcOriginID, OFF});
CurrOID = SrcOriginID;
}
@@ -243,6 +244,28 @@ class AnalysisImpl
return {};
}
+ llvm::SmallVector<OriginID>
+ buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID,
+ const LoanID TargetLoan) const {
+ llvm::SmallVector<OriginID> OriginFlowChain;
+ for (const OriginFlowChainStep &Step :
+ buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan))
+ OriginFlowChain.push_back(Step.OID);
+ return OriginFlowChain;
+ }
+
+ llvm::SmallVector<OriginFlowChainStep>
+ buildOriginFlowChainWithFacts(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 buildOriginFlowChainWithFacts(UF, Cur->getOuterOriginID(),
+ TargetLoan);
+
+ return {};
+ }
+
llvm::SmallVector<OriginID>
buildOriginFlowChain(const UseFact *UF, const LoanID TargetLoan) const {
for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
@@ -315,4 +338,17 @@ LoanPropagationAnalysis::buildOriginFlowChain(const
UseFact *UF,
const LoanID TargetLoan) const {
return PImpl->buildOriginFlowChain(UF, TargetLoan);
}
+
+llvm::SmallVector<OriginFlowChainStep>
+LoanPropagationAnalysis::buildOriginFlowChainWithFacts(
+ ProgramPoint StartPoint, const OriginID StartOID,
+ const LoanID TargetLoan) const {
+ return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID,
TargetLoan);
+}
+
+llvm::SmallVector<OriginFlowChainStep>
+LoanPropagationAnalysis::buildOriginFlowChainWithFacts(
+ const UseFact *UF, const LoanID TargetLoan) const {
+ return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan);
+}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h
b/clang/lib/Sema/SemaLifetimeSafety.h
index a3d546792fe41..3d8ca9e90abff 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -103,7 +103,8 @@ class LifetimeSafetySemaHelperImpl : public
LifetimeSafetySemaHelper {
void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
const Expr *MovedExpr, SourceLocation FreeLoc,
- llvm::ArrayRef<const Expr *> ExprChain) override {
+ llvm::ArrayRef<LifetimeSafetyAliasChainEntry>
+ AliasChain) override {
unsigned DiagID = MovedExpr
? diag::warn_lifetime_safety_use_after_scope_moved
: diag::warn_lifetime_safety_use_after_scope;
@@ -117,7 +118,7 @@ class LifetimeSafetySemaHelperImpl : public
LifetimeSafetySemaHelper {
S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here)
<< DestroyedSubject;
- reportAliasingChain(ExprChain);
+ reportAliasingChain(AliasChain);
S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
<< UseExpr->getSourceRange();
@@ -655,20 +656,46 @@ class LifetimeSafetySemaHelperImpl : public
LifetimeSafetySemaHelper {
return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
}
- void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
+ void reportAliasingChain(
+ llvm::ArrayRef<LifetimeSafetyAliasChainEntry> OriginExprChain) {
if (OriginExprChain.empty())
return;
- const Expr *LastExpr = OriginExprChain.back();
+ LifetimeSafetyAliasChainEntry Last = OriginExprChain.back();
+ const Expr *LastExpr = Last.E;
std::string IssueStr = getDiagSubjectDescription(LastExpr);
- for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
- if (!shouldShowInAliasChain(CurrExpr, LastExpr))
+ for (LifetimeSafetyAliasChainEntry Curr :
+ reverse(OriginExprChain.drop_back())) {
+ const Expr *CurrExpr = Curr.E;
+ if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
+ if (!Last.LifetimeBoundImplicitObject && !Last.LifetimeBoundParam &&
+ (Curr.LifetimeBoundImplicitObject || Curr.LifetimeBoundParam)) {
+ Last.LifetimeBoundImplicitObject = Curr.LifetimeBoundImplicitObject;
+ Last.LifetimeBoundParam = Curr.LifetimeBoundParam;
+ }
continue;
- S.Diag(CurrExpr->getBeginLoc(),
- diag::note_lifetime_safety_aliases_storage)
- << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
- << IssueStr;
+ }
+ if (Last.LifetimeBoundImplicitObject || Last.LifetimeBoundParam) {
+ std::string LifetimeBoundSubject = "the implicit object parameter";
+ if (Last.LifetimeBoundParam) {
+ LifetimeBoundSubject = "parameter ";
+ if (Last.LifetimeBoundParam->getIdentifier())
+ LifetimeBoundSubject +=
+ "'" + Last.LifetimeBoundParam->getNameAsString() + "'";
+ else
+ LifetimeBoundSubject += "'<unnamed>'";
+ }
+ S.Diag(CurrExpr->getBeginLoc(),
+ diag::note_lifetime_safety_aliases_storage_lifetimebound)
+ << CurrExpr->getSourceRange() <<
getDiagSubjectDescription(CurrExpr)
+ << IssueStr << LifetimeBoundSubject;
+ } else
+ S.Diag(CurrExpr->getBeginLoc(),
+ diag::note_lifetime_safety_aliases_storage)
+ << CurrExpr->getSourceRange() <<
getDiagSubjectDescription(CurrExpr)
+ << IssueStr;
+ Last = Curr;
LastExpr = CurrExpr;
}
}
>From 9208df06da6819083e24a7652de1fb0da06ecae1 Mon Sep 17 00:00:00 2001
From: iitianpushkar <[email protected]>
Date: Sun, 28 Jun 2026 18:33:51 +0530
Subject: [PATCH 4/4] updated tests to include lifetimebound reason in note
---
.../LifetimeSafety/annotation-suggestions.cpp | 12 +-
clang/test/Sema/LifetimeSafety/safety-c.c | 2 +-
clang/test/Sema/LifetimeSafety/safety.cpp | 109 +++++++++---------
3 files changed, 62 insertions(+), 61 deletions(-)
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 98c528516b8e7..f551f17d8f5e4 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -278,21 +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
{{temporary object does not live long enough}}.
// expected-note@-1
{{temporary object is destroyed here}}
- // expected-note@-2
{{result of call to 'get' aliases the storage of temporary object}}
+ // expected-note@-2
{{result of call to 'get' aliases the storage of temporary object because the
implicit object parameter is marked [[clang::lifetimebound]]}}
(void)s_ref; // expected-note {{later
used here}}
}
void test_get_on_temporary_ref() {
const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning
{{temporary object does not live long enough}}.
// expected-note@-1
{{temporary object is destroyed here}}
- // expected-note@-2
{{result of call to 'get' aliases the storage of temporary object}}
+ // expected-note@-2
{{result of call to 'get' aliases the storage of temporary object because the
implicit object parameter is marked [[clang::lifetimebound]]}}
(void)s_ref; // expected-note {{later
used here}}
}
void test_getView_on_temporary() {
View sv = ViewProvider{1}.getView(); // expected-warning {{temporary
object does not live long enough}}.
// expected-note@-1 {{temporary
object is destroyed here}}
- // expected-note@-2 {{result of
call to 'getView' aliases the storage of temporary object}}
+ // expected-note@-2 {{result of
call to 'getView' aliases the storage of temporary object because the implicit
object parameter is marked [[clang::lifetimebound]]}}
(void)sv; // expected-note {{later used
here}}
}
@@ -603,7 +603,7 @@ void uaf_via_inferred_lifetimebound() {
{
int local;
f = return_lambda_capturing_param(local); // expected-warning {{local
variable 'local' does not live long enough}} \
- // expected-note {{result of
call to 'return_lambda_capturing_param' aliases the storage of local variable
'local'}}
+ // expected-note {{result of
call to 'return_lambda_capturing_param' aliases the storage of local variable
'local' because parameter 'x' is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'local' is destroyed here}}
(void)f; // expected-note {{later used here}}
}
@@ -627,7 +627,7 @@ void test_inference() {
{
MyObj obj;
ptr = create_target(obj); // expected-warning {{local variable 'obj' does
not live long enough}} \
- // expected-note {{result of call to
'create_target' aliases the storage of local variable 'obj'}}
+ // expected-note {{result of call to
'create_target' aliases the storage of local variable 'obj' because parameter
'obj' is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'obj' is destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
@@ -642,7 +642,7 @@ View* MakeView(const MyObj& in) { // expected-warning
{{parameter in intra-TU fu
void test_new_allocation() {
View* v = MakeView(MyObj{}); // expected-warning {{temporary object does not
live long enough}} \
// expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call to 'MakeView'
aliases the storage of temporary object}}
+ // expected-note {{result of call to 'MakeView'
aliases the storage of temporary object because parameter 'in' is marked
[[clang::lifetimebound]]}}
(void)v; // expected-note {{later used here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c
b/clang/test/Sema/LifetimeSafety/safety-c.c
index e9443899c9935..61fc6c8d7dbb3 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -63,7 +63,7 @@ void lifetimebound_call(void) {
{
int i;
p = identity(&i); // expected-warning {{local variable 'i' does not live
long enough}} \
- // expected-note {{result of call to 'identity' aliases
the storage of local variable 'i'}}
+ // expected-note {{result of call to 'identity' aliases
the storage of local variable 'i' because parameter 'p' is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'i' is destroyed here}}
(void)*p; // expected-note {{later used here}}
}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp
b/clang/test/Sema/LifetimeSafety/safety.cpp
index 2cbf651eb46b5..95037b634228a 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -697,7 +697,7 @@ void test_lifetimebound_multi_level() {
int** pp = &p;
int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live
long enough}}
result = return_inner_ptr_addr(ppp); // expected-note {{local variable
'ppp' aliases the storage of local variable 'pp'}} \
- // expected-note {{result of call to
'return_inner_ptr_addr' aliases the storage of local variable 'pp'}}
+ // expected-note {{result of call to
'return_inner_ptr_addr' aliases the storage of local variable 'pp' because
parameter 'ppp' is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'pp' is destroyed
here}}
(void)**result; // expected-note {{used here}}
}
@@ -821,7 +821,7 @@ void lifetimebound_simple_function() {
{
MyObj obj;
v = Identity(obj); // expected-warning {{local variable 'obj' does not
live long enough}} \
- // expected-note {{result of call to 'Identity' aliases
the storage of local variable 'obj'}}
+ // expected-note {{result of call to 'Identity' aliases
the storage of local variable 'obj' because parameter 'v' is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'obj' is destroyed
here}}
v.use(); // expected-note {{later used here}}
}
@@ -830,8 +830,8 @@ void lifetimebound_multiple_args_definite() {
View v;
{
MyObj obj1, obj2;
- v = Choose(true, // expected-note {{result of call to 'Choose' aliases
the storage of local variable 'obj1'}} \
- // expected-note {{result of call to 'Choose' aliases
the storage of local variable 'obj2'}}
+ v = Choose(true, // expected-note {{result of call to 'Choose' aliases
the storage of local variable 'obj1' because parameter 'a' is marked
[[clang::lifetimebound]]}} \
+ // expected-note {{result of call to 'Choose' aliases
the storage of local variable 'obj2' because parameter 'b' is marked
[[clang::lifetimebound]]}}
obj1, // expected-warning {{local variable 'obj1' does not
live long enough}}
obj2); // expected-warning {{local variable 'obj2' does not
live long enough}}
} // expected-note {{local variable 'obj1' is
destroyed here}} \
@@ -860,7 +860,7 @@ void lifetimebound_mixed_args() {
{
MyObj obj1, obj2;
v = SelectFirst(obj1, // expected-warning {{local variable 'obj1'
does not live long enough}} \
- // expected-note {{result of call to
'SelectFirst' aliases the storage of local variable 'obj1'}}
+ // expected-note {{result of call to
'SelectFirst' aliases the storage of local variable 'obj1' because parameter
'a' is marked [[clang::lifetimebound]]}}
obj2);
} // expected-note {{local variable 'obj1' is
destroyed here}}
v.use(); // expected-note {{later used here}}
@@ -877,7 +877,7 @@ void lifetimebound_member_function() {
{
MyObj obj;
v = obj.getView(); // expected-warning {{local variable 'obj' does not
live long enough}} \
- // expected-note {{result of call to 'getView' aliases
the storage of local variable 'obj'}}
+ // expected-note {{result of call to 'getView' aliases
the storage of local variable 'obj' because the implicit object parameter is
marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'obj' is destroyed
here}}
v.use(); // expected-note {{later used here}}
}
@@ -902,7 +902,7 @@ void lifetimebound_chained_calls() {
{
MyObj obj;
v = Identity(Identity(Identity(obj))); // expected-warning {{local
variable 'obj' does not live long enough}} \
- // expected-note 3 {{result of call
to 'Identity' aliases the storage of local variable 'obj'}}
+ // expected-note 3 {{result of call
to 'Identity' aliases the storage of local variable 'obj' because parameter 'v'
is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable
'obj' is destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -912,7 +912,7 @@ void lifetimebound_with_pointers() {
{
MyObj obj;
ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not
live long enough}} \
- // expected-note {{result of call to 'GetPointer'
aliases the storage of local variable 'obj'}}
+ // expected-note {{result of call to 'GetPointer'
aliases the storage of local variable 'obj' because parameter 'obj' is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'obj' is
destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
@@ -922,7 +922,7 @@ void chained_assignment_lifetimebound_call() {
{
MyObj s;
p = Identity(obj = &s); // expected-warning {{does not live long enough}} \
- // expected-note {{result of call to 'Identity'
aliases the storage of local variable 's'}}
+ // expected-note {{result of call to 'Identity'
aliases the storage of local variable 's' because parameter 'v' is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 's' is destroyed
here}}
(void)*p; // expected-note {{later used here}}
}
@@ -955,7 +955,7 @@ void lifetimebound_return_reference() {
MyObj obj;
View temp_v = obj; // expected-warning {{local variable 'obj' does not
live long enough}}
const MyObj& ref = GetObject(temp_v); // expected-note {{local variable
'temp_v' aliases the storage of local variable 'obj'}} \
- // expected-note {{result of call to
'GetObject' aliases the storage of local variable 'obj'}}
+ // expected-note {{result of call to
'GetObject' aliases the storage of local variable 'obj' because parameter 'v'
is marked [[clang::lifetimebound]]}}
ptr = &ref;
} // expected-note {{local variable 'obj' is destroyed
here}}
(void)*ptr; // expected-note {{later used here}}
@@ -1010,7 +1010,7 @@ void lifetimebound_make_unique() {
{
MyObj obj;
ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local
variable 'obj' does not live long enough}} \
- // tu-note {{result of
call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local
variable 'obj'}}
+ // tu-note {{result of
call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local
variable 'obj' because parameter 'args' is marked [[clang::lifetimebound]]}}
} // tu-note {{local
variable 'obj' is destroyed here}}
(void)ptr; // tu-note {{later used
here}}
}
@@ -1028,7 +1028,7 @@ void non_lifetimebound_make_unique() {
void lifetimebound_make_unique_temp() {
std::unique_ptr<LifetimeBoundCtor> ptr =
std::make_unique<LifetimeBoundCtor>(MyObj()); // tu-warning {{temporary object
does not live long enough}} \
// tu-note {{temporary object is destroyed here}} \
-
// tu-note {{result of call to 'make_unique<LifetimeBoundCtor,
MyObj>' aliases the storage of temporary object}}
+
// tu-note {{result of call to 'make_unique<LifetimeBoundCtor,
MyObj>' aliases the storage of temporary object because parameter 'args' is
marked [[clang::lifetimebound]]}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1066,7 +1066,7 @@ void lifetimebound_make_unique_multi_params() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); //
tu-warning {{local variable 'obj_short' does not live long enough}} \
- //
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj
&>' aliases the storage of local variable 'obj_short'}}
+ //
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj
&>' aliases the storage of local variable 'obj_short' because parameter 'args'
is marked [[clang::lifetimebound]]}}
} // tu-note {{local variable 'obj_short' is destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1077,7 +1077,7 @@ void lifetimebound_make_unique_multi_params2() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); //
tu-warning {{local variable 'obj_short' does not live long enough}} \
- //
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj
&, int>' aliases the storage of local variable 'obj_short'}}
+ //
tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj
&, int>' aliases the storage of local variable 'obj_short' because parameter
'args' is marked [[clang::lifetimebound]]}}
} // tu-note {{local variable 'obj_short' is destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1098,7 +1098,7 @@ void lifetimebound_make_unique_multi_params3_1() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0);
// tu-warning {{local variable 'obj_short' does not live long enough}} \
-
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &,
MyObj &, double>' aliases the storage of local variable 'obj_short'}}
+
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &,
MyObj &, double>' aliases the storage of local variable 'obj_short' because
parameter 'args' is marked [[clang::lifetimebound]]}}
} // tu-note {{local variable 'obj_short' is destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1109,7 +1109,7 @@ void lifetimebound_make_unique_multi_params3_2() {
{
MyObj obj_short;
ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0);
// tu-warning {{local variable 'obj_short' does not live long enough}} \
-
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &,
MyObj &, double>' aliases the storage of local variable 'obj_short'}}
+
// tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &,
MyObj &, double>' aliases the storage of local variable 'obj_short' because
parameter 'args' is marked [[clang::lifetimebound]]}}
} // tu-note {{local variable 'obj_short' is destroyed here}}
(void)ptr; // tu-note {{later used here}}
}
@@ -1393,7 +1393,7 @@ void parentheses(bool cond) {
{
MyObj a;
p = ((GetPointer((a)))); // expected-warning {{local variable 'a' does
not live long enough}} \
- // expected-note {{result of call to
'GetPointer' aliases the storage of local variable 'a'}}
+ // expected-note {{result of call to
'GetPointer' aliases the storage of local variable 'a' because parameter 'obj'
is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'a' is
destroyed here}}
(void)*p; // expected-note {{later used here}}
@@ -1425,7 +1425,7 @@ void use_temporary_after_destruction() {
void passing_temporary_to_lifetime_bound_function() {
View a = construct_view(non_trivially_destructed_temporary()); //
expected-warning {{temporary object does not live long enough}} \
expected-note {{temporary object is destroyed here}} \
- expected-note {{result of call to 'construct_view' aliases the
storage of temporary object}}
+ expected-note {{result of call to 'construct_view' aliases the
storage of temporary object because parameter 'obj' is marked
[[clang::lifetimebound]]}}
use(a); // expected-note {{later used here}}
}
@@ -1467,7 +1467,7 @@ int **bit_cast_multilevel() {
namespace FullExprCleanupLoc {
void var_initializer() {
View v = non_trivially_destructed_temporary() // expected-warning
{{temporary object does not live long enough}} \
- // expected-note {{result of
call to 'getView' aliases the storage of temporary object}}
+ // expected-note {{result of
call to 'getView' aliases the storage of temporary object because the implicit
object parameter is marked [[clang::lifetimebound]]}}
.getView(); // expected-note {{temporary object is destroyed
here}}
v.use(); // expected-note {{later used here}}
}
@@ -1475,7 +1475,7 @@ void var_initializer() {
void expr_statement() {
View v;
v = non_trivially_destructed_temporary() // expected-warning {{temporary
object does not live long enough}} \
- // expected-note {{result of call
to 'getView' aliases the storage of temporary object}}
+ // expected-note {{result of call
to 'getView' aliases the storage of temporary object because the implicit
object parameter is marked [[clang::lifetimebound]]}}
.getView(); // expected-note {{temporary object is destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1519,7 +1519,7 @@ void foobar() {
{
StatusOr<MyObj> string_or = getStringOr();
view = string_or. // expected-warning {{local variable 'string_or' does
not live long enough}} \
- // expected-note {{result of call to 'value' aliases the
storage of local variable 'string_or'}}
+ // expected-note {{result of call to 'value' aliases the
storage of local variable 'string_or' because the implicit object parameter is
marked [[clang::lifetimebound]]}}
value();
} // expected-note {{local variable 'string_or' is
destroyed here}}
(void)view; // expected-note {{later used here}}
@@ -1594,7 +1594,7 @@ void test_user_defined_deref_uaf() {
MyObj obj;
SmartPtr<MyObj> smart_ptr(&obj);
p = &(*smart_ptr); // expected-warning {{local variable 'smart_ptr' does
not live long enough}} \
- // expected-note {{expression aliases the storage of
local variable 'smart_ptr'}}
+ // expected-note {{expression aliases the storage of
local variable 'smart_ptr' because the implicit object parameter is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'smart_ptr' is
destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1612,7 +1612,7 @@ void test_user_defined_deref_with_view() {
MyObj obj;
SmartPtr<MyObj> smart_ptr(&obj);
v = *smart_ptr; // expected-warning {{local variable 'smart_ptr' does not
live long enough}} \
- // expected-note {{expression aliases the storage of
local variable 'smart_ptr'}}
+ // expected-note {{expression aliases the storage of
local variable 'smart_ptr' because the implicit object parameter is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'smart_ptr' is
destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -1623,7 +1623,7 @@ 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}} \
- // expected-note {{expression aliases the
storage of local variable 'smart_ptr'}}
+ // expected-note {{expression aliases the
storage of local variable 'smart_ptr' because the implicit object parameter is
marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'smart_ptr'
is destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1634,7 +1634,7 @@ void test_user_defined_deref_chained() {
MyObj obj;
SmartPtr<SmartPtr<MyObj>> double_ptr;
p = &(**double_ptr); // expected-warning {{local variable 'double_ptr'
does not live long enough}} \
- // expected-note 2 {{expression aliases the storage
of local variable 'double_ptr'}}
+ // expected-note 2 {{expression aliases the storage
of local variable 'double_ptr' because the implicit object parameter is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'double_ptr' is
destroyed here}}
(void)*p; // expected-note {{later used here}}
}
@@ -1829,7 +1829,7 @@ void wrong_use_of_move_is_permissive() {
MyObj a;
p = std::move(a).getData(); // expected-warning {{local variable 'a' does
not live long enough}} \
// expected-note {{result of call to
'move<MyObj &>' aliases the storage of local variable 'a'}} \
- // expected-note {{result of call to 'getData'
aliases the storage of local variable 'a'}}
+ // expected-note {{result of call to 'getData'
aliases the storage of local variable 'a' because the implicit object parameter
is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'a' is destroyed here}}
(void)p; // expected-note {{later used here}}
}
@@ -1864,10 +1864,10 @@ void bar() {
{
S s;
x = s.x(); // expected-warning {{local variable 's' does not live long
enough}} \
- // expected-note {{result of call to 'x' aliases the
storage of local variable 's'}}
+ // expected-note {{result of call to 'x' aliases the
storage of local variable 's' because the implicit object parameter is marked
[[clang::lifetimebound]]}}
View y = S().x(); // expected-warning {{temporary object does not live
long enough}} \
expected-note {{temporary object is destroyed
here}} \
- expected-note {{result of call to 'x' aliases the
storage of temporary object}}
+ expected-note {{result of call to 'x' aliases the
storage of temporary object because the implicit object parameter is marked
[[clang::lifetimebound]]}}
(void)y; // expected-note {{used here}}
} // expected-note {{local variable 's' is destroyed here}}
(void)x; // expected-note {{used here}}
@@ -1956,19 +1956,19 @@ const S& identity(const S& in [[clang::lifetimebound]]);
void test_temporary() {
const std::string& x = S().x(); // expected-warning {{temporary object does
not live long enough}} expected-note {{temporary object is destroyed here}} \
- // expected-note {{result of call to 'x'
aliases the storage of temporary object}}
+ // expected-note {{result of call to 'x'
aliases the storage of temporary object because the implicit object parameter
is marked [[clang::lifetimebound]]}}
(void)x; // expected-note {{later used here}}
const std::string& y = identity(S().x()); // expected-warning {{temporary
object does not live long enough}} expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call
to 'x' aliases the storage of temporary object}} \
- // expected-note {{result of call
to 'identity' aliases the storage of temporary object}}
+ // expected-note {{result of call
to 'x' aliases the storage of temporary object because the implicit object
parameter is marked [[clang::lifetimebound]]}} \
+ // expected-note {{result of call
to 'identity' aliases the storage of temporary object because parameter 'in' is
marked [[clang::lifetimebound]]}}
(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}} \
- // expected-note {{result of call to 'x'
aliases the storage of local variable 's'}}
+ // expected-note {{result of call to 'x'
aliases the storage of local variable 's' because the implicit object parameter
is marked [[clang::lifetimebound]]}}
z = zz; // expected-note {{expression aliases the
storage of local variable 's'}}
} // expected-note {{local variable 's' is destroyed here}}
(void)z; // expected-note {{later used here}}
@@ -1978,13 +1978,13 @@ void test_lifetime_extension_ok() {
const S& x = S();
(void)x;
const S& y = identity(S()); // expected-warning {{temporary object does not
live long enough}} expected-note {{temporary object is destroyed here}} \
- // expected-note {{result of call to 'identity'
aliases the storage of temporary object}}
+ // expected-note {{result of call to 'identity'
aliases the storage of temporary object because parameter 'in' is marked
[[clang::lifetimebound]]}}
(void)y; // expected-note {{later used here}}
}
const std::string& test_return() {
const std::string& x = S().x(); // expected-warning {{temporary object does
not live long enough}} expected-note {{temporary object is destroyed here}} \
- // expected-note {{result of call to 'x'
aliases the storage of temporary object}}
+ // expected-note {{result of call to 'x'
aliases the storage of temporary object because the implicit object parameter
is marked [[clang::lifetimebound]]}}
return x; // expected-note {{later used here}}
}
} // namespace reference_type_decl_ref_expr
@@ -2165,11 +2165,11 @@ const T* MemberFuncsTpl<T>::memberC(const T& x
[[clang::lifetimebound]]) {
void test() {
MemberFuncsTpl<MyObj> mtf;
const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{temporary
object does not live long enough}} // expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call
to 'memberA' aliases the storage of temporary object}}
+ // expected-note {{result of call
to 'memberA' aliases the storage of temporary object because parameter 'x' is
marked [[clang::lifetimebound]]}}
const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{temporary object
does not live long enough}} // tu-note {{temporary object is destroyed here}} \
- // tu-note {{result of call to
'memberB' aliases the storage of temporary object}}
+ // tu-note {{result of call to
'memberB' aliases the storage of temporary object because parameter 'x' is
marked [[clang::lifetimebound]]}}
const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{temporary
object does not live long enough}} // expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call
to 'memberC' aliases the storage of temporary object}}
+ // expected-note {{result of call
to 'memberC' aliases the storage of temporary object because parameter 'x' is
marked [[clang::lifetimebound]]}}
(void)pTMA; // expected-note {{later used here}}
(void)pTMB; // tu-note {{later used here}}
(void)pTMC; // expected-note {{later used here}}
@@ -2217,7 +2217,7 @@ void test_optional_arrow_lifetimebound() {
std::optional<MyObj> opt;
v = opt->getView(); // expected-warning {{local variable 'opt' does not
live long enough}} \
// expected-note {{expression aliases the storage of
local variable 'opt'}} \
- // expected-note {{result of call to 'getView'
aliases the storage of local variable 'opt'}}
+ // expected-note {{result of call to 'getView'
aliases the storage of local variable 'opt' because the implicit object
parameter is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'opt' is destroyed
here}}
v.use(); // expected-note {{later used here}}
}
@@ -2580,7 +2580,8 @@ struct S {
void indexing_with_static_operator() {
S()(1, 2);
- S& x = S()("1", // expected-note 2 {{expression aliases the storage of
temporary object}}
+ S& x = S()("1", // expected-note {{expression aliases the storage of
temporary object because parameter 'a' is marked [[clang::lifetimebound]]}} \
+ // expected-note {{expression aliases the storage of
temporary object because parameter 'b' is marked [[clang::lifetimebound]]}}
2, // expected-warning {{temporary object does not live long
enough}}
3); // expected-warning {{temporary object does not live long
enough}} expected-note 2 {{temporary object is destroyed here}}
@@ -2606,7 +2607,7 @@ S getS(const std::string &s [[clang::lifetimebound]]);
void from_free_function() {
S s = getS(std::string("temp")); // expected-warning {{temporary object does
not live long enough}} \
// expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call to 'getS'
aliases the storage of temporary object}}
+ // expected-note {{result of call to 'getS'
aliases the storage of temporary object because parameter 's' is marked
[[clang::lifetimebound]]}}
use(s); // expected-note {{later used here}}
}
@@ -2626,14 +2627,14 @@ void from_method() {
Factory f;
S s = f.make(std::string("temp")); // expected-warning {{temporary object
does not live long enough}} \
// expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call to
'make' aliases the storage of temporary object}}
+ // expected-note {{result of call to
'make' aliases the storage of temporary object because parameter 's' is marked
[[clang::lifetimebound]]}}
use(s); // expected-note {{later used here}}
}
void from_static_method() {
S s = Factory::create(std::string("temp")); // expected-warning {{temporary
object does not live long enough}} \
// expected-note {{temporary
object is destroyed here}} \
- // expected-note {{result of
call to 'create' aliases the storage of temporary object}}
+ // expected-note {{result of
call to 'create' aliases the storage of temporary object because parameter 's'
is marked [[clang::lifetimebound]]}}
use(s); // expected-note {{later used
here}}
}
@@ -2642,7 +2643,7 @@ void from_lifetimebound_this_method() {
{
Factory f;
value = f.makeThis(); // expected-warning {{local variable 'f' does not
live long enough}} \
- // expected-note {{result of call to 'makeThis'
aliases the storage of local variable 'f'}}
+ // expected-note {{result of call to 'makeThis'
aliases the storage of local variable 'f' because the implicit object parameter
is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'f' is destroyed
here}}
use(value); // expected-note {{later used here}}
}
@@ -2652,7 +2653,7 @@ void across_scope() {
{
std::string str{"abc"};
s = getS(str); // expected-warning {{local variable 'str' does not live
long enough}} \
- // expected-note {{result of call to 'getS' aliases the
storage of local variable 'str'}}
+ // expected-note {{result of call to 'getS' aliases the
storage of local variable 'str' because parameter 's' is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'str' is destroyed here}}
use(s); // expected-note {{later used here}}
}
@@ -2675,7 +2676,7 @@ void assignment_propagation() {
{
std::string str{"abc"};
a = getS(str); // expected-warning {{local variable 'str' does not live
long enough}} \
- // expected-note {{result of call to 'getS' aliases the
storage of local variable 'str'}}
+ // expected-note {{result of call to 'getS' aliases the
storage of local variable 'str' because parameter 's' is marked
[[clang::lifetimebound]]}}
b = a; // expected-note {{local variable 'a' aliases the storage
of local variable 'str'}}
} // expected-note {{local variable 'str' is destroyed here}}
use(b); // expected-note {{later used here}}
@@ -2686,7 +2687,7 @@ void chained_defaulted_assignment_propagation() {
{
std::string str{"abc"};
S a = getS(str); // expected-warning {{local variable 'str' does not live
long enough}} \
- // expected-note {{result of call to 'getS' aliases the
storage of local variable 'str'}}
+ // expected-note {{result of call to 'getS' aliases the
storage of local variable 'str' because parameter 's' is marked
[[clang::lifetimebound]]}}
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 {{local variable 'str' is destroyed
here}}
@@ -2703,7 +2704,7 @@ void no_annotation() {
void mix_annotated_and_not() {
S s1 = getS(std::string("temp")); // expected-warning {{temporary object
does not live long enough}} \
// expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call to
'getS' aliases the storage of temporary object}}
+ // expected-note {{result of call to
'getS' aliases the storage of temporary object because parameter 's' is marked
[[clang::lifetimebound]]}}
S s2 = getSNoAnnotation(std::string("temp"));
use(s1); // expected-note {{later used here}}
use(s2);
@@ -2715,7 +2716,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 {{temporary
object does not live long enough}} \
- // expected-note {{result of call to
'getS2' aliases the storage of temporary object}} \
+ // expected-note {{result of call to
'getS2' aliases the storage of temporary object because parameter 'b' is marked
[[clang::lifetimebound]]}} \
// expected-note {{temporary object
is destroyed here}}
return s; // expected-note {{returned here}} \
// expected-note {{later used here}}
@@ -2736,7 +2737,7 @@ T make(const std::string &s [[clang::lifetimebound]]);
void from_template_instantiation() {
S s = make<S>(std::string("temp")); // expected-warning {{temporary object
does not live long enough}} \
// expected-note {{temporary object is
destroyed here}} \
- // expected-note {{result of call to
'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of
temporary object}}
+ // expected-note {{result of call to
'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of
temporary object because parameter 's' is marked [[clang::lifetimebound]]}}
use(s); // expected-note {{later used here}}
}
@@ -2800,7 +2801,7 @@ SAlias getSAlias(const std::string &s
[[clang::lifetimebound]]);
void from_typedef_return() {
SAlias s = getSAlias(std::string("temp")); // expected-warning {{temporary
object does not live long enough}} \
// expected-note {{temporary
object is destroyed here}} \
- // expected-note {{result of call
to 'getSAlias' aliases the storage of temporary object}}
+ // expected-note {{result of call
to 'getSAlias' aliases the storage of temporary object because parameter 's' is
marked [[clang::lifetimebound]]}}
use(s); // expected-note {{later used
here}}
}
@@ -2875,7 +2876,7 @@ 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 {{temporary
object does not live long enough}} \
// expected-note {{temporary
object is destroyed here}} \
- // expected-note {{result of
call to 'getUniqueS' aliases the storage of temporary object}}
+ // expected-note {{result of
call to 'getUniqueS' aliases the storage of temporary object because parameter
's' is marked [[clang::lifetimebound]]}}
(void)ptr; // expected-note {{later used
here}}
}
@@ -2892,7 +2893,7 @@ void owner_outlives_lifetimebound_source() {
{
std::string local;
ups = getUniqueS(local); // expected-warning {{local variable 'local' does
not live long enough}} \
- // expected-note {{result of call to 'getUniqueS'
aliases the storage of local variable 'local'}}
+ // expected-note {{result of call to 'getUniqueS'
aliases the storage of local variable 'local' because parameter 's' is marked
[[clang::lifetimebound]]}}
} // expected-note {{local variable 'local' is destroyed here}}
(void)ups; // expected-note {{later used here}}
}
@@ -2927,7 +2928,7 @@ void nested_local_pointer() {
{
Bar v;
p = Pointer(v); // expected-warning {{local variable 'v' does not live
long enough}}
- pp = Pointer(p); // expected-note {{local variable 'p' aliases the
storage of local variable 'v'}}
+ pp = Pointer(p); // expected-note {{local variable 'p' aliases the
storage of local variable 'v' because parameter 'bar' is marked
[[clang::lifetimebound]]}}
ppp = Pointer(pp); // expected-note {{local variable 'pp' aliases the
storage of local variable 'v'}}
} // expected-note {{local variable 'v' is destroyed
here}}
use(***ppp); // expected-note {{later used here}}
@@ -3685,7 +3686,7 @@ void uaf_via_lifetimebound() {
{
int local;
f = capture_lifetimebound_param(local); // expected-warning {{local
variable 'local' does not live long enough}} \
- // expected-note {{result of call
to 'capture_lifetimebound_param' aliases the storage of local variable 'local'}}
+ // expected-note {{result of call
to 'capture_lifetimebound_param' aliases the storage of local variable 'local'
because parameter 'x' is marked [[clang::lifetimebound]]}}
} // expected-note {{local variable 'local' is destroyed here}}
(void)f; // expected-note {{later used here}}
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits