[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 &registry) {
+  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 &registry,
-                             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);
 }