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