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 30 using namespace clang; 31 using namespace clang::index; 32 using namespace llvm; 33 34 extern "C" int indextest_core_main(int argc, const char **argv); 35 extern "C" int indextest_perform_shell_execution(const char *command_line); 36 37 namespace { 38 39 enum class ActionType { 40 None, 41 PrintSourceSymbols, 42 }; 43 44 namespace options { 45 46 static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); 47 48 static cl::opt<ActionType> 49 Action(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 55 static cl::extrahelp MoreHelp( 56 "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " 57 "invocation\n" 58 ); 59 60 static cl::opt<bool> 61 DumpModuleImports("dump-imported-module-files", 62 cl::desc("Print symbols and input files from imported modules")); 63 64 static cl::opt<bool> 65 IncludeLocals("include-locals", cl::desc("Print local symbols")); 66 67 static cl::opt<bool> IgnoreMacros("ignore-macros", 68 cl::desc("Skip indexing macros")); 69 70 static cl::opt<std::string> 71 ModuleFilePath("module-file", 72 cl::desc("Path to module file to print symbols from")); 73 static 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 80 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); 81 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, 82 raw_ostream &OS); 83 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS); 84 85 namespace { 86 87 class PrintIndexDataConsumer : public IndexDataConsumer { 88 raw_ostream &OS; 89 std::unique_ptr<ASTNameGenerator> ASTNameGen; 90 std::shared_ptr<Preprocessor> PP; 91 92 public: 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 204 static 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 215 static 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 auto CInvok = createInvocation(ArgsWithProgName, std::move(CIOpts)); 227 if (!CInvok) 228 return true; 229 230 raw_ostream &OS = outs(); 231 auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); 232 IndexingOptions IndexOpts; 233 IndexOpts.IndexFunctionLocals = indexLocals; 234 IndexOpts.IndexMacros = !ignoreMacros; 235 IndexOpts.IndexMacrosInPreprocessor = !ignoreMacros; 236 std::unique_ptr<FrontendAction> IndexAction = 237 createIndexingAction(DataConsumer, IndexOpts); 238 239 auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); 240 std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( 241 std::move(CInvok), PCHContainerOps, Diags, IndexAction.get())); 242 243 if (!Unit) 244 return true; 245 246 if (dumpModuleImports) { 247 if (auto Reader = Unit->getASTReader()) { 248 Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool { 249 OS << "==== Module " << Mod.ModuleName << " ====\n"; 250 indexModuleFile(Mod, *Reader, *DataConsumer, IndexOpts); 251 dumpModuleFileInputs(Mod, *Reader, OS); 252 return true; // skip module dependencies. 253 }); 254 } 255 } 256 257 return false; 258 } 259 260 static bool printSourceSymbolsFromModule(StringRef modulePath, 261 StringRef format) { 262 FileSystemOptions FileSystemOpts; 263 auto pchContOps = std::make_shared<PCHContainerOperations>(); 264 // Register the support for object-file-wrapped Clang modules. 265 pchContOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>()); 266 auto pchRdr = pchContOps->getReaderOrNull(format); 267 if (!pchRdr) { 268 errs() << "unknown module format: " << format << '\n'; 269 return true; 270 } 271 272 IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 273 CompilerInstance::createDiagnostics(new DiagnosticOptions()); 274 std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( 275 std::string(modulePath), *pchRdr, ASTUnit::LoadASTOnly, Diags, 276 FileSystemOpts, /*UseDebugInfo=*/false, 277 /*OnlyLocalDecls=*/true, CaptureDiagsKind::None, 278 /*AllowASTWithCompilerErrors=*/true, 279 /*UserFilesAreVolatile=*/false); 280 if (!AU) { 281 errs() << "failed to create TU for: " << modulePath << '\n'; 282 return true; 283 } 284 285 PrintIndexDataConsumer DataConsumer(outs()); 286 IndexingOptions IndexOpts; 287 indexASTUnit(*AU, DataConsumer, IndexOpts); 288 289 return false; 290 } 291 292 //===----------------------------------------------------------------------===// 293 // Helper Utils 294 //===----------------------------------------------------------------------===// 295 296 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { 297 OS << getSymbolKindString(SymInfo.Kind); 298 if (SymInfo.SubKind != SymbolSubKind::None) 299 OS << '/' << getSymbolSubKindString(SymInfo.SubKind); 300 if (SymInfo.Properties) { 301 OS << '('; 302 printSymbolProperties(SymInfo.Properties, OS); 303 OS << ')'; 304 } 305 OS << '/' << getSymbolLanguageString(SymInfo.Lang); 306 } 307 308 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, 309 raw_ostream &OS) { 310 if (printSymbolName(D, Ctx.getLangOpts(), OS)) { 311 OS << "<no-name>"; 312 } 313 OS << " | "; 314 315 SmallString<256> USRBuf; 316 if (generateUSRForDecl(D, USRBuf)) { 317 OS << "<no-usr>"; 318 } else { 319 OS << USRBuf; 320 } 321 } 322 323 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) { 324 assert(Mod); 325 OS << Mod->getFullModuleName() << " | "; 326 generateFullUSRForModule(Mod, OS); 327 } 328 329 //===----------------------------------------------------------------------===// 330 // Command line processing. 331 //===----------------------------------------------------------------------===// 332 333 int indextest_core_main(int argc, const char **argv) { 334 sys::PrintStackTraceOnErrorSignal(argv[0]); 335 PrettyStackTraceProgram X(argc, argv); 336 void *MainAddr = (void*) (intptr_t) indextest_core_main; 337 std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr); 338 339 assert(argv[1] == StringRef("core")); 340 ++argv; 341 --argc; 342 343 std::vector<const char *> CompArgs; 344 const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); 345 if (DoubleDash != argv + argc) { 346 CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc); 347 argc = DoubleDash - argv; 348 } 349 350 cl::HideUnrelatedOptions(options::IndexTestCoreCategory); 351 cl::ParseCommandLineOptions(argc, argv, "index-test-core"); 352 353 if (options::Action == ActionType::None) { 354 errs() << "error: action required; pass '-help' for options\n"; 355 return 1; 356 } 357 358 if (options::Action == ActionType::PrintSourceSymbols) { 359 if (!options::ModuleFilePath.empty()) { 360 return printSourceSymbolsFromModule(options::ModuleFilePath, 361 options::ModuleFormat); 362 } 363 if (CompArgs.empty()) { 364 errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n"; 365 return 1; 366 } 367 return printSourceSymbols(Executable.c_str(), CompArgs, 368 options::DumpModuleImports, 369 options::IncludeLocals, options::IgnoreMacros); 370 } 371 372 return 0; 373 } 374 375 //===----------------------------------------------------------------------===// 376 // Utility functions 377 //===----------------------------------------------------------------------===// 378 379 int indextest_perform_shell_execution(const char *command_line) { 380 BumpPtrAllocator Alloc; 381 llvm::StringSaver Saver(Alloc); 382 SmallVector<const char *, 4> Args; 383 llvm::cl::TokenizeGNUCommandLine(command_line, Saver, Args); 384 auto Program = llvm::sys::findProgramByName(Args[0]); 385 if (std::error_code ec = Program.getError()) { 386 llvm::errs() << "command not found: " << Args[0] << "\n"; 387 return ec.value(); 388 } 389 SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end()); 390 return llvm::sys::ExecuteAndWait(*Program, execArgs); 391 } 392