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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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"
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to