https://github.com/davidmenggx updated 
https://github.com/llvm/llvm-project/pull/199149

>From 8b45c1b928f158b610764da31ffbbc5ad069fdcd Mon Sep 17 00:00:00 2001
From: David Meng <[email protected]>
Date: Thu, 21 May 2026 19:17:06 -0700
Subject: [PATCH 1/4] [LifetimeSafety] Add fix-it for misplaced lifetimebound
 attributes

This patch adds a fix-it hint for 
`warn_lifetime_safety_intra_tu_misplaced_lifetimebound`
and `warn_lifetime_safety_cross_tu_misplaced_lifetimebound` to the appropriate 
declaration.

The fix-it attribute is emitted in the correct location, accounting for pure 
virtual
functions, overrides, trailing return types, and default arguments. The message 
is suppressed
for macros.

Resolves #198634
---
 clang/lib/Sema/SemaLifetimeSafety.h           | 44 ++++++++++++++--
 ...afety-misplaced-lifetimebound-cross-tu.cpp |  3 ++
 ...afety-misplaced-lifetimebound-intra-tu.cpp | 52 ++++++++++++++++++-
 3 files changed, 94 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index af5202c33fed0..eb1854548f97c 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -297,9 +297,25 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
         Scope == WarningScope::CrossTU
             ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
             : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
