//===-- import-test.cpp - ASTImporter/ExternalASTSource testbed -----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" #include "clang/Parse/ParseAST.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/Host.h" #include "llvm/Support/Signals.h" #include #include using namespace clang; static llvm::cl::opt Expression( "expression", llvm::cl::Required, llvm::cl::desc("Path to a file containing the expression to parse")); static llvm::cl::list Imports("import", llvm::cl::ZeroOrMore, llvm::cl::desc("Path to a file containing declarations to import")); static llvm::cl::list ClangArgs("Xcc", llvm::cl::ZeroOrMore, llvm::cl::desc("Argument to pass to the CompilerInvocation"), llvm::cl::CommaSeparated); namespace init_convenience { class TestDiagnosticConsumer : public DiagnosticConsumer { private: std::unique_ptr Passthrough; const LangOptions *LangOpts = nullptr; public: TestDiagnosticConsumer() : Passthrough(llvm::make_unique()) {} virtual void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP = nullptr) override { this->LangOpts = &LangOpts; return Passthrough->BeginSourceFile(LangOpts, PP); } virtual void EndSourceFile() override { this->LangOpts = nullptr; Passthrough->EndSourceFile(); } virtual bool IncludeInDiagnosticCounts() const override { return Passthrough->IncludeInDiagnosticCounts(); } private: static void PrintSourceForLocation(const SourceLocation &Loc, SourceManager &SM) { const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr); unsigned LocColumn = SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1; FileID FID = SM.getFileID(Loc); llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr); assert(LocData >= Buffer->getBufferStart() && LocData < Buffer->getBufferEnd()); const char *LineBegin = LocData - LocColumn; assert(LineBegin >= Buffer->getBufferStart()); const char *LineEnd = nullptr; for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' && LineEnd < Buffer->getBufferEnd(); ++LineEnd) ; llvm::StringRef LineString(LineBegin, LineEnd - LineBegin); llvm::errs() << LineString << '\n'; llvm::errs().indent(LocColumn); llvm::errs() << '^'; } virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override { if (Info.hasSourceManager() && LangOpts) { SourceManager &SM = Info.getSourceManager(); if (Info.getLocation().isValid()) { Info.getLocation().print(llvm::errs(), SM); llvm::errs() << ": "; } SmallString<16> DiagText; Info.FormatDiagnostic(DiagText); llvm::errs() << DiagText << '\n'; if (Info.getLocation().isValid()) { PrintSourceForLocation(Info.getLocation(), SM); } for (const CharSourceRange &Range : Info.getRanges()) { bool Invalid = true; StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid); if (!Invalid) { llvm::errs() << Ref << '\n'; } } } DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); } }; std::unique_ptr BuildCompilerInstance(ArrayRef ClangArgv) { auto Ins = llvm::make_unique(); auto DC = llvm::make_unique(); const bool ShouldOwnClient = true; Ins->createDiagnostics(DC.release(), ShouldOwnClient); auto Inv = llvm::make_unique(); CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(), &ClangArgv.data()[ClangArgv.size()], Ins->getDiagnostics()); Inv->getLangOpts()->CPlusPlus = true; Inv->getLangOpts()->CPlusPlus11 = true; Inv->getHeaderSearchOpts().UseLibcxx = true; Inv->getLangOpts()->Bool = true; Inv->getLangOpts()->WChar = true; Inv->getLangOpts()->Blocks = true; Inv->getLangOpts()->DebuggerSupport = true; Inv->getLangOpts()->SpellChecking = false; Inv->getLangOpts()->ThreadsafeStatics = false; Inv->getLangOpts()->AccessControl = false; Inv->getLangOpts()->DollarIdents = true; Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo); Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); Ins->setInvocation(std::move(Inv)); TargetInfo *TI = TargetInfo::CreateTargetInfo( Ins->getDiagnostics(), Ins->getInvocation().TargetOpts); Ins->setTarget(TI); Ins->getTarget().adjust(Ins->getLangOpts()); Ins->createFileManager(); Ins->createSourceManager(Ins->getFileManager()); Ins->createPreprocessor(TU_Complete); return Ins; } std::unique_ptr BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) { auto AST = llvm::make_unique( CI.getLangOpts(), CI.getSourceManager(), CI.getPreprocessor().getIdentifierTable(), ST, BC); AST->InitBuiltinTypes(CI.getTarget()); return AST; } std::unique_ptr BuildCodeGen(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx) { StringRef ModuleName("$__module"); return std::unique_ptr(CreateLLVMCodeGen( CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx)); } } // end namespace namespace { class TestExternalASTSource : public ExternalASTSource { private: llvm::ArrayRef> ImportCIs; std::map> ForwardImporters; std::map> ReverseImporters; public: TestExternalASTSource( CompilerInstance &ExpressionCI, llvm::ArrayRef> ImportCIs) : ImportCIs(ImportCIs) { for (const std::unique_ptr &ImportCI : ImportCIs) { ForwardImporters[ImportCI.get()] = llvm::make_unique( ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), ImportCI->getASTContext(), ImportCI->getFileManager(), /*MinimalImport=*/true); ReverseImporters[ImportCI.get()] = llvm::make_unique( ImportCI->getASTContext(), ImportCI->getFileManager(), ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), /*MinimalImport=*/true); } } bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) override { llvm::SmallVector Decls; if (isa(DC)) { for (const std::unique_ptr &I : ImportCIs) { DeclarationName FromName = ReverseImporters[I.get()]->Import(Name); DeclContextLookupResult Result = I->getASTContext().getTranslationUnitDecl()->lookup(FromName); for (NamedDecl *FromD : Result) { NamedDecl *D = llvm::cast(ForwardImporters[I.get()]->Import(FromD)); Decls.push_back(D); } } } if (Decls.empty()) { return false; } else { SetExternalVisibleDeclsForName(DC, Name, Decls); return true; } } }; void AddExternalSource( CompilerInstance &CI, llvm::ArrayRef> Imports) { ASTContext &AST = CI.getASTContext(); auto ES = llvm::make_unique(CI, Imports); AST.setExternalSource(ES.release()); AST.getTranslationUnitDecl()->setHasExternalVisibleStorage(); } llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, CodeGenerator &CG) { SourceManager &SM = CI.getSourceManager(); const FileEntry *FE = CI.getFileManager().getFile(Path); if (!FE) { return llvm::make_error( llvm::Twine("Couldn't open ", Path), std::error_code()); } SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User)); ParseAST(CI.getPreprocessor(), &CG, CI.getASTContext()); return llvm::Error::success(); } llvm::Expected> Parse(const std::string &Path, llvm::ArrayRef> Imports) { std::vector ClangArgv(ClangArgs.size()); std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), [](const std::string &s) -> const char * { return s.data(); }); std::unique_ptr CI = init_convenience::BuildCompilerInstance(ClangArgv); auto ST = llvm::make_unique(); auto BC = llvm::make_unique(); std::unique_ptr AST = init_convenience::BuildASTContext(*CI, *ST, *BC); CI->setASTContext(AST.release()); AddExternalSource(*CI, Imports); auto LLVMCtx = llvm::make_unique(); std::unique_ptr CG = init_convenience::BuildCodeGen(*CI, *LLVMCtx); CG->Initialize(CI->getASTContext()); CI->getDiagnosticClient().BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor()); if (llvm::Error PE = ParseSource(Path, *CI, *CG)) { return std::move(PE); } CI->getDiagnosticClient().EndSourceFile(); if (CI->getDiagnosticClient().getNumErrors()) { return llvm::make_error( "Errors occured while parsing the expression.", std::error_code()); } else { return std::move(CI); } } } // end namespace int main(int argc, const char **argv) { const bool DisableCrashReporting = true; llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting); llvm::cl::ParseCommandLineOptions(argc, argv); std::vector> ImportCIs; for (auto I : Imports) { llvm::Expected> ImportCI = Parse(I, {}); if (auto E = ImportCI.takeError()) { llvm::errs() << llvm::toString(std::move(E)); exit(-1); } else { ImportCIs.push_back(std::move(*ImportCI)); } } llvm::Expected> ExpressionCI = Parse(Expression, ImportCIs); if (auto E = ExpressionCI.takeError()) { llvm::errs() << llvm::toString(std::move(E)); exit(-1); } else { return 0; } }