Skip to content
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
35df1c6
Bugprone-default-lambda-capture
jjmarr-amd Jul 11, 2025
dbb903f
Merge branch 'main' of github.com:llvm/llvm-project into DefaultLambd…
jjmarr-amd Sep 22, 2025
689f95b
Fix headers
jjmarr-amd Sep 22, 2025
9991b9c
remove superfluous comments.
jjmarr-amd Sep 22, 2025
db17a4c
add new matcher
jjmarr-amd Sep 22, 2025
7dcf216
Replace if with assert
jjmarr-amd Sep 22, 2025
176d195
Remove specific warning type
jjmarr-amd Sep 22, 2025
5f23dff
Sync documentation to release notes
jjmarr-amd Sep 22, 2025
4c41332
Update clang-tools-extra/clang-tidy/bugprone/DefaultLambdaCaptureCheck.h
jjmarr-amd Sep 22, 2025
330dbd8
Remove superfluous include
jjmarr-amd Sep 22, 2025
31366e6
Fix doc and release notes
jjmarr-amd Sep 22, 2025
70df9df
Fix const
jjmarr-amd Sep 22, 2025
0418517
Merge branch 'main' into DefaultLambdaCapture
jjmarr-amd Sep 22, 2025
0bcdbc4
Fix header guard
jjmarr-amd Sep 23, 2025
ac709f4
Fix header spelling
jjmarr-amd Sep 23, 2025
5d4c2ca
Rename check to readability-default-lambda-capture
jjmarr-amd Sep 24, 2025
c0b90d1
Update clang-tools-extra/docs/clang-tidy/checks/readability/default-l…
jjmarr-amd Sep 24, 2025
1172f73
update doc
jjmarr-amd Sep 25, 2025
66e6aaa
rename check to readability-avoid-default-lambda-capture
jjmarr-amd Sep 25, 2025
4bf58b6
use C++ style comments
jjmarr-amd Sep 25, 2025
a807dd0
Update clang-tools-extra/docs/clang-tidy/checks/readability/avoid-def…
jjmarr-amd Sep 25, 2025
8fdc097
fix doc again
jjmarr-amd Sep 25, 2025
ab8f4b9
remove reference to coding guideline
jjmarr-amd Sep 25, 2025
a4348de
Merge branch 'main' of github.com:llvm/llvm-project into DefaultLambd…
jjmarr-amd Sep 29, 2025
13b5049
rephrase
jjmarr-amd Sep 29, 2025
043df7c
commit vibe coded work
jjmarr-amd Sep 29, 2025
33af58b
attempt to fix to use something that isn't weird string manipulation
jjmarr-amd Sep 29, 2025
51aa182
more vibe coding with human intervention
jjmarr-amd Sep 29, 2025
dd65b90
go back to the hacky string manipulation
jjmarr-amd Sep 29, 2025
a9509f3
remove AI comments
jjmarr-amd Sep 29, 2025
e9e0f9a
fix namespacing issues
jjmarr-amd Sep 29, 2025
b2c3cc2
remove anonymous namespace
jjmarr-amd Sep 29, 2025
969558e
simplify per code review
jjmarr-amd Oct 1, 2025
359fd6c
Use IIFE so ReplacementText can be const.
jjmarr-amd Oct 1, 2025
4e10e78
simplify code even more
jjmarr-amd Oct 3, 2025
9765c90
Turns out this check doesn't like templated lambdas
jjmarr-amd Oct 3, 2025
33a79a0
ignore VLAs
jjmarr-amd Oct 8, 2025
8f21201
move around doc paragraphs
jjmarr-amd Oct 8, 2025
e49dd93
Apply suggestions from code review
jjmarr-amd Oct 9, 2025
840c043
Update clang-tools-extra/clang-tidy/readability/AvoidDefaultLambdaCap…
jjmarr-amd Oct 9, 2025
c4870ed
remove namespace qualifiers
jjmarr-amd Oct 15, 2025
34ab8dd
add new test case
jjmarr-amd Oct 15, 2025
48416d1
explicit types
jjmarr-amd Oct 15, 2025
3ff1c7a
remove brackets around single statement ifs
jjmarr-amd Oct 15, 2025
6cefb5f
add init capture test cases
jjmarr-amd Oct 15, 2025
0eadc10
Apply suggestions from code review
jjmarr-amd Oct 15, 2025
8f374ff
alphabetize
jjmarr-amd Oct 15, 2025
31d0b93
alphabetize again since i missed one
jjmarr-amd Oct 15, 2025
597eb4a
gate C++20 features
jjmarr-amd Oct 16, 2025
5966f21
change diagnostic message
jjmarr-amd Nov 18, 2025
5477d2e
change documentation
jjmarr-amd Nov 18, 2025
b0919ac
Merge branch 'main' of github.com:llvm/llvm-project into DefaultLambd…
jjmarr-amd Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// 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 "AvoidDefaultLambdaCaptureCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/Lambda.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

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() == LCK_ByRef)
Result += "&";

