diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h @@ -0,0 +1,36 @@ +//===--- AvoidCapturingLambdaCoroutinesCheck.h - clang-tidy -----*- C++ -*-===// +// +// 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_CPPCOREGUIDELINES_AVOIDCAPTURINGLAMBDACOROUTINESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDCAPTURINGLAMBDACOROUTINESCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// The normal usage of captures in lambdas are problematic when the lambda is a +/// coroutine because the captures are destroyed after the first suspension +/// point. Using the captures after this point is a use-after-free issue. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-avoid-capturing-lambda-coroutines.html +class AvoidCapturingLambdaCoroutinesCheck : public ClangTidyCheck { +public: + AvoidCapturingLambdaCoroutinesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDCAPTURINGLAMBDACOROUTINESCHECK_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp @@ -0,0 +1,45 @@ +//===--- AvoidCapturingLambdaCoroutinesCheck.cpp - clang-tidy -------------===// +// +// 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 "AvoidCapturingLambdaCoroutinesCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +void AvoidCapturingLambdaCoroutinesCheck::registerMatchers( + MatchFinder *Finder) { + Finder->addMatcher(lambdaExpr().bind("lambda"), this); +} + +void AvoidCapturingLambdaCoroutinesCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Lambda = Result.Nodes.getNodeAs("lambda"); + if (!Lambda) { + return; + } + + const auto *Body = dyn_cast(Lambda->getBody()); + if (!Body) { + return; + } + + if (Lambda->captures().empty()) { + return; + } + + diag(Lambda->getBeginLoc(), "found capturing coroutine lambda"); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule AvoidCaptureDefaultWhenCapturingThisCheck.cpp + AvoidCapturingLambdaCoroutinesCheck.cpp AvoidConstOrRefDataMembersCheck.cpp AvoidDoWhileCheck.cpp AvoidGotoCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -15,6 +15,7 @@ #include "../modernize/UseOverrideCheck.h" #include "../readability/MagicNumbersCheck.h" #include "AvoidCaptureDefaultWhenCapturingThisCheck.h" +#include "AvoidCapturingLambdaCoroutinesCheck.h" #include "AvoidConstOrRefDataMembersCheck.h" #include "AvoidDoWhileCheck.h" #include "AvoidGotoCheck.h" @@ -50,6 +51,8 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "cppcoreguidelines-avoid-capture-default-when-capturing-this"); + CheckFactories.registerCheck( + "cppcoreguidelines-avoid-capturing-lambda-coroutines"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-c-arrays"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -120,6 +120,13 @@ Checks that all implicit and explicit inline functions in header files are tagged with the ``LIBC_INLINE`` macro. +- New :doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines + ` check. + + Adds check for cpp core guideline: "CP.51: Do not use capturing lambdas that + are coroutines." + + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst @@ -0,0 +1,31 @@ +.. title:: clang-tidy - cppcoreguidelines-avoid-capturing-lambda-coroutines + +cppcoreguidelines-avoid-capturing-lambda-coroutines +=================================================== + +Warns if a capturing lambda is a coroutine. For example: + +.. code-block:: c++ + + int c; + + [c] () -> task { co_return; }; + [&] () -> task { int y = c; co_return; }; + [=] () -> task { int y = c; co_return; }; + + struct s { + void m() { + [this] () -> task { co_return; }; + } + }; + +All of the cases above will trigger the warning. However, implicit captures +do not trigger the warning unless the body of the lambda uses the capture. +For example, the following do not trigger the warning. + +.. code-block:: c++ + + int c; + + [&] () -> task { co_return; }; + [=] () -> task { co_return; }; diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -180,6 +180,7 @@ `concurrency-mt-unsafe `_, `concurrency-thread-canceltype-asynchronous `_, `cppcoreguidelines-avoid-capture-default-when-capturing-this `_, + `cppcoreguidelines-avoid-capturing-lambda-coroutines `_, "Yes" `cppcoreguidelines-avoid-const-or-ref-data-members `_, `cppcoreguidelines-avoid-do-while `_, `cppcoreguidelines-avoid-goto `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp @@ -0,0 +1,35 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s cppcoreguidelines-avoid-capturing-lambda-coroutines %t -- -- \ +// RUN: -isystem %S/readability/Inputs/identifier-naming/system + +#include + +void Caught() { + int v; + + [&] () -> task { int y = v; co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] + [=] () -> task { int y = v; co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] + [v] () -> task { co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] + [&v] () -> task { co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] + [y=v] () -> task { co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] + [y{v}] () -> task { co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] +} + +struct S { + void m() { + [this] () -> task { co_return; }; + // CHECK-MESSAGES: [[@LINE-1]]:9: warning: found capturing coroutine lambda [cppcoreguidelines-avoid-capturing-lambda-coroutines] + } +}; + +void Safe() { + int v; + [] () -> task { co_return; }; + [&] () -> task { co_return; }; + [=] () -> task { co_return; }; +}