diff options
| author | Alexander Kornienko <alexfh@google.com> | 2018-06-27 14:56:12 +0000 |
|---|---|---|
| committer | Alexander Kornienko <alexfh@google.com> | 2018-06-27 14:56:12 +0000 |
| commit | d00ed8e2c20014044e42fb7b9380dc9e297cd66f (patch) | |
| tree | 8e90ce7b57988384c31b9b20fdf07f3acce3c63c | |
| parent | afc62b7032848e8dfbd30eb2323c7e7d57f981e4 (diff) | |
| download | bcm5719-llvm-d00ed8e2c20014044e42fb7b9380dc9e297cd66f.tar.gz bcm5719-llvm-d00ed8e2c20014044e42fb7b9380dc9e297cd66f.zip | |
[analyzer] Allow registering custom statically-linked analyzer checkers
Summary:
Add an extension point to allow registration of statically-linked Clang Static
Analyzer checkers that are not a part of the Clang tree. This extension point
employs the mechanism used when checkers are registered from dynamically loaded
plugins.
Reviewers: george.karpenkov, NoQ, xazax.hun, dcoughlin
Reviewed By: george.karpenkov
Subscribers: mgorny, mikhail.ramalho, rnkovacs, xazax.hun, szepet, a.sidorin, cfe-commits
Differential Revision: https://reviews.llvm.org/D45718
llvm-svn: 335740
7 files changed, 125 insertions, 11 deletions
diff --git a/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h b/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h index a9dad6c5e9a..59fbbc3ca80 100644 --- a/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h @@ -17,6 +17,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/Basic/LLVM.h" +#include <functional> #include <memory> namespace clang { @@ -29,10 +30,24 @@ class CompilerInstance; namespace ento { class PathDiagnosticConsumer; class CheckerManager; +class CheckerRegistry; class AnalysisASTConsumer : public ASTConsumer { public: virtual void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) = 0; + + /// This method allows registering statically linked custom checkers that are + /// not a part of the Clang tree. It employs the same mechanism that is used + /// by plugins. + /// + /// Example: + /// + /// Consumer->AddCheckerRegistrationFn([] (CheckerRegistry& Registry) { + /// Registry.addChecker<MyCustomChecker>("example.MyCustomChecker", + /// "Description"); + /// }); + virtual void + AddCheckerRegistrationFn(std::function<void(CheckerRegistry &)> Fn) = 0; }; /// CreateAnalysisConsumer - Creates an ASTConsumer to run various code diff --git a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h index 2985b7c117e..216a2359efb 100644 --- a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_STATICANALYZER_FRONTEND_CHECKERREGISTRATION_H #include "clang/Basic/LLVM.h" +#include <functional> #include <memory> #include <string> @@ -21,10 +22,13 @@ namespace clang { namespace ento { class CheckerManager; + class CheckerRegistry; - std::unique_ptr<CheckerManager> - createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, - ArrayRef<std::string> plugins, DiagnosticsEngine &diags); + std::unique_ptr<CheckerManager> createCheckerManager( + AnalyzerOptions &opts, const LangOptions &langOpts, + ArrayRef<std::string> plugins, + ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns, + DiagnosticsEngine &diags); } // end ento namespace diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 81f69436861..44abde5da6d 100644 --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -164,6 +164,8 @@ class AnalysisConsumer : public AnalysisASTConsumer, /// Bug Reporter to use while recursively visiting Decls. BugReporter *RecVisitorBR; + std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns; + public: ASTContext *Ctx; const Preprocessor &PP; @@ -293,8 +295,9 @@ public: void Initialize(ASTContext &Context) override { Ctx = &Context; - checkerMgr = createCheckerManager(*Opts, PP.getLangOpts(), Plugins, - PP.getDiagnostics()); + checkerMgr = + createCheckerManager(*Opts, PP.getLangOpts(), Plugins, + CheckerRegistrationFns, PP.getDiagnostics()); Mgr = llvm::make_unique<AnalysisManager>( *Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers, @@ -385,6 +388,10 @@ public: PathConsumers.push_back(Consumer); } + void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override { + CheckerRegistrationFns.push_back(std::move(Fn)); + } + private: void storeTopLevelDecls(DeclGroupRef DG); std::string getFunctionName(const Decl *D); diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index 6792f89876c..a260c2d85b1 100644 --- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -111,16 +111,21 @@ getCheckerOptList(const AnalyzerOptions &opts) { return checkerOpts; } -std::unique_ptr<CheckerManager> -ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, - ArrayRef<std::string> plugins, - DiagnosticsEngine &diags) { +std::unique_ptr<CheckerManager> ento::createCheckerManager( + AnalyzerOptions &opts, const LangOptions &langOpts, + ArrayRef<std::string> plugins, + ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns, + DiagnosticsEngine &diags) { std::unique_ptr<CheckerManager> checkerMgr( new CheckerManager(langOpts, opts)); SmallVector<CheckerOptInfo, 8> checkerOpts = getCheckerOptList(opts); ClangCheckerRegistry allCheckers(plugins, &diags); + + for (const auto &Fn : checkerRegistrationFns) + Fn(allCheckers); + allCheckers.initializeManager(*checkerMgr, checkerOpts); allCheckers.validateCheckerOptions(opts, diags); checkerMgr->finishedCheckerRegistration(); diff --git a/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp b/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp index cd2289d4f28..3d8332554fb 100644 --- a/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp +++ b/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp @@ -1,4 +1,4 @@ -//===- unittest/Analysis/AnalyzerOptionsTest.cpp - SA Options test --------===// +//===- unittest/StaticAnalyzer/AnalyzerOptionsTest.cpp - SA Options test --===// // // The LLVM Compiler Infrastructure // diff --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt index 4ca0be50e5c..117a5d9ae4c 100644 --- a/clang/unittests/StaticAnalyzer/CMakeLists.txt +++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -4,11 +4,14 @@ set(LLVM_LINK_COMPONENTS add_clang_unittest(StaticAnalysisTests AnalyzerOptionsTest.cpp + RegisterCustomCheckersTest.cpp ) target_link_libraries(StaticAnalysisTests PRIVATE clangBasic clangAnalysis - clangStaticAnalyzerCore + clangStaticAnalyzerCore + clangStaticAnalyzerFrontend + clangTooling ) diff --git a/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp b/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp new file mode 100644 index 00000000000..82887a1daf3 --- /dev/null +++ b/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp @@ -0,0 +1,80 @@ +//===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { +namespace { + +class CustomChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + BR.EmitBasicReport(D, this, "Custom diagnostic", categories::LogicError, + "Custom diagnostic description", + PathDiagnosticLocation(D, Mgr.getSourceManager()), {}); + } +}; + +class TestAction : public ASTFrontendAction { + class DiagConsumer : public PathDiagnosticConsumer { + llvm::raw_ostream &Output; + + public: + DiagConsumer(llvm::raw_ostream &Output) : Output(Output) {} + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) override { + for (const auto *PD : Diags) + Output << PD->getCheckName() << ":" << PD->getShortDescription(); + } + + StringRef getName() const override { return "Test"; } + }; + + llvm::raw_ostream &DiagsOutput; + +public: + TestAction(llvm::raw_ostream &DiagsOutput) : DiagsOutput(DiagsOutput) {} + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + std::unique_ptr<AnalysisASTConsumer> AnalysisConsumer = + CreateAnalysisConsumer(Compiler); + AnalysisConsumer->AddDiagnosticConsumer(new DiagConsumer(DiagsOutput)); + Compiler.getAnalyzerOpts()->CheckersControlList = { + {"custom.CustomChecker", true}}; + AnalysisConsumer->AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker<CustomChecker>("custom.CustomChecker", "Description"); + }); + return AnalysisConsumer; + } +}; + + +TEST(RegisterCustomCheckers, RegisterChecker) { + std::string Diags; + { + llvm::raw_string_ostream OS(Diags); + EXPECT_TRUE(tooling::runToolOnCode(new TestAction(OS), "void f() {;}")); + } + EXPECT_EQ(Diags, "custom.CustomChecker:Custom diagnostic description"); +} + +} +} +} |

