[clang] [LifetimeSafety] Add report on misuse of clang::noescape (PR #177260)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 26 05:28:59 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/16] [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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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;
>From 1b2170b777d56c83404542246bb19164798d7572 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 26 Jan 2026 16:00:33 +0300
Subject: [PATCH 14/16] pr fixes
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
.../Sema/warn-lifetime-safety-noescape.cpp | 29 ++++++++++++++-----
2 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 3113d0fa618be..488f3a94c4fb6 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -556,7 +556,7 @@ def LifetimeSafetySuggestions
def LifetimeSafetyNoescape
: DiagGroup<"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 (for example, through return).
}];
}
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index b6f29b1f43feb..90f81a71189ab 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 -Wlifetime-safety-noescape -Wlifetime-safety -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -Wlifetime-safety-noescape -Wlifetime-safety -verify %s
+
+#include "Inputs/lifetime-analysis.h"
struct [[gsl::Owner]] MyObj {
int id;
@@ -92,7 +94,7 @@ void pointer_used_locally(MyObj* p [[clang::noescape]]) {
p->id = 42;
}
-// Noescape should take precedence and warn since the parameter does escape.
+// FIXME: diagnose differently when parameter has both '[[clang::noescape]]' and '[[clang::lifetimebound]]'.
View both_noescape_and_lifetimebound(
const MyObj& in [[clang::noescape]] [[clang::lifetimebound]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return in; // expected-note {{returned here}}
@@ -107,16 +109,29 @@ View escape_through_lifetimebound_call(
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);
+View escape_through_unannotated_call(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
+ return no_annotation_identity(in); // expected-note {{returned here}}
}
-View view;
+View global_view;
// FIXME: Escaping through a global variable is not detected.
void escape_through_global_var(const MyObj& in [[clang::noescape]]) {
- view = in;
+ global_view = in;
+}
+
+// FIXME: Escaping through a member variable is not detected.
+struct ObjConsumer {
+ void escape_through_member(const MyObj& in [[clang::noescape]]) {
+ member_view = in;
+ }
+
+ View member_view;
+};
+
+// FIXME: Escaping through another param is not detected.
+void escape_through_param(const MyObj& in, std::vector<View> &v) {
+ v.push_back(in);
}
View reassign_to_second(
>From ddfa950bba2680b798420d6d5abd45e3a3ca41ae Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 26 Jan 2026 16:19:00 +0300
Subject: [PATCH 15/16] fix format
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 615 ++++++++++++-----------
1 file changed, 314 insertions(+), 301 deletions(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index ef136f567474a..e64eb6b760c76 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -70,60 +70,61 @@ using namespace clang;
//===----------------------------------------------------------------------===//
namespace {
-class UnreachableCodeHandler : public reachable_code::Callback {
- Sema &S;
- SourceRange PreviousSilenceableCondVal;
-
-public:
- UnreachableCodeHandler(Sema &s) : S(s) {}
-
- void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L,
- SourceRange SilenceableCondVal, SourceRange R1,
- SourceRange R2, bool HasFallThroughAttr) override {
- // If the diagnosed code is `[[fallthrough]];` and
- // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never
- // be executed` warning to avoid generating diagnostic twice
- if (HasFallThroughAttr &&
- !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr,
- SourceLocation()))
- return;
+ class UnreachableCodeHandler : public reachable_code::Callback {
+ Sema &S;
+ SourceRange PreviousSilenceableCondVal;
+
+ public:
+ UnreachableCodeHandler(Sema &s) : S(s) {}
+
+ void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L,
+ SourceRange SilenceableCondVal, SourceRange R1,
+ SourceRange R2, bool HasFallThroughAttr) override {
+ // If the diagnosed code is `[[fallthrough]];` and
+ // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never
+ // be executed` warning to avoid generating diagnostic twice
+ if (HasFallThroughAttr &&
+ !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr,
+ SourceLocation()))
+ return;
- // Avoid reporting multiple unreachable code diagnostics that are
- // triggered by the same conditional value.
- if (PreviousSilenceableCondVal.isValid() && SilenceableCondVal.isValid() &&
- PreviousSilenceableCondVal == SilenceableCondVal)
- return;
- PreviousSilenceableCondVal = SilenceableCondVal;
+ // Avoid reporting multiple unreachable code diagnostics that are
+ // triggered by the same conditional value.
+ if (PreviousSilenceableCondVal.isValid() &&
+ SilenceableCondVal.isValid() &&
+ PreviousSilenceableCondVal == SilenceableCondVal)
+ return;
+ PreviousSilenceableCondVal = SilenceableCondVal;
- unsigned diag = diag::warn_unreachable;
- switch (UK) {
- case reachable_code::UK_Break:
- diag = diag::warn_unreachable_break;
- break;
- case reachable_code::UK_Return:
- diag = diag::warn_unreachable_return;
- break;
- case reachable_code::UK_Loop_Increment:
- diag = diag::warn_unreachable_loop_increment;
- break;
- case reachable_code::UK_Other:
- break;
- }
+ unsigned diag = diag::warn_unreachable;
+ switch (UK) {
+ case reachable_code::UK_Break:
+ diag = diag::warn_unreachable_break;
+ break;
+ case reachable_code::UK_Return:
+ diag = diag::warn_unreachable_return;
+ break;
+ case reachable_code::UK_Loop_Increment:
+ diag = diag::warn_unreachable_loop_increment;
+ break;
+ case reachable_code::UK_Other:
+ break;
+ }
- S.Diag(L, diag) << R1 << R2;
+ S.Diag(L, diag) << R1 << R2;
- SourceLocation Open = SilenceableCondVal.getBegin();
- if (Open.isValid()) {
- SourceLocation Close = SilenceableCondVal.getEnd();
- Close = S.getLocForEndOfToken(Close);
- if (Close.isValid()) {
- S.Diag(Open, diag::note_unreachable_silence)
+ SourceLocation Open = SilenceableCondVal.getBegin();
+ if (Open.isValid()) {
+ SourceLocation Close = SilenceableCondVal.getEnd();
+ Close = S.getLocForEndOfToken(Close);
+ if (Close.isValid()) {
+ S.Diag(Open, diag::note_unreachable_silence)
<< FixItHint::CreateInsertion(Open, "/* DISABLES CODE */ (")
<< FixItHint::CreateInsertion(Close, ")");
+ }
}
}
- }
-};
+ };
} // anonymous namespace
/// CheckUnreachable - Check for unreachable code.
@@ -293,8 +294,7 @@ static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD,
return;
CFG *cfg = AC.getCFG();
- if (!cfg)
- return;
+ if (!cfg) return;
// If the exit block is unreachable, skip processing the function.
if (cfg->getExit().pred_empty())
@@ -329,9 +329,10 @@ static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock,
if (Succ->getBlockID() == Body->getExit().getBlockID())
return true;
- if (auto *Catch = dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) {
+ if (auto *Catch =
+ dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) {
QualType Caught = Catch->getCaughtType();
- if (Caught.isNull() || // catch (...) catches everything
+ if (Caught.isNull() || // catch (...) catches everything
!E->getSubExpr() || // throw; is considered cuaght by any handler
S.handlerCanCatch(Caught, E->getSubExpr()->getType()))
// Exception doesn't escape via this path.
@@ -350,8 +351,7 @@ static void visitReachableThrows(
CFG *BodyCFG,
llvm::function_ref<void(const CXXThrowExpr *, CFGBlock &)> Visit) {
llvm::BitVector Reachable(BodyCFG->getNumBlockIDs());
- clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(),
- Reachable);
+ clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), Reachable);
for (CFGBlock *B : *BodyCFG) {
if (!Reachable[B->getBlockID()])
continue;
@@ -374,8 +374,8 @@ static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc,
(isa<CXXDestructorDecl>(FD) ||
FD->getDeclName().getCXXOverloadedOperator() == OO_Delete ||
FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) {
- if (const auto *Ty =
- FD->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>())
+ if (const auto *Ty = FD->getTypeSourceInfo()->getType()->
+ getAs<FunctionProtoType>())
S.Diag(FD->getLocation(), diag::note_throw_in_dtor)
<< !isa<CXXDestructorDecl>(FD) << !Ty->hasExceptionSpec()
<< FD->getExceptionSpecSourceRange();
@@ -392,11 +392,10 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD,
return;
if (BodyCFG->getExit().pred_empty())
return;
- visitReachableThrows(
- BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) {
- if (throwEscapes(S, Throw, Block, BodyCFG))
- EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD);
- });
+ visitReachableThrows(BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) {
+ if (throwEscapes(S, Throw, Block, BodyCFG))
+ EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD);
+ });
}
static bool isNoexcept(const FunctionDecl *FD) {
@@ -569,14 +568,13 @@ enum ControlFlowKind {
/// will return.
static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
CFG *cfg = AC.getCFG();
- if (!cfg)
- return UnknownFallThrough;
+ if (!cfg) return UnknownFallThrough;
// The CFG leaves in dead things, and we don't want the dead code paths to
// confuse us, so we mark all live things first.
llvm::BitVector live(cfg->getNumBlockIDs());
- unsigned count =
- reachable_code::ScanReachableFromBlock(&cfg->getEntry(), live);
+ unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(),
+ live);
bool AddEHEdges = AC.getAddEHEdges();
if (!AddEHEdges && count != cfg->getNumBlockIDs())
@@ -629,7 +627,7 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
// statement (if it exists).
CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend();
- for (; ri != re; ++ri)
+ for ( ; ri != re ; ++ri)
if (ri->getAs<CFGStmt>())
break;
@@ -803,12 +801,14 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
else
ReturnsVoid = FD->getReturnType()->isVoidType();
HasNoReturn = FD->isNoReturn() || FD->hasAttr<InferredNoReturnAttr>();
- } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ }
+ else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
ReturnsVoid = MD->getReturnType()->isVoidType();
HasNoReturn = MD->hasAttr<NoReturnAttr>();
- } else if (isa<BlockDecl>(D)) {
+ }
+ else if (isa<BlockDecl>(D)) {
if (const FunctionType *FT =
- BlockType->getPointeeType()->getAs<FunctionType>()) {
+ BlockType->getPointeeType()->getAs<FunctionType>()) {
if (FT->getReturnType()->isVoidType())
ReturnsVoid = true;
if (FT->getNoReturnAttr())
@@ -820,7 +820,7 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
// Short circuit for compilation speed.
if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn))
- return;
+ return;
SourceLocation LBrace = Body->getBeginLoc(), RBrace = Body->getEndLoc();
// cpu_dispatch functions permit empty function bodies for ICC compatibility.
@@ -897,7 +897,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> {
typedef ConstEvaluatedExprVisitor<ContainsReference> Inherited;
ContainsReference(ASTContext &Context, const DeclRefExpr *Needle)
- : Inherited(Context), FoundReference(false), Needle(Needle) {}
+ : Inherited(Context), FoundReference(false), Needle(Needle) {}
void VisitExpr(const Expr *E) {
// Stop evaluating if we already have a reference.
@@ -920,7 +920,8 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> {
static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
QualType VariableTy = VD->getType().getCanonicalType();
- if (VariableTy->isBlockPointerType() && !VD->hasAttr<BlocksAttr>()) {
+ if (VariableTy->isBlockPointerType() &&
+ !VD->hasAttr<BlocksAttr>()) {
S.Diag(VD->getLocation(), diag::note_block_var_fixit_add_initialization)
<< VD->getDeclName()
<< FixItHint::CreateInsertion(VD->getLocation(), "__block ");
@@ -942,16 +943,16 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
if (Init.empty())
return false;
- S.Diag(Loc, diag::note_var_fixit_add_initialization)
- << VD->getDeclName() << FixItHint::CreateInsertion(Loc, Init);
+ S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName()
+ << FixItHint::CreateInsertion(Loc, Init);
return true;
}
/// Create a fixit to remove an if-like statement, on the assumption that its
/// condition is CondVal.
static void CreateIfFixit(Sema &S, const Stmt *If, const Stmt *Then,
- const Stmt *Else, bool CondVal, FixItHint &Fixit1,
- FixItHint &Fixit2) {
+ const Stmt *Else, bool CondVal,
+ FixItHint &Fixit1, FixItHint &Fixit2) {
if (CondVal) {
// If condition is always true, remove all but the 'then'.
Fixit1 = FixItHint::CreateRemoval(
@@ -1018,9 +1019,9 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
// For all binary terminators, branch 0 is taken if the condition is true,
// and branch 1 is taken if the condition is false.
int RemoveDiagKind = -1;
- const char *FixitStr = S.getLangOpts().CPlusPlus
- ? (I->Output ? "true" : "false")
- : (I->Output ? "1" : "0");
+ const char *FixitStr =
+ S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false")
+ : (I->Output ? "1" : "0");
FixItHint Fixit1, Fixit2;
switch (Term ? Term->getStmtClass() : Stmt::DeclStmtClass) {
@@ -1036,8 +1037,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
Str = "if";
Range = IS->getCond()->getSourceRange();
RemoveDiagKind = 0;
- CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), I->Output, Fixit1,
- Fixit2);
+ CreateIfFixit(S, IS, IS->getThen(), IS->getElse(),
+ I->Output, Fixit1, Fixit2);
break;
}
case Stmt::ConditionalOperatorClass: {
@@ -1046,8 +1047,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
Str = "?:";
Range = CO->getCond()->getSourceRange();
RemoveDiagKind = 0;
- CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), I->Output,
- Fixit1, Fixit2);
+ CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(),
+ I->Output, Fixit1, Fixit2);
break;
}
case Stmt::BinaryOperatorClass: {
@@ -1122,13 +1123,13 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
}
S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var)
- << VD->getDeclName() << IsCapturedByBlock << DiagKind << Str
- << I->Output << Range;
+ << VD->getDeclName() << IsCapturedByBlock << DiagKind
+ << Str << I->Output << Range;
S.Diag(User->getBeginLoc(), diag::note_uninit_var_use)
<< IsCapturedByBlock << User->getSourceRange();
if (RemoveDiagKind != -1)
S.Diag(Fixit1.RemoveRange.getBegin(), diag::note_uninit_fixit_remove_cond)
- << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2;
+ << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2;
Diagnosed = true;
}
@@ -1298,32 +1299,32 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor {
// Don't care about other unreachable statements.
}
}
- // If there are no unreachable statements, this may be a special
- // case in CFG:
- // case X: {
- // A a; // A has a destructor.
- // break;
- // }
- // // <<<< This place is represented by a 'hanging' CFG block.
- // case Y:
- continue;
+ // If there are no unreachable statements, this may be a special
+ // case in CFG:
+ // case X: {
+ // A a; // A has a destructor.
+ // break;
+ // }
+ // // <<<< This place is represented by a 'hanging' CFG block.
+ // case Y:
+ continue;
}
- const Stmt *LastStmt = getLastStmt(*P);
- if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) {
- markFallthroughVisited(AS);
- ++AnnotatedCnt;
- continue; // Fallthrough annotation, good.
- }
+ const Stmt *LastStmt = getLastStmt(*P);
+ if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) {
+ markFallthroughVisited(AS);
+ ++AnnotatedCnt;
+ continue; // Fallthrough annotation, good.
+ }
- if (!LastStmt) { // This block contains no executable statements.
- // Traverse its predecessors.
- std::copy(P->pred_begin(), P->pred_end(),
- std::back_inserter(BlockQueue));
- continue;
- }
+ if (!LastStmt) { // This block contains no executable statements.
+ // Traverse its predecessors.
+ std::copy(P->pred_begin(), P->pred_end(),
+ std::back_inserter(BlockQueue));
+ continue;
+ }
- ++UnannotatedCnt;
+ ++UnannotatedCnt;
}
return !!UnannotatedCnt;
}
@@ -1339,63 +1340,64 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor {
return true;
}
- // We don't want to traverse local type declarations. We analyze their
- // methods separately.
- bool TraverseDecl(Decl *D) override { return true; }
+ // We don't want to traverse local type declarations. We analyze their
+ // methods separately.
+ bool TraverseDecl(Decl *D) override { return true; }
- // We analyze lambda bodies separately. Skip them here.
- bool TraverseLambdaExpr(LambdaExpr *LE) override {
- // Traverse the captures, but not the body.
- for (const auto C : zip(LE->captures(), LE->capture_inits()))
- TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C));
- return true;
- }
+ // We analyze lambda bodies separately. Skip them here.
+ bool TraverseLambdaExpr(LambdaExpr *LE) override {
+ // Traverse the captures, but not the body.
+ for (const auto C : zip(LE->captures(), LE->capture_inits()))
+ TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C));
+ return true;
+ }
-private:
- static const AttributedStmt *asFallThroughAttr(const Stmt *S) {
- if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) {
- if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs()))
- return AS;
+ private:
+
+ static const AttributedStmt *asFallThroughAttr(const Stmt *S) {
+ if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) {
+ if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs()))
+ return AS;
+ }
+ return nullptr;
}
- return nullptr;
- }
- static const Stmt *getLastStmt(const CFGBlock &B) {
- if (const Stmt *Term = B.getTerminatorStmt())
- return Term;
- for (const CFGElement &Elem : llvm::reverse(B))
- if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>())
- return CS->getStmt();
- // Workaround to detect a statement thrown out by CFGBuilder:
- // case X: {} case Y:
- // case X: ; case Y:
- if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
- if (!isa<SwitchCase>(SW->getSubStmt()))
- return SW->getSubStmt();
+ static const Stmt *getLastStmt(const CFGBlock &B) {
+ if (const Stmt *Term = B.getTerminatorStmt())
+ return Term;
+ for (const CFGElement &Elem : llvm::reverse(B))
+ if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>())
+ return CS->getStmt();
+ // Workaround to detect a statement thrown out by CFGBuilder:
+ // case X: {} case Y:
+ // case X: ; case Y:
+ if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
+ if (!isa<SwitchCase>(SW->getSubStmt()))
+ return SW->getSubStmt();
- return nullptr;
- }
+ return nullptr;
+ }
- bool FoundSwitchStatements;
- AttrStmts FallthroughStmts;
- Sema &S;
- llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks;
+ bool FoundSwitchStatements;
+ AttrStmts FallthroughStmts;
+ Sema &S;
+ llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks;
};
} // anonymous namespace
static StringRef getFallthroughAttrSpelling(Preprocessor &PP,
SourceLocation Loc) {
- TokenValue FallthroughTokens[] = {tok::l_square, tok::l_square,
- PP.getIdentifierInfo("fallthrough"),
- tok::r_square, tok::r_square};
-
- TokenValue ClangFallthroughTokens[] = {tok::l_square,
- tok::l_square,
- PP.getIdentifierInfo("clang"),
- tok::coloncolon,
- PP.getIdentifierInfo("fallthrough"),
- tok::r_square,
- tok::r_square};
+ TokenValue FallthroughTokens[] = {
+ tok::l_square, tok::l_square,
+ PP.getIdentifierInfo("fallthrough"),
+ tok::r_square, tok::r_square
+ };
+
+ TokenValue ClangFallthroughTokens[] = {
+ tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
+ tok::coloncolon, PP.getIdentifierInfo("fallthrough"),
+ tok::r_square, tok::r_square
+ };
bool PreferClangAttr = !PP.getLangOpts().CPlusPlus17 && !PP.getLangOpts().C23;
@@ -1510,12 +1512,13 @@ static bool isInLoop(const ASTContext &Ctx, const ParentMap &PM,
static void diagnoseRepeatedUseOfWeak(Sema &S,
const sema::FunctionScopeInfo *CurFn,
- const Decl *D, const ParentMap &PM) {
+ const Decl *D,
+ const ParentMap &PM) {
typedef sema::FunctionScopeInfo::WeakObjectProfileTy WeakObjectProfileTy;
typedef sema::FunctionScopeInfo::WeakObjectUseMap WeakObjectUseMap;
typedef sema::FunctionScopeInfo::WeakUseVector WeakUseVector;
typedef std::pair<const Stmt *, WeakObjectUseMap::const_iterator>
- StmtUsesPair;
+ StmtUsesPair;
ASTContext &Ctx = S.getASTContext();
@@ -1529,7 +1532,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S,
// Find the first read of the weak object.
WeakUseVector::const_iterator UI = Uses.begin(), UE = Uses.end();
- for (; UI != UE; ++UI) {
+ for ( ; UI != UE; ++UI) {
if (UI->isUnsafe())
break;
}
@@ -1586,7 +1589,12 @@ static void diagnoseRepeatedUseOfWeak(Sema &S,
// warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak.
// FIXME: Should we use a common classification enum and the same set of
// possibilities all throughout Sema?
- enum { Function, Method, Block, Lambda } FunctionKind;
+ enum {
+ Function,
+ Method,
+ Block,
+ Lambda
+ } FunctionKind;
if (isa<sema::BlockScopeInfo>(CurFn))
FunctionKind = Block;
@@ -1617,7 +1625,12 @@ static void diagnoseRepeatedUseOfWeak(Sema &S,
// Classify the weak object being accessed for better warning text.
// This enum should stay in sync with the cases in
// warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak.
- enum { Variable, Property, ImplicitProperty, Ivar } ObjectKind;
+ enum {
+ Variable,
+ Property,
+ ImplicitProperty,
+ Ivar
+ } ObjectKind;
const NamedDecl *KeyProp = Key.getProperty();
if (isa<VarDecl>(KeyProp))
@@ -1719,7 +1732,7 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
}
private:
- static bool hasAlwaysUninitializedUse(const UsesVec *vec) {
+ static bool hasAlwaysUninitializedUse(const UsesVec* vec) {
return llvm::any_of(*vec, [](const UninitUse &U) {
return U.getKind() == UninitUse::Always ||
U.getKind() == UninitUse::AfterCall ||
@@ -1969,10 +1982,10 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
: getNotes();
}
-public:
+ public:
ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL)
- : S(S), FunLocation(FL), FunEndLocation(FEL), CurrentFunction(nullptr),
- Verbose(false) {}
+ : S(S), FunLocation(FL), FunEndLocation(FEL),
+ CurrentFunction(nullptr), Verbose(false) {}
void setVerbose(bool b) { Verbose = b; }
@@ -2067,18 +2080,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
bool ReentrancyMismatch) override {
unsigned DiagID = 0;
switch (LEK) {
- case LEK_LockedSomePredecessors:
- DiagID = diag::warn_lock_some_predecessors;
- break;
- case LEK_LockedSomeLoopIterations:
- DiagID = diag::warn_expecting_lock_held_on_loop;
- break;
- case LEK_LockedAtEndOfFunction:
- DiagID = diag::warn_no_unlock;
- break;
- case LEK_NotLockedAtEndOfFunction:
- DiagID = diag::warn_expecting_locked;
- break;
+ case LEK_LockedSomePredecessors:
+ DiagID = diag::warn_lock_some_predecessors;
+ break;
+ case LEK_LockedSomeLoopIterations:
+ DiagID = diag::warn_expecting_lock_held_on_loop;
+ break;
+ case LEK_LockedAtEndOfFunction:
+ DiagID = diag::warn_no_unlock;
+ break;
+ case LEK_NotLockedAtEndOfFunction:
+ DiagID = diag::warn_expecting_locked;
+ break;
}
if (LocEndOfScope.isInvalid())
LocEndOfScope = FunEndLocation;
@@ -2124,7 +2137,7 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
break;
}
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
- << D << getLockKindFromAccessKind(AK));
+ << D << getLockKindFromAccessKind(AK));
Warnings.emplace_back(std::move(Warning), getNotes());
}
@@ -2135,42 +2148,43 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
unsigned DiagID = 0;
if (PossibleMatch) {
switch (POK) {
- case POK_VarAccess:
- DiagID = diag::warn_variable_requires_lock_precise;
- break;
- case POK_VarDereference:
- DiagID = diag::warn_var_deref_requires_lock_precise;
- break;
- case POK_FunctionCall:
- DiagID = diag::warn_fun_requires_lock_precise;
- break;
- case POK_PassByRef:
- DiagID = diag::warn_guarded_pass_by_reference;
- break;
- case POK_PtPassByRef:
- DiagID = diag::warn_pt_guarded_pass_by_reference;
- break;
- case POK_ReturnByRef:
- DiagID = diag::warn_guarded_return_by_reference;
- break;
- case POK_PtReturnByRef:
- DiagID = diag::warn_pt_guarded_return_by_reference;
- break;
- case POK_PassPointer:
- DiagID = diag::warn_guarded_pass_pointer;
- break;
- case POK_PtPassPointer:
- DiagID = diag::warn_pt_guarded_pass_pointer;
- break;
- case POK_ReturnPointer:
- DiagID = diag::warn_guarded_return_pointer;
- break;
- case POK_PtReturnPointer:
- DiagID = diag::warn_pt_guarded_return_pointer;
- break;
+ case POK_VarAccess:
+ DiagID = diag::warn_variable_requires_lock_precise;
+ break;
+ case POK_VarDereference:
+ DiagID = diag::warn_var_deref_requires_lock_precise;
+ break;
+ case POK_FunctionCall:
+ DiagID = diag::warn_fun_requires_lock_precise;
+ break;
+ case POK_PassByRef:
+ DiagID = diag::warn_guarded_pass_by_reference;
+ break;
+ case POK_PtPassByRef:
+ DiagID = diag::warn_pt_guarded_pass_by_reference;
+ break;
+ case POK_ReturnByRef:
+ DiagID = diag::warn_guarded_return_by_reference;
+ break;
+ case POK_PtReturnByRef:
+ DiagID = diag::warn_pt_guarded_return_by_reference;
+ break;
+ case POK_PassPointer:
+ DiagID = diag::warn_guarded_pass_pointer;
+ break;
+ case POK_PtPassPointer:
+ DiagID = diag::warn_pt_guarded_pass_pointer;
+ break;
+ case POK_ReturnPointer:
+ DiagID = diag::warn_guarded_return_pointer;
+ break;
+ case POK_PtReturnPointer:
+ DiagID = diag::warn_pt_guarded_return_pointer;
+ break;
}
- PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
- << Kind << D << LockName << LK);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
+ << D
+ << LockName << LK);
PartialDiagnosticAt Note(Loc, S.PDiag(diag::note_found_mutex_near_match)
<< *PossibleMatch);
if (Verbose && POK == POK_VarAccess) {
@@ -2182,42 +2196,43 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
Warnings.emplace_back(std::move(Warning), getNotes(Note));
} else {
switch (POK) {
- case POK_VarAccess:
- DiagID = diag::warn_variable_requires_lock;
- break;
- case POK_VarDereference:
- DiagID = diag::warn_var_deref_requires_lock;
- break;
- case POK_FunctionCall:
- DiagID = diag::warn_fun_requires_lock;
- break;
- case POK_PassByRef:
- DiagID = diag::warn_guarded_pass_by_reference;
- break;
- case POK_PtPassByRef:
- DiagID = diag::warn_pt_guarded_pass_by_reference;
- break;
- case POK_ReturnByRef:
- DiagID = diag::warn_guarded_return_by_reference;
- break;
- case POK_PtReturnByRef:
- DiagID = diag::warn_pt_guarded_return_by_reference;
- break;
- case POK_PassPointer:
- DiagID = diag::warn_guarded_pass_pointer;
- break;
- case POK_PtPassPointer:
- DiagID = diag::warn_pt_guarded_pass_pointer;
- break;
- case POK_ReturnPointer:
- DiagID = diag::warn_guarded_return_pointer;
- break;
- case POK_PtReturnPointer:
- DiagID = diag::warn_pt_guarded_return_pointer;
- break;
+ case POK_VarAccess:
+ DiagID = diag::warn_variable_requires_lock;
+ break;
+ case POK_VarDereference:
+ DiagID = diag::warn_var_deref_requires_lock;
+ break;
+ case POK_FunctionCall:
+ DiagID = diag::warn_fun_requires_lock;
+ break;
+ case POK_PassByRef:
+ DiagID = diag::warn_guarded_pass_by_reference;
+ break;
+ case POK_PtPassByRef:
+ DiagID = diag::warn_pt_guarded_pass_by_reference;
+ break;
+ case POK_ReturnByRef:
+ DiagID = diag::warn_guarded_return_by_reference;
+ break;
+ case POK_PtReturnByRef:
+ DiagID = diag::warn_pt_guarded_return_by_reference;
+ break;
+ case POK_PassPointer:
+ DiagID = diag::warn_guarded_pass_pointer;
+ break;
+ case POK_PtPassPointer:
+ DiagID = diag::warn_pt_guarded_pass_pointer;
+ break;
+ case POK_ReturnPointer:
+ DiagID = diag::warn_guarded_return_pointer;
+ break;
+ case POK_PtReturnPointer:
+ DiagID = diag::warn_pt_guarded_return_pointer;
+ break;
}
- PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
- << Kind << D << LockName << LK);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
+ << D
+ << LockName << LK);
if (Verbose && POK == POK_VarAccess) {
PartialDiagnosticAt Note(D->getLocation(),
S.PDiag(diag::note_guarded_by_declared_here));
@@ -2229,9 +2244,9 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg,
SourceLocation Loc) override {
- PartialDiagnosticAt Warning(
- Loc, S.PDiag(diag::warn_acquire_requires_negative_cap)
- << Kind << LockName << Neg);
+ PartialDiagnosticAt Warning(Loc,
+ S.PDiag(diag::warn_acquire_requires_negative_cap)
+ << Kind << LockName << Neg);
Warnings.emplace_back(std::move(Warning), getNotes());
}
@@ -2251,20 +2266,22 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
void handleLockAcquiredBefore(StringRef Kind, Name L1Name, Name L2Name,
SourceLocation Loc) override {
- PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_acquired_before)
- << Kind << L1Name << L2Name);
+ PartialDiagnosticAt Warning(Loc,
+ S.PDiag(diag::warn_acquired_before) << Kind << L1Name << L2Name);
Warnings.emplace_back(std::move(Warning), getNotes());
}
void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) override {
- PartialDiagnosticAt Warning(
- Loc, S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name);
+ PartialDiagnosticAt Warning(Loc,
+ S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name);
Warnings.emplace_back(std::move(Warning), getNotes());
}
- void enterFunction(const FunctionDecl *FD) override { CurrentFunction = FD; }
+ void enterFunction(const FunctionDecl* FD) override {
+ CurrentFunction = FD;
+ }
- void leaveFunction(const FunctionDecl *FD) override {
+ void leaveFunction(const FunctionDecl* FD) override {
CurrentFunction = nullptr;
}
};
@@ -2285,6 +2302,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
DiagList Warnings;
public:
+
ConsumedWarningsHandler(Sema &S) : S(S) {}
void emitDiagnostics() override {
@@ -2298,8 +2316,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
void warnLoopStateMismatch(SourceLocation Loc,
StringRef VariableName) override {
- PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch)
- << VariableName);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) <<
+ VariableName);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
@@ -2309,9 +2327,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
StringRef ExpectedState,
StringRef ObservedState) override {
- PartialDiagnosticAt Warning(
- Loc, S.PDiag(diag::warn_param_return_typestate_mismatch)
- << VariableName << ExpectedState << ObservedState);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_param_return_typestate_mismatch) << VariableName <<
+ ExpectedState << ObservedState);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
@@ -2319,18 +2337,16 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
void warnParamTypestateMismatch(SourceLocation Loc, StringRef ExpectedState,
StringRef ObservedState) override {
- PartialDiagnosticAt Warning(Loc,
- S.PDiag(diag::warn_param_typestate_mismatch)
- << ExpectedState << ObservedState);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_param_typestate_mismatch) << ExpectedState << ObservedState);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
StringRef TypeName) override {
- PartialDiagnosticAt Warning(
- Loc, S.PDiag(diag::warn_return_typestate_for_unconsumable_type)
- << TypeName);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_return_typestate_for_unconsumable_type) << TypeName);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
@@ -2338,9 +2354,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState,
StringRef ObservedState) override {
- PartialDiagnosticAt Warning(Loc,
- S.PDiag(diag::warn_return_typestate_mismatch)
- << ExpectedState << ObservedState);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
@@ -2348,9 +2363,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State,
SourceLocation Loc) override {
- PartialDiagnosticAt Warning(Loc,
- S.PDiag(diag::warn_use_of_temp_in_invalid_state)
- << MethodName << State);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_use_of_temp_in_invalid_state) << MethodName << State);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
@@ -2358,9 +2372,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
void warnUseInInvalidState(StringRef MethodName, StringRef VariableName,
StringRef State, SourceLocation Loc) override {
- PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state)
- << MethodName << VariableName
- << State);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) <<
+ MethodName << VariableName << State);
Warnings.emplace_back(std::move(Warning), OptionalNotes());
}
@@ -2376,7 +2389,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
namespace {
class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
Sema &S;
- bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions?
+ bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions?
// Lists as a string the names of variables in `VarGroupForVD` except for `VD`
// itself:
@@ -2415,7 +2428,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
public:
UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions)
- : S(S), SuggestSuggestions(SuggestSuggestions) {}
+ : S(S), SuggestSuggestions(SuggestSuggestions) {}
void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl,
ASTContext &Ctx) override {
@@ -2600,7 +2613,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
#ifndef NDEBUG
if (areDebugNotesRequested())
- for (const DebugNote &Note : DebugNotesByVar[Variable])
+ for (const DebugNote &Note: DebugNotesByVar[Variable])
S.Diag(Note.first, diag::note_safe_buffer_debug_mode) << Note.second;
#endif
}
@@ -2702,7 +2715,8 @@ sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
MaxCFGBlocksPerFunction(0), NumUninitAnalysisFunctions(0),
NumUninitAnalysisVariables(0), MaxUninitAnalysisVariablesPerFunction(0),
NumUninitAnalysisBlockVisits(0),
- MaxUninitAnalysisBlockVisitsPerFunction(0) {}
+ MaxUninitAnalysisBlockVisitsPerFunction(0) {
+}
// We need this here for unique_ptr with forward declared class.
sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default;
@@ -2974,7 +2988,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU,
}
void clang::sema::AnalysisBasedWarnings::IssueWarnings(
- TranslationUnitDecl *TU) {
+ TranslationUnitDecl *TU) {
if (!TU)
return; // This is unexpected, give up quietly.
@@ -2988,7 +3002,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
// UnsafeBufferUsage analysis settings.
bool UnsafeBufferUsageCanEmitSuggestions = S.getLangOpts().CPlusPlus20;
- bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can.
+ bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can.
UnsafeBufferUsageCanEmitSuggestions &&
DiagOpts.ShowSafeBufferUsageSuggestions;
bool UnsafeBufferUsageShouldSuggestSuggestions =
@@ -3103,13 +3117,13 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
AC.getCFGBuildOptions().setAllAlwaysAdd();
} else {
AC.getCFGBuildOptions()
- .setAlwaysAdd(Stmt::BinaryOperatorClass)
- .setAlwaysAdd(Stmt::CompoundAssignOperatorClass)
- .setAlwaysAdd(Stmt::BlockExprClass)
- .setAlwaysAdd(Stmt::CStyleCastExprClass)
- .setAlwaysAdd(Stmt::DeclRefExprClass)
- .setAlwaysAdd(Stmt::ImplicitCastExprClass)
- .setAlwaysAdd(Stmt::UnaryOperatorClass);
+ .setAlwaysAdd(Stmt::BinaryOperatorClass)
+ .setAlwaysAdd(Stmt::CompoundAssignOperatorClass)
+ .setAlwaysAdd(Stmt::BlockExprClass)
+ .setAlwaysAdd(Stmt::CStyleCastExprClass)
+ .setAlwaysAdd(Stmt::DeclRefExprClass)
+ .setAlwaysAdd(Stmt::ImplicitCastExprClass)
+ .setAlwaysAdd(Stmt::UnaryOperatorClass);
}
if (EnableLifetimeSafetyAnalysis)
AC.getCFGBuildOptions().AddLifetime = true;
@@ -3190,10 +3204,12 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
++NumUninitAnalysisFunctions;
NumUninitAnalysisVariables += stats.NumVariablesAnalyzed;
NumUninitAnalysisBlockVisits += stats.NumBlockVisits;
- MaxUninitAnalysisVariablesPerFunction = std::max(
- MaxUninitAnalysisVariablesPerFunction, stats.NumVariablesAnalyzed);
- MaxUninitAnalysisBlockVisitsPerFunction = std::max(
- MaxUninitAnalysisBlockVisitsPerFunction, stats.NumBlockVisits);
+ MaxUninitAnalysisVariablesPerFunction =
+ std::max(MaxUninitAnalysisVariablesPerFunction,
+ stats.NumVariablesAnalyzed);
+ MaxUninitAnalysisBlockVisitsPerFunction =
+ std::max(MaxUninitAnalysisBlockVisitsPerFunction,
+ stats.NumBlockVisits);
}
}
}
@@ -3231,6 +3247,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, D->getBeginLoc()))
diagnoseRepeatedUseOfWeak(S, fscope, D, AC.getParentMap());
+
// Check for infinite self-recursion in functions
if (!Diags.isIgnored(diag::warn_infinite_recursive_function,
D->getBeginLoc())) {
@@ -3261,8 +3278,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
// If we successfully built a CFG for this context, record some more
// detail information about it.
NumCFGBlocks += cfg->getNumBlockIDs();
- MaxCFGBlocksPerFunction =
- std::max(MaxCFGBlocksPerFunction, cfg->getNumBlockIDs());
+ MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction,
+ cfg->getNumBlockIDs());
} else {
++NumFunctionsWithBadCFGs;
}
@@ -3274,7 +3291,7 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const {
unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
unsigned AvgCFGBlocksPerFunction =
- !NumCFGsBuilt ? 0 : NumCFGBlocks / NumCFGsBuilt;
+ !NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt;
llvm::errs() << NumFunctionsAnalyzed << " functions analyzed ("
<< NumFunctionsWithBadCFGs << " w/o CFGs).\n"
<< " " << NumCFGBlocks << " CFG blocks built.\n"
@@ -3283,14 +3300,10 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const {
<< " " << MaxCFGBlocksPerFunction
<< " max CFG blocks per function.\n";
- unsigned AvgUninitVariablesPerFunction =
- !NumUninitAnalysisFunctions
- ? 0
- : NumUninitAnalysisVariables / NumUninitAnalysisFunctions;
- unsigned AvgUninitBlockVisitsPerFunction =
- !NumUninitAnalysisFunctions
- ? 0
- : NumUninitAnalysisBlockVisits / NumUninitAnalysisFunctions;
+ unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0
+ : NumUninitAnalysisVariables/NumUninitAnalysisFunctions;
+ unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0
+ : NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions;
llvm::errs() << NumUninitAnalysisFunctions
<< " functions analyzed for uninitialiazed variables\n"
<< " " << NumUninitAnalysisVariables << " variables analyzed.\n"
>From 5d8471810be86fc33d55d1e99dc1c1f7a151acc9 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 26 Jan 2026 16:28:31 +0300
Subject: [PATCH 16/16] add noescape to IsLifetimeSafetyDiagnosticEnabled
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 2 ++
clang/test/Sema/warn-lifetime-safety-noescape.cpp | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index e64eb6b760c76..913962dc0c3e0 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -3099,6 +3099,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
!Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_permissive,
D->getBeginLoc()) ||
!Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_strict,
+ D->getBeginLoc()) ||
+ !Diags.isIgnored(diag::warn_lifetime_safety_noescape_escapes,
D->getBeginLoc());
bool EnableLifetimeSafetyAnalysis =
S.getLangOpts().EnableLifetimeSafety &&
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 90f81a71189ab..46c3a6753111c 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 -flifetime-safety-inference -Wlifetime-safety-noescape -Wlifetime-safety -verify %s
+// RUN: %clang_cc1 -fsyntax-only -flifetime-safety -flifetime-safety-inference -Wlifetime-safety-noescape -verify %s
#include "Inputs/lifetime-analysis.h"
More information about the cfe-commits
mailing list