segoon updated this revision to Diff 323008.
segoon added a comment.

-fix typo


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D94622/new/

https://reviews.llvm.org/D94622

Files:
  clang-tools-extra/clang-tidy/concurrency/AsyncNoNewThreadsCheck.cpp
  clang-tools-extra/clang-tidy/concurrency/AsyncNoNewThreadsCheck.h
  clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
  clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/concurrency-async-no-new-threads.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  clang-tools-extra/test/clang-tidy/checkers/concurrency-async-no-new-threads.c
  
clang-tools-extra/test/clang-tidy/checkers/concurrency-async-no-new-threads.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/concurrency-async-no-new-threads.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/concurrency-async-no-new-threads.cpp
@@ -0,0 +1,133 @@
+// RUN: %check_clang_tidy %s concurrency-async-no-new-threads %t -- \
+// RUN: -config='{CheckOptions: [{key: "concurrency-async-no-new-threads.FunctionsExtra", value: "::my::create_thread"},{key: "concurrency-async-no-new-threads.TypesExtra", value: "::my::thread"}]}'
+
+namespace std {
+
+class thread {
+public:
+  void join() {}
+};
+
+class jthread {};
+
+class nothread {};
+
+namespace execution {
+
+class sequenced_policy {};
+
+class parallel_policy {};
+
+class parallel_unsequenced_policy {};
+
+class unsequenced_policy {};
+
+sequenced_policy seq;
+parallel_policy par;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: type 'parallel_policy' creates new threads [concurrency-async-no-new-threads]
+parallel_unsequenced_policy par_unseq;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: type 'parallel_unsequenced_policy' creates new threads [concurrency-async-no-new-threads]
+unsequenced_policy unseq;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: type 'unsequenced_policy' creates new threads [concurrency-async-no-new-threads]
+
+} // namespace execution
+
+void async(int (*)()) {
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+void sort(T1 &&, T2, T3, T4);
+
+} // namespace std
+
+int pthread_create(int *thread, const int *attr,
+                   void *(*start_routine)(void *), void *arg);
+
+int thrd_create(int *thr, int func, void *arg);
+
+int fork();
+
+int vfork();
+
+int clone(int (*fn)(void *), void *child_stack,
+          int flags, void *arg);
+
+long clone3(int *cl_args, int size);
+
+namespace boost {
+
+class thread {};
+
+class scoped_thread {};
+
+void async(int (*)()) {}
+
+namespace compute {
+class device {};
+
+} // namespace compute
+
+} // namespace boost
+
+void test_threads() {
+  std::thread t;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'thread' creates new threads [concurrency-async-no-new-threads]
+
+  std::jthread j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'jthread' creates new threads [concurrency-async-no-new-threads]
+
+  std::execution::parallel_policy pp;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'parallel_policy' creates new threads [concurrency-async-no-new-threads]
+
+  std::execution::sequenced_policy sp;
+
+  std::sort(std::execution::par, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of 'par' creates new threads [concurrency-async-no-new-threads]
+
+  boost::thread bt;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'thread' creates new threads [concurrency-async-no-new-threads]
+  boost::scoped_thread bst;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'scoped_thread' creates new threads [concurrency-async-no-new-threads]
+
+  boost::compute::device bcd;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'device' creates new threads [concurrency-async-no-new-threads]
+
+  std::async(fork);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'async' creates new threads [concurrency-async-no-new-threads]
+
+  boost::async(fork);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'async' creates new threads [concurrency-async-no-new-threads]
+
+  pthread_create(0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'pthread_create' creates new threads [concurrency-async-no-new-threads]
+
+  thrd_create(0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'thrd_create' creates new threads [concurrency-async-no-new-threads]
+
+  fork();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'fork' creates new threads [concurrency-async-no-new-threads]
+
+  vfork();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'vfork' creates new threads [concurrency-async-no-new-threads]
+
+  clone(0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'clone' creates new threads [concurrency-async-no-new-threads]
+
+  clone3(0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'clone3' creates new threads [concurrency-async-no-new-threads]
+}
+
+namespace my {
+
+class thread {};
+void create_thread();
+
+} // namespace my
+
+void test_extra() {
+  my::thread t;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: type 'thread' creates new threads [concurrency-async-no-new-threads]
+
+  my::create_thread();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'create_thread' creates new threads [concurrency-async-no-new-threads]
+}
Index: clang-tools-extra/test/clang-tidy/checkers/concurrency-async-no-new-threads.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/concurrency-async-no-new-threads.c
@@ -0,0 +1,35 @@
+// RUN: %check_clang_tidy %s concurrency-async-no-new-threads %t
+
+int pthread_create(int *thread, const int *attr,
+                   void *(*start_routine)(void *), void *arg);
+
+int thrd_create(int *thr, int func, void *arg);
+
+int fork();
+
+int vfork();
+
+int clone(int (*fn)(void *), void *child_stack,
+          int flags, void *arg);
+
+long clone3(int *cl_args, int size);
+
+void test_threads() {
+  pthread_create(0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'pthread_create' creates new threads [concurrency-async-no-new-threads]
+
+  thrd_create(0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'thrd_create' creates new threads [concurrency-async-no-new-threads]
+
+  fork();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'fork' creates new threads [concurrency-async-no-new-threads]
+
+  vfork();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'vfork' creates new threads [concurrency-async-no-new-threads]
+
+  clone(0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'clone' creates new threads [concurrency-async-no-new-threads]
+
+  clone3(0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'clone3' creates new threads [concurrency-async-no-new-threads]
+}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -139,6 +139,7 @@
    `clang-analyzer-valist.CopyToSelf <clang-analyzer-valist.CopyToSelf.html>`_,
    `clang-analyzer-valist.Uninitialized <clang-analyzer-valist.Uninitialized.html>`_,
    `clang-analyzer-valist.Unterminated <clang-analyzer-valist.Unterminated.html>`_,
+   `concurrency-async-no-new-threads <concurrency-async-no-new-threads.html>`_,
    `concurrency-mt-unsafe <concurrency-mt-unsafe.html>`_,
    `cppcoreguidelines-avoid-goto <cppcoreguidelines-avoid-goto.html>`_,
    `cppcoreguidelines-avoid-non-const-global-variables <cppcoreguidelines-avoid-non-const-global-variables.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/concurrency-async-no-new-threads.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/concurrency-async-no-new-threads.rst
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - concurrency-async-no-new-threads
+
+concurrency-async-no-new-threads
+================================
+
+Finds functions and types that create new system threads. It is not
+a bug per se, but asynchronous code is expected to use only asynchronous
+functions and types. E.g. if the code uses C++ coroutines, it is expected
+that only new coroutines or coroutine-based primitives are created
+instead of heavy system threads.
+
+.. note::
+
+   The check doesn't identify synchronous and asynchronous code. Instead, it
+   assumes that all analyzed code is asynchronous and all blocking calls have to
+   be found. You should split the sync and async code at the filesystem level
+   and enable `concurrency-async-*` checks for files with asynchronous code
+   only.
+
+The check by default searches for types/functions from the following categories:
+
+* C++ std
+* Boost.Thread, part of Boost.Compute
+* C11 threads
+* POSIX threads (pthreads)
+* Linux syscalls
+
+Options
+-------
+
+.. option:: FunctionsExtra
+
+  Specifies additional functions to search for, separated with semicolon.
+  Defaults to empty string (no functions).
+
+.. option:: TypesExtra
+
+  Specifies additional types to search for, separated with semicolon.
+  Defaults to empty string (no types).
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -121,6 +121,11 @@
   Finds structs that are inefficiently packed or aligned, and recommends
   packing and/or aligning of said structs as needed.
 
+- New :doc:`concurrency-async-no-new-threads
+  <clang-tidy/checks/concurrency-async-no-new-threads>` check.
+
+  Finds functions and types that create new system threads.
+
 - New :doc:`cppcoreguidelines-prefer-member-initializer
   <clang-tidy/checks/cppcoreguidelines-prefer-member-initializer>` check.
 
Index: clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
+++ clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "AsyncNoNewThreadsCheck.h"
 #include "MtUnsafeCheck.h"
 
 namespace clang {
@@ -18,6 +19,8 @@
 class ConcurrencyModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AsyncNoNewThreadsCheck>(
+        "concurrency-async-no-new-threads");
     CheckFactories.registerCheck<concurrency::MtUnsafeCheck>(
         "concurrency-mt-unsafe");
   }
Index: clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_library(clangTidyConcurrencyModule
+  AsyncNoNewThreadsCheck.cpp
   ConcurrencyTidyModule.cpp
   MtUnsafeCheck.cpp
 
Index: clang-tools-extra/clang-tidy/concurrency/AsyncNoNewThreadsCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/concurrency/AsyncNoNewThreadsCheck.h
@@ -0,0 +1,38 @@
+//===--- AsyncNoNewThreadsCheck.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_CONCURRENCY_ASYNCNONEWTHREADSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CONCURRENCY_ASYNCNONEWTHREADSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace concurrency {
+
+/// Checks for types/functions that create new system threads
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/concurrency-async-no-new-threads.html
+class AsyncNoNewThreadsCheck : public ClangTidyCheck {
+public:
+  AsyncNoNewThreadsCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const std::string FunctionsExtra;
+  const std::string TypesExtra;
+};
+
+} // namespace concurrency
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CONCURRENCY_ASYNCNONEWTHREADSCHECK_H
Index: clang-tools-extra/clang-tidy/concurrency/AsyncNoNewThreadsCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/concurrency/AsyncNoNewThreadsCheck.cpp
@@ -0,0 +1,139 @@
+//===--- AsyncNoNewThreadsCheck.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 "AsyncNoNewThreadsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+static const char Function[] = "func";
+static const char TypeName[] = "type";
+static const char Identifier[] = "id";
+static const char Name[] = "name";
+
+static const clang::StringRef ThreadFunctions[] = {
+    /* C++ std */
+    "::std::async", //
+
+    /* Boost.Thread */
+    "::boost::async",
+
+    /* POSIX */
+    "::pthread_create", //
+
+    /* С11 */
+    "::thrd_create", //
+
+    /* Linux */
+    "::fork",     //
+    "::vfork",    //
+    "::clone",    //
+    "::__clone2", //
+    "::clone3",   //
+
+    /* WinAPI */
+    "CreateThread",         //
+    "CreateRemoteThread",   //
+    "CreateRemoteThreadEx", //
+    "_beginthread",         //
+    "_beginthreadex",       //
+};
+
+static const clang::StringRef ThreadTypes[] = {
+    /* C++ std */
+    "::std::thread",                                 //
+    "::std::jthread",                                //
+    "::std::execution::parallel_policy",             //
+    "::std::execution::parallel_unsequenced_policy", //
+    "::std::execution::unsequenced_policy",          //
+
+    /* Boost.Thread */
+    "::boost::thread",        //
+    "::boost::thread_group",  //
+    "::boost::scoped_thread", //
+
+    /* Boost.Compute */
+    "::boost::compute::device", //
+};
+
+static const clang::StringRef ThreadIdentifiers[] = {
+    /* C++ std */
+    "::std::execution::par",       //
+    "::std::execution::par_unseq", //
+    "::std::execution::unseq",     //
+};
+
+static std::vector<llvm::StringRef>
+toVector(llvm::ArrayRef<llvm::StringRef> Base, llvm::StringRef Extra) {
+  llvm::SmallVector<llvm::StringRef, 4> Tmp{Base.begin(), Base.end()};
+  if (!Extra.empty()) {
+    Extra.split(Tmp, ";");
+  }
+
+  return {Tmp.begin(), Tmp.end()};
+}
+
+namespace clang {
+namespace tidy {
+namespace concurrency {
+
+AsyncNoNewThreadsCheck::AsyncNoNewThreadsCheck(StringRef Name,
+                                               ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      FunctionsExtra(Options.get("FunctionsExtra", "")),
+      TypesExtra(Options.get("TypesExtra", "")) {}
+
+void AsyncNoNewThreadsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "FunctionsExtra", FunctionsExtra);
+  Options.store(Opts, "TypesExtra", TypesExtra);
+}
+
+void AsyncNoNewThreadsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(
+                          hasAnyName(toVector(ThreadFunctions, FunctionsExtra)))
+                          .bind(Name)))
+          .bind(Function),
+      this);
+
+  Finder->addMatcher(
+      valueDecl(
+          hasType(cxxRecordDecl(hasAnyName(toVector(ThreadTypes, TypesExtra)))
+                      .bind(Name)))
+          .bind(TypeName),
+      this);
+
+  Finder->addMatcher(
+      declRefExpr(
+          hasDeclaration(namedDecl(hasAnyName(ThreadIdentifiers)).bind(Name)))
+          .bind(Identifier),
+      this);
+}
+
+void AsyncNoNewThreadsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *N = Result.Nodes.getNodeAs<NamedDecl>(Name);
+  assert(N);
+
+  if (const auto *CE = Result.Nodes.getNodeAs<CallExpr>(Function)) {
+    diag(CE->getBeginLoc(), "function %0 creates new threads")
+        << N << CE->getSourceRange();
+  } else if (const auto *Decl = Result.Nodes.getNodeAs<ValueDecl>(TypeName)) {
+    diag(Decl->getBeginLoc(), "type %0 creates new threads")
+        << N << Decl->getSourceRange();
+  } else if (const auto *Id = Result.Nodes.getNodeAs<DeclRefExpr>(Identifier)) {
+    diag(Id->getBeginLoc(), "use of %0 creates new threads")
+        << N << Id->getSourceRange();
+  } else {
+    assert(false);
+  }
+}
+
+} // namespace concurrency
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to