https://github.com/vbvictor created 
https://github.com/llvm/llvm-project/pull/181699

Closes https://github.com/llvm/llvm-project/issues/180344.

>From cced057ff7dcb6505cb9532bf19e87192fe011be Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Mon, 16 Feb 2026 19:36:07 +0300
Subject: [PATCH] [LifetimeSafety] Correctly place lifetimebound attr in corner
 cases

---
 clang/lib/Sema/AnalysisBasedWarnings.cpp      | 32 +++++++++++++++----
 .../test/Sema/warn-lifetime-safety-fixits.cpp | 24 ++++++++++++++
 .../Sema/warn-lifetime-safety-suggestions.cpp | 31 ++++++++++++++++++
 3 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 8c7bff27718de..5c49e601840cf 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2937,10 +2937,16 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
             : 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]] ";
+    }
     S.Diag(ParmToAnnotate->getBeginLoc(), DiagID)
         << ParmToAnnotate->getSourceRange()
-        << FixItHint::CreateInsertion(InsertionPoint,
-                                      " [[clang::lifetimebound]]");
+        << FixItHint::CreateInsertion(InsertionPoint, FixItText);
     S.Diag(EscapeExpr->getBeginLoc(),
            diag::note_lifetime_safety_suggestion_returned_here)
         << EscapeExpr->getSourceRange();
@@ -2952,10 +2958,24 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
     unsigned DiagID = (Scope == SuggestionScope::CrossTU)
                           ? diag::warn_lifetime_safety_cross_tu_this_suggestion
                           : 
diag::warn_lifetime_safety_intra_tu_this_suggestion;
-    SourceLocation InsertionPoint;
-    InsertionPoint = Lexer::getLocForEndOfToken(
-        MD->getTypeSourceInfo()->getTypeLoc().getEndLoc(), 0,
-        S.getSourceManager(), S.getLangOpts());
+    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());
+    }
     S.Diag(InsertionPoint, DiagID)
         << MD->getNameInfo().getSourceRange()
         << FixItHint::CreateInsertion(InsertionPoint,
diff --git a/clang/test/Sema/warn-lifetime-safety-fixits.cpp 
b/clang/test/Sema/warn-lifetime-safety-fixits.cpp
index 82b36caf8d141..3ae252a9d00f2 100644
--- a/clang/test/Sema/warn-lifetime-safety-fixits.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-fixits.cpp
@@ -138,3 +138,27 @@ const OutOfLine &OutOfLine::get() const {
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:40-[[@LINE-2]]:40}:" 
{{\[\[}}clang::lifetimebound]]"
   return *this;
 }
+
+struct TrailingReturn {
+  TrailingReturn() {}
+  ~TrailingReturn() {}
+  MyObj data;
+
+  auto get_view() -> 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;
+  }
+
+  auto get_view_const() const -> View {
+    // CHECK: :[[@LINE-1]]:30: warning: implicit this in intra-TU function 
should be marked
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:30-[[@LINE-2]]:30}:" 
{{\[\[}}clang::lifetimebound]]"
+    return data;
+  }
+
+  auto get_ref() const -> const MyObj & {
+    // CHECK: :[[@LINE-1]]:23: warning: implicit this in intra-TU function 
should be marked
+    // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:23-[[@LINE-2]]:23}:" 
{{\[\[}}clang::lifetimebound]]"
+    return data;
+  }
+};
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 0ed545d06ca8a..1887b2f257226 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -50,6 +50,9 @@ inline View redeclared_in_header(View a) {  // 
expected-warning {{parameter in i
   return a;                                 // expected-note {{param returned 
here}}
 }
 
+View return_unnamed_view(View);                // expected-warning {{parameter 
in cross-TU function should be marked [[clang::lifetimebound]]}}
+MyObj& return_unnamed_ref(MyObj&, bool);       // expected-warning {{parameter 
in cross-TU function should be marked [[clang::lifetimebound]]}}
+
 struct ReturnThis {
   const ReturnThis& get() const;           // expected-warning {{implicit this 
in cross-TU function should be marked [[clang::lifetimebound]]}}.
 };
@@ -90,6 +93,14 @@ MyObj* return_pointer_object(MyObj* a) {
   return a;                                // expected-note {{param returned 
here}} 
 }
 
+View return_unnamed_view(View a) {
+  return a;                               // expected-note {{param returned 
here}}
+}
+
+MyObj& return_unnamed_ref(MyObj& a, bool c) {
+  return a;                               // expected-note {{param returned 
here}}
+}
+
 MyObj& return_reference(MyObj& a, // expected-warning {{parameter in intra-TU 
function should be marked [[clang::lifetimebound]]}}
                         MyObj& b, // expected-warning {{parameter in intra-TU 
function should be marked [[clang::lifetimebound]]}}
                         bool c) {
@@ -344,6 +355,26 @@ MyObj* test_template_inference_with_stack() {
 }
 } // namespace inference_with_templates
 
+namespace trailing_return {
+struct TrailingReturn {
+  TrailingReturn() {}
+  ~TrailingReturn() {}
+  MyObj data;
+
+  auto get_view() -> View {                   // expected-warning {{implicit 
this in intra-TU function should be marked [[clang::lifetimebound]]}}
+    return data;                              // expected-note {{param 
returned here}}
+  }
+
+  auto get_view_const() const -> View {       // expected-warning {{implicit 
this in intra-TU function should be marked [[clang::lifetimebound]]}}
+    return data;                              // expected-note {{param 
returned here}}
+  }
+
+  auto get_ref() const -> const MyObj& {      // expected-warning {{implicit 
this in intra-TU function should be marked [[clang::lifetimebound]]}}
+    return data;                              // expected-note {{param 
returned here}}
+  }
+};
+} // namespace trailing_return
+
 
//===----------------------------------------------------------------------===//
 // Negative Test Cases
 
//===----------------------------------------------------------------------===//

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

Reply via email to