diff --git a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt @@ -29,11 +29,17 @@ clangToolingCore ) +# Support plugins. +if(CLANG_PLUGIN_SUPPORT) + set(support_plugins SUPPORT_PLUGINS) +endif() + add_clang_tool(clang-tidy ClangTidyToolMain.cpp - ) -add_dependencies(clang-tidy + + DEPENDS clang-resource-headers + ${support_plugins} ) clang_target_link_libraries(clang-tidy PRIVATE @@ -50,6 +56,9 @@ ${ALL_CLANG_TIDY_CHECKS} ) +if(CLANG_PLUGIN_SUPPORT) + export_executable_symbols_for_plugins(clang-tidy) +endif() install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -20,6 +20,7 @@ #include "../GlobList.h" #include "clang/Tooling/CommonOptionsParser.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/PluginLoader.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" @@ -386,6 +387,12 @@ int clangTidyMain(int argc, const char **argv) { llvm::InitLLVM X(argc, argv); + + // Enable help for -load option, if plugins are enabled. + cl::Option *load_opt = cl::getRegisteredOptions().lookup("load"); + if (load_opt) + load_opt->addCategory(ClangTidyCategory); + llvm::Expected OptionsParser = CommonOptionsParser::create(argc, argv, ClangTidyCategory, cl::ZeroOrMore); 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 @@ -73,6 +73,8 @@ - Added support for `NOLINTBEGIN` ... `NOLINTEND` comments to suppress Clang-Tidy warnings over multiple lines. +- Added support for external plugin checks with `-load`. + New checks ^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/Contributing.rst b/clang-tools-extra/docs/clang-tidy/Contributing.rst --- a/clang-tools-extra/docs/clang-tidy/Contributing.rst +++ b/clang-tools-extra/docs/clang-tidy/Contributing.rst @@ -415,6 +415,21 @@ .. _FileCheck: https://llvm.org/docs/CommandGuide/FileCheck.html .. _test/clang-tidy/google-readability-casting.cpp: https://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp +Out-of-tree check plugins +------------------------- + +If you want to develop this out-of-tree, the steps above are the largely same. +External to the clang-tidy build system, put all of the new code into a single +shared library. Build and link it against llvm, while allowing some symbols to +be undefined during linking, almost exactly as you would define a clang plugin. + +Then we can run it by passing `-load` to `clang-tidy`, in addition to the name +of our new checks. + +.. code-block:: console + + $ clang-tidy --checks=-*,my-explicit-constructor -list-checks -load myplugin.so + Running clang-tidy on LLVM -------------------------- diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -218,6 +218,14 @@ --list-checks - List all enabled checks and exit. Use with -checks=* to list all available checks. + -load= - + Load the dynamic object ``plugin``. This + object should register new static analyzer or clang-tidy passes. Once loaded, the object + will add new command line options to run + various analyses. To see the new complete + list of passes, use the + :option:`--list-checks` and + :option:`-load` options together. -p= - Build path --quiet - Run clang-tidy in quiet mode. This suppresses diff --git a/clang-tools-extra/test/CMakeLists.txt b/clang-tools-extra/test/CMakeLists.txt --- a/clang-tools-extra/test/CMakeLists.txt +++ b/clang-tools-extra/test/CMakeLists.txt @@ -17,6 +17,7 @@ llvm_canonicalize_cmake_booleans( CLANG_TIDY_ENABLE_STATIC_ANALYZER + LLVM_ENABLE_PLUGINS ) configure_lit_site_cfg( @@ -78,6 +79,22 @@ endif() endforeach() +llvm_add_library( + CTTestTidyModule + MODULE clang-tidy/CTTestTidyModule.cpp + PLUGIN_TOOL clang-tidy + DEPENDS clang-tidy-headers) + +if(TARGET CTTestTidyModule) + list(APPEND CLANG_TOOLS_TEST_DEPS CTTestTidyModule) + target_include_directories(CTTestTidyModule PUBLIC BEFORE "${CLANG_TOOLS_SOURCE_DIR}") + if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN)) + set(LLVM_LINK_COMPONENTS + Support + ) + endif() +endif() + add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CLANG_TOOLS_TEST_DEPS} diff --git a/clang-tools-extra/test/clang-tidy/CTTestTidyModule.cpp b/clang-tools-extra/test/clang-tidy/CTTestTidyModule.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/CTTestTidyModule.cpp @@ -0,0 +1,50 @@ +// REQUIRES: plugins +// RUN: clang-tidy -checks='-*,mytest' --list-checks -load %llvmshlibdir/CTTestTidyModule%pluginext | FileCheck %s +// CHECK: Enabled checks: +// CHECK-NEXT: mytest + +#include "clang-tidy/ClangTidy.h" +#include "clang-tidy/ClangTidyCheck.h" +#include "clang-tidy/ClangTidyModule.h" +#include "clang-tidy/ClangTidyModuleRegistry.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang; +using namespace clang::tidy; +using namespace clang::ast_matchers; + +namespace { +class MyTestCheck : public ClangTidyCheck { + +public: + MyTestCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + //void registerMatchers(ast_matchers::MatchFinder *Finder) override; + //void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: +}; + +class CTTestModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck("mytest"); + } +}; +} // namespace + +namespace clang { +namespace tidy { + +// Register the CTTestTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add<::CTTestModule> + X("mytest-module", "Adds my checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the CTTestModule. +volatile int CTTestModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/test/lit.cfg.py b/clang-tools-extra/test/lit.cfg.py --- a/clang-tools-extra/test/lit.cfg.py +++ b/clang-tools-extra/test/lit.cfg.py @@ -149,3 +149,9 @@ "clangd", "benchmarks") config.substitutions.append(('%clangd-benchmark-dir', '%s' % (clangd_benchmarks_dir))) +config.substitutions.append(('%llvmshlibdir', config.clang_libs_dir)) +config.substitutions.append(('%pluginext', config.llvm_plugin_ext)) + +# Plugins (loadable modules) +if config.has_plugins and config.llvm_plugin_ext: + config.available_features.add('plugins') diff --git a/clang-tools-extra/test/lit.site.cfg.py.in b/clang-tools-extra/test/lit.site.cfg.py.in --- a/clang-tools-extra/test/lit.site.cfg.py.in +++ b/clang-tools-extra/test/lit.site.cfg.py.in @@ -4,6 +4,7 @@ config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@" config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@" config.clang_tools_dir = "@CLANG_TOOLS_DIR@" @@ -11,6 +12,7 @@ config.python_executable = "@Python3_EXECUTABLE@" config.target_triple = "@TARGET_TRIPLE@" config.clang_tidy_staticanalyzer = @CLANG_TIDY_ENABLE_STATIC_ANALYZER@ +config.has_plugins = @LLVM_ENABLE_PLUGINS@ # Support substitution of the tools and libs dirs with user parameters. This is # used when we can't determine the tool dir at configuration time.