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