diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:04:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:04:05 +0000 |
commit | 676fbe8105eeb6ff4bb2ed261cb212fcfdbe7b63 (patch) | |
tree | 02a1ac369cb734d0abfa5000dd86e5b7797e6a74 /lib/StaticAnalyzer/Frontend | |
parent | c7e70c433efc6953dc3888b9fbf9f3512d7da2b0 (diff) |
Diffstat (limited to 'lib/StaticAnalyzer/Frontend')
-rw-r--r-- | lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 156 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp | 196 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp | 247 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelInjector.cpp | 2 |
5 files changed, 349 insertions, 253 deletions
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 44abde5da6d11..d87937d9b63df 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -50,8 +50,6 @@ using namespace ento; #define DEBUG_TYPE "AnalysisConsumer" -static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz(); - STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); STATISTIC(NumFunctionsAnalyzed, "The # of functions and blocks analyzed (as top level " @@ -206,7 +204,7 @@ public: PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)), Plugins(plugins), Injector(injector), CTU(CI) { DigestAnalyzerOptions(); - if (Opts->PrintStats || Opts->shouldSerializeStats()) { + if (Opts->PrintStats || Opts->ShouldSerializeStats) { AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>( "analyzer", "Analyzer timers"); TUTotalTimer = llvm::make_unique<llvm::Timer>( @@ -295,13 +293,12 @@ public: void Initialize(ASTContext &Context) override { Ctx = &Context; - checkerMgr = - createCheckerManager(*Opts, PP.getLangOpts(), Plugins, - CheckerRegistrationFns, PP.getDiagnostics()); + checkerMgr = createCheckerManager( + *Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics()); Mgr = llvm::make_unique<AnalysisManager>( - *Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers, - CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); + *Ctx, PP.getDiagnostics(), PathConsumers, CreateStoreMgr, + CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); } /// Store the top level decls in the set to be processed later on. @@ -334,9 +331,6 @@ public: void RunPathSensitiveChecks(Decl *D, ExprEngine::InliningModes IMode, SetOfConstDecls *VisitedCallees); - void ActionExprEngine(Decl *D, bool ObjCGCEnabled, - ExprEngine::InliningModes IMode, - SetOfConstDecls *VisitedCallees); /// Visitors for the RecursiveASTVisitor. bool shouldWalkTypesOfTypeLocs() const { return false; } @@ -682,7 +676,7 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { // - System headers: don't run any checks. SourceManager &SM = Ctx->getSourceManager(); const Stmt *Body = D->getBody(); - SourceLocation SL = Body ? Body->getLocStart() : D->getLocation(); + SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation(); SL = SM.getExpansionLoc(SL); if (!Opts->AnalyzeAll && !Mgr->isInCodeFile(SL)) { @@ -729,9 +723,9 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, // Path-sensitive checking. //===----------------------------------------------------------------------===// -void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, - ExprEngine::InliningModes IMode, - SetOfConstDecls *VisitedCallees) { +void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, + ExprEngine::InliningModes IMode, + SetOfConstDecls *VisitedCallees) { // Construct the analysis engine. First check if the CFG is valid. // FIXME: Inter-procedural analysis will need to handle invalid CFGs. if (!Mgr->getCFG(D)) @@ -741,23 +735,14 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) return; - ExprEngine Eng(CTU, *Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries, - IMode); - - // Set the graph auditor. - std::unique_ptr<ExplodedNode::Auditor> Auditor; - if (Mgr->options.visualizeExplodedGraphWithUbiGraph) { - Auditor = CreateUbiViz(); - ExplodedNode::SetAuditor(Auditor.get()); - } + ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode); // Execute the worklist algorithm. Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), - Mgr->options.getMaxNodesPerTopLevelFunction()); + Mgr->options.MaxNodesPerTopLevelFunction); - // Release the auditor (if any) so that it doesn't monitor the graph - // created BugReporter. - ExplodedNode::SetAuditor(nullptr); + if (!Mgr->options.DumpExplodedGraphTo.empty()) + Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo); // Visualize the exploded graph. if (Mgr->options.visualizeExplodedGraphWithGraphViz) @@ -767,26 +752,6 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, Eng.getBugReporter().FlushReports(); } -void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, - ExprEngine::InliningModes IMode, - SetOfConstDecls *Visited) { - - switch (Mgr->getLangOpts().getGC()) { - case LangOptions::NonGC: - ActionExprEngine(D, false, IMode, Visited); - break; - - case LangOptions::GCOnly: - ActionExprEngine(D, true, IMode, Visited); - break; - - case LangOptions::HybridGC: - ActionExprEngine(D, false, IMode, Visited); - ActionExprEngine(D, true, IMode, Visited); - break; - } -} - //===----------------------------------------------------------------------===// // AnalysisConsumer creation. //===----------------------------------------------------------------------===// @@ -804,98 +769,3 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) { CI.getFrontendOpts().Plugins, hasModelPath ? new ModelInjector(CI) : nullptr); } - -//===----------------------------------------------------------------------===// -// Ubigraph Visualization. FIXME: Move to separate file. -//===----------------------------------------------------------------------===// - -namespace { - -class UbigraphViz : public ExplodedNode::Auditor { - std::unique_ptr<raw_ostream> Out; - std::string Filename; - unsigned Cntr; - - typedef llvm::DenseMap<void*,unsigned> VMap; - VMap M; - -public: - UbigraphViz(std::unique_ptr<raw_ostream> Out, StringRef Filename); - - ~UbigraphViz() override; - - void AddEdge(ExplodedNode *Src, ExplodedNode *Dst) override; -}; - -} // end anonymous namespace - -static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz() { - SmallString<128> P; - int FD; - llvm::sys::fs::createTemporaryFile("llvm_ubi", "", FD, P); - llvm::errs() << "Writing '" << P << "'.\n"; - - auto Stream = llvm::make_unique<llvm::raw_fd_ostream>(FD, true); - - return llvm::make_unique<UbigraphViz>(std::move(Stream), P); -} - -void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) { - - assert (Src != Dst && "Self-edges are not allowed."); - - // Lookup the Src. If it is a new node, it's a root. - VMap::iterator SrcI= M.find(Src); - unsigned SrcID; - - if (SrcI == M.end()) { - M[Src] = SrcID = Cntr++; - *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; - } - else - SrcID = SrcI->second; - - // Lookup the Dst. - VMap::iterator DstI= M.find(Dst); - unsigned DstID; - - if (DstI == M.end()) { - M[Dst] = DstID = Cntr++; - *Out << "('vertex', " << DstID << ")\n"; - } - else { - // We have hit DstID before. Change its style to reflect a cache hit. - DstID = DstI->second; - *Out << "('change_vertex_style', " << DstID << ", 1)\n"; - } - - // Add the edge. - *Out << "('edge', " << SrcID << ", " << DstID - << ", ('arrow','true'), ('oriented', 'true'))\n"; -} - -UbigraphViz::UbigraphViz(std::unique_ptr<raw_ostream> OutStream, - StringRef Filename) - : Out(std::move(OutStream)), Filename(Filename), Cntr(0) { - - *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; - *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," - " ('size', '1.5'))\n"; -} - -UbigraphViz::~UbigraphViz() { - Out.reset(); - llvm::errs() << "Running 'ubiviz' program... "; - std::string ErrMsg; - std::string Ubiviz; - if (auto Path = llvm::sys::findProgramByName("ubiviz")) - Ubiviz = *Path; - std::array<StringRef, 2> Args{{Ubiviz, Filename}}; - - if (llvm::sys::ExecuteAndWait(Ubiviz, Args, llvm::None, {}, 0, 0, &ErrMsg)) { - llvm::errs() << "Error viewing graph: " << ErrMsg << "\n"; - } - - // Delete the file. - llvm::sys::fs::remove(Filename); -} diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index ff0a6e19fc97f..5e7dd8f18cd73 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangStaticAnalyzerFrontend AnalysisConsumer.cpp CheckerRegistration.cpp + CheckerRegistry.cpp FrontendActions.cpp ModelConsumer.cpp ModelInjector.cpp diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index a260c2d85b11f..1c31c35b75e4a 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -14,146 +14,124 @@ #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/CheckerOptInfo.h" -#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/DynamicLibrary.h" -#include "llvm/Support/Path.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" #include <memory> 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<std::string> plugins, - DiagnosticsEngine *diags = nullptr); -}; - -} // end anonymous namespace - -ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef<std::string> plugins, - DiagnosticsEngine *diags) { - registerBuiltinCheckers(*this); - - for (ArrayRef<std::string>::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; -} - -static SmallVector<CheckerOptInfo, 8> -getCheckerOptList(const AnalyzerOptions &opts) { - SmallVector<CheckerOptInfo, 8> checkerOpts; - for (unsigned i = 0, e = opts.CheckersControlList.size(); i != e; ++i) { - const std::pair<std::string, bool> &opt = opts.CheckersControlList[i]; - checkerOpts.push_back(CheckerOptInfo(opt.first, opt.second)); - } - return checkerOpts; -} std::unique_ptr<CheckerManager> ento::createCheckerManager( - AnalyzerOptions &opts, const LangOptions &langOpts, + ASTContext &context, + AnalyzerOptions &opts, 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); + auto checkerMgr = llvm::make_unique<CheckerManager>(context, opts); - ClangCheckerRegistry allCheckers(plugins, &diags); + CheckerRegistry allCheckers(plugins, diags); for (const auto &Fn : checkerRegistrationFns) Fn(allCheckers); - allCheckers.initializeManager(*checkerMgr, checkerOpts); - allCheckers.validateCheckerOptions(opts, diags); + allCheckers.initializeManager(*checkerMgr, opts); + allCheckers.validateCheckerOptions(opts); checkerMgr->finishedCheckerRegistration(); - for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) { - if (checkerOpts[i].isUnclaimed()) { - diags.Report(diag::err_unknown_analyzer_checker) - << checkerOpts[i].getName(); - diags.Report(diag::note_suggest_disabling_all_checkers); - } - - } - return checkerMgr; } -void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins) { +void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins, + DiagnosticsEngine &diags) { out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n"; out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n"; - ClangCheckerRegistry(plugins).printHelp(out); + CheckerRegistry(plugins, diags).printHelp(out); } void ento::printEnabledCheckerList(raw_ostream &out, ArrayRef<std::string> plugins, - const AnalyzerOptions &opts) { + const AnalyzerOptions &opts, + DiagnosticsEngine &diags) { out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; - SmallVector<CheckerOptInfo, 8> checkerOpts = getCheckerOptList(opts); - ClangCheckerRegistry(plugins).printList(out, checkerOpts); + CheckerRegistry(plugins, diags).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 " + "<OPTION1=VALUE,OPTION2=VALUE,...>\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" + "<OPTION1=VALUE,OPTION2=VALUE,...>\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<StringRef, std::string>; + 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"; + } } diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp new file mode 100644 index 0000000000000..620c0e588906a --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -0,0 +1,247 @@ +//===- CheckerRegistry.cpp - Maintains all available checkers -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +using namespace clang; +using namespace ento; +using llvm::sys::DynamicLibrary; + +using RegisterCheckersFn = void (*)(CheckerRegistry &); + +static bool 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; +} + +CheckerRegistry::CheckerRegistry(ArrayRef<std::string> plugins, + DiagnosticsEngine &diags) : Diags(diags) { +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ + addChecker(register##CLASS, FULLNAME, HELPTEXT, DOC_URI); +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + + for (ArrayRef<std::string>::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)) { + Diags.Report(diag::warn_incompatible_analyzer_plugin_api) + << llvm::sys::path::filename(*i); + Diags.Report(diag::note_incompatible_analyzer_plugin_api) + << CLANG_ANALYZER_API_VERSION_STRING + << pluginAPIVersion; + continue; + } + + // Register its checkers. + RegisterCheckersFn registerPluginCheckers = + (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol( + "clang_registerCheckers"); + if (registerPluginCheckers) + registerPluginCheckers(*this); + } +} + +static constexpr char PackageSeparator = '.'; + +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + +static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, + StringRef packageName) { + // Does the checker's full name have the package as a prefix? + if (!checker.FullName.startswith(packageName)) + return false; + + // Is the package actually just the name of a specific checker? + if (checker.FullName.size() == packageName.size()) + return true; + + // Is the checker in the package (or a subpackage)? + if (checker.FullName[packageName.size()] == PackageSeparator) + return true; + + return false; +} + +CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers( + const AnalyzerOptions &Opts) const { + + assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && + "In order to efficiently gather checkers, this function expects them " + "to be already sorted!"); + + CheckerInfoSet enabledCheckers; + const auto end = Checkers.cend(); + + for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) { + // Use a binary search to find the possible start of the package. + CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "", ""); + auto firstRelatedChecker = + std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT); + + if (firstRelatedChecker == end || + !isInPackage(*firstRelatedChecker, opt.first)) { + Diags.Report(diag::err_unknown_analyzer_checker) << opt.first; + Diags.Report(diag::note_suggest_disabling_all_checkers); + return {}; + } + + // See how large the package is. + // If the package doesn't exist, assume the option refers to a single + // checker. + size_t size = 1; + llvm::StringMap<size_t>::const_iterator packageSize = + Packages.find(opt.first); + if (packageSize != Packages.end()) + size = packageSize->getValue(); + + // Step through all the checkers in the package. + for (auto lastRelatedChecker = firstRelatedChecker+size; + firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) + if (opt.second) + enabledCheckers.insert(&*firstRelatedChecker); + else + enabledCheckers.remove(&*firstRelatedChecker); + } + + return enabledCheckers; +} + +void CheckerRegistry::addChecker(InitializationFunction Fn, StringRef Name, + StringRef Desc, StringRef DocsUri) { + Checkers.emplace_back(Fn, Name, Desc, DocsUri); + + // Record the presence of the checker in its packages. + StringRef packageName, leafName; + std::tie(packageName, leafName) = Name.rsplit(PackageSeparator); + while (!leafName.empty()) { + Packages[packageName] += 1; + std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); + } +} + +void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, + const AnalyzerOptions &Opts) const { + // Sort checkers for efficient collection. + llvm::sort(Checkers, checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts); + + // Initialize the CheckerManager with all enabled checkers. + for (const auto *i : enabledCheckers) { + checkerMgr.setCurrentCheckName(CheckName(i->FullName)); + i->Initialize(checkerMgr); + } +} + +void CheckerRegistry::validateCheckerOptions( + const AnalyzerOptions &opts) const { + for (const auto &config : opts.Config) { + size_t pos = config.getKey().find(':'); + if (pos == StringRef::npos) + continue; + + bool hasChecker = false; + StringRef checkerName = config.getKey().substr(0, pos); + for (const auto &checker : Checkers) { + if (checker.FullName.startswith(checkerName) && + (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { + hasChecker = true; + break; + } + } + if (!hasChecker) + Diags.Report(diag::err_unknown_analyzer_checker) << checkerName; + } +} + +void CheckerRegistry::printHelp(raw_ostream &out, + size_t maxNameChars) const { + // FIXME: Alphabetical sort puts 'experimental' in the middle. + // Would it be better to name it '~experimental' or something else + // that's ASCIIbetically last? + llvm::sort(Checkers, checkerNameLT); + + // FIXME: Print available packages. + + out << "CHECKERS:\n"; + + // Find the maximum option length. + size_t optionFieldWidth = 0; + for (const auto &i : Checkers) { + // Limit the amount of padding we are willing to give up for alignment. + // Package.Name Description [Hidden] + size_t nameLength = i.FullName.size(); + if (nameLength <= maxNameChars) + optionFieldWidth = std::max(optionFieldWidth, nameLength); + } + + const size_t initialPad = 2; + for (const auto &i : Checkers) { + out.indent(initialPad) << i.FullName; + + int pad = optionFieldWidth - i.FullName.size(); + + // Break on long option names. + if (pad < 0) { + out << '\n'; + pad = optionFieldWidth + initialPad; + } + out.indent(pad + 2) << i.Desc; + + out << '\n'; + } +} + +void CheckerRegistry::printList(raw_ostream &out, + const AnalyzerOptions &opts) const { + // Sort checkers for efficient collection. + llvm::sort(Checkers, checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers = getEnabledCheckers(opts); + + for (const auto *i : enabledCheckers) + out << i->FullName << '\n'; +} diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp index c43d30440c8f5..b1927c8401d66 100644 --- a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -48,7 +48,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) { FileID mainFileID = SM.getMainFileID(); AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); - llvm::StringRef modelPath = analyzerOpts->Config["model-path"]; + llvm::StringRef modelPath = analyzerOpts->ModelPath; llvm::SmallString<128> fileName; |