llvm-project/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
Corentin Jabot adff142dc2 [clang] Implement Change scope of lambda trailing-return-type
Implement P2036R3.

Captured variables by copy (explicitely or not), are deduced
correctly at the point we know whether the lambda is mutable,
and ill-formed before that.

Up until now, the entire lambda declaration up to the start of the body would be parsed in the parent scope, such that capture would not be available to look up.

The scoping is changed to have an outer lambda scope, followed by the lambda prototype and body.

The lambda scope is necessary because there may be a template scope between the start of the lambda (to which we want to attach the captured variable) and the prototype scope.

We also need to introduce a declaration context to attach the captured variable to (and several parts of clang assume captures are handled from the call operator context), before we know the type of the call operator.

The order of operations is as follow:

* Parse the init capture in the lambda's parent scope

* Introduce a lambda scope

* Create the lambda class and call operator

* Add the init captures to the call operator context and the lambda scope. But the variables are not capured yet (because we don't know their type).
Instead, explicit  captures are stored in a temporary map that conserves the order of capture (for the purpose of having a stable order in the ast dumps).

* A flag is set on LambdaScopeInfo to indicate that we have not yet injected the captures.

* The parameters are parsed (in the parent context, as lambda mangling recurses in the parent context, we couldn't mangle a lambda that is attached to the context of a lambda whose type is not yet known).

* The lambda qualifiers are parsed, at this point We can switch (for the second time) inside the lambda context, unset the flag indicating that we have not parsed the lambda qualifiers,
record the lambda is mutable and capture the explicit variables.

* We can parse the rest of the lambda type, transform the lambda and call operator's types and also transform the call operator to a template function decl where necessary.

At this point, both captures and parameters can be injected in the body's scope. When trying to capture an implicit variable, if we are before the qualifiers of a lambda, we need to remember that the variables are still in the parent's context (rather than in the call operator's).

Reviewed By: aaron.ballman, #clang-language-wg, ChuanqiXu

Differential Revision: https://reviews.llvm.org/D119136
2022-04-13 20:00:03 +02:00

149 lines
6.1 KiB
C++

// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -Wshadow -D AVOID %s
// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -Wshadow -Wshadow-uncaptured-local %s
// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -Wshadow-all %s
// RUN: %clang_cc1 -std=c++17 -verify -fsyntax-only -Wshadow-all %s
void foo(int param) { // expected-note 1+ {{previous declaration is here}}
int var = 0; // expected-note 1+ {{previous declaration is here}}
// Avoid warnings for variables that aren't implicitly captured.
{
#ifdef AVOID
auto f1 = [=] { int var = 1; }; // no warning
auto f2 = [&] { int var = 2; }; // no warning
auto f3 = [=] (int param) { ; }; // no warning
auto f4 = [&] (int param) { ; }; // no warning
#else
auto f1 = [=] { int var = 1; }; // expected-warning {{declaration shadows a local variable}}
auto f2 = [&] { int var = 2; }; // expected-warning {{declaration shadows a local variable}}
auto f3 = [=] (int param) { ; }; // expected-warning {{declaration shadows a local variable}}
auto f4 = [&] (int param) { ; }; // expected-warning {{declaration shadows a local variable}}
#endif
}
// Warn for variables that are implicitly captured.
{
auto f1 = [=] () {
{
int var = 1; // expected-warning {{declaration shadows a local variable}}
}
int x = var; // expected-note {{variable 'var' is captured here}}
};
auto f2 = [&]
#ifdef AVOID
(int param) {
#else
(int param) { // expected-warning {{declaration shadows a local variable}}
#endif
int x = var; // expected-note {{variable 'var' is captured here}}
int var = param; // expected-warning {{declaration shadows a local variable}}
};
}
// Warn for variables that are explicitly captured when a lambda has a default
// capture specifier.
{
auto f1 = [=, &var] () { // expected-note {{variable 'var' is captured here}}
int x = param; // expected-note {{variable 'param' is captured here}}
int var = 0; // expected-warning {{declaration shadows a local variable}}
int param = 0; // expected-warning {{declaration shadows a local variable}}
};
}
// Warn normally inside of lambdas.
auto l1 = [] { // expected-note {{previous declaration is here}}
int x = 1; // expected-note {{previous declaration is here}}
{ int x = 2; } // expected-warning {{declaration shadows a local variable}}
};
auto l2 = [] (int x) { // expected-note {{previous declaration is here}}
{ int x = 1; } // expected-warning {{declaration shadows a local variable}}
};
// Avoid warnings for variables that aren't explicitly captured.
{
#ifdef AVOID
auto f1 = [] { int var = 1; }; // no warning
auto f2 = [] (int param) { ; }; // no warning
auto f3 = [param] () { int var = 1; }; // no warning
auto f4 = [var] (int param) { ; }; // no warning
#else
auto f1 = [] { int var = 1; }; // expected-warning {{declaration shadows a local variable}}
auto f2 = [] (int param) { ; }; // expected-warning {{declaration shadows a local variable}}
auto f3 = [param] () { int var = 1; }; // expected-warning {{declaration shadows a local variable}}
auto f4 = [var] (int param) { ; }; // expected-warning {{declaration shadows a local variable}}
#endif
};
// Warn for variables that are explicitly captured.
{
auto f1 = [var] () { // expected-note {{variable 'var' is explicitly captured here}}
int var = 1; // expected-warning {{declaration shadows a local variable}}
};
auto f2 = [param] // expected-note {{variable 'param' is explicitly captured here}}
(int param) { ; }; // expected-error {{a lambda parameter cannot shadow an explicitly captured entity}}
}
// Warn for variables defined in the capture list.
auto l3 = [z = var] { // expected-note {{previous declaration is here}}
#ifdef AVOID
int var = 1; // no warning
#else
int var = 1; // expected-warning {{declaration shadows a local variable}}
#endif
{ int z = 1; } // expected-warning {{declaration shadows a local variable}}
};
#ifdef AVOID
auto l4 = [var = param] (int param) { ; }; // no warning
#else
auto l4 = [var = param](int param) { ; }; // expected-warning 2{{declaration shadows a local variable}}
#endif
// Make sure that inner lambdas work as well.
auto l5 = [var, l1] { // expected-note {{variable 'l1' is explicitly captured here}}
auto l1 = [] { // expected-warning {{declaration shadows a local variable}}
#ifdef AVOID
int var = 1; // no warning
#else
int var = 1; // expected-warning {{declaration shadows a local variable}}
#endif
};
#ifdef AVOID
auto f1 = [] { int var = 1; }; // no warning
auto f2 = [=] { int var = 1; }; // no warning
#else
auto f1 = [] { int var = 1; }; // expected-warning {{declaration shadows a local variable}}
auto f2 = [=] { int var = 1; }; // expected-warning {{declaration shadows a local variable}}
#endif
auto f3 = [var] // expected-note {{variable 'var' is explicitly captured here}}
{ int var = 1; }; // expected-warning {{declaration shadows a local variable}}
auto f4 = [&] {
int x = var; // expected-note {{variable 'var' is captured here}}
int var = 2; // expected-warning {{declaration shadows a local variable}}
};
};
auto l6 = [&] {
auto f1 = [param] { // expected-note {{variable 'param' is explicitly captured here}}
int param = 0; // expected-warning {{declaration shadows a local variable}}
};
};
// Generic lambda arguments should work.
#ifdef AVOID
auto g1 = [](auto param) { ; }; // no warning
auto g2 = [=](auto param) { ; }; // no warning
#else
auto g1 = [](auto param) { ; }; // expected-warning {{declaration shadows a local variable}}
auto g2 = [=](auto param) { ; }; // expected-warning {{declaration shadows a local variable}}
#endif
auto g3 = [param] // expected-note {{variable 'param' is explicitly captured here}}
(auto param) { ; }; // expected-error {{a lambda parameter cannot shadow an explicitly captured entity}}
}
void avoidWarningWhenRedefining() {
int a = 1;
auto l = [b = a] { // expected-note {{previous definition is here}}
// Don't warn on redefinitions.
int b = 0; // expected-error {{redefinition of 'b'}}
};
}