https://github.com/zeyi2 updated 
https://github.com/llvm/llvm-project/pull/204045

>From 1ee4bc273b8a1ecc17e8645a13ee97986d397ab2 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Tue, 16 Jun 2026 10:02:39 +0800
Subject: [PATCH 1/7] [LifetimeSafety] Prefer macro spelling for lifetimebound
 fix-its

---
 clang/lib/Sema/SemaLifetimeSafety.h           | 26 ++++++++++----
 .../test/Sema/warn-lifetime-safety-fixits.cpp | 34 +++++++++++++++++++
 2 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 6da4953dea56d..f76fb6a58b8f9 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -19,6 +19,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/Sema.h"
 #include <string>
 
@@ -403,27 +404,39 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
   }
 
 private:
-  std::pair<SourceLocation, StringRef>
+  std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace) 
{
+    const Preprocessor &PP = S.getPreprocessor();
+    const StringRef Spelling = PP.getLastMacroWithSpelling(
+        Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
+              tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
+              tok::r_square, tok::r_square});
+    const std::string Text =
+        Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
+    return LeadingSpace ? " " + Text : Text + " ";
+  }
+
+  std::pair<SourceLocation, std::string>
   getLifetimeBoundFixIt(const ParmVarDecl *Decl) {
     SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
         Decl->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts());
-    StringRef FixItText = " [[clang::lifetimebound]]";
+    bool LeadingSpace = true;
 
     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]] ";
+      LeadingSpace = false;
     } 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};
+    return {InsertionPoint,
+            getLifetimeBoundFixItText(InsertionPoint, LeadingSpace)};
   }
 
-  std::pair<SourceLocation, StringRef>
+  std::pair<SourceLocation, std::string>
   getLifetimeBoundFixIt(const CXXMethodDecl *MD) {
     const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
     SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
@@ -444,7 +457,8 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
               ->getLocation(),
           0, S.getSourceManager(), S.getLangOpts());
     }
-    return {InsertionPoint, " [[clang::lifetimebound]]"};
+    return {InsertionPoint, getLifetimeBoundFixItText(InsertionPoint,
+                                                      /*LeadingSpace=*/true)};
   }
 
   std::string getDiagSubjectDescription(const ValueDecl *VD) {
diff --git a/clang/test/Sema/warn-lifetime-safety-fixits.cpp 
b/clang/test/Sema/warn-lifetime-safety-fixits.cpp
index d9c7e8d3f0519..e1a89f9047457 100644
--- a/clang/test/Sema/warn-lifetime-safety-fixits.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-fixits.cpp
@@ -174,3 +174,37 @@ struct TrailingReturn {
     return data;
   }
 };
+
+#define MY_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
+
+View unnamed_macro(View);
+// CHECK: :[[@LINE-1]]:20: warning: parameter in intra-TU function should be 
marked
+// CHECK: 
fix-it:"{{.*}}":{[[@LINE-2]]:20-[[@LINE-2]]:20}:"MY_LIFETIMEBOUND_MACRO "
+View unnamed_macro(View a) {
+  return a;
+}
+
+View return_view_with_macro(View a) {
+  // CHECK: :[[@LINE-1]]:29: warning: parameter in intra-TU function should be 
marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:35-[[@LINE-2]]:35}:" 
MY_LIFETIMEBOUND_MACRO"
+  return a;
+}
+
+#define FIRST_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
+#define SECOND_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
+
+View return_view_with_latest_macro(View a) {
+  // CHECK: :[[@LINE-1]]:36: warning: parameter in intra-TU function should be 
marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:42-[[@LINE-2]]:42}:" 
SECOND_LIFETIMEBOUND_MACRO"
+  return a;
+}
+
+struct MacroMember {
+  MyObj data;
+
+  View get_view() {
+    // CHECK: :[[@LINE-1]]:18: warning: implicit this in intra-TU function 
should be marked
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:" 
SECOND_LIFETIMEBOUND_MACRO"
+    return data;
+  }
+};

