diff options
Diffstat (limited to 'lib/StaticAnalyzer/Frontend')
-rw-r--r-- | lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 62 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CMakeLists.txt | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp | 16 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/FrontendActions.cpp | 19 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelConsumer.cpp | 42 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelInjector.cpp | 117 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelInjector.h | 74 |
7 files changed, 294 insertions, 39 deletions
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index f0dd274..183ef35 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "ModelInjector.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/DataRecursiveASTVisitor.h" #include "clang/AST/Decl.h" @@ -21,8 +22,10 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CallGraph.h" +#include "clang/Analysis/CodeInjector.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" @@ -51,7 +54,7 @@ using llvm::SmallPtrSet; #define DEBUG_TYPE "AnalysisConsumer" -static ExplodedNode::Auditor* CreateUbiViz(); +static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz(); STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); STATISTIC(NumFunctionsAnalyzed, @@ -157,6 +160,7 @@ public: const std::string OutDir; AnalyzerOptionsRef Opts; ArrayRef<std::string> Plugins; + CodeInjector *Injector; /// \brief Stores the declarations from the local translation unit. /// Note, we pre-compute the local declarations at parse time as an @@ -184,9 +188,10 @@ public: AnalysisConsumer(const Preprocessor& pp, const std::string& outdir, AnalyzerOptionsRef opts, - ArrayRef<std::string> plugins) - : RecVisitorMode(0), RecVisitorBR(nullptr), - Ctx(nullptr), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins) { + ArrayRef<std::string> plugins, + CodeInjector *injector) + : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp), + OutDir(outdir), Opts(opts), Plugins(plugins), Injector(injector) { DigestAnalyzerOptions(); if (Opts->PrintStats) { llvm::EnableStatistics(); @@ -284,16 +289,12 @@ public: void Initialize(ASTContext &Context) override { Ctx = &Context; - checkerMgr.reset(createCheckerManager(*Opts, PP.getLangOpts(), Plugins, - PP.getDiagnostics())); - Mgr.reset(new AnalysisManager(*Ctx, - PP.getDiagnostics(), - PP.getLangOpts(), - PathConsumers, - CreateStoreMgr, - CreateConstraintMgr, - checkerMgr.get(), - *Opts)); + checkerMgr = createCheckerManager(*Opts, PP.getLangOpts(), Plugins, + PP.getDiagnostics()); + + Mgr = llvm::make_unique<AnalysisManager>( + *Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers, + CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); } /// \brief Store the top level decls in the set to be processed later on. @@ -307,7 +308,7 @@ public: /// analyzed. This allows to redefine the default inlining policies when /// analyzing a given function. ExprEngine::InliningModes - getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); + getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); /// \brief Build the call graph for all the top level decls of this TU and /// use it to define the order in which the functions should be visited. @@ -506,6 +507,11 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) return; + // Don't analyze if the user explicitly asked for no checks to be performed + // on this file. + if (Opts->DisableAllChecks) + return; + { if (TUTotalTimer) TUTotalTimer->startTimer(); @@ -643,7 +649,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, // Set the graph auditor. std::unique_ptr<ExplodedNode::Auditor> Auditor; if (Mgr->options.visualizeExplodedGraphWithUbiGraph) { - Auditor.reset(CreateUbiViz()); + Auditor = CreateUbiViz(); ExplodedNode::SetAuditor(Auditor.get()); } @@ -687,14 +693,18 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, // AnalysisConsumer creation. //===----------------------------------------------------------------------===// -AnalysisASTConsumer * -ento::CreateAnalysisConsumer(const Preprocessor &pp, const std::string &outDir, - AnalyzerOptionsRef opts, - ArrayRef<std::string> plugins) { +std::unique_ptr<AnalysisASTConsumer> +ento::CreateAnalysisConsumer(CompilerInstance &CI) { // Disable the effects of '-Werror' when using the AnalysisConsumer. - pp.getDiagnostics().setWarningsAsErrors(false); + CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false); + + AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); + bool hasModelPath = analyzerOpts->Config.count("model-path") > 0; - return new AnalysisConsumer(pp, outDir, opts, plugins); + return llvm::make_unique<AnalysisConsumer>( + CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts, + CI.getFrontendOpts().Plugins, + hasModelPath ? new ModelInjector(CI) : nullptr); } //===----------------------------------------------------------------------===// @@ -721,7 +731,7 @@ public: } // end anonymous namespace -static ExplodedNode::Auditor* CreateUbiViz() { +static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz() { SmallString<128> P; int FD; llvm::sys::fs::createTemporaryFile("llvm_ubi", "", FD, P); @@ -729,7 +739,7 @@ static ExplodedNode::Auditor* CreateUbiViz() { auto Stream = llvm::make_unique<llvm::raw_fd_ostream>(FD, true); - return new UbigraphViz(std::move(Stream), P); + return llvm::make_unique<UbigraphViz>(std::move(Stream), P); } void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) { @@ -778,7 +788,9 @@ UbigraphViz::~UbigraphViz() { Out.reset(); llvm::errs() << "Running 'ubiviz' program... "; std::string ErrMsg; - std::string Ubiviz = llvm::sys::FindProgramByName("ubiviz"); + std::string Ubiviz; + if (auto Path = llvm::sys::findProgramByName("ubiviz")) + Ubiviz = *Path; std::vector<const char*> args; args.push_back(Ubiviz.c_str()); args.push_back(Filename.c_str()); diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index 5349ed9..e3ca91a 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -7,13 +7,16 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangStaticAnalyzerFrontend AnalysisConsumer.cpp CheckerRegistration.cpp + ModelConsumer.cpp FrontendActions.cpp + ModelInjector.cpp LINK_LIBS clangAST clangAnalysis clangBasic clangFrontend + clangLex clangStaticAnalyzerCheckers clangStaticAnalyzerCore ) diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index e2577c3..36565cb 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -99,11 +99,10 @@ void ClangCheckerRegistry::warnIncompatible(DiagnosticsEngine *diags, << pluginAPIVersion; } - -CheckerManager *ento::createCheckerManager(AnalyzerOptions &opts, - const LangOptions &langOpts, - ArrayRef<std::string> plugins, - DiagnosticsEngine &diags) { +std::unique_ptr<CheckerManager> +ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, + ArrayRef<std::string> plugins, + DiagnosticsEngine &diags) { std::unique_ptr<CheckerManager> checkerMgr( new CheckerManager(langOpts, &opts)); @@ -118,12 +117,15 @@ CheckerManager *ento::createCheckerManager(AnalyzerOptions &opts, checkerMgr->finishedCheckerRegistration(); for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) { - if (checkerOpts[i].isUnclaimed()) + if (checkerOpts[i].isUnclaimed()) { diags.Report(diag::err_unknown_analyzer_checker) << checkerOpts[i].getName(); + diags.Report(diag::note_suggest_disabling_all_checkers); + } + } - return checkerMgr.release(); + return std::move(checkerMgr); } void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins) { diff --git a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp index aa38077..b336080 100644 --- a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -8,16 +8,21 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" -#include "clang/Frontend/CompilerInstance.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" using namespace clang; using namespace ento; -ASTConsumer *AnalysisAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { - return CreateAnalysisConsumer(CI.getPreprocessor(), - CI.getFrontendOpts().OutputFile, - CI.getAnalyzerOpts(), - CI.getFrontendOpts().Plugins); +std::unique_ptr<ASTConsumer> +AnalysisAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateAnalysisConsumer(CI); } +ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies) + : Bodies(Bodies) {} + +std::unique_ptr<ASTConsumer> +ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique<ModelConsumer>(Bodies); +} diff --git a/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp new file mode 100644 index 0000000..a65a5ee --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp @@ -0,0 +1,42 @@ +//===--- ModelConsumer.cpp - ASTConsumer for consuming model files --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements an ASTConsumer for consuming model files. +/// +/// This ASTConsumer handles the AST of a parsed model file. All top level +/// function definitions will be collected from that model file for later +/// retrieval during the static analysis. The body of these functions will not +/// be injected into the ASTUnit of the analyzed translation unit. It will be +/// available through the BodyFarm which is utilized by the AnalysisDeclContext +/// class. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" + +using namespace clang; +using namespace ento; + +ModelConsumer::ModelConsumer(llvm::StringMap<Stmt *> &Bodies) + : Bodies(Bodies) {} + +bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + + // Only interested in definitions. + const FunctionDecl *func = llvm::dyn_cast<FunctionDecl>(*I); + if (func && func->hasBody()) { + Bodies.insert(std::make_pair(func->getName(), func->getBody())); + } + } + return true; +} diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp new file mode 100644 index 0000000..63bb1e2 --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -0,0 +1,117 @@ +//===-- ModelInjector.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModelInjector.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include <string> +#include <utility> + +using namespace clang; +using namespace ento; + +ModelInjector::ModelInjector(CompilerInstance &CI) : CI(CI) {} + +Stmt *ModelInjector::getBody(const FunctionDecl *D) { + onBodySynthesis(D); + return Bodies[D->getName()]; +} + +Stmt *ModelInjector::getBody(const ObjCMethodDecl *D) { + onBodySynthesis(D); + return Bodies[D->getName()]; +} + +void ModelInjector::onBodySynthesis(const NamedDecl *D) { + + // FIXME: what about overloads? Declarations can be used as keys but what + // about file name index? Mangled names may not be suitable for that either. + if (Bodies.count(D->getName()) != 0) + return; + + SourceManager &SM = CI.getSourceManager(); + FileID mainFileID = SM.getMainFileID(); + + AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); + llvm::StringRef modelPath = analyzerOpts->Config["model-path"]; + + llvm::SmallString<128> fileName; + + if (!modelPath.empty()) + fileName = + llvm::StringRef(modelPath.str() + "/" + D->getName().str() + ".model"); + else + fileName = llvm::StringRef(D->getName().str() + ".model"); + + if (!llvm::sys::fs::exists(fileName.str())) { + Bodies[D->getName()] = nullptr; + return; + } + + IntrusiveRefCntPtr<CompilerInvocation> Invocation( + new CompilerInvocation(CI.getInvocation())); + + FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); + InputKind IK = IK_CXX; // FIXME + FrontendOpts.Inputs.clear(); + FrontendOpts.Inputs.push_back(FrontendInputFile(fileName, IK)); + FrontendOpts.DisableFree = true; + + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; + + // Modules are parsed by a separate CompilerInstance, so this code mimics that + // behavior for models + CompilerInstance Instance; + Instance.setInvocation(&*Invocation); + Instance.createDiagnostics( + new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + Instance.getDiagnostics().setSourceManager(&SM); + + Instance.setVirtualFileSystem(&CI.getVirtualFileSystem()); + + // The instance wants to take ownership, however DisableFree frontend option + // is set to true to avoid double free issues + Instance.setFileManager(&CI.getFileManager()); + Instance.setSourceManager(&SM); + Instance.setPreprocessor(&CI.getPreprocessor()); + Instance.setASTContext(&CI.getASTContext()); + + Instance.getPreprocessor().InitializeForModelFile(); + + ParseModelFileAction parseModelFile(Bodies); + + const unsigned ThreadStackSize = 8 << 20; + llvm::CrashRecoveryContext CRC; + + CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); }, + ThreadStackSize); + + Instance.getPreprocessor().FinalizeForModelFile(); + + Instance.resetAndLeakSourceManager(); + Instance.resetAndLeakFileManager(); + Instance.resetAndLeakPreprocessor(); + + // The preprocessor enters to the main file id when parsing is started, so + // the main file id is changed to the model file during parsing and it needs + // to be reseted to the former main file id after parsing of the model file + // is done. + SM.setMainFileID(mainFileID); +} diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.h b/lib/StaticAnalyzer/Frontend/ModelInjector.h new file mode 100644 index 0000000..fd24e32 --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.h @@ -0,0 +1,74 @@ +//===-- ModelInjector.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file defines the clang::ento::ModelInjector class which implements the +/// clang::CodeInjector interface. This class is responsible for injecting +/// function definitions that were synthesized from model files. +/// +/// Model files allow definitions of functions to be lazily constituted for functions +/// which lack bodies in the original source code. This allows the analyzer +/// to more precisely analyze code that calls such functions, analyzing the +/// artificial definitions (which typically approximate the semantics of the +/// called function) when called by client code. These definitions are +/// reconstituted lazily, on-demand, by the static analyzer engine. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H +#define LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H + +#include "clang/Analysis/CodeInjector.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringMap.h" +#include <map> +#include <memory> +#include <vector> + +namespace clang { + +class CompilerInstance; +class ASTUnit; +class ASTReader; +class NamedDecl; +class Module; + +namespace ento { +class ModelInjector : public CodeInjector { +public: + ModelInjector(CompilerInstance &CI); + Stmt *getBody(const FunctionDecl *D); + Stmt *getBody(const ObjCMethodDecl *D); + +private: + /// \brief Synthesize a body for a declaration + /// + /// This method first looks up the appropriate model file based on the + /// model-path configuration option and the name of the declaration that is + /// looked up. If no model were synthesized yet for a function with that name + /// it will create a new compiler instance to parse the model file using the + /// ASTContext, Preprocessor, SourceManager of the original compiler instance. + /// The former resources are shared between the two compiler instance, so the + /// newly created instance have to "leak" these objects, since they are owned + /// by the original instance. + /// + /// The model-path should be either an absolute path or relative to the + /// working directory of the compiler. + void onBodySynthesis(const NamedDecl *D); + + CompilerInstance &CI; + + // FIXME: double memoization is redundant, with memoization both here and in + // BodyFarm. + llvm::StringMap<Stmt *> Bodies; +}; +} +} + +#endif |