-    S.Diag(Lexer::getLocForEndOfToken(FDecl->getEndLoc(), 0,
-                                      S.getSourceManager(), S.getLangOpts()),
-           DiagID);
+
+    SourceLocation DiagLoc = Lexer::getLocForEndOfToken(
+        FDecl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+
+    // Scope so diagnostic emits first.
+    {
+      auto DB = S.Diag(DiagLoc, DiagID);
+
+      SourceLocation FixItLoc;
+      if (const TypeSourceInfo *TSI = FDecl->getTypeSourceInfo())
+        FixItLoc =
+            Lexer::getLocForEndOfToken(TSI->getTypeLoc().getEndLoc(), 0,
+                                       S.getSourceManager(), S.getLangOpts());
+      else
+        FixItLoc = DiagLoc;
+
+      if (FixItLoc.isValid() && !FixItLoc.isMacroID())
+        DB << FixItHint::CreateInsertion(FixItLoc, " 
[[clang::lifetimebound]]");
+    }
 
     S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
@@ -315,7 +331,27 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
         Scope == WarningScope::CrossTU
             ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
             : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
-    S.Diag(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
+
+    // Scope so diagnostic emits first.
+    {
+      auto DB = S.Diag(PVDDecl->getBeginLoc(), DiagID)
+                << PVDDecl->getSourceRange();
+
+      SourceLocation FixItLoc;
+      if (PVDDecl->getIdentifier())
+        FixItLoc = Lexer::getLocForEndOfToken(
+            PVDDecl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
+      else if (const TypeSourceInfo *TSI = PVDDecl->getTypeSourceInfo())
+        FixItLoc =
+            Lexer::getLocForEndOfToken(TSI->getTypeLoc().getEndLoc(), 0,
+                                       S.getSourceManager(), S.getLangOpts());
+      else
+        FixItLoc = Lexer::getLocForEndOfToken(
+            PVDDecl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+
+      if (FixItLoc.isValid() && !FixItLoc.isMacroID())
+        DB << FixItHint::CreateInsertion(FixItLoc, " 
[[clang::lifetimebound]]");
+    }
 
     S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
index 5fd49023a042a..c763f4a422943 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
@@ -1,6 +1,7 @@
 // RUN: rm -rf %t
 // RUN: split-file %s %t
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t -verify 
%t/cross.cpp
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t 
-fdiagnostics-parseable-fixits %t/cross.cpp 2>&1 | FileCheck %s
 
 //--- cross.h
 struct HeaderObj {
@@ -8,10 +9,12 @@ struct HeaderObj {
 };
 
 HeaderObj &header_param(HeaderObj &obj); // expected-warning {{'lifetimebound' 
attribute on this definition is not visible to callers in other translation 
units; add it to the declaration instead}}
+// CHECK: fix-it:"{{.*}}cross.h":{[[#]]:{{[0-9]+}}-[[#]]:{{[0-9]+}}}:" 
{{\[\[}}clang::lifetimebound{{\]\]}}"
 
 struct HeaderS {
   HeaderObj data;
   HeaderObj &header_this(); // expected-warning {{'lifetimebound' attribute on 
this definition is not visible to callers in other translation units; add it to 
the declaration instead}}
+  // CHECK: fix-it:"{{.*}}cross.h":{[[#]]:{{[0-9]+}}-[[#]]:{{[0-9]+}}}:" 
{{\[\[}}clang::lifetimebound{{\]\]}}"
 };
 
 //--- cross.cpp
diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
index caf805cea5416..29ab6244d7826 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
@@ -1,10 +1,13 @@
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
 
 struct MyObj {
   ~MyObj() {}
 };
 
 MyObj &free_param(MyObj &obj);                           // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+
 MyObj &free_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
   return obj;
 }
@@ -12,8 +15,14 @@ MyObj &free_param(MyObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lif
 struct S {
   MyObj data;
   const MyObj &implicit_this_only();         // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+  
   const MyObj &param_only(const MyObj &obj); // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+  
   const MyObj &both(const MyObj &obj, bool); // expected-warning 2 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
 };
 
 const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
@@ -35,6 +44,8 @@ template <class T>
 struct MixedSpecializations {
   T data;
   T &both(T &arg, bool); // expected-warning 2 {{'lifetimebound' attribute on 
this definition is not visible to callers before the definition; add it to the 
declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
 };
 
 template <>
@@ -50,6 +61,8 @@ struct InternalObj {
 
 namespace {
 InternalObj &anon_param(InternalObj &obj);                           // 
expected-warning {{'lifetimebound' attribute on this definition is not visible 
to callers before the definition; add it to the declaration instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+
 InternalObj &anon_param(InternalObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
   return obj;
 }
@@ -57,6 +70,7 @@ InternalObj &anon_param(InternalObj &obj 
[[clang::lifetimebound]]) { // expected
 struct AnonS {
   InternalObj data;
   InternalObj &anon_this(); // expected-warning {{'lifetimebound' attribute on 
this definition is not visible to callers before the definition; add it to the 
declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
 };
 
 InternalObj &AnonS::anon_this() [[clang::lifetimebound]] { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
@@ -65,6 +79,8 @@ InternalObj &AnonS::anon_this() [[clang::lifetimebound]] { // 
expected-note {{'l
 } // namespace
 
 static InternalObj &static_param(InternalObj &obj); // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+
 static InternalObj &static_param(InternalObj &obj [[clang::lifetimebound]]) { 
// expected-note {{'lifetimebound' attribute appears here on the definition}}
   return obj;
 }
@@ -74,6 +90,8 @@ struct IntraSuppressedObj {
 };
 
 IntraSuppressedObj &intra_suppressed(IntraSuppressedObj &obj); // 
expected-warning {{'lifetimebound' attribute on this definition is not visible 
to callers before the definition; add it to the declaration instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+
 IntraSuppressedObj &intra_suppressed(
     IntraSuppressedObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
   return obj;
@@ -81,11 +99,11 @@ IntraSuppressedObj &intra_suppressed(
 
 struct View {
   friend View friend_redecl(MyObj &obj); // expected-warning {{'lifetimebound' 
attribute on this definition is not visible to callers before the definition; 
add it to the declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
 };
 
 // FIXME: This diagnoses an attribute inherited from another redeclaration, 
not one written on the definition. Once we enforce that redeclarations agree on 
lifetimebound, handle this with a dedicated warning and note.
 View friend_redecl(MyObj &obj [[clang::lifetimebound]]); // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
-
 View friend_redecl(MyObj &obj) {
   return View{};
 }
@@ -93,6 +111,7 @@ View friend_redecl(MyObj &obj) {
 template <typename T>
 // FIXME: Current analysis suggests adding to the primary template 
declaration, which is not ideal, as it will affect all specializations.
 MyObj &spec_func(T &obj); // expected-warning {{'lifetimebound' attribute on 
this definition is not visible to callers before the definition; add it to the 
declaration instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
 
 template <>
 // FIXME: Attribute is inhetired, diagnostic's wording is not correct.
@@ -100,3 +119,34 @@ MyObj &spec_func<MyObj>(MyObj &obj 
[[clang::lifetimebound]]); // expected-note {
 
 template <>
 MyObj &spec_func<MyObj>(MyObj &obj) { return obj; }
+
+MyObj get_default_obj();
+
+const MyObj &default_arg_param(const MyObj &obj = get_default_obj()); // 
expected-warning {{'lifetimebound' attribute on this definition is not visible 
to callers before the definition; add it to the declaration instead}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:48-[[@LINE-1]]:48}:" 
{{\[\[clang::lifetimebound\]\]}}"
+
+const MyObj &default_arg_param(const MyObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+struct Base {
+  virtual const MyObj& virtual_get(const MyObj& obj) const = 0;
+};
+
+struct Derived : Base {
+  auto virtual_get(const MyObj& obj) const -> const MyObj& override; // 
expected-warning {{'lifetimebound' attribute on this definition is not visible 
to callers before the definition; add it to the declaration instead}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:36-[[@LINE-1]]:36}:" 
{{\[\[clang::lifetimebound\]\]}}"
+};
+
+auto Derived::virtual_get(const MyObj& obj [[clang::lifetimebound]]) const -> 
const MyObj& { // expected-note {{'lifetimebound' attribute appears here on the 
definition}}
+  return obj;
+}
+
+#define REF_PARAM MyObj &obj
+
+MyObj &macro_param(REF_PARAM); // expected-warning {{'lifetimebound' attribute 
on this definition is not visible to callers before the definition; add it to 
the declaration instead}}
+// Fix-it suppressed for macro.
+
+MyObj &macro_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}

>From 30b95ef40a91b8c6a615f24aae4a64af783ae9ee Mon Sep 17 00:00:00 2001
From: David Meng <[email protected]>
Date: Fri, 22 May 2026 08:20:58 -0700
Subject: [PATCH 2/4] Apply NeKon69s feedback

---
 clang/lib/Sema/SemaLifetimeSafety.h           | 75 ++++++++++---------
 ...afety-misplaced-lifetimebound-cross-tu.cpp |  2 +
 ...afety-misplaced-lifetimebound-intra-tu.cpp |  5 +-
 3 files changed, 45 insertions(+), 37 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index eb1854548f97c..63898d496891e 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -298,25 +298,29 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
             ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
             : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
 
-    SourceLocation DiagLoc = Lexer::getLocForEndOfToken(
-        FDecl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
-
-    // Scope so diagnostic emits first.
-    {
-      auto DB = S.Diag(DiagLoc, DiagID);
-
-      SourceLocation FixItLoc;
-      if (const TypeSourceInfo *TSI = FDecl->getTypeSourceInfo())
-        FixItLoc =
-            Lexer::getLocForEndOfToken(TSI->getTypeLoc().getEndLoc(), 0,
-                                       S.getSourceManager(), S.getLangOpts());
-      else
-        FixItLoc = DiagLoc;
-
-      if (FixItLoc.isValid() && !FixItLoc.isMacroID())
-        DB << FixItHint::CreateInsertion(FixItLoc, " 
[[clang::lifetimebound]]");
+    const auto MDL = FDecl->getTypeSourceInfo()->getTypeLoc();
+    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
+        MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+
+    if (const auto *FPT = FDecl->getType()->getAs<FunctionProtoType>();
+        FPT && FPT->hasTrailingReturn()) {
+      // For trailing return types, 'getEndLoc()' includes the return type
+      // after '->', placing the attribute in an invalid position.
+      // Instead use 'getLocalRangeEnd()' which gives the '->' location
+      // for trailing returns, so find the last token before it.
+      const auto FTL = MDL.getAs<FunctionTypeLoc>();
+      assert(FTL);
+      InsertionPoint = Lexer::getLocForEndOfToken(
+          Lexer::findPreviousToken(FTL.getLocalRangeEnd(), 
S.getSourceManager(),
+                                   S.getLangOpts(),
+                                   /*IncludeComments=*/false)
+              ->getLocation(),
+          0, S.getSourceManager(), S.getLangOpts());
     }
 
+    S.Diag(InsertionPoint, DiagID) << FixItHint::CreateInsertion(
+        InsertionPoint, " [[clang::lifetimebound]]");
+
     S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
   }
@@ -332,27 +336,26 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
             ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
             : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
 
-    // Scope so diagnostic emits first.
-    {
-      auto DB = S.Diag(PVDDecl->getBeginLoc(), DiagID)
-                << PVDDecl->getSourceRange();
-
-      SourceLocation FixItLoc;
-      if (PVDDecl->getIdentifier())
-        FixItLoc = Lexer::getLocForEndOfToken(
-            PVDDecl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
-      else if (const TypeSourceInfo *TSI = PVDDecl->getTypeSourceInfo())
-        FixItLoc =
-            Lexer::getLocForEndOfToken(TSI->getTypeLoc().getEndLoc(), 0,
-                                       S.getSourceManager(), S.getLangOpts());
-      else
-        FixItLoc = Lexer::getLocForEndOfToken(
-            PVDDecl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
-
-      if (FixItLoc.isValid() && !FixItLoc.isMacroID())
-        DB << FixItHint::CreateInsertion(FixItLoc, " 
[[clang::lifetimebound]]");
+    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
+        PVDDecl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+    StringRef FixItText = " [[clang::lifetimebound]]";
+
+    if (!PVDDecl->getIdentifier()) {
+      // For unnamed parameters, placing attributes after the type would be
+      // parsed as a type attribute, not a parameter attribute.
+      InsertionPoint = PVDDecl->getBeginLoc();
+      FixItText = "[[clang::lifetimebound]] ";
+    } else if (PVDDecl->hasDefaultArg()) {
+      // If the parameter has a default argument, place the attribute after the
+      // named argument.
+      InsertionPoint = Lexer::getLocForEndOfToken(
+          PVDDecl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
     }
 
+    S.Diag(PVDDecl->getBeginLoc(), DiagID)
+        << PVDDecl->getSourceRange()
+        << FixItHint::CreateInsertion(InsertionPoint, FixItText);
+
     S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
   }
diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
index c763f4a422943..d227b8d8656c7 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
@@ -2,6 +2,8 @@
 // RUN: split-file %s %t
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t -verify 
%t/cross.cpp
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t 
-fdiagnostics-parseable-fixits %t/cross.cpp 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -Wlifetime-safety-cross-tu-misplaced-lifetimebound 
-Wno-dangling -I%t -fixit %t/cross.cpp
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t -Werror 
%t/cross.cpp
 
 //--- cross.h
 struct HeaderObj {
diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
index 29ab6244d7826..9ee15ecac9ae3 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
@@ -1,5 +1,8 @@
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling -verify %s
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling 
-fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: cp %s %t.intra.cpp
+// RUN: %clang_cc1 -Wlifetime-safety-intra-tu-misplaced-lifetimebound 
-Wno-dangling -fixit %t.intra.cpp
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling -Werror 
%t.intra.cpp
 
 struct MyObj {
   ~MyObj() {}
@@ -145,7 +148,7 @@ auto Derived::virtual_get(const MyObj& obj 
[[clang::lifetimebound]]) const -> co
 #define REF_PARAM MyObj &obj
 
 MyObj &macro_param(REF_PARAM); // expected-warning {{'lifetimebound' attribute 
on this definition is not visible to callers before the definition; add it to 
the declaration instead}}
-// Fix-it suppressed for macro.
+// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
 
 MyObj &macro_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
   return obj;

>From f14a811ba3ce5a9546aa818a3636b25f15e9837b Mon Sep 17 00:00:00 2001
From: David Meng <[email protected]>
Date: Fri, 22 May 2026 10:40:11 -0700
Subject: [PATCH 3/4] Extract fix-it text and location into helper functions

---
 clang/lib/Sema/SemaLifetimeSafety.h | 127 ++++++++++++----------------
 1 file changed, 56 insertions(+), 71 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 63898d496891e..c075829cef92e 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -239,21 +239,9 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
         (Scope == WarningScope::CrossTU)
             ? diag::warn_lifetime_safety_cross_tu_param_suggestion
             : diag::warn_lifetime_safety_intra_tu_param_suggestion;
-    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
-        ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
-    StringRef FixItText = " [[clang::lifetimebound]]";
-    if (!ParmToAnnotate->getIdentifier()) {
-      // For unnamed parameters, placing attributes after the type would be
-      // parsed as a type attribute, not a parameter attribute.
-      InsertionPoint = ParmToAnnotate->getBeginLoc();
-      FixItText = "[[clang::lifetimebound]] ";
-    } else if (ParmToAnnotate->hasDefaultArg()) {
-      // If the parameter has a default argument, place the attribute after the
-      // named argument.
-      InsertionPoint =
-          Lexer::getLocForEndOfToken(ParmToAnnotate->getLocation(), 0,
-                                     S.getSourceManager(), S.getLangOpts());
-    }
+
+    auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(ParmToAnnotate);
+
     S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
         << ParmToAnnotate->getSourceRange()
         << FixItHint::CreateInsertion(InsertionPoint, FixItText);
@@ -298,28 +286,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
             ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
             : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
 
-    const auto MDL = FDecl->getTypeSourceInfo()->getTypeLoc();
-    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
-        MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+    auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(FDecl);
 
-    if (const auto *FPT = FDecl->getType()->getAs<FunctionProtoType>();
-        FPT && FPT->hasTrailingReturn()) {
-      // For trailing return types, 'getEndLoc()' includes the return type
-      // after '->', placing the attribute in an invalid position.
-      // Instead use 'getLocalRangeEnd()' which gives the '->' location
-      // for trailing returns, so find the last token before it.
-      const auto FTL = MDL.getAs<FunctionTypeLoc>();
-      assert(FTL);
-      InsertionPoint = Lexer::getLocForEndOfToken(
-          Lexer::findPreviousToken(FTL.getLocalRangeEnd(), 
S.getSourceManager(),
-                                   S.getLangOpts(),
-                                   /*IncludeComments=*/false)
-              ->getLocation(),
-          0, S.getSourceManager(), S.getLangOpts());
-    }
-
-    S.Diag(InsertionPoint, DiagID) << FixItHint::CreateInsertion(
-        InsertionPoint, " [[clang::lifetimebound]]");
+    S.Diag(InsertionPoint, DiagID)
+        << FixItHint::CreateInsertion(InsertionPoint, FixItText);
 
     S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
@@ -336,21 +306,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
             ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
             : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
 
-    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
-        PVDDecl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
-    StringRef FixItText = " [[clang::lifetimebound]]";
-
-    if (!PVDDecl->getIdentifier()) {
-      // For unnamed parameters, placing attributes after the type would be
-      // parsed as a type attribute, not a parameter attribute.
-      InsertionPoint = PVDDecl->getBeginLoc();
-      FixItText = "[[clang::lifetimebound]] ";
-    } else if (PVDDecl->hasDefaultArg()) {
-      // If the parameter has a default argument, place the attribute after the
-      // named argument.
-      InsertionPoint = Lexer::getLocForEndOfToken(
-          PVDDecl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
-    }
+    auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(PVDDecl);
 
     S.Diag(PVDDecl->getBeginLoc(), DiagID)
         << PVDDecl->getSourceRange()
@@ -366,28 +322,13 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     unsigned DiagID = (Scope == WarningScope::CrossTU)
                           ? diag::warn_lifetime_safety_cross_tu_this_suggestion
                           : 
diag::warn_lifetime_safety_intra_tu_this_suggestion;
-    const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
-    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
-        MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
-    if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
-        FPT && FPT->hasTrailingReturn()) {
-      // For trailing return types, 'getEndLoc()' includes the return type
-      // after '->', placing the attribute in an invalid position.
-      // Instead use 'getLocalRangeEnd()' which gives the '->' location
-      // for trailing returns, so find the last token before it.
-      const auto FTL = MDL.getAs<FunctionTypeLoc>();
-      assert(FTL);
-      InsertionPoint = Lexer::getLocForEndOfToken(
-          Lexer::findPreviousToken(FTL.getLocalRangeEnd(), 
S.getSourceManager(),
-                                   S.getLangOpts(),
-                                   /*IncludeComments=*/false)
-              ->getLocation(),
-          0, S.getSourceManager(), S.getLangOpts());
-    }
+
+    auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(MD);
+
     S.Diag(InsertionPoint, DiagID)
         << MD->getNameInfo().getSourceRange()
-        << FixItHint::CreateInsertion(InsertionPoint,
-                                      " [[clang::lifetimebound]]");
+        << FixItHint::CreateInsertion(InsertionPoint, FixItText);
+
     S.Diag(EscapeExpr->getBeginLoc(),
            diag::note_lifetime_safety_suggestion_returned_here)
         << EscapeExpr->getSourceRange();
@@ -436,6 +377,50 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 
 private:
   Sema &S;
+
+  std::pair<SourceLocation, StringRef>
+  getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
+    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
+        Decl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+    StringRef FixItText = " [[clang::lifetimebound]]";
+
+    if (!Decl->getIdentifier()) {
+      // For unnamed parameters, placing attributes after the type would be
+      // parsed as a type attribute, not a parameter attribute.
+      InsertionPoint = Decl->getBeginLoc();
+      FixItText = "[[clang::lifetimebound]] ";
+    } else if (Decl->hasDefaultArg()) {
+      // If the parameter has a default argument, place the attribute after the
+      // named argument.
+      InsertionPoint = Lexer::getLocForEndOfToken(
+          Decl->getLocation(), 0, S.getSourceManager(), S.getLangOpts());
+    }
+    return {InsertionPoint, FixItText};
+  }
+
+  std::pair<SourceLocation, StringRef>
+  getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
+    const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
+    SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
+        MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
+
+    if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
+        FPT && FPT->hasTrailingReturn()) {
+      // For trailing return types, 'getEndLoc()' includes the return type
+      // after '->', placing the attribute in an invalid position.
+      // Instead use 'getLocalRangeEnd()' which gives the '->' location
+      // for trailing returns, so find the last token before it.
+      const auto FTL = MDL.getAs<FunctionTypeLoc>();
+      assert(FTL);
+      InsertionPoint = Lexer::getLocForEndOfToken(
+          Lexer::findPreviousToken(FTL.getLocalRangeEnd(), 
S.getSourceManager(),
+                                   S.getLangOpts(),
+                                   /*IncludeComments=*/false)
+              ->getLocation(),
+          0, S.getSourceManager(), S.getLangOpts());
+    }
+    return {InsertionPoint, " [[clang::lifetimebound]]"};
+  }
 };
 
 } // namespace clang::lifetimes

>From 031c191e25f7be0f0b9166d31a35b00fc6a91c06 Mon Sep 17 00:00:00 2001
From: David Meng <[email protected]>
Date: Mon, 25 May 2026 07:50:32 -0700
Subject: [PATCH 4/4] Apply usx95s feedback

---
 clang/lib/Sema/SemaLifetimeSafety.h                  |  4 ++--
 ...etime-safety-misplaced-lifetimebound-intra-tu.cpp | 12 +++++++-----
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index c075829cef92e..7a3baa9adf2e3 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -376,8 +376,6 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
   }
 
 private:
-  Sema &S;
-
   std::pair<SourceLocation, StringRef>
   getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
     SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
@@ -421,6 +419,8 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     }
     return {InsertionPoint, " [[clang::lifetimebound]]"};
   }
+
+  Sema &S;
 };
 
 } // namespace clang::lifetimes
diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
index 9ee15ecac9ae3..801e10e289f31 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
@@ -107,6 +107,7 @@ struct View {
 
 // FIXME: This diagnoses an attribute inherited from another redeclaration, 
not one written on the definition. Once we enforce that redeclarations agree on 
lifetimebound, handle this with a dedicated warning and note.
 View friend_redecl(MyObj &obj [[clang::lifetimebound]]); // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
+
 View friend_redecl(MyObj &obj) {
   return View{};
 }
@@ -124,9 +125,9 @@ template <>
 MyObj &spec_func<MyObj>(MyObj &obj) { return obj; }
 
 MyObj get_default_obj();
-
-const MyObj &default_arg_param(const MyObj &obj = get_default_obj()); // 
expected-warning {{'lifetimebound' attribute on this definition is not visible 
to callers before the definition; add it to the declaration instead}}
-// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:48-[[@LINE-1]]:48}:" 
{{\[\[clang::lifetimebound\]\]}}"
+const MyObj &default_arg_param(const MyObj&
+    obj  // CHECK: 
fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+    = get_default_obj()); // expected-warning@-2 {{'lifetimebound' attribute 
on this definition is not visible to callers before the definition; add it to 
the declaration instead}}
 
 const MyObj &default_arg_param(const MyObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
   return obj;
@@ -137,8 +138,9 @@ struct Base {
 };
 
 struct Derived : Base {
-  auto virtual_get(const MyObj& obj) const -> const MyObj& override; // 
expected-warning {{'lifetimebound' attribute on this definition is not visible 
to callers before the definition; add it to the declaration instead}}
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:36-[[@LINE-1]]:36}:" 
{{\[\[clang::lifetimebound\]\]}}"
+  auto virtual_get(const MyObj& 
+      obj  // CHECK: 
fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
+  ) const -> const MyObj& override; // expected-warning@-2 {{'lifetimebound' 
attribute on this definition is not visible to callers before the definition; 
add it to the declaration instead}}
 };
 
 auto Derived::virtual_get(const MyObj& obj [[clang::lifetimebound]]) const -> 
const MyObj& { // expected-note {{'lifetimebound' attribute appears here on the 
definition}}

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

Reply via email to