- Address reviewer comments

Hi klimek,

http://llvm-reviews.chandlerc.com/D2097

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2097?vs=5336&id=5347#toc

Files:
  include/clang/Tooling/Tooling.h
  lib/Tooling/Tooling.cpp
  unittests/Tooling/ToolingTest.cpp
Index: include/clang/Tooling/Tooling.h
===================================================================
--- include/clang/Tooling/Tooling.h
+++ include/clang/Tooling/Tooling.h
@@ -53,14 +53,33 @@
 
 namespace tooling {
 
+/// \brief Interface to process a clang::CompilerInvocation.
+///
+/// If your tool is based on FrontendAction, you should be deriving from
+/// FrontendActionFactory instead.
+class ToolAction {
+public:
+  virtual ~ToolAction();
+
+  /// \brief Perform an action for an invocation.
+  virtual bool runInvocation(clang::CompilerInvocation *Invocation,
+                             FileManager *Files) = 0;
+};
+
 /// \brief Interface to generate clang::FrontendActions.
 ///
 /// Having a factory interface allows, for example, a new FrontendAction to be
-/// created for each translation unit processed by ClangTool.
-class FrontendActionFactory {
+/// created for each translation unit processed by ClangTool.  This class is
+/// also a ToolAction which uses the FrontendActions created by create() to
+/// process each translation unit.
+class FrontendActionFactory : public ToolAction {
 public:
   virtual ~FrontendActionFactory();
 
+  /// \brief Invokes the compiler with a FrontendAction created by create().
+  bool runInvocation(clang::CompilerInvocation *Invocation,
+                     FileManager *Files);
+
   /// \brief Returns a new clang::FrontendAction.
   ///
   /// The caller takes ownership of the returned action.
@@ -133,6 +152,26 @@
                            const std::vector<std::string> &Args,
                            const Twine &FileName = "input.cc");
 
+/// \brief Builds an AST for 'Code'.
+///
+/// \param Code C++ code.
+/// \param FileName The file name which 'Code' will be mapped as.
+///
+/// \return The resulting AST or null if an error occurred.
+ASTUnit *buildASTFromCode(const Twine &Code,
+                          const Twine &FileName = "input.cc");
+
+/// \brief Builds an AST for 'Code' with additional flags.
+///
+/// \param Code C++ code.
+/// \param Args Additional flags to pass on.
+/// \param FileName The file name which 'Code' will be mapped as.
+///
+/// \return The resulting AST or null if an error occurred.
+ASTUnit *buildASTFromCodeWithArgs(const Twine &Code,
+                                  const std::vector<std::string> &Args,
+                                  const Twine &FileName = "input.cc");
+
 /// \brief Utility to run a FrontendAction in a single clang invocation.
 class ToolInvocation {
  public:
@@ -145,9 +184,19 @@
   /// \param ToolAction The action to be executed. Class takes ownership.
   /// \param Files The FileManager used for the execution. Class does not take
   /// ownership.
-  ToolInvocation(ArrayRef<std::string> CommandLine, FrontendAction *ToolAction,
+  ToolInvocation(ArrayRef<std::string> CommandLine, FrontendAction *FAction,
                  FileManager *Files);
 
+  /// \brief Create a tool invocation.
+  ///
+  /// \param CommandLine The command line arguments to clang.
+  /// \param Action The action to be executed.
+  /// \param Files The FileManager used for the execution.
+  ToolInvocation(ArrayRef<std::string> CommandLine, ToolAction *Action,
+                 FileManager *Files);
+
+  ~ToolInvocation();
+
   /// \brief Map a virtual file to be used while running the tool.
   ///
   /// \param FilePath The path at which the content will be mapped.
@@ -167,7 +216,8 @@
                      clang::CompilerInvocation *Invocation);
 
   std::vector<std::string> CommandLine;
-  OwningPtr<FrontendAction> ToolAction;
+  ToolAction *Action;
+  bool OwnsAction;
   FileManager *Files;
   // Maps <file name> -> <file content>.
   llvm::StringMap<StringRef> MappedFileContents;
@@ -216,23 +266,25 @@
   /// \brief Clear the command line arguments adjuster chain.
   void clearArgumentsAdjusters();
 
-  /// Runs a frontend action over all files specified in the command line.
+  /// Runs an action over all files specified in the command line.
   ///
-  /// \param ActionFactory Factory generating the frontend actions. The function
-  /// takes ownership of this parameter. A new action is generated for every
-  /// processed translation unit.
-  virtual int run(FrontendActionFactory *ActionFactory);
+  /// \param Action Tool action.
+  virtual int run(ToolAction *Action);
+
+  /// \brief Create an AST for each file specified in the command line and
+  /// append them to ASTs.
+  int buildASTs(std::vector<ASTUnit *> &ASTs);
 
   /// \brief Returns the file manager used in the tool.
   ///
   /// The file manager is shared between all translation units.
-  FileManager &getFiles() { return Files; }
+  FileManager &getFiles() { return *Files; }
 
  private:
   // We store compile commands as pair (file name, compile command).
   std::vector< std::pair<std::string, CompileCommand> > CompileCommands;
 
-  FileManager Files;
+  llvm::IntrusiveRefCntPtr<FileManager> Files;
   // Contains a list of pairs (<file name>, <file content>).
   std::vector< std::pair<StringRef, StringRef> > MappedFileContents;
 
Index: lib/Tooling/Tooling.cpp
===================================================================
--- lib/Tooling/Tooling.cpp
+++ lib/Tooling/Tooling.cpp
@@ -13,9 +13,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Tooling/Tooling.h"
+#include "clang/AST/ASTConsumer.h"
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Driver/Tool.h"
+#include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
@@ -38,6 +40,8 @@
 namespace clang {
 namespace tooling {
 
+ToolAction::~ToolAction() {}
+
 FrontendActionFactory::~FrontendActionFactory() {}
 
 // FIXME: This file contains structural duplication with other parts of the
@@ -104,18 +108,26 @@
       ToolAction, Code, std::vector<std::string>(), FileName);
 }
 
+static std::vector<std::string>
+getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs,
+                      StringRef FileName) {
+  std::vector<std::string> Args;
+  Args.push_back("clang-tool");
+  Args.push_back("-fsyntax-only");
+  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
+  Args.push_back(FileName.str());
+  return Args;
+}
+
 bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code,
                            const std::vector<std::string> &Args,
                            const Twine &FileName) {
   SmallString<16> FileNameStorage;
   StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
-  std::vector<std::string> Commands;
-  Commands.push_back("clang-tool");
-  Commands.push_back("-fsyntax-only");
-  Commands.insert(Commands.end(), Args.begin(), Args.end());
-  Commands.push_back(FileNameRef.data());
-  FileManager Files((FileSystemOptions()));
-  ToolInvocation Invocation(Commands, ToolAction, &Files);
+  llvm::IntrusiveRefCntPtr<FileManager> Files(
+      new FileManager(FileSystemOptions()));
+  ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), ToolAction,
+                            Files.getPtr());
 
   SmallString<1024> CodeStorage;
   Invocation.mapVirtualFile(FileNameRef,
@@ -138,10 +150,33 @@
   return AbsolutePath.str();
 }
 
