https://github.com/ojhunt updated 
https://github.com/llvm/llvm-project/pull/141148

>From 0db205bb8f6925dd725885395f58a688000c7b8b Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Thu, 22 May 2025 02:38:26 -0700
Subject: [PATCH 1/5] [clang] Correct FixIt ranges for unused capture warnings

Fixes #106445 by using the lexer to find the correct range
for the removal FixIts. Previously the ranges that were
generated assuming no unsurprising formatting, which for
the most part works. Being correct in all cases requires
using the lexer to find the bounding tokens for the region
to remove.

As part of this it adds Sema::getRangeForNextToken to wrap
Lexer::findNextToken.
---
 clang/include/clang/Sema/Sema.h               |   6 +
 clang/lib/Sema/Sema.cpp                       |  15 ++
 clang/lib/Sema/SemaLambda.cpp                 |  26 +-
 ...-unused-lambda-capture-trailing-tokens.cpp | 250 ++++++++++++++++++
 4 files changed, 291 insertions(+), 6 deletions(-)
 create mode 100644 
clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a994b845e11fc..f83047d63adab 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -972,6 +972,12 @@ class Sema final : public SemaBase {
   /// Calls \c Lexer::getLocForEndOfToken()
   SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset = 0);
 
+  /// Calls \c Lexer::findNextToken() to find the next token, and if the
+  /// locations of both ends of the token can be resolved it return that
+  /// range; Otherwise it returns an invalid SourceRange.
+  SourceRange getRangeForNextToken(SourceLocation Loc,
+                                   bool IncludeComments = false);
+
   /// Retrieve the module loader associated with the preprocessor.
   ModuleLoader &getModuleLoader() const;
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 1901d19b14dfc..9f706579b1b79 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -84,6 +84,21 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, 
unsigned Offset) {
   return Lexer::getLocForEndOfToken(Loc, Offset, SourceMgr, LangOpts);
 }
 
+SourceRange Sema::getRangeForNextToken(SourceLocation Loc,
+                                       bool IncludeComments) {
+  if (!Loc.isValid())
+    return SourceRange();
+  std::optional<Token> NextToken =
+      Lexer::findNextToken(Loc, SourceMgr, LangOpts, IncludeComments);
+  if (!NextToken)
+    return SourceRange();
+  SourceLocation TokenStart = NextToken->getLocation();
+  SourceLocation TokenEnd = NextToken->getLastLoc();
+  if (!TokenStart.isValid() || !TokenEnd.isValid())
+    return SourceRange();
+  return SourceRange(TokenStart, TokenEnd);
+}
+
 ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); }
 
 DarwinSDKInfo *
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index aad16290422f5..d55dbf82ffdc0 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -2164,15 +2164,29 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation 
StartLoc, SourceLocation EndLoc,
           bool IsLast = (I + 1) == LSI->NumExplicitCaptures;
           SourceRange FixItRange;
           if (CaptureRange.isValid()) {
+            auto GetTrailingEndLocation = [&](SourceLocation StartPoint) {
+              SourceRange NextToken =
+                  getRangeForNextToken(StartPoint, /*IncludeComments=*/true);
+              if (!NextToken.isValid())
+                return SourceLocation();
+              // Return the last location preceding the next token
+              return NextToken.getBegin().getLocWithOffset(-1);
+            };
             if (!CurHasPreviousCapture && !IsLast) {
               // If there are no captures preceding this capture, remove the
-              // following comma.
-              FixItRange = SourceRange(CaptureRange.getBegin(),
-                                       
getLocForEndOfToken(CaptureRange.getEnd()));
+              // trailing comma and anything up to the next token
+              SourceRange CommaRange =
+                  getRangeForNextToken(CaptureRange.getEnd());
+              SourceLocation FixItEnd =
+                  GetTrailingEndLocation(CommaRange.getBegin());
+              FixItRange = SourceRange(CaptureRange.getBegin(), FixItEnd);
             } else {
-              // Otherwise, remove the comma since the last used capture.
-              FixItRange = SourceRange(getLocForEndOfToken(PrevCaptureLoc),
-                                       CaptureRange.getEnd());
+              // Otherwise, remove the comma since the last used capture, and
+              // anything up to the next token
+              SourceLocation FixItStart = getLocForEndOfToken(PrevCaptureLoc);
+              SourceLocation FixItEnd =
+                  GetTrailingEndLocation(CaptureRange.getEnd());
+              FixItRange = SourceRange(FixItStart, FixItEnd);
             }
           }
 
