blob: 1ea7ee3611df1b926eecee6bbd590580525a877c [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
53static llvm::cl::opt<bool>
54 UseOrigins("use-origins", llvm::cl::Optional,
55 llvm::cl::desc("Use DeclContext origin information for more accurate lookups"));
Sean Callanan9092d472017-05-13 00:46:33 +000056
Sean Callanan7d982502016-12-22 20:03:14 +000057static llvm::cl::list<std::string>
58 ClangArgs("Xcc", llvm::cl::ZeroOrMore,
59 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
60 llvm::cl::CommaSeparated);
61
Sean Callanan1eac8792017-07-25 19:54:22 +000062static llvm::cl::opt<std::string>
63 Input("x", llvm::cl::Optional,
64 llvm::cl::desc("The language to parse (default: c++)"),
65 llvm::cl::init("c++"));
66
Sean Callanan967d4382017-09-27 19:57:58 +000067static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
68 llvm::cl::desc("Dump combined AST"));
Lang Hames19e07e12017-06-20 21:06:00 +000069
Sean Callanan967d4382017-09-27 19:57:58 +000070static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
71 llvm::cl::desc("Dump IR from final parse"));
Sean Callanan2b3a54b2017-08-07 22:27:30 +000072
Sean Callanan7d982502016-12-22 20:03:14 +000073namespace init_convenience {
74class TestDiagnosticConsumer : public DiagnosticConsumer {
75private:
76 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
77 const LangOptions *LangOpts = nullptr;
78
79public:
80 TestDiagnosticConsumer()
81 : Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {}
82
83 virtual void BeginSourceFile(const LangOptions &LangOpts,
84 const Preprocessor *PP = nullptr) override {
85 this->LangOpts = &LangOpts;
86 return Passthrough->BeginSourceFile(LangOpts, PP);
87 }
88
89 virtual void EndSourceFile() override {
90 this->LangOpts = nullptr;
91 Passthrough->EndSourceFile();
92 }
93
94 virtual bool IncludeInDiagnosticCounts() const override {
95 return Passthrough->IncludeInDiagnosticCounts();
96 }
97
98private:
99 static void PrintSourceForLocation(const SourceLocation &Loc,
100 SourceManager &SM) {
101 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
102 unsigned LocColumn =
103 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
104 FileID FID = SM.getFileID(Loc);
105 llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr);
106
107 assert(LocData >= Buffer->getBufferStart() &&
108 LocData < Buffer->getBufferEnd());
109
110 const char *LineBegin = LocData - LocColumn;
111
112 assert(LineBegin >= Buffer->getBufferStart());
113
114 const char *LineEnd = nullptr;
115
116 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
117 LineEnd < Buffer->getBufferEnd();
118 ++LineEnd)
119 ;
120
121 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
122
123 llvm::errs() << LineString << '\n';
124 llvm::errs().indent(LocColumn);
125 llvm::errs() << '^';
Sean Callanan1eac8792017-07-25 19:54:22 +0000126 llvm::errs() << '\n';
Sean Callanan7d982502016-12-22 20:03:14 +0000127 }
128
129 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
130 const Diagnostic &Info) override {
131 if (Info.hasSourceManager() && LangOpts) {
132 SourceManager &SM = Info.getSourceManager();
133
134 if (Info.getLocation().isValid()) {
135 Info.getLocation().print(llvm::errs(), SM);
136 llvm::errs() << ": ";
137 }
138
139 SmallString<16> DiagText;
140 Info.FormatDiagnostic(DiagText);
141 llvm::errs() << DiagText << '\n';
142
143 if (Info.getLocation().isValid()) {
144 PrintSourceForLocation(Info.getLocation(), SM);
145 }
146
147 for (const CharSourceRange &Range : Info.getRanges()) {
148 bool Invalid = true;
149 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
150 if (!Invalid) {
151 llvm::errs() << Ref << '\n';
152 }
153 }
154 }
155 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
156 }
157};
158
Sean Callanan967d4382017-09-27 19:57:58 +0000159std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
Sean Callanan7d982502016-12-22 20:03:14 +0000160 auto Ins = llvm::make_unique<CompilerInstance>();
161 auto DC = llvm::make_unique<TestDiagnosticConsumer>();
162 const bool ShouldOwnClient = true;
163 Ins->createDiagnostics(DC.release(), ShouldOwnClient);
164
165 auto Inv = llvm::make_unique<CompilerInvocation>();
166
Sean Callanan1eac8792017-07-25 19:54:22 +0000167 std::vector<const char *> ClangArgv(ClangArgs.size());
168 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
169 [](const std::string &s) -> const char * { return s.data(); });
Sean Callanan7d982502016-12-22 20:03:14 +0000170 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
171 &ClangArgv.data()[ClangArgv.size()],
172 Ins->getDiagnostics());
173
Sean Callanan1eac8792017-07-25 19:54:22 +0000174 {
175 using namespace driver::types;
176 ID Id = lookupTypeForTypeSpecifier(Input.c_str());
177 assert(Id != TY_INVALID);
178 if (isCXX(Id)) {
179 Inv->getLangOpts()->CPlusPlus = true;
180 Inv->getLangOpts()->CPlusPlus11 = true;
181 Inv->getHeaderSearchOpts().UseLibcxx = true;
182 }
183 if (isObjC(Id)) {
184 Inv->getLangOpts()->ObjC1 = 1;
185 Inv->getLangOpts()->ObjC2 = 1;
186 }
187 }
Sean Callanan7d982502016-12-22 20:03:14 +0000188 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->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
197 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
198
David Blaikieea4395e2017-01-06 19:49:01 +0000199 Ins->setInvocation(std::move(Inv));
Sean Callanan7d982502016-12-22 20:03:14 +0000200
201 TargetInfo *TI = TargetInfo::CreateTargetInfo(
202 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
203 Ins->setTarget(TI);
204 Ins->getTarget().adjust(Ins->getLangOpts());
205 Ins->createFileManager();
206 Ins->createSourceManager(Ins->getFileManager());
207 Ins->createPreprocessor(TU_Complete);
208
209 return Ins;
210}
211
212std::unique_ptr<ASTContext>
213BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
214 auto AST = llvm::make_unique<ASTContext>(
215 CI.getLangOpts(), CI.getSourceManager(),
216 CI.getPreprocessor().getIdentifierTable(), ST, BC);
217 AST->InitBuiltinTypes(CI.getTarget());
218 return AST;
219}
220
221std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
222 llvm::LLVMContext &LLVMCtx) {
223 StringRef ModuleName("$__module");
224 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
225 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
226 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
227}
228} // end namespace
229
230namespace {
Sean Callanan967d4382017-09-27 19:57:58 +0000231
232/// A container for a CompilerInstance (possibly with an ExternalASTMerger
233/// attached to its ASTContext).
234///
235/// Provides an accessor for the DeclContext origins associated with the
236/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
237/// attached).
238///
239/// This is the main unit of parsed source code maintained by clang-import-test.
240struct CIAndOrigins {
241 using OriginMap = clang::ExternalASTMerger::OriginMap;
242 std::unique_ptr<CompilerInstance> CI;
243
244 ASTContext &getASTContext() { return CI->getASTContext(); }
245 FileManager &getFileManager() { return CI->getFileManager(); }
246 const OriginMap &getOriginMap() {
247 static const OriginMap EmptyOriginMap;
248 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
249 return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
250 return EmptyOriginMap;
Sean Callananb7160ca2017-04-11 19:33:35 +0000251 }
Sean Callanan967d4382017-09-27 19:57:58 +0000252 DiagnosticConsumer &getDiagnosticClient() {
253 return CI->getDiagnosticClient();
254 }
255 CompilerInstance &getCompilerInstance() { return *CI; }
256};
257
258void AddExternalSource(CIAndOrigins &CI,
259 llvm::MutableArrayRef<CIAndOrigins> Imports) {
260 ExternalASTMerger::ImporterTarget Target(
261 {CI.getASTContext(), CI.getFileManager()});
262 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
263 for (CIAndOrigins &Import : Imports)
264 Sources.push_back(
265 {Import.getASTContext(), Import.getFileManager(), Import.getOriginMap()});
Sean Callananb7160ca2017-04-11 19:33:35 +0000266 auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
267 CI.getASTContext().setExternalSource(ES.release());
268 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
Sean Callanan7d982502016-12-22 20:03:14 +0000269}
270
Sean Callanan967d4382017-09-27 19:57:58 +0000271CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
272 CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
Sean Callanan9092d472017-05-13 00:46:33 +0000273 auto ST = llvm::make_unique<SelectorTable>();
274 auto BC = llvm::make_unique<Builtin::Context>();
Sean Callanan967d4382017-09-27 19:57:58 +0000275 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
276 IndirectCI.getCompilerInstance(), *ST, *BC);
277 IndirectCI.getCompilerInstance().setASTContext(AST.release());
278 AddExternalSource(IndirectCI, CI);
Sean Callanan9092d472017-05-13 00:46:33 +0000279 return IndirectCI;
280}
281
Sean Callanan7d982502016-12-22 20:03:14 +0000282llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
Lang Hames19e07e12017-06-20 21:06:00 +0000283 ASTConsumer &Consumer) {
Sean Callanan7d982502016-12-22 20:03:14 +0000284 SourceManager &SM = CI.getSourceManager();
285 const FileEntry *FE = CI.getFileManager().getFile(Path);
286 if (!FE) {
287 return llvm::make_error<llvm::StringError>(
288 llvm::Twine("Couldn't open ", Path), std::error_code());
289 }
290 SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
Lang Hames19e07e12017-06-20 21:06:00 +0000291 ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
Sean Callanan7d982502016-12-22 20:03:14 +0000292 return llvm::Error::success();
293}
294
Sean Callanan967d4382017-09-27 19:57:58 +0000295llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
296 llvm::MutableArrayRef<CIAndOrigins> Imports,
297 bool ShouldDumpAST, bool ShouldDumpIR) {
298 CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
Sean Callanan7d982502016-12-22 20:03:14 +0000299 auto ST = llvm::make_unique<SelectorTable>();
300 auto BC = llvm::make_unique<Builtin::Context>();
301 std::unique_ptr<ASTContext> AST =
Sean Callanan967d4382017-09-27 19:57:58 +0000302 init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
303 CI.getCompilerInstance().setASTContext(AST.release());
Sean Callanan9092d472017-05-13 00:46:33 +0000304 if (Imports.size())
Sean Callanan967d4382017-09-27 19:57:58 +0000305 AddExternalSource(CI, Imports);
Sean Callanan7d982502016-12-22 20:03:14 +0000306
Lang Hames19e07e12017-06-20 21:06:00 +0000307 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
308
Sean Callanan7d982502016-12-22 20:03:14 +0000309 auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
Sean Callanan967d4382017-09-27 19:57:58 +0000310 ASTConsumers.push_back(
311 init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
312 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
Lang Hames19e07e12017-06-20 21:06:00 +0000313
314 if (ShouldDumpAST)
315 ASTConsumers.push_back(CreateASTDumper("", true, false, false));
Sean Callanan7d982502016-12-22 20:03:14 +0000316
Sean Callanan967d4382017-09-27 19:57:58 +0000317 CI.getDiagnosticClient().BeginSourceFile(
318 CI.getCompilerInstance().getLangOpts(),
319 &CI.getCompilerInstance().getPreprocessor());
Lang Hames19e07e12017-06-20 21:06:00 +0000320 MultiplexConsumer Consumers(std::move(ASTConsumers));
Sean Callanan967d4382017-09-27 19:57:58 +0000321 Consumers.Initialize(CI.getASTContext());
Lang Hames19e07e12017-06-20 21:06:00 +0000322
Sean Callanan967d4382017-09-27 19:57:58 +0000323 if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
Sean Callanan7d982502016-12-22 20:03:14 +0000324 return std::move(PE);
Sean Callanan967d4382017-09-27 19:57:58 +0000325 CI.getDiagnosticClient().EndSourceFile();
Sean Callanan2b3a54b2017-08-07 22:27:30 +0000326 if (ShouldDumpIR)
327 CG.GetModule()->print(llvm::outs(), nullptr);
Sean Callanan967d4382017-09-27 19:57:58 +0000328 if (CI.getDiagnosticClient().getNumErrors())
Sean Callanan7d982502016-12-22 20:03:14 +0000329 return llvm::make_error<llvm::StringError>(
330 "Errors occured while parsing the expression.", std::error_code());
Sean Callanan967d4382017-09-27 19:57:58 +0000331 return std::move(CI);
332}
333
334void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
335 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
336 for (CIAndOrigins &Import : Imports)
337 Sources.push_back(
338 {Import.getASTContext(), Import.getFileManager(), Import.getOriginMap()});
339 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
340 auto *Merger = static_cast<ExternalASTMerger *>(Source);
341 Merger->RemoveSources(Sources);
Sean Callanan7d982502016-12-22 20:03:14 +0000342}
Sean Callananb7160ca2017-04-11 19:33:35 +0000343
Sean Callanan7d982502016-12-22 20:03:14 +0000344} // end namespace
345
346int main(int argc, const char **argv) {
347 const bool DisableCrashReporting = true;
348 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
349 llvm::cl::ParseCommandLineOptions(argc, argv);
Sean Callanan967d4382017-09-27 19:57:58 +0000350 std::vector<CIAndOrigins> ImportCIs;
Sean Callanan7d982502016-12-22 20:03:14 +0000351 for (auto I : Imports) {
Sean Callanan967d4382017-09-27 19:57:58 +0000352 llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
Sean Callanan7d982502016-12-22 20:03:14 +0000353 if (auto E = ImportCI.takeError()) {
354 llvm::errs() << llvm::toString(std::move(E));
355 exit(-1);
Sean Callanan7d982502016-12-22 20:03:14 +0000356 }
Sean Callanan967d4382017-09-27 19:57:58 +0000357 ImportCIs.push_back(std::move(*ImportCI));
Sean Callanan7d982502016-12-22 20:03:14 +0000358 }
Sean Callanan967d4382017-09-27 19:57:58 +0000359 std::vector<CIAndOrigins> IndirectCIs;
360 if (!Direct || UseOrigins) {
Sean Callanan9092d472017-05-13 00:46:33 +0000361 for (auto &ImportCI : ImportCIs) {
Sean Callanan967d4382017-09-27 19:57:58 +0000362 CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
Sean Callanan6d565022017-07-11 00:27:57 +0000363 IndirectCIs.push_back(std::move(IndirectCI));
Sean Callanan9092d472017-05-13 00:46:33 +0000364 }
365 }
Sean Callanan967d4382017-09-27 19:57:58 +0000366 if (UseOrigins)
367 for (auto &ImportCI : ImportCIs)
368 IndirectCIs.push_back(std::move(ImportCI));
369 llvm::Expected<CIAndOrigins> ExpressionCI =
370 Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
371 DumpAST, DumpIR);
Sean Callanan7d982502016-12-22 20:03:14 +0000372 if (auto E = ExpressionCI.takeError()) {
373 llvm::errs() << llvm::toString(std::move(E));
374 exit(-1);
Sean Callanan7d982502016-12-22 20:03:14 +0000375 }
Sean Callanan967d4382017-09-27 19:57:58 +0000376 Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
377 return 0;
Sean Callanan7d982502016-12-22 20:03:14 +0000378}