Hi there,

I am working on a system to model API calls in the Static Analyzer. The
main idea is to have C/C++ source files that contains definitions of
functions and methods, and they are parsed by Clang and used by the Static
Analyzer when the definition of a function or a method is in another
translation unit. This can help the analyzer to find more possible issues
when known APIs are modelled. (More details on my GSoC project page:
http://www.google-melange.com/gsoc/project/details/google/gsoc2014/xazax/5717271485874176
)

The patch I attached can parse a model file using the ASTContext of the
currently analyzed translation unit, so the model files do not need to be a
standalone translation units and they can be parsed quickly. Any review on
my current implementation is welcome. Because the error reporting is still
a work in progress and the path is not truncated properly yet (the parts
that are in a model file are not omitted) the test I included does not
pass. I will fix it as soon as possible, but in the meanwhile any comments
are welcome.

Thanks,
Gábor Horváth
Index: include/clang/Analysis/AnalysisContext.h
===================================================================
--- include/clang/Analysis/AnalysisContext.h	(revision 212534)
+++ include/clang/Analysis/AnalysisContext.h	(working copy)
@@ -17,8 +17,10 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CodeInjector.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/OwningPtr.h"
 #include "llvm/Support/Allocator.h"
 #include <memory>
 
@@ -398,6 +400,10 @@
   ContextMap Contexts;
   LocationContextManager LocContexts;
   CFG::BuildOptions cfgBuildOptions;
+
+  /// Pointer to an interface that can provide function bodies for
+  /// declarations from external source
+  llvm::OwningPtr<CodeInjector> Injector;
   
   /// Flag to indicate whether or not bodies should be synthesized
   /// for well-known functions.
@@ -410,7 +416,8 @@
                              bool addTemporaryDtors = false,
                              bool synthesizeBodies = false,
                              bool addStaticInitBranches = false,
-                             bool addCXXNewAllocator = true);
+                             bool addCXXNewAllocator = true,
+                             CodeInjector* injector = nullptr);
 
   ~AnalysisDeclContextManager();
 
Index: include/clang/Analysis/CodeInjector.h
===================================================================
--- include/clang/Analysis/CodeInjector.h	(revision 0)
+++ include/clang/Analysis/CodeInjector.h	(working copy)
@@ -0,0 +1,29 @@
+//===-- CodeInjector.h ------------------------------------------*- 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_ANALYSIS_CODEINJECTOR_H
+#define LLVM_CLANG_ANALYSIS_CODEINJECTOR_H
+
+namespace clang {
+
+class Stmt;
+class FunctionDecl;
+class ObjCMethodDecl;
+
+class CodeInjector {
+public:
+  CodeInjector();
+  virtual ~CodeInjector();
+
+  virtual Stmt *getBody(const FunctionDecl *D) = 0;
+  virtual Stmt *getBody(const ObjCMethodDecl *D) = 0;
+};
+}
+
+#endif
\ No newline at end of file
Index: include/clang/Frontend/FrontendAction.h
===================================================================
--- include/clang/Frontend/FrontendAction.h	(revision 212534)
+++ include/clang/Frontend/FrontendAction.h	(working copy)
@@ -34,6 +34,10 @@
 
 /// Abstract base class for actions which can be performed by the frontend.
 class FrontendAction {
+  /// Is this action invoked on a model file? Model files are incomplete
+  /// translation units that relies on type information from another translation
+  /// unit. Check ParseModelFileAction for details.
+  bool ModelFile;
   FrontendInputFile CurrentInput;
   std::unique_ptr<ASTUnit> CurrentASTUnit;
   CompilerInstance *Instance;
@@ -105,7 +109,11 @@
   /// @}
 
 public:
-  FrontendAction();
+  /// \brief Constructor
+  ///
+  /// \param modelFile determines whether the source files this action invoked
+  /// on should be treated as a model file. Defaults to false.
+  FrontendAction(bool modelFile = false);
   virtual ~FrontendAction();
 
   /// @name Compiler Instance Access
@@ -127,6 +135,11 @@
     return (bool)CurrentASTUnit;
   }
 
