From: Jordy Rose Date: Wed, 17 Aug 2011 01:30:59 +0000 (+0000) Subject: [analyzer] Add basic support for pluggable checkers. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=77a33a71701b59affb5337d9e2b57d69bc095c7d;p=clang [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 0000000000..865422684c --- /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 0000000000..bf753899c2 --- /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 > { + mutable llvm::OwningPtr 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("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 0000000000..5537ee03d8 --- /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 76f98a8cda..b64fbf2c1f 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h +++ b/include/clang/StaticAnalyzer/Core/CheckerOptInfo.h @@ -37,42 +37,3 @@ public: } // 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 16f98cf7ce..8bf0dd7052 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 8ed74a2267..922cd844bb 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 plugins) { - registerBuiltinCheckers(registry); +namespace { +class ClangCheckerRegistry : public CheckerRegistry { + typedef void (*RegisterCheckersFn)(CheckerRegistry &); +public: + ClangCheckerRegistry(ArrayRef plugins); - // FIXME: register plugins. + static bool isCompatibleAPIVersion(const char *versionString); +}; + +} // end anonymous namespace + +ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef plugins) { + registerBuiltinCheckers(*this); + + for (ArrayRef::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 plugins, @@ -46,10 +89,7 @@ CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts, 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 @@ void ento::printCheckerHelp(raw_ostream &out, ArrayRef plugins) { out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n"; out << "USAGE: -analyzer-checker \n\n"; - CheckerRegistry allCheckers; - registerCheckers(allCheckers, plugins); - allCheckers.printHelp(out); + ClangCheckerRegistry(plugins).printHelp(out); }