>From dc8807842ffcb6aa90f15f500f35e017a1d4ad54 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Tue, 16 Jun 2026 11:07:22 +0800
Subject: [PATCH 2/7] fix crash !!!

---
 clang/lib/Sema/SemaLifetimeSafety.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index d2657252b3351..48099274ac982 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -424,6 +424,10 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 
 private:
   std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace) 
{
+    if (Loc.isInvalid())
+      return LeadingSpace ? " [[clang::lifetimebound]]"
+                          : "[[clang::lifetimebound]] ";
+
     const Preprocessor &PP = S.getPreprocessor();
     const StringRef Spelling = PP.getLastMacroWithSpelling(
         Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),

>From 6b2543187c2df7d107289fe5872e611673cf643c Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Tue, 16 Jun 2026 21:32:20 +0800
Subject: [PATCH 3/7] add command line option

---
 clang/docs/LifetimeSafety.rst                   |  7 ++++++-
 clang/include/clang/Basic/LangOptions.h         |  3 +++
 clang/include/clang/Options/Options.td          |  9 +++++++++
 clang/lib/Sema/SemaLifetimeSafety.h             | 17 ++++++++---------
 .../annotation-suggestions-fixits.cpp           | 11 +++++++++++
 5 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/clang/docs/LifetimeSafety.rst b/clang/docs/LifetimeSafety.rst
index 9ae2e6ee54826..7f024e610fd2c 100644
--- a/clang/docs/LifetimeSafety.rst
+++ b/clang/docs/LifetimeSafety.rst
@@ -462,6 +462,11 @@ more accurate checks in calling code.
 
 To enable annotation suggestions, use ``-Wlifetime-safety-suggestions``.
 
+Fix-it hints normally insert ``[[clang::lifetimebound]]``. If a visible
+object-like macro expands to ``[[clang::lifetimebound]]``, Clang will use the
+last such macro visible at the insertion point. To force a project-specific
+macro spelling, use ``-lifetime-safety-lifetimebound-macro=<macro>``.
+
 .. code-block:: c++
 
   #include <string_view>
@@ -688,5 +693,5 @@ Performance
 Lifetime analysis relies on Clang's CFG (Control Flow Graph). For functions
 with very large or complex CFGs, analysis time can sometimes be significant. 
To mitigate
 this, the analysis allows to skip functions where the number of CFG blocks 
exceeds
-a certain threshold, controlled by the ``-flifetime-safety-max-cfg-blocks=N`` 
language
+a certain threshold, controlled by the ``-lifetime-safety-max-cfg-blocks=N`` 
language
 option.
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 9af036156b1ad..53c4c1084784a 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -549,6 +549,9 @@ class LangOptions : public LangOptionsBase {
   /// A prefix map for __FILE__, __BASE_FILE__ and __builtin_FILE().
   std::map<std::string, std::string, std::greater<std::string>> MacroPrefixMap;
 
+  /// Macro name to use in lifetimebound fix-it suggestions.
+  std::string LifetimeSafetyLifetimeBoundMacro;
+
   /// Triples of the OpenMP targets that the host code codegen should
   /// take into account in order to generate accurate offloading descriptors.
   std::vector<llvm::Triple> OMPTargetTriples;
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index a4b9cb802af4d..248e1651c2cb4 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2025,6 +2025,15 @@ def lifetime_safety_max_cfg_blocks
                "count exceeding this threshold. Specify 0 for no limit.">,
       MarshallingInfoInt<LangOpts<"LifetimeSafetyMaxCFGBlocks">>;
 
+def lifetime_safety_lifetimebound_macro
+    : Joined<["-"], "lifetime-safety-lifetimebound-macro=">,
+      Group<m_Group>,
+      Visibility<[ClangOption, CC1Option]>,
+      MetaVarName<"<macro>">,
+      HelpText<"Use the given macro name when suggesting lifetimebound "
+               "attributes">,
+      MarshallingInfoString<LangOpts<"LifetimeSafetyLifetimeBoundMacro">>;
+
 defm lifetime_safety_inference
     : BoolFOption<"lifetime-safety-inference",
                   LangOpts<"EnableLifetimeSafetyInference">, DefaultFalse,
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 48099274ac982..f3f669428697f 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -424,15 +424,14 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 
 private:
   std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace) 
{
-    if (Loc.isInvalid())
-      return LeadingSpace ? " [[clang::lifetimebound]]"
-                          : "[[clang::lifetimebound]] ";
-
-    const Preprocessor &PP = S.getPreprocessor();
-    const StringRef Spelling = PP.getLastMacroWithSpelling(
-        Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
-              tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
-              tok::r_square, tok::r_square});
+    StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
+    if (Spelling.empty() && Loc.isValid()) {
+      const Preprocessor &PP = S.getPreprocessor();
+      Spelling = PP.getLastMacroWithSpelling(
+          Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
+                tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
+                tok::r_square, tok::r_square});
+    }
     const std::string Text =
         Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
     return LeadingSpace ? " " + Text : Text + " ";
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp 
b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
index e2c5816cc5193..7fc2c46a2f1ee 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
@@ -2,6 +2,11 @@
 // RUN:   -fexperimental-lifetime-safety-tu-analysis \
 // RUN:   -Wlifetime-safety-suggestions -Wlifetime-safety-annotation-placement 
