[clang] [LifetimeSafety] Add report on misuse of clang::noescape (PR #177260)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 25 12:26:07 PST 2026
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/177260
>From af5debc62026f37b686644b467b239b6f730d1df Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Thu, 22 Jan 2026 01:18:49 +0300
Subject: [PATCH 01/13] [LifetimeSafety] Add report on misuse of
clang::noescape
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 4 +
clang/include/clang/Basic/DiagnosticGroups.td | 6 +
.../clang/Basic/DiagnosticSemaKinds.td | 6 +
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 16 +-
clang/lib/Sema/AnalysisBasedWarnings.cpp | 11 ++
.../Sema/warn-lifetime-safety-noescape.cpp | 160 ++++++++++++++++++
6 files changed, 202 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Sema/warn-lifetime-safety-noescape.cpp
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 9c91355355233..f0682580c8340 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -60,6 +60,10 @@ class LifetimeSafetyReporter {
virtual void suggestAnnotation(SuggestionScope Scope,
const ParmVarDecl *ParmToAnnotate,
const Expr *EscapeExpr) {}
+
+ // Reports misuse of [[clang::noescape]] when parameter escapes through return
+ virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
+ const Expr *EscapeExpr) {}
};
/// The main entry point for the analysis.
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 34624dd3eed3a..20665a908d616 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -553,6 +553,12 @@ def LifetimeSafetySuggestions
Lifetime annotation suggestions for function parameters that should be marked [[clang::lifetimebound]] based on lifetime analysis.
}];
}
+def LifetimeSafetyNoescape
+ : DiagGroup<"experimental-lifetime-safety-noescape"> {
+ code Documentation = [{
+ Detects misuse of [[clang::noescape]] annotation where the parameter escapes through return.
+ }];
+}
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 33461284e11dd..ad52d5f779507 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10844,6 +10844,12 @@ def warn_lifetime_safety_cross_tu_suggestion
def note_lifetime_safety_suggestion_returned_here : Note<"param returned here">;
+def warn_lifetime_safety_noescape_escapes
+ : Warning<
+ "parameter is marked [[clang::noescape]] but escapes through return">,
+ InGroup<LifetimeSafetyNoescape>,
+ DefaultIgnore;
+
// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
// Array comparisons have similar warnings
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index f7383126fac38..461e200f5856a 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -53,6 +53,7 @@ class LifetimeChecker {
private:
llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
llvm::DenseMap<const ParmVarDecl *, const Expr *> AnnotationWarningsMap;
+ llvm::DenseMap<const ParmVarDecl *, const Expr *> NoescapeWarningsMap;
const LoanPropagationAnalysis &LoanPropagation;
const LiveOriginsAnalysis &LiveOrigins;
const FactManager &FactMgr;
@@ -73,6 +74,7 @@ class LifetimeChecker {
checkAnnotations(OEF);
issuePendingWarnings();
suggestAnnotations();
+ reportNoescapeViolations();
// 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.
@@ -81,7 +83,8 @@ class LifetimeChecker {
}
/// Checks if an escaping origin holds a placeholder loan, indicating a
- /// missing [[clang::lifetimebound]] annotation.
+ /// missing [[clang::lifetimebound]] annotation or a violation of
+ /// [[clang::noescape]].
void checkAnnotations(const OriginEscapesFact *OEF) {
OriginID EscapedOID = OEF->getEscapedOriginID();
LoanSet EscapedLoans = LoanPropagation.getLoans(EscapedOID, OEF);
@@ -89,6 +92,10 @@ class LifetimeChecker {
const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
if (const auto *PL = dyn_cast<PlaceholderLoan>(L)) {
const ParmVarDecl *PVD = PL->getParmVarDecl();
+ if (PVD->hasAttr<NoEscapeAttr>()) {
+ NoescapeWarningsMap.try_emplace(PVD, OEF->getEscapeExpr());
+ continue;
+ }
if (PVD->hasAttr<LifetimeBoundAttr>())
continue;
AnnotationWarningsMap.try_emplace(PVD, OEF->getEscapeExpr());
@@ -194,6 +201,13 @@ class LifetimeChecker {
}
}
+ void reportNoescapeViolations() {
+ if (!Reporter)
+ return;
+ for (const auto &[PVD, EscapeExpr] : NoescapeWarningsMap)
+ Reporter->reportNoescapeViolation(PVD, EscapeExpr);
+ }
+
void inferAnnotations() {
for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) {
ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD);
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 03d84fc935b8e..f941918f5c158 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2927,6 +2927,17 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter {
<< EscapeExpr->getSourceRange();
}
+ void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
+ const Expr *EscapeExpr) override {
+ S.Diag(ParmWithNoescape->getBeginLoc(),
+ diag::warn_lifetime_safety_noescape_escapes)
+ << ParmWithNoescape->getSourceRange();
+
+ S.Diag(EscapeExpr->getBeginLoc(),
+ diag::note_lifetime_safety_suggestion_returned_here)
+ << EscapeExpr->getSourceRange();
+ }
+
private:
Sema &S;
};
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
new file mode 100644
index 0000000000000..813ae54fa9e45
--- /dev/null
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -0,0 +1,160 @@
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
+
+struct [[gsl::Owner]] MyObj {
+ int id;
+ ~MyObj() {} // Non-trivial destructor
+};
+
+struct [[gsl::Pointer()]] View {
+ View(const MyObj&); // Borrows from MyObj
+ View();
+ void use() const;
+};
+
+View return_noescape_directly(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return in; // expected-note {{returned here}}
+}
+
+View return_one_of_two(
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& b [[clang::noescape]],
+ bool cond) {
+ if (cond)
+ return a; // expected-note {{returned here}}
+ return View();
+}
+
+View return_both(
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ bool cond) {
+ if (cond)
+ return a; // expected-note {{returned here}}
+ return b; // expected-note {{returned here}}
+}
+
+int* return_noescape_pointer(int* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return p; // expected-note {{returned here}}
+}
+
+MyObj& return_noescape_reference(MyObj& r [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return r; // expected-note {{returned here}}
+}
+
+View return_via_local(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ View v = in;
+ return v; // expected-note {{returned here}}
+}
+
+void use_locally(const MyObj& in [[clang::noescape]]) {
+ View v = in;
+ v.use();
+}
+
+View return_unrelated(const MyObj& in [[clang::noescape]]) {
+ (void)in;
+ MyObj local;
+ return local;
+}
+
+View return_without_noescape(const MyObj& in) {
+ return in;
+}
+
+View return_with_lifetimebound(const MyObj& in [[clang::lifetimebound]]) {
+ return in;
+}
+
+void pointer_used_locally(MyObj* p [[clang::noescape]]) {
+ p->id = 42;
+}
+
+// Both noescape and lifetimebound - contradictory annotations
+// noescape should take precedence and warn since the parameter does escape
+View both_noescape_and_lifetimebound(
+ const MyObj& in [[clang::noescape]] [[clang::lifetimebound]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return in; // expected-note {{returned here}}
+}
+
+View mixed_noescape_lifetimebound(
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& b [[clang::lifetimebound]],
+ bool cond) {
+ if (cond)
+ return a; // expected-note {{returned here}}
+ return b;
+}
+
+View mixed_only_noescape_escapes(
+ const MyObj& a [[clang::noescape]],
+ const MyObj& b [[clang::lifetimebound]]) {
+ (void)a;
+ return b;
+}
+
+View identity_lifetimebound(View v [[clang::lifetimebound]]) { return v; }
+
+View escape_through_lifetimebound_call(
+ const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return identity_lifetimebound(in); // expected-note {{returned here}}
+}
+
+View no_annotation_identity(View v) { return v; }
+
+// FIXME: Escaping through a function without lifetimebound is not detected.
+View escape_through_unannotated_call(const MyObj& in [[clang::noescape]]) {
+ return no_annotation_identity(in); // Not detected - no lifetimebound
+}
+
+View reassign_to_second(
+ const MyObj& a [[clang::noescape]],
+ const MyObj& b [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ View v = a;
+ v = b;
+ return v; // expected-note {{returned here}}
+}
+
+View multiple_reassign(
+ const MyObj& a [[clang::noescape]],
+ const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& c [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ bool cond) {
+ View v = a;
+ if (cond)
+ v = b;
+ else
+ v = c;
+ return v; // expected-note 2 {{returned here}}
+}
+
+struct Container {
+ MyObj data;
+ const MyObj& getRef() const [[clang::lifetimebound]] { return data; }
+};
+
+View access_noescape_field(
+ const Container& c [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return c.data; // expected-note {{returned here}}
+}
+
+View access_noescape_through_getter(
+ Container& c [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return c.getRef(); // expected-note {{returned here}}
+}
+
+MyObj* return_ptr_from_noescape_ref(
+ MyObj& r [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return &r; // expected-note {{returned here}}
+}
+
+MyObj& return_ref_from_noescape_ptr(
+ MyObj* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ return *p; // expected-note {{returned here}}
+}
+
+View construct_but_return_other(const MyObj& in [[clang::noescape]]) {
+ View v = in;
+ v.use();
+ MyObj other;
+ return other;
+}
>From 99dcd227113a53f40f0dc4a555182b0e2df4e36d Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Thu, 22 Jan 2026 01:24:04 +0300
Subject: [PATCH 02/13] reorder tests
---
.../Sema/warn-lifetime-safety-noescape.cpp | 61 +++++++++----------
1 file changed, 30 insertions(+), 31 deletions(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 813ae54fa9e45..a8db83ce63cb5 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -33,6 +33,35 @@ View return_both(
return b; // expected-note {{returned here}}
}
+View mixed_noescape_lifetimebound(
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& b [[clang::lifetimebound]],
+ bool cond) {
+ if (cond)
+ return a; // expected-note {{returned here}}
+ return b;
+}
+
+View mixed_only_noescape_escapes(
+ const MyObj& a [[clang::noescape]],
+ const MyObj& b [[clang::lifetimebound]]) {
+ (void)a;
+ return b;
+}
+
+View multiple_reassign(
+ const MyObj& a [[clang::noescape]],
+ const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& c [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ bool cond) {
+ View v = a;
+ if (cond)
+ v = b;
+ else
+ v = c;
+ return v; // expected-note 2 {{returned here}}
+}
+
int* return_noescape_pointer(int* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
return p; // expected-note {{returned here}}
}
@@ -69,29 +98,12 @@ void pointer_used_locally(MyObj* p [[clang::noescape]]) {
p->id = 42;
}
-// Both noescape and lifetimebound - contradictory annotations
-// noescape should take precedence and warn since the parameter does escape
+// Noescape should take precedence and warn since the parameter does escape
View both_noescape_and_lifetimebound(
const MyObj& in [[clang::noescape]] [[clang::lifetimebound]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
return in; // expected-note {{returned here}}
}
-View mixed_noescape_lifetimebound(
- const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
- const MyObj& b [[clang::lifetimebound]],
- bool cond) {
- if (cond)
- return a; // expected-note {{returned here}}
- return b;
-}
-
-View mixed_only_noescape_escapes(
- const MyObj& a [[clang::noescape]],
- const MyObj& b [[clang::lifetimebound]]) {
- (void)a;
- return b;
-}
-
View identity_lifetimebound(View v [[clang::lifetimebound]]) { return v; }
View escape_through_lifetimebound_call(
@@ -114,19 +126,6 @@ View reassign_to_second(
return v; // expected-note {{returned here}}
}
-View multiple_reassign(
- const MyObj& a [[clang::noescape]],
- const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
- const MyObj& c [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
- bool cond) {
- View v = a;
- if (cond)
- v = b;
- else
- v = c;
- return v; // expected-note 2 {{returned here}}
-}
-
struct Container {
MyObj data;
const MyObj& getRef() const [[clang::lifetimebound]] { return data; }
>From 949fcb624f398d5e6844daa8cdb47aba5533b251 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sat, 24 Jan 2026 14:11:36 +0300
Subject: [PATCH 03/13] fix review
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 3 +-
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 2 +-
clang/lib/Sema/AnalysisBasedWarnings.cpp | 6 ++-
.../Sema/warn-lifetime-safety-noescape.cpp | 49 +++++++++++--------
5 files changed, 36 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 20665a908d616..f26d9985be19a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -556,7 +556,7 @@ def LifetimeSafetySuggestions
def LifetimeSafetyNoescape
: DiagGroup<"experimental-lifetime-safety-noescape"> {
code Documentation = [{
- Detects misuse of [[clang::noescape]] annotation where the parameter escapes through return.
+ Detects misuse of [[clang::noescape]] annotation where the parameter escapes.
}];
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ad52d5f779507..806de5a0a819f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10845,8 +10845,7 @@ def warn_lifetime_safety_cross_tu_suggestion
def note_lifetime_safety_suggestion_returned_here : Note<"param returned here">;
def warn_lifetime_safety_noescape_escapes
- : Warning<
- "parameter is marked [[clang::noescape]] but escapes through return">,
+ : Warning<"parameter is marked [[clang::noescape]] but escapes">,
InGroup<LifetimeSafetyNoescape>,
DefaultIgnore;
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 461e200f5856a..5103a8be482c6 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -204,7 +204,7 @@ class LifetimeChecker {
void reportNoescapeViolations() {
if (!Reporter)
return;
- for (const auto &[PVD, EscapeExpr] : NoescapeWarningsMap)
+ for (auto [PVD, EscapeExpr] : NoescapeWarningsMap)
Reporter->reportNoescapeViolation(PVD, EscapeExpr);
}
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index f941918f5c158..7eb125d5c5b24 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2929,10 +2929,12 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter {
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const Expr *EscapeExpr) override {
+ const auto *Attr = ParmWithNoescape->getAttr<NoEscapeAttr>();
+
S.Diag(ParmWithNoescape->getBeginLoc(),
diag::warn_lifetime_safety_noescape_escapes)
- << ParmWithNoescape->getSourceRange();
-
+ << ParmWithNoescape->getSourceRange()
+ << FixItHint::CreateRemoval(Attr->getRange());
S.Diag(EscapeExpr->getBeginLoc(),
diag::note_lifetime_safety_suggestion_returned_here)
<< EscapeExpr->getSourceRange();
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index a8db83ce63cb5..d8e1b94dc87db 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
+// RUN: cp %s %t
+// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -fixit -verify %t
+// RUN: %clang_cc1 -x c++ -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -Werror %t
struct [[gsl::Owner]] MyObj {
int id;
@@ -11,12 +13,12 @@ struct [[gsl::Pointer()]] View {
void use() const;
};
-View return_noescape_directly(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+View return_noescape_directly(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return in; // expected-note {{returned here}}
}
View return_one_of_two(
- const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
const MyObj& b [[clang::noescape]],
bool cond) {
if (cond)
@@ -25,8 +27,8 @@ View return_one_of_two(
}
View return_both(
- const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
- const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
+ const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
bool cond) {
if (cond)
return a; // expected-note {{returned here}}
@@ -34,7 +36,7 @@ View return_both(
}
View mixed_noescape_lifetimebound(
- const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& a [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
const MyObj& b [[clang::lifetimebound]],
bool cond) {
if (cond)
@@ -51,8 +53,8 @@ View mixed_only_noescape_escapes(
View multiple_reassign(
const MyObj& a [[clang::noescape]],
- const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
- const MyObj& c [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& b [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
+ const MyObj& c [[clang::noescape]], // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
bool cond) {
View v = a;
if (cond)
@@ -62,15 +64,15 @@ View multiple_reassign(
return v; // expected-note 2 {{returned here}}
}
-int* return_noescape_pointer(int* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+int* return_noescape_pointer(int* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return p; // expected-note {{returned here}}
}
-MyObj& return_noescape_reference(MyObj& r [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+MyObj& return_noescape_reference(MyObj& r [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return r; // expected-note {{returned here}}
}
-View return_via_local(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+View return_via_local(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
View v = in;
return v; // expected-note {{returned here}}
}
@@ -98,16 +100,16 @@ void pointer_used_locally(MyObj* p [[clang::noescape]]) {
p->id = 42;
}
-// Noescape should take precedence and warn since the parameter does escape
+// Noescape should take precedence and warn since the parameter does escape.
View both_noescape_and_lifetimebound(
- const MyObj& in [[clang::noescape]] [[clang::lifetimebound]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& in [[clang::noescape]] [[clang::lifetimebound]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return in; // expected-note {{returned here}}
}
View identity_lifetimebound(View v [[clang::lifetimebound]]) { return v; }
View escape_through_lifetimebound_call(
- const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return identity_lifetimebound(in); // expected-note {{returned here}}
}
@@ -115,12 +117,19 @@ View no_annotation_identity(View v) { return v; }
// FIXME: Escaping through a function without lifetimebound is not detected.
View escape_through_unannotated_call(const MyObj& in [[clang::noescape]]) {
- return no_annotation_identity(in); // Not detected - no lifetimebound
+ return no_annotation_identity(in);
+}
+
+View view;
+
+// FIXME: Escaping through a global variable is not detected.
+void escape_through_global_var(const MyObj& in [[clang::noescape]]) {
+ view = in;
}
View reassign_to_second(
const MyObj& a [[clang::noescape]],
- const MyObj& b [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const MyObj& b [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
View v = a;
v = b;
return v; // expected-note {{returned here}}
@@ -132,22 +141,22 @@ struct Container {
};
View access_noescape_field(
- const Container& c [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ const Container& c [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return c.data; // expected-note {{returned here}}
}
View access_noescape_through_getter(
- Container& c [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ Container& c [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return c.getRef(); // expected-note {{returned here}}
}
MyObj* return_ptr_from_noescape_ref(
- MyObj& r [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ MyObj& r [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return &r; // expected-note {{returned here}}
}
MyObj& return_ref_from_noescape_ptr(
- MyObj* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes through return}}
+ MyObj* p [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return *p; // expected-note {{returned here}}
}
>From 101c7c1edf2d50b5228cf3a6628baaf2768d9638 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sat, 24 Jan 2026 14:16:05 +0300
Subject: [PATCH 04/13] back diag group
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index f26d9985be19a..20665a908d616 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -556,7 +556,7 @@ def LifetimeSafetySuggestions
def LifetimeSafetyNoescape
: DiagGroup<"experimental-lifetime-safety-noescape"> {
code Documentation = [{
- Detects misuse of [[clang::noescape]] annotation where the parameter escapes.
+ Detects misuse of [[clang::noescape]] annotation where the parameter escapes through return.
}];
}
>From fb3c6e9a6eb3de7ef8652d97fb48af89b2f5d72b Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sat, 24 Jan 2026 16:20:17 +0300
Subject: [PATCH 05/13] fix failing test
---
clang/test/Sema/warn-lifetime-safety-noescape.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index d8e1b94dc87db..8e0a27536234b 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,5 +1,6 @@
+// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
// RUN: cp %s %t
-// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -fixit -verify %t
+// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -fixit %t
// RUN: %clang_cc1 -x c++ -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -Werror %t
struct [[gsl::Owner]] MyObj {
>From b964726a79ada8ec88954eaff1ae545e6b35255b Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sat, 24 Jan 2026 17:59:42 +0300
Subject: [PATCH 06/13] handle [[ ]] correctly
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 31 ++++++++++++++++++-
.../Sema/warn-lifetime-safety-noescape.cpp | 4 +++
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 7eb125d5c5b24..5246f77a06c95 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2930,11 +2930,40 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter {
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const Expr *EscapeExpr) override {
const auto *Attr = ParmWithNoescape->getAttr<NoEscapeAttr>();
+ SourceRange RemovalRange = Attr->getRange();
+
+ // For [[clang::noescape]], Attr->getRange() only covers the inner
+ // 'clang::noescape' part. Extend to include the '[[' and ']]' brackets.
+ if (Attr->isStandardAttributeSyntax()) {
+ const SourceManager &SM = S.getSourceManager();
+ const LangOptions &LO = S.getLangOpts();
+
+ auto SecondOpen = Lexer::findPreviousToken(
+ Attr->getRange().getBegin(), SM, LO, /*IncludeComments=*/false);
+ auto FirstOpen =
+ SecondOpen && SecondOpen->is(tok::l_square)
+ ? Lexer::findPreviousToken(SecondOpen->getLocation(), SM, LO,
+ /*IncludeComments=*/false)
+ : std::nullopt;
+
+ auto FirstClose = Lexer::findNextToken(Attr->getRange().getEnd(), SM, LO,
+ /*IncludeComments=*/false);
+ auto SecondClose =
+ FirstClose && FirstClose->is(tok::r_square)
+ ? Lexer::findNextToken(FirstClose->getLocation(), SM, LO,
+ /*IncludeComments=*/false)
+ : std::nullopt;
+
+ if ((FirstOpen && FirstOpen->is(tok::l_square)) &&
+ (SecondClose && SecondClose->is(tok::r_square)))
+ RemovalRange = {FirstOpen->getLocation(), SecondClose->getLocation()};
+ }
S.Diag(ParmWithNoescape->getBeginLoc(),
diag::warn_lifetime_safety_noescape_escapes)
<< ParmWithNoescape->getSourceRange()
- << FixItHint::CreateRemoval(Attr->getRange());
+ << FixItHint::CreateRemoval(RemovalRange);
+
S.Diag(EscapeExpr->getBeginLoc(),
diag::note_lifetime_safety_suggestion_returned_here)
<< EscapeExpr->getSourceRange();
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 8e0a27536234b..300c616de1feb 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -167,3 +167,7 @@ View construct_but_return_other(const MyObj& in [[clang::noescape]]) {
MyObj other;
return other;
}
+
+int* return_spaced_brackets(int* p [ [clang::noescape] /*some comment*/ ]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
+ return p; // expected-note {{returned here}}
+}
>From aca1492047175c4fbcaf87623e83e813f4d8e620 Mon Sep 17 00:00:00 2001
From: Baranov Victor <bar.victor.2002 at gmail.com>
Date: Sat, 24 Jan 2026 19:19:08 +0300
Subject: [PATCH 07/13] Apply suggestion from @vbvictor
---
clang/test/Sema/warn-lifetime-safety-noescape.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 300c616de1feb..cf20cbc4ec849 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
// RUN: cp %s %t
// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -fixit %t
// RUN: %clang_cc1 -x c++ -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -Werror %t
>From 7d727665f2b42445781f61e91642e76a737821f0 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 25 Jan 2026 11:33:13 +0300
Subject: [PATCH 08/13] fix build?
---
.../Sema/warn-lifetime-safety-noescape.cpp | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index cf20cbc4ec849..9f991d88ef345 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wexperimental-lifetime-safety -Wno-dangling -verify %s
// RUN: cp %s %t
-// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -fixit %t
-// RUN: %clang_cc1 -x c++ -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -Werror %t
+// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wexperimental-lifetime-safety -Wno-dangling -fixit %t
+// RUN: %clang_cc1 -x c++ -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wexperimental-lifetime-safety -Wno-dangling -Werror %t
struct [[gsl::Owner]] MyObj {
int id;
@@ -83,12 +83,6 @@ void use_locally(const MyObj& in [[clang::noescape]]) {
v.use();
}
-View return_unrelated(const MyObj& in [[clang::noescape]]) {
- (void)in;
- MyObj local;
- return local;
-}
-
View return_without_noescape(const MyObj& in) {
return in;
}
@@ -161,13 +155,6 @@ MyObj& return_ref_from_noescape_ptr(
return *p; // expected-note {{returned here}}
}
-View construct_but_return_other(const MyObj& in [[clang::noescape]]) {
- View v = in;
- v.use();
- MyObj other;
- return other;
-}
-
int* return_spaced_brackets(int* p [ [clang::noescape] /*some comment*/ ]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return p; // expected-note {{returned here}}
}
>From a9c2d2c2ed9a62b0fedb23e0e5a75505db999b1a Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 25 Jan 2026 13:43:24 +0300
Subject: [PATCH 09/13] only bare minimum
---
clang/test/Sema/warn-lifetime-safety-noescape.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 9f991d88ef345..beca877255c0c 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,7 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wexperimental-lifetime-safety -Wno-dangling -verify %s
-// RUN: cp %s %t
-// RUN: %clang_cc1 -x c++ -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wexperimental-lifetime-safety -Wno-dangling -fixit %t
-// RUN: %clang_cc1 -x c++ -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wexperimental-lifetime-safety -Wno-dangling -Werror %t
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s
struct [[gsl::Owner]] MyObj {
int id;
>From 21f9a4d60ecdca2804c111058756ad9351c774e3 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 25 Jan 2026 13:45:26 +0300
Subject: [PATCH 10/13] bare minimum 2
---
clang/test/Sema/warn-lifetime-safety-noescape.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index beca877255c0c..eed7842506fc0 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
struct [[gsl::Owner]] MyObj {
int id;
>From e54c85aa4f10d874de014068d48ab204e0b9807e Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 25 Jan 2026 14:04:14 +0300
Subject: [PATCH 11/13] fix final build..
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
clang/test/Sema/warn-lifetime-safety-noescape.cpp | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 20665a908d616..3113d0fa618be 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -554,7 +554,7 @@ def LifetimeSafetySuggestions
}];
}
def LifetimeSafetyNoescape
- : DiagGroup<"experimental-lifetime-safety-noescape"> {
+ : DiagGroup<"lifetime-safety-noescape"> {
code Documentation = [{
Detects misuse of [[clang::noescape]] annotation where the parameter escapes through return.
}];
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index eed7842506fc0..a34caf6077dc7 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,4 +1,8 @@
-// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wexperimental-lifetime-safety-noescape -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -verify %s
+// RUN: cp %s %t
+// RUN: %clang_cc1 -x c++ -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -fixit %t
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -Werror %t
+
struct [[gsl::Owner]] MyObj {
int id;
>From f99056282d2334dafbfef8b47550eb943564938f Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 25 Jan 2026 14:12:25 +0300
Subject: [PATCH 12/13] Add release notes
---
clang/docs/ReleaseNotes.rst | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 14384ea3b51c1..22ab4219a9361 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -159,6 +159,24 @@ Improvements to Clang's diagnostics
int* p(int *in) { return in; }
^~
+- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
+ annotation where the parameter escapes through return. For example:
+
+ .. code-block:: c++
+
+ int* p(int *in [[clang::noescape]]) { return in; }
+
+ Clang will warn:
+
+ .. code-block:: c++
+
+ warning: parameter is marked [[clang::noescape]] but escapes
+ int* p(int *in [[clang::noescape]]) { return in; }
+ ^~~~~~~
+ note: returned here
+ int* p(int *in [[clang::noescape]]) { return in; }
+ ^~
+
Improvements to Clang's time-trace
----------------------------------
>From b3b39b9c7447d9552d821667ab5e4cfa85eda299 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 25 Jan 2026 23:20:49 +0300
Subject: [PATCH 13/13] remove fixits
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 33 +------------------
.../Sema/warn-lifetime-safety-noescape.cpp | 4 ---
2 files changed, 1 insertion(+), 36 deletions(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 5246f77a06c95..f941918f5c158 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2929,40 +2929,9 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter {
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const Expr *EscapeExpr) override {
- const auto *Attr = ParmWithNoescape->getAttr<NoEscapeAttr>();
- SourceRange RemovalRange = Attr->getRange();
-
- // For [[clang::noescape]], Attr->getRange() only covers the inner
- // 'clang::noescape' part. Extend to include the '[[' and ']]' brackets.
- if (Attr->isStandardAttributeSyntax()) {
- const SourceManager &SM = S.getSourceManager();
- const LangOptions &LO = S.getLangOpts();
-
- auto SecondOpen = Lexer::findPreviousToken(
- Attr->getRange().getBegin(), SM, LO, /*IncludeComments=*/false);
- auto FirstOpen =
- SecondOpen && SecondOpen->is(tok::l_square)
- ? Lexer::findPreviousToken(SecondOpen->getLocation(), SM, LO,
- /*IncludeComments=*/false)
- : std::nullopt;
-
- auto FirstClose = Lexer::findNextToken(Attr->getRange().getEnd(), SM, LO,
- /*IncludeComments=*/false);
- auto SecondClose =
- FirstClose && FirstClose->is(tok::r_square)
- ? Lexer::findNextToken(FirstClose->getLocation(), SM, LO,
- /*IncludeComments=*/false)
- : std::nullopt;
-
- if ((FirstOpen && FirstOpen->is(tok::l_square)) &&
- (SecondClose && SecondClose->is(tok::r_square)))
- RemovalRange = {FirstOpen->getLocation(), SecondClose->getLocation()};
- }
-
S.Diag(ParmWithNoescape->getBeginLoc(),
diag::warn_lifetime_safety_noescape_escapes)
- << ParmWithNoescape->getSourceRange()
- << FixItHint::CreateRemoval(RemovalRange);
+ << ParmWithNoescape->getSourceRange();
S.Diag(EscapeExpr->getBeginLoc(),
diag::note_lifetime_safety_suggestion_returned_here)
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index a34caf6077dc7..b6f29b1f43feb 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -1,8 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -verify %s
-// RUN: cp %s %t
-// RUN: %clang_cc1 -x c++ -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -fixit %t
-// RUN: %clang_cc1 -x c++ -fsyntax-only -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -Werror %t
-
struct [[gsl::Owner]] MyObj {
int id;
More information about the cfe-commits
mailing list