[clang] [LifetimeSafety] Warn on incorrectly placed `[[clang::lifetimebound]]` attributes (PR #196144)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 8 06:38:46 PDT 2026
https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/196144
>From 61b80f15e64db8cdbc4ef6cc813d8d2af8cb60d8 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
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 <nobodqwe at gmail.com>
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 <nobodqwe at gmail.com>
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 <nobodqwe at gmail.com>
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 <nobodqwe at gmail.com>
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 <nobodqwe at gmail.com>
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
More information about the cfe-commits
mailing list