-Wno-dangling \
 // RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++17 -flifetime-safety-inference \
+// RUN:   -fexperimental-lifetime-safety-tu-analysis \
+// RUN:   -Wlifetime-safety-suggestions -Wlifetime-safety-annotation-placement 
-Wno-dangling \
+// RUN:   -lifetime-safety-lifetimebound-macro=LIFETIMEBOUND_MACRO \
+// RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s 
--check-prefix=CHECK-MACRO
 // RUN: cp %s %t.cpp
 // RUN: %clang_cc1 -std=c++17 -flifetime-safety-inference \
 // RUN:   -fexperimental-lifetime-safety-tu-analysis \
@@ -30,6 +35,8 @@ struct [[gsl::Pointer()]] View {
 View return_view(View a) {
   // CHECK: :[[@LINE-1]]:18: warning: parameter in intra-TU function should be 
marked {{\[\[}}clang::lifetimebound]] [-Wlifetime-safety-intra-tu-suggestions]
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:24-[[@LINE-2]]:24}:" 
{{\[\[}}clang::lifetimebound]]"
+  // CHECK-MACRO: :[[@LINE-3]]:18: warning: parameter in intra-TU function 
should be marked
+  // CHECK-MACRO: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:24}:" 
LIFETIMEBOUND_MACRO"
   return a;
 }
 
@@ -196,6 +203,8 @@ View return_view_with_macro(View a) {
 View return_view_with_latest_macro(View a) {
   // CHECK: :[[@LINE-1]]:36: warning: parameter in intra-TU function should be 
marked
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:42-[[@LINE-2]]:42}:" 
SECOND_LIFETIMEBOUND_MACRO"
+  // CHECK-MACRO: :[[@LINE-3]]:36: warning: parameter in intra-TU function 
should be marked
+  // CHECK-MACRO: fix-it:"{{.*}}":{[[@LINE-4]]:42-[[@LINE-4]]:42}:" 
LIFETIMEBOUND_MACRO"
   return a;
 }
 
@@ -205,6 +214,8 @@ struct MacroMember {
   View get_view() {
     // CHECK: :[[@LINE-1]]:18: warning: implicit this in intra-TU function 
should be marked
     // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:" 
SECOND_LIFETIMEBOUND_MACRO"
+    // CHECK-MACRO: :[[@LINE-3]]:18: warning: implicit this in intra-TU 
function should be marked
+    // CHECK-MACRO: fix-it:"{{.*}}":{[[@LINE-4]]:18-[[@LINE-4]]:18}:" 
LIFETIMEBOUND_MACRO"
     return data;
   }
 };

