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/9] [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 ¶m_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 ¯o_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 ¯o_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/9] 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 ¯o_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 ¯o_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/9] 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/9] 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}} >From 63dcc3a17a7a0f418a0c5b611a86cd086b0381ee Mon Sep 17 00:00:00 2001 From: David Meng <[email protected]> Date: Tue, 26 May 2026 07:23:50 -0700 Subject: [PATCH 5/9] Do not emit fix-its on macros --- clang/lib/Sema/SemaLifetimeSafety.h | 24 +++++++++++++---- ...misplaced-lifetimebound-intra-tu-macro.cpp | 26 +++++++++++++++++++ ...afety-misplaced-lifetimebound-intra-tu.cpp | 17 +++--------- 3 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu-macro.cpp diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 7a3baa9adf2e3..e8e113c8b5eb3 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -288,8 +288,15 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(FDecl); - S.Diag(InsertionPoint, DiagID) - << FixItHint::CreateInsertion(InsertionPoint, FixItText); + // Do not emit fix-its in macros or at invalid locations. + bool IsMacro = + FDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID(); + + if (IsMacro || InsertionPoint.isInvalid()) + S.Diag(FDecl->getLocation(), DiagID); + else + S.Diag(InsertionPoint, DiagID) + << FixItHint::CreateInsertion(InsertionPoint, FixItText); S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here) << Attr->getRange(); @@ -308,9 +315,16 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { auto [InsertionPoint, FixItText] = getLifetimeBoundFixIt(PVDDecl); - S.Diag(PVDDecl->getBeginLoc(), DiagID) - << PVDDecl->getSourceRange() - << FixItHint::CreateInsertion(InsertionPoint, FixItText); + // Do not emit fix-its in macros or at invalid locations. + bool IsMacro = + PVDDecl->getBeginLoc().isMacroID() || InsertionPoint.isMacroID(); + + if (IsMacro || InsertionPoint.isInvalid()) + S.Diag(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange(); + else + 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-intra-tu-macro.cpp b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu-macro.cpp new file mode 100644 index 0000000000000..ccda9aed01832 --- /dev/null +++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu-macro.cpp @@ -0,0 +1,26 @@ +// 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 + +// CHECK-NOT: fix-it: + +struct MyObj{}; + +// We do not emit lifetimebound fix-its on macros. + +#define REF_PARAM MyObj &obj + +MyObj ¯o_param(REF_PARAM); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + +MyObj ¯o_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} + return obj; +} + +#define REF_PARAMS MyObj &obj1, MyObj &obj2 + +MyObj ¯o_params(bool condition, REF_PARAMS); // expected-warning 2 {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + +MyObj ¯o_params(bool condition, + MyObj &obj1 [[clang::lifetimebound]], // expected-note {{'lifetimebound' attribute appears here on the definition}} + MyObj &obj2 [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} + return condition ? obj1 : obj2; +} 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 801e10e289f31..b5b11a6831584 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 @@ -125,9 +125,9 @@ template <> MyObj &spec_func<MyObj>(MyObj &obj) { return obj; } MyObj get_default_obj(); -const MyObj &default_arg_param(const MyObj& +const MyObj &default_arg_param(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\]\]}}" - = 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}} + = get_default_obj()); const MyObj &default_arg_param(const MyObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -138,20 +138,11 @@ struct Base { }; struct Derived : Base { - auto virtual_get(const MyObj& + auto virtual_get(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 -> 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}} + ) const -> const MyObj& override; }; 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 ¯o_param(REF_PARAM); // 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 ¯o_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} - return obj; -} >From e4a5e0348639e78d13cb07077a4fb5f00888f0c6 Mon Sep 17 00:00:00 2001 From: David Meng <[email protected]> Date: Wed, 27 May 2026 01:44:49 -0700 Subject: [PATCH 6/9] Rename test file for clarity --- ...cpp => warn-lifetime-safety-misplaced-lifetimebound-macro.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clang/test/Sema/{warn-lifetime-safety-misplaced-lifetimebound-intra-tu-macro.cpp => warn-lifetime-safety-misplaced-lifetimebound-macro.cpp} (100%) diff --git a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu-macro.cpp b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-macro.cpp similarity index 100% rename from clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu-macro.cpp rename to clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-macro.cpp >From 3f108fd4007e9eb8d155640aafde87afd7e01d85 Mon Sep 17 00:00:00 2001 From: David Meng <[email protected]> Date: Wed, 27 May 2026 09:41:41 -0700 Subject: [PATCH 7/9] Reformat tests to place fix-it checks directly in line --- ...afety-misplaced-lifetimebound-cross-tu.cpp | 10 +-- ...afety-misplaced-lifetimebound-intra-tu.cpp | 63 ++++++++++++------- 2 files changed, 45 insertions(+), 28 deletions(-) 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 d227b8d8656c7..d809ab2650f74 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 @@ -10,13 +10,15 @@ struct HeaderObj { ~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{{\]\]}}" +HeaderObj &header_param(HeaderObj & // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers in other translation units; add it to the declaration instead}} + obj // 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{{\]\]}}" + 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 b5b11a6831584..8d25ebeea6a28 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 @@ -8,8 +8,9 @@ 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 & // 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\]\]}}" +); MyObj &free_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -17,15 +18,20 @@ 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 &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 &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 ¶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 &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 &both(const MyObj & // 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+4]]:{{[0-9]+}}-[[@LINE+4]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + obj, + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + bool + ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} }; const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute appears here on the definition}} @@ -46,9 +52,12 @@ const MyObj &S::both( 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\]\]}}" + T &both(T & // 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+4]]:{{[0-9]+}}-[[@LINE+4]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + arg, + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + bool + ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} }; template <> @@ -63,8 +72,9 @@ 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 & // 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\]\]}}" +); InternalObj &anon_param(InternalObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -72,8 +82,9 @@ 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 &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}} @@ -81,8 +92,9 @@ 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 & // 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\]\]}}" +); static InternalObj &static_param(InternalObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -92,8 +104,9 @@ struct IntraSuppressedObj { ~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 & // 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\]\]}}" +); IntraSuppressedObj &intra_suppressed( IntraSuppressedObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} @@ -101,8 +114,9 @@ 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\]\]}}" + friend View friend_redecl(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\]\]}}" + ); }; // 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. @@ -114,8 +128,9 @@ 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\]\]}}" +MyObj &spec_func(T & // 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\]\]}}" +); template <> // FIXME: Attribute is inhetired, diagnostic's wording is not correct. >From d072bac191dcd87b6472c7926d6984cc657d361c Mon Sep 17 00:00:00 2001 From: David Meng <[email protected]> Date: Wed, 27 May 2026 10:19:20 -0700 Subject: [PATCH 8/9] More explicit fix-it locations for implicit this parameter --- ...ifetime-safety-misplaced-lifetimebound-cross-tu.cpp | 3 ++- ...ifetime-safety-misplaced-lifetimebound-intra-tu.cpp | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) 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 d809ab2650f74..406412ca37277 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 @@ -18,7 +18,8 @@ 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{{\]\]}}" + // CHECK: cross.h:[[#WARN_LINE:]]:{{[0-9]+}}: warning: 'lifetimebound' attribute on this definition is not visible + // CHECK: fix-it:"{{.*}}cross.h":{[[#WARN_LINE]]:{{[0-9]+}}-[[#WARN_LINE]]:{{[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 8d25ebeea6a28..61bc72d725472 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 @@ -27,11 +27,10 @@ struct S { ); 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}} - // CHECK: fix-it:"{{.*}}":{[[@LINE+4]]:{{[0-9]+}}-[[@LINE+4]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" - obj, - // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + obj, // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" bool ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" }; const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute appears here on the definition}} @@ -53,11 +52,10 @@ template <class T> struct MixedSpecializations { T data; T &both(T & // 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+4]]:{{[0-9]+}}-[[@LINE+4]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" - arg, - // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + arg, // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" bool ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" }; template <> >From 8267d896d97c70d6545a76f48aacb036ccc5b2e4 Mon Sep 17 00:00:00 2001 From: David Meng <[email protected]> Date: Wed, 27 May 2026 16:19:31 -0700 Subject: [PATCH 9/9] Fix formatting --- ...afety-misplaced-lifetimebound-cross-tu.cpp | 11 ++-- ...afety-misplaced-lifetimebound-intra-tu.cpp | 62 +++++++++---------- 2 files changed, 37 insertions(+), 36 deletions(-) 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 406412ca37277..0d22d148173b8 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 @@ -11,15 +11,16 @@ struct HeaderObj { }; HeaderObj &header_param(HeaderObj & // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers in other translation units; add it to the declaration instead}} - obj // CHECK: fix-it:"{{.*}}cross.h":{[[#]]:{{[0-9]+}}-[[#]]:{{[0-9]+}}}:" {{\[\[}}clang::lifetimebound{{\]\]}}" -); + // CHECK: cross.h:[[#PARAM_WARN_LINE:]]:{{[0-9]+}}: warning: 'lifetimebound' attribute on this definition is not visible + obj // CHECK: fix-it:"{{.*}}cross.h":{[[#PARAM_WARN_LINE+2]]:{{[0-9]+}}-[[#PARAM_WARN_LINE+2]]:{{[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: cross.h:[[#WARN_LINE:]]:{{[0-9]+}}: warning: 'lifetimebound' attribute on this definition is not visible - // CHECK: fix-it:"{{.*}}cross.h":{[[#WARN_LINE]]:{{[0-9]+}}-[[#WARN_LINE]]:{{[0-9]+}}}:" {{\[\[}}clang::lifetimebound{{\]\]}}" + ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers in other translation units; add it to the declaration instead}} + // CHECK: cross.h:[[#THIS_WARN_LINE:]]:{{[0-9]+}}: warning: 'lifetimebound' attribute on this definition is not visible + // CHECK: fix-it:"{{.*}}cross.h":{[[#THIS_WARN_LINE]]:{{[0-9]+}}-[[#THIS_WARN_LINE]]:{{[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 61bc72d725472..bbcd572d9130f 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 @@ -9,8 +9,8 @@ struct MyObj { }; MyObj &free_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]+}}}:" {{\[\[clang::lifetimebound\]\]}}" -); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + ); MyObj &free_param(MyObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -19,18 +19,18 @@ 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\]\]}}" + ); // 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\]\]}}" - ); + 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 - ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} - // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + obj, // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + bool + ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" }; const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute appears here on the definition}} @@ -51,11 +51,11 @@ const MyObj &S::both( template <class T> struct MixedSpecializations { T data; - T &both(T & // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} - arg, // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" - bool - ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} - // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + T &both(T & // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + arg, // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + bool + ); // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:{{[0-9]+}}-[[@LINE-1]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" }; template <> @@ -71,8 +71,8 @@ struct InternalObj { namespace { InternalObj &anon_param(InternalObj & // 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\]\]}}" -); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + ); InternalObj &anon_param(InternalObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -81,8 +81,8 @@ 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\]\]}}" + ); // 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}} @@ -91,8 +91,8 @@ InternalObj &AnonS::anon_this() [[clang::lifetimebound]] { // expected-note {{'l } // namespace static InternalObj &static_param(InternalObj & // 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\]\]}}" -); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + ); static InternalObj &static_param(InternalObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -103,8 +103,8 @@ struct IntraSuppressedObj { }; IntraSuppressedObj &intra_suppressed(IntraSuppressedObj & // 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\]\]}}" -); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + ); IntraSuppressedObj &intra_suppressed( IntraSuppressedObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} @@ -113,8 +113,8 @@ IntraSuppressedObj &intra_suppressed( struct View { friend View friend_redecl(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\]\]}}" - ); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[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. @@ -127,8 +127,8 @@ 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 & // 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\]\]}}" -); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + ); template <> // FIXME: Attribute is inhetired, diagnostic's wording is not correct. @@ -139,8 +139,8 @@ MyObj &spec_func<MyObj>(MyObj &obj) { return obj; } MyObj get_default_obj(); const MyObj &default_arg_param(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\]\]}}" - = get_default_obj()); + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + = get_default_obj()); const MyObj &default_arg_param(const MyObj &obj [[clang::lifetimebound]]) { // expected-note {{'lifetimebound' attribute appears here on the definition}} return obj; @@ -152,8 +152,8 @@ struct Base { struct Derived : Base { auto virtual_get(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 -> const MyObj& override; + obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" + ) const -> const MyObj& override; }; 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
