//===--- CheckerRegistration.cpp - Registration for the Analyzer Checkers -===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Defines the registration function for the analyzer checkers. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" #include "clang/Basic/Diagnostic.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/StaticAnalyzer/Checkers/ClangCheckers.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include using namespace clang; using namespace ento; using llvm::sys::DynamicLibrary; namespace { class ClangCheckerRegistry : public CheckerRegistry { typedef void (*RegisterCheckersFn)(CheckerRegistry &); static bool isCompatibleAPIVersion(const char *versionString); static void warnIncompatible(DiagnosticsEngine *diags, StringRef pluginPath, const char *pluginAPIVersion); public: ClangCheckerRegistry(ArrayRef plugins, DiagnosticsEngine *diags = nullptr); }; } // end anonymous namespace ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef plugins, DiagnosticsEngine *diags) { registerBuiltinCheckers(*this); for (ArrayRef::iterator i = plugins.begin(), e = plugins.end(); i != e; ++i) { // Get access to the plugin. std::string err; DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err); if (!lib.isValid()) { diags->Report(diag::err_fe_unable_to_load_plugin) << *i << err; continue; } // See if it's compatible with this build of clang. const char *pluginAPIVersion = (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString"); if (!isCompatibleAPIVersion(pluginAPIVersion)) { warnIncompatible(diags, *i, pluginAPIVersion); continue; } // Register its checkers. RegisterCheckersFn registerPluginCheckers = (RegisterCheckersFn) (intptr_t) 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) return false; // For now, none of the static analyzer API is considered stable. // Versions must match exactly. return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; } void ClangCheckerRegistry::warnIncompatible(DiagnosticsEngine *diags, StringRef pluginPath, const char *pluginAPIVersion) { if (!diags) return; if (!pluginAPIVersion) return; diags->Report(diag::warn_incompatible_analyzer_plugin_api) << llvm::sys::path::filename(pluginPath); diags->Report(diag::note_incompatible_analyzer_plugin_api) << CLANG_ANALYZER_API_VERSION_STRING << pluginAPIVersion; } std::unique_ptr ento::createCheckerManager( ASTContext &context, AnalyzerOptions &opts, ArrayRef plugins, ArrayRef> checkerRegistrationFns, DiagnosticsEngine &diags) { auto checkerMgr = llvm::make_unique(context, opts); ClangCheckerRegistry allCheckers(plugins, &diags); for (const auto &Fn : checkerRegistrationFns) Fn(allCheckers); allCheckers.initializeManager(*checkerMgr, opts, diags); allCheckers.validateCheckerOptions(opts, diags); checkerMgr->finishedCheckerRegistration(); return checkerMgr; } void ento::printCheckerHelp(raw_ostream &out, ArrayRef plugins) { out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n"; out << "USAGE: -analyzer-checker \n\n"; ClangCheckerRegistry(plugins).printHelp(out); } void ento::printEnabledCheckerList(raw_ostream &out, ArrayRef plugins, const AnalyzerOptions &opts) { out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; ClangCheckerRegistry(plugins).printList(out, opts); } void ento::printAnalyzerConfigList(raw_ostream &out) { out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n"; out << "USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config " "\n\n"; out << " clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, " "-analyzer-config OPTION2=VALUE, ...\n\n"; out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang" "\n\n"; out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang " "OPTION1=VALUE, -Xclang -analyzer-config -Xclang " "OPTION2=VALUE, ...\n\n"; out << "OPTIONS:\n\n"; using OptionAndDescriptionTy = std::pair; OptionAndDescriptionTy PrintableOptions[] = { #define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ { \ CMDFLAG, \ llvm::Twine(llvm::Twine() + "(" + \ (StringRef(#TYPE) == "StringRef" ? "string" : #TYPE ) + \ ") " DESC \ " (default: " #DEFAULT_VAL ")").str() \ }, #define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ SHALLOW_VAL, DEEP_VAL) \ { \ CMDFLAG, \ llvm::Twine(llvm::Twine() + "(" + \ (StringRef(#TYPE) == "StringRef" ? "string" : #TYPE ) + \ ") " DESC \ " (default: " #SHALLOW_VAL " in shallow mode, " #DEEP_VAL \ " in deep mode)").str() \ }, #include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" #undef ANALYZER_OPTION #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE }; llvm::sort(PrintableOptions, [](const OptionAndDescriptionTy &LHS, const OptionAndDescriptionTy &RHS) { return LHS.first < RHS.first; }); constexpr size_t MinLineWidth = 70; constexpr size_t PadForOpt = 2; constexpr size_t OptionWidth = 30; constexpr size_t PadForDesc = PadForOpt + OptionWidth; static_assert(MinLineWidth > PadForDesc, "MinLineWidth must be greater!"); llvm::formatted_raw_ostream FOut(out); for (const auto &Pair : PrintableOptions) { FOut.PadToColumn(PadForOpt) << Pair.first; // If the buffer's length is greater then PadForDesc, print a newline. if (FOut.getColumn() > PadForDesc) FOut << '\n'; FOut.PadToColumn(PadForDesc); for (char C : Pair.second) { if (FOut.getColumn() > MinLineWidth && C == ' ') { FOut << '\n'; FOut.PadToColumn(PadForDesc); continue; } FOut << C; } FOut << "\n\n"; } }