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