diff --git a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h --- a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -204,16 +204,14 @@ using PackageInfoList = llvm::SmallVector; -private: - template static void initializeManager(CheckerManager &mgr) { + template static void addToCheckerMgr(CheckerManager &mgr) { mgr.registerChecker(); } - template static bool returnTrue(const LangOptions &LO) { + static bool returnTrue(const LangOptions &LO) { return true; } -public: /// Adds a checker to the registry. Use this non-templated overload when your /// checker requires custom initialization. void addChecker(InitializationFunction Fn, ShouldRegisterFunction sfn, @@ -227,9 +225,8 @@ bool IsHidden = false) { // Avoid MSVC's Compiler Error C2276: // http://msdn.microsoft.com/en-us/library/850cstw1(v=VS.80).aspx - addChecker(&CheckerRegistry::initializeManager, - &CheckerRegistry::returnTrue, FullName, Desc, DocsUri, - IsHidden); + addChecker(&CheckerRegistry::addToCheckerMgr, + &CheckerRegistry::returnTrue, FullName, Desc, DocsUri, IsHidden); } /// Makes the checker with the full name \p fullName depends on the checker diff --git a/clang/unittests/StaticAnalyzer/CheckerRegistration.h b/clang/unittests/StaticAnalyzer/CheckerRegistration.h new file mode 100644 --- /dev/null +++ b/clang/unittests/StaticAnalyzer/CheckerRegistration.h @@ -0,0 +1,81 @@ +//===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===// +// +// 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 "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/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include "clang/Tooling/Tooling.h" + +namespace clang { +namespace ento { + +class DiagConsumer : public PathDiagnosticConsumer { + llvm::raw_ostream &Output; + +public: + DiagConsumer(llvm::raw_ostream &Output) : Output(Output) {} + void FlushDiagnosticsImpl(std::vector &Diags, + FilesMade *filesMade) override { + for (const auto *PD : Diags) + Output << PD->getCheckerName() << ":" << PD->getShortDescription() << '\n'; + } + + StringRef getName() const override { return "Test"; } +}; + +using AddCheckerFn = void(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts); + +template +void addChecker(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + Fn1(AnalysisConsumer, AnOpts); + addChecker(AnalysisConsumer, AnOpts); +} + +template +void addChecker(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + Fn1(AnalysisConsumer, AnOpts); +} + +template +class TestAction : public ASTFrontendAction { + llvm::raw_ostream &DiagsOutput; + +public: + TestAction(llvm::raw_ostream &DiagsOutput) : DiagsOutput(DiagsOutput) {} + + std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + std::unique_ptr AnalysisConsumer = + CreateAnalysisConsumer(Compiler); + AnalysisConsumer->AddDiagnosticConsumer(new DiagConsumer(DiagsOutput)); + addChecker(*AnalysisConsumer, *Compiler.getAnalyzerOpts()); + return std::move(AnalysisConsumer); + } +}; + +template +bool runCheckerOnCode(const std::string &Code, std::string &Diags) { + llvm::raw_string_ostream OS(Diags); + return tooling::runToolOnCode(std::make_unique>(OS), Code); +} + +template +bool runCheckerOnCode(const std::string &Code) { + std::string Diags; + return runCheckerOnCode(Code, Diags); +} + +} // namespace ento +} // namespace clang diff --git a/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp b/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp --- a/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp +++ b/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp @@ -6,11 +6,13 @@ // //===----------------------------------------------------------------------===// +#include "CheckerRegistration.h" #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/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" #include "clang/Tooling/Tooling.h" @@ -20,53 +22,10 @@ namespace ento { namespace { -template -class TestAction : public ASTFrontendAction { - class DiagConsumer : public PathDiagnosticConsumer { - llvm::raw_ostream &Output; - - public: - DiagConsumer(llvm::raw_ostream &Output) : Output(Output) {} - void FlushDiagnosticsImpl(std::vector &Diags, - FilesMade *filesMade) override { - for (const auto *PD : Diags) - Output << PD->getCheckerName() << ":" << PD->getShortDescription(); - } - - StringRef getName() const override { return "Test"; } - }; - - llvm::raw_ostream &DiagsOutput; - -public: - TestAction(llvm::raw_ostream &DiagsOutput) : DiagsOutput(DiagsOutput) {} - - std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler, - StringRef File) override { - std::unique_ptr AnalysisConsumer = - CreateAnalysisConsumer(Compiler); - AnalysisConsumer->AddDiagnosticConsumer(new DiagConsumer(DiagsOutput)); - Compiler.getAnalyzerOpts()->CheckersAndPackages = { - {"custom.CustomChecker", true}}; - AnalysisConsumer->AddCheckerRegistrationFn([](CheckerRegistry &Registry) { - Registry.addChecker("custom.CustomChecker", "Description", ""); - }); - return std::move(AnalysisConsumer); - } -}; - -template -bool runCheckerOnCode(const std::string &Code, std::string &Diags) { - llvm::raw_string_ostream OS(Diags); - return tooling::runToolOnCode(std::make_unique>(OS), - Code); -} -template -bool runCheckerOnCode(const std::string &Code) { - std::string Diags; - return runCheckerOnCode(Code, Diags); -} - +//===----------------------------------------------------------------------===// +// Just a minimal test for how checker registration works with statically +// linked, non TableGen generated checkers. +//===----------------------------------------------------------------------===// class CustomChecker : public Checker { public: @@ -78,12 +37,25 @@ } }; +void addCustomChecker(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages = {{"custom.CustomChecker", true}}; + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker("custom.CustomChecker", "Description", + ""); + }); +} + TEST(RegisterCustomCheckers, RegisterChecker) { std::string Diags; - EXPECT_TRUE(runCheckerOnCode("void f() {;}", Diags)); - EXPECT_EQ(Diags, "custom.CustomChecker:Custom diagnostic description"); + EXPECT_TRUE(runCheckerOnCode("void f() {;}", Diags)); + EXPECT_EQ(Diags, "custom.CustomChecker:Custom diagnostic description\n"); } +//===----------------------------------------------------------------------===// +// Pretty much the same. +//===----------------------------------------------------------------------===// + class LocIncDecChecker : public Checker { public: void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, @@ -95,11 +67,20 @@ } }; +void addLocIncDecChecker(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages = {{"test.LocIncDecChecker", true}}; + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker("test.LocIncDecChecker", "Description", + ""); + }); +} + TEST(RegisterCustomCheckers, CheckLocationIncDec) { EXPECT_TRUE( - runCheckerOnCode("void f() { int *p; (*p)++; }")); + runCheckerOnCode("void f() { int *p; (*p)++; }")); } -} -} -} +} // namespace +} // namespace ento +} // namespace clang