From 521bf9c529e653ab28896d027352d3e16e2672d5 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar <daniel@zuster.org> Date: Tue, 1 Dec 2009 09:51:01 +0000 Subject: [PATCH] Add ASTUnit::LoadFromCompilerInvocation, which does what it says. Also, add an -ast-from-source option to index-test which allows index-test to run on source files directly. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90223 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/ASTUnit.h | 43 +++++++++----- lib/Frontend/ASTUnit.cpp | 99 +++++++++++++++++++++++++++++-- tools/c-index-test/CMakeLists.txt | 3 + tools/c-index-test/Makefile | 3 +- tools/index-test/CMakeLists.txt | 2 + tools/index-test/Makefile | 3 +- tools/index-test/index-test.cpp | 33 ++++++++++- 7 files changed, 165 insertions(+), 21 deletions(-) diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 7dfabbadd8f..04dc5ed8cc5 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -22,16 +22,17 @@ #include <string> namespace clang { - class FileManager; - class FileEntry; - class SourceManager; - class Diagnostic; - class TextDiagnosticBuffer; - class HeaderSearch; - class TargetInfo; - class Preprocessor; - class ASTContext; - class Decl; +class ASTContext; +class CompilerInvocation; +class Decl; +class Diagnostic; +class FileEntry; +class FileManager; +class HeaderSearch; +class Preprocessor; +class SourceManager; +class TargetInfo; +class TextDiagnosticBuffer; using namespace idx; @@ -92,21 +93,35 @@ public: /// /// \param Filename - The PCH file to load. /// - /// \param diagClient - The diagnostics client to use. Specify NULL + /// \param DiagClient - The diagnostics client to use. Specify NULL /// to use a default client that emits warnings/errors to standard error. /// The ASTUnit objects takes ownership of this object. /// - /// \param FileMgr - The FileManager to use. - /// /// \param ErrMsg - Error message to report if the PCH file could not be /// loaded. /// /// \returns - The initialized ASTUnit or null if the PCH failed to load. static ASTUnit *LoadFromPCHFile(const std::string &Filename, std::string *ErrMsg = 0, - DiagnosticClient *diagClient = NULL, + DiagnosticClient *DiagClient = NULL, bool OnlyLocalDecls = false, bool UseBumpAllocator = false); + + /// LoadFromCompilerInvocation - Create an ASTUnit from a source file, via a + /// CompilerInvocation object. + /// + /// \param CI - The compiler invocation to use; it must have exactly one input + /// source file. + /// + /// \param Diags - The diagnostics engine to use for reporting errors. + // + // FIXME: Move OnlyLocalDecls, UseBumpAllocator to setters on the ASTUnit, we + // shouldn't need to specify them at construction time. + static ASTUnit *LoadFromCompilerInvocation(const CompilerInvocation &CI, + Diagnostic &Diags, + bool OnlyLocalDecls = false, + bool UseBumpAllocator = false); + }; } // namespace clang diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 9e9d216ef76..f647c8a2370 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -14,24 +14,28 @@ #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/PCHReader.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendOptions.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Basic/TargetOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Diagnostic.h" +#include "llvm/LLVMContext.h" #include "llvm/System/Path.h" - using namespace clang; -ASTUnit::ASTUnit(DiagnosticClient *diagClient) : tempFile(false) { +ASTUnit::ASTUnit(DiagnosticClient *diagClient) : tempFile(false) { Diags.setClient(diagClient ? diagClient : new TextDiagnosticBuffer()); } -ASTUnit::~ASTUnit() { +ASTUnit::~ASTUnit() { if (tempFile) llvm::sys::Path(getPCHFileName()).eraseFromDisk(); - + // The ASTUnit object owns the DiagnosticClient. delete Diags.getClient(); } @@ -170,3 +174,90 @@ ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename, return AST.take(); } + +namespace { + +class NullAction : public ASTFrontendAction { + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return new ASTConsumer(); + } + +public: + virtual bool hasCodeCompletionSupport() const { return false; } +}; + +} + +ASTUnit *ASTUnit::LoadFromCompilerInvocation(const CompilerInvocation &CI, + Diagnostic &Diags, + bool OnlyLocalDecls, + bool UseBumpAllocator) { + // Create the compiler instance to use for building the AST. + CompilerInstance Clang(&llvm::getGlobalContext(), false); + llvm::OwningPtr<ASTUnit> AST; + NullAction Act; + + Clang.getInvocation() = CI; + + Clang.setDiagnostics(&Diags); + Clang.setDiagnosticClient(Diags.getClient()); + + // Create the target instance. + Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), + Clang.getTargetOpts())); + if (!Clang.hasTarget()) + goto error; + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); + + assert(Clang.getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang.getFrontendOpts().Inputs[0].first != FrontendOptions::IK_AST && + "FIXME: AST inputs not yet supported here!"); + + // Create the AST unit. + // + // FIXME: Use the provided diagnostic client. + AST.reset(new ASTUnit()); + + // Create a file manager object to provide access to and cache the filesystem. + Clang.setFileManager(&AST->getFileManager()); + + // Create the source manager. + Clang.setSourceManager(&AST->getSourceManager()); + + // Create the preprocessor. + Clang.createPreprocessor(); + + if (!Act.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, + /*IsAST=*/false)) + goto error; + + Act.Execute(); + + // Steal the created context and preprocessor, and take back the source and + // file managers. + AST->Ctx.reset(Clang.takeASTContext()); + AST->PP.reset(Clang.takePreprocessor()); + Clang.takeSourceManager(); + Clang.takeFileManager(); + + Act.EndSourceFile(); + + Clang.takeDiagnosticClient(); + Clang.takeDiagnostics(); + + return AST.take(); + +error: + Clang.takeSourceManager(); + Clang.takeFileManager(); + Clang.takeDiagnosticClient(); + Clang.takeDiagnostics(); + return 0; +} diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 4c724659f6f..4678461de1f 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -4,8 +4,11 @@ set( LLVM_USED_LIBS CIndex clangIndex clangFrontend + clangDriver + clangAnalysis clangSema clangAST + clangParse clangLex clangBasic ) diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile index 81fee40b66e..ae49bf4d7af 100644 --- a/tools/c-index-test/Makefile +++ b/tools/c-index-test/Makefile @@ -19,6 +19,7 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := bitreader mc -USEDLIBS = CIndex.a clangIndex.a clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a +USEDLIBS = CIndex.a clangIndex.a clangFrontend.a clangDriver.a clangAnalysis.a \ + clangSema.a clangAST.a clangParse.a clangLex.a clangBasic.a include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/index-test/CMakeLists.txt b/tools/index-test/CMakeLists.txt index 9c9656a1130..163afc4ac9f 100644 --- a/tools/index-test/CMakeLists.txt +++ b/tools/index-test/CMakeLists.txt @@ -3,8 +3,10 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS clangIndex clangFrontend + clangAnalysis clangSema clangAST + clangParse clangLex clangBasic ) diff --git a/tools/index-test/Makefile b/tools/index-test/Makefile index 76602e1d278..8e7bfe55400 100644 --- a/tools/index-test/Makefile +++ b/tools/index-test/Makefile @@ -19,6 +19,7 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := bitreader mc -USEDLIBS = clangIndex.a clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a +USEDLIBS = clangIndex.a clangFrontend.a clangDriver.a clangAnalysis.a clangSema.a \ + clangAST.a clangParse.a clangLex.a clangBasic.a include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/index-test/index-test.cpp b/tools/index-test/index-test.cpp index fce48edf26e..dd7cbb21642 100644 --- a/tools/index-test/index-test.cpp +++ b/tools/index-test/index-test.cpp @@ -43,6 +43,10 @@ #include "clang/Index/Analyzer.h" #include "clang/Index/Utils.h" #include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/CommandLineSourceLoc.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprObjC.h" @@ -202,9 +206,24 @@ static void ProcessASTLocation(ASTLocation ASTLoc, Indexer &Idxer) { } } +static llvm::cl::opt<bool> +ASTFromSource("ast-from-source", + llvm::cl::desc("Treat the inputs as source files to parse.")); + static llvm::cl::list<std::string> InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input AST files>")); +void CreateCompilerInvocation(const std::string &Filename, + CompilerInvocation &CI, Diagnostic &Diags, + const char *argv0) { + llvm::SmallVector<const char *, 16> Args; + Args.push_back(Filename.c_str()); + + void *MainAddr = (void*) (intptr_t) CreateCompilerInvocation; + CompilerInvocation::CreateFromArgs(CI, Args.data(), Args.data() + Args.size(), + argv0, MainAddr, Diags); +} + int main(int argc, char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc, argv); @@ -215,6 +234,10 @@ int main(int argc, char **argv) { Indexer Idxer(Prog); llvm::SmallVector<TUnit*, 4> TUnits; + TextDiagnosticPrinter DiagClient(llvm::errs(), DiagnosticOptions(), false); + llvm::OwningPtr<Diagnostic> Diags( + CompilerInstance::createDiagnostics(DiagnosticOptions(), argc, argv)); + // If no input was specified, read from stdin. if (InputFilenames.empty()) InputFilenames.push_back("-"); @@ -225,7 +248,15 @@ int main(int argc, char **argv) { std::string ErrMsg; llvm::OwningPtr<ASTUnit> AST; - AST.reset(ASTUnit::LoadFromPCHFile(InFile, &ErrMsg)); + if (ASTFromSource) { + CompilerInvocation CI; + CreateCompilerInvocation(InFile, CI, *Diags, argv[0]); + AST.reset(ASTUnit::LoadFromCompilerInvocation(CI, *Diags)); + if (!AST) + ErrMsg = "unable to create AST"; + } else + AST.reset(ASTUnit::LoadFromPCHFile(InFile, &ErrMsg)); + if (!AST) { llvm::errs() << "[" << InFile << "] Error: " << ErrMsg << '\n'; return 1; -- GitLab