blob: 106f3d1d150d0ec27c8a9e5b6899328f2ba2a41e [file] [log] [blame]
Sean Callanan7d982502016-12-22 20:03:14 +00001//===-- import-test.cpp - ASTImporter/ExternalASTSource testbed -----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/ASTImporter.h"
Sean Callananb7160ca2017-04-11 19:33:35 +000012#include "clang/AST/DeclObjC.h"
13#include "clang/AST/ExternalASTMerger.h"
Sean Callanan7d982502016-12-22 20:03:14 +000014#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"
Sean Callanan1eac8792017-07-25 19:54:22 +000020#include "clang/Driver/Types.h"
Lang Hames19e07e12017-06-20 21:06:00 +000021#include "clang/Frontend/ASTConsumers.h"
Sean Callanan7d982502016-12-22 20:03:14 +000022#include "clang/Frontend/CompilerInstance.h"
Lang Hames19e07e12017-06-20 21:06:00 +000023#include "clang/Frontend/MultiplexConsumer.h"
Sean Callanan7d982502016-12-22 20:03:14 +000024#include "clang/Frontend/TextDiagnosticBuffer.h"
25#include "clang/Lex/Lexer.h"
26#include "clang/Lex/Preprocessor.h"
27#include "clang/Parse/ParseAST.h"
28
29#include "llvm/IR/LLVMContext.h"
Sean Callanan2b3a54b2017-08-07 22:27:30 +000030#include "llvm/IR/Module.h"
Sean Callanan7d982502016-12-22 20:03:14 +000031#include "llvm/Support/CommandLine.h"
32#include "llvm/Support/Error.h"
33#include "llvm/Support/Host.h"
34#include "llvm/Support/Signals.h"
35
36#include <memory>
37#include <string>
38
39using namespace clang;
40
41static llvm::cl::opt<std::string> Expression(
42 "expression", llvm::cl::Required,
43 llvm::cl::desc("Path to a file containing the expression to parse"));
44
45static llvm::cl::list<std::string>
46 Imports("import", llvm::cl::ZeroOrMore,
47 llvm::cl::desc("Path to a file containing declarations to import"));
48
Sean Callanan9092d472017-05-13 00:46:33 +000049static llvm::cl::opt<bool>
50 Direct("direct", llvm::cl::Optional,
Sean Callanan967d4382017-09-27 19:57:58 +000051 llvm::cl::desc("Use the parsed declarations without indirection"));
52
Vedant Kumar3df72582018-02-07 21:17:22 +000053static llvm::cl::opt<bool> UseOrigins(
54 "use-origins", llvm::cl::Optional,
55 llvm::cl::desc(
56 "Use DeclContext origin information for more accurate lookups"));
Sean Callanan9092d472017-05-13 00:46:33 +000057
Sean Callanan7d982502016-12-22 20:03:14 +000058static 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);
62
Sean Callanan1eac8792017-07-25 19:54:22 +000063static 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++"));
67
Sean Callanan967d4382017-09-27 19:57:58 +000068static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
69 llvm::cl::desc("Dump combined AST"));
Lang Hames19e07e12017-06-20 21:06:00 +000070
Sean Callanan967d4382017-09-27 19:57:58 +000071static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
72 llvm::cl::desc("Dump IR from final parse"));
Sean Callanan2b3a54b2017-08-07 22:27:30 +000073
Sean Callanan7d982502016-12-22 20:03:14 +000074namespace init_convenience {
75class TestDiagnosticConsumer : public DiagnosticConsumer {
76private:
77 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
78 const LangOptions *LangOpts = nullptr;
79
80public:
81 TestDiagnosticConsumer()
82 : Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {}
83
84 virtual void BeginSourceFile(const LangOptions &LangOpts,
85 const Preprocessor *PP = nullptr) override {
86 this->LangOpts = &LangOpts;
87 return Passthrough->BeginSourceFile(LangOpts, PP);
88 }
89
90 virtual void EndSourceFile() override {
91 this->LangOpts = nullptr;
92 Passthrough->EndSourceFile();
93 }
94
95 virtual bool IncludeInDiagnosticCounts() const override {
96 return Passthrough->IncludeInDiagnosticCounts();
97 }
98
99private:
100 static void PrintSourceForLocation(const SourceLocation &Loc,
101 SourceManager &SM) {
102 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
103 unsigned LocColumn =
104 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
105 FileID FID = SM.getFileID(Loc);
106 llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr);
107
108 assert(LocData >= Buffer->getBufferStart() &&
109 LocData < Buffer->getBufferEnd());
110
111 const char *LineBegin = LocData - LocColumn;
112
113 assert(LineBegin >= Buffer->getBufferStart());
114
115 const char *LineEnd = nullptr;
116
117 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
118 LineEnd < Buffer->getBufferEnd();
119 ++LineEnd)
120 ;
121
122 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
123
124 llvm::errs() << LineString << '\n';
125 llvm::errs().indent(LocColumn);
126 llvm::errs() << '^';
Sean Callanan1eac8792017-07-25 19:54:22 +0000127 llvm::errs() << '\n';
Sean Callanan7d982502016-12-22 20:03:14 +0000128 }
129
130 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
131 const Diagnostic &Info) override {
132 if (Info.hasSourceManager() && LangOpts) {
133 SourceManager &SM = Info.getSourceManager();
134
135 if (Info.getLocation().isValid()) {
136 Info.getLocation().print(llvm::errs(), SM);
137 llvm::errs() << ": ";
138 }
139
140 SmallString<16> DiagText;
141 Info.FormatDiagnostic(DiagText);
142 llvm::errs() << DiagText << '\n';
143
144 if (Info.getLocation().isValid()) {
145 PrintSourceForLocation(Info.getLocation(), SM);
146 }
147
148 for (const CharSourceRange &Range : Info.getRanges()) {
149 bool Invalid = true;
150 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
151 if (!Invalid) {
152 llvm::errs() << Ref << '\n';
153 }
154 }
155 }
156 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
157 }
158};
159
Sean Callanan967d4382017-09-27 19:57:58 +0000160std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
Sean Callanan7d982502016-12-22 20:03:14 +0000161 auto Ins = llvm::make_unique<CompilerInstance>();
162 auto DC = llvm::make_unique<TestDiagnosticConsumer>();
163 const bool ShouldOwnClient = true;
164 Ins->createDiagnostics(DC.release(), ShouldOwnClient);
165
166 auto Inv = llvm::make_unique<CompilerInvocation>();
167
Sean Callanan1eac8792017-07-25 19:54:22 +0000168 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(); });
Sean Callanan7d982502016-12-22 20:03:14 +0000171 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
172 &ClangArgv.data()[ClangArgv.size()],
173 Ins->getDiagnostics());
174
Sean Callanan1eac8792017-07-25 19:54:22 +0000175 {
176 using namespace driver::types;
177 ID Id = lookupTypeForTypeSpecifier(Input.c_str());
178 assert(Id != TY_INVALID);
179 if (isCXX(Id)) {
180 Inv->getLangOpts()->CPlusPlus = true;
181 Inv->getLangOpts()->CPlusPlus11 = true;
182 Inv->getHeaderSearchOpts().UseLibcxx = true;
183 }
184 if (isObjC(Id)) {
185 Inv->getLangOpts()->ObjC1 = 1;
186 Inv->getLangOpts()->ObjC2 = 1;
187 }
188 }
Sean Callanan7d982502016-12-22 20:03:14 +0000189 Inv->getLangOpts()->Bool = true;
190 Inv->getLangOpts()->WChar = true;
191 Inv->getLangOpts()->Blocks = true;
192 Inv->getLangOpts()->DebuggerSupport = true;
193 Inv->getLangOpts()->SpellChecking = false;
194 Inv->getLangOpts()->ThreadsafeStatics = false;
195 Inv->getLangOpts()->AccessControl = false;
196 Inv->getLangOpts()->DollarIdents = true;
197 Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
198 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
199
David Blaikieea4395e2017-01-06 19:49:01 +0000200 Ins->setInvocation(std::move(Inv));
Sean Callanan7d982502016-12-22 20:03:14 +0000201
202 TargetInfo *TI = TargetInfo::CreateTargetInfo(
203 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
204 Ins->setTarget(TI);
205 Ins->getTarget().adjust(Ins->getLangOpts());
206 Ins->createFileManager();
207 Ins->createSourceManager(Ins->getFileManager());
208 Ins->createPreprocessor(TU_Complete);
209
210 return Ins;
211}
212
213std::unique_ptr<ASTContext>
214BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
215 auto AST = llvm::make_unique<ASTContext>(
216 CI.getLangOpts(), CI.getSourceManager(),
217 CI.getPreprocessor().getIdentifierTable(), ST, BC);
218 AST->InitBuiltinTypes(CI.getTarget());
219 return AST;
220}
221
222std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
223 llvm::LLVMContext &LLVMCtx) {
224 StringRef ModuleName("$__module");
225 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
226 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
227 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
228}
Vedant Kumar3df72582018-02-07 21:17:22 +0000229} // namespace init_convenience
Sean Callanan7d982502016-12-22 20:03:14 +0000230
231namespace {
Sean Callanan967d4382017-09-27 19:57:58 +0000232
233/// A container for a CompilerInstance (possibly with an ExternalASTMerger
234/// attached to its ASTContext).
235///
236/// Provides an accessor for the DeclContext origins associated with the
237/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
238/// attached).
239///
240/// This is the main unit of parsed source code maintained by clang-import-test.
241struct CIAndOrigins {
242 using OriginMap = clang::ExternalASTMerger::OriginMap;
243 std::unique_ptr<CompilerInstance> CI;
244
245 ASTContext &getASTContext() { return CI->getASTContext(); }
246 FileManager &getFileManager() { return CI->getFileManager(); }
247 const OriginMap &getOriginMap() {
Pavel Labath034c0f22018-03-13 11:28:27 +0000248 static const OriginMap EmptyOriginMap{};
Sean Callanan967d4382017-09-27 19:57:58 +0000249 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
250 return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
251 return EmptyOriginMap;
Sean Callananb7160ca2017-04-11 19:33:35 +0000252 }
Sean Callanan967d4382017-09-27 19:57:58 +0000253 DiagnosticConsumer &getDiagnosticClient() {
254 return CI->getDiagnosticClient();
255 }
256 CompilerInstance &getCompilerInstance() { return *CI; }
257};
258
259void AddExternalSource(CIAndOrigins &CI,
260 llvm::MutableArrayRef<CIAndOrigins> Imports) {
261 ExternalASTMerger::ImporterTarget Target(
262 {CI.getASTContext(), CI.getFileManager()});
263 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
264 for (CIAndOrigins &Import : Imports)
Vedant Kumar3df72582018-02-07 21:17:22 +0000265 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
266 Import.getOriginMap()});
Sean Callananb7160ca2017-04-11 19:33:35 +0000267 auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
268 CI.getASTContext().setExternalSource(ES.release());
269 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
Sean Callanan7d982502016-12-22 20:03:14 +0000270}
271
Sean Callanan967d4382017-09-27 19:57:58 +0000272CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
273 CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
Sean Callanan9092d472017-05-13 00:46:33 +0000274 auto ST = llvm::make_unique<SelectorTable>();
275 auto BC = llvm::make_unique<Builtin::Context>();
Sean Callanan967d4382017-09-27 19:57:58 +0000276 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
277 IndirectCI.getCompilerInstance(), *ST, *BC);
278 IndirectCI.getCompilerInstance().setASTContext(AST.release());
279 AddExternalSource(IndirectCI, CI);
Sean Callanan9092d472017-05-13 00:46:33 +0000280 return IndirectCI;
281}
282
Sean Callanan7d982502016-12-22 20:03:14 +0000283llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
Lang Hames19e07e12017-06-20 21:06:00 +0000284 ASTConsumer &Consumer) {
Sean Callanan7d982502016-12-22 20:03:14 +0000285 SourceManager &SM = CI.getSourceManager();
286 const FileEntry *FE = CI.getFileManager().getFile(Path);
287 if (!FE) {
288 return llvm::make_error<llvm::StringError>(
289 llvm::Twine("Couldn't open ", Path), std::error_code());
290 }
291 SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
Lang Hames19e07e12017-06-20 21:06:00 +0000292 ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
Sean Callanan7d982502016-12-22 20:03:14 +0000293 return llvm::Error::success();
294}
295
Sean Callanan967d4382017-09-27 19:57:58 +0000296llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
297 llvm::MutableArrayRef<CIAndOrigins> Imports,
298 bool ShouldDumpAST, bool ShouldDumpIR) {
299 CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
Sean Callanan7d982502016-12-22 20:03:14 +0000300 auto ST = llvm::make_unique<SelectorTable>();
301 auto BC = llvm::make_unique<Builtin::Context>();
302 std::unique_ptr<ASTContext> AST =
Sean Callanan967d4382017-09-27 19:57:58 +0000303 init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
304 CI.getCompilerInstance().setASTContext(AST.release());
Sean Callanan9092d472017-05-13 00:46:33 +0000305 if (Imports.size())
Sean Callanan967d4382017-09-27 19:57:58 +0000306 AddExternalSource(CI, Imports);
Sean Callanan7d982502016-12-22 20:03:14 +0000307
Lang Hames19e07e12017-06-20 21:06:00 +0000308 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
309
Sean Callanan7d982502016-12-22 20:03:14 +0000310 auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
Sean Callanan967d4382017-09-27 19:57:58 +0000311 ASTConsumers.push_back(
312 init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
313 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
Lang Hames19e07e12017-06-20 21:06:00 +0000314
315 if (ShouldDumpAST)
Alexander Kornienkod10d7902018-04-06 13:01:12 +0000316 ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/,
317 "", true, false, false));
Sean Callanan7d982502016-12-22 20:03:14 +0000318
Sean Callanan967d4382017-09-27 19:57:58 +0000319 CI.getDiagnosticClient().BeginSourceFile(
320 CI.getCompilerInstance().getLangOpts(),
321 &CI.getCompilerInstance().getPreprocessor());
Lang Hames19e07e12017-06-20 21:06:00 +0000322 MultiplexConsumer Consumers(std::move(ASTConsumers));
Sean Callanan967d4382017-09-27 19:57:58 +0000323 Consumers.Initialize(CI.getASTContext());
Lang Hames19e07e12017-06-20 21:06:00 +0000324
Sean Callanan967d4382017-09-27 19:57:58 +0000325 if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
Sean Callanan7d982502016-12-22 20:03:14 +0000326 return std::move(PE);
Sean Callanan967d4382017-09-27 19:57:58 +0000327 CI.getDiagnosticClient().EndSourceFile();
Sean Callanan2b3a54b2017-08-07 22:27:30 +0000328 if (ShouldDumpIR)
329 CG.GetModule()->print(llvm::outs(), nullptr);
Sean Callanan967d4382017-09-27 19:57:58 +0000330 if (CI.getDiagnosticClient().getNumErrors())
Sean Callanan7d982502016-12-22 20:03:14 +0000331 return llvm::make_error<llvm::StringError>(
Malcolm Parsons51d3fb02018-01-24 10:26:09 +0000332 "Errors occurred while parsing the expression.", std::error_code());
Sean Callanan967d4382017-09-27 19:57:58 +0000333 return std::move(CI);
334}
335
336void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
337 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
338 for (CIAndOrigins &Import : Imports)
Vedant Kumar3df72582018-02-07 21:17:22 +0000339 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
340 Import.getOriginMap()});
Sean Callanan967d4382017-09-27 19:57:58 +0000341 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
342 auto *Merger = static_cast<ExternalASTMerger *>(Source);
343 Merger->RemoveSources(Sources);
Sean Callanan7d982502016-12-22 20:03:14 +0000344}
Sean Callananb7160ca2017-04-11 19:33:35 +0000345
Sean Callanan7d982502016-12-22 20:03:14 +0000346} // end namespace
347
348int main(int argc, const char **argv) {
349 const bool DisableCrashReporting = true;
350 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
351 llvm::cl::ParseCommandLineOptions(argc, argv);
Sean Callanan967d4382017-09-27 19:57:58 +0000352 std::vector<CIAndOrigins> ImportCIs;
Sean Callanan7d982502016-12-22 20:03:14 +0000353 for (auto I : Imports) {
Sean Callanan967d4382017-09-27 19:57:58 +0000354 llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
Sean Callanan7d982502016-12-22 20:03:14 +0000355 if (auto E = ImportCI.takeError()) {
356 llvm::errs() << llvm::toString(std::move(E));
357 exit(-1);
Sean Callanan7d982502016-12-22 20:03:14 +0000358 }
Sean Callanan967d4382017-09-27 19:57:58 +0000359 ImportCIs.push_back(std::move(*ImportCI));
Sean Callanan7d982502016-12-22 20:03:14 +0000360 }
Sean Callanan967d4382017-09-27 19:57:58 +0000361 std::vector<CIAndOrigins> IndirectCIs;
362 if (!Direct || UseOrigins) {
Sean Callanan9092d472017-05-13 00:46:33 +0000363 for (auto &ImportCI : ImportCIs) {
Sean Callanan967d4382017-09-27 19:57:58 +0000364 CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
Sean Callanan6d565022017-07-11 00:27:57 +0000365 IndirectCIs.push_back(std::move(IndirectCI));
Sean Callanan9092d472017-05-13 00:46:33 +0000366 }
367 }
Sean Callanan967d4382017-09-27 19:57:58 +0000368 if (UseOrigins)
369 for (auto &ImportCI : ImportCIs)
370 IndirectCIs.push_back(std::move(ImportCI));
371 llvm::Expected<CIAndOrigins> ExpressionCI =
372 Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
373 DumpAST, DumpIR);
Sean Callanan7d982502016-12-22 20:03:14 +0000374 if (auto E = ExpressionCI.takeError()) {
375 llvm::errs() << llvm::toString(std::move(E));
376 exit(-1);
Sean Callanan7d982502016-12-22 20:03:14 +0000377 }
Sean Callanan967d4382017-09-27 19:57:58 +0000378 Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
379 return 0;
Sean Callanan7d982502016-12-22 20:03:14 +0000380}