Skip to content

Commit

Permalink
[analyzer] Allow registering custom statically-linked analyzer checkers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alexfh committed Jun 27, 2018
1 parent afc62b7 commit d00ed8e
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 11 deletions.
15 changes: 15 additions & 0 deletions clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h
Expand Up @@ -17,6 +17,7 @@

#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/LLVM.h"
#include <functional>
#include <memory>

namespace clang {
Expand All @@ -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
Expand Down
Expand Up @@ -11,6 +11,7 @@
#define LLVM_CLANG_STATICANALYZER_FRONTEND_CHECKERREGISTRATION_H

#include "clang/Basic/LLVM.h"
#include <functional>
#include <memory>
#include <string>

Expand All @@ -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

Expand Down
11 changes: 9 additions & 2 deletions clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -293,8 +295,9 @@ class AnalysisConsumer : public AnalysisASTConsumer,

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,
Expand Down Expand Up @@ -385,6 +388,10 @@ class AnalysisConsumer : public AnalysisASTConsumer,
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);
Expand Down
13 changes: 9 additions & 4 deletions clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion 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
//
Expand Down
5 changes: 4 additions & 1 deletion clang/unittests/StaticAnalyzer/CMakeLists.txt
Expand Up @@ -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
)
80 changes: 80 additions & 0 deletions 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");
}

}
}
}

0 comments on commit d00ed8e

Please sign in to comment.