blob: d7ab18478c3234fef29b296f03cde0a4401bfd7c [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
45static llvm::cl::list<std::string>
46 ClangArgs("Xcc", llvm::cl::ZeroOrMore,
47 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
48 llvm::cl::CommaSeparated);
49
50namespace init_convenience {
51class TestDiagnosticConsumer : public DiagnosticConsumer {
52private:
53 std::unique_ptr<TextDiagnosticBuffer> Passthrough;
54 const LangOptions *LangOpts = nullptr;
55
56public:
57 TestDiagnosticConsumer()
58 : Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {}
59
60 virtual void BeginSourceFile(const LangOptions &LangOpts,
61 const Preprocessor *PP = nullptr) override {
62 this->LangOpts = &LangOpts;
63 return Passthrough->BeginSourceFile(LangOpts, PP);
64 }
65
66 virtual void EndSourceFile() override {
67 this->LangOpts = nullptr;
68 Passthrough->EndSourceFile();
69 }
70
71 virtual bool IncludeInDiagnosticCounts() const override {
72 return Passthrough->IncludeInDiagnosticCounts();
73 }
74
75private:
76 static void PrintSourceForLocation(const SourceLocation &Loc,
77 SourceManager &SM) {
78 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
79 unsigned LocColumn =
80 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
81 FileID FID = SM.getFileID(Loc);
82 llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr);
83
84 assert(LocData >= Buffer->getBufferStart() &&
85 LocData < Buffer->getBufferEnd());
86
87 const char *LineBegin = LocData - LocColumn;
88
89 assert(LineBegin >= Buffer->getBufferStart());
90
91 const char *LineEnd = nullptr;
92
93 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
94 LineEnd < Buffer->getBufferEnd();
95 ++LineEnd)
96 ;
97
98 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
99
100 llvm::errs() << LineString << '\n';
101 llvm::errs().indent(LocColumn);
102 llvm::errs() << '^';
103 }
104
105 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
106 const Diagnostic &Info) override {
107 if (Info.hasSourceManager() && LangOpts) {
108 SourceManager &SM = Info.getSourceManager();
109
110 if (Info.getLocation().isValid()) {
111 Info.getLocation().print(llvm::errs(), SM);
112 llvm::errs() << ": ";
113 }
114
115 SmallString<16> DiagText;
116 Info.FormatDiagnostic(DiagText);
117 llvm::errs() << DiagText << '\n';
118
119 if (Info.getLocation().isValid()) {
120 PrintSourceForLocation(Info.getLocation(), SM);
121 }
122
123 for (const CharSourceRange &Range : Info.getRanges()) {
124 bool Invalid = true;
125 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
126 if (!Invalid) {
127 llvm::errs() << Ref << '\n';
128 }
129 }
130 }
131 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
132 }
133};
134
135std::unique_ptr<CompilerInstance>
136BuildCompilerInstance(ArrayRef<const char *> ClangArgv) {
137 auto Ins = llvm::make_unique<CompilerInstance>();
138 auto DC = llvm::make_unique<TestDiagnosticConsumer>();
139 const bool ShouldOwnClient = true;
140 Ins->createDiagnostics(DC.release(), ShouldOwnClient);
141
142 auto Inv = llvm::make_unique<CompilerInvocation>();
143
144 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
145 &ClangArgv.data()[ClangArgv.size()],
146 Ins->getDiagnostics());
147
148 Inv->getLangOpts()->CPlusPlus = true;
149 Inv->getLangOpts()->CPlusPlus11 = true;
150 Inv->getHeaderSearchOpts().UseLibcxx = true;
151 Inv->getLangOpts()->Bool = true;
152 Inv->getLangOpts()->WChar = true;
153 Inv->getLangOpts()->Blocks = true;
154 Inv->getLangOpts()->DebuggerSupport = true;
155 Inv->getLangOpts()->SpellChecking = false;
156 Inv->getLangOpts()->ThreadsafeStatics = false;
157 Inv->getLangOpts()->AccessControl = false;
158 Inv->getLangOpts()->DollarIdents = true;
159 Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
160 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
161
David Blaikieea4395e2017-01-06 19:49:01 +0000162 Ins->setInvocation(std::move(Inv));
Sean Callanan7d982502016-12-22 20:03:14 +0000163
164 TargetInfo *TI = TargetInfo::CreateTargetInfo(
165 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
166 Ins->setTarget(TI);
167 Ins->getTarget().adjust(Ins->getLangOpts());
168 Ins->createFileManager();
169 Ins->createSourceManager(Ins->getFileManager());
170 Ins->createPreprocessor(TU_Complete);
171
172 return Ins;
173}
174
175std::unique_ptr<ASTContext>
176BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
177 auto AST = llvm::make_unique<ASTContext>(
178 CI.getLangOpts(), CI.getSourceManager(),
179 CI.getPreprocessor().getIdentifierTable(), ST, BC);
180 AST->InitBuiltinTypes(CI.getTarget());
181 return AST;
182}
183
184std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
185 llvm::LLVMContext &LLVMCtx) {
186 StringRef ModuleName("$__module");
187 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
188 CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
189 CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
190}
191} // end namespace
192
193namespace {
Sean Callananb7160ca2017-04-11 19:33:35 +0000194
Sean Callanan7d982502016-12-22 20:03:14 +0000195void AddExternalSource(
196 CompilerInstance &CI,
197 llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
Sean Callananb7160ca2017-04-11 19:33:35 +0000198 ExternalASTMerger::ImporterEndpoint Target({CI.getASTContext(), CI.getFileManager()});
199 llvm::SmallVector<ExternalASTMerger::ImporterEndpoint, 3> Sources;
200 for (const std::unique_ptr<CompilerInstance> &CI : Imports) {
201 Sources.push_back({CI->getASTContext(), CI->getFileManager()});
202 }
203 auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
204 CI.getASTContext().setExternalSource(ES.release());
205 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
Sean Callanan7d982502016-12-22 20:03:14 +0000206}
207
208llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
209 CodeGenerator &CG) {
210 SourceManager &SM = CI.getSourceManager();
211 const FileEntry *FE = CI.getFileManager().getFile(Path);
212 if (!FE) {
213 return llvm::make_error<llvm::StringError>(
214 llvm::Twine("Couldn't open ", Path), std::error_code());
215 }
216 SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
217 ParseAST(CI.getPreprocessor(), &CG, CI.getASTContext());
218 return llvm::Error::success();
219}
220
221llvm::Expected<std::unique_ptr<CompilerInstance>>
222Parse(const std::string &Path,
223 llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
224 std::vector<const char *> ClangArgv(ClangArgs.size());
225 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
226 [](const std::string &s) -> const char * { return s.data(); });
227 std::unique_ptr<CompilerInstance> CI =
228 init_convenience::BuildCompilerInstance(ClangArgv);
229 auto ST = llvm::make_unique<SelectorTable>();
230 auto BC = llvm::make_unique<Builtin::Context>();
231 std::unique_ptr<ASTContext> AST =
232 init_convenience::BuildASTContext(*CI, *ST, *BC);
233 CI->setASTContext(AST.release());
234 AddExternalSource(*CI, Imports);
235
236 auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
237 std::unique_ptr<CodeGenerator> CG =
238 init_convenience::BuildCodeGen(*CI, *LLVMCtx);
239 CG->Initialize(CI->getASTContext());
240
241 CI->getDiagnosticClient().BeginSourceFile(CI->getLangOpts(),
242 &CI->getPreprocessor());
243 if (llvm::Error PE = ParseSource(Path, *CI, *CG)) {
244 return std::move(PE);
245 }
246 CI->getDiagnosticClient().EndSourceFile();
247 if (CI->getDiagnosticClient().getNumErrors()) {
248 return llvm::make_error<llvm::StringError>(
249 "Errors occured while parsing the expression.", std::error_code());
250 } else {
251 return std::move(CI);
252 }
253}
Sean Callananb7160ca2017-04-11 19:33:35 +0000254
Sean Callanan7d982502016-12-22 20:03:14 +0000255} // end namespace
256
257int main(int argc, const char **argv) {
258 const bool DisableCrashReporting = true;
259 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
260 llvm::cl::ParseCommandLineOptions(argc, argv);
261 std::vector<std::unique_ptr<CompilerInstance>> ImportCIs;
262 for (auto I : Imports) {
263 llvm::Expected<std::unique_ptr<CompilerInstance>> ImportCI = Parse(I, {});
264 if (auto E = ImportCI.takeError()) {
265 llvm::errs() << llvm::toString(std::move(E));
266 exit(-1);
267 } else {
268 ImportCIs.push_back(std::move(*ImportCI));
269 }
270 }
271 llvm::Expected<std::unique_ptr<CompilerInstance>> ExpressionCI =
272 Parse(Expression, ImportCIs);
273 if (auto E = ExpressionCI.takeError()) {
274 llvm::errs() << llvm::toString(std::move(E));
275 exit(-1);
276 } else {
277 return 0;
278 }
279}