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(Val: 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(Val: "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(p: 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(SpellingLoc: Loc); |
113 | unsigned Line = SM.getLineNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc)); |
114 | unsigned Col = SM.getColumnNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc)); |
115 | OS << Line << ':' << Col << " | "; |
116 | |
117 | printSymbolInfo(SymInfo: 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(Roles: SymRel.Roles, OS); |
135 | OS << " | "; |
136 | printSymbolNameAndUSR(D: 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(SpellingLoc: Loc); |
151 | unsigned Line = SM.getLineNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc)); |
152 | unsigned Col = SM.getColumnNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc)); |
153 | OS << Line << ':' << Col << " | "; |
154 | |
155 | printSymbolInfo(SymInfo: 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(SpellingLoc: Loc); |
174 | unsigned Line = SM.getLineNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc)); |
175 | unsigned Col = SM.getColumnNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc)); |
176 | OS << Line << ':' << Col << " | "; |
177 | |
178 | printSymbolInfo(SymInfo: getSymbolInfoForMacro(MI: *MI), OS); |
179 | OS << " | "; |
180 | |
181 | OS << Name->getName(); |
182 | OS << " | "; |
183 | |
184 | SmallString<256> USRBuf; |
185 | if (generateUSRForMacro(MacroName: Name->getName(), Loc: MI->getDefinitionLoc(), SM, |
186 | Buf&: 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(MF&: Mod, /*IncludeSystem=*/true, /*Complain=*/false, |
210 | Visitor: [&](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(Elt: Executable); |
222 | ArgsWithProgName.append(in_start: Args.begin(), in_end: Args.end()); |
223 | auto DiagOpts = std::make_shared<DiagnosticOptions>(); |
224 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
225 | CompilerInstance::createDiagnostics(VFS&: *llvm::vfs::getRealFileSystem(), |
226 | Opts&: *DiagOpts)); |
227 | CreateInvocationOptions CIOpts; |
228 | CIOpts.Diags = Diags; |
229 | CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed? |
230 | auto CInvok = createInvocation(Args: ArgsWithProgName, Opts: std::move(CIOpts)); |
231 | if (!CInvok) |
232 | return true; |
233 | |
234 | raw_ostream &OS = outs(); |
235 | auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(args&: OS); |
236 | IndexingOptions IndexOpts; |
237 | IndexOpts.IndexFunctionLocals = indexLocals; |
238 | IndexOpts.IndexMacros = !ignoreMacros; |
239 | IndexOpts.IndexMacrosInPreprocessor = !ignoreMacros; |
240 | std::unique_ptr<FrontendAction> IndexAction = |
241 | createIndexingAction(DataConsumer, Opts: IndexOpts); |
242 | |
243 | auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); |
244 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( |
245 | CI: std::move(CInvok), PCHContainerOps, DiagOpts, Diags, Action: IndexAction.get())); |
246 | |
247 | if (!Unit) |
248 | return true; |
249 | |
250 | if (dumpModuleImports) { |
251 | if (auto Reader = Unit->getASTReader()) { |
252 | Reader->getModuleManager().visit(Visitor: [&](serialization::ModuleFile &Mod) -> bool { |
253 | OS << "==== Module "<< Mod.ModuleName << " ====\n"; |
254 | indexModuleFile(Mod, Reader&: *Reader, DataConsumer&: *DataConsumer, Opts: IndexOpts); |
255 | dumpModuleFileInputs(Mod, Reader&: *Reader, OS); |
256 | return true; // skip module dependencies. |
257 | }); |
258 | } |
259 | } |
260 | |
261 | return false; |
262 | } |
263 | |
264 | static bool printSourceSymbolsFromModule(StringRef modulePath, |
265 | StringRef format) { |
266 | FileSystemOptions FileSystemOpts; |
267 | auto pchContOps = std::make_shared<PCHContainerOperations>(); |
268 | // Register the support for object-file-wrapped Clang modules. |
269 | pchContOps->registerReader(Reader: std::make_unique<ObjectFilePCHContainerReader>()); |
270 | auto pchRdr = pchContOps->getReaderOrNull(Format: format); |
271 | if (!pchRdr) { |
272 | errs() << "unknown module format: "<< format << '\n'; |
273 | return true; |
274 | } |
275 | |
276 | HeaderSearchOptions HSOpts; |
277 | |
278 | auto DiagOpts = std::make_shared<DiagnosticOptions>(); |
279 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags = |
280 | CompilerInstance::createDiagnostics(VFS&: *llvm::vfs::getRealFileSystem(), |
281 | Opts&: *DiagOpts); |
282 | std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( |
283 | Filename: modulePath, PCHContainerRdr: *pchRdr, ToLoad: ASTUnit::LoadASTOnly, DiagOpts, Diags, |
284 | FileSystemOpts, HSOpts, /*LangOpts=*/nullptr, |
285 | /*OnlyLocalDecls=*/true, CaptureDiagnostics: CaptureDiagsKind::None, |
286 | /*AllowASTWithCompilerErrors=*/true, |
287 | /*UserFilesAreVolatile=*/false); |
288 | if (!AU) { |
289 | errs() << "failed to create TU for: "<< modulePath << '\n'; |
290 | return true; |
291 | } |
292 | |
293 | PrintIndexDataConsumer DataConsumer(outs()); |
294 | IndexingOptions IndexOpts; |
295 | indexASTUnit(Unit&: *AU, DataConsumer, Opts: IndexOpts); |
296 | |
297 | return false; |
298 | } |
299 | |
300 | //===----------------------------------------------------------------------===// |
301 | // Helper Utils |
302 | //===----------------------------------------------------------------------===// |
303 | |
304 | static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { |
305 | OS << getSymbolKindString(K: SymInfo.Kind); |
306 | if (SymInfo.SubKind != SymbolSubKind::None) |
307 | OS << '/' << getSymbolSubKindString(K: SymInfo.SubKind); |
308 | if (SymInfo.Properties) { |
309 | OS << '('; |
310 | printSymbolProperties(Props: SymInfo.Properties, OS); |
311 | OS << ')'; |
312 | } |
313 | OS << '/' << getSymbolLanguageString(K: SymInfo.Lang); |
314 | } |
315 | |
316 | static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, |
317 | raw_ostream &OS) { |
318 | if (printSymbolName(D, LO: Ctx.getLangOpts(), OS)) { |
319 | OS << "<no-name>"; |
320 | } |
321 | OS << " | "; |
322 | |
323 | SmallString<256> USRBuf; |
324 | if (generateUSRForDecl(D, Buf&: USRBuf)) { |
325 | OS << "<no-usr>"; |
326 | } else { |
327 | OS << USRBuf; |
328 | } |
329 | } |
330 | |
331 | static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) { |
332 | assert(Mod); |
333 | OS << Mod->getFullModuleName() << " | "; |
334 | generateFullUSRForModule(Mod, OS); |
335 | } |
336 | |
337 | //===----------------------------------------------------------------------===// |
338 | // Command line processing. |
339 | //===----------------------------------------------------------------------===// |
340 | |
341 | int indextest_core_main(int argc, const char **argv) { |
342 | sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]); |
343 | PrettyStackTraceProgram X(argc, argv); |
344 | void *MainAddr = (void*) (intptr_t) indextest_core_main; |
345 | std::string Executable = llvm::sys::fs::getMainExecutable(argv0: argv[0], MainExecAddr: MainAddr); |
346 | |
347 | assert(argv[1] == StringRef("core")); |
348 | ++argv; |
349 | --argc; |
350 | |
351 | std::vector<const char *> CompArgs; |
352 | const char **DoubleDash = std::find(first: argv, last: argv + argc, val: StringRef("--")); |
353 | if (DoubleDash != argv + argc) { |
354 | CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc); |
355 | argc = DoubleDash - argv; |
356 | } |
357 | |
358 | cl::HideUnrelatedOptions(Category&: options::IndexTestCoreCategory); |
359 | cl::ParseCommandLineOptions(argc, argv, Overview: "index-test-core"); |
360 | |
361 | if (options::Action == ActionType::None) { |
362 | errs() << "error: action required; pass '-help' for options\n"; |
363 | return 1; |
364 | } |
365 | |
366 | if (options::Action == ActionType::PrintSourceSymbols) { |
367 | if (!options::ModuleFilePath.empty()) { |
368 | return printSourceSymbolsFromModule(modulePath: options::ModuleFilePath, |
369 | format: options::ModuleFormat); |
370 | } |
371 | if (CompArgs.empty()) { |
372 | errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n"; |
373 | return 1; |
374 | } |
375 | return printSourceSymbols(Executable: Executable.c_str(), Args: CompArgs, |
376 | dumpModuleImports: options::DumpModuleImports, |
377 | indexLocals: options::IncludeLocals, ignoreMacros: options::IgnoreMacros); |
378 | } |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | //===----------------------------------------------------------------------===// |
384 | // Utility functions |
385 | //===----------------------------------------------------------------------===// |
386 | |
387 | int indextest_perform_shell_execution(const char *command_line) { |
388 | BumpPtrAllocator Alloc; |
389 | llvm::StringSaver Saver(Alloc); |
390 | SmallVector<const char *, 4> Args; |
391 | llvm::cl::TokenizeGNUCommandLine(Source: command_line, Saver, NewArgv&: Args); |
392 | auto Program = llvm::sys::findProgramByName(Name: Args[0]); |
393 | if (std::error_code ec = Program.getError()) { |
394 | llvm::errs() << "command not found: "<< Args[0] << "\n"; |
395 | return ec.value(); |
396 | } |
397 | SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end()); |
398 | return llvm::sys::ExecuteAndWait(Program: *Program, Args: execArgs); |
399 | } |
400 |
Definitions
- ActionType
- IndexTestCoreCategory
- Action
- MoreHelp
- DumpModuleImports
- IncludeLocals
- IgnoreMacros
- ModuleFilePath
- ModuleFormat
- PrintIndexDataConsumer
- PrintIndexDataConsumer
- initialize
- setPreprocessor
- handleDeclOccurrence
- handleModuleOccurrence
- handleMacroOccurrence
- dumpModuleFileInputs
- printSourceSymbols
- printSourceSymbolsFromModule
- printSymbolInfo
- printSymbolNameAndUSR
- printSymbolNameAndUSR
- indextest_core_main
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more