| //===-- 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<ActionType> |
| 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 \"-- <compiler arguments>\" at the end to setup the compiler " |
| "invocation\n" |
| ); |
| |
| static cl::opt<bool> |
| DumpModuleImports("dump-imported-module-files", |
| cl::desc("Print symbols and input files from imported modules")); |
| |
| static cl::opt<bool> |
| IncludeLocals("include-locals", cl::desc("Print local symbols")); |
| |
| static cl::opt<std::string> |
| ModuleFilePath("module-file", |
| cl::desc("Path to module file to print symbols from")); |
| static cl::opt<std::string> |
| 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<ASTNameGenerator> ASTNameGen; |
| std::shared_ptr<Preprocessor> PP; |
| |
| public: |
| PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { |
| } |
| |
| void initialize(ASTContext &Ctx) override { |
| ASTNameGen.reset(new ASTNameGenerator(Ctx)); |
| } |
| |
| void setPreprocessor(std::shared_ptr<Preprocessor> PP) override { |
| this->PP = std::move(PP); |
| } |
| |
| bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, |
| ArrayRef<SymbolRelation> 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 << "<no-cgname>"; |
| 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 << "<no-usr>"; |
| } 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<const char *> Args, |
| bool dumpModuleImports, bool indexLocals) { |
| SmallVector<const char *, 4> ArgsWithProgName; |
| ArgsWithProgName.push_back(Executable); |
| ArgsWithProgName.append(Args.begin(), Args.end()); |
| IntrusiveRefCntPtr<DiagnosticsEngine> |
| Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); |
| auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags); |
| if (!CInvok) |
| return true; |
| |
| raw_ostream &OS = outs(); |
| auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); |
| IndexingOptions IndexOpts; |
| IndexOpts.IndexFunctionLocals = indexLocals; |
| std::unique_ptr<FrontendAction> IndexAction = |
| createIndexingAction(DataConsumer, IndexOpts); |
| |
| auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); |
| std::unique_ptr<ASTUnit> 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<PCHContainerOperations>(); |
| // Register the support for object-file-wrapped Clang modules. |
| pchContOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>()); |
| auto pchRdr = pchContOps->getReaderOrNull(format); |
| if (!pchRdr) { |
| errs() << "unknown module format: " << format << '\n'; |
| return true; |
| } |
| |
| IntrusiveRefCntPtr<DiagnosticsEngine> Diags = |
| CompilerInstance::createDiagnostics(new DiagnosticOptions()); |
| std::unique_ptr<ASTUnit> 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 << "<no-name>"; |
| } |
| OS << " | "; |
| |
| SmallString<256> USRBuf; |
| if (generateUSRForDecl(D, USRBuf)) { |
| OS << "<no-usr>"; |
| } 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<const char *> CompArgs; |
| const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); |
| if (DoubleDash != argv + argc) { |
| CompArgs = std::vector<const char *>(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 '-- <compiler arguments>'\n"; |
| return 1; |
| } |
| return printSourceSymbols(Executable.c_str(), CompArgs, |
| options::DumpModuleImports, |
| options::IncludeLocals); |
| } |
| |
| return 0; |
| } |