xref: /llvm-project/clang/tools/clang-import-test/clang-import-test.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
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("c++"));
68 
69 static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(false),
70                                    llvm::cl::desc("Emable ObjC ARC"));
71 
72 static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
73                                    llvm::cl::desc("Dump combined AST"));
74 
75 static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(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(Loc, /*Invalid=*/nullptr);
107     unsigned LocColumn =
108         SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
109     FileID FID = SM.getFileID(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(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(llvm::errs(), SM);
141         llvm::errs() << ": ";
142       }
143 
144       SmallString<16> DiagText;
145       Info.FormatDiagnostic(DiagText);
146       llvm::errs() << DiagText << '\n';
147 
148       if (Info.getLocation().isValid()) {
149         PrintSourceForLocation(Info.getLocation(), SM);
150       }
151 
152       for (const CharSourceRange &Range : Info.getRanges()) {
153         bool Invalid = true;
154         StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &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   auto Ins = std::make_unique<CompilerInstance>();
166   auto DC = std::make_unique<TestDiagnosticConsumer>();
167   const bool ShouldOwnClient = true;
168   Ins->createDiagnostics(*llvm::vfs::getRealFileSystem(), DC.release(),
169                          ShouldOwnClient);
170 
171   auto Inv = std::make_unique<CompilerInvocation>();
172 
173   std::vector<const char *> ClangArgv(ClangArgs.size());
174   std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
175                  [](const std::string &s) -> const char * { return s.data(); });
176   CompilerInvocation::CreateFromArgs(*Inv, ClangArgv, Ins->getDiagnostics());
177 
178   {
179     using namespace driver::types;
180     ID Id = lookupTypeForTypeSpecifier(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   Ins->setInvocation(std::move(Inv));
209 
210   TargetInfo *TI = TargetInfo::CreateTargetInfo(
211       Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
212   Ins->setTarget(TI);
213   Ins->getTarget().adjust(Ins->getDiagnostics(), Ins->getLangOpts());
214   Ins->createFileManager();
215   Ins->createSourceManager(Ins->getFileManager());
216   Ins->createPreprocessor(TU_Complete);
217 
218   return Ins;
219 }
220 
221 std::unique_ptr<ASTContext>
222 BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
223   auto &PP = CI.getPreprocessor();
224   auto AST = std::make_unique<ASTContext>(
225       CI.getLangOpts(), CI.getSourceManager(),
226       PP.getIdentifierTable(), ST, BC, PP.TUKind);
227   AST->InitBuiltinTypes(CI.getTarget());
228   return AST;
229 }
230 
231 std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
232                                             llvm::LLVMContext &LLVMCtx) {
233   StringRef ModuleName("$__module");
234   return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
235       CI.getDiagnostics(), ModuleName, &CI.getVirtualFileSystem(),
236       CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(),
237       LLVMCtx));
238 }
239 } // namespace init_convenience
240 
241 namespace {
242 
243 /// A container for a CompilerInstance (possibly with an ExternalASTMerger
244 /// attached to its ASTContext).
245 ///
246 /// Provides an accessor for the DeclContext origins associated with the
247 /// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
248 /// attached).
249 ///
250 /// This is the main unit of parsed source code maintained by clang-import-test.
251 struct CIAndOrigins {
252   using OriginMap = clang::ExternalASTMerger::OriginMap;
253   std::unique_ptr<CompilerInstance> CI;
254 
255   ASTContext &getASTContext() { return CI->getASTContext(); }
256   FileManager &getFileManager() { return CI->getFileManager(); }
257   const OriginMap &getOriginMap() {
258     static const OriginMap EmptyOriginMap{};
259     if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
260       return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
261     return EmptyOriginMap;
262   }
263   DiagnosticConsumer &getDiagnosticClient() {
264     return CI->getDiagnosticClient();
265   }
266   CompilerInstance &getCompilerInstance() { return *CI; }
267 };
268 
269 void AddExternalSource(CIAndOrigins &CI,
270                        llvm::MutableArrayRef<CIAndOrigins> Imports) {
271   ExternalASTMerger::ImporterTarget Target(
272       {CI.getASTContext(), CI.getFileManager()});
273   llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
274   for (CIAndOrigins &Import : Imports)
275     Sources.emplace_back(Import.getASTContext(), Import.getFileManager(),
276                          Import.getOriginMap());
277   auto ES = std::make_unique<ExternalASTMerger>(Target, Sources);
278   CI.getASTContext().setExternalSource(ES.release());
279   CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
280 }
281 
282 CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
283   CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
284   auto ST = std::make_unique<SelectorTable>();
285   auto BC = std::make_unique<Builtin::Context>();
286   std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
287       IndirectCI.getCompilerInstance(), *ST, *BC);
288   IndirectCI.getCompilerInstance().setASTContext(AST.release());
289   AddExternalSource(IndirectCI, CI);
290   return IndirectCI;
291 }
292 
293 llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
294                         ASTConsumer &Consumer) {
295   SourceManager &SM = CI.getSourceManager();
296   auto FE = CI.getFileManager().getFileRef(Path);
297   if (!FE) {
298     llvm::consumeError(FE.takeError());
299     return llvm::make_error<llvm::StringError>(
300         llvm::Twine("No such file or directory: ", Path), std::error_code());
301   }
302   SM.setMainFileID(SM.createFileID(*FE, SourceLocation(), SrcMgr::C_User));
303   ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
304   return llvm::Error::success();
305 }
306 
307 llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
308                                    llvm::MutableArrayRef<CIAndOrigins> Imports,
309                                    bool ShouldDumpAST, bool ShouldDumpIR) {
310   CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
311   auto ST = std::make_unique<SelectorTable>();
312   auto BC = std::make_unique<Builtin::Context>();
313   std::unique_ptr<ASTContext> AST =
314       init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
315   CI.getCompilerInstance().setASTContext(AST.release());
316   if (Imports.size())
317     AddExternalSource(CI, Imports);
318 
319   std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
320 
321   auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
322   ASTConsumers.push_back(
323       init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
324   auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
325 
326   if (ShouldDumpAST)
327     ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/, "",
328                                            true, false, false, false,
329                                            clang::ADOF_Default));
330 
331   CI.getDiagnosticClient().BeginSourceFile(
332       CI.getCompilerInstance().getLangOpts(),
333       &CI.getCompilerInstance().getPreprocessor());
334   MultiplexConsumer Consumers(std::move(ASTConsumers));
335   Consumers.Initialize(CI.getASTContext());
336 
337   if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
338     return std::move(PE);
339   CI.getDiagnosticClient().EndSourceFile();
340   if (ShouldDumpIR)
341     CG.GetModule()->print(llvm::outs(), nullptr);
342   if (CI.getDiagnosticClient().getNumErrors())
343     return llvm::make_error<llvm::StringError>(
344         "Errors occurred while parsing the expression.", std::error_code());
345   return std::move(CI);
346 }
347 
348 void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
349   llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
350   for (CIAndOrigins &Import : Imports)
351     Sources.push_back({Import.getASTContext(), Import.getFileManager(),
352                        Import.getOriginMap()});
353   ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
354   auto *Merger = static_cast<ExternalASTMerger *>(Source);
355   Merger->RemoveSources(Sources);
356 }
357 
358 } // end namespace
359 
360 int main(int argc, const char **argv) {
361   const bool DisableCrashReporting = true;
362   llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
363   llvm::cl::ParseCommandLineOptions(argc, argv);
364   std::vector<CIAndOrigins> ImportCIs;
365   for (auto I : Imports) {
366     llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
367     if (auto E = ImportCI.takeError()) {
368       llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n";
369       exit(-1);
370     }
371     ImportCIs.push_back(std::move(*ImportCI));
372   }
373   std::vector<CIAndOrigins> IndirectCIs;
374   if (!Direct || UseOrigins) {
375     for (auto &ImportCI : ImportCIs) {
376       CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
377       IndirectCIs.push_back(std::move(IndirectCI));
378     }
379   }
380   if (UseOrigins)
381     for (auto &ImportCI : ImportCIs)
382       IndirectCIs.push_back(std::move(ImportCI));
383   llvm::Expected<CIAndOrigins> ExpressionCI =
384       Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
385             DumpAST, DumpIR);
386   if (auto E = ExpressionCI.takeError()) {
387     llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n";
388     exit(-1);
389   }
390   Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
391   return 0;
392 }
393