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