Home | History | Annotate | Line # | Download | only in c-index-test
      1 //===-- core_main.cpp - Core Index Tool 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/Mangle.h"
     10 #include "clang/Basic/LangOptions.h"
     11 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
     12 #include "clang/Frontend/ASTUnit.h"
     13 #include "clang/Frontend/CompilerInstance.h"
     14 #include "clang/Frontend/CompilerInvocation.h"
     15 #include "clang/Frontend/FrontendAction.h"
     16 #include "clang/Index/IndexDataConsumer.h"
     17 #include "clang/Index/IndexingAction.h"
     18 #include "clang/Index/USRGeneration.h"
     19 #include "clang/Lex/Preprocessor.h"
     20 #include "clang/Serialization/ASTReader.h"
     21 #include "llvm/Support/CommandLine.h"
     22 #include "llvm/Support/FileSystem.h"
     23 #include "llvm/Support/PrettyStackTrace.h"
     24 #include "llvm/Support/Program.h"
     25 #include "llvm/Support/Signals.h"
     26 #include "llvm/Support/StringSaver.h"
     27 #include "llvm/Support/raw_ostream.h"
     28 
     29 using namespace clang;
     30 using namespace clang::index;
     31 using namespace llvm;
     32 
     33 extern "C" int indextest_core_main(int argc, const char **argv);
     34 extern "C" int indextest_perform_shell_execution(const char *command_line);
     35 
     36 namespace {
     37 
     38 enum class ActionType {
     39   None,
     40   PrintSourceSymbols,
     41 };
     42 
     43 namespace options {
     44 
     45 static cl::OptionCategory IndexTestCoreCategory("index-test-core options");
     46 
     47 static cl::opt<ActionType>
     48 Action(cl::desc("Action:"), cl::init(ActionType::None),
     49        cl::values(
     50           clEnumValN(ActionType::PrintSourceSymbols,
     51                      "print-source-symbols", "Print symbols from source")),
     52        cl::cat(IndexTestCoreCategory));
     53 
     54 static cl::extrahelp MoreHelp(
     55   "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
     56   "invocation\n"
     57 );
     58 
     59 static cl::opt<bool>
     60 DumpModuleImports("dump-imported-module-files",
     61                cl::desc("Print symbols and input files from imported modules"));
     62 
     63 static cl::opt<bool>
     64 IncludeLocals("include-locals", cl::desc("Print local symbols"));
     65 
     66 static cl::opt<bool> IgnoreMacros("ignore-macros",
     67                                   cl::desc("Skip indexing macros"));
     68 
     69 static cl::opt<std::string>
     70 ModuleFilePath("module-file",
     71                cl::desc("Path to module file to print symbols from"));
     72 static cl::opt<std::string>
     73   ModuleFormat("fmodule-format", cl::init("raw"),
     74         cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
     75 
     76 }
     77 } // anonymous namespace
     78 
     79 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS);
     80 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
     81                                   raw_ostream &OS);
     82 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS);
     83 
     84 namespace {
     85 
     86 class PrintIndexDataConsumer : public IndexDataConsumer {
     87   raw_ostream &OS;
     88   std::unique_ptr<ASTNameGenerator> ASTNameGen;
     89   std::shared_ptr<Preprocessor> PP;
     90 
     91 public:
     92   PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) {
     93   }
     94 
     95   void initialize(ASTContext &Ctx) override {
     96     ASTNameGen.reset(new ASTNameGenerator(Ctx));
     97   }
     98 
     99   void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
    100     this->PP = std::move(PP);
    101   }
    102 
    103   bool handleDeclOccurrence(const Decl *D, SymbolRoleSet Roles,
    104                             ArrayRef<SymbolRelation> Relations,
    105                             SourceLocation Loc, ASTNodeInfo ASTNode) override {
    106     ASTContext &Ctx = D->getASTContext();
    107     SourceManager &SM = Ctx.getSourceManager();
    108 
    109     Loc = SM.getFileLoc(Loc);
    110     FileID FID = SM.getFileID(Loc);
    111     unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc));
    112     unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc));
    113     OS << Line << ':' << Col << " | ";
    114 
    115     printSymbolInfo(getSymbolInfo(D), OS);
    116     OS << " | ";
    117 
    118     printSymbolNameAndUSR(D, Ctx, OS);
    119     OS << " | ";
    120 
    121     if (ASTNameGen->writeName(D, OS))
    122       OS << "<no-cgname>";
    123     OS << " | ";
    124 
    125     printSymbolRoles(Roles, OS);
    126     OS << " | ";
    127 
    128     OS << "rel: " << Relations.size() << '\n';
    129 
    130     for (auto &SymRel : Relations) {
    131       OS << '\t';
    132       printSymbolRoles(SymRel.Roles, OS);
    133       OS << " | ";
    134       printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS);
    135       OS << '\n';
    136     }
    137 
    138     return true;
    139   }
    140 
    141   bool handleModuleOccurrence(const ImportDecl *ImportD,
    142                               const clang::Module *Mod, SymbolRoleSet Roles,
    143                               SourceLocation Loc) override {
    144     ASTContext &Ctx = ImportD->getASTContext();
    145     SourceManager &SM = Ctx.getSourceManager();
    146 
    147     Loc = SM.getFileLoc(Loc);
    148     FileID FID = SM.getFileID(Loc);
    149     unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc));
    150     unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc));
    151     OS << Line << ':' << Col << " | ";
    152 
    153     printSymbolInfo(getSymbolInfo(ImportD), OS);
    154     OS << " | ";
    155 
    156     printSymbolNameAndUSR(Mod, OS);
    157     OS << " | ";
    158 
    159     printSymbolRoles(Roles, OS);
    160     OS << " |\n";
    161 
    162     return true;
    163   }
    164 
    165   bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI,
    166                              SymbolRoleSet Roles, SourceLocation Loc) override {
    167     assert(PP);
    168     SourceManager &SM = PP->getSourceManager();
    169 
    170     Loc = SM.getFileLoc(Loc);
    171     FileID FID = SM.getFileID(Loc);
    172     unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc));
    173     unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc));
    174     OS << Line << ':' << Col << " | ";
    175 
    176     printSymbolInfo(getSymbolInfoForMacro(*MI), OS);
    177     OS << " | ";
    178 
    179     OS << Name->getName();
    180     OS << " | ";
    181 
    182     SmallString<256> USRBuf;
    183     if (generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM,
    184                             USRBuf)) {
    185       OS << "<no-usr>";
    186     } else {
    187       OS << USRBuf;
    188     }
    189     OS << " | ";
    190 
    191     printSymbolRoles(Roles, OS);
    192     OS << " |\n";
    193     return true;
    194   }
    195 };
    196 
    197 } // anonymous namespace
    198 
    199 //===----------------------------------------------------------------------===//
    200 // Print Source Symbols
    201 //===----------------------------------------------------------------------===//
    202 
    203 static void dumpModuleFileInputs(serialization::ModuleFile &Mod,
    204                                  ASTReader &Reader,
    205                                  raw_ostream &OS) {
    206   OS << "---- Module Inputs ----\n";
    207   Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false,
    208                         [&](const serialization::InputFile &IF, bool isSystem) {
    209     OS << (isSystem ? "system" : "user") << " | ";
    210     OS << IF.getFile()->getName() << '\n';
    211   });
    212 }
    213 
    214 static bool printSourceSymbols(const char *Executable,
    215                                ArrayRef<const char *> Args,
    216                                bool dumpModuleImports, bool indexLocals,
    217                                bool ignoreMacros) {
    218   SmallVector<const char *, 4> ArgsWithProgName;
    219   ArgsWithProgName.push_back(Executable);
    220   ArgsWithProgName.append(Args.begin(), Args.end());
    221   IntrusiveRefCntPtr<DiagnosticsEngine>
    222     Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
    223   auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags);
    224   if (!CInvok)
    225     return true;
    226 
    227   raw_ostream &OS = outs();
    228   auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS);
    229   IndexingOptions IndexOpts;
    230   IndexOpts.IndexFunctionLocals = indexLocals;
    231   IndexOpts.IndexMacros = !ignoreMacros;
    232   IndexOpts.IndexMacrosInPreprocessor = !ignoreMacros;
    233   std::unique_ptr<FrontendAction> IndexAction =
    234       createIndexingAction(DataConsumer, IndexOpts);
    235 
    236   auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
    237   std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
    238       std::move(CInvok), PCHContainerOps, Diags, IndexAction.get()));
    239 
    240   if (!Unit)
    241     return true;
    242 
    243   if (dumpModuleImports) {
    244     if (auto Reader = Unit->getASTReader()) {
    245       Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool {
    246         OS << "==== Module " << Mod.ModuleName << " ====\n";
    247         indexModuleFile(Mod, *Reader, *DataConsumer, IndexOpts);
    248         dumpModuleFileInputs(Mod, *Reader, OS);
    249         return true; // skip module dependencies.
    250       });
    251     }
    252   }
    253 
    254   return false;
    255 }
    256 
    257 static bool printSourceSymbolsFromModule(StringRef modulePath,
    258                                          StringRef format) {
    259   FileSystemOptions FileSystemOpts;
    260   auto pchContOps = std::make_shared<PCHContainerOperations>();
    261   // Register the support for object-file-wrapped Clang modules.
    262   pchContOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
    263   auto pchRdr = pchContOps->getReaderOrNull(format);
    264   if (!pchRdr) {
    265     errs() << "unknown module format: " << format << '\n';
    266     return true;
    267   }
    268 
    269   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
    270       CompilerInstance::createDiagnostics(new DiagnosticOptions());
    271   std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
    272       std::string(modulePath), *pchRdr, ASTUnit::LoadASTOnly, Diags,
    273       FileSystemOpts, /*UseDebugInfo=*/false,
    274       /*OnlyLocalDecls=*/true, CaptureDiagsKind::None,
    275       /*AllowASTWithCompilerErrors=*/true,
    276       /*UserFilesAreVolatile=*/false);
    277   if (!AU) {
    278     errs() << "failed to create TU for: " << modulePath << '\n';
    279     return true;
    280   }
    281 
    282   PrintIndexDataConsumer DataConsumer(outs());
    283   IndexingOptions IndexOpts;
    284   indexASTUnit(*AU, DataConsumer, IndexOpts);
    285 
    286   return false;
    287 }
    288 
    289 //===----------------------------------------------------------------------===//
    290 // Helper Utils
    291 //===----------------------------------------------------------------------===//
    292 
    293 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
    294   OS << getSymbolKindString(SymInfo.Kind);
    295   if (SymInfo.SubKind != SymbolSubKind::None)
    296     OS << '/' << getSymbolSubKindString(SymInfo.SubKind);
    297   if (SymInfo.Properties) {
    298     OS << '(';
    299     printSymbolProperties(SymInfo.Properties, OS);
    300     OS << ')';
    301   }
    302   OS << '/' << getSymbolLanguageString(SymInfo.Lang);
    303 }
    304 
    305 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
    306                                   raw_ostream &OS) {
    307   if (printSymbolName(D, Ctx.getLangOpts(), OS)) {
    308     OS << "<no-name>";
    309   }
    310   OS << " | ";
    311 
    312   SmallString<256> USRBuf;
    313   if (generateUSRForDecl(D, USRBuf)) {
    314     OS << "<no-usr>";
    315   } else {
    316     OS << USRBuf;
    317   }
    318 }
    319 
    320 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) {
    321   assert(Mod);
    322   OS << Mod->getFullModuleName() << " | ";
    323   generateFullUSRForModule(Mod, OS);
    324 }
    325 
    326 //===----------------------------------------------------------------------===//
    327 // Command line processing.
    328 //===----------------------------------------------------------------------===//
    329 
    330 int indextest_core_main(int argc, const char **argv) {
    331   sys::PrintStackTraceOnErrorSignal(argv[0]);
    332   PrettyStackTraceProgram X(argc, argv);
    333   void *MainAddr = (void*) (intptr_t) indextest_core_main;
    334   std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
    335 
    336   assert(argv[1] == StringRef("core"));
    337   ++argv;
    338   --argc;
    339 
    340   std::vector<const char *> CompArgs;
    341   const char **DoubleDash = std::find(argv, argv + argc, StringRef("--"));
    342   if (DoubleDash != argv + argc) {
    343     CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc);
    344     argc = DoubleDash - argv;
    345   }
    346 
    347   cl::HideUnrelatedOptions(options::IndexTestCoreCategory);
    348   cl::ParseCommandLineOptions(argc, argv, "index-test-core");
    349 
    350   if (options::Action == ActionType::None) {
    351     errs() << "error: action required; pass '-help' for options\n";
    352     return 1;
    353   }
    354 
    355   if (options::Action == ActionType::PrintSourceSymbols) {
    356     if (!options::ModuleFilePath.empty()) {
    357       return printSourceSymbolsFromModule(options::ModuleFilePath,
    358                                           options::ModuleFormat);
    359     }
    360     if (CompArgs.empty()) {
    361       errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
    362       return 1;
    363     }
    364     return printSourceSymbols(Executable.c_str(), CompArgs,
    365                               options::DumpModuleImports,
    366                               options::IncludeLocals, options::IgnoreMacros);
    367   }
    368 
    369   return 0;
    370 }
    371 
    372 //===----------------------------------------------------------------------===//
    373 // Utility functions
    374 //===----------------------------------------------------------------------===//
    375 
    376 int indextest_perform_shell_execution(const char *command_line) {
    377   BumpPtrAllocator Alloc;
    378   llvm::StringSaver Saver(Alloc);
    379   SmallVector<const char *, 4> Args;
    380   llvm::cl::TokenizeGNUCommandLine(command_line, Saver, Args);
    381   auto Program = llvm::sys::findProgramByName(Args[0]);
    382   if (std::error_code ec = Program.getError()) {
    383     llvm::errs() << "command not found: " << Args[0] << "\n";
    384     return ec.value();
    385   }
    386   SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end());
    387   return llvm::sys::ExecuteAndWait(*Program, execArgs);
    388 }
    389