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 ¶m_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 f86ac7b90d2d3255dc295a8e0c510e84841a3cef 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..99f0d16cd8e68 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
