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