+  /// \brief Is this action responsible for parsing a model file?
+  bool isModelParsingAction() const {
+    return ModelFile;
+  }
+
   const FrontendInputFile &getCurrentInput() const {
     return CurrentInput;
   }
@@ -222,6 +235,7 @@
   void ExecuteAction() override;
 
 public:
+  ASTFrontendAction(bool ModelFile = false) : FrontendAction(ModelFile) {}
   bool usesPreprocessorOnly() const override { return false; }
 };
 
Index: include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h	(revision 212534)
+++ include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h	(working copy)
@@ -23,6 +23,8 @@
 
 namespace clang {
 
+class CodeInjector;
+
 namespace ento {
   class CheckerManager;
 
@@ -50,7 +52,8 @@
                   StoreManagerCreator storemgr,
                   ConstraintManagerCreator constraintmgr, 
                   CheckerManager *checkerMgr,
-                  AnalyzerOptions &Options);
+                  AnalyzerOptions &Options,
+                  CodeInjector* injector = nullptr);
 
   ~AnalysisManager();
   
Index: include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h
===================================================================
--- include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h	(revision 212534)
+++ include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h	(working copy)
@@ -25,6 +25,7 @@
 
 class Preprocessor;
 class DiagnosticsEngine;
+class CodeInjector;
 
 namespace ento {
 class CheckerManager;
@@ -40,7 +41,8 @@
 AnalysisASTConsumer *CreateAnalysisConsumer(const Preprocessor &pp,
                                             const std::string &output,
                                             AnalyzerOptionsRef opts,
-                                            ArrayRef<std::string> plugins);
+                                            ArrayRef<std::string> plugins,
+                                            CodeInjector* injector = nullptr);
 
 } // end GR namespace
 
Index: include/clang/StaticAnalyzer/Frontend/FrontendActions.h
===================================================================
--- include/clang/StaticAnalyzer/Frontend/FrontendActions.h	(revision 212534)
+++ include/clang/StaticAnalyzer/Frontend/FrontendActions.h	(working copy)
@@ -10,7 +10,11 @@
 #ifndef LLVM_CLANG_GR_FRONTENDACTIONS_H
 #define LLVM_CLANG_GR_FRONTENDACTIONS_H
 
+#include <map>
+
 #include "clang/Frontend/FrontendAction.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringMap.h"
 
 namespace clang {
 
@@ -26,6 +30,25 @@
                                  StringRef InFile) override;
 };
 
+/// \brief Frontend action to parse model files.
+///
+/// This frontend action is responsible for parsing model files. Model files can
+/// not be parsed on their own, they rely on type information that is available
+/// in another translation unit. The parsing of model files is done by a
+/// separate compiler instance that reuses the ASTContext and othen information
+/// from the main translation unit that is being compiled. After a model file is
+/// parsed, the function definitions will be collected into a StringMap.
+class ParseModelFileAction : public ASTFrontendAction {
+public:
+  ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies);
+
+protected:
+  ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
+                                 StringRef InFile) override;
+private:
+  llvm::StringMap<Stmt *> &Bodies;
+};
+
 void printCheckerHelp(raw_ostream &OS, ArrayRef<std::string> plugins);
 
 } // end GR namespace
Index: include/clang/StaticAnalyzer/Frontend/ModelConsumer.h
===================================================================
--- include/clang/StaticAnalyzer/Frontend/ModelConsumer.h	(revision 0)
+++ include/clang/StaticAnalyzer/Frontend/ModelConsumer.h	(working copy)
@@ -0,0 +1,32 @@
+#ifndef LLVM_CLANG_GR_MODELCONSUMER_H
+#define LLVM_CLANG_GR_MODELCONSUMER_H
+
+#include <map>
+
+#include "clang/AST/ASTConsumer.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+
+class Stmt;
+
+namespace ento {
+
+/// \brief ASTConsumer to consume model files' AST.
+///
+/// This consumer collects the bodies of function definitions into a StringMap
+/// from a model file.
+class ModelConsumer : public ASTConsumer {
+public:
+  ModelConsumer(llvm::StringMap<Stmt *> &Bodies);
+
+  bool HandleTopLevelDecl(DeclGroupRef D) override;
+
+private:
+  llvm::StringMap<Stmt *> &Bodies;
+};
+
+}
+}
+
+#endif
Index: lib/Analysis/AnalysisDeclContext.cpp
===================================================================
--- lib/Analysis/AnalysisDeclContext.cpp	(revision 212534)
+++ lib/Analysis/AnalysisDeclContext.cpp	(working copy)
@@ -69,8 +69,9 @@
                                                        bool addTemporaryDtors,
                                                        bool synthesizeBodies,
                                                        bool addStaticInitBranch,
