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