1//===-- clang-import-test.cpp - ASTImporter/ExternalASTSource testbed -----===//
2//
3// 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
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/AST/ASTContext.h"
10#include "clang/AST/ASTImporter.h"
11#include "clang/AST/DeclObjC.h"
12#include "clang/AST/ExternalASTMerger.h"
13#include "clang/Basic/Builtins.h"
14#include "clang/Basic/FileManager.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"
28
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/Signals.h"
34#include "llvm/Support/VirtualFileSystem.h"
35#include "llvm/TargetParser/Host.h"
36
37#include <memory>
38#include <string>
39
40using namespace clang;
41
42static llvm::cl::opt<std::string> Expression(
43 "expression", llvm::cl::Required,
44 llvm::cl::desc("Path to a file containing the expression to parse"));
45
46static llvm::cl::list<std::string>
47 Imports("import",
48 llvm::cl::desc("Path to a file containing declarations to import"));
49
50static llvm::cl::opt<bool>
51 Direct("direct", llvm::cl::Optional,
52 llvm::cl::desc("Use the parsed declarations without indirection"));
53
54static llvm::cl::opt<bool> UseOrigins(
55 "use-origins", llvm::cl::Optional,
56 llvm::cl::desc(
57 "Use DeclContext origin information for more accurate lookups"));
58
59static llvm::cl::list<std::string>
60 ClangArgs("Xcc",
61 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
62 llvm::cl::CommaSeparated);
63
64static llvm::cl::opt<std::string>
65 Input("x", llvm::cl::Optional,
66 llvm::cl::desc("The language to parse (default: c++)"),
67 llvm::cl::init(Val: "c++"));
68
69static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(Val: false),
70 llvm::cl::desc("Emable ObjC ARC"));
71
72static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(Val: false),
73 llvm::cl::desc("Dump combined AST"));
74
75static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(Val: false),
76 llvm::cl::desc("Dump IR from final parse"));
77
78namespace init_convenience {
79class TestDiagnosticConsumer : public DiagnosticConsumer {
80private:
81 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
82 const LangOptions *LangOpts = nullptr;
83
84public:
85 TestDiagnosticConsumer()
86 : Passthrough(std::make_unique<TextDiagnosticBuffer>()) {}
87
88 void BeginSourceFile(const LangOptions &LangOpts,
89 const Preprocessor *PP = nullptr) override {
90 this->LangOpts = &LangOpts;
91 return Passthrough->BeginSourceFile(LangOpts, PP);
92 }
93
94 void EndSourceFile() override {
95 this->LangOpts = nullptr;
96 Passthrough->EndSourceFile();
97 }
98
99 bool IncludeInDiagnosticCounts() const override {
100 return Passthrough->IncludeInDiagnosticCounts();
101 }
102
103private:
104 static void PrintSourceForLocation(const SourceLocation &Loc,
105 SourceManager &SM) {
106 const char *LocData = SM.getCharacterData(SL: Loc, /*Invalid=*/nullptr);
107 unsigned LocColumn =
108 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
109 FileID FID = SM.getFileID(SpellingLoc: Loc);
110 llvm::MemoryBufferRef Buffer = SM.getBufferOrFake(FID, Loc);
111
112 assert(LocData >= Buffer.getBufferStart() &&
113 LocData < Buffer.getBufferEnd());
114
115 const char *LineBegin = LocData - LocColumn;
116
117 assert(LineBegin >= Buffer.getBufferStart());
118
119 const char *LineEnd = nullptr;
120
121 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
122 LineEnd < Buffer.getBufferEnd();
123 ++LineEnd)
124 ;
125
126 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
127
128 llvm::errs() << LineString << '\n';
129 llvm::errs().indent(NumSpaces: LocColumn);
130 llvm::errs() << '^';
131 llvm::errs() << '\n';
132 }
133
134 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
135 const Diagnostic &Info) override {
136 if (Info.hasSourceManager() && LangOpts) {
137 SourceManager &SM = Info.getSourceManager();
138
139 if (Info.getLocation().isValid()) {
140 Info.getLocation().print(OS&: llvm::errs(), SM);
141 llvm::errs() << ": ";
142 }
143
144 SmallString<16> DiagText;
145 Info.FormatDiagnostic(OutStr&: DiagText);
146 llvm::errs() << DiagText << '\n';
147
148 if (Info.getLocation().isValid()) {
149 PrintSourceForLocation(Loc: Info.getLocation(), SM);
150 }
151
152 for (const CharSourceRange &Range : Info.getRanges()) {
153 bool Invalid = true;
154 StringRef Ref = Lexer::getSourceText(Range, SM, LangOpts: *LangOpts, Invalid: &Invalid);
155 if (!Invalid) {
156 llvm::errs() << Ref << '\n';
157 }
158 }
159 }
160 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
161 }
162};
163
164std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
165 DiagnosticOptions DiagOpts;
166 auto DC = std::make_unique<TestDiagnosticConsumer>();
167 auto Diags = CompilerInstance::createDiagnostics(
168 VFS&: *llvm::vfs::getRealFileSystem(), Opts&: DiagOpts, Client: DC.get(),
169 /*ShouldOwnClient=*/false);
170
171 auto Inv = std::make_unique<CompilerInvocation>();
172
173 std::vector<const char *> ClangArgv(ClangArgs.size());
174 std::transform(first: ClangArgs.begin(), last: ClangArgs.end(), result: ClangArgv.begin(),
175 unary_op: [](const std::string &s) -> const char * { return s.data(); });
176 CompilerInvocation::CreateFromArgs(Res&: *Inv, CommandLineArgs: ClangArgv, Diags&: *Diags);
177
178 {
179 using namespace driver::types;
180 ID Id = lookupTypeForTypeSpecifier(Name: Input.c_str());
181 assert(Id != TY_INVALID);
182 if (isCXX(Id)) {
183 Inv->getLangOpts().CPlusPlus = true;
184 Inv->getLangOpts().CPlusPlus11 = true;
185 Inv->getHeaderSearchOpts().UseLibcxx = true;
186 }
187 if (isObjC(Id)) {
188 Inv->getLangOpts().ObjC = 1;
189 }
190 }
191 Inv->getLangOpts().ObjCAutoRefCount = ObjCARC;
192
193 Inv->getLangOpts().Bool = true;
194 Inv->getLangOpts().WChar = true;
195 Inv->getLangOpts().Blocks = true;
196 Inv->getLangOpts().DebuggerSupport = true;
197 Inv->getLangOpts().SpellChecking = false;
198 Inv->getLangOpts().ThreadsafeStatics = false;
199 Inv->getLangOpts().AccessControl = false;
200 Inv->getLangOpts().DollarIdents = true;
201 Inv->getLangOpts().Exceptions = true;
202 Inv->getLangOpts().CXXExceptions = true;
203 // Needed for testing dynamic_cast.
204 Inv->getLangOpts().RTTI = true;
205 Inv->getCodeGenOpts().setDebugInfo(llvm::codegenoptions::FullDebugInfo);
206 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
207
208 auto Ins = std::make_unique<CompilerInstance>(args: std::move(Inv));
209
210 Ins->createVirtualFileSystem(BaseFS: llvm::vfs::getRealFileSystem(), DC: DC.get());
211 Ins->createDiagnostics(Client: DC.release(), /*ShouldOwnClient=*/true);
212
213 TargetInfo *TI = TargetInfo::CreateTargetInfo(
214 Diags&: Ins->getDiagnostics(), Opts&: Ins->getInvocation().getTargetOpts());
215 Ins->setTarget(TI);
216 Ins->getTarget().adjust(Diags&: Ins->getDiagnostics(), Opts&: Ins->getLangOpts(),
217 /*AuxTarget=*/Aux: nullptr);
218 Ins->createFileManager();
219 Ins->createSourceManager();
220 Ins->createPreprocessor(TUKind: TU_Complete);
221
222 return Ins;
223}
224
225std::unique_ptr<ASTContext>
226BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
227 auto &PP = CI.getPreprocessor();
228 auto AST = std::make_unique<ASTContext>(
229 args&: CI.getLangOpts(), args&: CI.getSourceManager(),
230 args&: PP.getIdentifierTable(), args&: ST, args&: BC, args: PP.TUKind);
231 AST->InitBuiltinTypes(Target: CI.getTarget());
232 return AST;
233}
234
235std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
236 llvm::LLVMContext &LLVMCtx) {
237 StringRef ModuleName("$__module");
238 return CreateLLVMCodeGen(CI, ModuleName, C&: LLVMCtx);
239}
240} // namespace init_convenience
241
242namespace {
243
244/// A container for a CompilerInstance (possibly with an ExternalASTMerger
245/// attached to its ASTContext).
246///
247/// Provides an accessor for the DeclContext origins associated with the
248/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
249/// attached).
250///
251/// This is the main unit of parsed source code maintained by clang-import-test.
252struct CIAndOrigins {
253 using OriginMap = clang::ExternalASTMerger::OriginMap;
254 std::unique_ptr<CompilerInstance> CI;
255
256 ASTContext &getASTContext() { return CI->getASTContext(); }
257 FileManager &getFileManager() { return CI->getFileManager(); }
258 const OriginMap &getOriginMap() {
259 static const OriginMap EmptyOriginMap{};
260 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
261 return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
262 return EmptyOriginMap;
263 }
264 DiagnosticConsumer &getDiagnosticClient() {
265 return CI->getDiagnosticClient();
266 }
267 CompilerInstance &getCompilerInstance() { return *CI; }
268};
269
270void AddExternalSource(CIAndOrigins &CI,
271 llvm::MutableArrayRef<CIAndOrigins> Imports) {
272 ExternalASTMerger::ImporterTarget Target(
273 {.AST: CI.getASTContext(), .FM: CI.getFileManager()});
274 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
275 for (CIAndOrigins &Import : Imports)
276 Sources.emplace_back(Args&: Import.getASTContext(), Args&: Import.getFileManager(),
277 Args: Import.getOriginMap());
278 auto ES = std::make_unique<ExternalASTMerger>(args&: Target, args&: Sources);
279 CI.getASTContext().setExternalSource(ES.release());
280 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
281}
282
283CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
284 CIAndOrigins IndirectCI{.CI: init_convenience::BuildCompilerInstance()};
285 auto ST = std::make_unique<SelectorTable>();
286 auto BC = std::make_unique<Builtin::Context>();
287 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
288 CI&: IndirectCI.getCompilerInstance(), ST&: *ST, BC&: *BC);
289 IndirectCI.getCompilerInstance().setASTContext(AST.release());
290 AddExternalSource(CI&: IndirectCI, Imports: CI);
291 return IndirectCI;
292}
293
294llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
295 ASTConsumer &Consumer) {
296 SourceManager &SM = CI.getSourceManager();
297 auto FE = CI.getFileManager().getFileRef(Filename: Path);
298 if (!FE) {
299 llvm::consumeError(Err: FE.takeError());
300 return llvm::make_error<llvm::StringError>(
301 Args: llvm::Twine("No such file or directory: ", Path), Args: std::error_code());
302 }
303 SM.setMainFileID(SM.createFileID(SourceFile: *FE, IncludePos: SourceLocation(), FileCharacter: SrcMgr::C_User));
304 ParseAST(pp&: CI.getPreprocessor(), C: &Consumer, Ctx&: CI.getASTContext());
305 return llvm::Error::success();
306}
307
308llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
309 llvm::MutableArrayRef<CIAndOrigins> Imports,
310 bool ShouldDumpAST, bool ShouldDumpIR) {
311 CIAndOrigins CI{.CI: init_convenience::BuildCompilerInstance()};
312 auto ST = std::make_unique<SelectorTable>();
313 auto BC = std::make_unique<Builtin::Context>();
314 std::unique_ptr<ASTContext> AST =
315 init_convenience::BuildASTContext(CI&: CI.getCompilerInstance(), ST&: *ST, BC&: *BC);
316 CI.getCompilerInstance().setASTContext(AST.release());
317 if (Imports.size())
318 AddExternalSource(CI, Imports);
319
320 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
321
322 auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
323 ASTConsumers.push_back(
324 x: init_convenience::BuildCodeGen(CI&: CI.getCompilerInstance(), LLVMCtx&: *LLVMCtx));
325 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
326
327 if (ShouldDumpAST)
328 ASTConsumers.push_back(x: CreateASTDumper(OS: nullptr /*Dump to stdout.*/, FilterString: "",
329 DumpDecls: true, Deserialize: false, DumpLookups: false, DumpDeclTypes: false,
330 Format: clang::ADOF_Default));
331
332 CI.getDiagnosticClient().BeginSourceFile(
333 LangOpts: CI.getCompilerInstance().getLangOpts(),
334 PP: &CI.getCompilerInstance().getPreprocessor());
335 MultiplexConsumer Consumers(std::move(ASTConsumers));
336 Consumers.Initialize(Context&: CI.getASTContext());
337
338 if (llvm::Error PE = ParseSource(Path, CI&: CI.getCompilerInstance(), Consumer&: Consumers))
339 return std::move(PE);
340 CI.getDiagnosticClient().EndSourceFile();
341 if (ShouldDumpIR)
342 CG.GetModule()->print(OS&: llvm::outs(), AAW: nullptr);
343 if (CI.getDiagnosticClient().getNumErrors())
344 return llvm::make_error<llvm::StringError>(
345 Args: "Errors occurred while parsing the expression.", Args: std::error_code());
346 return std::move(CI);
347}
348
349void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
350 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
351 for (CIAndOrigins &Import : Imports)
352 Sources.push_back(Elt: {Import.getASTContext(), Import.getFileManager(),
353 Import.getOriginMap()});
354 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
355 auto *Merger = static_cast<ExternalASTMerger *>(Source);
356 Merger->RemoveSources(Sources);
357}
358
359} // end namespace
360
361int main(int argc, const char **argv) {
362 const bool DisableCrashReporting = true;
363 llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0], DisableCrashReporting);
364 llvm::cl::ParseCommandLineOptions(argc, argv);
365 std::vector<CIAndOrigins> ImportCIs;
366 for (auto I : Imports) {
367 llvm::Expected<CIAndOrigins> ImportCI = Parse(Path: I, Imports: {}, ShouldDumpAST: false, ShouldDumpIR: false);
368 if (auto E = ImportCI.takeError()) {
369 llvm::errs() << "error: " << llvm::toString(E: std::move(E)) << "\n";
370 exit(status: -1);
371 }
372 ImportCIs.push_back(x: std::move(*ImportCI));
373 }
374 std::vector<CIAndOrigins> IndirectCIs;
375 if (!Direct || UseOrigins) {
376 for (auto &ImportCI : ImportCIs) {
377 CIAndOrigins IndirectCI = BuildIndirect(CI&: ImportCI);
378 IndirectCIs.push_back(x: std::move(IndirectCI));
379 }
380 }
381 if (UseOrigins)
382 for (auto &ImportCI : ImportCIs)
383 IndirectCIs.push_back(x: std::move(ImportCI));
384 llvm::Expected<CIAndOrigins> ExpressionCI =
385 Parse(Path: Expression, Imports: (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
386 ShouldDumpAST: DumpAST, ShouldDumpIR: DumpIR);
387 if (auto E = ExpressionCI.takeError()) {
388 llvm::errs() << "error: " << llvm::toString(E: std::move(E)) << "\n";
389 exit(status: -1);
390 }
391 Forget(CI&: *ExpressionCI, Imports: (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
392 return 0;
393}
394