diff --git a/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp 
b/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
new file mode 100644
index 0000000000000..e2c2cdb6a2d92
--- /dev/null
+++ b/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
@@ -0,0 +1,250 @@
+// RUN: cp %s %t
+// RUN: %clang_cc1 -x c++ -Wunused-lambda-capture -Wno-unused-value -std=c++1z 
-fixit %t
+// RUN: grep -v CHECK %t | FileCheck %s
+
+
+#define MACRO_CAPTURE(...) __VA_ARGS__
+int main() {
+    int a = 0, b = 0, c = 0;
+    auto F0 = [a, &b]() mutable {
+    // CHECK: auto F0 = [a]()
+        a++;
+    };
+
+    auto F1 = [&a, &b]() {
+    // CHECK: auto F1 = []() {
+    };
+
+    auto F2 = [&a, b]() {
+    // CHECK: auto F2 = []() {
+    };
+
+    auto F3 = [&a,
+         &b]() {
+    // CHECK: auto F3 = []() {
+    };
+
+    auto F4 = [&a
+        , &b]() {
+    // CHECK: auto F4 = []() {
+    };
+    auto F5 = [&a ,&b]()  {
+    // CHECK: auto F5 = []() {
+    };
+
+    auto F0a = [a, &b]() mutable {
+    // CHECK: auto F0a = [a]() mutable {
+        a++;
+    };
+
+    auto F1a = [&a, &b]() {
+    // CHECK: auto F1a = [&a]() {
+        a++;
+    };
+
+    auto F2a = [&a, b]() {
+    // CHECK: auto F2a = [&a]() {
+        a++;
+    };
+
+    auto F3a = [&a,
+         &b]() {
+    // CHECK: auto F3a = [&a]() {
+        a++;
+    };
+
+    auto F4a = [&a
+        , &b]() {
+    // CHECK: auto F4a = [&a]() {
+        a++;
+    };
+
+    auto F5a = [&a ,&b]() {
+    // CHECK: auto F5a = [&a]() {
+        a++;
+    };
+    auto F0b = [a, &b]() mutable {
+    // CHECK: auto F0b = [ &b]() mutable
+        b++;
+    };
+
+    auto F1b = [&a, &b]() {
+    // CHECK: auto F1b = [ &b]() {
+        b++;
+    };
+
+    auto F2b = [&a, b]() mutable {
+    // CHECK: auto F2b = [ b]() mutable {
+        b++;
+    };
+
+    auto F3b = [&a,
+         &b]() {
+    // CHECK: auto F3b = [ &b]() {
+        b++;
+    };
+
+    auto F4b = [&a
+        , &b]() {
+    // CHECK: auto F4b = [ &b]() {
+        b++;
+    };
+    auto F5b = [&a ,&b]() {
+    // CHECK: auto F5b = [&b]() {
+        b++;
+    };
+
+    auto F6 = [&a, &b, &c]() {
+    // CHECK: auto F6 = [&a, &b]() {
+        a++;
+        b++;
+    };
+    auto F7 = [&a, &b, &c]() {
+    // CHECK: auto F7 = [&a, &c]() {
+        a++;
+        c++;
+    };
+    auto F8 = [&a, &b, &c]() {
+    // CHECK: auto F8 = [ &b, &c]() {
+        b++;
+        c++;
+    };
+    auto F9 = [&a, &b    , &c]() {
+    // CHECK: auto F9 = [&a   , &c]() {
+        a++;
+        c++;
+    };
+    auto F10 = [&a,
+         &b, &c]() {
+    // CHECK: auto F10 = [&a, &c]() {
+        a++;
+        c++;
+    };
+    auto F11 = [&a,  &b  ,
+         &c]() {
+    // CHECK: auto F11 = [&a ,
+    // CHECK-NEXT:      &c]() {
+        a++;
+        c++;
+    };
+    auto F12 = [a = 0,  b  ,
+         c]() mutable {
+    // CHECK: auto F12 = [ b  ,
+    // CHECK-NEXT:     c]() mutable {
+        b++;
+        c++;
+    };
+    auto F13 = [a,  b = 0 ,
+         c]() mutable {
+    // CHECK: auto F13 = [a ,
+    // CHECK-NEXT:     c]() mutable {
+        a++;
+        c++;
+    };
+    auto F14 = [a,  b ,
+         c
+        = 0]() mutable {
+    // CHECK: auto F14 = [a,  b]() mutable {
+        a++;
+        b++;
+    };
+
+    // We want to remove everything including the comment
+    // as well as the comma following the capture of `a`
+    auto F15 = [&a /* comment */, &b]() {
+    // CHECK: auto F15 = [ &b]() {
+        b++;
+    };
+
+    // The comment preceding the first capture remains. This is more
+    // by design of the fixit logic than anything else, but we should
+    // consider the preceding comment might actually be a comment for
+    // the entire capture set, so maybe we do want it to hang around
+    auto F16 = [/* comment */ &a , &b]() {
+    // CHECK: auto F16 = [/* comment */ &b]() {
+        b++;
+    };
+
+    auto F16b = [&a ,    /* comment */ &b]() {
+    // CHECK: auto F16b = [ /* comment */ &b]() {
+        b++;
+    };
+
+    auto F17 = [&a /* comment */, &b]() {
+    // CHECK: auto F17 = [&a]() {
+        a++;
+    };
+
+    auto F18 = [&a , /* comment */ &b]() {
+    // CHECK: auto F18 = [&a]() {
+        a++;
+    };
+    
+    auto F19 = [&a /* comment */, &b /* comment */]() {
+    // CHECK: auto F19 = [&a /* comment */]() {
+        a++;
+    };
+
+    auto F20 = [MACRO_CAPTURE(&a, &b)]() {
+    // CHECK: auto F20 = [MACRO_CAPTURE(&a, &b)]() {
+    };
+
+    auto F21 = [MACRO_CAPTURE(&a), &b]() {
+    // CHECK: auto F21 = []() {
+    };
+
+    auto F22 = [MACRO_CAPTURE(&a,) &b]() {
+    // CHECK: auto F22 = [MACRO_CAPTURE(&a,) &b]() {
+    };
+    auto F23 = [&a MACRO_CAPTURE(, &b)]() {
+    // CHECK: auto F23 = []() {
+    };
+    auto F24 = [&a, MACRO_CAPTURE(&b)]() {
+    // CHECK: auto F24 = []() {
+    };
+
+    auto F20a = [MACRO_CAPTURE(&a, &b)]() {
+    // CHECK: auto F20a = [MACRO_CAPTURE(&a, &b)]() {
+      a++;
+    };
+
+    auto F21a = [MACRO_CAPTURE(&a), &b]() {
+    // CHECK: auto F21a = [MACRO_CAPTURE(&a)]() {
+      a++;
+    };
+
+    auto F22a = [MACRO_CAPTURE(&a,) &b]() {
+    // Cauto F22a = [MACRO_CAPTURE(&a,) &b]() {
+      a++;
+    };
+    auto F23a = [&a MACRO_CAPTURE(, &b)]() {
+    // CHECK: auto F23a = [&a]() {
+      a++;
+    };
+    auto F24a = [&a, MACRO_CAPTURE(&b)]() {
+    // CHECK: auto F24a = [&a]() {
+      a++;
+    };
+    auto F20b = [MACRO_CAPTURE(&a, &b)]() {
+    // CHECK: auto F20b = [MACRO_CAPTURE(&a, &b)]() {
+      b++;
+    };
+
+    auto F21b = [MACRO_CAPTURE(&a), &b]() {
+    // CHECK: auto F21b = [ &b]() {
+      b++;
+    };
+
+    auto F22b = [MACRO_CAPTURE(&a,) &b]() {
+    // CHECK: auto F22b = [MACRO_CAPTURE(&a,) &b]() {
+      b++;
+    };
+    auto F23b = [&a MACRO_CAPTURE(, &b)]() {
+    // CHECK: auto F23b = [(, &b)]() {
+      b++;
+    };
+    auto F24b = [&a, MACRO_CAPTURE(&b)]() {
+    // CHECK: auto F24b = [ MACRO_CAPTURE(&b)]() {
+      b++;
+    };
+}

>From 2e67f4ce3763a88ac1e85629b2517788d78d642f Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Mon, 2 Jun 2025 19:38:08 -0700
Subject: [PATCH 2/5] Fixing macros and adding tests

