spyffe updated this revision to Diff 79605. spyffe marked 2 inline comments as done. spyffe added a comment.
Updated to reflect Aleksei's comments. Repository: rL LLVM https://reviews.llvm.org/D27180 Files: test/Import/empty-struct/Inputs/S.c test/Import/empty-struct/test.c tools/CMakeLists.txt tools/clang-import-test/CMakeLists.txt tools/clang-import-test/clang-import-test.cpp
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,342 @@ +//===-- 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 <memory> +#include <string> + +using namespace clang; + +static llvm::cl::opt<std::string> Expression( + "expression", llvm::cl::Required, + llvm::cl::desc("Path to a file containing the expression to parse")); + +static llvm::cl::list<std::string> + Imports("import", llvm::cl::ZeroOrMore, + llvm::cl::desc("Path to a file containing declarations to import")); + +static llvm::cl::list<std::string> + ClangArgs("-Xcc", llvm::cl::ZeroOrMore, + llvm::cl::desc("Argument to pass to the CompilerInvocation"), + llvm::cl::CommaSeparated); + +static llvm::cl::opt<bool> LogLookups( + "log-lookups", + llvm::cl::desc("Print each lookup performed on behalf of the expression")); + +namespace { + +class TestDiagnosticConsumer : public DiagnosticConsumer { +private: + std::unique_ptr<TextDiagnosticBuffer> Passthrough; + const LangOptions *LangOpts = nullptr; + +public: + TestDiagnosticConsumer() + : Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {} + + 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) + ; + + llvm::StringRef LineString(LineBegin, LineEnd - LineBegin); + + llvm::errs() << LineString << '\n'; + std::string Space(LocColumn, ' '); + llvm::errs() << Space.c_str() << '\n'; + } + + 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.str() << '\n'; + } + } + } + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + } +}; + +class TestExternalASTSource : public ExternalASTSource { +private: + llvm::ArrayRef<CompilerInstance *> ImportCIs; + std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ForwardImporters; + std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ReverseImporters; + +public: + TestExternalASTSource(CompilerInstance &ExpressionCI, + llvm::ArrayRef<CompilerInstance *> ImportCIs) + : ImportCIs(ImportCIs) { + for (CompilerInstance *ImportCI : ImportCIs) { + const bool MinimalImport = true; + ForwardImporters[ImportCI] = llvm::make_unique<ASTImporter>( + ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), + ImportCI->getASTContext(), ImportCI->getFileManager(), MinimalImport); + ReverseImporters[ImportCI] = llvm::make_unique<ASTImporter>( + ImportCI->getASTContext(), ImportCI->getFileManager(), + ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), + MinimalImport); + } + } + + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override { + llvm::SmallVector<NamedDecl *, 1> Decls; + + if (isa<TranslationUnitDecl>(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<NamedDecl>(ForwardImporters[I]->Import(FromD)); + Decls.push_back(D); + } + } + } + if (LogLookups) { + if (auto ND = llvm::dyn_cast<NamedDecl>(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<bool(Decl::Kind)> IsKindWeWant, + SmallVectorImpl<Decl *> &Result) override {} +}; + +std::unique_ptr<CompilerInstance> BuildCompilerInstance() { + auto Ins = llvm::make_unique<CompilerInstance>(); + auto DC = llvm::make_unique<TestDiagnosticConsumer>(); + const bool ShouldOwnClient = true; + Ins->createDiagnostics(DC.release(), ShouldOwnClient); + + auto Inv = llvm::make_unique<CompilerInvocation>(); + + std::vector<const char *> ClangArgv(ClangArgs.size()); + std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), + [](const 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<ASTContext> +BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) { + auto AST = llvm::make_unique<ASTContext>( + CI.getLangOpts(), CI.getSourceManager(), + CI.getPreprocessor().getIdentifierTable(), ST, BC); + AST->InitBuiltinTypes(CI.getTarget()); + return AST; +} + +void AddExternalSource(CompilerInstance &CI, + llvm::ArrayRef<CompilerInstance *> Imports) { + ASTContext &AST = CI.getASTContext(); + auto ES = llvm::make_unique<TestExternalASTSource>(CI, Imports); + AST.setExternalSource(ES.release()); + AST.getTranslationUnitDecl()->setHasExternalVisibleStorage(); +} + +std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI, + llvm::LLVMContext &LLVMCtx) { + std::string ModuleName("$__module"); + return std::unique_ptr<CodeGenerator>(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<CompilerInstance> &CI, + llvm::ArrayRef<CompilerInstance *> Imports) { + CI = BuildCompilerInstance(); + auto ST = llvm::make_unique<SelectorTable>(); + auto BC = llvm::make_unique<Builtin::Context>(); + std::unique_ptr<ASTContext> AST = BuildASTContext(*CI, *ST, *BC); + CI->setASTContext(AST.release()); + AddExternalSource(*CI, Imports); + + auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>(); + std::unique_ptr<CodeGenerator> 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); +} +} // 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<std::unique_ptr<CompilerInstance>> ImportCIs(Imports.size()); + for (auto I : llvm::zip(Imports, ImportCIs)) { + if (!Parse(std::get<0>(I), std::get<1>(I), {})) { + exit(-1); + } + } + std::vector<CompilerInstance *> UnownedCIs(Imports.size()); + std::transform(ImportCIs.begin(), ImportCIs.end(), UnownedCIs.begin(), + std::mem_fn(&std::unique_ptr<CompilerInstance>::get)); + std::unique_ptr<CompilerInstance> ExpressionCI; + if (!Parse(Expression, ExpressionCI, UnownedCIs)) { + exit(-1); + } + return 0; +} 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} + ) 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: 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: 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 { +};
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits