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