[clang] [LifetimeSafety] Add report on misuse of clang::noescape (PR #177260)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 24 03:11:55 PST 2026
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/177260
>From a2cbddef40454b62d4ae6cf34caaa694f14197fe 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 1/3] [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 de1d1e13ea712..fc7f7c58e855d 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 a2be7ab3791b9..9e066ebf70672 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10834,6 +10834,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 14d8618f0afdd..49a6fe52c4ef9 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 1692f395058973b552e2f40224150baed0f1d19c 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 2/3] 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 8fd4772b596a9be111d06abba943336c9b5f8cd8 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 3/3] 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 fc7f7c58e855d..f22263fc9f70e 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 9e066ebf70672..c5420798960c1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10835,8 +10835,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 49a6fe52c4ef9..bafbe7e79306b 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}}
}
More information about the cfe-commits
mailing list