Index: test/Import/empty-struct/Inputs/S.c =================================================================== --- test/Import/empty-struct/Inputs/S.c +++ test/Import/empty-struct/Inputs/S.c @@ -0,0 +1,2 @@ +struct S { +}; Index: test/Import/empty-struct/test.c =================================================================== --- test/Import/empty-struct/test.c +++ test/Import/empty-struct/test.c @@ -0,0 +1,5 @@ +// RUN: clang-import-test -import %S/Inputs/S.c -expression %s +void expr() { + struct S MyS; + void *MyPtr = &MyS; +} Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_subdirectory(clang-format) add_clang_subdirectory(clang-format-vs) add_clang_subdirectory(clang-fuzzer) +add_clang_subdirectory(clang-import-test) add_clang_subdirectory(clang-offload-bundler) add_clang_subdirectory(c-index-test) Index: tools/clang-import-test/CMakeLists.txt =================================================================== --- tools/clang-import-test/CMakeLists.txt +++ tools/clang-import-test/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + support +) + +add_clang_tool(clang-import-test + clang-import-test.cpp + ) + +set(CLANG_IMPORT_TEST_LIB_DEPS + clangAST + clangBasic + clangCodeGen + clangFrontend + ) + +target_link_libraries(clang-import-test + ${CLANG_IMPORT_TEST_LIB_DEPS} + ) \ No newline at end of file Index: tools/clang-import-test/clang-import-test.cpp =================================================================== --- tools/clang-import-test/clang-import-test.cpp +++ tools/clang-import-test/clang-import-test.cpp @@ -0,0 +1,347 @@ +//===-- 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/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); + +static llvm::cl::opt LogLookups( + "log-lookups", + llvm::cl::desc("Print each lookup performed on behalf of the expression")); + +namespace { + +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 void finish() override { Passthrough->finish(); } + + virtual bool IncludeInDiagnosticCounts() const override { + return Passthrough->IncludeInDiagnosticCounts(); + } + +private: + static void PrintSourceForLocation(const SourceLocation &Loc, + SourceManager &SM) { + bool Invalid = true; + const char *LocData = SM.getCharacterData(Loc, &Invalid); + if (Invalid) { + return; + } + unsigned LocColumn = SM.getSpellingColumnNumber(Loc, &Invalid) - 1; + if (Invalid) { + return; + } + FileID FID = SM.getFileID(Loc); + llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, &Invalid); + if (Invalid) { + return; + } + + assert(LocData >= Buffer->getBufferStart() && + LocData < Buffer->getBufferEnd()); + + const char *LineBegin = LocData - LocColumn; + + if (LineBegin < Buffer->getBufferStart()) { + LineBegin = Buffer->getBufferStart(); + } + + const char *LineEnd = nullptr; + + for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' && + LineEnd < Buffer->getBufferEnd(); + ++LineEnd) + ; + + std::string LineString(LineBegin, LineEnd - LineBegin); + + fprintf(stderr, "%s\n", LineString.c_str()); + std::string Space(LocColumn, ' '); + fprintf(stderr, "%s^\n", Space.c_str()); + } + + 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() << ": "; + } + + SmallVector DiagText; + Info.FormatDiagnostic(DiagText); + DiagText.push_back('\0'); + llvm::errs() << DiagText.data() << '\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.str().c_str() << '\n'; + } + } + } + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + } +}; + +class TestExternalASTSource : public ExternalASTSource { +private: llvm::ArrayRef ImportCIs; + std::map> ForwardImporters; + std::map> ReverseImporters; + +public: + TestExternalASTSource(CompilerInstance &ExpressionCI, + llvm::ArrayRef ImportCIs) + : ImportCIs(ImportCIs) { + for (CompilerInstance *ImportCI : ImportCIs) { + const bool MinimalImport = true; + ForwardImporters.emplace(std::make_pair( + ImportCI, + llvm::make_unique( + ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), + ImportCI->getASTContext(), ImportCI->getFileManager(), + MinimalImport))); + ReverseImporters.emplace(std::make_pair( + ImportCI, + llvm::make_unique( + ImportCI->getASTContext(), ImportCI->getFileManager(), + ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), + MinimalImport))); + } + } + + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override { + std::vector Decls; + + if (llvm::isa(DC)) { + for (CompilerInstance *I : ImportCIs) { + DeclarationName FromName = ReverseImporters[I]->Import(Name); + DeclContextLookupResult Result = + I->getASTContext().getTranslationUnitDecl()->lookup(FromName); + for (NamedDecl *FromD : Result) { + NamedDecl *D = llvm::cast(ForwardImporters[I]->Import(FromD)); + Decls.push_back(D); + } + } + } + if (LogLookups) { + if (auto ND = llvm::dyn_cast(DC)) { + llvm::outs() << "[log-lookups] (in " << DC->getDeclKindName() << " " + << ND->getName() << ") " << Name; + } else { + llvm::outs() << "[log-lookups] (in a " << DC->getDeclKindName() << ") " + << Name; + } + if (Decls.empty()) { + llvm::outs() << " -> {}" << '\n'; + } else { + llvm::outs() << +" -> {" << '\n'; + for (NamedDecl *Decl : Decls) { + llvm::outs() << +"[log-lookups] . " << *Decl << '\n'; + } + llvm::outs() << +"[log-lookups] }" << '\n'; + } + } + if (Decls.empty()) { + return false; + } else { + SetExternalVisibleDeclsForName(DC, Name, Decls); + return true; + } + } + + void + FindExternalLexicalDecls(const DeclContext *DC, + llvm::function_ref IsKindWeWant, + SmallVectorImpl &Result) override { + + } +}; + +std::unique_ptr BuildCompilerInstance() { + auto Ins = llvm::make_unique(); + auto DC = llvm::make_unique(); + const bool ShouldOwnClient = true; + Ins->createDiagnostics(DC.release(), ShouldOwnClient); + + auto Inv = llvm::make_unique(); + + std::vector ClangArgv(ClangArgs.size()); + std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), + [](std::string &s) -> const char * { return s.data(); }); + + 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(Inv.release()); + + 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; +} + +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(); +} + +std::unique_ptr BuildCodeGen(CompilerInstance &CI, + llvm::LLVMContext &LLVMCtx) { + std::string ModuleName("$__module"); + return std::unique_ptr(CreateLLVMCodeGen( + CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(), + CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx)); +} + +bool ParseSource(const std::string &Path, CompilerInstance &CI, + CodeGenerator &CG) { + SourceManager &SM = CI.getSourceManager(); + const FileEntry *FE = CI.getFileManager().getFile(Path); + if (!FE) { + llvm::errs() << "Couldn't open " << Path << '\n'; + return false; + } + SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User)); + ParseAST(CI.getPreprocessor(), &CG, CI.getASTContext()); + return true; +} + +bool Parse(const std::string &Path, std::unique_ptr &CI, + llvm::ArrayRef Imports) { + CI = BuildCompilerInstance(); + auto ST = llvm::make_unique(); + auto BC = llvm::make_unique(); + std::unique_ptr AST = BuildASTContext(*CI, *ST, *BC); + CI->setASTContext(AST.release()); + AddExternalSource(*CI, Imports); + + auto LLVMCtx = llvm::make_unique(); + std::unique_ptr CG = BuildCodeGen(*CI, *LLVMCtx); + CG->Initialize(CI->getASTContext()); + + CI->getDiagnosticClient().BeginSourceFile(CI->getLangOpts(), + &CI->getPreprocessor()); + if (!ParseSource(Path, *CI, *CG)) { + return false; + } + CI->getDiagnosticClient().EndSourceFile(); + return (CI->getDiagnosticClient().getNumErrors() == 0); +} +} + +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(Imports.size()); + for (auto I : llvm::zip(Imports, ImportCIs)) { + if (!Parse(std::get<0>(I), std::get<1>(I), {})) { + exit(-1); + } + } + std::vector UnownedCIs(Imports.size()); + std::transform(ImportCIs.begin(), ImportCIs.end(), UnownedCIs.begin(), + std::mem_fn(&std::unique_ptr::get)); + std::unique_ptr ExpressionCI; + if (!Parse(Expression, ExpressionCI, UnownedCIs)) { + exit(-1); + } + return 0; +}