-ToolInvocation::ToolInvocation(
-    ArrayRef<std::string> CommandLine, FrontendAction *ToolAction,
-    FileManager *Files)
-    : CommandLine(CommandLine.vec()), ToolAction(ToolAction), Files(Files) {
+namespace {
+
+class SingleFrontendActionFactory : public FrontendActionFactory {
+  FrontendAction *Action;
+
+public:
+  SingleFrontendActionFactory(FrontendAction *Action) : Action(Action) {}
+
+  FrontendAction *create() { return Action; }
+};
+
+}
+
+ToolInvocation::ToolInvocation(ArrayRef<std::string> CommandLine,
+                               ToolAction *Action, FileManager *Files)
+    : CommandLine(CommandLine.vec()), Action(Action), OwnsAction(false),
+      Files(Files) {}
+
+ToolInvocation::ToolInvocation(ArrayRef<std::string> CommandLine,
+                               FrontendAction *FAction, FileManager *Files)
+    : CommandLine(CommandLine.vec()),
+      Action(new SingleFrontendActionFactory(FAction)), OwnsAction(true),
+      Files(Files) {}
+
+ToolInvocation::~ToolInvocation() {
+  if (OwnsAction)
+    delete Action;
 }
 
 void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
@@ -175,6 +210,14 @@
   }
   OwningPtr<clang::CompilerInvocation> Invocation(
       newInvocation(&Diagnostics, *CC1Args));
+  for (llvm::StringMap<StringRef>::const_iterator
+           It = MappedFileContents.begin(), End = MappedFileContents.end();
+       It != End; ++It) {
+    // Inject the code as the given file name into the preprocessor options.
+    const llvm::MemoryBuffer *Input =
+        llvm::MemoryBuffer::getMemBuffer(It->getValue());
+    Invocation->getPreprocessorOpts().addRemappedFile(It->getKey(), Input);
+  }
   return runInvocation(BinaryName, Compilation.get(), Invocation.take());
 }
 
