//===-- core_main.cpp - Core Index Tool testbed ---------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/AST/Mangle.h" #include "clang/Basic/LangOptions.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Index/IndexingAction.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/PrettyStackTrace.h" using namespace clang; using namespace clang::index; using namespace llvm; extern "C" int indextest_core_main(int argc, const char **argv); namespace { enum class ActionType { None, PrintSourceSymbols, }; namespace options { static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); static cl::opt Action(cl::desc("Action:"), cl::init(ActionType::None), cl::values( clEnumValN(ActionType::PrintSourceSymbols, "print-source-symbols", "Print symbols from source")), cl::cat(IndexTestCoreCategory)); static cl::extrahelp MoreHelp( "\nAdd \"-- \" at the end to setup the compiler " "invocation\n" ); static cl::opt DumpModuleImports("dump-imported-module-files", cl::desc("Print symbols and input files from imported modules")); static cl::opt IncludeLocals("include-locals", cl::desc("Print local symbols")); static cl::opt ModuleFilePath("module-file", cl::desc("Path to module file to print symbols from")); static cl::opt ModuleFormat("fmodule-format", cl::init("raw"), cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'")); } } // anonymous namespace static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, raw_ostream &OS); static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS); namespace { class PrintIndexDataConsumer : public IndexDataConsumer { raw_ostream &OS; std::unique_ptr ASTNameGen; std::shared_ptr PP; public: PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { } void initialize(ASTContext &Ctx) override { ASTNameGen.reset(new ASTNameGenerator(Ctx)); } void setPreprocessor(std::shared_ptr PP) override { this->PP = std::move(PP); } bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, SourceLocation Loc, ASTNodeInfo ASTNode) override { ASTContext &Ctx = D->getASTContext(); SourceManager &SM = Ctx.getSourceManager(); Loc = SM.getFileLoc(Loc); FileID FID = SM.getFileID(Loc); unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc)); unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc)); OS << Line << ':' << Col << " | "; printSymbolInfo(getSymbolInfo(D), OS); OS << " | "; printSymbolNameAndUSR(D, Ctx, OS); OS << " | "; if (ASTNameGen->writeName(D, OS)) OS << ""; OS << " | "; printSymbolRoles(Roles, OS); OS << " | "; OS << "rel: " << Relations.size() << '\n'; for (auto &SymRel : Relations) { OS << '\t'; printSymbolRoles(SymRel.Roles, OS); OS << " | "; printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS); OS << '\n'; } return true; } bool handleModuleOccurence(const ImportDecl *ImportD, const clang::Module *Mod, SymbolRoleSet Roles, SourceLocation Loc) override { ASTContext &Ctx = ImportD->getASTContext(); SourceManager &SM = Ctx.getSourceManager(); Loc = SM.getFileLoc(Loc); FileID FID = SM.getFileID(Loc); unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc)); unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc)); OS << Line << ':' << Col << " | "; printSymbolInfo(getSymbolInfo(ImportD), OS); OS << " | "; printSymbolNameAndUSR(Mod, OS); OS << " | "; printSymbolRoles(Roles, OS); OS << " |\n"; return true; } bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, SymbolRoleSet Roles, SourceLocation Loc) override { assert(PP); SourceManager &SM = PP->getSourceManager(); Loc = SM.getFileLoc(Loc); FileID FID = SM.getFileID(Loc); unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc)); unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc)); OS << Line << ':' << Col << " | "; printSymbolInfo(getSymbolInfoForMacro(*MI), OS); OS << " | "; OS << Name->getName(); OS << " | "; SmallString<256> USRBuf; if (generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM, USRBuf)) { OS << ""; } else { OS << USRBuf; } OS << " | "; printSymbolRoles(Roles, OS); OS << " |\n"; return true; } }; } // anonymous namespace //===----------------------------------------------------------------------===// // Print Source Symbols //===----------------------------------------------------------------------===// static void dumpModuleFileInputs(serialization::ModuleFile &Mod, ASTReader &Reader, raw_ostream &OS) { OS << "---- Module Inputs ----\n"; Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false, [&](const serialization::InputFile &IF, bool isSystem) { OS << (isSystem ? "system" : "user") << " | "; OS << IF.getFile()->getName() << '\n'; }); } static bool printSourceSymbols(const char *Executable, ArrayRef Args, bool dumpModuleImports, bool indexLocals) { SmallVector ArgsWithProgName; ArgsWithProgName.push_back(Executable); ArgsWithProgName.append(Args.begin(), Args.end()); IntrusiveRefCntPtr Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags); if (!CInvok) return true; raw_ostream &OS = outs(); auto DataConsumer = std::make_shared(OS); IndexingOptions IndexOpts; IndexOpts.IndexFunctionLocals = indexLocals; std::unique_ptr IndexAction = createIndexingAction(DataConsumer, IndexOpts); auto PCHContainerOps = std::make_shared(); std::unique_ptr Unit(ASTUnit::LoadFromCompilerInvocationAction( std::move(CInvok), PCHContainerOps, Diags, IndexAction.get())); if (!Unit) return true; if (dumpModuleImports) { if (auto Reader = Unit->getASTReader()) { Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool { OS << "==== Module " << Mod.ModuleName << " ====\n"; indexModuleFile(Mod, *Reader, *DataConsumer, IndexOpts); dumpModuleFileInputs(Mod, *Reader, OS); return true; // skip module dependencies. }); } } return false; } static bool printSourceSymbolsFromModule(StringRef modulePath, StringRef format) { FileSystemOptions FileSystemOpts; auto pchContOps = std::make_shared(); // Register the support for object-file-wrapped Clang modules. pchContOps->registerReader(std::make_unique()); auto pchRdr = pchContOps->getReaderOrNull(format); if (!pchRdr) { errs() << "unknown module format: " << format << '\n'; return true; } IntrusiveRefCntPtr Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); std::unique_ptr AU = ASTUnit::LoadFromASTFile( modulePath, *pchRdr, ASTUnit::LoadASTOnly, Diags, FileSystemOpts, /*UseDebugInfo=*/false, /*OnlyLocalDecls=*/true, None, CaptureDiagsKind::None, /*AllowPCHWithCompilerErrors=*/true, /*UserFilesAreVolatile=*/false); if (!AU) { errs() << "failed to create TU for: " << modulePath << '\n'; return true; } PrintIndexDataConsumer DataConsumer(outs()); IndexingOptions IndexOpts; indexASTUnit(*AU, DataConsumer, IndexOpts); return false; } //===----------------------------------------------------------------------===// // Helper Utils //===----------------------------------------------------------------------===// static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { OS << getSymbolKindString(SymInfo.Kind); if (SymInfo.SubKind != SymbolSubKind::None) OS << '/' << getSymbolSubKindString(SymInfo.SubKind); if (SymInfo.Properties) { OS << '('; printSymbolProperties(SymInfo.Properties, OS); OS << ')'; } OS << '/' << getSymbolLanguageString(SymInfo.Lang); } static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, raw_ostream &OS) { if (printSymbolName(D, Ctx.getLangOpts(), OS)) { OS << ""; } OS << " | "; SmallString<256> USRBuf; if (generateUSRForDecl(D, USRBuf)) { OS << ""; } else { OS << USRBuf; } } static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) { assert(Mod); OS << Mod->getFullModuleName() << " | "; generateFullUSRForModule(Mod, OS); } //===----------------------------------------------------------------------===// // Command line processing. //===----------------------------------------------------------------------===// int indextest_core_main(int argc, const char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); void *MainAddr = (void*) (intptr_t) indextest_core_main; std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr); assert(argv[1] == StringRef("core")); ++argv; --argc; std::vector CompArgs; const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); if (DoubleDash != argv + argc) { CompArgs = std::vector(DoubleDash + 1, argv + argc); argc = DoubleDash - argv; } cl::HideUnrelatedOptions(options::IndexTestCoreCategory); cl::ParseCommandLineOptions(argc, argv, "index-test-core"); if (options::Action == ActionType::None) { errs() << "error: action required; pass '-help' for options\n"; return 1; } if (options::Action == ActionType::PrintSourceSymbols) { if (!options::ModuleFilePath.empty()) { return printSourceSymbolsFromModule(options::ModuleFilePath, options::ModuleFormat); } if (CompArgs.empty()) { errs() << "error: missing compiler args; pass '-- '\n"; return 1; } return printSourceSymbols(Executable.c_str(), CompArgs, options::DumpModuleImports, options::IncludeLocals); } return 0; }