1 //===-- import-test.cpp - ASTImporter/ExternalASTSource testbed -----------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/ASTImporter.h"
12 #include "clang/AST/DeclObjC.h"
13 #include "clang/AST/ExternalASTMerger.h"
14 #include "clang/Basic/Builtins.h"
15 #include "clang/Basic/IdentifierTable.h"
16 #include "clang/Basic/SourceLocation.h"
17 #include "clang/Basic/TargetInfo.h"
18 #include "clang/Basic/TargetOptions.h"
19 #include "clang/CodeGen/ModuleBuilder.h"
20 #include "clang/Driver/Types.h"
21 #include "clang/Frontend/ASTConsumers.h"
22 #include "clang/Frontend/CompilerInstance.h"
23 #include "clang/Frontend/MultiplexConsumer.h"
24 #include "clang/Frontend/TextDiagnosticBuffer.h"
25 #include "clang/Lex/Lexer.h"
26 #include "clang/Lex/Preprocessor.h"
27 #include "clang/Parse/ParseAST.h"
29 #include "llvm/IR/LLVMContext.h"
30 #include "llvm/IR/Module.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/Error.h"
33 #include "llvm/Support/Host.h"
34 #include "llvm/Support/Signals.h"
39 using namespace clang;
41 static llvm::cl::opt<std::string> Expression(
42 "expression", llvm::cl::Required,
43 llvm::cl::desc("Path to a file containing the expression to parse"));
45 static llvm::cl::list<std::string>
46 Imports("import", llvm::cl::ZeroOrMore,
47 llvm::cl::desc("Path to a file containing declarations to import"));
49 static llvm::cl::opt<bool>
50 Direct("direct", llvm::cl::Optional,
51 llvm::cl::desc("Use the parsed declarations without indirection"));
53 static llvm::cl::opt<bool> UseOrigins(
54 "use-origins", llvm::cl::Optional,
56 "Use DeclContext origin information for more accurate lookups"));
58 static llvm::cl::list<std::string>
59 ClangArgs("Xcc", llvm::cl::ZeroOrMore,
60 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
61 llvm::cl::CommaSeparated);
63 static llvm::cl::opt<std::string>
64 Input("x", llvm::cl::Optional,
65 llvm::cl::desc("The language to parse (default: c++)"),
66 llvm::cl::init("c++"));
68 static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
69 llvm::cl::desc("Dump combined AST"));
71 static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
72 llvm::cl::desc("Dump IR from final parse"));
74 namespace init_convenience {
75 class TestDiagnosticConsumer : public DiagnosticConsumer {
77 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
78 const LangOptions *LangOpts = nullptr;
81 TestDiagnosticConsumer()
82 : Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {}
84 virtual void BeginSourceFile(const LangOptions &LangOpts,
85 const Preprocessor *PP = nullptr) override {
86 this->LangOpts = &LangOpts;
87 return Passthrough->BeginSourceFile(LangOpts, PP);
90 virtual void EndSourceFile() override {
91 this->LangOpts = nullptr;
92 Passthrough->EndSourceFile();
95 virtual bool IncludeInDiagnosticCounts() const override {
96 return Passthrough->IncludeInDiagnosticCounts();
100 static void PrintSourceForLocation(const SourceLocation &Loc,
102 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
104 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
105 FileID FID = SM.getFileID(Loc);
106 llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr);
108 assert(LocData >= Buffer->getBufferStart() &&
109 LocData < Buffer->getBufferEnd());
111 const char *LineBegin = LocData - LocColumn;
113 assert(LineBegin >= Buffer->getBufferStart());
115 const char *LineEnd = nullptr;
117 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
118 LineEnd < Buffer->getBufferEnd();
122 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
124 llvm::errs() << LineString << '\n';
125 llvm::errs().indent(LocColumn);
127 llvm::errs() << '\n';
130 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
131 const Diagnostic &Info) override {
132 if (Info.hasSourceManager() && LangOpts) {
133 SourceManager &SM = Info.getSourceManager();
135 if (Info.getLocation().isValid()) {
136 Info.getLocation().print(llvm::errs(), SM);
137 llvm::errs() << ": ";
140 SmallString<16> DiagText;
141 Info.FormatDiagnostic(DiagText);
142 llvm::errs() << DiagText << '\n';
144 if (Info.getLocation().isValid()) {
145 PrintSourceForLocation(Info.getLocation(), SM);
148 for (const CharSourceRange &Range : Info.getRanges()) {
150 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
152 llvm::errs() << Ref << '\n';
156 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
160 std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
161 auto Ins = llvm::make_unique<CompilerInstance>();
162 auto DC = llvm::make_unique<TestDiagnosticConsumer>();
163 const bool ShouldOwnClient = true;
164 Ins->createDiagnostics(DC.release(), ShouldOwnClient);
166 auto Inv = llvm::make_unique<CompilerInvocation>();
168 std::vector<const char *> ClangArgv(ClangArgs.size());
169 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
170 [](const std::string &s) -> const char * { return s.data(); });
171 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
172 &ClangArgv.data()[ClangArgv.size()],
173 Ins->getDiagnostics());
176 using namespace driver::types;
177 ID Id = lookupTypeForTypeSpecifier(Input.c_str());
178 assert(Id != TY_INVALID);
180 Inv->getLangOpts()->CPlusPlus = true;
181 Inv->getLangOpts()->CPlusPlus11 = true;
182 Inv->getHeaderSearchOpts().UseLibcxx = true;
185 Inv->getLangOpts()->ObjC = 1;
188 Inv->getLangOpts()->Bool = true;
189 Inv->getLangOpts()->WChar = true;
190 Inv->getLangOpts()->Blocks = true;
191 Inv->getLangOpts()->DebuggerSupport = true;
192 Inv->getLangOpts()->SpellChecking = false;
193 Inv->getLangOpts()->ThreadsafeStatics = false;
194 Inv->getLangOpts()->AccessControl = false;
195 Inv->getLangOpts()->DollarIdents = true;
196 Inv->getLangOpts()->Exceptions = true;
197 Inv->getLangOpts()->CXXExceptions = true;
198 // Needed for testing dynamic_cast.
199 Inv->getLangOpts()->RTTI = true;
200 Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
201 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
203 Ins->setInvocation(std::move(Inv));
205 TargetInfo *TI = TargetInfo::CreateTargetInfo(
206 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
208 Ins->getTarget().adjust(Ins->getLangOpts());
209 Ins->createFileManager();
210 Ins->createSourceManager(Ins->getFileManager());
211 Ins->createPreprocessor(TU_Complete);
216 std::unique_ptr<ASTContext>
217 BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
218 auto AST = llvm::make_unique<ASTContext>(
219 CI.getLangOpts(), CI.getSourceManager(),
220 CI.getPreprocessor().getIdentifierTable(), ST, BC);
221 AST->InitBuiltinTypes(CI.getTarget());
225 std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
226 llvm::LLVMContext &LLVMCtx) {
227 StringRef ModuleName("$__module");
228 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
229 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
230 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
232 } // namespace init_convenience
236 /// A container for a CompilerInstance (possibly with an ExternalASTMerger
237 /// attached to its ASTContext).
239 /// Provides an accessor for the DeclContext origins associated with the
240 /// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
243 /// This is the main unit of parsed source code maintained by clang-import-test.
244 struct CIAndOrigins {
245 using OriginMap = clang::ExternalASTMerger::OriginMap;
246 std::unique_ptr<CompilerInstance> CI;
248 ASTContext &getASTContext() { return CI->getASTContext(); }
249 FileManager &getFileManager() { return CI->getFileManager(); }
250 const OriginMap &getOriginMap() {
251 static const OriginMap EmptyOriginMap{};
252 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
253 return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
254 return EmptyOriginMap;
256 DiagnosticConsumer &getDiagnosticClient() {
257 return CI->getDiagnosticClient();
259 CompilerInstance &getCompilerInstance() { return *CI; }
262 void AddExternalSource(CIAndOrigins &CI,
263 llvm::MutableArrayRef<CIAndOrigins> Imports) {
264 ExternalASTMerger::ImporterTarget Target(
265 {CI.getASTContext(), CI.getFileManager()});
266 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
267 for (CIAndOrigins &Import : Imports)
268 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
269 Import.getOriginMap()});
270 auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
271 CI.getASTContext().setExternalSource(ES.release());
272 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
275 CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
276 CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
277 auto ST = llvm::make_unique<SelectorTable>();
278 auto BC = llvm::make_unique<Builtin::Context>();
279 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
280 IndirectCI.getCompilerInstance(), *ST, *BC);
281 IndirectCI.getCompilerInstance().setASTContext(AST.release());
282 AddExternalSource(IndirectCI, CI);
286 llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
287 ASTConsumer &Consumer) {
288 SourceManager &SM = CI.getSourceManager();
289 const FileEntry *FE = CI.getFileManager().getFile(Path);
291 return llvm::make_error<llvm::StringError>(
292 llvm::Twine("Couldn't open ", Path), std::error_code());
294 SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
295 ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
296 return llvm::Error::success();
299 llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
300 llvm::MutableArrayRef<CIAndOrigins> Imports,
301 bool ShouldDumpAST, bool ShouldDumpIR) {
302 CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
303 auto ST = llvm::make_unique<SelectorTable>();
304 auto BC = llvm::make_unique<Builtin::Context>();
305 std::unique_ptr<ASTContext> AST =
306 init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
307 CI.getCompilerInstance().setASTContext(AST.release());
309 AddExternalSource(CI, Imports);
311 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
313 auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
314 ASTConsumers.push_back(
315 init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
316 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
319 ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/,
320 "", true, false, false));
322 CI.getDiagnosticClient().BeginSourceFile(
323 CI.getCompilerInstance().getLangOpts(),
324 &CI.getCompilerInstance().getPreprocessor());
325 MultiplexConsumer Consumers(std::move(ASTConsumers));
326 Consumers.Initialize(CI.getASTContext());
328 if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
329 return std::move(PE);
330 CI.getDiagnosticClient().EndSourceFile();
332 CG.GetModule()->print(llvm::outs(), nullptr);
333 if (CI.getDiagnosticClient().getNumErrors())
334 return llvm::make_error<llvm::StringError>(
335 "Errors occurred while parsing the expression.", std::error_code());
336 return std::move(CI);
339 void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
340 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
341 for (CIAndOrigins &Import : Imports)
342 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
343 Import.getOriginMap()});
344 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
345 auto *Merger = static_cast<ExternalASTMerger *>(Source);
346 Merger->RemoveSources(Sources);
351 int main(int argc, const char **argv) {
352 const bool DisableCrashReporting = true;
353 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
354 llvm::cl::ParseCommandLineOptions(argc, argv);
355 std::vector<CIAndOrigins> ImportCIs;
356 for (auto I : Imports) {
357 llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
358 if (auto E = ImportCI.takeError()) {
359 llvm::errs() << llvm::toString(std::move(E));
362 ImportCIs.push_back(std::move(*ImportCI));
364 std::vector<CIAndOrigins> IndirectCIs;
365 if (!Direct || UseOrigins) {
366 for (auto &ImportCI : ImportCIs) {
367 CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
368 IndirectCIs.push_back(std::move(IndirectCI));
372 for (auto &ImportCI : ImportCIs)
373 IndirectCIs.push_back(std::move(ImportCI));
374 llvm::Expected<CIAndOrigins> ExpressionCI =
375 Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
377 if (auto E = ExpressionCI.takeError()) {
378 llvm::errs() << llvm::toString(std::move(E));
381 Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);