diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7446,6 +7446,8 @@ "duration">; def err_this_capture : Error< "'this' cannot be %select{implicitly |}0captured in this context">; + def note_lambda_this_capture_fixit : Note< + "explicitly capture 'this'">; def err_lambda_capture_anonymous_var : Error< "unnamed variable cannot be implicitly captured in a lambda expression">; def err_lambda_capture_flexarray_type : Error< @@ -7454,6 +7456,10 @@ def err_lambda_impcap : Error< "variable %0 cannot be implicitly captured in a lambda with no " "capture-default specified">; + def note_lambda_variable_capture_fixit : Note< + "capture variable %0 by %select{value|reference}1">; + def note_lambda_default_capture_fixit : Note< + "capture all by %select{value|reference}0">; def note_lambda_decl : Note<"lambda expression begins here">; def err_lambda_unevaluated_operand : Error< "lambda expression in an unevaluated operand">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17406,6 +17406,39 @@ return !Invalid; } +static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI, + VarDecl *Var) { + SmallString<32> FixBuffer; + StringRef Separator = LSI->NumExplicitCaptures > 0 ? ", " : ""; + if (Var->getDeclName().isIdentifier()) { + SourceLocation VarInsertLoc = LSI->IntroducerRange.getEnd(); + FixBuffer.assign({Separator, Var->getName()}); + Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) + << Var << /*value*/ 0 + << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); + FixBuffer.assign({Separator, "&", Var->getName()}); + Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) + << Var << /*reference*/ 1 + << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); + } + + // Default capture mode must be specified first. + SourceLocation DefaultInsertLoc = + LSI->IntroducerRange.getBegin().getLocWithOffset(1); + + // Can't default capture by value if this is captured before c++20. + if (!LSI->isCXXThisCaptured() || Sema.getLangOpts().CPlusPlus20) { + FixBuffer.assign({"=", Separator}); + Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) + << /*value*/ 0 + << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + } + FixBuffer.assign({"&", Separator}); + Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) + << /*reference*/ 1 + << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); +} + bool Sema::tryCaptureVariable( VarDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType, @@ -17495,6 +17528,7 @@ Diag(ExprLoc, diag::err_lambda_impcap) << Var; Diag(Var->getLocation(), diag::note_previous_decl) << Var; Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); + buildLambdaCaptureFixit(*this, LSI, Var); } else diagnoseUncapturableValueReference(*this, ExprLoc, Var, DC); } @@ -17573,9 +17607,11 @@ if (BuildAndDiagnose) { Diag(ExprLoc, diag::err_lambda_impcap) << Var; Diag(Var->getLocation(), diag::note_previous_decl) << Var; - if (cast(CSI)->Lambda) + if (cast(CSI)->Lambda) { Diag(cast(CSI)->Lambda->getBeginLoc(), diag::note_lambda_decl); + buildLambdaCaptureFixit(*this, cast(CSI), Var); + } // FIXME: If we error out because an outer lambda can not implicitly // capture a variable that an inner lambda explicitly captures, we // should have the inner lambda do the explicit capture - because diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1226,6 +1226,14 @@ } } +static void buildLambdaThisCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI) { + SourceLocation DiagLoc = LSI->IntroducerRange.getEnd(); + assert(!LSI->isCXXThisCaptured()); + Sema.Diag(DiagLoc, diag::note_lambda_this_capture_fixit) + << FixItHint::CreateInsertion( + DiagLoc, LSI->NumExplicitCaptures > 0 ? ", this" : "this"); +} + bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt, const bool ByCopy) { @@ -1274,9 +1282,12 @@ LambdaScopeInfo *LSI = dyn_cast(CSI); if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) { // This context can't implicitly capture 'this'; fail out. - if (BuildAndDiagnose) + if (BuildAndDiagnose) { Diag(Loc, diag::err_this_capture) << (Explicit && idx == MaxFunctionScopesIndex); + if (!Explicit) + buildLambdaThisCaptureFixit(*this, LSI); + } return true; } if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || @@ -1296,6 +1307,9 @@ if (BuildAndDiagnose) Diag(Loc, diag::err_this_capture) << (Explicit && idx == MaxFunctionScopesIndex); + + if (!Explicit) + buildLambdaThisCaptureFixit(*this, LSI); return true; } break;