>From e173a3b7fc01b61fa30f145815e0bc96978c5597 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Thu, 18 Jun 2026 18:16:15 +0800
Subject: [PATCH 4/7] handle GNU Attr spellings

---
 clang/docs/LifetimeSafety.rst                 |  7 +--
 clang/lib/Sema/SemaLifetimeSafety.h           | 14 +++--
 .../annotation-suggestions-fixits.cpp         | 19 +++++++
 .../misplaced-lifetimebound-intra-tu.cpp      | 51 ++++++++++++++++++-
 4 files changed, 83 insertions(+), 8 deletions(-)

diff --git a/clang/docs/LifetimeSafety.rst b/clang/docs/LifetimeSafety.rst
index 7f024e610fd2c..be9b32f8d4b2d 100644
--- a/clang/docs/LifetimeSafety.rst
+++ b/clang/docs/LifetimeSafety.rst
@@ -463,9 +463,10 @@ more accurate checks in calling code.
 To enable annotation suggestions, use ``-Wlifetime-safety-suggestions``.
 
 Fix-it hints normally insert ``[[clang::lifetimebound]]``. If a visible
-object-like macro expands to ``[[clang::lifetimebound]]``, Clang will use the
-last such macro visible at the insertion point. To force a project-specific
-macro spelling, use ``-lifetime-safety-lifetimebound-macro=<macro>``.
+object-like macro expands to ``[[clang::lifetimebound]]`` or
+``__attribute__((lifetimebound))``, Clang will use the last such macro
+visible at the insertion point. To force a project-specific macro spelling,
+use ``-lifetime-safety-lifetimebound-macro=<macro>``.
 
 .. code-block:: c++
 
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index f3f669428697f..dd170aaa14e11 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -423,7 +423,8 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
   }
 
 private:
-  std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace) 
{
+  std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
+                                        bool AllowGNUAttrMacro = true) {
     StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
     if (Spelling.empty() && Loc.isValid()) {
       const Preprocessor &PP = S.getPreprocessor();
@@ -431,6 +432,12 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
           Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
                 tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
                 tok::r_square, tok::r_square});
+
+      if (Spelling.empty() && AllowGNUAttrMacro)
+        Spelling = PP.getLastMacroWithSpelling(
+            Loc, {tok::kw___attribute, tok::l_paren, tok::l_paren,
+                  PP.getIdentifierInfo("lifetimebound"), tok::r_paren,
+                  tok::r_paren});
     }
     const std::string Text =
         Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
@@ -479,8 +486,9 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
               ->getLocation(),
           0, S.getSourceManager(), S.getLangOpts());
     }
-    return {InsertionPoint, getLifetimeBoundFixItText(InsertionPoint,
-                                                      /*LeadingSpace=*/true)};
+    return {InsertionPoint,
+            getLifetimeBoundFixItText(InsertionPoint, /*LeadingSpace=*/true,
+                                      /*AllowGNUAttrMacro=*/false)};
   }
 
   std::string getDiagSubjectDescription(const ValueDecl *VD) {
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp 
b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
index 7fc2c46a2f1ee..d7ea1b389196c 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
@@ -182,6 +182,25 @@ struct TrailingReturn {
   }
 };
 
+#define GNU_LIFETIMEBOUND_MACRO __attribute__((lifetimebound))
+
+View return_view_with_gnu_macro(View a) {
+  // CHECK: :[[@LINE-1]]:33: warning: parameter in intra-TU function should be 
marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:39-[[@LINE-2]]:39}:" 
GNU_LIFETIMEBOUND_MACRO"
+  return a;
+}
+
+struct OnlyGNUMember {
+  MyObj data;
+
+  View get_view() {
+    // CHECK: :[[@LINE-1]]:18: warning: implicit this in intra-TU function 
should be marked
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:" 
{{\[\[}}clang::lifetimebound]]"
+    return data;
+  }
+};
+
+#define LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
 #define MY_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
 
 View unnamed_macro(View);