-                                                       bool addCXXNewAllocator)
-  : SynthesizeBodies(synthesizeBodies)
+                                                       bool addCXXNewAllocator,
+                                                       CodeInjector *injector)
+  : Injector(injector), SynthesizeBodies(synthesizeBodies)
 {
   cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
   cfgBuildOptions.AddImplicitDtors = addImplicitDtors;
@@ -84,8 +85,8 @@
   llvm::DeleteContainerSeconds(Contexts);
 }
 
-static BodyFarm &getBodyFarm(ASTContext &C) {
-  static BodyFarm *BF = new BodyFarm(C);
+static BodyFarm &getBodyFarm(ASTContext &C, CodeInjector *injector = nullptr) {
+  static BodyFarm *BF = new BodyFarm(C, injector);
   return *BF;
 }
 
@@ -94,7 +95,7 @@
   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     Stmt *Body = FD->getBody();
     if (!Body && Manager && Manager->synthesizeBodies()) {
-      Body = getBodyFarm(getASTContext()).getBody(FD);
+      Body = getBodyFarm(getASTContext(), Manager->Injector.get()).getBody(FD);
       if (Body)
         IsAutosynthesized = true;
     }
@@ -103,7 +104,7 @@
   else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
     Stmt *Body = MD->getBody();
     if (!Body && Manager && Manager->synthesizeBodies()) {
-      Body = getBodyFarm(getASTContext()).getBody(MD);
+      Body = getBodyFarm(getASTContext(), Manager->Injector.get()).getBody(MD);
       if (Body)
         IsAutosynthesized = true;
     }
Index: lib/Analysis/BodyFarm.cpp
===================================================================
--- lib/Analysis/BodyFarm.cpp	(revision 212534)
+++ lib/Analysis/BodyFarm.cpp	(working copy)
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "BodyFarm.h"
+#include "clang/Analysis/CodeInjector.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
@@ -383,6 +384,7 @@
   }
   
   if (FF) { Val = FF(C, D); }
+  else if (Injector) { Val = Injector->getBody(D); }
   return Val.getValue();
 }
 
Index: lib/Analysis/BodyFarm.h
===================================================================
--- lib/Analysis/BodyFarm.h	(revision 212534)
+++ lib/Analysis/BodyFarm.h	(working copy)
@@ -27,10 +27,11 @@
 class ObjCMethodDecl;
 class ObjCPropertyDecl;
 class Stmt;
+class CodeInjector;
   
 class BodyFarm {
 public:
-  BodyFarm(ASTContext &C) : C(C) {}
+  BodyFarm(ASTContext &C, CodeInjector *injector) : C(C), Injector(injector) {}
   
   /// Factory method for creating bodies for ordinary functions.
   Stmt *getBody(const FunctionDecl *D);
@@ -43,6 +44,7 @@
 
   ASTContext &C;
   BodyMap Bodies;
+  CodeInjector *Injector;
 };
 }
 
Index: lib/Analysis/CMakeLists.txt
===================================================================
--- lib/Analysis/CMakeLists.txt	(revision 212534)
+++ lib/Analysis/CMakeLists.txt	(working copy)
@@ -12,6 +12,7 @@
   CallGraph.cpp
   CocoaConventions.cpp
   Consumed.cpp
+  CodeInjector.cpp
   Dominators.cpp
   FormatString.cpp
   LiveVariables.cpp
