[clang] [clang-tools-extra] [clang-tidy] Add readability-avoid-default-lambda-capture (PR #160150)
JJ Marr via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 15 12:11:35 PDT 2025
https://github.com/jjmarr-amd updated https://github.com/llvm/llvm-project/pull/160150
>From 35df1c6ea3af8dbaea2d7049ae79b14ce39f0774 Mon Sep 17 00:00:00 2001
From: JJ Marr <JJ.Marr at amd.com>
Date: Fri, 11 Jul 2025 14:40:57 -0400
Subject: [PATCH 01/42] Bugprone-default-lambda-capture
on-behalf-of: @AMD <JJ.Marr at amd.com>
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../bugprone/DefaultLambdaCaptureCheck.cpp | 35 +++++++
.../bugprone/DefaultLambdaCaptureCheck.h | 26 +++++
clang-tools-extra/docs/ReleaseNotes.rst | 9 ++
.../bugprone/default-lambda-capture.rst | 42 ++++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../bugprone/default-lambda-capture.cpp | 98 +++++++++++++++++++
8 files changed, 215 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 491de6acea2b7..db99d57c511b8 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -23,6 +23,7 @@
#include "CopyConstructorInitCheck.h"
#include "CrtpConstructorAccessibilityCheck.h"
#include "DanglingHandleCheck.h"
+#include "DefaultLambdaCaptureCheck.h"
#include "DynamicStaticInitializersCheck.h"
#include "EasilySwappableParametersCheck.h"
#include "EmptyCatchCheck.h"
@@ -134,6 +135,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
"bugprone-dangling-handle");
+ CheckFactories.registerCheck<DefaultLambdaCaptureCheck>(
+ "bugprone-default-lambda-capture");
CheckFactories.registerCheck<DynamicStaticInitializersCheck>(
"bugprone-dynamic-static-initializers");
CheckFactories.registerCheck<EasilySwappableParametersCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 46bc8efd44bc5..66125c9d22c1c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -19,6 +19,7 @@ add_clang_library(clangTidyBugproneModule STATIC
CopyConstructorInitCheck.cpp
CrtpConstructorAccessibilityCheck.cpp
DanglingHandleCheck.cpp
+ DefaultLambdaCaptureCheck.cpp
DynamicStaticInitializersCheck.cpp
EasilySwappableParametersCheck.cpp
EmptyCatchCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
new file mode 100644
index 0000000000000..6c95fbc984c5a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -0,0 +1,35 @@
+#include "DefaultLambdaCaptureCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void DefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
+ // Match any lambda expression
+ Finder->addMatcher(lambdaExpr().bind("lambda"), this);
+}
+
+void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
+ if (!Lambda)
+ return;
+
+ // Check if lambda has a default capture
+ if (Lambda->getCaptureDefault() == LCD_None)
+ return;
+
+ SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
+ if (DefaultCaptureLoc.isInvalid())
+ return;
+
+ const char *CaptureKind =
+ (Lambda->getCaptureDefault() == LCD_ByCopy) ? "by-copy" : "by-reference";
+
+ diag(DefaultCaptureLoc, "lambda %0 default capture is discouraged; "
+ "prefer to capture specific variables explicitly")
+ << CaptureKind;
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
new file mode 100644
index 0000000000000..ac47c866cfccd
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
@@ -0,0 +1,26 @@
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/** Flags lambdas that use default capture modes
+ *
+ * For the user-facing documentation see:
+ * http://clang.llvm.org/extra/clang-tidy/checks/bugprone/default-lambda-capture.html
+ */
+class DefaultLambdaCaptureCheck : public ClangTidyCheck {
+public:
+ DefaultLambdaCaptureCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 7cdff86beeec6..30d0bffb78f39 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -194,6 +194,15 @@ New checks
Finds virtual function overrides with different visibility than the function
in the base class.
+- New :doc:`bugprone-default-lambda-capture
+ <clang-tidy/checks/bugprone/default-lambda-capture>` check.
+
+ Finds lambda expressions that use default capture modes (``[=]`` or ``[&]``)
+ and suggests using explicit captures instead. Default captures can lead to
+ subtle bugs including dangling references with ``[&]``, unnecessary copies
+ with ``[=]``, and make code less maintainable by hiding which variables are
+ actually being captured.
+
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
new file mode 100644
index 0000000000000..026acf9f1894b
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
@@ -0,0 +1,42 @@
+.. title:: clang-tidy - bugprone-default-lambda-capture
+
+bugprone-default-lambda-capture
+===============================
+
+Warns when lambda expressions use default capture modes (``[=]`` or ``[&]``)
+instead of explicitly capturing specific variables. Default captures can make
+code less readable and more error-prone by making it unclear which variables
+are being captured and how.
+
+Implements Item 31 of Effective Modern C++ by Scott Meyers.
+
+Example
+-------
+
+.. code-block:: c++
+
+ void example() {
+ int x = 1;
+ int y = 2;
+
+ // Bad - default capture by copy
+ auto lambda1 = [=]() { return x + y; };
+
+ // Bad - default capture by reference
+ auto lambda2 = [&]() { return x + y; };
+
+ // Good - explicit captures
+ auto lambda3 = [x, y]() { return x + y; };
+ auto lambda4 = [&x, &y]() { return x + y; };
+ }
+
+The check will warn on:
+
+- Default capture by copy: ``[=]``
+- Default capture by reference: ``[&]``
+- Mixed captures with defaults: ``[=, &x]`` or ``[&, x]``
+
+The check will not warn on:
+
+- Explicit captures: ``[x]``, ``[&x]``, ``[x, y]``, ``[this]``
+- Empty capture lists: ``[]``
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c490d2ece2e0a..cb6254ee36f42 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -91,6 +91,7 @@ Clang-Tidy Checks
:doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, "Yes"
:doc:`bugprone-crtp-constructor-accessibility <bugprone/crtp-constructor-accessibility>`, "Yes"
:doc:`bugprone-dangling-handle <bugprone/dangling-handle>`,
+ :doc:`bugprone-default-lambda-capture <bugprone/default-lambda-capture>`,
:doc:`bugprone-dynamic-static-initializers <bugprone/dynamic-static-initializers>`,
:doc:`bugprone-easily-swappable-parameters <bugprone/easily-swappable-parameters>`,
:doc:`bugprone-empty-catch <bugprone/empty-catch>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
new file mode 100644
index 0000000000000..f820b4c9b8a0e
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
@@ -0,0 +1,98 @@
+// RUN: %check_clang_tidy %s bugprone-default-lambda-capture %t
+
+void test_default_captures() {
+ int value = 42;
+ int another = 10;
+
+ auto lambda1 = [=](int x) { return value + x; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto lambda2 = [&](int x) { return value + x; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto lambda3 = [=, &another](int x) { return value + another + x; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto lambda4 = [&, value](int x) { return value + another + x; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+}
+
+void test_acceptable_captures() {
+ int value = 42;
+ int another = 10;
+
+ auto lambda1 = [value](int x) { return value + x; };
+ auto lambda2 = [&value](int x) { return value + x; };
+ auto lambda3 = [value, another](int x) { return value + another + x; };
+ auto lambda4 = [&value, &another](int x) { return value + another + x; };
+
+ auto lambda5 = [](int x, int y) { return x + y; };
+
+ struct S {
+ int member = 5;
+ void foo() {
+ auto lambda = [this]() { return member; };
+ }
+ };
+}
+
+void test_nested_lambdas() {
+ int outer_var = 1;
+ int middle_var = 2;
+ int inner_var = 3;
+
+ auto outer = [=]() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto inner = [&](int x) { return outer_var + middle_var + inner_var + x; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ return inner(10);
+ };
+}
+
+void test_lambda_returns() {
+ int a = 1, b = 2, c = 3;
+
+ auto create_adder = [=](int x) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ return [x](int y) { return x + y; }; // Inner lambda is fine - explicit capture
+ };
+
+ auto func1 = [&]() { return a; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto func2 = [=]() { return b; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+}
+
+class TestClass {
+ int member = 42;
+
+public:
+ void test_member_function_lambdas() {
+ int local = 10;
+
+ auto lambda1 = [=]() { return member + local; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto lambda2 = [&]() { return member + local; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+
+ auto lambda3 = [this, local]() { return member + local; };
+ auto lambda4 = [this, &local]() { return member + local; };
+ }
+};
+
+template<typename T>
+void test_template_lambdas() {
+ T value{};
+
+ auto lambda = [=](T x) { return value + x; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+}
+
+void instantiate_templates() {
+ test_template_lambdas<int>();
+ test_template_lambdas<double>();
+}
>From 689f95b2ba46d6ca319e0735b5f5474bfc14f999 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 09:58:59 -0400
Subject: [PATCH 02/42] Fix headers
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp | 8 ++++++++
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.h | 8 ++++++++
2 files changed, 16 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 6c95fbc984c5a..0ee65df4b9860 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -1,3 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
#include "DefaultLambdaCaptureCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
index ac47c866cfccd..9af861aaf2e93 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
@@ -1,3 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
>From 9991b9ceda13c036e78c0e95978fe16f0bde5959 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 10:21:44 -0400
Subject: [PATCH 03/42] remove superfluous comments.
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 0ee65df4b9860..6db5cc8fc10bb 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -15,7 +15,6 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
void DefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
- // Match any lambda expression
Finder->addMatcher(lambdaExpr().bind("lambda"), this);
}
@@ -24,7 +23,6 @@ void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
if (!Lambda)
return;
- // Check if lambda has a default capture
if (Lambda->getCaptureDefault() == LCD_None)
return;
>From db17a4c5a29faa47c69ddb48edb2b0316287c5bc Mon Sep 17 00:00:00 2001
From: "Marr, J J" <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 10:37:30 -0400
Subject: [PATCH 04/42] add new matcher
on-behalf-of: @AMD <JJ.Marr at amd.com>
---
.../bugprone/DefaultLambdaCaptureCheck.cpp | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 6db5cc8fc10bb..7fc3e571db220 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -14,8 +14,15 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
+namespace {
+AST_MATCHER(LambdaExpr, hasDefaultCapture) {
+ return Node.getCaptureDefault() != LCD_None;
+}
+
+} // namespace
+
void DefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(lambdaExpr().bind("lambda"), this);
+ Finder->addMatcher(lambdaExpr(hasDefaultCapture()).bind("lambda"), this);
}
void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
@@ -23,8 +30,8 @@ void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
if (!Lambda)
return;
- if (Lambda->getCaptureDefault() == LCD_None)
- return;
+ // No need to check getCaptureDefault() != LCD_None since our custom matcher
+ // hasDefaultCapture() already ensures this condition
SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
>From 7dcf2162c2a6df4057585e2898bc8feb4536a67c Mon Sep 17 00:00:00 2001
From: "Marr, J J" <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 11:51:27 -0400
Subject: [PATCH 05/42] Replace if with assert
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 7fc3e571db220..4db95101025cc 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -27,11 +27,7 @@ void DefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
- if (!Lambda)
- return;
-
- // No need to check getCaptureDefault() != LCD_None since our custom matcher
- // hasDefaultCapture() already ensures this condition
+ assert(Lambda);
SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
>From 176d195cc753b3c95b9a82305e1fe1267e181cfa Mon Sep 17 00:00:00 2001
From: "Marr, J J" <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 11:52:40 -0400
Subject: [PATCH 06/42] Remove specific warning type
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../bugprone/DefaultLambdaCaptureCheck.cpp | 8 ++-----
.../bugprone/default-lambda-capture.cpp | 24 +++++++++----------
2 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 4db95101025cc..288feb7853e0b 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -33,12 +33,8 @@ void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
if (DefaultCaptureLoc.isInvalid())
return;
- const char *CaptureKind =
- (Lambda->getCaptureDefault() == LCD_ByCopy) ? "by-copy" : "by-reference";
-
- diag(DefaultCaptureLoc, "lambda %0 default capture is discouraged; "
- "prefer to capture specific variables explicitly")
- << CaptureKind;
+ diag(DefaultCaptureLoc, "lambda default captures are discouraged; "
+ "prefer to capture specific variables explicitly");
}
} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
index f820b4c9b8a0e..010edf11f0a2b 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
@@ -5,16 +5,16 @@ void test_default_captures() {
int another = 10;
auto lambda1 = [=](int x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto lambda2 = [&](int x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto lambda3 = [=, &another](int x) { return value + another + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto lambda4 = [&, value](int x) { return value + another + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
}
void test_acceptable_captures() {
@@ -42,10 +42,10 @@ void test_nested_lambdas() {
int inner_var = 3;
auto outer = [=]() {
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto inner = [&](int x) { return outer_var + middle_var + inner_var + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
return inner(10);
};
@@ -55,15 +55,15 @@ void test_lambda_returns() {
int a = 1, b = 2, c = 3;
auto create_adder = [=](int x) {
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
return [x](int y) { return x + y; }; // Inner lambda is fine - explicit capture
};
auto func1 = [&]() { return a; };
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto func2 = [=]() { return b; };
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
}
class TestClass {
@@ -74,10 +74,10 @@ class TestClass {
int local = 10;
auto lambda1 = [=]() { return member + local; };
- // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto lambda2 = [&]() { return member + local; };
- // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda by-reference default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
auto lambda3 = [this, local]() { return member + local; };
auto lambda4 = [this, &local]() { return member + local; };
@@ -89,7 +89,7 @@ void test_template_lambdas() {
T value{};
auto lambda = [=](T x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda by-copy default capture is discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
}
void instantiate_templates() {
>From 5f23dfff1a8368ebca7c2da691201ea1277ad128 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 12:25:50 -0400
Subject: [PATCH 07/42] Sync documentation to release notes
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../checks/bugprone/default-lambda-capture.rst | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
index 026acf9f1894b..f1fcf3ec52948 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
@@ -3,10 +3,11 @@
bugprone-default-lambda-capture
===============================
-Warns when lambda expressions use default capture modes (``[=]`` or ``[&]``)
-instead of explicitly capturing specific variables. Default captures can make
-code less readable and more error-prone by making it unclear which variables
-are being captured and how.
+ Finds lambda expressions that use default capture modes (``[=]`` or ``[&]``)
+ and suggests using explicit captures instead. Default captures can lead to
+ subtle bugs including dangling references with ``[&]``, unnecessary copies
+ with ``[=]``, and make code less maintainable by hiding which variables are
+ actually being captured.
Implements Item 31 of Effective Modern C++ by Scott Meyers.
>From 4c413327a64669d5254f0d6e343ca5c394701611 Mon Sep 17 00:00:00 2001
From: JJ Marr <jjmarr12 at amd.com>
Date: Mon, 22 Sep 2025 13:00:22 -0400
Subject: [PATCH 08/42] Update
clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
index 9af861aaf2e93..ad48316708b1b 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
@@ -16,7 +16,7 @@ namespace clang::tidy::bugprone {
/** Flags lambdas that use default capture modes
*
* For the user-facing documentation see:
- * http://clang.llvm.org/extra/clang-tidy/checks/bugprone/default-lambda-capture.html
+ * https://clang.llvm.org/extra/clang-tidy/checks/bugprone/default-lambda-capture.html
*/
class DefaultLambdaCaptureCheck : public ClangTidyCheck {
public:
>From 330dbd8ca5eeed45d63d1a44c15eeb5357a8a4ef Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 13:01:23 -0400
Subject: [PATCH 09/42] Remove superfluous include
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 288feb7853e0b..8f52aeb59ead6 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "DefaultLambdaCaptureCheck.h"
-#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
>From 31366e6b34dce3ab7e5f24d07f81ad10ecd9b875 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 13:09:55 -0400
Subject: [PATCH 10/42] Fix doc and release notes
on-behalf-of: @amd <JJ.Marr at amd.com>
---
clang-tools-extra/docs/ReleaseNotes.rst | 14 +++++---------
.../checks/bugprone/default-lambda-capture.rst | 10 +++++-----
2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 780cf41373128..9d2e8dcad7045 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -167,6 +167,11 @@ New checks
Detects default initialization (to 0) of variables with ``enum`` type where
the enum has no enumerator with value of 0.
+- New :doc:`bugprone-default-lambda-capture
+ <clang-tidy/checks/bugprone/default-lambda-capture>` check.
+
+ Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
+
- New :doc:`bugprone-derived-method-shadowing-base-method
<clang-tidy/checks/bugprone/derived-method-shadowing-base-method>` check.
@@ -203,15 +208,6 @@ New checks
Finds virtual function overrides with different visibility than the function
in the base class.
-- New :doc:`bugprone-default-lambda-capture
- <clang-tidy/checks/bugprone/default-lambda-capture>` check.
-
- Finds lambda expressions that use default capture modes (``[=]`` or ``[&]``)
- and suggests using explicit captures instead. Default captures can lead to
- subtle bugs including dangling references with ``[&]``, unnecessary copies
- with ``[=]``, and make code less maintainable by hiding which variables are
- actually being captured.
-
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
index f1fcf3ec52948..9cedc6276e1b3 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
@@ -3,11 +3,11 @@
bugprone-default-lambda-capture
===============================
- Finds lambda expressions that use default capture modes (``[=]`` or ``[&]``)
- and suggests using explicit captures instead. Default captures can lead to
- subtle bugs including dangling references with ``[&]``, unnecessary copies
- with ``[=]``, and make code less maintainable by hiding which variables are
- actually being captured.
+Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
+
+Default captures can lead to subtle bugs including dangling references with
+``[&]``, unnecessary copies with ``[=]``, and make code less maintainable by
+hiding which variables are actually being captured.
Implements Item 31 of Effective Modern C++ by Scott Meyers.
>From 70df9dff73c3b3de4dcdfbed8b44186ff2000997 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 22 Sep 2025 14:43:19 -0400
Subject: [PATCH 11/42] Fix const
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
index 8f52aeb59ead6..a7a18ccb30864 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
@@ -28,7 +28,7 @@ void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
assert(Lambda);
- SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
+ const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
return;
>From 0bcdbc4373a6d320f49f30a64c647b1da3956756 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Tue, 23 Sep 2025 13:15:11 -0400
Subject: [PATCH 12/42] Fix header guard
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
index ad48316708b1b..fa69245b91c94 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURE_H
#include "../ClangTidyCheck.h"
@@ -31,4 +31,4 @@ class DefaultLambdaCaptureCheck : public ClangTidyCheck {
} // namespace clang::tidy::bugprone
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULT_LAMBDA_CAPTURE_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURE_H
>From ac709f4fcf092da2b240b9e0a62d1f8c314fdebb Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Tue, 23 Sep 2025 16:07:45 -0400
Subject: [PATCH 13/42] Fix header spelling
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/bugprone/DefaultLambdaCaptureCheck.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
index fa69245b91c94..c41d7257592d5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURE_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURE_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURECHECK_H
#include "../ClangTidyCheck.h"
@@ -31,4 +31,4 @@ class DefaultLambdaCaptureCheck : public ClangTidyCheck {
} // namespace clang::tidy::bugprone
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURE_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURECHECK_H
>From 5d4c2ca704cce01a491ac38ecd25bf722b94ad78 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 24 Sep 2025 13:24:17 -0400
Subject: [PATCH 14/42] Rename check to readability-default-lambda-capture
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../bugprone/BugproneTidyModule.cpp | 3 ---
.../clang-tidy/bugprone/CMakeLists.txt | 1 -
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../DefaultLambdaCaptureCheck.cpp | 4 +--
.../DefaultLambdaCaptureCheck.h | 12 ++++-----
.../readability/ReadabilityTidyModule.cpp | 3 +++
clang-tools-extra/docs/ReleaseNotes.rst | 10 +++----
.../docs/clang-tidy/checks/list.rst | 2 +-
.../default-lambda-capture.rst | 4 +--
.../default-lambda-capture.cpp | 26 +++++++++----------
10 files changed, 33 insertions(+), 33 deletions(-)
rename clang-tools-extra/clang-tidy/{bugprone => readability}/DefaultLambdaCaptureCheck.cpp (94%)
rename clang-tools-extra/clang-tidy/{bugprone => readability}/DefaultLambdaCaptureCheck.h (69%)
rename clang-tools-extra/docs/clang-tidy/checks/{bugprone => readability}/default-lambda-capture.rst (91%)
rename clang-tools-extra/test/clang-tidy/checkers/{bugprone => readability}/default-lambda-capture.cpp (84%)
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 67222cbafb5f9..8baa8f6b35d4c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -23,7 +23,6 @@
#include "CopyConstructorInitCheck.h"
#include "CrtpConstructorAccessibilityCheck.h"
#include "DanglingHandleCheck.h"
-#include "DefaultLambdaCaptureCheck.h"
#include "DerivedMethodShadowingBaseMethodCheck.h"
#include "DynamicStaticInitializersCheck.h"
#include "EasilySwappableParametersCheck.h"
@@ -137,8 +136,6 @@ class BugproneModule : public ClangTidyModule {
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
"bugprone-dangling-handle");
- CheckFactories.registerCheck<DefaultLambdaCaptureCheck>(
- "bugprone-default-lambda-capture");
CheckFactories.registerCheck<DerivedMethodShadowingBaseMethodCheck>(
"bugprone-derived-method-shadowing-base-method");
CheckFactories.registerCheck<DynamicStaticInitializersCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index c8c31f9f96bb0..b0dbe84a16cd4 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -19,7 +19,6 @@ add_clang_library(clangTidyBugproneModule STATIC
CopyConstructorInitCheck.cpp
CrtpConstructorAccessibilityCheck.cpp
DanglingHandleCheck.cpp
- DefaultLambdaCaptureCheck.cpp
DerivedMethodShadowingBaseMethodCheck.cpp
DynamicStaticInitializersCheck.cpp
EasilySwappableParametersCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 4b4c49d3b17d1..829caa15377e6 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -15,6 +15,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
ContainerDataPointerCheck.cpp
ContainerSizeEmptyCheck.cpp
ConvertMemberFunctionsToStatic.cpp
+ DefaultLambdaCaptureCheck.cpp
DeleteNullPointerCheck.cpp
DuplicateIncludeCheck.cpp
ElseAfterReturnCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.cpp
similarity index 94%
rename from clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
rename to clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.cpp
index a7a18ccb30864..60914fc7e7069 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.cpp
@@ -11,7 +11,7 @@
using namespace clang::ast_matchers;
-namespace clang::tidy::bugprone {
+namespace clang::tidy::readability {
namespace {
AST_MATCHER(LambdaExpr, hasDefaultCapture) {
@@ -36,4 +36,4 @@ void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
"prefer to capture specific variables explicitly");
}
-} // namespace clang::tidy::bugprone
+} // namespace clang::tidy::readability
diff --git a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.h
similarity index 69%
rename from clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
rename to clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.h
index c41d7257592d5..fd29d71b7c542 100644
--- a/clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.h
@@ -6,17 +6,17 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURECHECK_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURECHECK_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DEFAULTLAMBDACAPTURECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DEFAULTLAMBDACAPTURECHECK_H
#include "../ClangTidyCheck.h"
-namespace clang::tidy::bugprone {
+namespace clang::tidy::readability {
/** Flags lambdas that use default capture modes
*
* For the user-facing documentation see:
- * https://clang.llvm.org/extra/clang-tidy/checks/bugprone/default-lambda-capture.html
+ * https://clang.llvm.org/extra/clang-tidy/checks/readability/default-lambda-capture.html
*/
class DefaultLambdaCaptureCheck : public ClangTidyCheck {
public:
@@ -29,6 +29,6 @@ class DefaultLambdaCaptureCheck : public ClangTidyCheck {
}
};
-} // namespace clang::tidy::bugprone
+} // namespace clang::tidy::readability
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DEFAULTLAMBDACAPTURECHECK_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DEFAULTLAMBDACAPTURECHECK_H
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index d01882dfc9daa..4b9bf3d417a7b 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -20,6 +20,7 @@
#include "ContainerDataPointerCheck.h"
#include "ContainerSizeEmptyCheck.h"
#include "ConvertMemberFunctionsToStatic.h"
+#include "DefaultLambdaCaptureCheck.h"
#include "DeleteNullPointerCheck.h"
#include "DuplicateIncludeCheck.h"
#include "ElseAfterReturnCheck.h"
@@ -92,6 +93,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-container-size-empty");
CheckFactories.registerCheck<ConvertMemberFunctionsToStatic>(
"readability-convert-member-functions-to-static");
+ CheckFactories.registerCheck<DefaultLambdaCaptureCheck>(
+ "readability-default-lambda-capture");
CheckFactories.registerCheck<DeleteNullPointerCheck>(
"readability-delete-null-pointer");
CheckFactories.registerCheck<DuplicateIncludeCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9d2e8dcad7045..6f29f0e99d6f7 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -167,11 +167,6 @@ New checks
Detects default initialization (to 0) of variables with ``enum`` type where
the enum has no enumerator with value of 0.
-- New :doc:`bugprone-default-lambda-capture
- <clang-tidy/checks/bugprone/default-lambda-capture>` check.
-
- Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
-
- New :doc:`bugprone-derived-method-shadowing-base-method
<clang-tidy/checks/bugprone/derived-method-shadowing-base-method>` check.
@@ -356,6 +351,11 @@ Changes in existing checks
<clang-tidy/checks/readability/uppercase-literal-suffix>` check to recognize
literal suffixes added in C++23 and C23.
+- New :doc:`readability-default-lambda-capture
+ <clang-tidy/checks/readability/default-lambda-capture>` check.
+
+ Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
+
Removed checks
^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5232f650f6579..e72cbf75fa03f 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -91,7 +91,6 @@ Clang-Tidy Checks
:doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, "Yes"
:doc:`bugprone-crtp-constructor-accessibility <bugprone/crtp-constructor-accessibility>`, "Yes"
:doc:`bugprone-dangling-handle <bugprone/dangling-handle>`,
- :doc:`bugprone-default-lambda-capture <bugprone/default-lambda-capture>`,
:doc:`bugprone-derived-method-shadowing-base-method <bugprone/derived-method-shadowing-base-method>`,
:doc:`bugprone-dynamic-static-initializers <bugprone/dynamic-static-initializers>`,
:doc:`bugprone-easily-swappable-parameters <bugprone/easily-swappable-parameters>`,
@@ -378,6 +377,7 @@ Clang-Tidy Checks
:doc:`readability-container-data-pointer <readability/container-data-pointer>`, "Yes"
:doc:`readability-container-size-empty <readability/container-size-empty>`, "Yes"
:doc:`readability-convert-member-functions-to-static <readability/convert-member-functions-to-static>`, "Yes"
+ :doc:`readability-default-lambda-capture <readability/default-lambda-capture>`,
:doc:`readability-delete-null-pointer <readability/delete-null-pointer>`, "Yes"
:doc:`readability-duplicate-include <readability/duplicate-include>`, "Yes"
:doc:`readability-else-after-return <readability/else-after-return>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
similarity index 91%
rename from clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
rename to clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
index 9cedc6276e1b3..00734b427d131 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
@@ -1,6 +1,6 @@
-.. title:: clang-tidy - bugprone-default-lambda-capture
+.. title:: clang-tidy - readability-default-lambda-capture
-bugprone-default-lambda-capture
+readability-default-lambda-capture
===============================
Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/default-lambda-capture.cpp
similarity index 84%
rename from clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
rename to clang-tools-extra/test/clang-tidy/checkers/readability/default-lambda-capture.cpp
index 010edf11f0a2b..ecc45130a6cdf 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/default-lambda-capture.cpp
@@ -1,20 +1,20 @@
-// RUN: %check_clang_tidy %s bugprone-default-lambda-capture %t
+// RUN: %check_clang_tidy %s readability-default-lambda-capture %t
void test_default_captures() {
int value = 42;
int another = 10;
auto lambda1 = [=](int x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto lambda2 = [&](int x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto lambda3 = [=, &another](int x) { return value + another + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto lambda4 = [&, value](int x) { return value + another + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
}
void test_acceptable_captures() {
@@ -42,10 +42,10 @@ void test_nested_lambdas() {
int inner_var = 3;
auto outer = [=]() {
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto inner = [&](int x) { return outer_var + middle_var + inner_var + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
return inner(10);
};
@@ -55,15 +55,15 @@ void test_lambda_returns() {
int a = 1, b = 2, c = 3;
auto create_adder = [=](int x) {
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
return [x](int y) { return x + y; }; // Inner lambda is fine - explicit capture
};
auto func1 = [&]() { return a; };
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto func2 = [=]() { return b; };
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
}
class TestClass {
@@ -74,10 +74,10 @@ class TestClass {
int local = 10;
auto lambda1 = [=]() { return member + local; };
- // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto lambda2 = [&]() { return member + local; };
- // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
auto lambda3 = [this, local]() { return member + local; };
auto lambda4 = [this, &local]() { return member + local; };
@@ -89,7 +89,7 @@ void test_template_lambdas() {
T value{};
auto lambda = [=](T x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [bugprone-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
}
void instantiate_templates() {
>From c0b90d1d0a20ac71d4f6944e0818a1d56eba4acd Mon Sep 17 00:00:00 2001
From: JJ Marr <jjmarr12 at amd.com>
Date: Wed, 24 Sep 2025 13:32:04 -0400
Subject: [PATCH 15/42] Update
clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../clang-tidy/checks/readability/default-lambda-capture.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
index 00734b427d131..785f112a70b11 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
@@ -1,7 +1,7 @@
.. title:: clang-tidy - readability-default-lambda-capture
readability-default-lambda-capture
-===============================
+==================================
Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
>From 1172f730ba67151e560dc4ec46a203a8bf83a52b Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Thu, 25 Sep 2025 10:29:37 -0400
Subject: [PATCH 16/42] update doc
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/default-lambda-capture.rst | 62 +++++++++++--------
1 file changed, 37 insertions(+), 25 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
index 785f112a70b11..daabdd1a92995 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
@@ -5,39 +5,51 @@ readability-default-lambda-capture
Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
-Default captures can lead to subtle bugs including dangling references with
-``[&]``, unnecessary copies with ``[=]``, and make code less maintainable by
-hiding which variables are actually being captured.
+Captures can lead to subtle bugs including dangling references and unnecessary
+copies. Writing out the name of the variables being captured reminds programmers
+and reviewers to know what is being captured. And knowing is half the battle.
-Implements Item 31 of Effective Modern C++ by Scott Meyers.
+Coding guidelines that recommend against defaulted lambda captures include:
+
+* Item 31 of Effective Modern C++ by Scott Meyers
+* `AUTOSAR C++ Rule A5-1-2 <https://www.mathworks.com/help//releases/
+ R2021a/bugfinder/ref/autosarc14rulea512.html>`__
Example
-------
.. code-block:: c++
- void example() {
- int x = 1;
- int y = 2;
-
- // Bad - default capture by copy
- auto lambda1 = [=]() { return x + y; };
-
- // Bad - default capture by reference
- auto lambda2 = [&]() { return x + y; };
-
- // Good - explicit captures
- auto lambda3 = [x, y]() { return x + y; };
- auto lambda4 = [&x, &y]() { return x + y; };
+ #include <iostream>
+
+ class Widget {
+ std::vector<std::function<void(int)>> callbacks;
+ int widgetId;
+ void addCallback(int factoryId) {
+ callbacks.emplace_back(
+ [&](){
+ std::cout << "Widget " << widgetId << " made in factory " << factoryId;
+ }
+ );
+ }
}
-The check will warn on:
-
-- Default capture by copy: ``[=]``
-- Default capture by reference: ``[&]``
-- Mixed captures with defaults: ``[=, &x]`` or ``[&, x]``
+When ``callbacks`` is executed, ``factoryId`` will dangle. Writing the name of
+``factoryId`` in the capture list makes it easy to review the captures and
+detect obvious bugs.
-The check will not warn on:
+.. code-block:: c++
-- Explicit captures: ``[x]``, ``[&x]``, ``[x, y]``, ``[this]``
-- Empty capture lists: ``[]``
+ #include <iostream>
+
+ class Widget {
+ std::vector<std::function<void(int)>> callbacks;
+ int widgetId;
+ void addCallback(int factoryId) {
+ callbacks.emplace_back(
+ [&factoryId, &widgetId](){ // Why isn't factoryId captured by value??
+ std::cout << "Widget " << widgetId << " made in factory " << factoryId;
+ }
+ );
+ }
+ }
>From 66e6aaa168928f18e8a427b0cf4abe21fb52fb9b Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Thu, 25 Sep 2025 11:04:26 -0400
Subject: [PATCH 17/42] rename check to
readability-avoid-default-lambda-capture
on-behalf-of: @amd <JJ.Marr at amd.com>
---
...cpp => AvoidDefaultLambdaCaptureCheck.cpp} | 7 ++--
...eck.h => AvoidDefaultLambdaCaptureCheck.h} | 12 +++----
.../clang-tidy/readability/CMakeLists.txt | 2 +-
.../readability/ReadabilityTidyModule.cpp | 6 ++--
clang-tools-extra/docs/ReleaseNotes.rst | 4 +--
.../docs/clang-tidy/checks/list.rst | 2 +-
...e.rst => avoid-default-lambda-capture.rst} | 6 ++--
...e.cpp => avoid-default-lambda-capture.cpp} | 32 +++++++++----------
8 files changed, 36 insertions(+), 35 deletions(-)
rename clang-tools-extra/clang-tidy/readability/{DefaultLambdaCaptureCheck.cpp => AvoidDefaultLambdaCaptureCheck.cpp} (83%)
rename clang-tools-extra/clang-tidy/readability/{DefaultLambdaCaptureCheck.h => AvoidDefaultLambdaCaptureCheck.h} (64%)
rename clang-tools-extra/docs/clang-tidy/checks/readability/{default-lambda-capture.rst => avoid-default-lambda-capture.rst} (91%)
rename clang-tools-extra/test/clang-tidy/checkers/readability/{default-lambda-capture.cpp => avoid-default-lambda-capture.cpp} (83%)
diff --git a/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
similarity index 83%
rename from clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.cpp
rename to clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 60914fc7e7069..1036d764497b4 100644
--- a/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "DefaultLambdaCaptureCheck.h"
+#include "AvoidDefaultLambdaCaptureCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
@@ -20,11 +20,12 @@ AST_MATCHER(LambdaExpr, hasDefaultCapture) {
} // namespace
-void DefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
+void AvoidDefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(lambdaExpr(hasDefaultCapture()).bind("lambda"), this);
}
-void DefaultLambdaCaptureCheck::check(const MatchFinder::MatchResult &Result) {
+void AvoidDefaultLambdaCaptureCheck::check(
+ const MatchFinder::MatchResult &Result) {
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
assert(Lambda);
diff --git a/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h
similarity index 64%
rename from clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.h
rename to clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h
index fd29d71b7c542..604cf85b2a71f 100644
--- a/clang-tools-extra/clang-tidy/readability/DefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DEFAULTLAMBDACAPTURECHECK_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DEFAULTLAMBDACAPTURECHECK_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDDEFAULTLAMBDACAPTURECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDDEFAULTLAMBDACAPTURECHECK_H
#include "../ClangTidyCheck.h"
@@ -16,11 +16,11 @@ namespace clang::tidy::readability {
/** Flags lambdas that use default capture modes
*
* For the user-facing documentation see:
- * https://clang.llvm.org/extra/clang-tidy/checks/readability/default-lambda-capture.html
+ * https://clang.llvm.org/extra/clang-tidy/checks/readability/avoid-default-lambda-capture.html
*/
-class DefaultLambdaCaptureCheck : public ClangTidyCheck {
+class AvoidDefaultLambdaCaptureCheck : public ClangTidyCheck {
public:
- DefaultLambdaCaptureCheck(StringRef Name, ClangTidyContext *Context)
+ AvoidDefaultLambdaCaptureCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
@@ -31,4 +31,4 @@ class DefaultLambdaCaptureCheck : public ClangTidyCheck {
} // namespace clang::tidy::readability
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DEFAULTLAMBDACAPTURECHECK_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDDEFAULTLAMBDACAPTURECHECK_H
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 829caa15377e6..b4e0c7b8a4a43 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangTidyReadabilityModule STATIC
AmbiguousSmartptrResetCallCheck.cpp
AvoidConstParamsInDecls.cpp
+ AvoidDefaultLambdaCaptureCheck.cpp
AvoidNestedConditionalOperatorCheck.cpp
AvoidReturnWithVoidValueCheck.cpp
AvoidUnconditionalPreprocessorIfCheck.cpp
@@ -15,7 +16,6 @@ add_clang_library(clangTidyReadabilityModule STATIC
ContainerDataPointerCheck.cpp
ContainerSizeEmptyCheck.cpp
ConvertMemberFunctionsToStatic.cpp
- DefaultLambdaCaptureCheck.cpp
DeleteNullPointerCheck.cpp
DuplicateIncludeCheck.cpp
ElseAfterReturnCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 4b9bf3d417a7b..34c9e71926d18 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -11,6 +11,7 @@
#include "../ClangTidyModuleRegistry.h"
#include "AmbiguousSmartptrResetCallCheck.h"
#include "AvoidConstParamsInDecls.h"
+#include "AvoidDefaultLambdaCaptureCheck.h"
#include "AvoidNestedConditionalOperatorCheck.h"
#include "AvoidReturnWithVoidValueCheck.h"
#include "AvoidUnconditionalPreprocessorIfCheck.h"
@@ -20,7 +21,6 @@
#include "ContainerDataPointerCheck.h"
#include "ContainerSizeEmptyCheck.h"
#include "ConvertMemberFunctionsToStatic.h"
-#include "DefaultLambdaCaptureCheck.h"
#include "DeleteNullPointerCheck.h"
#include "DuplicateIncludeCheck.h"
#include "ElseAfterReturnCheck.h"
@@ -93,8 +93,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-container-size-empty");
CheckFactories.registerCheck<ConvertMemberFunctionsToStatic>(
"readability-convert-member-functions-to-static");
- CheckFactories.registerCheck<DefaultLambdaCaptureCheck>(
- "readability-default-lambda-capture");
+ CheckFactories.registerCheck<AvoidDefaultLambdaCaptureCheck>(
+ "readability-avoid-default-lambda-capture");
CheckFactories.registerCheck<DeleteNullPointerCheck>(
"readability-delete-null-pointer");
CheckFactories.registerCheck<DuplicateIncludeCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6f29f0e99d6f7..d3edc1a9865ab 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -351,8 +351,8 @@ Changes in existing checks
<clang-tidy/checks/readability/uppercase-literal-suffix>` check to recognize
literal suffixes added in C++23 and C23.
-- New :doc:`readability-default-lambda-capture
- <clang-tidy/checks/readability/default-lambda-capture>` check.
+- New :doc:`readability-avoid-default-lambda-capture
+ <clang-tidy/checks/readability/avoid-default-lambda-capture>` check.
Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index e72cbf75fa03f..0b8dcd440e1f6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -377,7 +377,7 @@ Clang-Tidy Checks
:doc:`readability-container-data-pointer <readability/container-data-pointer>`, "Yes"
:doc:`readability-container-size-empty <readability/container-size-empty>`, "Yes"
:doc:`readability-convert-member-functions-to-static <readability/convert-member-functions-to-static>`, "Yes"
- :doc:`readability-default-lambda-capture <readability/default-lambda-capture>`,
+ :doc:`readability-avoid-default-lambda-capture <readability/avoid-default-lambda-capture>`,
:doc:`readability-delete-null-pointer <readability/delete-null-pointer>`, "Yes"
:doc:`readability-duplicate-include <readability/duplicate-include>`, "Yes"
:doc:`readability-else-after-return <readability/else-after-return>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
similarity index 91%
rename from clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
rename to clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index daabdd1a92995..9851896026be5 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -1,7 +1,7 @@
-.. title:: clang-tidy - readability-default-lambda-capture
+.. title:: clang-tidy - readability-avoid-default-lambda-capture
-readability-default-lambda-capture
-==================================
+readability-avoid-default-lambda-capture
+=========================================
Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
similarity index 83%
rename from clang-tools-extra/test/clang-tidy/checkers/readability/default-lambda-capture.cpp
rename to clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index ecc45130a6cdf..f452ea6f6ccb1 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -1,20 +1,20 @@
-// RUN: %check_clang_tidy %s readability-default-lambda-capture %t
+// RUN: %check_clang_tidy %s readability-avoid-default-lambda-capture %t
void test_default_captures() {
int value = 42;
int another = 10;
auto lambda1 = [=](int x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
auto lambda2 = [&](int x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
auto lambda3 = [=, &another](int x) { return value + another + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
auto lambda4 = [&, value](int x) { return value + another + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
}
void test_acceptable_captures() {
@@ -42,10 +42,10 @@ void test_nested_lambdas() {
int inner_var = 3;
auto outer = [=]() {
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
-
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
auto inner = [&](int x) { return outer_var + middle_var + inner_var + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
return inner(10);
};
@@ -55,15 +55,15 @@ void test_lambda_returns() {
int a = 1, b = 2, c = 3;
auto create_adder = [=](int x) {
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
return [x](int y) { return x + y; }; // Inner lambda is fine - explicit capture
};
auto func1 = [&]() { return a; };
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
-
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
auto func2 = [=]() { return b; };
- // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
}
class TestClass {
@@ -74,10 +74,10 @@ class TestClass {
int local = 10;
auto lambda1 = [=]() { return member + local; };
- // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
-
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
auto lambda2 = [&]() { return member + local; };
- // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
auto lambda3 = [this, local]() { return member + local; };
auto lambda4 = [this, &local]() { return member + local; };
@@ -89,7 +89,7 @@ void test_template_lambdas() {
T value{};
auto lambda = [=](T x) { return value + x; };
- // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-default-lambda-capture]
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
}
void instantiate_templates() {
>From 4bf58b6eddee9b1027654653fa409d6bd668e79c Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Thu, 25 Sep 2025 11:05:23 -0400
Subject: [PATCH 18/42] use C++ style comments
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/AvoidDefaultLambdaCaptureCheck.h | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h
index 604cf85b2a71f..82262a210e4c4 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.h
@@ -13,11 +13,10 @@
namespace clang::tidy::readability {
-/** Flags lambdas that use default capture modes
- *
- * For the user-facing documentation see:
- * https://clang.llvm.org/extra/clang-tidy/checks/readability/avoid-default-lambda-capture.html
- */
+/// Flags lambdas that use default capture modes
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/readability/avoid-default-lambda-capture.html
class AvoidDefaultLambdaCaptureCheck : public ClangTidyCheck {
public:
AvoidDefaultLambdaCaptureCheck(StringRef Name, ClangTidyContext *Context)
>From a807dd0e29f5af7af8cc6747082bac606b3ea2ec Mon Sep 17 00:00:00 2001
From: JJ Marr <jjmarr12 at amd.com>
Date: Thu, 25 Sep 2025 11:19:41 -0400
Subject: [PATCH 19/42] Update
clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
.../checks/readability/avoid-default-lambda-capture.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index 9851896026be5..4513e5d1ef305 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -1,7 +1,7 @@
.. title:: clang-tidy - readability-avoid-default-lambda-capture
readability-avoid-default-lambda-capture
-=========================================
+========================================
Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
>From 8fdc0974ed84e8177ab61c6dbeeccf6b43ed1593 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Thu, 25 Sep 2025 11:20:22 -0400
Subject: [PATCH 20/42] fix doc again
on-behalf-of: @amd <JJ.Marr at amd.com>
---
clang-tools-extra/docs/ReleaseNotes.rst | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index d3edc1a9865ab..583d4de022218 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -203,6 +203,11 @@ New checks
Finds virtual function overrides with different visibility than the function
in the base class.
+- New :doc:`readability-avoid-default-lambda-capture
+ <clang-tidy/checks/readability/avoid-default-lambda-capture>` check.
+
+ Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
+
New check aliases
^^^^^^^^^^^^^^^^^
@@ -351,11 +356,6 @@ Changes in existing checks
<clang-tidy/checks/readability/uppercase-literal-suffix>` check to recognize
literal suffixes added in C++23 and C23.
-- New :doc:`readability-avoid-default-lambda-capture
- <clang-tidy/checks/readability/avoid-default-lambda-capture>` check.
-
- Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
-
Removed checks
^^^^^^^^^^^^^^
>From ab8f4b9db4011587bc51407abf4f2a4ad9d7920c Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Thu, 25 Sep 2025 12:58:26 -0400
Subject: [PATCH 21/42] remove reference to coding guideline
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../checks/readability/avoid-default-lambda-capture.rst | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index 4513e5d1ef305..6b63e103e588e 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -12,8 +12,6 @@ and reviewers to know what is being captured. And knowing is half the battle.
Coding guidelines that recommend against defaulted lambda captures include:
* Item 31 of Effective Modern C++ by Scott Meyers
-* `AUTOSAR C++ Rule A5-1-2 <https://www.mathworks.com/help//releases/
- R2021a/bugfinder/ref/autosarc14rulea512.html>`__
Example
-------
>From 13b50491bbb329fa24b714d1af0203ab6ce85ce3 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 09:50:39 -0400
Subject: [PATCH 22/42] rephrase
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../checks/readability/avoid-default-lambda-capture.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index 6b63e103e588e..d4ecd4c7eb27d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -3,7 +3,7 @@
readability-avoid-default-lambda-capture
========================================
-Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``)
+Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``).
Captures can lead to subtle bugs including dangling references and unnecessary
copies. Writing out the name of the variables being captured reminds programmers
@@ -33,8 +33,8 @@ Example
}
When ``callbacks`` is executed, ``factoryId`` will dangle. Writing the name of
-``factoryId`` in the capture list makes it easy to review the captures and
-detect obvious bugs.
+``factoryId`` in the capture list reminds the reader that it is being captured,
+which will hopefully lead to the bug being fixed during code review.
.. code-block:: c++
>From 043df7c76564f55e5ff76e5139ca29fa827e402c Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 10:27:18 -0400
Subject: [PATCH 23/42] commit vibe coded work
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 57 ++++++++++++++++++-
.../avoid-default-lambda-capture.cpp | 12 ++++
2 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 1036d764497b4..361af6794a9b5 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -8,6 +8,8 @@
#include "AvoidDefaultLambdaCaptureCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/Lambda.h"
+#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
@@ -18,6 +20,45 @@ AST_MATCHER(LambdaExpr, hasDefaultCapture) {
return Node.getCaptureDefault() != LCD_None;
}
+std::string getCaptureString(const LambdaCapture &Capture) {
+ if (Capture.capturesThis()) {
+ return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
+ }
+
+ if (Capture.capturesVariable()) {
+ std::string Result;
+ if (Capture.getCaptureKind() == LCK_ByRef) {
+ Result += "&";
+ }
+ Result += Capture.getCapturedVar()->getName().str();
+ return Result;
+ }
+
+ // Handle VLA captures - these are rare but possible
+ return "/* VLA capture */";
+}
+
+std::string buildExplicitCaptureList(const LambdaExpr *Lambda) {
+ std::vector<std::string> CaptureStrings;
+
+ // Add explicit captures first (preserve their order and syntax)
+ for (const auto &Capture : Lambda->explicit_captures()) {
+ CaptureStrings.push_back(getCaptureString(Capture));
+ }
+
+ // Add implicit captures (convert to explicit syntax)
+ for (const auto &Capture : Lambda->implicit_captures()) {
+ CaptureStrings.push_back(getCaptureString(Capture));
+ }
+
+ return "[" + llvm::join(CaptureStrings, ", ") + "]";
+}
+
+SourceRange getCaptureListRange(const LambdaExpr *Lambda) {
+ SourceRange IntroducerRange = Lambda->getIntroducerRange();
+ return IntroducerRange;
+}
+
} // namespace
void AvoidDefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
@@ -33,8 +74,20 @@ void AvoidDefaultLambdaCaptureCheck::check(
if (DefaultCaptureLoc.isInvalid())
return;
- diag(DefaultCaptureLoc, "lambda default captures are discouraged; "
- "prefer to capture specific variables explicitly");
+ // Build the replacement capture list
+ std::string NewCaptureList = buildExplicitCaptureList(Lambda);
+
+ // Get the range of the entire capture list [...]
+ SourceRange CaptureListRange = getCaptureListRange(Lambda);
+
+ auto Diag = diag(DefaultCaptureLoc,
+ "lambda default captures are discouraged; "
+ "prefer to capture specific variables explicitly");
+
+ // Only provide fixit if we can determine a valid replacement
+ if (CaptureListRange.isValid() && !NewCaptureList.empty()) {
+ Diag << FixItHint::CreateReplacement(CaptureListRange, NewCaptureList);
+ }
}
} // namespace clang::tidy::readability
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index f452ea6f6ccb1..6e81a9eb2b3d4 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -6,15 +6,19 @@ void test_default_captures() {
auto lambda1 = [=](int x) { return value + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda1 = [value](int x) { return value + x; };
auto lambda2 = [&](int x) { return value + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda2 = [&value](int x) { return value + x; };
auto lambda3 = [=, &another](int x) { return value + another + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda3 = [&another, value](int x) { return value + another + x; };
auto lambda4 = [&, value](int x) { return value + another + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda4 = [value, &another](int x) { return value + another + x; };
}
void test_acceptable_captures() {
@@ -43,9 +47,11 @@ void test_nested_lambdas() {
auto outer = [=]() {
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto outer = [outer_var, middle_var, inner_var]() {
auto inner = [&](int x) { return outer_var + middle_var + inner_var + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto inner = [&outer_var, &middle_var, &inner_var](int x) { return outer_var + middle_var + inner_var + x; };
return inner(10);
};
@@ -56,14 +62,17 @@ void test_lambda_returns() {
auto create_adder = [=](int x) {
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto create_adder = [](int x) {
return [x](int y) { return x + y; }; // Inner lambda is fine - explicit capture
};
auto func1 = [&]() { return a; };
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto func1 = [&a]() { return a; };
auto func2 = [=]() { return b; };
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto func2 = [b]() { return b; };
}
class TestClass {
@@ -75,9 +84,11 @@ class TestClass {
auto lambda1 = [=]() { return member + local; };
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda1 = [this, local]() { return member + local; };
auto lambda2 = [&]() { return member + local; };
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda2 = [this, &local]() { return member + local; };
auto lambda3 = [this, local]() { return member + local; };
auto lambda4 = [this, &local]() { return member + local; };
@@ -90,6 +101,7 @@ void test_template_lambdas() {
auto lambda = [=](T x) { return value + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: auto lambda = [](T x) { return value + x; };
}
void instantiate_templates() {
>From 33af58bf9f683c21f0263f2d1e9b8810c76d30f3 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 10:37:55 -0400
Subject: [PATCH 24/42] attempt to fix to use something that isn't weird string
manipulation
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 39 +++++++++++++++++--
1 file changed, 36 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 361af6794a9b5..b64717534acd6 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -20,6 +20,29 @@ AST_MATCHER(LambdaExpr, hasDefaultCapture) {
return Node.getCaptureDefault() != LCD_None;
}
+std::string getExplicitCaptureText(const LambdaCapture &Capture,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (!Capture.isExplicit() || !Capture.getLocation().isValid()) {
+ return "";
+ }
+
+ // For explicit captures, extract the actual source text to preserve
+ // original formatting, spacing, and comments
+ SourceLocation CaptureBegin = Capture.getLocation();
+
+ // Find the end of this capture by looking for the next comma or closing
+ // bracket This is a simplified approach - a more robust implementation would
+ // parse tokens
+ SourceLocation CaptureEnd =
+ Lexer::getLocForEndOfToken(CaptureBegin, 0, SM, LangOpts);
+
+ // For now, we'll still reconstruct to ensure correctness
+ // but this framework allows for future enhancement to preserve exact source
+ // text
+ return "";
+}
+
std::string getCaptureString(const LambdaCapture &Capture) {
if (Capture.capturesThis()) {
return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
@@ -38,12 +61,21 @@ std::string getCaptureString(const LambdaCapture &Capture) {
return "/* VLA capture */";
}
-std::string buildExplicitCaptureList(const LambdaExpr *Lambda) {
+std::string buildExplicitCaptureList(const LambdaExpr *Lambda,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
std::vector<std::string> CaptureStrings;
// Add explicit captures first (preserve their order and syntax)
for (const auto &Capture : Lambda->explicit_captures()) {
- CaptureStrings.push_back(getCaptureString(Capture));
+ // Try to get the original source text first
+ std::string ExplicitText = getExplicitCaptureText(Capture, SM, LangOpts);
+ if (!ExplicitText.empty()) {
+ CaptureStrings.push_back(ExplicitText);
+ } else {
+ // Fall back to reconstructed text
+ CaptureStrings.push_back(getCaptureString(Capture));
+ }
}
// Add implicit captures (convert to explicit syntax)
@@ -75,7 +107,8 @@ void AvoidDefaultLambdaCaptureCheck::check(
return;
// Build the replacement capture list
- std::string NewCaptureList = buildExplicitCaptureList(Lambda);
+ std::string NewCaptureList = buildExplicitCaptureList(
+ Lambda, *Result.SourceManager, Result.Context->getLangOpts());
// Get the range of the entire capture list [...]
SourceRange CaptureListRange = getCaptureListRange(Lambda);
>From 51aa1820f59dbd749807900ea53eb3710477d9ab Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 12:58:51 -0400
Subject: [PATCH 25/42] more vibe coding with human intervention
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 156 +++++++++++-------
1 file changed, 99 insertions(+), 57 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index b64717534acd6..72294cc164360 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -20,30 +20,47 @@ AST_MATCHER(LambdaExpr, hasDefaultCapture) {
return Node.getCaptureDefault() != LCD_None;
}
-std::string getExplicitCaptureText(const LambdaCapture &Capture,
+// Find the source range of the default capture token (= or &)
+SourceRange getDefaultCaptureRange(const LambdaExpr *Lambda,
const SourceManager &SM,
const LangOptions &LangOpts) {
- if (!Capture.isExplicit() || !Capture.getLocation().isValid()) {
- return "";
- }
+ SourceLocation DefaultLoc = Lambda->getCaptureDefaultLoc();
+ if (DefaultLoc.isInvalid())
+ return SourceRange();
+
+ // The default capture is a single token
+ SourceLocation EndLoc =
+ Lexer::getLocForEndOfToken(DefaultLoc, 0, SM, LangOpts);
+ return SourceRange(DefaultLoc, EndLoc);
+}
- // For explicit captures, extract the actual source text to preserve
- // original formatting, spacing, and comments
- SourceLocation CaptureBegin = Capture.getLocation();
+// Find where to insert implicit captures
+SourceLocation getImplicitCaptureInsertionLoc(const LambdaExpr *Lambda,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ // If there are explicit captures, insert after the last one
+ if (Lambda->explicit_capture_begin() != Lambda->explicit_capture_end()) {
+ // Find the location after the last explicit capture
+ const auto *LastExplicit = Lambda->explicit_capture_end() - 1;
+ SourceLocation LastLoc = LastExplicit->getLocation();
+ if (LastLoc.isValid()) {
+ return Lexer::getLocForEndOfToken(LastLoc, 0, SM, LangOpts);
+ }
+ }
- // Find the end of this capture by looking for the next comma or closing
- // bracket This is a simplified approach - a more robust implementation would
- // parse tokens
- SourceLocation CaptureEnd =
- Lexer::getLocForEndOfToken(CaptureBegin, 0, SM, LangOpts);
+ // If no explicit captures, insert after the default capture
+ SourceLocation DefaultLoc = Lambda->getCaptureDefaultLoc();
+ if (DefaultLoc.isValid()) {
+ return Lexer::getLocForEndOfToken(DefaultLoc, 0, SM, LangOpts);
+ }
- // For now, we'll still reconstruct to ensure correctness
- // but this framework allows for future enhancement to preserve exact source
- // text
- return "";
+ // Fallback: insert at the beginning of the capture list
+ return Lambda->getIntroducerRange().getBegin().getLocWithOffset(1);
}
-std::string getCaptureString(const LambdaCapture &Capture) {
+// Generate the text for an implicit capture
+std::optional<std::string>
+generateImplicitCaptureText(const LambdaCapture &Capture) {
if (Capture.capturesThis()) {
return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
}
@@ -57,38 +74,15 @@ std::string getCaptureString(const LambdaCapture &Capture) {
return Result;
}
- // Handle VLA captures - these are rare but possible
- return "/* VLA capture */";
-}
-
-std::string buildExplicitCaptureList(const LambdaExpr *Lambda,
- const SourceManager &SM,
- const LangOptions &LangOpts) {
- std::vector<std::string> CaptureStrings;
-
- // Add explicit captures first (preserve their order and syntax)
- for (const auto &Capture : Lambda->explicit_captures()) {
- // Try to get the original source text first
- std::string ExplicitText = getExplicitCaptureText(Capture, SM, LangOpts);
- if (!ExplicitText.empty()) {
- CaptureStrings.push_back(ExplicitText);
- } else {
- // Fall back to reconstructed text
- CaptureStrings.push_back(getCaptureString(Capture));
- }
- }
-
- // Add implicit captures (convert to explicit syntax)
- for (const auto &Capture : Lambda->implicit_captures()) {
- CaptureStrings.push_back(getCaptureString(Capture));
- }
-
- return "[" + llvm::join(CaptureStrings, ", ") + "]";
+ // TODO: handle VLAs and other weird captures
+ return std::nullopt;
}
-SourceRange getCaptureListRange(const LambdaExpr *Lambda) {
- SourceRange IntroducerRange = Lambda->getIntroducerRange();
- return IntroducerRange;
+// Check if we need a comma before inserting captures
+bool needsCommaBefore(const LambdaExpr *Lambda, SourceLocation InsertLoc,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ // If there are explicit captures, we need a comma
+ return Lambda->explicit_capture_begin() != Lambda->explicit_capture_end();
}
} // namespace
@@ -102,24 +96,72 @@ void AvoidDefaultLambdaCaptureCheck::check(
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
assert(Lambda);
+ const SourceManager &SM = *Result.SourceManager;
+ const LangOptions &LangOpts = Result.Context->getLangOpts();
+
const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
return;
- // Build the replacement capture list
- std::string NewCaptureList = buildExplicitCaptureList(
- Lambda, *Result.SourceManager, Result.Context->getLangOpts());
-
- // Get the range of the entire capture list [...]
- SourceRange CaptureListRange = getCaptureListRange(Lambda);
-
auto Diag = diag(DefaultCaptureLoc,
"lambda default captures are discouraged; "
"prefer to capture specific variables explicitly");
- // Only provide fixit if we can determine a valid replacement
- if (CaptureListRange.isValid() && !NewCaptureList.empty()) {
- Diag << FixItHint::CreateReplacement(CaptureListRange, NewCaptureList);
+ // Get the range of the default capture token to remove
+ SourceRange DefaultRange = getDefaultCaptureRange(Lambda, SM, LangOpts);
+ if (!DefaultRange.isValid())
+ return;
+
+ // Collect all implicit captures that need to be made explicit
+ std::vector<std::string> ImplicitCaptureTexts;
+ for (const auto &Capture : Lambda->implicit_captures()) {
+ if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
+ ImplicitCaptureTexts.push_back(CaptureText.value());
+ }
+ }
+
+ // If there are no implicit captures, just remove the default capture
+ if (ImplicitCaptureTexts.empty()) {
+ // Also remove any trailing comma if it exists
+ SourceLocation AfterDefault = DefaultRange.getEnd();
+ SourceLocation CommaLoc = Lexer::findLocationAfterToken(
+ AfterDefault, tok::comma, SM, LangOpts, false);
+
+ if (CommaLoc.isValid()) {
+ // Remove default capture and the comma
+ SourceRange RemovalRange(DefaultRange.getBegin(), CommaLoc);
+ Diag << FixItHint::CreateRemoval(RemovalRange);
+ } else {
+ // Just remove the default capture
+ Diag << FixItHint::CreateRemoval(DefaultRange);
+ }
+ return;
+ }
+
+ // Find where to insert the implicit captures
+ SourceLocation InsertLoc =
+ getImplicitCaptureInsertionLoc(Lambda, SM, LangOpts);
+ if (!InsertLoc.isValid())
+ return;
+
+ // Apply the transformations:
+ // 1. Remove the default capture
+ Diag << FixItHint::CreateRemoval(DefaultRange);
+
+ // 2. Insert the explicit captures if any
+ if (!ImplicitCaptureTexts.empty()) {
+ // Build the insertion text for implicit captures
+ std::string InsertionText;
+ bool NeedsComma = needsCommaBefore(Lambda, InsertLoc, SM, LangOpts);
+
+ for (size_t I = 0; I < ImplicitCaptureTexts.size(); ++I) {
+ if (NeedsComma || I > 0) {
+ InsertionText += ", ";
+ }
+ InsertionText += ImplicitCaptureTexts[I];
+ }
+
+ Diag << FixItHint::CreateInsertion(InsertLoc, InsertionText);
}
}
>From dd65b903b04d4bc2cd5587723eb55fc8ea5400e9 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 13:16:40 -0400
Subject: [PATCH 26/42] go back to the hacky string manipulation
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 122 ++++--------------
1 file changed, 26 insertions(+), 96 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 72294cc164360..e0b9a8b44a29f 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -20,45 +20,6 @@ AST_MATCHER(LambdaExpr, hasDefaultCapture) {
return Node.getCaptureDefault() != LCD_None;
}
-// Find the source range of the default capture token (= or &)
-SourceRange getDefaultCaptureRange(const LambdaExpr *Lambda,
- const SourceManager &SM,
- const LangOptions &LangOpts) {
- SourceLocation DefaultLoc = Lambda->getCaptureDefaultLoc();
- if (DefaultLoc.isInvalid())
- return SourceRange();
-
- // The default capture is a single token
- SourceLocation EndLoc =
- Lexer::getLocForEndOfToken(DefaultLoc, 0, SM, LangOpts);
- return SourceRange(DefaultLoc, EndLoc);
-}
-
-// Find where to insert implicit captures
-SourceLocation getImplicitCaptureInsertionLoc(const LambdaExpr *Lambda,
- const SourceManager &SM,
- const LangOptions &LangOpts) {
- // If there are explicit captures, insert after the last one
- if (Lambda->explicit_capture_begin() != Lambda->explicit_capture_end()) {
- // Find the location after the last explicit capture
- const auto *LastExplicit = Lambda->explicit_capture_end() - 1;
- SourceLocation LastLoc = LastExplicit->getLocation();
- if (LastLoc.isValid()) {
- return Lexer::getLocForEndOfToken(LastLoc, 0, SM, LangOpts);
- }
- }
-
- // If no explicit captures, insert after the default capture
- SourceLocation DefaultLoc = Lambda->getCaptureDefaultLoc();
- if (DefaultLoc.isValid()) {
- return Lexer::getLocForEndOfToken(DefaultLoc, 0, SM, LangOpts);
- }
-
- // Fallback: insert at the beginning of the capture list
- return Lambda->getIntroducerRange().getBegin().getLocWithOffset(1);
-}
-
-// Generate the text for an implicit capture
std::optional<std::string>
generateImplicitCaptureText(const LambdaCapture &Capture) {
if (Capture.capturesThis()) {
@@ -74,15 +35,13 @@ generateImplicitCaptureText(const LambdaCapture &Capture) {
return Result;
}
- // TODO: handle VLAs and other weird captures
- return std::nullopt;
-}
+ if (Capture.capturesVLAType()) {
+ // VLA captures are rare and complex - for now we skip them
+ // A full implementation would need to handle the VLA type properly
+ return std::nullopt;
+ }
-// Check if we need a comma before inserting captures
-bool needsCommaBefore(const LambdaExpr *Lambda, SourceLocation InsertLoc,
- const SourceManager &SM, const LangOptions &LangOpts) {
- // If there are explicit captures, we need a comma
- return Lambda->explicit_capture_begin() != Lambda->explicit_capture_end();
+ return std::nullopt;
}
} // namespace
@@ -96,9 +55,6 @@ void AvoidDefaultLambdaCaptureCheck::check(
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
assert(Lambda);
- const SourceManager &SM = *Result.SourceManager;
- const LangOptions &LangOpts = Result.Context->getLangOpts();
-
const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
return;
@@ -107,61 +63,35 @@ void AvoidDefaultLambdaCaptureCheck::check(
"lambda default captures are discouraged; "
"prefer to capture specific variables explicitly");
- // Get the range of the default capture token to remove
- SourceRange DefaultRange = getDefaultCaptureRange(Lambda, SM, LangOpts);
- if (!DefaultRange.isValid())
- return;
+ // Build the complete replacement capture list
+ std::vector<std::string> AllCaptures;
- // Collect all implicit captures that need to be made explicit
- std::vector<std::string> ImplicitCaptureTexts;
- for (const auto &Capture : Lambda->implicit_captures()) {
+ // Add explicit captures first (preserve their order)
+ for (const auto &Capture : Lambda->explicit_captures()) {
if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
- ImplicitCaptureTexts.push_back(CaptureText.value());
+ AllCaptures.push_back(CaptureText.value());
}
}
- // If there are no implicit captures, just remove the default capture
- if (ImplicitCaptureTexts.empty()) {
- // Also remove any trailing comma if it exists
- SourceLocation AfterDefault = DefaultRange.getEnd();
- SourceLocation CommaLoc = Lexer::findLocationAfterToken(
- AfterDefault, tok::comma, SM, LangOpts, false);
-
- if (CommaLoc.isValid()) {
- // Remove default capture and the comma
- SourceRange RemovalRange(DefaultRange.getBegin(), CommaLoc);
- Diag << FixItHint::CreateRemoval(RemovalRange);
- } else {
- // Just remove the default capture
- Diag << FixItHint::CreateRemoval(DefaultRange);
+ // Add implicit captures (convert to explicit)
+ for (const auto &Capture : Lambda->implicit_captures()) {
+ if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
+ AllCaptures.push_back(CaptureText.value());
}
- return;
}
- // Find where to insert the implicit captures
- SourceLocation InsertLoc =
- getImplicitCaptureInsertionLoc(Lambda, SM, LangOpts);
- if (!InsertLoc.isValid())
- return;
-
- // Apply the transformations:
- // 1. Remove the default capture
- Diag << FixItHint::CreateRemoval(DefaultRange);
-
- // 2. Insert the explicit captures if any
- if (!ImplicitCaptureTexts.empty()) {
- // Build the insertion text for implicit captures
- std::string InsertionText;
- bool NeedsComma = needsCommaBefore(Lambda, InsertLoc, SM, LangOpts);
-
- for (size_t I = 0; I < ImplicitCaptureTexts.size(); ++I) {
- if (NeedsComma || I > 0) {
- InsertionText += ", ";
- }
- InsertionText += ImplicitCaptureTexts[I];
- }
+ // Build the final capture list
+ std::string ReplacementText;
+ if (AllCaptures.empty()) {
+ ReplacementText = "[]";
+ } else {
+ ReplacementText = "[" + llvm::join(AllCaptures, ", ") + "]";
+ }
- Diag << FixItHint::CreateInsertion(InsertLoc, InsertionText);
+ // Replace the entire capture list with the explicit version
+ SourceRange IntroducerRange = Lambda->getIntroducerRange();
+ if (IntroducerRange.isValid()) {
+ Diag << FixItHint::CreateReplacement(IntroducerRange, ReplacementText);
}
}
>From a9509f3a37fa9350e1de1ee0223f10dc17ff9a1d Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 13:17:28 -0400
Subject: [PATCH 27/42] remove AI comments
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/AvoidDefaultLambdaCaptureCheck.cpp | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index e0b9a8b44a29f..66d4c9653860a 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -63,24 +63,21 @@ void AvoidDefaultLambdaCaptureCheck::check(
"lambda default captures are discouraged; "
"prefer to capture specific variables explicitly");
- // Build the complete replacement capture list
std::vector<std::string> AllCaptures;
- // Add explicit captures first (preserve their order)
for (const auto &Capture : Lambda->explicit_captures()) {
if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
AllCaptures.push_back(CaptureText.value());
}
}
- // Add implicit captures (convert to explicit)
for (const auto &Capture : Lambda->implicit_captures()) {
if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
AllCaptures.push_back(CaptureText.value());
}
}
- // Build the final capture list
+ // Replace with new capture list
std::string ReplacementText;
if (AllCaptures.empty()) {
ReplacementText = "[]";
@@ -88,7 +85,6 @@ void AvoidDefaultLambdaCaptureCheck::check(
ReplacementText = "[" + llvm::join(AllCaptures, ", ") + "]";
}
- // Replace the entire capture list with the explicit version
SourceRange IntroducerRange = Lambda->getIntroducerRange();
if (IntroducerRange.isValid()) {
Diag << FixItHint::CreateReplacement(IntroducerRange, ReplacementText);
>From e9e0f9ac4d6d272f22a961ab17028849e6a03142 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 14:03:59 -0400
Subject: [PATCH 28/42] fix namespacing issues
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 38 +++++++++----------
clang/include/clang/ASTMatchers/ASTMatchers.h | 14 +++++++
2 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 66d4c9653860a..7dfe07a8cf7b1 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -11,24 +11,18 @@
#include "clang/Basic/Lambda.h"
#include "clang/Lex/Lexer.h"
-using namespace clang::ast_matchers;
-
-namespace clang::tidy::readability {
+using namespace clang::tidy::readability;
namespace {
-AST_MATCHER(LambdaExpr, hasDefaultCapture) {
- return Node.getCaptureDefault() != LCD_None;
-}
-
-std::optional<std::string>
-generateImplicitCaptureText(const LambdaCapture &Capture) {
+static std::optional<std::string>
+generateImplicitCaptureText(const clang::LambdaCapture &Capture) {
if (Capture.capturesThis()) {
- return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
+ return Capture.getCaptureKind() == clang::LCK_StarThis ? "*this" : "this";
}
if (Capture.capturesVariable()) {
std::string Result;
- if (Capture.getCaptureKind() == LCK_ByRef) {
+ if (Capture.getCaptureKind() == clang::LCK_ByRef) {
Result += "&";
}
Result += Capture.getCapturedVar()->getName().str();
@@ -46,16 +40,21 @@ generateImplicitCaptureText(const LambdaCapture &Capture) {
} // namespace
-void AvoidDefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(lambdaExpr(hasDefaultCapture()).bind("lambda"), this);
+void AvoidDefaultLambdaCaptureCheck::registerMatchers(
+ clang::ast_matchers::MatchFinder *Finder) {
+ Finder->addMatcher(
+ clang::ast_matchers::lambdaExpr(clang::ast_matchers::hasDefaultCapture())
+ .bind("lambda"),
+ this);
}
void AvoidDefaultLambdaCaptureCheck::check(
- const MatchFinder::MatchResult &Result) {
- const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
+ const clang::ast_matchers::MatchFinder::MatchResult &Result) {
+ const auto *Lambda = Result.Nodes.getNodeAs<clang::LambdaExpr>("lambda");
assert(Lambda);
- const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
+ const clang::SourceLocation DefaultCaptureLoc =
+ Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
return;
@@ -85,10 +84,9 @@ void AvoidDefaultLambdaCaptureCheck::check(
ReplacementText = "[" + llvm::join(AllCaptures, ", ") + "]";
}
- SourceRange IntroducerRange = Lambda->getIntroducerRange();
+ clang::SourceRange IntroducerRange = Lambda->getIntroducerRange();
if (IntroducerRange.isValid()) {
- Diag << FixItHint::CreateReplacement(IntroducerRange, ReplacementText);
+ Diag << clang::FixItHint::CreateReplacement(IntroducerRange,
+ ReplacementText);
}
}
-
-} // namespace clang::tidy::readability
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 492863ddfc4a1..0a0d42ca259b8 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -5096,6 +5096,20 @@ AST_MATCHER_P(LambdaCapture, capturesVar, internal::Matcher<ValueDecl>,
/// matches `[this]() { return cc; }`.
AST_MATCHER(LambdaCapture, capturesThis) { return Node.capturesThis(); }
+/// Matches lambda expressions that have default capture modes.
+///
+/// Given
+/// \code
+/// auto l1 = [=]() {}; // matches
+/// auto l2 = [&]() {}; // matches
+/// auto l3 = []() {}; // does not match
+/// \endcode
+/// lambdaExpr(hasDefaultCapture())
+/// matches l1 and l2, but not l3.
+AST_MATCHER(LambdaExpr, hasDefaultCapture) {
+ return Node.getCaptureDefault() != LCD_None;
+}
+
/// Matches a constructor call expression which uses list initialization.
AST_MATCHER(CXXConstructExpr, isListInitialization) {
return Node.isListInitialization();
>From b2c3cc2e8e927a9d10598b2c625324e4df180b69 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Mon, 29 Sep 2025 14:31:34 -0400
Subject: [PATCH 29/42] remove anonymous namespace
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 7dfe07a8cf7b1..e75acb9551701 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -13,7 +13,6 @@
using namespace clang::tidy::readability;
-namespace {
static std::optional<std::string>
generateImplicitCaptureText(const clang::LambdaCapture &Capture) {
if (Capture.capturesThis()) {
@@ -38,8 +37,6 @@ generateImplicitCaptureText(const clang::LambdaCapture &Capture) {
return std::nullopt;
}
-} // namespace
-
void AvoidDefaultLambdaCaptureCheck::registerMatchers(
clang::ast_matchers::MatchFinder *Finder) {
Finder->addMatcher(
>From 969558e11e859721f4f77e1dd23888a7a9eb81f9 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 1 Oct 2025 10:46:54 -0400
Subject: [PATCH 30/42] simplify per code review
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/AvoidDefaultLambdaCaptureCheck.cpp | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index e75acb9551701..eea67233be318 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -14,7 +14,7 @@
using namespace clang::tidy::readability;
static std::optional<std::string>
-generateImplicitCaptureText(const clang::LambdaCapture &Capture) {
+generateCaptureText(const clang::LambdaCapture &Capture) {
if (Capture.capturesThis()) {
return Capture.getCaptureKind() == clang::LCK_StarThis ? "*this" : "this";
}
@@ -61,14 +61,8 @@ void AvoidDefaultLambdaCaptureCheck::check(
std::vector<std::string> AllCaptures;
- for (const auto &Capture : Lambda->explicit_captures()) {
- if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
- AllCaptures.push_back(CaptureText.value());
- }
- }
-
- for (const auto &Capture : Lambda->implicit_captures()) {
- if (const auto CaptureText = generateImplicitCaptureText(Capture)) {
+ for (const auto &Capture : Lambda->captures()) {
+ if (const auto CaptureText = generateCaptureText(Capture)) {
AllCaptures.push_back(CaptureText.value());
}
}
>From 359fd6c01c5f679560620c117b42e11a782b1486 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 1 Oct 2025 10:52:18 -0400
Subject: [PATCH 31/42] Use IIFE so ReplacementText can be const.
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index eea67233be318..ac49de1f9b08e 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -67,15 +67,14 @@ void AvoidDefaultLambdaCaptureCheck::check(
}
}
- // Replace with new capture list
- std::string ReplacementText;
- if (AllCaptures.empty()) {
- ReplacementText = "[]";
- } else {
- ReplacementText = "[" + llvm::join(AllCaptures, ", ") + "]";
- }
+ const auto ReplacementText = [&AllCaptures]() -> std::string {
+ if (AllCaptures.empty()) {
+ return "[]";
+ }
+ return "[" + llvm::join(AllCaptures, ", ") + "]";
+ }();
- clang::SourceRange IntroducerRange = Lambda->getIntroducerRange();
+ const clang::SourceRange IntroducerRange = Lambda->getIntroducerRange();
if (IntroducerRange.isValid()) {
Diag << clang::FixItHint::CreateReplacement(IntroducerRange,
ReplacementText);
>From 4e10e7812ccd40aae112f2a38874ee9dd5c0fab7 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Fri, 3 Oct 2025 12:29:52 -0400
Subject: [PATCH 32/42] simplify code even more
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 45 ++++++-------------
.../avoid-default-lambda-capture.cpp | 27 +++++++++--
2 files changed, 37 insertions(+), 35 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index ac49de1f9b08e..2eacf1ed8935e 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -13,28 +13,17 @@
using namespace clang::tidy::readability;
-static std::optional<std::string>
-generateCaptureText(const clang::LambdaCapture &Capture) {
+static std::string generateCaptureText(const clang::LambdaCapture &Capture) {
if (Capture.capturesThis()) {
return Capture.getCaptureKind() == clang::LCK_StarThis ? "*this" : "this";
}
- if (Capture.capturesVariable()) {
- std::string Result;
- if (Capture.getCaptureKind() == clang::LCK_ByRef) {
- Result += "&";
- }
- Result += Capture.getCapturedVar()->getName().str();
- return Result;
+ std::string Result;
+ if (Capture.getCaptureKind() == clang::LCK_ByRef) {
+ Result += "&";
}
-
- if (Capture.capturesVLAType()) {
- // VLA captures are rare and complex - for now we skip them
- // A full implementation would need to handle the VLA type properly
- return std::nullopt;
- }
-
- return std::nullopt;
+ Result += Capture.getCapturedVar()->getName().str();
+ return Result;
}
void AvoidDefaultLambdaCaptureCheck::registerMatchers(
@@ -59,24 +48,16 @@ void AvoidDefaultLambdaCaptureCheck::check(
"lambda default captures are discouraged; "
"prefer to capture specific variables explicitly");
- std::vector<std::string> AllCaptures;
+ std::vector<std::string> ImplicitCaptures;
- for (const auto &Capture : Lambda->captures()) {
- if (const auto CaptureText = generateCaptureText(Capture)) {
- AllCaptures.push_back(CaptureText.value());
- }
+ for (const auto &Capture : Lambda->implicit_captures()) {
+ ImplicitCaptures.push_back(generateCaptureText(Capture));
}
- const auto ReplacementText = [&AllCaptures]() -> std::string {
- if (AllCaptures.empty()) {
- return "[]";
- }
- return "[" + llvm::join(AllCaptures, ", ") + "]";
+ const auto ReplacementText = [&ImplicitCaptures]() {
+ return llvm::join(ImplicitCaptures, ", ");
}();
- const clang::SourceRange IntroducerRange = Lambda->getIntroducerRange();
- if (IntroducerRange.isValid()) {
- Diag << clang::FixItHint::CreateReplacement(IntroducerRange,
- ReplacementText);
- }
+ Diag << clang::FixItHint::CreateReplacement(Lambda->getCaptureDefaultLoc(),
+ ReplacementText);
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index 6e81a9eb2b3d4..f33f06b656132 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -14,11 +14,11 @@ void test_default_captures() {
auto lambda3 = [=, &another](int x) { return value + another + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
- // CHECK-FIXES: auto lambda3 = [&another, value](int x) { return value + another + x; };
+ // CHECK-FIXES: auto lambda3 = [value, &another](int x) { return value + another + x; };
auto lambda4 = [&, value](int x) { return value + another + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
- // CHECK-FIXES: auto lambda4 = [value, &another](int x) { return value + another + x; };
+ // CHECK-FIXES: auto lambda4 = [&another, value](int x) { return value + another + x; };
}
void test_acceptable_captures() {
@@ -101,10 +101,31 @@ void test_template_lambdas() {
auto lambda = [=](T x) { return value + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
- // CHECK-FIXES: auto lambda = [](T x) { return value + x; };
+ // CHECK-FIXES: auto lambda = [value](T x) { return value + x; };
}
void instantiate_templates() {
test_template_lambdas<int>();
test_template_lambdas<double>();
}
+
+void test_init_captures() {
+ int x = 3;
+ int nx = 5;
+
+ int y1 = [&, z = x + 5]() -> int {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: int y1 = [&nx, z = x + 5]() -> int {
+ return z * z + nx;
+ }();
+
+ int y2 = [=, &ref = x]() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: int y2 = [nx, &ref = x]() {
+ ref += 1;
+ return nx - ref;
+ }();
+
+ (void)y1;
+ (void)y2;
+}
>From 9765c90ba30bc8467169d870fe3139058e4fe5a8 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Fri, 3 Oct 2025 16:53:46 -0400
Subject: [PATCH 33/42] Turns out this check doesn't like templated lambdas
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/AvoidDefaultLambdaCaptureCheck.cpp | 5 +++++
.../checkers/readability/avoid-default-lambda-capture.cpp | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 2eacf1ed8935e..43a6cfe13d45b 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -54,6 +54,11 @@ void AvoidDefaultLambdaCaptureCheck::check(
ImplicitCaptures.push_back(generateCaptureText(Capture));
}
+ // For template-dependent lambdas, the list of captures hasn't been created
+ // yet, so the list of implicit captures is empty.
+ if (ImplicitCaptures.empty() && Lambda->isGenericLambda())
+ return;
+
const auto ReplacementText = [&ImplicitCaptures]() {
return llvm::join(ImplicitCaptures, ", ");
}();
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index f33f06b656132..c6d3de7e481b9 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -95,13 +95,13 @@ class TestClass {
}
};
+// Lambda captures dependent on a template parameter don't have a fix it
template<typename T>
void test_template_lambdas() {
T value{};
auto lambda = [=](T x) { return value + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
- // CHECK-FIXES: auto lambda = [value](T x) { return value + x; };
}
void instantiate_templates() {
>From 33a79a06d56e8f7cd98d7823ca4f651e8d5d0d49 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 8 Oct 2025 11:17:40 -0400
Subject: [PATCH 34/42] ignore VLAs
---
.../readability/AvoidDefaultLambdaCaptureCheck.cpp | 13 ++++++++-----
.../readability/avoid-default-lambda-capture.rst | 4 ++++
.../readability/avoid-default-lambda-capture.cpp | 14 +++++++++++++-
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 43a6cfe13d45b..7e19ec9d98979 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -44,16 +44,19 @@ void AvoidDefaultLambdaCaptureCheck::check(
if (DefaultCaptureLoc.isInvalid())
return;
- auto Diag = diag(DefaultCaptureLoc,
- "lambda default captures are discouraged; "
- "prefer to capture specific variables explicitly");
-
std::vector<std::string> ImplicitCaptures;
-
for (const auto &Capture : Lambda->implicit_captures()) {
+ // It is impossible to explicitly capture a VLA in C++, since VLAs don't
+ // exist in ISO C++ and so the syntax was never created to capture them.
+ if (Capture.getCaptureKind() == LCK_VLAType)
+ return;
ImplicitCaptures.push_back(generateCaptureText(Capture));
}
+ auto Diag = diag(DefaultCaptureLoc,
+ "lambda default captures are discouraged; "
+ "prefer to capture specific variables explicitly");
+
// For template-dependent lambdas, the list of captures hasn't been created
// yet, so the list of implicit captures is empty.
if (ImplicitCaptures.empty() && Lambda->isGenericLambda())
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index d4ecd4c7eb27d..646fbf625361a 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -13,6 +13,10 @@ Coding guidelines that recommend against defaulted lambda captures include:
* Item 31 of Effective Modern C++ by Scott Meyers
+This check does not lint for variable-length array (VLA) captures. VLAs are not
+ISO C++, and it is impossible to explicitly capture them as the syntax does not
+exist.
+
Example
-------
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index c6d3de7e481b9..140e00960e8b2 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy %s readability-avoid-default-lambda-capture %t
+// RUN: %check_clang_tidy %s readability-avoid-default-lambda-capture %t -- -- -Wno-vla-extension
void test_default_captures() {
int value = 42;
@@ -129,3 +129,15 @@ void test_init_captures() {
(void)y1;
(void)y2;
}
+
+void test_vla_no_crash() {
+ // VLAs create implicit VLA bound captures that cannot be written explicitly.
+ // No warning should be issued.
+ int n = 5;
+ int vla[n];
+ for (int i = 0; i < n; ++i) {
+ vla[i] = i * 10;
+ }
+
+ auto lambda = [&]() { return vla[0]; };
+}
>From 8f21201a966ce51ef52486cf1acf9018ef1c71b8 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 8 Oct 2025 11:25:44 -0400
Subject: [PATCH 35/42] move around doc paragraphs
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../checks/readability/avoid-default-lambda-capture.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index 646fbf625361a..b345a566488c6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -9,14 +9,14 @@ Captures can lead to subtle bugs including dangling references and unnecessary
copies. Writing out the name of the variables being captured reminds programmers
and reviewers to know what is being captured. And knowing is half the battle.
+This check does not lint for variable-length array (VLA) captures. VLAs are not
+ISO C++, and it is impossible to explicitly capture them as the syntax for doing
+so does not exist.
+
Coding guidelines that recommend against defaulted lambda captures include:
* Item 31 of Effective Modern C++ by Scott Meyers
-This check does not lint for variable-length array (VLA) captures. VLAs are not
-ISO C++, and it is impossible to explicitly capture them as the syntax does not
-exist.
-
Example
-------
>From e49dd930b55b6d1fc2dbc6a943860fecf11888da Mon Sep 17 00:00:00 2001
From: JJ Marr <jjmarr12 at amd.com>
Date: Thu, 9 Oct 2025 15:11:38 -0400
Subject: [PATCH 36/42] Apply suggestions from code review
Co-authored-by: Congcong Cai <congcongcai0907 at 163.com>
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
.../checks/readability/avoid-default-lambda-capture.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
index b345a566488c6..b4af772b86026 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-default-lambda-capture.rst
@@ -7,9 +7,9 @@ Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``).
Captures can lead to subtle bugs including dangling references and unnecessary
copies. Writing out the name of the variables being captured reminds programmers
-and reviewers to know what is being captured. And knowing is half the battle.
+and reviewers to know what is being captured.
-This check does not lint for variable-length array (VLA) captures. VLAs are not
+This check does not warn on variable-length array (VLA) captures. VLAs are not
ISO C++, and it is impossible to explicitly capture them as the syntax for doing
so does not exist.
>From 840c043c01995f7e6d3b60c9af7f6950ccb3eb84 Mon Sep 17 00:00:00 2001
From: JJ Marr <jjmarr12 at amd.com>
Date: Thu, 9 Oct 2025 15:12:07 -0400
Subject: [PATCH 37/42] Update
clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
Co-authored-by: Baranov Victor <bar.victor.2002 at gmail.com>
---
.../clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 7e19ec9d98979..109f077872502 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -14,9 +14,8 @@
using namespace clang::tidy::readability;
static std::string generateCaptureText(const clang::LambdaCapture &Capture) {
- if (Capture.capturesThis()) {
+ if (Capture.capturesThis())
return Capture.getCaptureKind() == clang::LCK_StarThis ? "*this" : "this";
- }
std::string Result;
if (Capture.getCaptureKind() == clang::LCK_ByRef) {
>From c4870ed91addf4e7f5fa89e09f105e2ff882400a Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 15 Oct 2025 13:27:58 -0400
Subject: [PATCH 38/42] remove namespace qualifiers
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../AvoidDefaultLambdaCaptureCheck.cpp | 34 +++++++++----------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 109f077872502..cc68c0607adb2 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -11,35 +11,33 @@
#include "clang/Basic/Lambda.h"
#include "clang/Lex/Lexer.h"
-using namespace clang::tidy::readability;
+using namespace clang::ast_matchers;
-static std::string generateCaptureText(const clang::LambdaCapture &Capture) {
- if (Capture.capturesThis())
- return Capture.getCaptureKind() == clang::LCK_StarThis ? "*this" : "this";
+namespace clang::tidy::readability {
+
+static std::string generateCaptureText(const LambdaCapture &Capture) {
+ if (Capture.capturesThis()) {
+ return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
+ }
std::string Result;
- if (Capture.getCaptureKind() == clang::LCK_ByRef) {
+ if (Capture.getCaptureKind() == LCK_ByRef) {
Result += "&";
}
Result += Capture.getCapturedVar()->getName().str();
return Result;
}
-void AvoidDefaultLambdaCaptureCheck::registerMatchers(
- clang::ast_matchers::MatchFinder *Finder) {
- Finder->addMatcher(
- clang::ast_matchers::lambdaExpr(clang::ast_matchers::hasDefaultCapture())
- .bind("lambda"),
- this);
+void AvoidDefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(lambdaExpr(hasDefaultCapture()).bind("lambda"), this);
}
void AvoidDefaultLambdaCaptureCheck::check(
- const clang::ast_matchers::MatchFinder::MatchResult &Result) {
- const auto *Lambda = Result.Nodes.getNodeAs<clang::LambdaExpr>("lambda");
+ const MatchFinder::MatchResult &Result) {
+ const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
assert(Lambda);
- const clang::SourceLocation DefaultCaptureLoc =
- Lambda->getCaptureDefaultLoc();
+ const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
return;
@@ -65,6 +63,8 @@ void AvoidDefaultLambdaCaptureCheck::check(
return llvm::join(ImplicitCaptures, ", ");
}();
- Diag << clang::FixItHint::CreateReplacement(Lambda->getCaptureDefaultLoc(),
- ReplacementText);
+ Diag << FixItHint::CreateReplacement(Lambda->getCaptureDefaultLoc(),
+ ReplacementText);
}
+
+} // namespace clang::tidy::readability
>From 34ab8dd10bc7b76d6b7cebd29a3db79d9a03228c Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 15 Oct 2025 13:30:19 -0400
Subject: [PATCH 39/42] add new test case
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../checkers/readability/avoid-default-lambda-capture.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index 140e00960e8b2..637a4fc549102 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -126,8 +126,16 @@ void test_init_captures() {
return nx - ref;
}();
+ int y3 = [=, &ref = x, z = x + 5]() {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+ // CHECK-FIXES: int y3 = [nx, &ref = x, z = x + 5]() {
+ ref += 2;
+ return nx + z - ref;
+ }();
+
(void)y1;
(void)y2;
+ (void)y3;
}
void test_vla_no_crash() {
>From 48416d14d02eac48763a48c7dbf1a1d47ae8b063 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 15 Oct 2025 13:40:05 -0400
Subject: [PATCH 40/42] explicit types
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/AvoidDefaultLambdaCaptureCheck.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index cc68c0607adb2..92067ca5f535c 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -42,7 +42,7 @@ void AvoidDefaultLambdaCaptureCheck::check(
return;
std::vector<std::string> ImplicitCaptures;
- for (const auto &Capture : Lambda->implicit_captures()) {
+ for (const LambdaCapture &Capture : Lambda->implicit_captures()) {
// It is impossible to explicitly capture a VLA in C++, since VLAs don't
// exist in ISO C++ and so the syntax was never created to capture them.
if (Capture.getCaptureKind() == LCK_VLAType)
@@ -59,9 +59,7 @@ void AvoidDefaultLambdaCaptureCheck::check(
if (ImplicitCaptures.empty() && Lambda->isGenericLambda())
return;
- const auto ReplacementText = [&ImplicitCaptures]() {
- return llvm::join(ImplicitCaptures, ", ");
- }();
+ const std::string ReplacementText = llvm::join(ImplicitCaptures, ", ");
Diag << FixItHint::CreateReplacement(Lambda->getCaptureDefaultLoc(),
ReplacementText);
>From 3ff1c7a19ed1b8a88cc0d6e005867a0667a90f93 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 15 Oct 2025 13:56:57 -0400
Subject: [PATCH 41/42] remove brackets around single statement ifs
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../readability/AvoidDefaultLambdaCaptureCheck.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
index 92067ca5f535c..ae333464f5ab4 100644
--- a/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCaptureCheck.cpp
@@ -16,14 +16,13 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
static std::string generateCaptureText(const LambdaCapture &Capture) {
- if (Capture.capturesThis()) {
+ if (Capture.capturesThis())
return Capture.getCaptureKind() == LCK_StarThis ? "*this" : "this";
- }
std::string Result;
- if (Capture.getCaptureKind() == LCK_ByRef) {
+ if (Capture.getCaptureKind() == LCK_ByRef)
Result += "&";
- }
+
Result += Capture.getCapturedVar()->getName().str();
return Result;
}
>From 6cefb5f9b0e125beec8522939550aee4da2b3233 Mon Sep 17 00:00:00 2001
From: jjmarr-amd <JJ.Marr at amd.com>
Date: Wed, 15 Oct 2025 15:10:52 -0400
Subject: [PATCH 42/42] add init capture test cases
on-behalf-of: @amd <JJ.Marr at amd.com>
---
.../avoid-default-lambda-capture.cpp | 42 +++++++++++++++----
1 file changed, 35 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
index 637a4fc549102..89822440849a7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-default-lambda-capture.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy %s readability-avoid-default-lambda-capture %t -- -- -Wno-vla-extension
+// RUN: %check_clang_tidy %s readability-avoid-default-lambda-capture %t -- -- -Wno-vla-extension -std=c++20
void test_default_captures() {
int value = 42;
@@ -21,6 +21,34 @@ void test_default_captures() {
// CHECK-FIXES: auto lambda4 = [&another, value](int x) { return value + another + x; };
}
+template<typename... Args>
+void test_pack_expansion_captures(Args... args) {
+ int local = 5;
+
+ auto lambda1 = [=]() { return (args + ...); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
+ auto lambda2 = [&]() { return (args + ...); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
+ auto lambda3 = [=]() { return (args + ...) + local; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
+ auto lambda4 = [&]() { return (args + ...) + local; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
+ auto lambda5 = [=, ...copied = args]() { return (copied + ...); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+
+ auto lambda6 = [&, ...refs = args]() { return (refs + ...); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
+}
+
+void instantiate_pack_expansion_tests() {
+ test_pack_expansion_captures(1, 2, 3);
+ test_pack_expansion_captures(1.0, 2.0, 3.0);
+}
+
void test_acceptable_captures() {
int value = 42;
int another = 10;
@@ -52,7 +80,7 @@ void test_nested_lambdas() {
auto inner = [&](int x) { return outer_var + middle_var + inner_var + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
// CHECK-FIXES: auto inner = [&outer_var, &middle_var, &inner_var](int x) { return outer_var + middle_var + inner_var + x; };
-
+
return inner(10);
};
}
@@ -65,7 +93,7 @@ void test_lambda_returns() {
// CHECK-FIXES: auto create_adder = [](int x) {
return [x](int y) { return x + y; }; // Inner lambda is fine - explicit capture
};
-
+
auto func1 = [&]() { return a; };
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
// CHECK-FIXES: auto func1 = [&a]() { return a; };
@@ -77,11 +105,11 @@ void test_lambda_returns() {
class TestClass {
int member = 42;
-
+
public:
void test_member_function_lambdas() {
int local = 10;
-
+
auto lambda1 = [=]() { return member + local; };
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
// CHECK-FIXES: auto lambda1 = [this, local]() { return member + local; };
@@ -89,7 +117,7 @@ class TestClass {
auto lambda2 = [&]() { return member + local; };
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
// CHECK-FIXES: auto lambda2 = [this, &local]() { return member + local; };
-
+
auto lambda3 = [this, local]() { return member + local; };
auto lambda4 = [this, &local]() { return member + local; };
}
@@ -99,7 +127,7 @@ class TestClass {
template<typename T>
void test_template_lambdas() {
T value{};
-
+
auto lambda = [=](T x) { return value + x; };
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: lambda default captures are discouraged; prefer to capture specific variables explicitly [readability-avoid-default-lambda-capture]
}
More information about the cfe-commits
mailing list