brintos

brintos / llvm-project-archived public Read only

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