https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/196824
>From 412dab8b1d16689a7461b68b659927bf6757666f Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Sun, 10 May 2026 20:29:32 +0300 Subject: [PATCH 1/3] [LifetimeSafety] warn on declaration and implicit this --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 8 ++- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 13 +++-- clang/lib/Sema/SemaLifetimeSafety.h | 22 +++++++-- .../warn-lifetime-safety-lifetimebound.cpp | 49 +++++++++++++++++-- 5 files changed, 82 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 7ccf30ba14987..b4f918a4cb3e7 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -110,7 +110,13 @@ class LifetimeSafetySemaHelper { // Reports misuse of [[clang::lifetimebound]] when parameter doesn't escape // through return. - virtual void reportLifetimeboundViolation(const ParmVarDecl *VD) {} + virtual void + reportLifetimeboundViolation(const ParmVarDecl *ParmWithLifetimebound) {} + + // Reports misuse of [[clang::lifetimebound]] when implicit this parameter + // doesn't escape through return. + virtual void + reportLifetimeboundViolation(const CXXMethodDecl *MDWithLifetimebound) {} // Suggests lifetime bound annotations for implicit this. virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 879812f3de0d3..4374ca7235d03 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11007,8 +11007,8 @@ def warn_lifetime_safety_dangling_global_moved InGroup<LifetimeSafetyDanglingGlobalMoved>, DefaultIgnore; -def warn_lifetime_safety_param_lifetimebound_violation - : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'}0">, +def warn_lifetime_safety_lifetimebound_violation + : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'|an implicit this parameter}0">, InGroup<LifetimeSafetyLifetimeboundViolation>, DefaultIgnore; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index fc77ed3097602..f60448b23e6e8 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -60,7 +60,7 @@ class LifetimeChecker { llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap; llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap; llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap; - llvm::DenseSet<const ParmVarDecl *> VerifiedLiftimeboundEscapes; + llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes; const LoanPropagationAnalysis &LoanPropagation; const MovedLoansAnalysis &MovedLoans; const LiveOriginsAnalysis &LiveOrigins; @@ -147,9 +147,10 @@ class LifetimeChecker { // field! }; auto CheckImplicitThis = [&](const CXXMethodDecl *MD) { - if (!implicitObjectParamIsLifetimeBound(MD)) - if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) - AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); + if (implicitObjectParamIsLifetimeBound(MD)) + VerifiedLiftimeboundEscapes.insert(MD); + else if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) + AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); }; auto MovedAtEscape = MovedLoans.getMovedLoans(OEF); for (LoanID LID : EscapedLoans) { @@ -366,6 +367,10 @@ class LifetimeChecker { void reportLifetimeboundViolations() { if (!isa<FunctionDecl>(FD)) return; + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); + MD && implicitObjectParamIsLifetimeBound(MD) && + !VerifiedLiftimeboundEscapes.contains(MD)) + SemaHelper->reportLifetimeboundViolation(MD); for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) { if (!PVD->hasAttr<LifetimeBoundAttr>()) continue; diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 5b1cf41445399..91b1b96d40fb3 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -36,7 +36,7 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) { diag::warn_lifetime_safety_dangling_global, diag::warn_lifetime_safety_dangling_global_moved, diag::warn_lifetime_safety_noescape_escapes, - diag::warn_lifetime_safety_param_lifetimebound_violation, + diag::warn_lifetime_safety_lifetimebound_violation, }; for (unsigned DiagID : DiagIDs) if (!Diags.isIgnored(DiagID, D->getBeginLoc())) @@ -180,11 +180,25 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportLifetimeboundViolation( const ParmVarDecl *ParmWithLifetimebound) override { + const auto *Attr = ParmWithLifetimebound->getAttr<LifetimeBoundAttr>(); StringRef ParamName = ParmWithLifetimebound->getName(); bool HasName = ParamName.size() > 0; - S.Diag(ParmWithLifetimebound->getLocation(), - diag::warn_lifetime_safety_param_lifetimebound_violation) - << HasName << ParamName << ParmWithLifetimebound->getSourceRange(); + S.Diag(Attr->getLocation(), + diag::warn_lifetime_safety_lifetimebound_violation) + << HasName << ParamName << Attr->getRange(); + } + + void reportLifetimeboundViolation( + const CXXMethodDecl *MDWithLifetimebound) override { + const Stmt *Body = MDWithLifetimebound->getBody(); + assert(Body && "Expected a body"); + // FIXME: When #196549 lands, we can extract the attribute location and warn + // on it, for now warn on everything before the body. + S.Diag(MDWithLifetimebound->getLocation(), + diag::warn_lifetime_safety_lifetimebound_violation) + << 2 << "this" + << CharSourceRange::getCharRange(MDWithLifetimebound->getBeginLoc(), + Body->getBeginLoc()); } void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index 941a3bb8ce1e3..fb8f96623a5b2 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -81,9 +81,52 @@ View unnamed_lifetimebound_param( return View(); } -// FIXME: Should warn on declaration, not definiton -View annotated_decl_but_not_def_not_returned(const MyObj &obj [[clang::lifetimebound]]); +View annotated_decl_but_not_def_not_returned(const MyObj &obj [[clang::lifetimebound]]); // expected-warning {{could not verify that the return value can be lifetime bound to 'obj'}} -View annotated_decl_but_not_def_not_returned(const MyObj &obj) { // expected-warning {{could not verify that the return value can be lifetime bound to 'obj'}} +View annotated_decl_but_not_def_not_returned(const MyObj &obj) { return not_lb(obj); } + +struct BadThisReturn { + MyObj data; + + View get() const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + return not_lb(data); + } +}; + +struct GoodThisReturn { + MyObj data; + + View get() const [[clang::lifetimebound]] { + return data; + } +}; + +struct RedeclaredThis { + MyObj data; + View get() const [[clang::lifetimebound]]; +}; + +View RedeclaredThis::get() const { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + return not_lb(data); +} + +struct ThisAndParam { + MyObj data; + + View get(const MyObj &obj [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + return lb(obj); + } +}; + +struct ThisAndMixedParams { + MyObj data; + + View get( // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} + const MyObj &a [[clang::lifetimebound]], + const MyObj &b, + const MyObj &c [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to 'c'}} + return cond() ? lb(a) : not_lb(b); + } +}; >From 2da7668ba61f42d435744d3976a4498b26a136a1 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Sun, 10 May 2026 20:34:57 +0300 Subject: [PATCH 2/3] add FIXME --- clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index fb8f96623a5b2..04636ba70fc90 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -103,6 +103,7 @@ struct GoodThisReturn { } }; +// FIXME: Wrong warning loc struct RedeclaredThis { MyObj data; View get() const [[clang::lifetimebound]]; >From ea2e8ae4934789c38b08dbd9b211903bffd7e907 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 11 May 2026 12:51:18 +0300 Subject: [PATCH 3/3] remove this verification --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 5 --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 13 ++---- clang/lib/Sema/SemaLifetimeSafety.h | 17 +------ .../warn-lifetime-safety-lifetimebound.cpp | 45 ------------------- 5 files changed, 8 insertions(+), 76 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index b4f918a4cb3e7..7b0799d923f40 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -113,11 +113,6 @@ class LifetimeSafetySemaHelper { virtual void reportLifetimeboundViolation(const ParmVarDecl *ParmWithLifetimebound) {} - // Reports misuse of [[clang::lifetimebound]] when implicit this parameter - // doesn't escape through return. - virtual void - reportLifetimeboundViolation(const CXXMethodDecl *MDWithLifetimebound) {} - // Suggests lifetime bound annotations for implicit this. virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4374ca7235d03..879812f3de0d3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11007,8 +11007,8 @@ def warn_lifetime_safety_dangling_global_moved InGroup<LifetimeSafetyDanglingGlobalMoved>, DefaultIgnore; -def warn_lifetime_safety_lifetimebound_violation - : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'|an implicit this parameter}0">, +def warn_lifetime_safety_param_lifetimebound_violation + : Warning<"could not verify that the return value can be lifetime bound to %select{an unnamed parameter|'%1'}0">, InGroup<LifetimeSafetyLifetimeboundViolation>, DefaultIgnore; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index f60448b23e6e8..fc77ed3097602 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -60,7 +60,7 @@ class LifetimeChecker { llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap; llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap; llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap; - llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes; + llvm::DenseSet<const ParmVarDecl *> VerifiedLiftimeboundEscapes; const LoanPropagationAnalysis &LoanPropagation; const MovedLoansAnalysis &MovedLoans; const LiveOriginsAnalysis &LiveOrigins; @@ -147,10 +147,9 @@ class LifetimeChecker { // field! }; auto CheckImplicitThis = [&](const CXXMethodDecl *MD) { - if (implicitObjectParamIsLifetimeBound(MD)) - VerifiedLiftimeboundEscapes.insert(MD); - else if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) - AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); + if (!implicitObjectParamIsLifetimeBound(MD)) + if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) + AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); }; auto MovedAtEscape = MovedLoans.getMovedLoans(OEF); for (LoanID LID : EscapedLoans) { @@ -367,10 +366,6 @@ class LifetimeChecker { void reportLifetimeboundViolations() { if (!isa<FunctionDecl>(FD)) return; - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); - MD && implicitObjectParamIsLifetimeBound(MD) && - !VerifiedLiftimeboundEscapes.contains(MD)) - SemaHelper->reportLifetimeboundViolation(MD); for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) { if (!PVD->hasAttr<LifetimeBoundAttr>()) continue; diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 91b1b96d40fb3..1ef28d8ba2cee 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -36,7 +36,7 @@ inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) { diag::warn_lifetime_safety_dangling_global, diag::warn_lifetime_safety_dangling_global_moved, diag::warn_lifetime_safety_noescape_escapes, - diag::warn_lifetime_safety_lifetimebound_violation, + diag::warn_lifetime_safety_param_lifetimebound_violation, }; for (unsigned DiagID : DiagIDs) if (!Diags.isIgnored(DiagID, D->getBeginLoc())) @@ -184,23 +184,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { StringRef ParamName = ParmWithLifetimebound->getName(); bool HasName = ParamName.size() > 0; S.Diag(Attr->getLocation(), - diag::warn_lifetime_safety_lifetimebound_violation) + diag::warn_lifetime_safety_param_lifetimebound_violation) << HasName << ParamName << Attr->getRange(); } - void reportLifetimeboundViolation( - const CXXMethodDecl *MDWithLifetimebound) override { - const Stmt *Body = MDWithLifetimebound->getBody(); - assert(Body && "Expected a body"); - // FIXME: When #196549 lands, we can extract the attribute location and warn - // on it, for now warn on everything before the body. - S.Diag(MDWithLifetimebound->getLocation(), - diag::warn_lifetime_safety_lifetimebound_violation) - << 2 << "this" - << CharSourceRange::getCharRange(MDWithLifetimebound->getBeginLoc(), - Body->getBeginLoc()); - } - void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) override { diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp index 04636ba70fc90..5764647ca62e0 100644 --- a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -86,48 +86,3 @@ View annotated_decl_but_not_def_not_returned(const MyObj &obj [[clang::lifetimeb View annotated_decl_but_not_def_not_returned(const MyObj &obj) { return not_lb(obj); } - -struct BadThisReturn { - MyObj data; - - View get() const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} - return not_lb(data); - } -}; - -struct GoodThisReturn { - MyObj data; - - View get() const [[clang::lifetimebound]] { - return data; - } -}; - -// FIXME: Wrong warning loc -struct RedeclaredThis { - MyObj data; - View get() const [[clang::lifetimebound]]; -}; - -View RedeclaredThis::get() const { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} - return not_lb(data); -} - -struct ThisAndParam { - MyObj data; - - View get(const MyObj &obj [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} - return lb(obj); - } -}; - -struct ThisAndMixedParams { - MyObj data; - - View get( // expected-warning {{could not verify that the return value can be lifetime bound to an implicit this parameter}} - const MyObj &a [[clang::lifetimebound]], - const MyObj &b, - const MyObj &c [[clang::lifetimebound]]) const [[clang::lifetimebound]] { // expected-warning {{could not verify that the return value can be lifetime bound to 'c'}} - return cond() ? lb(a) : not_lb(b); - } -}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
