diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.cpp index 11ef35178765f..1489ca6c442b1 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.cpp @@ -18,6 +18,19 @@ using namespace clang::ast_matchers; namespace clang::tidy::cppcoreguidelines { +AvoidCaptureDefaultWhenCapturingThisCheck:: + AvoidCaptureDefaultWhenCapturingThisCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoreCaptureDefaultByReference( + Options.get("IgnoreCaptureDefaultByReference", false)) {} + +void AvoidCaptureDefaultWhenCapturingThisCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreCaptureDefaultByReference", + IgnoreCaptureDefaultByReference); +} + void AvoidCaptureDefaultWhenCapturingThisCheck::registerMatchers( MatchFinder *Finder) { Finder->addMatcher(lambdaExpr(hasAnyCapture(capturesThis())).bind("lambda"), @@ -74,24 +87,30 @@ static std::string createReplacementText(const LambdaExpr *Lambda) { void AvoidCaptureDefaultWhenCapturingThisCheck::check( const MatchFinder::MatchResult &Result) { - if (const auto *Lambda = Result.Nodes.getNodeAs("lambda")) { - if (Lambda->getCaptureDefault() != LCD_None) { - bool IsThisImplicitlyCaptured = std::any_of( - Lambda->implicit_capture_begin(), Lambda->implicit_capture_end(), - [](const LambdaCapture &Capture) { return Capture.capturesThis(); }); - auto Diag = diag(Lambda->getCaptureDefaultLoc(), - "lambdas that %select{|implicitly }0capture 'this' " - "should not specify a capture default") - << IsThisImplicitlyCaptured; - - std::string ReplacementText = createReplacementText(Lambda); - SourceLocation DefaultCaptureEnd = - findDefaultCaptureEnd(Lambda, *Result.Context); - Diag << FixItHint::CreateReplacement( - CharSourceRange::getCharRange(Lambda->getCaptureDefaultLoc(), - DefaultCaptureEnd), - ReplacementText); - } + const auto *Lambda = Result.Nodes.getNodeAs("lambda"); + if (!Lambda) + return; + + if (IgnoreCaptureDefaultByReference && + Lambda->getCaptureDefault() == LCD_ByRef) + return; + + if (Lambda->getCaptureDefault() != LCD_None) { + bool IsThisImplicitlyCaptured = std::any_of( + Lambda->implicit_capture_begin(), Lambda->implicit_capture_end(), + [](const LambdaCapture &Capture) { return Capture.capturesThis(); }); + auto Diag = diag(Lambda->getCaptureDefaultLoc(), + "lambdas that %select{|implicitly }0capture 'this' " + "should not specify a capture default") + << IsThisImplicitlyCaptured; + + std::string ReplacementText = createReplacementText(Lambda); + SourceLocation DefaultCaptureEnd = + findDefaultCaptureEnd(Lambda, *Result.Context); + Diag << FixItHint::CreateReplacement( + CharSourceRange::getCharRange(Lambda->getCaptureDefaultLoc(), + DefaultCaptureEnd), + ReplacementText); } } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.h index ae51fe5a10640..08bd3ebd3c3ec 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCaptureDefaultWhenCapturingThisCheck.h @@ -27,13 +27,16 @@ namespace clang::tidy::cppcoreguidelines { class AvoidCaptureDefaultWhenCapturingThisCheck : public ClangTidyCheck { public: AvoidCaptureDefaultWhenCapturingThisCheck(StringRef Name, - ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + ClangTidyContext *Context); void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus11; } + +private: + bool IgnoreCaptureDefaultByReference; }; } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capture-default-when-capturing-this.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capture-default-when-capturing-this.rst index 9c9702a292938..d22f5b6f9343b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capture-default-when-capturing-this.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capture-default-when-capturing-this.rst @@ -41,3 +41,13 @@ Examples: This check implements `CppCoreGuideline F.54 `_. + + +Options +------- + +.. option:: IgnoreCaptureDefaultByReference + + Do not warn when using capture default by reference. In this case, there is no + confusion as to whether variables are captured by value or reference. + Defaults to `false`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capture-default-when-capturing-this.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capture-default-when-capturing-this.cpp index 8a06bd61607ab..3ae8cc47e0147 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capture-default-when-capturing-this.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capture-default-when-capturing-this.cpp @@ -1,4 +1,7 @@ -// RUN: %check_clang_tidy -std=c++11-or-later %s cppcoreguidelines-avoid-capture-default-when-capturing-this %t +// RUN: %check_clang_tidy -std=c++11-or-later %s cppcoreguidelines-avoid-capture-default-when-capturing-this %t \ +// RUN: -check-suffixes=,DEFAULT +// RUN: %check_clang_tidy -std=c++11-or-later %s cppcoreguidelines-avoid-capture-default-when-capturing-this %t \ +// RUN: -config="{CheckOptions: [{key: cppcoreguidelines-avoid-capture-default-when-capturing-this.IgnoreCaptureDefaultByReference, value: true}]}" struct Obj { void lambdas_that_warn_default_capture_copy() { @@ -55,24 +58,24 @@ struct Obj { int local2{}; auto ref_explicit_this_capture = [&, this]() { }; - // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: lambdas that capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] - // CHECK-FIXES: auto ref_explicit_this_capture = [this]() { }; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:39: warning: lambdas that capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] + // CHECK-FIXES-DEFAULT: auto ref_explicit_this_capture = [this]() { }; auto ref_explicit_this_capture_local = [&, this]() { return (local+x) > 10; }; - // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: lambdas that capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] - // CHECK-FIXES: auto ref_explicit_this_capture_local = [&local, this]() { return (local+x) > 10; }; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:45: warning: lambdas that capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] + // CHECK-FIXES-DEFAULT: auto ref_explicit_this_capture_local = [&local, this]() { return (local+x) > 10; }; auto ref_implicit_this_capture = [&]() { return x > 10; }; - // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: lambdas that implicitly capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] - // CHECK-FIXES: auto ref_implicit_this_capture = [this]() { return x > 10; }; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:39: warning: lambdas that implicitly capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] + // CHECK-FIXES-DEFAULT: auto ref_implicit_this_capture = [this]() { return x > 10; }; auto ref_implicit_this_capture_local = [&]() { return (local+x) > 10; }; - // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: lambdas that implicitly capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] - // CHECK-FIXES: auto ref_implicit_this_capture_local = [&local, this]() { return (local+x) > 10; }; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:45: warning: lambdas that implicitly capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] + // CHECK-FIXES-DEFAULT: auto ref_implicit_this_capture_local = [&local, this]() { return (local+x) > 10; }; auto ref_implicit_this_capture_locals = [&]() { return (local+local2+x) > 10; }; - // CHECK-MESSAGES: :[[@LINE-1]]:46: warning: lambdas that implicitly capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] - // CHECK-FIXES: auto ref_implicit_this_capture_locals = [&local, &local2, this]() { return (local+local2+x) > 10; }; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:46: warning: lambdas that implicitly capture 'this' should not specify a capture default [cppcoreguidelines-avoid-capture-default-when-capturing-this] + // CHECK-FIXES-DEFAULT: auto ref_implicit_this_capture_locals = [&local, &local2, this]() { return (local+local2+x) > 10; }; } void lambdas_that_dont_warn() {