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 modulePath, *pchRdr, ASTUnit::LoadASTOnly, Diags, 264 FileSystemOpts, /*UseDebugInfo=*/false, 265 /*OnlyLocalDecls=*/true, None, 266 CaptureDiagsKind::None, 267 /*AllowPCHWithCompilerErrors=*/true, 268 /*UserFilesAreVolatile=*/false); 269 if (!AU) { 270 errs() << "failed to create TU for: " << modulePath << '\n'; 271 return true; 272 } 273 274 PrintIndexDataConsumer DataConsumer(outs()); 275 IndexingOptions IndexOpts; 276 indexASTUnit(*AU, DataConsumer, IndexOpts); 277 278 return false; 279 } 280 281 //===----------------------------------------------------------------------===// 282 // Helper Utils 283 //===----------------------------------------------------------------------===// 284 285 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { 286 OS << getSymbolKindString(SymInfo.Kind); 287 if (SymInfo.SubKind != SymbolSubKind::None) 288 OS << '/' << getSymbolSubKindString(SymInfo.SubKind); 289 if (SymInfo.Properties) { 290 OS << '('; 291 printSymbolProperties(SymInfo.Properties, OS); 292 OS << ')'; 293 } 294 OS << '/' << getSymbolLanguageString(SymInfo.Lang); 295 } 296 297 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, 298 raw_ostream &OS) { 299 if (printSymbolName(D, Ctx.getLangOpts(), OS)) { 300 OS << "<no-name>"; 301 } 302 OS << " | "; 303 304 SmallString<256> USRBuf; 305 if (generateUSRForDecl(D, USRBuf)) { 306 OS << "<no-usr>"; 307 } else { 308 OS << USRBuf; 309 } 310 } 311 312 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) { 313 assert(Mod); 314 OS << Mod->getFullModuleName() << " | "; 315 generateFullUSRForModule(Mod, OS); 316 } 317 318 //===----------------------------------------------------------------------===// 319 // Command line processing. 320 //===----------------------------------------------------------------------===// 321 322 int indextest_core_main(int argc, const char **argv) { 323 sys::PrintStackTraceOnErrorSignal(argv[0]); 324 PrettyStackTraceProgram X(argc, argv); 325 void *MainAddr = (void*) (intptr_t) indextest_core_main; 326 std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr); 327 328 assert(argv[1] == StringRef("core")); 329 ++argv; 330 --argc; 331 332 std::vector<const char *> CompArgs; 333 const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); 334 if (DoubleDash != argv + argc) { 335 CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc); 336 argc = DoubleDash - argv; 337 } 338 339 cl::HideUnrelatedOptions(options::IndexTestCoreCategory); 340 cl::ParseCommandLineOptions(argc, argv, "index-test-core"); 341 342 if (options::Action == ActionType::None) { 343 errs() << "error: action required; pass '-help' for options\n"; 344 return 1; 345 } 346 347 if (options::Action == ActionType::PrintSourceSymbols) { 348 if (!options::ModuleFilePath.empty()) { 349 return printSourceSymbolsFromModule(options::ModuleFilePath, 350 options::ModuleFormat); 351 } 352 if (CompArgs.empty()) { 353 errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n"; 354 return 1; 355 } 356 return printSourceSymbols(Executable.c_str(), CompArgs, 357 options::DumpModuleImports, 358 options::IncludeLocals); 359 } 360 361 return 0; 362 } 363