Index: clang-tidy/CMakeLists.txt =================================================================== --- clang-tidy/CMakeLists.txt +++ clang-tidy/CMakeLists.txt @@ -2,6 +2,10 @@ Support ) +if(CLANG_PLUGIN_SUPPORT) + set(LLVM_NO_DEAD_STRIP 1) +endif() + add_clang_library(clangTidy ClangTidy.cpp ClangTidyModule.cpp Index: clang-tidy/ClangTidy.h =================================================================== --- clang-tidy/ClangTidy.h +++ clang-tidy/ClangTidy.h @@ -219,6 +219,7 @@ void exportReplacements(const std::vector &Errors, raw_ostream &OS); +void populateStaticAnalyzerCheckerList(StringRef Plugin, const char **Argv); } // end namespace tidy } // end namespace clang Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -18,6 +18,8 @@ #include "ClangTidy.h" #include "ClangTidyDiagnosticConsumer.h" #include "ClangTidyModuleRegistry.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Compilation.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -33,6 +35,7 @@ #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/ReplacementsYaml.h" #include "clang/Tooling/Tooling.h" @@ -55,14 +58,7 @@ namespace { static const char *AnalyzerCheckNamePrefix = "clang-analyzer-"; -static StringRef StaticAnalyzerChecks[] = { -#define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ - FULLNAME, -#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc" -#undef CHECKER -#undef GET_CHECKERS -}; +static std::vector StaticAnalyzerChecks; class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { public: @@ -410,6 +406,36 @@ return Context.getStats(); } +void populateStaticAnalyzerCheckerList(StringRef Plugin, const char **Argv) { + IntrusiveRefCntPtr DiagOpts(new DiagnosticOptions()); + TextDiagnosticPrinter DiagPrinter(llvm::outs(), &*DiagOpts); + DiagnosticsEngine DiagEng( + IntrusiveRefCntPtr(new DiagnosticIDs), &*DiagOpts, + &DiagPrinter, /*ShouldOwn=*/false); + std::unique_ptr ClangDriver( + tooling::newDriver(&DiagEng, Argv[0])); + ClangDriver->setCheckInputsExist(false); + std::vector CommandLine{Argv[0], "-c", "dummy.cpp"}; + if (!Plugin.empty()) { + CommandLine.push_back("-Xclang"); + CommandLine.push_back("-load"); + CommandLine.push_back("-Xclang"); + CommandLine.push_back(Plugin.data()); + } + + std::unique_ptr Comp(ClangDriver->BuildCompilation(CommandLine)); + if (!Comp) + return; + + auto ArgList(getCC1Arguments(&DiagEng, &*Comp)); + if (ArgList == nullptr) + return; + + std::unique_ptr CI( + tooling::newInvocation(&DiagEng, *ArgList)); + StaticAnalyzerChecks = ento::getCheckerList(CI->getFrontendOpts().Plugins); +} + void handleErrors(const std::vector &Errors, bool Fix) { ErrorReporter Reporter(Fix); for (const ClangTidyError &Error : Errors) Index: clang-tidy/tool/CMakeLists.txt =================================================================== --- clang-tidy/tool/CMakeLists.txt +++ clang-tidy/tool/CMakeLists.txt @@ -20,3 +20,6 @@ install(TARGETS clang-tidy RUNTIME DESTINATION bin) +if(CLANG_PLUGIN_SUPPORT) + set_target_properties(clang-tidy PROPERTIES ENABLE_EXPORTS 1) +endif() Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -146,6 +146,12 @@ "code with clang-apply-replacements."), cl::value_desc("filename"), cl::cat(ClangTidyCategory)); +static cl::opt PluginPath( + "plugin-path", + cl::desc("Path of the shared object that contains static\n" + "analyzer checkers."), + cl::value_desc("filename"), cl::cat(ClangTidyCategory)); + namespace clang { namespace tidy { @@ -261,6 +267,7 @@ OverrideOptions); } + static int clangTidyMain(int argc, const char **argv) { CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory); @@ -270,6 +277,7 @@ std::string FileName = OptionsParser.getSourcePathList().front(); ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); + populateStaticAnalyzerCheckerList(PluginPath, argv); std::vector EnabledChecks = getCheckNames(EffectiveOptions); // FIXME: Allow using --list-checks without positional arguments. @@ -294,7 +302,7 @@ llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true); return 1; } - + ProfileData Profile; std::vector Errors; Index: clang-tidy/tool/Makefile =================================================================== --- clang-tidy/tool/Makefile +++ clang-tidy/tool/Makefile @@ -11,9 +11,12 @@ TOOLNAME = clang-tidy -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 +ifeq ($(CLANG_PLUGIN_SUPPORT), 1) +NO_DEAD_STRIP := 1 +else +TOOL_NO_EXPORTS := 1 +endif include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangTidy.a clangTidyLLVMModule.a clangTidyGoogleModule.a \ Index: test/clang-tidy/clang-static-analyzer-plugin.cpp =================================================================== --- test/clang-tidy/clang-static-analyzer-plugin.cpp +++ test/clang-tidy/clang-static-analyzer-plugin.cpp @@ -0,0 +1,12 @@ +// RUN: clang-tidy %s -checks='-*,clang-analyzer-example.MainCallChecker' -- -load %llvmshlibdir/SampleAnalyzerPlugin%pluginext | FileCheck %s +// REQUIRES: plugins, examples + +// Test that the MainCallChecker example analyzer plugin loads and runs. + +int main(); + +void caller() { + main(); + // CHECK: warning: call to main +} +