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/Host.h"
34 #include "llvm/Support/Signals.h"
35
36 #include <memory>
37 #include <string>
38
39 using namespace clang;
40
41 static 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
45 static llvm::cl::list<std::string>
46 Imports("import", llvm::cl::ZeroOrMore,
47 llvm::cl::desc("Path to a file containing declarations to import"));
48
49 static llvm::cl::opt<bool>
50 Direct("direct", llvm::cl::Optional,
51 llvm::cl::desc("Use the parsed declarations without indirection"));
52
53 static 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
58 static llvm::cl::list<std::string>
59 ClangArgs("Xcc", llvm::cl::ZeroOrMore,
60 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
61 llvm::cl::CommaSeparated);
62
63 static 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("c++"));
67
68 static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(false),
69 llvm::cl::desc("Emable ObjC ARC"));
70
71 static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
72 llvm::cl::desc("Dump combined AST"));
73
74 static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
75 llvm::cl::desc("Dump IR from final parse"));
76
77 namespace init_convenience {
78 class TestDiagnosticConsumer : public DiagnosticConsumer {
79 private:
80 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
81 const LangOptions *LangOpts = nullptr;
82
83 public:
TestDiagnosticConsumer()84 TestDiagnosticConsumer()
85 : Passthrough(std::make_unique<TextDiagnosticBuffer>()) {}
86
BeginSourceFile(const LangOptions & LangOpts,const Preprocessor * PP=nullptr)87 virtual void BeginSourceFile(const LangOptions &LangOpts,
88 const Preprocessor *PP = nullptr) override {
89 this->LangOpts = &LangOpts;
90 return Passthrough->BeginSourceFile(LangOpts, PP);
91 }
92
EndSourceFile()93 virtual void EndSourceFile() override {
94 this->LangOpts = nullptr;
95 Passthrough->EndSourceFile();
96 }
97
IncludeInDiagnosticCounts() const98 virtual bool IncludeInDiagnosticCounts() const override {
99 return Passthrough->IncludeInDiagnosticCounts();
100 }
101
102 private:
PrintSourceForLocation(const SourceLocation & Loc,SourceManager & SM)103 static void PrintSourceForLocation(const SourceLocation &Loc,
104 SourceManager &SM) {
105 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
106 unsigned LocColumn =
107 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
108 FileID FID = SM.getFileID(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(LocColumn);
129 llvm::errs() << '^';
130 llvm::errs() << '\n';
131 }
132
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)133 virtual 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(llvm::errs(), SM);
140 llvm::errs() << ": ";
141 }
142
143 SmallString<16> DiagText;
144 Info.FormatDiagnostic(DiagText);
145 llvm::errs() << DiagText << '\n';
146
147 if (Info.getLocation().isValid()) {
148 PrintSourceForLocation(Info.getLocation(), SM);
149 }
150
151 for (const CharSourceRange &Range : Info.getRanges()) {
152 bool Invalid = true;
153 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
154 if (!Invalid) {
155 llvm::errs() << Ref << '\n';
156 }
157 }
158 }
159 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
160 }
161 };
162
BuildCompilerInstance()163 std::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(DC.release(), ShouldOwnClient);
168
169 auto Inv = std::make_unique<CompilerInvocation>();
170
171 std::vector<const char *> ClangArgv(ClangArgs.size());
172 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
173 [](const std::string &s) -> const char * { return s.data(); });
174 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv, Ins->getDiagnostics());
175
176 {
177 using namespace driver::types;
178 ID Id = lookupTypeForTypeSpecifier(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(codegenoptions::FullDebugInfo);
204 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
205
206 Ins->setInvocation(std::move(Inv));
207
208 TargetInfo *TI = TargetInfo::CreateTargetInfo(
209 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
210 Ins->setTarget(TI);
211 Ins->getTarget().adjust(Ins->getLangOpts());
212 Ins->createFileManager();
213 Ins->createSourceManager(Ins->getFileManager());
214 Ins->createPreprocessor(TU_Complete);
215
216 return Ins;
217 }
218
219 std::unique_ptr<ASTContext>
BuildASTContext(CompilerInstance & CI,SelectorTable & ST,Builtin::Context & BC)220 BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
221 auto AST = std::make_unique<ASTContext>(
222 CI.getLangOpts(), CI.getSourceManager(),
223 CI.getPreprocessor().getIdentifierTable(), ST, BC);
224 AST->InitBuiltinTypes(CI.getTarget());
225 return AST;
226 }
227
BuildCodeGen(CompilerInstance & CI,llvm::LLVMContext & LLVMCtx)228 std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
229 llvm::LLVMContext &LLVMCtx) {
230 StringRef ModuleName("$__module");
231 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
232 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
233 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
234 }
235 } // namespace init_convenience
236
237 namespace {
238
239 /// A container for a CompilerInstance (possibly with an ExternalASTMerger
240 /// attached to its ASTContext).
241 ///
242 /// Provides an accessor for the DeclContext origins associated with the
243 /// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
244 /// attached).
245 ///
246 /// This is the main unit of parsed source code maintained by clang-import-test.
247 struct CIAndOrigins {
248 using OriginMap = clang::ExternalASTMerger::OriginMap;
249 std::unique_ptr<CompilerInstance> CI;
250
getASTContext__anon5a8deb180211::CIAndOrigins251 ASTContext &getASTContext() { return CI->getASTContext(); }
getFileManager__anon5a8deb180211::CIAndOrigins252 FileManager &getFileManager() { return CI->getFileManager(); }
getOriginMap__anon5a8deb180211::CIAndOrigins253 const OriginMap &getOriginMap() {
254 static const OriginMap EmptyOriginMap{};
255 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
256 return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
257 return EmptyOriginMap;
258 }
getDiagnosticClient__anon5a8deb180211::CIAndOrigins259 DiagnosticConsumer &getDiagnosticClient() {
260 return CI->getDiagnosticClient();
261 }
getCompilerInstance__anon5a8deb180211::CIAndOrigins262 CompilerInstance &getCompilerInstance() { return *CI; }
263 };
264
AddExternalSource(CIAndOrigins & CI,llvm::MutableArrayRef<CIAndOrigins> Imports)265 void AddExternalSource(CIAndOrigins &CI,
266 llvm::MutableArrayRef<CIAndOrigins> Imports) {
267 ExternalASTMerger::ImporterTarget Target(
268 {CI.getASTContext(), CI.getFileManager()});
269 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
270 for (CIAndOrigins &Import : Imports)
271 Sources.emplace_back(Import.getASTContext(), Import.getFileManager(),
272 Import.getOriginMap());
273 auto ES = std::make_unique<ExternalASTMerger>(Target, Sources);
274 CI.getASTContext().setExternalSource(ES.release());
275 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
276 }
277
BuildIndirect(CIAndOrigins & CI)278 CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
279 CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
280 auto ST = std::make_unique<SelectorTable>();
281 auto BC = std::make_unique<Builtin::Context>();
282 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
283 IndirectCI.getCompilerInstance(), *ST, *BC);
284 IndirectCI.getCompilerInstance().setASTContext(AST.release());
285 AddExternalSource(IndirectCI, CI);
286 return IndirectCI;
287 }
288
ParseSource(const std::string & Path,CompilerInstance & CI,ASTConsumer & Consumer)289 llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
290 ASTConsumer &Consumer) {
291 SourceManager &SM = CI.getSourceManager();
292 auto FE = CI.getFileManager().getFileRef(Path);
293 if (!FE) {
294 llvm::consumeError(FE.takeError());
295 return llvm::make_error<llvm::StringError>(
296 llvm::Twine("No such file or directory: ", Path), std::error_code());
297 }
298 SM.setMainFileID(SM.createFileID(*FE, SourceLocation(), SrcMgr::C_User));
299 ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
300 return llvm::Error::success();
301 }
302
Parse(const std::string & Path,llvm::MutableArrayRef<CIAndOrigins> Imports,bool ShouldDumpAST,bool ShouldDumpIR)303 llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
304 llvm::MutableArrayRef<CIAndOrigins> Imports,
305 bool ShouldDumpAST, bool ShouldDumpIR) {
306 CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
307 auto ST = std::make_unique<SelectorTable>();
308 auto BC = std::make_unique<Builtin::Context>();
309 std::unique_ptr<ASTContext> AST =
310 init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
311 CI.getCompilerInstance().setASTContext(AST.release());
312 if (Imports.size())
313 AddExternalSource(CI, Imports);
314
315 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
316
317 auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
318 ASTConsumers.push_back(
319 init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
320 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
321
322 if (ShouldDumpAST)
323 ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/, "",
324 true, false, false, false,
325 clang::ADOF_Default));
326
327 CI.getDiagnosticClient().BeginSourceFile(
328 CI.getCompilerInstance().getLangOpts(),
329 &CI.getCompilerInstance().getPreprocessor());
330 MultiplexConsumer Consumers(std::move(ASTConsumers));
331 Consumers.Initialize(CI.getASTContext());
332
333 if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
334 return std::move(PE);
335 CI.getDiagnosticClient().EndSourceFile();
336 if (ShouldDumpIR)
337 CG.GetModule()->print(llvm::outs(), nullptr);
338 if (CI.getDiagnosticClient().getNumErrors())
339 return llvm::make_error<llvm::StringError>(
340 "Errors occurred while parsing the expression.", std::error_code());
341 return std::move(CI);
342 }
343
Forget(CIAndOrigins & CI,llvm::MutableArrayRef<CIAndOrigins> Imports)344 void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
345 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
346 for (CIAndOrigins &Import : Imports)
347 Sources.push_back({Import.getASTContext(), Import.getFileManager(),
348 Import.getOriginMap()});
349 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
350 auto *Merger = static_cast<ExternalASTMerger *>(Source);
351 Merger->RemoveSources(Sources);
352 }
353
354 } // end namespace
355
main(int argc,const char ** argv)356 int main(int argc, const char **argv) {
357 const bool DisableCrashReporting = true;
358 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
359 llvm::cl::ParseCommandLineOptions(argc, argv);
360 std::vector<CIAndOrigins> ImportCIs;
361 for (auto I : Imports) {
362 llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
363 if (auto E = ImportCI.takeError()) {
364 llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n";
365 exit(-1);
366 }
367 ImportCIs.push_back(std::move(*ImportCI));
368 }
369 std::vector<CIAndOrigins> IndirectCIs;
370 if (!Direct || UseOrigins) {
371 for (auto &ImportCI : ImportCIs) {
372 CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
373 IndirectCIs.push_back(std::move(IndirectCI));
374 }
375 }
376 if (UseOrigins)
377 for (auto &ImportCI : ImportCIs)
378 IndirectCIs.push_back(std::move(ImportCI));
379 llvm::Expected<CIAndOrigins> ExpressionCI =
380 Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
381 DumpAST, DumpIR);
382 if (auto E = ExpressionCI.takeError()) {
383 llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n";
384 exit(-1);
385 }
386 Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
387 return 0;
388 }
389