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 | |
40 | using namespace clang; |
41 | |
42 | static 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 | |
46 | static llvm::cl::list<std::string> |
47 | Imports("import", |
48 | llvm::cl::desc("Path to a file containing declarations to import")); |
49 | |
50 | static llvm::cl::opt<bool> |
51 | Direct("direct", llvm::cl::Optional, |
52 | llvm::cl::desc("Use the parsed declarations without indirection")); |
53 | |
54 | static 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 | |
59 | static llvm::cl::list<std::string> |
60 | ClangArgs("Xcc", |
61 | llvm::cl::desc("Argument to pass to the CompilerInvocation"), |
62 | llvm::cl::CommaSeparated); |
63 | |
64 | static 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 | |
69 | static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(Val: false), |
70 | llvm::cl::desc("Emable ObjC ARC")); |
71 | |
72 | static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(Val: false), |
73 | llvm::cl::desc("Dump combined AST")); |
74 | |
75 | static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(Val: false), |
76 | llvm::cl::desc("Dump IR from final parse")); |
77 | |
78 | namespace init_convenience { |
79 | class TestDiagnosticConsumer : public DiagnosticConsumer { |
80 | private: |
81 | std::unique_ptr<TextDiagnosticBuffer> Passthrough; |
82 | const LangOptions *LangOpts = nullptr; |
83 | |
84 | public: |
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 | |
103 | private: |
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 | |
164 | std::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->createDiagnostics(VFS&: *llvm::vfs::getRealFileSystem(), Client: DC.release(), |
211 | /*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 | Ins->createFileManager(); |
218 | Ins->createSourceManager(FileMgr&: Ins->getFileManager()); |
219 | Ins->createPreprocessor(TUKind: TU_Complete); |
220 | |
221 | return Ins; |
222 | } |
223 | |
224 | std::unique_ptr<ASTContext> |
225 | BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) { |
226 | auto &PP = CI.getPreprocessor(); |
227 | auto AST = std::make_unique<ASTContext>( |
228 | args&: CI.getLangOpts(), args&: CI.getSourceManager(), |
229 | args&: PP.getIdentifierTable(), args&: ST, args&: BC, args: PP.TUKind); |
230 | AST->InitBuiltinTypes(Target: CI.getTarget()); |
231 | return AST; |
232 | } |
233 | |
234 | std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI, |
235 | llvm::LLVMContext &LLVMCtx) { |
236 | StringRef ModuleName("$__module"); |
237 | return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen( |
238 | Diags&: CI.getDiagnostics(), ModuleName, FS: &CI.getVirtualFileSystem(), |
239 | HeaderSearchOpts: CI.getHeaderSearchOpts(), PreprocessorOpts: CI.getPreprocessorOpts(), CGO: CI.getCodeGenOpts(), |
240 | C&: LLVMCtx)); |
241 | } |
242 | } // namespace init_convenience |
243 | |
244 | namespace { |
245 | |
246 | /// A container for a CompilerInstance (possibly with an ExternalASTMerger |
247 | /// attached to its ASTContext). |
248 | /// |
249 | /// Provides an accessor for the DeclContext origins associated with the |
250 | /// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is |
251 | /// attached). |
252 | /// |
253 | /// This is the main unit of parsed source code maintained by clang-import-test. |
254 | struct CIAndOrigins { |
255 | using OriginMap = clang::ExternalASTMerger::OriginMap; |
256 | std::unique_ptr<CompilerInstance> CI; |
257 | |
258 | ASTContext &getASTContext() { return CI->getASTContext(); } |
259 | FileManager &getFileManager() { return CI->getFileManager(); } |
260 | const OriginMap &getOriginMap() { |
261 | static const OriginMap EmptyOriginMap{}; |
262 | if (ExternalASTSource *Source = CI->getASTContext().getExternalSource()) |
263 | return static_cast<ExternalASTMerger *>(Source)->GetOrigins(); |
264 | return EmptyOriginMap; |
265 | } |
266 | DiagnosticConsumer &getDiagnosticClient() { |
267 | return CI->getDiagnosticClient(); |
268 | } |
269 | CompilerInstance &getCompilerInstance() { return *CI; } |
270 | }; |
271 | |
272 | void AddExternalSource(CIAndOrigins &CI, |
273 | llvm::MutableArrayRef<CIAndOrigins> Imports) { |
274 | ExternalASTMerger::ImporterTarget Target( |
275 | {.AST: CI.getASTContext(), .FM: CI.getFileManager()}); |
276 | llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources; |
277 | for (CIAndOrigins &Import : Imports) |
278 | Sources.emplace_back(Args&: Import.getASTContext(), Args&: Import.getFileManager(), |
279 | Args: Import.getOriginMap()); |
280 | auto ES = std::make_unique<ExternalASTMerger>(args&: Target, args&: Sources); |
281 | CI.getASTContext().setExternalSource(ES.release()); |
282 | CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); |
283 | } |
284 | |
285 | CIAndOrigins BuildIndirect(CIAndOrigins &CI) { |
286 | CIAndOrigins IndirectCI{.CI: init_convenience::BuildCompilerInstance()}; |
287 | auto ST = std::make_unique<SelectorTable>(); |
288 | auto BC = std::make_unique<Builtin::Context>(); |
289 | std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext( |
290 | CI&: IndirectCI.getCompilerInstance(), ST&: *ST, BC&: *BC); |
291 | IndirectCI.getCompilerInstance().setASTContext(AST.release()); |
292 | AddExternalSource(CI&: IndirectCI, Imports: CI); |
293 | return IndirectCI; |
294 | } |
295 | |
296 | llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, |
297 | ASTConsumer &Consumer) { |
298 | SourceManager &SM = CI.getSourceManager(); |
299 | auto FE = CI.getFileManager().getFileRef(Filename: Path); |
300 | if (!FE) { |
301 | llvm::consumeError(Err: FE.takeError()); |
302 | return llvm::make_error<llvm::StringError>( |
303 | Args: llvm::Twine("No such file or directory: ", Path), Args: std::error_code()); |
304 | } |
305 | SM.setMainFileID(SM.createFileID(SourceFile: *FE, IncludePos: SourceLocation(), FileCharacter: SrcMgr::C_User)); |
306 | ParseAST(pp&: CI.getPreprocessor(), C: &Consumer, Ctx&: CI.getASTContext()); |
307 | return llvm::Error::success(); |
308 | } |
309 | |
310 | llvm::Expected<CIAndOrigins> Parse(const std::string &Path, |
311 | llvm::MutableArrayRef<CIAndOrigins> Imports, |
312 | bool ShouldDumpAST, bool ShouldDumpIR) { |
313 | CIAndOrigins CI{.CI: init_convenience::BuildCompilerInstance()}; |
314 | auto ST = std::make_unique<SelectorTable>(); |
315 | auto BC = std::make_unique<Builtin::Context>(); |
316 | std::unique_ptr<ASTContext> AST = |
317 | init_convenience::BuildASTContext(CI&: CI.getCompilerInstance(), ST&: *ST, BC&: *BC); |
318 | CI.getCompilerInstance().setASTContext(AST.release()); |
319 | if (Imports.size()) |
320 | AddExternalSource(CI, Imports); |
321 | |
322 | std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers; |
323 | |
324 | auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); |
325 | ASTConsumers.push_back( |
326 | x: init_convenience::BuildCodeGen(CI&: CI.getCompilerInstance(), LLVMCtx&: *LLVMCtx)); |
327 | auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get()); |
328 | |
329 | if (ShouldDumpAST) |
330 | ASTConsumers.push_back(x: CreateASTDumper(OS: nullptr /*Dump to stdout.*/, FilterString: "", |
331 | DumpDecls: true, Deserialize: false, DumpLookups: false, DumpDeclTypes: false, |
332 | Format: clang::ADOF_Default)); |
333 | |
334 | CI.getDiagnosticClient().BeginSourceFile( |
335 | LangOpts: CI.getCompilerInstance().getLangOpts(), |
336 | PP: &CI.getCompilerInstance().getPreprocessor()); |
337 | MultiplexConsumer Consumers(std::move(ASTConsumers)); |
338 | Consumers.Initialize(Context&: CI.getASTContext()); |
339 | |
340 | if (llvm::Error PE = ParseSource(Path, CI&: CI.getCompilerInstance(), Consumer&: Consumers)) |
341 | return std::move(PE); |
342 | CI.getDiagnosticClient().EndSourceFile(); |
343 | if (ShouldDumpIR) |
344 | CG.GetModule()->print(OS&: llvm::outs(), AAW: nullptr); |
345 | if (CI.getDiagnosticClient().getNumErrors()) |
346 | return llvm::make_error<llvm::StringError>( |
347 | Args: "Errors occurred while parsing the expression.", Args: std::error_code()); |
348 | return std::move(CI); |
349 | } |
350 | |
351 | void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) { |
352 | llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources; |
353 | for (CIAndOrigins &Import : Imports) |
354 | Sources.push_back(Elt: {Import.getASTContext(), Import.getFileManager(), |
355 | Import.getOriginMap()}); |
356 | ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource(); |
357 | auto *Merger = static_cast<ExternalASTMerger *>(Source); |
358 | Merger->RemoveSources(Sources); |
359 | } |
360 | |
361 | } // end namespace |
362 | |
363 | int main(int argc, const char **argv) { |
364 | const bool DisableCrashReporting = true; |
365 | llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0], DisableCrashReporting); |
366 | llvm::cl::ParseCommandLineOptions(argc, argv); |
367 | std::vector<CIAndOrigins> ImportCIs; |
368 | for (auto I : Imports) { |
369 | llvm::Expected<CIAndOrigins> ImportCI = Parse(Path: I, Imports: {}, ShouldDumpAST: false, ShouldDumpIR: false); |
370 | if (auto E = ImportCI.takeError()) { |
371 | llvm::errs() << "error: "<< llvm::toString(E: std::move(E)) << "\n"; |
372 | exit(status: -1); |
373 | } |
374 | ImportCIs.push_back(x: std::move(*ImportCI)); |
375 | } |
376 | std::vector<CIAndOrigins> IndirectCIs; |
377 | if (!Direct || UseOrigins) { |
378 | for (auto &ImportCI : ImportCIs) { |
379 | CIAndOrigins IndirectCI = BuildIndirect(CI&: ImportCI); |
380 | IndirectCIs.push_back(x: std::move(IndirectCI)); |
381 | } |
382 | } |
383 | if (UseOrigins) |
384 | for (auto &ImportCI : ImportCIs) |
385 | IndirectCIs.push_back(x: std::move(ImportCI)); |
386 | llvm::Expected<CIAndOrigins> ExpressionCI = |
387 | Parse(Path: Expression, Imports: (Direct && !UseOrigins) ? ImportCIs : IndirectCIs, |
388 | ShouldDumpAST: DumpAST, ShouldDumpIR: DumpIR); |
389 | if (auto E = ExpressionCI.takeError()) { |
390 | llvm::errs() << "error: "<< llvm::toString(E: std::move(E)) << "\n"; |
391 | exit(status: -1); |
392 | } |
393 | Forget(CI&: *ExpressionCI, Imports: (Direct && !UseOrigins) ? ImportCIs : IndirectCIs); |
394 | return 0; |
395 | } |
396 |
Definitions
- Expression
- Imports
- Direct
- UseOrigins
- ClangArgs
- Input
- ObjCARC
- DumpAST
- DumpIR
- TestDiagnosticConsumer
- TestDiagnosticConsumer
- BeginSourceFile
- EndSourceFile
- IncludeInDiagnosticCounts
- PrintSourceForLocation
- HandleDiagnostic
- BuildCompilerInstance
- BuildASTContext
- BuildCodeGen
- CIAndOrigins
- getASTContext
- getFileManager
- getOriginMap
- getDiagnosticClient
- getCompilerInstance
- AddExternalSource
- BuildIndirect
- ParseSource
- Parse
- Forget
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more