Index: lib/Analysis/CodeInjector.cpp
===================================================================
--- lib/Analysis/CodeInjector.cpp	(revision 0)
+++ lib/Analysis/CodeInjector.cpp	(working copy)
@@ -0,0 +1,15 @@
+//===-- CodeInjector.cpp ----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/CodeInjector.h"
+
+using namespace clang;
+
+CodeInjector::CodeInjector() {}
+CodeInjector::~CodeInjector() {}
\ No newline at end of file
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp	(revision 212534)
+++ lib/Frontend/CompilerInstance.cpp	(working copy)
@@ -802,8 +802,9 @@
     llvm::EnableStatistics();
 
   for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) {
-    // Reset the ID tables if we are reusing the SourceManager.
-    if (hasSourceManager())
+    // Reset the ID tables if we are reusing the SourceManager and parsing
+    // regular files.
+    if (hasSourceManager() && !Act.isModelParsingAction())
       getSourceManager().clearIDTables();
 
     if (Act.BeginSourceFile(*this, getFrontendOpts().Inputs[i])) {
Index: lib/Frontend/FrontendAction.cpp
===================================================================
--- lib/Frontend/FrontendAction.cpp	(revision 212534)
+++ lib/Frontend/FrontendAction.cpp	(working copy)
@@ -122,7 +122,8 @@
 
 } // end anonymous namespace
 
-FrontendAction::FrontendAction() : Instance(nullptr) {}
+FrontendAction::FrontendAction(bool modelFile)
+    : ModelFile(modelFile), Instance(nullptr) {}
 
 FrontendAction::~FrontendAction() {}
 
@@ -283,8 +284,9 @@
     }
   }
 
-  // Set up the preprocessor.
-  CI.createPreprocessor(getTranslationUnitKind());
+  // Set up the preprocessor if needed.
+  if (!CI.hasPreprocessor())
+    CI.createPreprocessor(getTranslationUnitKind());
 
   // Inform the diagnostic client we are processing a source file.
   CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
@@ -303,7 +305,9 @@
   // Create the AST context and consumer unless this is a preprocessor only
   // action.
   if (!usesPreprocessorOnly()) {
-    CI.createASTContext();
+    // Parsing a model file should reuse the existing ASTContext.
+    if (!ModelFile)
+      CI.createASTContext();
 
     std::unique_ptr<ASTConsumer> Consumer(
         CreateWrappedASTConsumer(CI, InputFile));
@@ -310,7 +314,9 @@
     if (!Consumer)
       goto failure;
 
-    CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener());
+    // FIXME: should not overwrite ASTMutationListener when parsing model files?
+    if (!ModelFile)
+      CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener());
     
     if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) {
       // Convert headers to PCH and chain them.
Index: lib/StaticAnalyzer/Core/AnalysisManager.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalysisManager.cpp	(revision 212534)
+++ lib/StaticAnalyzer/Core/AnalysisManager.cpp	(working copy)
@@ -20,13 +20,16 @@
                                  StoreManagerCreator storemgr,
                                  ConstraintManagerCreator constraintmgr, 
                                  CheckerManager *checkerMgr,
-                                 AnalyzerOptions &Options)
+                                 AnalyzerOptions &Options,
+                                 CodeInjector *injector)
   : AnaCtxMgr(Options.UnoptimizedCFG,
               /*AddImplicitDtors=*/true,
               /*AddInitializers=*/true,
               Options.includeTemporaryDtorsInCFG(),
               Options.shouldSynthesizeBodies(),
-              Options.shouldConditionalizeStaticInitializers()),
+              Options.shouldConditionalizeStaticInitializers(),
+              /*addCXXNewAllocator=*/true,
+              injector),
     Ctx(ctx),
     Diags(diags),
     LangOpts(lang),
