blob: 2a43dc7f28528f47fc05f549b9d8c9d1c1039134 [file] [log] [blame]
Sean Callanan7d982502016-12-22 20:03:14 +00001//===-- import-test.cpp - ASTImporter/ExternalASTSource testbed -----------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Sean Callanan7d982502016-12-22 20:03:14 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "clang/AST/ASTContext.h"
10#include "clang/AST/ASTImporter.h"
Sean Callananb7160ca2017-04-11 19:33:35 +000011#include "clang/AST/DeclObjC.h"
12#include "clang/AST/ExternalASTMerger.h"
Sean Callanan7d982502016-12-22 20:03:14 +000013#include "clang/Basic/Builtins.h"
14#include "clang/Basic/IdentifierTable.h"
15#include "clang/Basic/SourceLocation.h"
16#include "clang/Basic/TargetInfo.h"
17#include "clang/Basic/TargetOptions.h"
18#include "clang/CodeGen/ModuleBuilder.h"
Sean Callanan1eac8792017-07-25 19:54:22 +000019#include "clang/Driver/Types.h"
Lang Hames19e07e12017-06-20 21:06:00 +000020#include "clang/Frontend/ASTConsumers.h"
Sean Callanan7d982502016-12-22 20:03:14 +000021#include "clang/Frontend/CompilerInstance.h"
Lang Hames19e07e12017-06-20 21:06:00 +000022#include "clang/Frontend/MultiplexConsumer.h"
Sean Callanan7d982502016-12-22 20:03:14 +000023#include "clang/Frontend/TextDiagnosticBuffer.h"
24#include "clang/Lex/Lexer.h"
25#include "clang/Lex/Preprocessor.h"
26#include "clang/Parse/ParseAST.h"
27
28#include "llvm/IR/LLVMContext.h"
Sean Callanan2b3a54b2017-08-07 22:27:30 +000029#include "llvm/IR/Module.h"
Sean Callanan7d982502016-12-22 20:03:14 +000030#include "llvm/Support/CommandLine.h"
31#include "llvm/Support/Error.h"
32#include "llvm/Support/Host.h"
33#include "llvm/Support/Signals.h"
34
35#include <memory>
36#include <string>
37
38using namespace clang;
39
40static llvm::cl::opt<std::string> Expression(
41 "expression", llvm::cl::Required,
42 llvm::cl::desc("Path to a file containing the expression to parse"));
43
44static llvm::cl::list<std::string>
45 Imports("import", llvm::cl::ZeroOrMore,
46 llvm::cl::desc("Path to a file containing declarations to import"));
47
Sean Callanan9092d472017-05-13 00:46:33 +000048static llvm::cl::opt<bool>
49 Direct("direct", llvm::cl::Optional,
Sean Callanan967d4382017-09-27 19:57:58 +000050 llvm::cl::desc("Use the parsed declarations without indirection"));
51
Vedant Kumar3df72582018-02-07 21:17:22 +000052static llvm::cl::opt<bool> UseOrigins(
53 "use-origins", llvm::cl::Optional,
54 llvm::cl::desc(
55 "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)) {
Erik Pilkingtonfa983902018-10-30 20:31:30 +0000184 Inv->getLangOpts()->ObjC = 1;
Sean Callanan1eac8792017-07-25 19:54:22 +0000185 }
186 }
Sean Callanan7d982502016-12-22 20:03:14 +0000187 Inv->getLangOpts()->Bool = true;
188 Inv->getLangOpts()->WChar = true;
189 Inv->getLangOpts()->Blocks = true;
190 Inv->getLangOpts()->DebuggerSupport = true;
191 Inv->getLangOpts()->SpellChecking = false;
192 Inv->getLangOpts()->ThreadsafeStatics = false;
193 Inv->getLangOpts()->AccessControl = false;
194 Inv->getLangOpts()->DollarIdents = true;
Raphael Isemann79d50a02018-08-20 22:13:24 +0000195 Inv->getLangOpts()->Exceptions = true;
196 Inv->getLangOpts()->CXXExceptions = true;
Raphael Isemannc705bb82018-08-20 16:20:01 +0000197 // Needed for testing dynamic_cast.
198 Inv->getLangOpts()->RTTI = true;
Sean Callanan7d982502016-12-22 20:03:14 +0000199 Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
200 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
201
David Blaikieea4395e2017-01-06 19:49:01 +0000202 Ins->setInvocation(std::move(Inv));
Sean Callanan7d982502016-12-22 20:03:14 +0000203
204 TargetInfo *TI = TargetInfo::CreateTargetInfo(
205 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
206 Ins->setTarget(TI);
207 Ins->getTarget().adjust(Ins->getLangOpts());
208 Ins->createFileManager();
209 Ins->createSourceManager(Ins->getFileManager());
210 Ins->createPreprocessor(TU_Complete);
211
212 return Ins;
213}
214
215std::unique_ptr<ASTContext>
216BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
217 auto AST = llvm::make_unique<ASTContext>(
218 CI.getLangOpts(), CI.getSourceManager(),
219 CI.getPreprocessor().getIdentifierTable(), ST, BC);
220 AST->InitBuiltinTypes(CI.getTarget());
221 return AST;
222}
223
224std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
225 llvm::LLVMContext &LLVMCtx) {
226 StringRef ModuleName("$__module");
227 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
228 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
229 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
230}
Vedant Kumar3df72582018-02-07 21:17:22 +0000231} // namespace init_convenience
Sean Callanan7d982502016-12-22 20:03:14 +0000232
233namespace {
Sean Callanan967d4382017-09-27 19:57:58 +0000234
235/// A container for a CompilerInstance (possibly with an ExternalASTMerger
236/// attached to its ASTContext).
237///
238/// Provides an accessor for the DeclContext origins associated with the
239/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
240/// attached).
241///
242/// This is the main unit of parsed source code maintained by clang-import-test.
243struct CIAndOrigins {
244 using OriginMap = clang::ExternalASTMerger::OriginMap;
245 std::unique_ptr<CompilerInstance> CI;
246
247 ASTContext &getASTContext() { return CI->getASTContext(); }
248 FileManager &getFileManager() { return CI->getFileManager(); }
249 const OriginMap &getOriginMap() {
Pavel Labath034c0f22018-03-13 11:28:27 +0000250 static const OriginMap EmptyOriginMap{};
Sean Callanan967d4382017-09-27 19:57:58 +0000251 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
252 return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
253 return EmptyOriginMap;
Sean Callananb7160ca2017-04-11 19:33:35 +0000254 }
Sean Callanan967d4382017-09-27 19:57:58 +0000255 DiagnosticConsumer &getDiagnosticClient() {
256 return CI->getDiagnosticClient();
257 }
258 CompilerInstance &getCompilerInstance() { return *CI; }
259};
260
261void AddExternalSource(CIAndOrigins &CI,
262 llvm::MutableArrayRef<CIAndOrigins> Imports) {
263 ExternalASTMerger::ImporterTarget Target(
264 {CI.getASTContext(), CI.getFileManager()});
265 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
266 for (CIAndOrigins &Import : Imports)
Vedant Kumar3df72582018-02-07 21:17:22 +0000267 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
268 Import.getOriginMap()});
Sean Callananb7160ca2017-04-11 19:33:35 +0000269 auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
270 CI.getASTContext().setExternalSource(ES.release());
271 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
Sean Callanan7d982502016-12-22 20:03:14 +0000272}
273
Sean Callanan967d4382017-09-27 19:57:58 +0000274CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
275 CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
Sean Callanan9092d472017-05-13 00:46:33 +0000276 auto ST = llvm::make_unique<SelectorTable>();
277 auto BC = llvm::make_unique<Builtin::Context>();
Sean Callanan967d4382017-09-27 19:57:58 +0000278 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
279 IndirectCI.getCompilerInstance(), *ST, *BC);
280 IndirectCI.getCompilerInstance().setASTContext(AST.release());
281 AddExternalSource(IndirectCI, CI);
Sean Callanan9092d472017-05-13 00:46:33 +0000282 return IndirectCI;
283}
284
Sean Callanan7d982502016-12-22 20:03:14 +0000285llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
Lang Hames19e07e12017-06-20 21:06:00 +0000286 ASTConsumer &Consumer) {
Sean Callanan7d982502016-12-22 20:03:14 +0000287 SourceManager &SM = CI.getSourceManager();
288 const FileEntry *FE = CI.getFileManager().getFile(Path);
289 if (!FE) {
290 return llvm::make_error<llvm::StringError>(
291 llvm::Twine("Couldn't open ", Path), std::error_code());
292 }
293 SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
Lang Hames19e07e12017-06-20 21:06:00 +0000294 ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
Sean Callanan7d982502016-12-22 20:03:14 +0000295 return llvm::Error::success();
296}
297
Sean Callanan967d4382017-09-27 19:57:58 +0000298llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
299 llvm::MutableArrayRef<CIAndOrigins> Imports,
300 bool ShouldDumpAST, bool ShouldDumpIR) {
301 CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
Sean Callanan7d982502016-12-22 20:03:14 +0000302 auto ST = llvm::make_unique<SelectorTable>();
303 auto BC = llvm::make_unique<Builtin::Context>();
304 std::unique_ptr<ASTContext> AST =
Sean Callanan967d4382017-09-27 19:57:58 +0000305 init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
306 CI.getCompilerInstance().setASTContext(AST.release());
Sean Callanan9092d472017-05-13 00:46:33 +0000307 if (Imports.size())
Sean Callanan967d4382017-09-27 19:57:58 +0000308 AddExternalSource(CI, Imports);
Sean Callanan7d982502016-12-22 20:03:14 +0000309
Lang Hames19e07e12017-06-20 21:06:00 +0000310 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
311
Sean Callanan7d982502016-12-22 20:03:14 +0000312 auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
Sean Callanan967d4382017-09-27 19:57:58 +0000313 ASTConsumers.push_back(
314 init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
315 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
Lang Hames19e07e12017-06-20 21:06:00 +0000316
317 if (ShouldDumpAST)
Alexander Kornienkod10d7902018-04-06 13:01:12 +0000318 ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/,
319 "", true, false, false));
Sean Callanan7d982502016-12-22 20:03:14 +0000320
Sean Callanan967d4382017-09-27 19:57:58 +0000321 CI.getDiagnosticClient().BeginSourceFile(
322 CI.getCompilerInstance().getLangOpts(),
323 &CI.getCompilerInstance().getPreprocessor());
Lang Hames19e07e12017-06-20 21:06:00 +0000324 MultiplexConsumer Consumers(std::move(ASTConsumers));
Sean Callanan967d4382017-09-27 19:57:58 +0000325 Consumers.Initialize(CI.getASTContext());
Lang Hames19e07e12017-06-20 21:06:00 +0000326
Sean Callanan967d4382017-09-27 19:57:58 +0000327 if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
Sean Callanan7d982502016-12-22 20:03:14 +0000328 return std::move(PE);
Sean Callanan967d4382017-09-27 19:57:58 +0000329 CI.getDiagnosticClient().EndSourceFile();
Sean Callanan2b3a54b2017-08-07 22:27:30 +0000330 if (ShouldDumpIR)
331 CG.GetModule()->print(llvm::outs(), nullptr);
Sean Callanan967d4382017-09-27 19:57:58 +0000332 if (CI.getDiagnosticClient().getNumErrors())
Sean Callanan7d982502016-12-22 20:03:14 +0000333 return llvm::make_error<llvm::StringError>(
Malcolm Parsons51d3fb02018-01-24 10:26:09 +0000334 "Errors occurred while parsing the expression.", std::error_code());
Sean Callanan967d4382017-09-27 19:57:58 +0000335 return std::move(CI);
336}
337
338void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
339 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
340 for (CIAndOrigins &Import : Imports)
Vedant Kumar3df72582018-02-07 21:17:22 +0000341 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
342 Import.getOriginMap()});
Sean Callanan967d4382017-09-27 19:57:58 +0000343 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
344 auto *Merger = static_cast<ExternalASTMerger *>(Source);
345 Merger->RemoveSources(Sources);
Sean Callanan7d982502016-12-22 20:03:14 +0000346}
Sean Callananb7160ca2017-04-11 19:33:35 +0000347
Sean Callanan7d982502016-12-22 20:03:14 +0000348} // end namespace
349
350int main(int argc, const char **argv) {
351 const bool DisableCrashReporting = true;
352 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
353 llvm::cl::ParseCommandLineOptions(argc, argv);
Sean Callanan967d4382017-09-27 19:57:58 +0000354 std::vector<CIAndOrigins> ImportCIs;
Sean Callanan7d982502016-12-22 20:03:14 +0000355 for (auto I : Imports) {
Sean Callanan967d4382017-09-27 19:57:58 +0000356 llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
Sean Callanan7d982502016-12-22 20:03:14 +0000357 if (auto E = ImportCI.takeError()) {
358 llvm::errs() << llvm::toString(std::move(E));
359 exit(-1);
Sean Callanan7d982502016-12-22 20:03:14 +0000360 }
Sean Callanan967d4382017-09-27 19:57:58 +0000361 ImportCIs.push_back(std::move(*ImportCI));
Sean Callanan7d982502016-12-22 20:03:14 +0000362 }
Sean Callanan967d4382017-09-27 19:57:58 +0000363 std::vector<CIAndOrigins> IndirectCIs;
364 if (!Direct || UseOrigins) {
Sean Callanan9092d472017-05-13 00:46:33 +0000365 for (auto &ImportCI : ImportCIs) {
Sean Callanan967d4382017-09-27 19:57:58 +0000366 CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
Sean Callanan6d565022017-07-11 00:27:57 +0000367 IndirectCIs.push_back(std::move(IndirectCI));
Sean Callanan9092d472017-05-13 00:46:33 +0000368 }
369 }
Sean Callanan967d4382017-09-27 19:57:58 +0000370 if (UseOrigins)
371 for (auto &ImportCI : ImportCIs)
372 IndirectCIs.push_back(std::move(ImportCI));
373 llvm::Expected<CIAndOrigins> ExpressionCI =
374 Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
375 DumpAST, DumpIR);
Sean Callanan7d982502016-12-22 20:03:14 +0000376 if (auto E = ExpressionCI.takeError()) {
377 llvm::errs() << llvm::toString(std::move(E));
378 exit(-1);
Sean Callanan7d982502016-12-22 20:03:14 +0000379 }
Sean Callanan967d4382017-09-27 19:57:58 +0000380 Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
381 return 0;
Sean Callanan7d982502016-12-22 20:03:14 +0000382}