diff --git 
a/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp 
b/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp
index 7fa4cae100509..25f5b6e94c28c 100644
--- a/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound 
-Wlifetime-safety-annotation-placement -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: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling 
-lifetime-safety-lifetimebound-macro=CONFIGURED_LIFETIMEBOUND_MACRO \
+// RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s 
--check-prefix=CHECK-CONFIG
 // 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
@@ -29,11 +31,11 @@ struct S {
   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 & // expected-warning {{'lifetimebound' 
attribute on this definition is not visible to callers before the definition; 
add it to the declaration instead}}
                           obj           // CHECK: 
fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
                           );
-  
+
   const MyObj &both(const MyObj &  // expected-warning {{'lifetimebound' 
attribute on this definition is not visible to callers before the definition; 
add it to the declaration instead}}
                     obj,           // CHECK-DAG: 
fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" 
{{\[\[clang::lifetimebound\]\]}}"
                     bool
@@ -167,3 +169,48 @@ struct Derived : Base {
 auto Derived::virtual_get(const MyObj& obj [[clang::lifetimebound]]) const -> 
const MyObj& { // expected-note {{'lifetimebound' attribute appears here on the 
definition}}
   return obj;
 }
+
+#define GNU_LIFETIMEBOUND_MACRO __attribute__((lifetimebound))
+
+MyObj &gnu_macro_param(MyObj&  // expected-warning {{'lifetimebound' attribute 
on this definition is not visible to callers before the definition; add it to 
the declaration instead}}
+                       obj     // CHECK: 
fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" 
GNU_LIFETIMEBOUND_MACRO"
+                       );
+
+MyObj &gnu_macro_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+struct OnlyGNUMember {
+  MyObj data;
+  const MyObj &only_gnu_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\]\]}}"
+};
+
+const MyObj &OnlyGNUMember::only_gnu_this() [[clang::lifetimebound]] { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
+  return data;
+}
+
+#define CONFIGURED_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
+#define LATEST_VISIBLE_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
+
+MyObj &configured_macro_param(MyObj&  // expected-warning {{'lifetimebound' 
attribute on this definition is not visible to callers before the definition; 
add it to the declaration instead}}
+                              obj     // CHECK: 
fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" 
LATEST_VISIBLE_LIFETIMEBOUND_MACRO"
+                                      // CHECK-CONFIG: 
fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" 
CONFIGURED_LIFETIMEBOUND_MACRO"
+                              );
+
+MyObj &configured_macro_param(MyObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+struct ConfiguredMacroMember {
+  MyObj data;
+  const MyObj &configured_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]+}}}:" 
LATEST_VISIBLE_LIFETIMEBOUND_MACRO"
+                                  // CHECK-CONFIG: 
fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
CONFIGURED_LIFETIMEBOUND_MACRO"
+};
+
+const MyObj &ConfiguredMacroMember::configured_this() [[clang::lifetimebound]] 
{ // expected-note {{'lifetimebound' attribute appears here on the definition}}
+  return data;
+}

>From 662d6c77cb036b9334126bdb4e700b58a5cb5c41 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Thu, 18 Jun 2026 23:35:30 +0800
Subject: [PATCH 5/7] handle C lang fix-its

---
 clang/lib/Sema/SemaLifetimeSafety.h           | 15 ++++---
 .../annotation-suggestions-fixits-c.c         | 45 +++++++++++++++++++
 2 files changed, 54 insertions(+), 6 deletions(-)
 create mode 100644 
clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 4bde272fb40a1..9714c4b6b6c95 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -444,13 +444,17 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 private:
   std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
                                         bool AllowGNUAttrMacro = true) {
+    const bool IsC = !S.getLangOpts().CPlusPlus;
+    const StringRef Fallback =
+        IsC ? "__attribute__((lifetimebound))" : "[[clang::lifetimebound]]";
     StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
     if (Spelling.empty() && Loc.isValid()) {
       const Preprocessor &PP = S.getPreprocessor();
-      Spelling = PP.getLastMacroWithSpelling(
-          Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
-                tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
-                tok::r_square, tok::r_square});
+      if (!IsC)
+        Spelling = PP.getLastMacroWithSpelling(
+            Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
+                  tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
+                  tok::r_square, tok::r_square});
 
       if (Spelling.empty() && AllowGNUAttrMacro)
         Spelling = PP.getLastMacroWithSpelling(
@@ -458,8 +462,7 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
                   PP.getIdentifierInfo("lifetimebound"), tok::r_paren,
                   tok::r_paren});
     }
-    const std::string Text =
-        Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
+    const std::string Text = Spelling.empty() ? Fallback.str() : 
Spelling.str();
     return LeadingSpace ? " " + Text : Text + " ";
   }
 
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c 
b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c
new file mode 100644
index 0000000000000..d0b0d0302106f
--- /dev/null
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c17 \
+// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
+// RUN:   -fexperimental-lifetime-safety-tu-analysis \
+// RUN:   -Wlifetime-safety-suggestions -Wno-dangling \
+// RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -std=c17 \
+// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
+// RUN:   -fexperimental-lifetime-safety-tu-analysis \
+// RUN:   -Wlifetime-safety-suggestions -Wno-dangling \
+// RUN:   -lifetime-safety-lifetimebound-macro=CONFIGURED_LIFETIMEBOUND_MACRO \
+// RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s 
--check-prefix=CHECK-CONFIG
+// RUN: cp %s %t.c
+// RUN: %clang_cc1 -std=c17 \
+// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
+// RUN:   -fexperimental-lifetime-safety-tu-analysis \
+// RUN:   -Wlifetime-safety-suggestions -Wno-dangling -fixit %t.c
+// RUN: %clang_cc1 -fsyntax-only -std=c17 \
+// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
+// RUN:   -fexperimental-lifetime-safety-tu-analysis \
+// RUN:   -Werror=lifetime-safety-suggestions -Wno-dangling %t.c
+
+int *return_ptr(int *p) {
+  // CHECK: :[[@LINE-1]]:17: warning: parameter in intra-TU function should be 
marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:23-[[@LINE-2]]:23}:" 
__attribute__((lifetimebound))"
+  return p;
+}
+
+#define CLANG_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
+
+int *return_ptr_with_clang_macro(int *p) {
+  // CHECK: :[[@LINE-1]]:{{[0-9]+}}: warning: parameter in intra-TU function 
should be marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
__attribute__((lifetimebound))"
+  return p;
+}
+
+#define CONFIGURED_LIFETIMEBOUND_MACRO __attribute__((lifetimebound))
+#define GNU_LIFETIMEBOUND_MACRO __attribute__((lifetimebound))
+
+int *return_ptr_with_gnu_macro(int *p) {
+  // CHECK: :[[@LINE-1]]:{{[0-9]+}}: warning: parameter in intra-TU function 
should be marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
GNU_LIFETIMEBOUND_MACRO"
+  // CHECK-CONFIG: :[[@LINE-3]]:{{[0-9]+}}: warning: parameter in intra-TU 
function should be marked
+  // CHECK-CONFIG: 
fix-it:"{{.*}}":{[[@LINE-4]]:{{[0-9]+}}-[[@LINE-4]]:{{[0-9]+}}}:" 
CONFIGURED_LIFETIMEBOUND_MACRO"
+  return p;
+}

>From 4c53904fa0a2afaf0841013aa78583cd6f560b4e Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Fri, 19 Jun 2026 14:28:39 +0800
Subject: [PATCH 6/7] Revert "handle C lang fix-its"