Result += Capture.getCapturedVar()->getName().str();
return Result;
}

void AvoidDefaultLambdaCaptureCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(lambdaExpr(hasDefaultCapture()).bind("lambda"), this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider excluding system headers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't clang-tidy have a global option to exclude system headers?

}

void AvoidDefaultLambdaCaptureCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
assert(Lambda);

const SourceLocation DefaultCaptureLoc = Lambda->getCaptureDefaultLoc();
if (DefaultCaptureLoc.isInvalid())
return;

std::vector<std::string> ImplicitCaptures;
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)
return;
ImplicitCaptures.push_back(generateCaptureText(Capture));
}

auto Diag = diag(DefaultCaptureLoc,
"lambda default captures are discouraged; "
"prefer to capture specific variables explicitly");
Copy link
Contributor

@vbvictor vbvictor Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other "avoid-xxx" checks we have different wordings which IMHO is more concise and language-neutral.
So to keep uniform, I'd suggest:
"avoid default lambda captures; capture specific variables explicitly instead"
OR
"avoid default lambda captures; prefer to capture specific variables explicitly instead"
OR
"avoid default lambda captures; prefer to capture specific variables explicitly"
But I think the shorter, the better, so I like the first variant

Copy link
Contributor Author

@jjmarr-amd jjmarr-amd Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other checks state the problem condition, then give the suggestion. e.g. clang-tools-extra/clang-tidy/readability/AmbiguousSmartptrResetCallCheck.cpp

ambiguous call to 'reset()' on a pointee of a smart pointer, prefer more explicit approach

So I'm going to say

lambda uses default capture mode; explicitly capture variables instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good would be list variables that are default captured, at least first one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good would be list variables that are default captured, at least first one

Could be an additional "note" message

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good would be list variables that are default captured, at least first one

Could be an additional "note" message

Is this necessary now that there's a suggested fix that contains all the default captures?

Copy link
Contributor

@vbvictor vbvictor Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess notes could be useful if some 3rd party software can capture warning/note messages from clang-tidy output but cant capture fixits. So in UI you would have more information.

But this feature IMHO not a blocker.


// 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())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this condition is quite right. A "generic lambda" is one with a templated call operator, but the capture lists of those are known (see lines 33-34 of this AST: https://godbolt.org/z/M6nYc8c9s). For example, we can provide a fix-it just fine in the following case (and I think the check already does, although it would be good to add a test):

void f() {
    int a;
    int b;
    auto l = [&]<template T>(T foo) { return a + b; };
    // becomes: [&a, &b]<template T>(T foo) { return a + b; };
}

As far as I can tell, it's only when the lambda's type is dependent that the capture list isn't known and we can't provide a fix-it, so I think the condition should be something like:

Suggested change
if (ImplicitCaptures.empty() && Lambda->isGenericLambda())
if (ImplicitCaptures.empty() && Lambda->getLambdaClass()->isDependentType())

