[analyzer] Add basic support for pluggable checkers.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@137802 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/examples/analyzer-plugin/CMakeLists.txt b/examples/analyzer-plugin/CMakeLists.txt
new file mode 100644
index 0000000..8654226
--- /dev/null
+++ b/examples/analyzer-plugin/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(MODULE TRUE)
+
+set( LLVM_USED_LIBS
+ clangStaticAnalyzerCore
+ )
+
+set( LLVM_LINK_COMPONENTS support mc)
+
+add_clang_library(SampleAnalyzerPlugin SampleAnalyzerPlugin)
+
+set_target_properties(SampleAnalyzerPlugin
+ PROPERTIES
+ LINKER_LANGUAGE CXX
+ PREFIX "")
diff --git a/examples/analyzer-plugin/MainCallChecker.cpp b/examples/analyzer-plugin/MainCallChecker.cpp
new file mode 100644
index 0000000..bf75389
--- /dev/null
+++ b/examples/analyzer-plugin/MainCallChecker.cpp
@@ -0,0 +1,52 @@
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MainCallChecker : public Checker < check::PreStmt<CallExpr> > {
+ mutable llvm::OwningPtr<BugType> BT;
+
+public:
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void MainCallChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
+ const ProgramState *state = C.getState();
+ const Expr *Callee = CE->getCallee();
+ const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl();
+
+ if (!FD)
+ return;
+
+ // Get the name of the callee.
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II) // if no identifier, not a simple C function
+ return;
+
+ if (II->isStr("main")) {
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+
+ if (!BT)
+ BT.reset(new BuiltinBug("call to main"));
+
+ RangedBugReport *report = new RangedBugReport(*BT, BT->getName(), N);
+ report->addRange(Callee->getSourceRange());
+ C.EmitReport(report);
+ }
+}
+
+// Register plugin!
+extern "C"
+void clang_registerCheckers (CheckerRegistry ®istry) {
+ registry.addChecker<MainCallChecker>("example.MainCallChecker", "Disallows calls to functions called main");
+}
+
+extern "C"
+const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING;
diff --git a/examples/analyzer-plugin/Makefile b/examples/analyzer-plugin/Makefile
new file mode 100644
index 0000000..5537ee0
--- /dev/null
+++ b/examples/analyzer-plugin/Makefile
@@ -0,0 +1,20 @@
+##===- examples/analyzer-plugin/Makefile -------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+LIBRARYNAME = SampleAnalyzerPlugin
+
+LINK_LIBS_IN_SHARED = 0
+SHARED_LIBRARY = 1
+
+include $(CLANG_LEVEL)/Makefile
+
+ifeq ($(OS),Darwin)
+ LDFLAGS=-Wl,-undefined,dynamic_lookup
+endif
diff --git a/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h b/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h
index 76f98a8..b64fbf2 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h
@@ -37,42 +37,3 @@
} // end namespace clang
#endif
-//===--- CheckerOptInfo.h - Specifies which checkers to use -----*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKEROPTINFO_H
-#define LLVM_CLANG_STATICANALYZER_CORE_CHECKEROPTINFO_H
-
-#include "clang/Basic/LLVM.h"
-
-namespace clang {
-namespace ento {
-
-class CheckerOptInfo {
- StringRef Name;
- bool Enable;
- bool Claimed;
-
-public:
- CheckerOptInfo(StringRef name, bool enable)
- : Name(name), Enable(enable), Claimed(false) { }
-
- StringRef getName() const { return Name; }
- bool isEnabled() const { return Enable; }
- bool isDisabled() const { return !isEnabled(); }
-
- bool isClaimed() const { return Claimed; }
- bool isUnclaimed() const { return !isClaimed(); }
- void claim() { Claimed = true; }
-};
-
-} // end namespace ento
-} // end namespace clang
-
-#endif
diff --git a/include/clang/StaticAnalyzer/Core/CheckerRegistry.h b/include/clang/StaticAnalyzer/Core/CheckerRegistry.h
index 16f98cf..8bf0dd7 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerRegistry.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerRegistry.h
@@ -17,6 +17,15 @@
namespace clang {
namespace ento {
+#ifndef CLANG_ANALYZER_API_VERSION_STRING
+// FIXME: The Clang version string is not particularly granular;
+// the analyzer infrastructure can change a lot between releases.
+// Unfortunately, this string has to be statically embedded in each plugin,
+// so we can't just use the functions defined in Version.h.
+#include "clang/Basic/Version.h"
+#define CLANG_ANALYZER_API_VERSION_STRING CLANG_VERSION_STRING
+#endif
+
class CheckerOptInfo;
class CheckerRegistry {
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
index 8ed74a2..922cd84 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -20,20 +20,63 @@
#include "clang/Frontend/AnalyzerOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Basic/Diagnostic.h"
+#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace ento;
+using llvm::sys::DynamicLibrary;
-static void registerCheckers(CheckerRegistry ®istry,
- ArrayRef<std::string> plugins) {
- registerBuiltinCheckers(registry);
+namespace {
+class ClangCheckerRegistry : public CheckerRegistry {
+ typedef void (*RegisterCheckersFn)(CheckerRegistry &);
+public:
+ ClangCheckerRegistry(ArrayRef<std::string> plugins);
- // FIXME: register plugins.
+ static bool isCompatibleAPIVersion(const char *versionString);
+};
+
+} // end anonymous namespace
+
+ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef<std::string> plugins) {
+ registerBuiltinCheckers(*this);
+
+ for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
+ i != e; ++i) {
+ // Get access to the plugin.
+ DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str());
+
+ // See if it's compatible with this build of clang.
+ const char *pluginAPIVersion =
+ (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
+ if (!isCompatibleAPIVersion(pluginAPIVersion))
+ continue;
+
+ // Register its checkers.
+ RegisterCheckersFn registerPluginCheckers =
+ (RegisterCheckersFn) lib.getAddressOfSymbol("clang_registerCheckers");
+ if (registerPluginCheckers)
+ registerPluginCheckers(*this);
+ }
}
+bool ClangCheckerRegistry::isCompatibleAPIVersion(const char *versionString) {
+ // If the version string is null, it's not an analyzer plugin.
+ if (versionString == 0)
+ return false;
+
+ // For now, none of the static analyzer API is considered stable.
+ // Versions must match exactly.
+ if (strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0)
+ return true;
+
+ // FIXME: Should we emit a diagnostic if the version doesn't match?
+ return false;
+}
+
+
CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts,
const LangOptions &langOpts,
ArrayRef<std::string> plugins,
@@ -46,10 +89,7 @@
checkerOpts.push_back(CheckerOptInfo(opt.first.c_str(), opt.second));
}
- CheckerRegistry allCheckers;
- registerCheckers(allCheckers, plugins);
- allCheckers.initializeManager(*checkerMgr, checkerOpts);
-
+ ClangCheckerRegistry(plugins).initializeManager(*checkerMgr, checkerOpts);
checkerMgr->finishedCheckerRegistration();
for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) {
@@ -65,7 +105,5 @@
out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n";
out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n";
- CheckerRegistry allCheckers;
- registerCheckers(allCheckers, plugins);
- allCheckers.printHelp(out);
+ ClangCheckerRegistry(plugins).printHelp(out);
}