This reverts commit 662d6c77cb036b9334126bdb4e700b58a5cb5c41.
---
 clang/lib/Sema/SemaLifetimeSafety.h           | 15 +++----
 .../annotation-suggestions-fixits-c.c         | 45 -------------------
 2 files changed, 6 insertions(+), 54 deletions(-)
 delete mode 100644 
clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c

diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 9714c4b6b6c95..4bde272fb40a1 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -444,17 +444,13 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
 private:
   std::string getLifetimeBoundFixItText(SourceLocation Loc, bool LeadingSpace,
                                         bool AllowGNUAttrMacro = true) {
-    const bool IsC = !S.getLangOpts().CPlusPlus;
-    const StringRef Fallback =
-        IsC ? "__attribute__((lifetimebound))" : "[[clang::lifetimebound]]";
     StringRef Spelling = S.getLangOpts().LifetimeSafetyLifetimeBoundMacro;
     if (Spelling.empty() && Loc.isValid()) {
       const Preprocessor &PP = S.getPreprocessor();
-      if (!IsC)
-        Spelling = PP.getLastMacroWithSpelling(
-            Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
-                  tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
-                  tok::r_square, tok::r_square});
+      Spelling = PP.getLastMacroWithSpelling(
+          Loc, {tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"),
+                tok::coloncolon, PP.getIdentifierInfo("lifetimebound"),
+                tok::r_square, tok::r_square});
 
       if (Spelling.empty() && AllowGNUAttrMacro)
         Spelling = PP.getLastMacroWithSpelling(
@@ -462,7 +458,8 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
                   PP.getIdentifierInfo("lifetimebound"), tok::r_paren,
                   tok::r_paren});
     }
-    const std::string Text = Spelling.empty() ? Fallback.str() : 
Spelling.str();
+    const std::string Text =
+        Spelling.empty() ? "[[clang::lifetimebound]]" : Spelling.str();
     return LeadingSpace ? " " + Text : Text + " ";
   }
 
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c 
b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c
deleted file mode 100644
index d0b0d0302106f..0000000000000
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits-c.c
+++ /dev/null
@@ -1,45 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c17 \
-// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
-// RUN:   -fexperimental-lifetime-safety-tu-analysis \
-// RUN:   -Wlifetime-safety-suggestions -Wno-dangling \
-// RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
-// RUN: %clang_cc1 -fsyntax-only -std=c17 \
-// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
-// RUN:   -fexperimental-lifetime-safety-tu-analysis \
-// RUN:   -Wlifetime-safety-suggestions -Wno-dangling \
-// RUN:   -lifetime-safety-lifetimebound-macro=CONFIGURED_LIFETIMEBOUND_MACRO \
-// RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s 
--check-prefix=CHECK-CONFIG
-// RUN: cp %s %t.c
-// RUN: %clang_cc1 -std=c17 \
-// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
-// RUN:   -fexperimental-lifetime-safety-tu-analysis \
-// RUN:   -Wlifetime-safety-suggestions -Wno-dangling -fixit %t.c
-// RUN: %clang_cc1 -fsyntax-only -std=c17 \
-// RUN:   -fexperimental-lifetime-safety-c -flifetime-safety-inference \
-// RUN:   -fexperimental-lifetime-safety-tu-analysis \
-// RUN:   -Werror=lifetime-safety-suggestions -Wno-dangling %t.c
-
-int *return_ptr(int *p) {
-  // CHECK: :[[@LINE-1]]:17: warning: parameter in intra-TU function should be 
marked
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:23-[[@LINE-2]]:23}:" 
__attribute__((lifetimebound))"
-  return p;
-}
-
-#define CLANG_LIFETIMEBOUND_MACRO [[clang::lifetimebound]]
-
-int *return_ptr_with_clang_macro(int *p) {
-  // CHECK: :[[@LINE-1]]:{{[0-9]+}}: warning: parameter in intra-TU function 
should be marked
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
__attribute__((lifetimebound))"
-  return p;
-}
-
-#define CONFIGURED_LIFETIMEBOUND_MACRO __attribute__((lifetimebound))
-#define GNU_LIFETIMEBOUND_MACRO __attribute__((lifetimebound))
-
-int *return_ptr_with_gnu_macro(int *p) {
-  // CHECK: :[[@LINE-1]]:{{[0-9]+}}: warning: parameter in intra-TU function 
should be marked
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:{{[0-9]+}}-[[@LINE-2]]:{{[0-9]+}}}:" 
GNU_LIFETIMEBOUND_MACRO"
-  // CHECK-CONFIG: :[[@LINE-3]]:{{[0-9]+}}: warning: parameter in intra-TU 
function should be marked
-  // CHECK-CONFIG: 
fix-it:"{{.*}}":{[[@LINE-4]]:{{[0-9]+}}-[[@LINE-4]]:{{[0-9]+}}}:" 
CONFIGURED_LIFETIMEBOUND_MACRO"
-  return p;
-}

