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