https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/196144
>From 61b80f15e64db8cdbc4ef6cc813d8d2af8cb60d8 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Sun, 3 May 2026 21:31:39 +0300 Subject: [PATCH 1/6] basic impl --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 2 ++ clang/include/clang/Basic/DiagnosticGroups.td | 9 ++++++++- clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 +++++ clang/lib/Analysis/LifetimeSafety/Checker.cpp | 15 +++++++++++++++ clang/lib/Sema/SemaLifetimeSafety.h | 7 +++++++ clang/test/Sema/warn-lifetime-safety.cpp | 4 ++-- 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index d20ac87a7c8d9..6e352ee50e011 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -108,6 +108,8 @@ class LifetimeSafetySemaHelper { virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const VarDecl *EscapeGlobal) {} + virtual void reportLifetimeboundViolation(const ParmVarDecl *VD) {} + // Suggests lifetime bound annotations for implicit this. virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 2b3055d6d6bdd..b454b68a7f5ac 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -624,12 +624,19 @@ Warning to detect invalidation of references. }]; } +def LifetimeSafetyLifetimeboundViolation : DiagGroup<"lifetime-safety-lifetimebound-violation"> { + code Documentation = [{ +Warning to detect lifetimebound violations introduced by marking parameter as lifetimebound but not actually returning it. + }]; +} + def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive", [LifetimeSafetyUseAfterScope, LifetimeSafetyReturnStackAddr, LifetimeSafetyDanglingField, LifetimeSafetyDanglingGlobal, - LifetimeSafetyUseAfterFree]>; + LifetimeSafetyUseAfterFree, + LifetimeSafetyLifetimeboundViolation]>; def LifetimeSafetyStrict : DiagGroup<"lifetime-safety-strict", [LifetimeSafetyPermissive, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1302c4296885b..40409c0174470 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11003,6 +11003,11 @@ def warn_lifetime_safety_dangling_global_moved InGroup<LifetimeSafetyDanglingGlobalMoved>, DefaultIgnore; +def warn_lifetime_safety_param_lifetimebound_violation + : Warning<"parameter is marked as [[clang::lifetimebound]] but doesn't escape">, + InGroup<LifetimeSafetyLifetimeboundViolation>, + DefaultIgnore; + def note_lifetime_safety_used_here : Note<"later used here">; def note_lifetime_safety_invalidated_here : Note<"invalidated here">; def note_lifetime_safety_destroyed_here : Note<"destroyed here">; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 4ae90cf751ec3..581e469d0e507 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -60,6 +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; const LoanPropagationAnalysis &LoanPropagation; const MovedLoansAnalysis &MovedLoans; const LiveOriginsAnalysis &LiveOrigins; @@ -101,6 +102,7 @@ class LifetimeChecker { issuePendingWarnings(); suggestAnnotations(); reportNoescapeViolations(); + reportLifetimeboundViolations(); // Annotation inference is currently guarded by a frontend flag. In the // future, this might be replaced by a design that differentiates between // explicit and inferred findings with separate warning groups. @@ -137,6 +139,8 @@ class LifetimeChecker { else if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF); FieldEsc && isa<CXXConstructorDecl>(FD)) AnnotationWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl()); + } else { + VerifiedLiftimeboundEscapes.insert(PVD); } // TODO: Suggest lifetime_capture_by(this) for parameter escaping to a // field! @@ -358,6 +362,17 @@ class LifetimeChecker { } } + void reportLifetimeboundViolations() { + if (!isa<FunctionDecl>(FD)) + return; + for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) { + if (!PVD->hasAttr<LifetimeBoundAttr>()) + return; + if (!PVD->getAttr<LifetimeBoundAttr>()->isImplicit() && + !VerifiedLiftimeboundEscapes.contains(PVD)) + SemaHelper->reportLifetimeboundViolation(PVD); + } + } void inferAnnotations() { for (auto [Target, EscapeTarget] : AnnotationWarningsMap) { if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 92e7b5cf14ae5..e900caadf1072 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -172,6 +172,13 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { << EscapeField->getSourceRange(); } + void reportLifetimeboundViolation( + const ParmVarDecl *ParmWithLifetimebound) override { + S.Diag(ParmWithLifetimebound->getLocation(), + diag::warn_lifetime_safety_param_lifetimebound_violation) + << ParmWithLifetimebound->getSourceRange(); + } + void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) override { diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 30b450c333fbd..7cea7ba607f65 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -3269,7 +3269,7 @@ void uaf_via_lifetimebound() { namespace GH126600 { struct [[gsl::Pointer]] function_ref { template <typename Callable> - function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {} + function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {} // expected-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} void (*ref)(); }; @@ -3279,7 +3279,7 @@ struct [[gsl::Pointer]] function_ref { // avoid this warning for non-capturing lambdas. void assign_non_capturing_to_function_ref(function_ref &r) { r = []() {}; // expected-warning {{object whose reference is captured does not live long enough}} \ - // expected-note {{destroyed here}} + // expected-note {{destroyed here}} function-note {{requested here}} (void)r; // expected-note {{later used here}} } >From 9f78214e1528b634cbd8c3b21fe288fb75e19797 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 4 May 2026 16:47:28 +0300 Subject: [PATCH 2/6] cleanup code --- 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 581e469d0e507..a8f8a6ede4b6d 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -367,12 +367,13 @@ class LifetimeChecker { return; for (const ParmVarDecl *PVD : cast<FunctionDecl>(FD)->parameters()) { if (!PVD->hasAttr<LifetimeBoundAttr>()) - return; + continue; if (!PVD->getAttr<LifetimeBoundAttr>()->isImplicit() && !VerifiedLiftimeboundEscapes.contains(PVD)) SemaHelper->reportLifetimeboundViolation(PVD); } } + void inferAnnotations() { for (auto [Target, EscapeTarget] : AnnotationWarningsMap) { if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { >From fc1bb8d414bd2ce96fdff73571a454b2c0d18fb6 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 4 May 2026 22:10:50 +0300 Subject: [PATCH 3/6] add tests --- clang/test/Sema/warn-lifetime-safety.cpp | 72 ++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 7cea7ba607f65..5aaddafec6d07 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -3284,3 +3284,75 @@ void assign_non_capturing_to_function_ref(function_ref &r) { } } // namespace GH126600 + +namespace LifetimeboundReturnVerification { + +bool cond(); + +View drop_lb(const MyObj &obj) { return obj; } + +View keep_lb(const MyObj &obj [[clang::lifetimebound]]) { + return obj; +} + +View return_through_unannotated_passthrough( + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + return drop_lb(obj); +} + +View return_through_lifetimebound_passthrough( + const MyObj &obj [[clang::lifetimebound]]) { + return keep_lb(obj); +} + +View keep_lb2(const MyObj &obj [[clang::lifetimebound]]) { + return keep_lb(obj); +} + +View lose_lb(const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + return drop_lb(obj); +} + +View return_through_alias(const MyObj &obj [[clang::lifetimebound]]) { + const MyObj &alias = obj; + return alias; +} + +View return_alias_through_unannotated_passthrough( + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &alias = obj; + return drop_lb(alias); +} + +View return_through_two_lifetimebound_calls( + const MyObj &obj [[clang::lifetimebound]]) { + return keep_lb2(obj); +} + +View return_through_broken_chain( + const MyObj &obj [[clang::lifetimebound]]) { + return lose_lb(obj); +} + +View fwd_view(View v) { return v; } + +View fwd_view_lb(View v [[clang::lifetimebound]]) { return v; } + +View return_constructed_view_through_unannotated_forwarder( + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + return fwd_view(View(obj)); +} + +View return_constructed_view_through_lifetimebound_forwarder( + const MyObj &obj [[clang::lifetimebound]]) { + return fwd_view_lb(View(obj)); +} + +View verify_each_annotated_param_independently( + const MyObj &a [[clang::lifetimebound]], + const MyObj &b [[clang::lifetimebound]], // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &c [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + return cond() ? a : drop_lb(b); +} + +} // namespace LifetimeboundReturnVerification >From d7696dbfebf9b25e4d0712c3ea6fc66901be04cb Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Mon, 4 May 2026 22:46:00 +0300 Subject: [PATCH 4/6] fix tests --- clang/test/Sema/warn-lifetime-safety.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 5aaddafec6d07..a39b156930e3b 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -3329,15 +3329,15 @@ View return_through_two_lifetimebound_calls( return keep_lb2(obj); } -View return_through_broken_chain( - const MyObj &obj [[clang::lifetimebound]]) { - return lose_lb(obj); -} - View fwd_view(View v) { return v; } View fwd_view_lb(View v [[clang::lifetimebound]]) { return v; } +View return_through_nested_broken_chain( + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + return fwd_view(fwd_view_lb(View(obj))); +} + View return_constructed_view_through_unannotated_forwarder( const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} return fwd_view(View(obj)); >From b8e90faf4162daffc98feb145af6248503e73183 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Wed, 6 May 2026 21:14:26 +0300 Subject: [PATCH 5/6] wording cleanup --- clang/include/clang/Basic/DiagnosticGroups.td | 2 +- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/test/Sema/warn-lifetime-safety.cpp | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index b454b68a7f5ac..c69fc8da20d87 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -626,7 +626,7 @@ Warning to detect invalidation of references. def LifetimeSafetyLifetimeboundViolation : DiagGroup<"lifetime-safety-lifetimebound-violation"> { code Documentation = [{ -Warning to detect lifetimebound violations introduced by marking parameter as lifetimebound but not actually returning it. +Warning to detect lifetimebound violations introduced by marking parameter as lifetimebound but not returning it in any way. }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 40409c0174470..ce9e00e4c48eb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11004,7 +11004,7 @@ def warn_lifetime_safety_dangling_global_moved DefaultIgnore; def warn_lifetime_safety_param_lifetimebound_violation - : Warning<"parameter is marked as [[clang::lifetimebound]] but doesn't escape">, + : Warning<"parameter is marked as [[clang::lifetimebound]] but isn't returned">, InGroup<LifetimeSafetyLifetimeboundViolation>, DefaultIgnore; diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index a39b156930e3b..6eb81995aaa4a 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -3269,7 +3269,7 @@ void uaf_via_lifetimebound() { namespace GH126600 { struct [[gsl::Pointer]] function_ref { template <typename Callable> - function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {} // expected-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {} // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} void (*ref)(); }; @@ -3296,7 +3296,7 @@ View keep_lb(const MyObj &obj [[clang::lifetimebound]]) { } View return_through_unannotated_passthrough( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} return drop_lb(obj); } @@ -3309,7 +3309,7 @@ View keep_lb2(const MyObj &obj [[clang::lifetimebound]]) { return keep_lb(obj); } -View lose_lb(const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} +View lose_lb(const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} return drop_lb(obj); } @@ -3319,7 +3319,7 @@ View return_through_alias(const MyObj &obj [[clang::lifetimebound]]) { } View return_alias_through_unannotated_passthrough( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} const MyObj &alias = obj; return drop_lb(alias); } @@ -3334,12 +3334,12 @@ View fwd_view(View v) { return v; } View fwd_view_lb(View v [[clang::lifetimebound]]) { return v; } View return_through_nested_broken_chain( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} return fwd_view(fwd_view_lb(View(obj))); } View return_constructed_view_through_unannotated_forwarder( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} return fwd_view(View(obj)); } @@ -3350,8 +3350,8 @@ View return_constructed_view_through_lifetimebound_forwarder( View verify_each_annotated_param_independently( const MyObj &a [[clang::lifetimebound]], - const MyObj &b [[clang::lifetimebound]], // function-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} - const MyObj &c [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but doesn't escape}} + const MyObj &b [[clang::lifetimebound]], // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + const MyObj &c [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} return cond() ? a : drop_lb(b); } >From 17c0508ebd4c70e40a16f87ad4c0bb6fddf7f879 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Fri, 8 May 2026 16:38:28 +0300 Subject: [PATCH 6/6] address review comments --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 2 + clang/include/clang/Basic/DiagnosticGroups.td | 9 +-- .../warn-lifetime-safety-lifetimebound.cpp | 80 +++++++++++++++++++ clang/test/Sema/warn-lifetime-safety.cpp | 76 +----------------- 4 files changed, 88 insertions(+), 79 deletions(-) create mode 100644 clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 6e352ee50e011..7ccf30ba14987 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -108,6 +108,8 @@ class LifetimeSafetySemaHelper { virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const VarDecl *EscapeGlobal) {} + // Reports misuse of [[clang::lifetimebound]] when parameter doesn't escape + // through return. virtual void reportLifetimeboundViolation(const ParmVarDecl *VD) {} // Suggests lifetime bound annotations for implicit this. diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index c69fc8da20d87..f4e0d58b7ba1b 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -626,7 +626,7 @@ Warning to detect invalidation of references. def LifetimeSafetyLifetimeboundViolation : DiagGroup<"lifetime-safety-lifetimebound-violation"> { code Documentation = [{ -Warning to detect lifetimebound violations introduced by marking parameter as lifetimebound but not returning it in any way. +Detects parameters marked as [[clang::lifetimebound]] that do not escape through the return value. }]; } @@ -635,8 +635,7 @@ def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive", LifetimeSafetyReturnStackAddr, LifetimeSafetyDanglingField, LifetimeSafetyDanglingGlobal, - LifetimeSafetyUseAfterFree, - LifetimeSafetyLifetimeboundViolation]>; + LifetimeSafetyUseAfterFree]>; def LifetimeSafetyStrict : DiagGroup<"lifetime-safety-strict", [LifetimeSafetyPermissive, @@ -673,10 +672,10 @@ Detects misuse of [[clang::noescape]] annotation where the parameter escapes (fo } def LifetimeSafetyValidations : DiagGroup<"lifetime-safety-validations", - [LifetimeSafetyNoescape]> { + [LifetimeSafetyNoescape, LifetimeSafetyLifetimeboundViolation]> { code Documentation = [{ Verify function implementations adhere to the annotated lifetime contracts through lifetime safety -like verifying [[clang::noescape]] and [[clang::lifetimebound]] (upcoming). +like verifying [[clang::noescape]] and [[clang::lifetimebound]]. }]; } diff --git a/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp new file mode 100644 index 0000000000000..89313b81c15fe --- /dev/null +++ b/clang/test/Sema/warn-lifetime-safety-lifetimebound.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-all -verify %s + +#include "Inputs/lifetime-analysis.h" + +struct [[gsl::Owner]] MyObj { + int id; + ~MyObj() {} // Non-trivial destructor +}; + +struct [[gsl::Pointer()]] View { + View(const MyObj &); // Borrows from MyObj + View(); + void use() const; +}; + +bool cond(); + +View not_lb(const MyObj &obj); + +View lb(const MyObj &obj [[clang::lifetimebound]]); + +View return_through_unannotated_passthrough( + const MyObj &obj [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + return not_lb(obj); +} + +View return_through_lifetimebound_passthrough( + const MyObj &obj [[clang::lifetimebound]]) { + return lb(obj); +} + +View lb2(const MyObj &obj [[clang::lifetimebound]]) { + return lb(obj); +} + +View lose_lb(const MyObj &obj [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + return not_lb(obj); +} + +View return_through_alias(const MyObj &obj [[clang::lifetimebound]]) { + const MyObj &alias = obj; + return alias; +} + +View return_alias_through_unannotated_passthrough( + const MyObj &obj [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + const MyObj &alias = obj; + return not_lb(alias); +} + +View return_through_two_lifetimebound_calls( + const MyObj &obj [[clang::lifetimebound]]) { + return lb2(obj); +} + +View not_lb_view(View v); + +View lb_view(View v [[clang::lifetimebound]]); + +View return_through_nested_broken_chain( + const MyObj &obj [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + return not_lb_view(lb_view(View(obj))); +} + +View return_constructed_view_through_unannotated_forwarder( + const MyObj &obj [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + return not_lb_view(View(obj)); +} + +View return_constructed_view_through_lifetimebound_forwarder( + const MyObj &obj [[clang::lifetimebound]]) { + return lb_view(View(obj)); +} + +View verify_each_annotated_param_independently( + const MyObj &a [[clang::lifetimebound]], + const MyObj &b [[clang::lifetimebound]], // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + const MyObj &c [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + return cond() ? a : not_lb(b); +} diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 6eb81995aaa4a..30b450c333fbd 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -3269,7 +3269,7 @@ void uaf_via_lifetimebound() { namespace GH126600 { struct [[gsl::Pointer]] function_ref { template <typename Callable> - function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {} // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} + function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {} void (*ref)(); }; @@ -3279,80 +3279,8 @@ struct [[gsl::Pointer]] function_ref { // avoid this warning for non-capturing lambdas. void assign_non_capturing_to_function_ref(function_ref &r) { r = []() {}; // expected-warning {{object whose reference is captured does not live long enough}} \ - // expected-note {{destroyed here}} function-note {{requested here}} + // expected-note {{destroyed here}} (void)r; // expected-note {{later used here}} } } // namespace GH126600 - -namespace LifetimeboundReturnVerification { - -bool cond(); - -View drop_lb(const MyObj &obj) { return obj; } - -View keep_lb(const MyObj &obj [[clang::lifetimebound]]) { - return obj; -} - -View return_through_unannotated_passthrough( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - return drop_lb(obj); -} - -View return_through_lifetimebound_passthrough( - const MyObj &obj [[clang::lifetimebound]]) { - return keep_lb(obj); -} - -View keep_lb2(const MyObj &obj [[clang::lifetimebound]]) { - return keep_lb(obj); -} - -View lose_lb(const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - return drop_lb(obj); -} - -View return_through_alias(const MyObj &obj [[clang::lifetimebound]]) { - const MyObj &alias = obj; - return alias; -} - -View return_alias_through_unannotated_passthrough( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - const MyObj &alias = obj; - return drop_lb(alias); -} - -View return_through_two_lifetimebound_calls( - const MyObj &obj [[clang::lifetimebound]]) { - return keep_lb2(obj); -} - -View fwd_view(View v) { return v; } - -View fwd_view_lb(View v [[clang::lifetimebound]]) { return v; } - -View return_through_nested_broken_chain( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - return fwd_view(fwd_view_lb(View(obj))); -} - -View return_constructed_view_through_unannotated_forwarder( - const MyObj &obj [[clang::lifetimebound]]) { // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - return fwd_view(View(obj)); -} - -View return_constructed_view_through_lifetimebound_forwarder( - const MyObj &obj [[clang::lifetimebound]]) { - return fwd_view_lb(View(obj)); -} - -View verify_each_annotated_param_independently( - const MyObj &a [[clang::lifetimebound]], - const MyObj &b [[clang::lifetimebound]], // function-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - const MyObj &c [[clang::lifetimebound]]) { // expected-warning {{parameter is marked as [[clang::lifetimebound]] but isn't returned}} - return cond() ? a : drop_lb(b); -} - -} // namespace LifetimeboundReturnVerification _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