@@ -189,49 +232,37 @@
     llvm::errs() << "\n";
   }
 
+  return Action->runInvocation(Invocation, Files);
+}
+
+bool FrontendActionFactory::runInvocation(CompilerInvocation *Invocation,
+                                          FileManager *Files) {
   // Create a compiler instance to handle the actual work.
   clang::CompilerInstance Compiler;
   Compiler.setInvocation(Invocation);
   Compiler.setFileManager(Files);
-  // FIXME: What about LangOpts?
 
-  // ToolAction can have lifetime requirements for Compiler or its members, and
-  // we need to ensure it's deleted earlier than Compiler. So we pass it to an
-  // OwningPtr declared after the Compiler variable.
-  OwningPtr<FrontendAction> ScopedToolAction(ToolAction.take());
+  // The FrontendAction can have lifetime requirements for Compiler or its
+  // members, and we need to ensure it's deleted earlier than Compiler. So we
+  // pass it to an OwningPtr declared after the Compiler variable.
+  OwningPtr<FrontendAction> ScopedToolAction(create());
 
   // Create the compilers actual diagnostics engine.
   Compiler.createDiagnostics();
   if (!Compiler.hasDiagnostics())
     return false;
 
   Compiler.createSourceManager(*Files);
-  addFileMappingsTo(Compiler.getSourceManager());
 
   const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
 
-  Compiler.resetAndLeakFileManager();
   Files->clearStatCaches();
   return Success;
 }
 
-void ToolInvocation::addFileMappingsTo(SourceManager &Sources) {
-  for (llvm::StringMap<StringRef>::const_iterator
-           It = MappedFileContents.begin(), End = MappedFileContents.end();
-       It != End; ++It) {
-    // Inject the code as the given file name into the preprocessor options.
-    const llvm::MemoryBuffer *Input =
-        llvm::MemoryBuffer::getMemBuffer(It->getValue());
-    // FIXME: figure out what '0' stands for.
-    const FileEntry *FromFile = Files->getVirtualFile(
-        It->getKey(), Input->getBufferSize(), 0);
-    Sources.overrideFileContents(FromFile, Input);
-  }
-}
-
 ClangTool::ClangTool(const CompilationDatabase &Compilations,
                      ArrayRef<std::string> SourcePaths)
-    : Files((FileSystemOptions())) {
+    : Files(new FileManager(FileSystemOptions())) {
   ArgsAdjusters.push_back(new ClangStripOutputAdjuster());
   ArgsAdjusters.push_back(new ClangSyntaxOnlyAdjuster());
   for (unsigned I = 0, E = SourcePaths.size(); I != E; ++I) {
@@ -274,7 +305,7 @@
   ArgsAdjusters.clear();
 }
 
-int ClangTool::run(FrontendActionFactory *ActionFactory) {
+int ClangTool::run(ToolAction *Action) {
   // Exists solely for the purpose of lookup of the resource path.
   // This just needs to be some symbol in the binary.
   static int StaticSymbol;
@@ -309,7 +340,7 @@
     DEBUG({
       llvm::dbgs() << "Processing: " << File << ".\n";
     });
-    ToolInvocation Invocation(CommandLine, ActionFactory->create(), &Files);
+    ToolInvocation Invocation(CommandLine, Action, Files.getPtr());
     for (int I = 0, E = MappedFileContents.size(); I != E; ++I) {
       Invocation.mapVirtualFile(MappedFileContents[I].first,
                                 MappedFileContents[I].second);
@@ -323,5 +354,58 @@
   return ProcessingFailed ? 1 : 0;
 }
 
+namespace {
+
+class ASTBuilderAction : public ToolAction {
+  std::vector<ASTUnit *> &ASTs;
+
+public:
+  ASTBuilderAction(std::vector<ASTUnit *> &ASTs) : ASTs(ASTs) {}
+
+  bool runInvocation(CompilerInvocation *Invocation,
+                     FileManager *Files) {
+    // FIXME: This should use the provided FileManager.
+    ASTUnit *AST = ASTUnit::LoadFromCompilerInvocation(
+        Invocation,
+        CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts()));
+    if (!AST)
+      return false;
+
+    ASTs.push_back(AST);
+    return true;
+  }
+};
+
+}
+
+int ClangTool::buildASTs(std::vector<ASTUnit *> &ASTs) {
+  ASTBuilderAction Action(ASTs);
+  return run(&Action);
+}
+
+ASTUnit *buildASTFromCode(const Twine &Code, const Twine &FileName) {
+  return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName);
+}
+
+ASTUnit *buildASTFromCodeWithArgs(const Twine &Code,
+                                  const std::vector<std::string> &Args,
+                                  const Twine &FileName) {
+  SmallString<16> FileNameStorage;
+  StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
+
+  std::vector<ASTUnit *> ASTs;
+  ASTBuilderAction Action(ASTs);
+  ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), &Action, 0);
+
+  SmallString<1024> CodeStorage;
+  Invocation.mapVirtualFile(FileNameRef,
+                            Code.toNullTerminatedStringRef(CodeStorage));
+  if (!Invocation.run())
+    return 0;
+
+  assert(ASTs.size() == 1);
+  return ASTs[0];
+}
+
 } // end namespace tooling
 } // end namespace clang