return;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid to fix-it hint in macro.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you elaborate on this comment? I don't really understand it.

Copy link
Contributor

@vbvictor vbvictor Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My best guess is:

// no fixes here
#define MY_LAMBDA() [=]() { std::cout << "implicit_captured" << some_var; };

voif foo() { int some_var; MY_LAMBDA };

voif bar() { int some_var; MY_LAMBDA };

or should worth testing too:

#define CAPTURE_ALL &
[CAPTURE_ALL]() { std::cout << "implicit_captured" << some_var; };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would obviously break the linter and I need to figure out how to handle this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would obviously break the linter and I need to figure out how to handle this case.

You could use IsInMacro on lambda location before generating fix-its.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case we could leave as is:

#define CAPTURE_ALL &
[CAPTURE_ALL]() { std::cout << "implicit_captured" << some_var; };

const std::string ReplacementText = llvm::join(ImplicitCaptures, ", ");

Diag << FixItHint::CreateReplacement(Lambda->getCaptureDefaultLoc(),
ReplacementText);
}

} // namespace clang::tidy::readability
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// 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_READABILITY_AVOIDDEFAULTLAMBDACAPTURECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDDEFAULTLAMBDACAPTURECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// Flags lambdas that use default capture modes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here should be the same as the first section in doc

///
/// 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)
: 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::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDDEFAULTLAMBDACAPTURECHECK_H
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -93,6 +94,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-container-size-empty");
CheckFactories.registerCheck<ConvertMemberFunctionsToStatic>(
"readability-convert-member-functions-to-static");
CheckFactories.registerCheck<AvoidDefaultLambdaCaptureCheck>(
"readability-avoid-default-lambda-capture");
CheckFactories.registerCheck<DeleteNullPointerCheck>(
"readability-delete-null-pointer");
CheckFactories.registerCheck<DuplicateIncludeCheck>(
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 :doc:`readability-redundant-parentheses
<clang-tidy/checks/readability/redundant-parentheses>` check.

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -377,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-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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.. title:: clang-tidy - readability-avoid-default-lambda-capture

readability-avoid-default-lambda-capture
========================================

Warns on default lambda captures (e.g. ``[&](){ ... }``, ``[=](){ ... }``).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mention somewere that check provide autofixes

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.

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.

Coding guidelines that recommend against defaulted lambda captures include:

* Item 31 of Effective Modern C++ by Scott Meyers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing punctuation?


Example
-------

.. code-block:: c++

#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;
}
);
}
}

When ``callbacks`` is executed, ``factoryId`` will dangle. Writing the name of
``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++

#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;
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// RUN: %check_clang_tidy %s readability-avoid-default-lambda-capture %t -- -- -Wno-vla-extension -std=c++20

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-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 = [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 = [&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;

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 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);
};
}

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-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 {
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; };

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; };
}
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These cases are contrived, but could we test that we suggest deleting default captures that don't capture anything?

auto lambda1 = [&]() {};
// CHECK-FIXES: auto lambda1 = []() {};
auto lambda2 = [=]() {};
// CHECK-FIXES: auto lambda2 = []() {};
    
// If I'm right about the isGenericLambda condition being incorrect, I think
// the check currently doesn't provide a fix-it in this case, even though it could:
auto lambda3 = [&](auto) {};
// CHECK-FIXES auto lambda3 = [](auto) {};

// 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]
}

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;
}();

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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine the test is named no_crash because the check used to crash on VLAs, but since that crash was fixed in development (never went out to users), I would prefer to not mention here:

Suggested change
void test_vla_no_crash() {
void test_vla() {

// 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;
}
Comment on lines +177 to +179
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop seems unnecessary; I don't think it exercises any part of the check:

Suggested change
for (int i = 0; i < n; ++i) {
vla[i] = i * 10;
}


auto lambda = [&]() { return vla[0]; };
}
Loading