blob: 33190af4bf459d2868de7693c0ed2d14116da3fe [file] [log] [blame]
Sean Callanan7d982502016-12-22 20:03:14 +00001//===-- 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/Basic/Builtins.h"
13#include "clang/Basic/IdentifierTable.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Basic/TargetInfo.h"
16#include "clang/Basic/TargetOptions.h"
17#include "clang/CodeGen/ModuleBuilder.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Frontend/TextDiagnosticBuffer.h"
20#include "clang/Lex/Lexer.h"
21#include "clang/Lex/Preprocessor.h"
22#include "clang/Parse/ParseAST.h"
23
24#include "llvm/IR/LLVMContext.h"
25#include "llvm/Support/CommandLine.h"
26#include "llvm/Support/Error.h"
27#include "llvm/Support/Host.h"
28#include "llvm/Support/Signals.h"
29
30#include <memory>
31#include <string>
32
33using namespace clang;
34
35static llvm::cl::opt<std::string> Expression(
36 "expression", llvm::cl::Required,
37 llvm::cl::desc("Path to a file containing the expression to parse"));
38
39static llvm::cl::list<std::string>
40 Imports("import", llvm::cl::ZeroOrMore,
41 llvm::cl::desc("Path to a file containing declarations to import"));
42
43static llvm::cl::list<std::string>
44 ClangArgs("Xcc", llvm::cl::ZeroOrMore,
45 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
46 llvm::cl::CommaSeparated);
47
48namespace init_convenience {
49class TestDiagnosticConsumer : public DiagnosticConsumer {
50private:
51 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
52 const LangOptions *LangOpts = nullptr;
53
54public:
55 TestDiagnosticConsumer()
56 : Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {}
57
58 virtual void BeginSourceFile(const LangOptions &LangOpts,
59 const Preprocessor *PP = nullptr) override {
60 this->LangOpts = &LangOpts;
61 return Passthrough->BeginSourceFile(LangOpts, PP);
62 }
63
64 virtual void EndSourceFile() override {
65 this->LangOpts = nullptr;
66 Passthrough->EndSourceFile();
67 }
68
69 virtual bool IncludeInDiagnosticCounts() const override {
70 return Passthrough->IncludeInDiagnosticCounts();
71 }
72
73private:
74 static void PrintSourceForLocation(const SourceLocation &Loc,
75 SourceManager &SM) {
76 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
77 unsigned LocColumn =
78 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
79 FileID FID = SM.getFileID(Loc);
80 llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr);
81
82 assert(LocData >= Buffer->getBufferStart() &&
83 LocData < Buffer->getBufferEnd());
84
85 const char *LineBegin = LocData - LocColumn;
86
87 assert(LineBegin >= Buffer->getBufferStart());
88
89 const char *LineEnd = nullptr;
90
91 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
92 LineEnd < Buffer->getBufferEnd();
93 ++LineEnd)
94 ;
95
96 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
97
98 llvm::errs() << LineString << '\n';
99 llvm::errs().indent(LocColumn);
100 llvm::errs() << '^';
101 }
102
103 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
104 const Diagnostic &Info) override {
105 if (Info.hasSourceManager() && LangOpts) {
106 SourceManager &SM = Info.getSourceManager();
107
108 if (Info.getLocation().isValid()) {
109 Info.getLocation().print(llvm::errs(), SM);
110 llvm::errs() << ": ";
111 }
112
113 SmallString<16> DiagText;
114 Info.FormatDiagnostic(DiagText);
115 llvm::errs() << DiagText << '\n';
116
117 if (Info.getLocation().isValid()) {
118 PrintSourceForLocation(Info.getLocation(), SM);
119 }
120
121 for (const CharSourceRange &Range : Info.getRanges()) {
122 bool Invalid = true;
123 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
124 if (!Invalid) {
125 llvm::errs() << Ref << '\n';
126 }
127 }
128 }
129 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
130 }
131};
132
133std::unique_ptr<CompilerInstance>
134BuildCompilerInstance(ArrayRef<const char *> ClangArgv) {
135 auto Ins = llvm::make_unique<CompilerInstance>();
136 auto DC = llvm::make_unique<TestDiagnosticConsumer>();
137 const bool ShouldOwnClient = true;
138 Ins->createDiagnostics(DC.release(), ShouldOwnClient);
139
140 auto Inv = llvm::make_unique<CompilerInvocation>();
141
142 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
143 &ClangArgv.data()[ClangArgv.size()],
144 Ins->getDiagnostics());
145
146 Inv->getLangOpts()->CPlusPlus = true;
147 Inv->getLangOpts()->CPlusPlus11 = true;
148 Inv->getHeaderSearchOpts().UseLibcxx = true;
149 Inv->getLangOpts()->Bool = true;
150 Inv->getLangOpts()->WChar = true;
151 Inv->getLangOpts()->Blocks = true;
152 Inv->getLangOpts()->DebuggerSupport = true;
153 Inv->getLangOpts()->SpellChecking = false;
154 Inv->getLangOpts()->ThreadsafeStatics = false;
155 Inv->getLangOpts()->AccessControl = false;
156 Inv->getLangOpts()->DollarIdents = true;
157 Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
158 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
159
David Blaikieea4395e2017-01-06 19:49:01 +0000160 Ins->setInvocation(std::move(Inv));
Sean Callanan7d982502016-12-22 20:03:14 +0000161
162 TargetInfo *TI = TargetInfo::CreateTargetInfo(
163 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
164 Ins->setTarget(TI);
165 Ins->getTarget().adjust(Ins->getLangOpts());
166 Ins->createFileManager();
167 Ins->createSourceManager(Ins->getFileManager());
168 Ins->createPreprocessor(TU_Complete);
169
170 return Ins;
171}
172
173std::unique_ptr<ASTContext>
174BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
175 auto AST = llvm::make_unique<ASTContext>(
176 CI.getLangOpts(), CI.getSourceManager(),
177 CI.getPreprocessor().getIdentifierTable(), ST, BC);
178 AST->InitBuiltinTypes(CI.getTarget());
179 return AST;
180}
181
182std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
183 llvm::LLVMContext &LLVMCtx) {
184 StringRef ModuleName("$__module");
185 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
186 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
187 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
188}
189} // end namespace
190
191namespace {
192class TestExternalASTSource : public ExternalASTSource {
193private:
194 llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs;
195 std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ForwardImporters;
196 std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ReverseImporters;
197
198public:
199 TestExternalASTSource(
200 CompilerInstance &ExpressionCI,
201 llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs)
202 : ImportCIs(ImportCIs) {
203 for (const std::unique_ptr<CompilerInstance> &ImportCI : ImportCIs) {
204 ForwardImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>(
205 ExpressionCI.getASTContext(), ExpressionCI.getFileManager(),
206 ImportCI->getASTContext(), ImportCI->getFileManager(),
207 /*MinimalImport=*/true);
208 ReverseImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>(
209 ImportCI->getASTContext(), ImportCI->getFileManager(),
210 ExpressionCI.getASTContext(), ExpressionCI.getFileManager(),
211 /*MinimalImport=*/true);
212 }
213 }
214
215 bool FindExternalVisibleDeclsByName(const DeclContext *DC,
216 DeclarationName Name) override {
217 llvm::SmallVector<NamedDecl *, 1> Decls;
218
219 if (isa<TranslationUnitDecl>(DC)) {
220 for (const std::unique_ptr<CompilerInstance> &I : ImportCIs) {
221 DeclarationName FromName = ReverseImporters[I.get()]->Import(Name);
222 DeclContextLookupResult Result =
223 I->getASTContext().getTranslationUnitDecl()->lookup(FromName);
224 for (NamedDecl *FromD : Result) {
225 NamedDecl *D =
226 llvm::cast<NamedDecl>(ForwardImporters[I.get()]->Import(FromD));
227 Decls.push_back(D);
228 }
229 }
230 }
231 if (Decls.empty()) {
232 return false;
233 } else {
234 SetExternalVisibleDeclsForName(DC, Name, Decls);
235 return true;
236 }
237 }
238};
239
240void AddExternalSource(
241 CompilerInstance &CI,
242 llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
243 ASTContext &AST = CI.getASTContext();
244 auto ES = llvm::make_unique<TestExternalASTSource>(CI, Imports);
245 AST.setExternalSource(ES.release());
246 AST.getTranslationUnitDecl()->setHasExternalVisibleStorage();
247}
248
249llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
250 CodeGenerator &CG) {
251 SourceManager &SM = CI.getSourceManager();
252 const FileEntry *FE = CI.getFileManager().getFile(Path);
253 if (!FE) {
254 return llvm::make_error<llvm::StringError>(
255 llvm::Twine("Couldn't open ", Path), std::error_code());
256 }
257 SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
258 ParseAST(CI.getPreprocessor(), &CG, CI.getASTContext());
259 return llvm::Error::success();
260}
261
262llvm::Expected<std::unique_ptr<CompilerInstance>>
263Parse(const std::string &Path,
264 llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
265 std::vector<const char *> ClangArgv(ClangArgs.size());
266 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
267 [](const std::string &s) -> const char * { return s.data(); });
268 std::unique_ptr<CompilerInstance> CI =
269 init_convenience::BuildCompilerInstance(ClangArgv);
270 auto ST = llvm::make_unique<SelectorTable>();
271 auto BC = llvm::make_unique<Builtin::Context>();
272 std::unique_ptr<ASTContext> AST =
273 init_convenience::BuildASTContext(*CI, *ST, *BC);
274 CI->setASTContext(AST.release());
275 AddExternalSource(*CI, Imports);
276
277 auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
278 std::unique_ptr<CodeGenerator> CG =
279 init_convenience::BuildCodeGen(*CI, *LLVMCtx);
280 CG->Initialize(CI->getASTContext());
281
282 CI->getDiagnosticClient().BeginSourceFile(CI->getLangOpts(),
283 &CI->getPreprocessor());
284 if (llvm::Error PE = ParseSource(Path, *CI, *CG)) {
285 return std::move(PE);
286 }
287 CI->getDiagnosticClient().EndSourceFile();
288 if (CI->getDiagnosticClient().getNumErrors()) {
289 return llvm::make_error<llvm::StringError>(
290 "Errors occured while parsing the expression.", std::error_code());
291 } else {
292 return std::move(CI);
293 }
294}
295} // end namespace
296
297int main(int argc, const char **argv) {
298 const bool DisableCrashReporting = true;
299 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
300 llvm::cl::ParseCommandLineOptions(argc, argv);
301 std::vector<std::unique_ptr<CompilerInstance>> ImportCIs;
302 for (auto I : Imports) {
303 llvm::Expected<std::unique_ptr<CompilerInstance>> ImportCI = Parse(I, {});
304 if (auto E = ImportCI.takeError()) {
305 llvm::errs() << llvm::toString(std::move(E));
306 exit(-1);
307 } else {
308 ImportCIs.push_back(std::move(*ImportCI));
309 }
310 }
311 llvm::Expected<std::unique_ptr<CompilerInstance>> ExpressionCI =
312 Parse(Expression, ImportCIs);
313 if (auto E = ExpressionCI.takeError()) {
314 llvm::errs() << llvm::toString(std::move(E));
315 exit(-1);
316 } else {
317 return 0;
318 }
319}