---
 clang/include/clang/Sema/Sema.h               |   5 +-
 clang/lib/Sema/Sema.cpp                       |  10 +-
 clang/lib/Sema/SemaLambda.cpp                 |  14 +-
 ...-unused-lambda-capture-trailing-tokens.cpp | 249 +++++++++++++++++-
 .../FixIt/fixit-unused-lambda-capture.cpp     |   2 +-
 5 files changed, 269 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f83047d63adab..6391af9d8e63b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -976,7 +976,9 @@ class Sema final : public SemaBase {
   /// locations of both ends of the token can be resolved it return that
   /// range; Otherwise it returns an invalid SourceRange.
   SourceRange getRangeForNextToken(SourceLocation Loc,
-                                   bool IncludeComments = false);
+                                   bool IncludeMacros,
+                                   bool IncludeComments,
+                                   std::optional<tok::TokenKind> ExpectedToken 
= std::nullopt);
 
   /// Retrieve the module loader associated with the preprocessor.
   ModuleLoader &getModuleLoader() const;
@@ -9129,6 +9131,7 @@ class Sema final : public SemaBase {
   /// Diagnose if an explicit lambda capture is unused. Returns true if a
   /// diagnostic is emitted.
   bool DiagnoseUnusedLambdaCapture(SourceRange CaptureRange,
+                                   SourceRange FixItRange,
                                    const sema::Capture &From);
 
   /// Build a FieldDecl suitable to hold the given capture.
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9f706579b1b79..6be6a11eaba6d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -85,17 +85,25 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation 
Loc, unsigned Offset) {
 }
 
 SourceRange Sema::getRangeForNextToken(SourceLocation Loc,
-                                       bool IncludeComments) {
+                                       bool IncludeMacros,
+                                       bool IncludeComments,
+                                       std::optional<tok::TokenKind> 
ExpectedToken) {
   if (!Loc.isValid())
     return SourceRange();
   std::optional<Token> NextToken =
       Lexer::findNextToken(Loc, SourceMgr, LangOpts, IncludeComments);
   if (!NextToken)
     return SourceRange();
+  if (ExpectedToken && NextToken->getKind() != *ExpectedToken)
+    return SourceRange();
   SourceLocation TokenStart = NextToken->getLocation();
   SourceLocation TokenEnd = NextToken->getLastLoc();
   if (!TokenStart.isValid() || !TokenEnd.isValid())
     return SourceRange();
+  if (!IncludeMacros) {
+    if (TokenStart.isMacroID() || TokenEnd.isMacroID())
+      return SourceRange();
+  }
   return SourceRange(TokenStart, TokenEnd);
 }
 
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index d55dbf82ffdc0..c6a7d0f323f93 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -2022,6 +2022,7 @@ bool Sema::CaptureHasSideEffects(const Capture &From) {
 }
 
 bool Sema::DiagnoseUnusedLambdaCapture(SourceRange CaptureRange,
+                                       SourceRange FixItRange,
                                        const Capture &From) {
   if (CaptureHasSideEffects(From))
     return false;
@@ -2041,7 +2042,12 @@ bool Sema::DiagnoseUnusedLambdaCapture(SourceRange 
CaptureRange,
   else
     diag << From.getVariable();
   diag << From.isNonODRUsed();
-  diag << FixItHint::CreateRemoval(CaptureRange);
+  // If we were able to resolve the fixit range we'll create a fixit,
+  // otherwise we just use the raw capture range for the diagnostic.
+  if (FixItRange.isValid())
+    diag << FixItHint::CreateRemoval(FixItRange);
+  else
+    diag << CaptureRange;
   return true;
 }
 
@@ -2166,7 +2172,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, 
SourceLocation EndLoc,
           if (CaptureRange.isValid()) {
             auto GetTrailingEndLocation = [&](SourceLocation StartPoint) {
               SourceRange NextToken =
-                  getRangeForNextToken(StartPoint, /*IncludeComments=*/true);
+                  getRangeForNextToken(StartPoint, /*IncludeMacros=*/false, 
/*IncludeComments=*/true);
               if (!NextToken.isValid())
                 return SourceLocation();
               // Return the last location preceding the next token
@@ -2176,7 +2182,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, 
SourceLocation EndLoc,
               // If there are no captures preceding this capture, remove the
               // trailing comma and anything up to the next token
               SourceRange CommaRange =
-                  getRangeForNextToken(CaptureRange.getEnd());
+                  getRangeForNextToken(CaptureRange.getEnd(), 
/*IncludeMacros=*/false, /*IncludeComments=*/false, tok::comma);
               SourceLocation FixItEnd =
                   GetTrailingEndLocation(CommaRange.getBegin());
               FixItRange = SourceRange(CaptureRange.getBegin(), FixItEnd);
@@ -2190,7 +2196,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, 
SourceLocation EndLoc,
             }
           }
 
-          IsCaptureUsed = !DiagnoseUnusedLambdaCapture(FixItRange, From);
+          IsCaptureUsed = !DiagnoseUnusedLambdaCapture(CaptureRange, 
FixItRange, From);
         }
       }
 
diff --git a/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp 
b/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
index e2c2cdb6a2d92..1bb99fa1ae2a8 100644
--- a/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
+++ b/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
@@ -1,122 +1,202 @@
 // RUN: cp %s %t
 // RUN: %clang_cc1 -x c++ -Wunused-lambda-capture -Wno-unused-value -std=c++1z 
-fixit %t
 // RUN: grep -v CHECK %t | FileCheck %s
+// RUN: %clang_cc1 -x c++ -Wunused-lambda-capture -Wno-unused-value -std=c++1z 
-fsyntax-only %s 2>&1 | FileCheck --match-full-lines --strict-whitespace 
--check-prefix POINTER %s
 
 
 #define MACRO_CAPTURE(...) __VA_ARGS__