>From a2209ddbb021dcc7a72df450db00611fcfa8f999 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <[email protected]>
Date: Fri, 19 Jun 2026 22:16:12 +0800
Subject: [PATCH 7/7] fix/add more tests

---
 .../LifetimeSafety/annotation-suggestions-fixits.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp 
b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
index d7ea1b389196c..07db3cb271c34 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions-fixits.cpp
@@ -5,6 +5,7 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++17 -flifetime-safety-inference \
 // RUN:   -fexperimental-lifetime-safety-tu-analysis \
 // RUN:   -Wlifetime-safety-suggestions -Wlifetime-safety-annotation-placement 
-Wno-dangling \
+// RUN:   '-DLIFETIMEBOUND_MACRO=[[clang::lifetimebound]]' \
 // RUN:   -lifetime-safety-lifetimebound-macro=LIFETIMEBOUND_MACRO \
 // RUN:   -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s 
--check-prefix=CHECK-MACRO
 // RUN: cp %s %t.cpp
@@ -14,6 +15,14 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++17 -flifetime-safety-inference \
 // RUN:   -fexperimental-lifetime-safety-tu-analysis \
 // RUN:   -Werror=lifetime-safety-suggestions -Wno-dangling %t.cpp
+// RUN: cp %s %t.bad-macro.cpp
+// RUN: %clang_cc1 -std=c++17 -flifetime-safety-inference \
+// RUN:   -fexperimental-lifetime-safety-tu-analysis \
+// RUN:   -Wlifetime-safety-suggestions -Wno-dangling \
+// RUN:   -lifetime-safety-lifetimebound-macro=BAD_LIFETIMEBOUND_MACRO \
+// RUN:   -fixit %t.bad-macro.cpp
+// RUN: not %clang_cc1 -fsyntax-only -std=c++17 %t.bad-macro.cpp 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=CHECK-BAD-MACRO
 
 struct View;
 
@@ -37,6 +46,8 @@ View return_view(View a) {
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:24-[[@LINE-2]]:24}:" 
{{\[\[}}clang::lifetimebound]]"
   // CHECK-MACRO: :[[@LINE-3]]:18: warning: parameter in intra-TU function 
should be marked
   // CHECK-MACRO: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:24}:" 
LIFETIMEBOUND_MACRO"
+  // CHECK-BAD-MACRO: :[[@LINE-5]]:25: error: expected ')'
+  // CHECK-BAD-MACRO: BAD_LIFETIMEBOUND_MACRO
   return a;
 }
 
@@ -104,6 +115,7 @@ struct ViewMember {
   View get_view() {
     // CHECK: :[[@LINE-1]]:18: warning: implicit this in intra-TU function 
should be marked
     // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:" 
{{\[\[}}clang::lifetimebound]]"
+    // CHECK-BAD-MACRO: :[[@LINE-3]]:18: error: expected ';' at end of 
declaration list
     return data;
   }
 

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

Reply via email to