[clang-tools-extra] [clang-tidy] Add bugprone-default-lambda-capture (PR #160150)
JJ Marr via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 22 10:10:44 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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.
More information about the cfe-commits
mailing list