Index: lib/StaticAnalyzer/Core/BugReporter.cpp
===================================================================
--- lib/StaticAnalyzer/Core/BugReporter.cpp	(revision 212534)
+++ lib/StaticAnalyzer/Core/BugReporter.cpp	(working copy)
@@ -3087,8 +3087,15 @@
   for (ArrayRef<BugReport*>::iterator I = bugReports.begin(),
                                       E = bugReports.end(); I != E; ++I) {
     if ((*I)->isValid()) {
+      const ExplodedNode *E = (*I)->getErrorNode();
+      const LocationContext *LCtx = E->getLocationContext();
+
+      // Truncate path that ends up in a modelled function.
+      if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized())
+        continue;
+
       HasValid = true;
-      errorNodes.push_back((*I)->getErrorNode());
+      errorNodes.push_back(E);
     } else {
       // Keep the errorNodes list in sync with the bugReports list.
       HasInvalid = true;
@@ -3246,18 +3253,6 @@
 void BugReporter::emitReport(BugReport* R) {
   // To guarantee memory release.
   std::unique_ptr<BugReport> UniqueR(R);
-
-  // Defensive checking: throw the bug away if it comes from a BodyFarm-
-  // generated body. We do this very early because report processing relies
-  // on the report's location being valid.
-  // FIXME: Valid bugs can occur in BodyFarm-generated bodies, so really we
-  // need to just find a reasonable location like we do later on with the path
-  // pieces.
-  if (const ExplodedNode *E = R->getErrorNode()) {
-    const LocationContext *LCtx = E->getLocationContext();
-    if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized())
-      return;
-  }
   
   bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid();
   assert(ValidSourceLoc);
Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
===================================================================
--- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp	(revision 212534)
+++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp	(working copy)
@@ -18,6 +18,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ParentMap.h"
+#include "clang/Analysis/CodeInjector.h"
 #include "clang/Analysis/Analyses/LiveVariables.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/CallGraph.h"
@@ -157,6 +158,7 @@
   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 +186,10 @@
   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();
@@ -286,6 +289,7 @@
     Ctx = &Context;
     checkerMgr.reset(createCheckerManager(*Opts, PP.getLangOpts(), Plugins,
                                           PP.getDiagnostics()));
+
     Mgr.reset(new AnalysisManager(*Ctx,
                                   PP.getDiagnostics(),
                                   PP.getLangOpts(),
@@ -293,7 +297,8 @@
                                   CreateStoreMgr,
                                   CreateConstraintMgr,
                                   checkerMgr.get(),
-                                  *Opts));
+                                  *Opts,
+                                  Injector));
   }
 
   /// \brief Store the top level decls in the set to be processed later on.
@@ -690,11 +695,12 @@
 AnalysisASTConsumer *
 ento::CreateAnalysisConsumer(const Preprocessor &pp, const std::string &outDir,
                              AnalyzerOptionsRef opts,
-                             ArrayRef<std::string> plugins) {
+                             ArrayRef<std::string> plugins,
+                             CodeInjector *injector) {
   // Disable the effects of '-Werror' when using the AnalysisConsumer.
   pp.getDiagnostics().setWarningsAsErrors(false);
 
-  return new AnalysisConsumer(pp, outDir, opts, plugins);
+  return new AnalysisConsumer(pp, outDir, opts, plugins, injector);
 }
 
 //===----------------------------------------------------------------------===//
Index: lib/StaticAnalyzer/Frontend/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Frontend/CMakeLists.txt	(revision 212534)
+++ lib/StaticAnalyzer/Frontend/CMakeLists.txt	(working copy)
@@ -7,7 +7,9 @@
 add_clang_library(clangStaticAnalyzerFrontend
   AnalysisConsumer.cpp
   CheckerRegistration.cpp
+  ModelConsumer.cpp
   FrontendActions.cpp
+  ModelInjector.cpp
 
   LINK_LIBS
   clangAST
Index: lib/StaticAnalyzer/Frontend/FrontendActions.cpp
===================================================================
--- lib/StaticAnalyzer/Frontend/FrontendActions.cpp	(revision 212534)
+++ lib/StaticAnalyzer/Frontend/FrontendActions.cpp	(working copy)
@@ -7,9 +7,11 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Frontend/CompilerInstance.h"
 #include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
-#include "clang/Frontend/CompilerInstance.h"
 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h"
+#include "ModelInjector.h"
 using namespace clang;
 using namespace ento;
 
@@ -18,6 +20,14 @@
   return CreateAnalysisConsumer(CI.getPreprocessor(),
                                 CI.getFrontendOpts().OutputFile,
                                 CI.getAnalyzerOpts(),
-                                CI.getFrontendOpts().Plugins);
+                                CI.getFrontendOpts().Plugins,
+                                new ModelInjector(CI));
 }
 
+ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies)
+    : ASTFrontendAction(/*ModelFile=*/true), Bodies(Bodies) {}
+
+ASTConsumer *ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI,
+                                                     StringRef InFile) {
+  return new ModelConsumer(Bodies);
+}
Index: lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
===================================================================
--- lib/StaticAnalyzer/Frontend/ModelConsumer.cpp	(revision 0)
+++ lib/StaticAnalyzer/Frontend/ModelConsumer.cpp	(working copy)
@@ -0,0 +1,34 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// "Meta" ASTConsumer for consuming model files.
+//
+//===----------------------------------------------------------------------===//
+
+#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;
+}
\ No newline at end of file
Index: lib/StaticAnalyzer/Frontend/ModelInjector.cpp
===================================================================
--- lib/StaticAnalyzer/Frontend/ModelInjector.cpp	(revision 0)
+++ lib/StaticAnalyzer/Frontend/ModelInjector.cpp	(working copy)
@@ -0,0 +1,114 @@
+//===-- 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 <string>
+#include <utility>
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
+#include "clang/Serialization/ASTReader.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/AST/Decl.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/ADT/STLExtras.h"
+
+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 create a separate compilerinstance for parsing modules, maybe it is
+  // for reason so I mimic this behavior
+  CompilerInstance Instance;
+  Instance.setInvocation(&*Invocation);
+  Instance.createDiagnostics(
+      new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()),
+      /*ShouldOwnClient=*/true);
+
+  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.setSema(&CI.getSema());
+
+  ParseModelFileAction parseModelFile(Bodies);
+
+  const unsigned ThreadStackSize = 8 << 20;
+  llvm::CrashRecoveryContext CRC;
+
+  CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); },
+                        ThreadStackSize);
+
+  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);
+}
Index: lib/StaticAnalyzer/Frontend/ModelInjector.h
===================================================================
--- lib/StaticAnalyzer/Frontend/ModelInjector.h	(revision 0)
+++ lib/StaticAnalyzer/Frontend/ModelInjector.h	(working copy)
@@ -0,0 +1,63 @@
+//===-- ModelInjector.h -----------------------------------------*- 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_SA_FRONTEND_MODELINJECTOR_H
+#define LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H
+
+#include <map>
+#include <vector>
+#include <memory>
+
+#include "clang/Analysis/CodeInjector.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringMap.h"
+
+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 Synthetize 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 synthetized 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;
+  std::vector<std::unique_ptr<ASTUnit> > ModelAsts;
+  std::vector<Module *> Modules;
+  llvm::IntrusiveRefCntPtr<ASTReader> ModuleManager;
+
+  // FIXME: double memoization is redundant. Here and in bodyfarm.
+  llvm::StringMap<Stmt *> Bodies;
+};
+}
+}
+
+#endif
\ No newline at end of file
Index: test/Analysis/model-file.cpp
===================================================================
--- test/Analysis/model-file.cpp	(revision 0)
+++ test/Analysis/model-file.cpp	(working copy)
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config faux-bodies=true -verify %s
+typedef int* intptr;
+
+void modelled(intptr p);
+
+int main() {
+	modelled(0); // expected-warning{{Dereference of null pointer}}
+	return 0;
+}
\ No newline at end of file
Index: test/Analysis/modelled.model
===================================================================
--- test/Analysis/modelled.model	(revision 0)
+++ test/Analysis/modelled.model	(working copy)
@@ -0,0 +1,3 @@
+void modelled(intptr p) {
+	++*p;
+}
\ No newline at end of file
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to