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