Clang Project

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