Index: unittests/Tooling/ToolingTest.cpp
===================================================================
--- unittests/Tooling/ToolingTest.cpp
+++ unittests/Tooling/ToolingTest.cpp
@@ -10,12 +10,14 @@
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclGroup.h"
+#include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "clang/Tooling/Tooling.h"
 #include "gtest/gtest.h"
+#include "llvm/ADT/STLExtras.h"
 #include <string>
 
 namespace clang {
@@ -83,6 +85,18 @@
  private:
   bool *FoundClassDeclX;
 };
+bool FindClassDeclX(ASTUnit *AST) {
+  for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
+                                     e = AST->top_level_end();
+       i != e; ++i) {
+    if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
+      if (Record->getName() == "X") {
+        return true;
+      }
+    }
+  }
+  return false;
+}
 } // end namespace
 
 TEST(runToolOnCode, FindsClassDecl) {
@@ -97,6 +111,16 @@
   EXPECT_FALSE(FoundClassDeclX);
 }
 
+TEST(buildASTFromCode, FindsClassDecl) {
+  OwningPtr<ASTUnit> AST(buildASTFromCode("class X;"));
+  ASSERT_TRUE(AST.get());
+  EXPECT_TRUE(FindClassDeclX(AST.get()));
+
+  AST.reset(buildASTFromCode("class Y;"));
+  ASSERT_TRUE(AST.get());
+  EXPECT_FALSE(FindClassDeclX(AST.get()));
+}
+
 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
   OwningPtr<FrontendActionFactory> Factory(
       newFrontendActionFactory<SyntaxOnlyAction>());
@@ -119,13 +143,15 @@
 }
 
 TEST(ToolInvocation, TestMapVirtualFile) {
-  clang::FileManager Files((clang::FileSystemOptions()));
+  IntrusiveRefCntPtr<clang::FileManager> Files(
+      new clang::FileManager(clang::FileSystemOptions()));
   std::vector<std::string> Args;
   Args.push_back("tool-executable");
   Args.push_back("-Idef");
   Args.push_back("-fsyntax-only");
   Args.push_back("test.cpp");
-  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files);
+  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
+                                            &*Files);
   Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
   Invocation.mapVirtualFile("def/abc", "\n");
   EXPECT_TRUE(Invocation.run());
@@ -136,13 +162,15 @@
   // mapped module.map is found on the include path. In the future, expand this
   // test to run a full modules enabled compilation, so we make sure we can
   // rerun modules compilations with a virtual file system.
-  clang::FileManager Files((clang::FileSystemOptions()));
+  IntrusiveRefCntPtr<clang::FileManager> Files(
+      new clang::FileManager(clang::FileSystemOptions()));
   std::vector<std::string> Args;
   Args.push_back("tool-executable");
   Args.push_back("-Idef");
   Args.push_back("-fsyntax-only");
   Args.push_back("test.cpp");
-  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files);
+  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
+                                            &*Files);
   Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
   Invocation.mapVirtualFile("def/abc", "\n");
   // Add a module.map file in the include directory of our header, so we trigger
@@ -254,5 +282,23 @@
   EXPECT_FALSE(Found);
 }
 
+TEST(ClangToolTest, BuildASTs) {
+  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+
+  std::vector<std::string> Sources;
+  Sources.push_back("/a.cc");
+  Sources.push_back("/b.cc");
+  ClangTool Tool(Compilations, Sources);
+
+  Tool.mapVirtualFile("/a.cc", "void a() {}");
+  Tool.mapVirtualFile("/b.cc", "void b() {}");
+
+  std::vector<ASTUnit *> ASTs;
+  EXPECT_EQ(0, Tool.buildASTs(ASTs));
+  EXPECT_EQ(2u, ASTs.size());
+
+  llvm::DeleteContainerPointers(ASTs);
+}
+
 } // end namespace tooling
 } // end namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to