+#define M_A a
+#define M_B b
+
 int main() {
-    int a = 0, b = 0, c = 0;
+    int a = 0, b = 0, c = 0, d = 0;
     auto F0 = [a, &b]() mutable {
     // CHECK: auto F0 = [a]()
+         // POINTER:{{^.*}}|     auto F0 = [a, &b]() mutable {
+    // POINTER-NEXT:{{^.*}}|                 ~~~^
+
         a++;
     };
 
     auto F1 = [&a, &b]() {
     // CHECK: auto F1 = []() {
+         // POINTER:{{^.*}}|     auto F1 = [&a, &b]() {
+    // POINTER-NEXT:{{^.*}}|                ~^~
+         // POINTER:{{^.*}}|     auto F1 = [&a, &b]() {
+    // POINTER-NEXT:{{^.*}}|                  ~~~^
     };
 
     auto F2 = [&a, b]() {
     // CHECK: auto F2 = []() {
+         // POINTER:{{^.*}}|     auto F2 = [&a, b]() {
+    // POINTER-NEXT:{{^.*}}|                ~^~
+         // POINTER:{{^.*}}|     auto F2 = [&a, b]() {
+    // POINTER-NEXT:{{^.*}}|                  ~~^
     };
 
     auto F3 = [&a,
          &b]() {
     // CHECK: auto F3 = []() {
+         // POINTER:{{^.*}}|     auto F3 = [&a,
+    // POINTER-NEXT:{{^.*}}|                ~^~
+    // POINTER-NEXT:{{^.*}}|          &b]() {
+         // POINTER:{{^.*}}|     auto F3 = [&a,
+    // POINTER-NEXT:{{^.*}}|                  ~
+    // POINTER-NEXT:{{^.*}}|          &b]() {
+    // POINTER-NEXT:{{^.*}}|          ~^
+
     };
 
     auto F4 = [&a
         , &b]() {
     // CHECK: auto F4 = []() {
+         // POINTER:{{^.*}}|     auto F4 = [&a
+    // POINTER-NEXT:{{^.*}}|                ~^
+    // POINTER-NEXT:{{^.*}}|         , &b]() {
+    // POINTER-NEXT:{{^.*}}|         ~
+         // POINTER:{{^.*}}|     auto F4 = [&a
+    // POINTER-NEXT:{{^.*}}|                  
+    // POINTER-NEXT:{{^.*}}|         , &b]() {
+    // POINTER-NEXT:{{^.*}}|         ~~~^
     };
     auto F5 = [&a ,&b]()  {
     // CHECK: auto F5 = []() {
+         // POINTER:{{^.*}}|     auto F5 = [&a ,&b]()  {
+    // POINTER-NEXT:{{^.*}}|                ~^~~
+         // POINTER:{{^.*}}|     auto F5 = [&a ,&b]()  {
+    // POINTER-NEXT:{{^.*}}|                   ~~^
     };
 
     auto F0a = [a, &b]() mutable {
     // CHECK: auto F0a = [a]() mutable {
+         // POINTER:{{^.*}}|     auto F0a = [a, &b]() mutable {
+    // POINTER-NEXT:{{^.*}}|                  ~~~^
         a++;
     };
 
     auto F1a = [&a, &b]() {
     // CHECK: auto F1a = [&a]() {
+         // POINTER:{{^.*}}|     auto F1a = [&a, &b]() {
+    // POINTER-NEXT:{{^.*}}|                   ~~~^
         a++;
     };
 
     auto F2a = [&a, b]() {
     // CHECK: auto F2a = [&a]() {
+         // POINTER:{{^.*}}|     auto F2a = [&a, b]() {
+    // POINTER-NEXT:{{^.*}}|                   ~~^
         a++;
     };
 
     auto F3a = [&a,
          &b]() {
     // CHECK: auto F3a = [&a]() {
+         // POINTER:{{^.*}}|     auto F3a = [&a,
+    // POINTER-NEXT:{{^.*}}|                   ~
+    // POINTER-NEXT:{{^.*}}|          &b]() {
+    // POINTER-NEXT:{{^.*}}|          ~^
         a++;
     };
 
     auto F4a = [&a
         , &b]() {
     // CHECK: auto F4a = [&a]() {
+         // POINTER:{{^.*}}|     auto F4a = [&a
+    // POINTER-NEXT:{{^.*}}|                   
+    // POINTER-NEXT:{{^.*}}|         , &b]() {
+    // POINTER-NEXT:{{^.*}}|         ~~~^
         a++;
     };
 
     auto F5a = [&a ,&b]() {
     // CHECK: auto F5a = [&a]() {
+         // POINTER:{{^.*}}|     auto F5a = [&a ,&b]() {
+    // POINTER-NEXT:{{^.*}}|                    ~~^
         a++;
     };
     auto F0b = [a, &b]() mutable {
     // CHECK: auto F0b = [ &b]() mutable
+         // POINTER:{{^.*}}|     auto F0b = [a, &b]() mutable {
+    // POINTER-NEXT:{{^.*}}|                 ^~
+
         b++;
     };
 
     auto F1b = [&a, &b]() {
     // CHECK: auto F1b = [ &b]() {
+         // POINTER:{{^.*}}|     auto F1b = [&a, &b]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~
         b++;
     };
 
     auto F2b = [&a, b]() mutable {
     // CHECK: auto F2b = [ b]() mutable {
+         // POINTER:{{^.*}}|     auto F2b = [&a, b]() mutable {
+    // POINTER-NEXT:{{^.*}}|                 ~^~
         b++;
     };
 
     auto F3b = [&a,
          &b]() {
     // CHECK: auto F3b = [ &b]() {
+         // POINTER:{{^.*}}|     auto F3b = [&a,
+    // POINTER-NEXT:{{^.*}}|                 ~^~
+    // POINTER-NEXT:{{^.*}}|          &b]() {
         b++;
     };
 
     auto F4b = [&a
         , &b]() {
     // CHECK: auto F4b = [ &b]() {
+         // POINTER:{{^.*}}|     auto F4b = [&a
+    // POINTER-NEXT:{{^.*}}|                 ~^
+    // POINTER-NEXT:{{^.*}}|         , &b]() {
+    // POINTER-NEXT:{{^.*}}|         ~
+
         b++;
     };
     auto F5b = [&a ,&b]() {
     // CHECK: auto F5b = [&b]() {
+         // POINTER:{{^.*}}|     auto F5b = [&a ,&b]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~~
         b++;
     };
 
     auto F6 = [&a, &b, &c]() {
     // CHECK: auto F6 = [&a, &b]() {
+         // POINTER:{{^.*}}|     auto F6 = [&a, &b, &c]() {
+    // POINTER-NEXT:{{^.*}}|                      ~~~^
         a++;
         b++;
     };
     auto F7 = [&a, &b, &c]() {
     // CHECK: auto F7 = [&a, &c]() {
+         // POINTER:{{^.*}}|     auto F7 = [&a, &b, &c]() {
+    // POINTER-NEXT:{{^.*}}|                  ~~~^
         a++;
         c++;
     };
     auto F8 = [&a, &b, &c]() {
     // CHECK: auto F8 = [ &b, &c]() {
+         // POINTER:{{^.*}}|     auto F8 = [&a, &b, &c]() {
+    // POINTER-NEXT:{{^.*}}|                ~^~
         b++;
         c++;
     };
     auto F9 = [&a, &b    , &c]() {
     // CHECK: auto F9 = [&a   , &c]() {
+         // POINTER:{{^.*}}|     auto F9 = [&a, &b    , &c]() {
+    // POINTER-NEXT:{{^.*}}|                  ~~~^
         a++;
         c++;
     };
     auto F10 = [&a,
          &b, &c]() {
     // CHECK: auto F10 = [&a, &c]() {
+         // POINTER:{{^.*}}|     auto F10 = [&a,
+    // POINTER-NEXT:{{^.*}}|                   ~
+    // POINTER-NEXT:{{^.*}}|          &b, &c]() {
+    // POINTER-NEXT:{{^.*}}|          ~^
         a++;
         c++;
     };
@@ -124,6 +204,8 @@ int main() {
          &c]() {
     // CHECK: auto F11 = [&a ,
     // CHECK-NEXT:      &c]() {
+         // POINTER:{{^.*}}|     auto F11 = [&a,  &b  ,
+    // POINTER-NEXT:{{^.*}}|                   ~~~~^
         a++;
         c++;
     };
@@ -131,6 +213,8 @@ int main() {
          c]() mutable {
     // CHECK: auto F12 = [ b  ,
     // CHECK-NEXT:     c]() mutable {
+         // POINTER:{{^.*}}|     auto F12 = [a = 0,  b  ,
+    // POINTER-NEXT:{{^.*}}|                 ^~~~~~
         b++;
         c++;
     };
@@ -138,6 +222,8 @@ int main() {
          c]() mutable {
     // CHECK: auto F13 = [a ,
     // CHECK-NEXT:     c]() mutable {
+         // POINTER:{{^.*}}|     auto F13 = [a,  b = 0 ,
+    // POINTER-NEXT:{{^.*}}|                  ~~~^~~~~
         a++;
         c++;
     };
@@ -145,6 +231,12 @@ int main() {
          c
         = 0]() mutable {
     // CHECK: auto F14 = [a,  b]() mutable {
+         // POINTER:{{^.*}}|     auto F14 = [a,  b ,
+    // POINTER-NEXT:{{^.*}}|                       ~
+    // POINTER-NEXT:{{^.*}}|          c
+    // POINTER-NEXT:{{^.*}}|          ^
+    // POINTER-NEXT:{{^.*}}|         = 0]() mutable {
+    // POINTER-NEXT:{{^.*}}|         ~~~
         a++;
         b++;
     };
@@ -153,6 +245,8 @@ int main() {
     // as well as the comma following the capture of `a`
     auto F15 = [&a /* comment */, &b]() {
     // CHECK: auto F15 = [ &b]() {
+         // POINTER:{{^.*}}|     auto F15 = [&a /* comment */, &b]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~~~~~~~~~~~~~~~
         b++;
     };
 
@@ -162,89 +256,236 @@ int main() {
     // the entire capture set, so maybe we do want it to hang around
     auto F16 = [/* comment */ &a , &b]() {
     // CHECK: auto F16 = [/* comment */ &b]() {
+         // POINTER:{{^.*}}|     auto F16 = [/* comment */ &a , &b]() {
+    // POINTER-NEXT:{{^.*}}|                               ~^~~
         b++;
     };
 
     auto F16b = [&a ,    /* comment */ &b]() {
     // CHECK: auto F16b = [ /* comment */ &b]() {
+         // POINTER:{{^.*}}|     auto F16b = [&a ,    /* comment */ &b]() {
+    // POINTER-NEXT:{{^.*}}|                  ~^~~
         b++;
     };
 
     auto F17 = [&a /* comment */, &b]() {
     // CHECK: auto F17 = [&a]() {
+         // POINTER:{{^.*}}|     auto F17 = [&a /* comment */, &b]() {
+    // POINTER-NEXT:{{^.*}}|                    ~~~~~~~~~~~~~~~~^
+
         a++;
     };
 
     auto F18 = [&a , /* comment */ &b]() {
     // CHECK: auto F18 = [&a]() {
+         // POINTER:{{^.*}}|     auto F18 = [&a , /* comment */ &b]() {
+    // POINTER-NEXT:{{^.*}}|                    ~~~~~~~~~~~~~~~~~^
+
         a++;
     };
     
     auto F19 = [&a /* comment */, &b /* comment */]() {
     // CHECK: auto F19 = [&a /* comment */]() {
+         // POINTER:{{^.*}}|     auto F19 = [&a /* comment */, &b /* comment 
*/]() {
+    // POINTER-NEXT:{{^.*}}|                    ~~~~~~~~~~~~~~~~^
+
         a++;
     };
 
     auto F20 = [MACRO_CAPTURE(&a, &b)]() {
     // CHECK: auto F20 = [MACRO_CAPTURE(&a, &b)]() {
+         // POINTER:{{^.*}}|     auto F20 = [MACRO_CAPTURE(&a, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                               ~^
+          // POINTER:{{^.*}}|     auto F20 = [MACRO_CAPTURE(&a, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                                   ~^
     };
 
     auto F21 = [MACRO_CAPTURE(&a), &b]() {
     // CHECK: auto F21 = []() {
+         // POINTER:{{^.*}}|     auto F21 = [MACRO_CAPTURE(&a), &b]() {
+    // POINTER-NEXT:{{^.*}}|                 ~~~~~~~~~~~~~~~^~~
+         // POINTER:{{^.*}}|     auto F21 = [MACRO_CAPTURE(&a), &b]() {
+    // POINTER-NEXT:{{^.*}}|                                  ~~~^
     };
 
     auto F22 = [MACRO_CAPTURE(&a,) &b]() {
     // CHECK: auto F22 = [MACRO_CAPTURE(&a,) &b]() {
+         // POINTER:{{^.*}}|     auto F22 = [MACRO_CAPTURE(&a,) &b]() {
+    // POINTER-NEXT:{{^.*}}|                               ~^
+         // POINTER:{{^.*}}|     auto F22 = [MACRO_CAPTURE(&a,) &b]() {
+    // POINTER-NEXT:{{^.*}}|                                    ~^
     };
     auto F23 = [&a MACRO_CAPTURE(, &b)]() {
-    // CHECK: auto F23 = []() {
+    // CHECK: auto F23 = [&a]() {
+         // POINTER:{{^.*}}|     auto F23 = [&a MACRO_CAPTURE(, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^
+         // POINTER:{{^.*}}|     auto F23 = [&a MACRO_CAPTURE(, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                    ~~~~~~~~~~~~~~~~~^~
     };
     auto F24 = [&a, MACRO_CAPTURE(&b)]() {
     // CHECK: auto F24 = []() {
+         // POINTER:{{^.*}}|     auto F24 = [&a, MACRO_CAPTURE(&b)]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~
+         // POINTER:{{^.*}}|     auto F24 = [&a, MACRO_CAPTURE(&b)]() {
+    // POINTER-NEXT:{{^.*}}|                   ~~~~~~~~~~~~~~~~~^~
     };
 
     auto F20a = [MACRO_CAPTURE(&a, &b)]() {
     // CHECK: auto F20a = [MACRO_CAPTURE(&a, &b)]() {
+         // POINTER:{{^.*}}|     auto F20a = [MACRO_CAPTURE(&a, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                                    ~^
       a++;
     };
 
     auto F21a = [MACRO_CAPTURE(&a), &b]() {
     // CHECK: auto F21a = [MACRO_CAPTURE(&a)]() {
+         // POINTER:{{^.*}}|     auto F21a = [MACRO_CAPTURE(&a), &b]() {
+    // POINTER-NEXT:{{^.*}}|                                   ~~~^
       a++;
     };
 
     auto F22a = [MACRO_CAPTURE(&a,) &b]() {
-    // Cauto F22a = [MACRO_CAPTURE(&a,) &b]() {
+    // CHECK: auto F22a = [MACRO_CAPTURE(&a,) &b]() {
+         // POINTER:{{^.*}}|     auto F22a = [MACRO_CAPTURE(&a,) &b]() {
+    // POINTER-NEXT:{{^.*}}|                                     ~^
       a++;
     };
     auto F23a = [&a MACRO_CAPTURE(, &b)]() {
     // CHECK: auto F23a = [&a]() {
+         // POINTER:{{^.*}}|     auto F23a = [&a MACRO_CAPTURE(, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                     ~~~~~~~~~~~~~~~~~^~
       a++;
     };
     auto F24a = [&a, MACRO_CAPTURE(&b)]() {
     // CHECK: auto F24a = [&a]() {
+         // POINTER:{{^.*}}|     auto F24a = [&a, MACRO_CAPTURE(&b)]() {
+    // POINTER-NEXT:{{^.*}}|                    ~~~~~~~~~~~~~~~~~^~
       a++;
     };
     auto F20b = [MACRO_CAPTURE(&a, &b)]() {
     // CHECK: auto F20b = [MACRO_CAPTURE(&a, &b)]() {
+         // POINTER:{{^.*}}|     auto F20b = [MACRO_CAPTURE(&a, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                                ~^
       b++;
     };
 
     auto F21b = [MACRO_CAPTURE(&a), &b]() {
     // CHECK: auto F21b = [ &b]() {
+         // POINTER:{{^.*}}|     auto F21b = [MACRO_CAPTURE(&a), &b]() {
+    // POINTER-NEXT:{{^.*}}|                  ~~~~~~~~~~~~~~~^~~
       b++;
     };
 
     auto F22b = [MACRO_CAPTURE(&a,) &b]() {
     // CHECK: auto F22b = [MACRO_CAPTURE(&a,) &b]() {
+         // POINTER:{{^.*}}|     auto F22b = [MACRO_CAPTURE(&a,) &b]() {
+    // POINTER-NEXT:{{^.*}}|                                ~^
       b++;
     };
     auto F23b = [&a MACRO_CAPTURE(, &b)]() {
-    // CHECK: auto F23b = [(, &b)]() {
+    // CHECK: auto F23b = [&a MACRO_CAPTURE(, &b)]() {
+         // POINTER:{{^.*}}|     auto F23b = [&a MACRO_CAPTURE(, &b)]() {
+    // POINTER-NEXT:{{^.*}}|                  ~^
       b++;
     };
     auto F24b = [&a, MACRO_CAPTURE(&b)]() {
     // CHECK: auto F24b = [ MACRO_CAPTURE(&b)]() {
+         // POINTER:{{^.*}}|     auto F24b = [&a, MACRO_CAPTURE(&b)]() {
+    // POINTER-NEXT:{{^.*}}|                  ~^~
       b++;
     };
+
+    auto F25ma = [&M_A, &b]() {
+    // CHECK:     auto F25ma = []() {
+         // POINTER:{{^.*}}|     auto F25ma = [&M_A, &b]() {
+    // POINTER-NEXT:{{^.*}}|                   ~^~~~
+         // POINTER:{{^.*}}|     auto F25ma = [&M_A, &b]() {
+    // POINTER-NEXT:{{^.*}}|                       ~~~^
+    };
+    auto F25mb = [&a, &M_B]() {
+    // CHECK: auto F25mb = []() {
+         // POINTER:{{^.*}}|     auto F25mb = [&a, &M_B]() {
+    // POINTER-NEXT:{{^.*}}|                   ~^~
+         // POINTER:{{^.*}}|     auto F25mb = [&a, &M_B]() {
+    // POINTER-NEXT:{{^.*}}|                     ~~~^~~
+    };
+
+    auto F25mab = [&M_A, &b]() {
+    // CHECK: auto F25mab = [ &b]() {
+         // POINTER:{{^.*}}|     auto F25mab = [&M_A, &b]() {
+    // POINTER-NEXT:{{^.*}}|                    ~^~~~
+        b++;
+    };
+    auto F25amb = [&a, &M_B]() {
+    //CHECK: auto F25amb = []() {
+         // POINTER:{{^.*}}|     auto F25amb = [&a, &M_B]() {
+    // POINTER-NEXT:{{^.*}}|                    ~^~
+         // POINTER:{{^.*}}|     auto F25amb = [&a, &M_B]() {
+    // POINTER-NEXT:{{^.*}}|                      ~~~^~~
+    };
+    
+    auto F26 = [&a, &b, &c, &d]() {
+    // CHECK: auto F26 = [&a, &b]() {
+         // POINTER:{{^.*}}|     auto F26 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                       ~~~^
+         // POINTER:{{^.*}}|     auto F26 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                           ~~~^
+
+        (void)a;
+        (void)b;
+    };
+
+    auto F27 = [&a, &b, &c, &d]() {
+    // CHECK: auto F27 = [&a, &c]() {
+         // POINTER:{{^.*}}|     auto F27 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                   ~~~^
+         // POINTER:{{^.*}}|     auto F27 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                           ~~~^
+
+        (void)a;
+        (void)c;
+    };
+
+    auto F28 = [&a, &b, &c, &d]() {
+        // CHECK: auto F28 = [&a, &d]() {
+         // POINTER:{{^.*}}|     auto F28 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                   ~~~^
+         // POINTER:{{^.*}}|     auto F28 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                       ~~~^
+
+        (void)a;
+        (void)d;
+    };
+
+    auto F29 = [&a, &b, &c, &d]() {
+    // CHECK: auto F29 = [ &b, &c]() {
+         // POINTER:{{^.*}}|     auto F29 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~
+         // POINTER:{{^.*}}|     auto F29 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                           ~~~^
+
+        (void)b;
+        (void)c;
+    };
+
+    auto F30 = [&a, &b, &c, &d]() {
+    // CHECK: auto F30 = [ &b, &d]() {
+         // POINTER:{{^.*}}|     auto F30 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~
+         // POINTER:{{^.*}}|     auto F30 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                       ~~~^
+
+        (void)b;
+        (void)d;
+    };
+
+    auto F31 = [&a, &b, &c, &d]() {
+    // CHECK: auto F31 = [ &c, &d]() {
+         // POINTER:{{^.*}}|     auto F31 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                 ~^~
+         // POINTER:{{^.*}}|     auto F31 = [&a, &b, &c, &d]() {
+    // POINTER-NEXT:{{^.*}}|                     ~^~
+        (void)c;
+        (void)d;
+    };
 }
diff --git a/clang/test/FixIt/fixit-unused-lambda-capture.cpp 
b/clang/test/FixIt/fixit-unused-lambda-capture.cpp
index ce0c78d677099..0fe78364c7136 100644
--- a/clang/test/FixIt/fixit-unused-lambda-capture.cpp
+++ b/clang/test/FixIt/fixit-unused-lambda-capture.cpp
@@ -1,5 +1,5 @@
 // RUN: cp %s %t
-// RUN: %clang_cc1 -x c++ -Wunused-lambda-capture -Wno-unused-value -std=c++1z 
-fixit %t
+// RUN: %clang_cc1 -x c++  -Wno-vla-cxx-extension -Wunused-lambda-capture 
-Wno-unused-value -std=c++1z -fixit %t
 // RUN: grep -v CHECK %t | FileCheck %s
 
 void test() {

>From 00f1e7d21f1e6034e9f6dd1afcb581a1b28acfb6 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Mon, 2 Jun 2025 22:08:40 -0700
Subject: [PATCH 3/5] Extract the fixit range construction into a separate
 function

---
 clang/include/clang/Sema/Sema.h |  7 ++--
 clang/lib/Sema/Sema.cpp         |  8 ++--
 clang/lib/Sema/SemaLambda.cpp   | 67 +++++++++++++++++++--------------
 3 files changed, 45 insertions(+), 37 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6391af9d8e63b..ce45ac5a399ec 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -975,10 +975,9 @@ class Sema final : public SemaBase {
   /// Calls \c Lexer::findNextToken() to find the next token, and if the
   /// locations of both ends of the token can be resolved it return that
   /// range; Otherwise it returns an invalid SourceRange.
-  SourceRange getRangeForNextToken(SourceLocation Loc,
-                                   bool IncludeMacros,
-                                   bool IncludeComments,
-                                   std::optional<tok::TokenKind> ExpectedToken 
= std::nullopt);
+  SourceRange getRangeForNextToken(
+      SourceLocation Loc, bool IncludeMacros, bool IncludeComments,
+      std::optional<tok::TokenKind> ExpectedToken = std::nullopt);
 
   /// Retrieve the module loader associated with the preprocessor.
   ModuleLoader &getModuleLoader() const;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 6be6a11eaba6d..e577bf2ecf591 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -84,10 +84,10 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation 
Loc, unsigned Offset) {
   return Lexer::getLocForEndOfToken(Loc, Offset, SourceMgr, LangOpts);
 }
 
-SourceRange Sema::getRangeForNextToken(SourceLocation Loc,
-                                       bool IncludeMacros,
-                                       bool IncludeComments,
-                                       std::optional<tok::TokenKind> 
ExpectedToken) {
+SourceRange
+Sema::getRangeForNextToken(SourceLocation Loc, bool IncludeMacros,
+                           bool IncludeComments,
+                           std::optional<tok::TokenKind> ExpectedToken) {
   if (!Loc.isValid())
     return SourceRange();
   std::optional<Token> NextToken =
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index c6a7d0f323f93..9ce512fb0928c 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -2101,6 +2101,39 @@ FieldDecl *Sema::BuildCaptureField(RecordDecl *RD,
   return Field;
 }
 
+static SourceRange
+constructFixItRangeForUnusedCapture(Sema &S, SourceRange CaptureRange,
+                                    SourceLocation PrevCaptureLoc,
+                                    bool CurHasPreviousCapture, bool IsLast) {
+  if (!CaptureRange.isValid())
+    return SourceRange();
+
+  auto GetTrailingEndLocation = [&](SourceLocation StartPoint) {
+    SourceRange NextToken = S.getRangeForNextToken(
+        StartPoint, /*IncludeMacros=*/false, /*IncludeComments=*/true);
+    if (!NextToken.isValid())
+      return SourceLocation();
+    // Return the last location preceding the next token
+    return NextToken.getBegin().getLocWithOffset(-1);
+  };
+
+  if (!CurHasPreviousCapture && !IsLast) {
+    // If there are no captures preceding this capture, remove the
+    // trailing comma and anything up to the next token
+    SourceRange CommaRange =
+        S.getRangeForNextToken(CaptureRange.getEnd(), /*IncludeMacros=*/false,
+                               /*IncludeComments=*/false, tok::comma);
+    SourceLocation FixItEnd = GetTrailingEndLocation(CommaRange.getBegin());
+    return SourceRange(CaptureRange.getBegin(), FixItEnd);
+  }
+
+  // Otherwise, remove the comma since the last used capture, and
+  // anything up to the next token
+  SourceLocation FixItStart = S.getLocForEndOfToken(PrevCaptureLoc);
+  SourceLocation FixItEnd = GetTrailingEndLocation(CaptureRange.getEnd());
+  return SourceRange(FixItStart, FixItEnd);
+}
+
 ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation 
EndLoc,
                                  LambdaScopeInfo *LSI) {
   // Collect information from the lambda scope.
@@ -2168,35 +2201,11 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation 
StartLoc, SourceLocation EndLoc,
             IsGenericLambda && From.isNonODRUsed() && From.isInitCapture();
         if (!NonODRUsedInitCapture) {
           bool IsLast = (I + 1) == LSI->NumExplicitCaptures;
-          SourceRange FixItRange;
-          if (CaptureRange.isValid()) {
-            auto GetTrailingEndLocation = [&](SourceLocation StartPoint) {
-              SourceRange NextToken =
-                  getRangeForNextToken(StartPoint, /*IncludeMacros=*/false, 
/*IncludeComments=*/true);
-              if (!NextToken.isValid())
-                return SourceLocation();
-              // Return the last location preceding the next token
-              return NextToken.getBegin().getLocWithOffset(-1);
-            };
-            if (!CurHasPreviousCapture && !IsLast) {
-              // If there are no captures preceding this capture, remove the
-              // trailing comma and anything up to the next token
-              SourceRange CommaRange =
-                  getRangeForNextToken(CaptureRange.getEnd(), 
/*IncludeMacros=*/false, /*IncludeComments=*/false, tok::comma);
-              SourceLocation FixItEnd =
-                  GetTrailingEndLocation(CommaRange.getBegin());
-              FixItRange = SourceRange(CaptureRange.getBegin(), FixItEnd);
-            } else {
-              // Otherwise, remove the comma since the last used capture, and
-              // anything up to the next token
-              SourceLocation FixItStart = getLocForEndOfToken(PrevCaptureLoc);
-              SourceLocation FixItEnd =
-                  GetTrailingEndLocation(CaptureRange.getEnd());
-              FixItRange = SourceRange(FixItStart, FixItEnd);
-            }
-          }
-
-          IsCaptureUsed = !DiagnoseUnusedLambdaCapture(CaptureRange, 
FixItRange, From);
+          SourceRange FixItRange = constructFixItRangeForUnusedCapture(
+              *this, CaptureRange, PrevCaptureLoc, CurHasPreviousCapture,
+              IsLast);
+          IsCaptureUsed =
+              !DiagnoseUnusedLambdaCapture(CaptureRange, FixItRange, From);
         }
       }
 

>From 357995f44261ef0d9c48b1a8e7645abd496ff30b Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Mon, 2 Jun 2025 22:17:57 -0700
Subject: [PATCH 4/5] Add release note

---
 clang/docs/ReleaseNotes.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 648b32c659b4f..138569a7026b8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -561,6 +561,8 @@ Improvements to Clang's diagnostics
 - Fixed a crash when checking a ``__thread``-specified variable declaration
   with a dependent type in C++. (#GH140509)
 
+- Improved the FixIts for unused lambda captures.
+
 Improvements to Clang's time-trace
 ----------------------------------
 

>From 2f769f03f4d1b95366249959b80e9004ed15bd70 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Tue, 3 Jun 2025 13:18:22 -0700
Subject: [PATCH 5/5] Correct corentin's nits :D

---
 clang/lib/Sema/Sema.cpp                                   | 7 +++----
 clang/lib/Sema/SemaLambda.cpp                             | 4 ++--
 .../FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp | 8 ++++----
 clang/test/FixIt/fixit-unused-lambda-capture.cpp          | 2 +-
 4 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 051844e3d238d..370ade6dea7a1 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -100,10 +100,9 @@ Sema::getRangeForNextToken(SourceLocation Loc, bool 
IncludeMacros,
   SourceLocation TokenEnd = NextToken->getLastLoc();
   if (!TokenStart.isValid() || !TokenEnd.isValid())
     return SourceRange();
-  if (!IncludeMacros) {
-    if (TokenStart.isMacroID() || TokenEnd.isMacroID())
-      return SourceRange();
-  }
+  if (!IncludeMacros && (TokenStart.isMacroID() || TokenEnd.isMacroID()))
+    return SourceRange();
+
   return SourceRange(TokenStart, TokenEnd);
 }
 
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 9ce512fb0928c..2c00616bc62d7 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -2102,7 +2102,7 @@ FieldDecl *Sema::BuildCaptureField(RecordDecl *RD,
 }
 
 static SourceRange
-constructFixItRangeForUnusedCapture(Sema &S, SourceRange CaptureRange,
+ConstructFixItRangeForUnusedCapture(Sema &S, SourceRange CaptureRange,
                                     SourceLocation PrevCaptureLoc,
                                     bool CurHasPreviousCapture, bool IsLast) {
   if (!CaptureRange.isValid())
@@ -2201,7 +2201,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, 
SourceLocation EndLoc,
             IsGenericLambda && From.isNonODRUsed() && From.isInitCapture();
         if (!NonODRUsedInitCapture) {
           bool IsLast = (I + 1) == LSI->NumExplicitCaptures;
-          SourceRange FixItRange = constructFixItRangeForUnusedCapture(
+          SourceRange FixItRange = ConstructFixItRangeForUnusedCapture(
               *this, CaptureRange, PrevCaptureLoc, CurHasPreviousCapture,
               IsLast);
           IsCaptureUsed =
diff --git a/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp 
b/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
index 1bb99fa1ae2a8..80250e6d1851f 100644
--- a/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
+++ b/clang/test/FixIt/fixit-unused-lambda-capture-trailing-tokens.cpp
@@ -55,7 +55,7 @@ int main() {
     // POINTER-NEXT:{{^.*}}|         , &b]() {
     // POINTER-NEXT:{{^.*}}|         ~
          // POINTER:{{^.*}}|     auto F4 = [&a
-    // POINTER-NEXT:{{^.*}}|                  
+    // POINTER-NEXT:{{^.*}}|                  {{$}}
     // POINTER-NEXT:{{^.*}}|         , &b]() {
     // POINTER-NEXT:{{^.*}}|         ~~~^
     };
@@ -102,7 +102,7 @@ int main() {
         , &b]() {
     // CHECK: auto F4a = [&a]() {
          // POINTER:{{^.*}}|     auto F4a = [&a
-    // POINTER-NEXT:{{^.*}}|                   
+    // POINTER-NEXT:{{^.*}}|                   {{$}}
     // POINTER-NEXT:{{^.*}}|         , &b]() {
     // POINTER-NEXT:{{^.*}}|         ~~~^
         a++;
@@ -283,7 +283,7 @@ int main() {
 
         a++;
     };
-    
+
     auto F19 = [&a /* comment */, &b /* comment */]() {
     // CHECK: auto F19 = [&a /* comment */]() {
          // POINTER:{{^.*}}|     auto F19 = [&a /* comment */, &b /* comment 
*/]() {
@@ -423,7 +423,7 @@ int main() {
          // POINTER:{{^.*}}|     auto F25amb = [&a, &M_B]() {
     // POINTER-NEXT:{{^.*}}|                      ~~~^~~
     };
-    
+
     auto F26 = [&a, &b, &c, &d]() {
     // CHECK: auto F26 = [&a, &b]() {
          // POINTER:{{^.*}}|     auto F26 = [&a, &b, &c, &d]() {
diff --git a/clang/test/FixIt/fixit-unused-lambda-capture.cpp 
b/clang/test/FixIt/fixit-unused-lambda-capture.cpp
index 0fe78364c7136..ce0c78d677099 100644
--- a/clang/test/FixIt/fixit-unused-lambda-capture.cpp
+++ b/clang/test/FixIt/fixit-unused-lambda-capture.cpp
@@ -1,5 +1,5 @@
 // RUN: cp %s %t
-// RUN: %clang_cc1 -x c++  -Wno-vla-cxx-extension -Wunused-lambda-capture 
-Wno-unused-value -std=c++1z -fixit %t
+// RUN: %clang_cc1 -x c++ -Wunused-lambda-capture -Wno-unused-value -std=c++1z 
-fixit %t
 // RUN: grep -v CHECK %t | FileCheck %s
 
 void test() {

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to