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
30using namespace clang;
31using namespace clang::index;
32using namespace llvm;
33
34extern "C" int indextest_core_main(int argc, const char **argv);
35extern "C" int indextest_perform_shell_execution(const char *command_line);
36
37namespace {
38
39enum class ActionType {
40 None,
41 PrintSourceSymbols,
42};
43
44namespace options {
45
46static cl::OptionCategory IndexTestCoreCategory("index-test-core options");
47
48static cl::opt<ActionType>
49Action(cl::desc("Action:"), cl::init(Val: ActionType::None),
50 cl::values(
51 clEnumValN(ActionType::PrintSourceSymbols,
52 "print-source-symbols", "Print symbols from source")),
53 cl::cat(IndexTestCoreCategory));
54
55static cl::extrahelp MoreHelp(
56 "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
57 "invocation\n"
58);
59
60static cl::opt<bool>
61DumpModuleImports("dump-imported-module-files",
62 cl::desc("Print symbols and input files from imported modules"));
63
64static cl::opt<bool>
65IncludeLocals("include-locals", cl::desc("Print local symbols"));
66
67static cl::opt<bool> IgnoreMacros("ignore-macros",
68 cl::desc("Skip indexing macros"));
69
70static cl::opt<std::string>
71ModuleFilePath("module-file",
72 cl::desc("Path to module file to print symbols from"));
73static cl::opt<std::string>
74 ModuleFormat("fmodule-format", cl::init(Val: "raw"),
75 cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
76
77}
78} // anonymous namespace
79
80static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS);
81static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
82 raw_ostream &OS);
83static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS);
84
85namespace {
86
87class PrintIndexDataConsumer : public IndexDataConsumer {
88 raw_ostream &OS;
89 std::unique_ptr<ASTNameGenerator> ASTNameGen;
90 std::shared_ptr<Preprocessor> PP;
91
92public:
93 PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) {
94 }
95
96 void initialize(ASTContext &Ctx) override {
97 ASTNameGen.reset(p: 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(SpellingLoc: Loc);
112 unsigned Line = SM.getLineNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc));
113 unsigned Col = SM.getColumnNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc));
114 OS << Line << ':' << Col << " | ";
115
116 printSymbolInfo(SymInfo: 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(Roles: SymRel.Roles, OS);
134 OS << " | ";
135 printSymbolNameAndUSR(D: 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(SpellingLoc: Loc);
150 unsigned Line = SM.getLineNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc));
151 unsigned Col = SM.getColumnNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc));
152 OS << Line << ':' << Col << " | ";
153
154 printSymbolInfo(SymInfo: getSymbolInfo(D: 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(SpellingLoc: Loc);
173 unsigned Line = SM.getLineNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc));
174 unsigned Col = SM.getColumnNumber(FID, FilePos: SM.getFileOffset(SpellingLoc: Loc));
175 OS << Line << ':' << Col << " | ";
176
177 printSymbolInfo(SymInfo: getSymbolInfoForMacro(MI: *MI), OS);
178 OS << " | ";
179
180 OS << Name->getName();
181 OS << " | ";
182
183 SmallString<256> USRBuf;
184 if (generateUSRForMacro(MacroName: Name->getName(), Loc: MI->getDefinitionLoc(), SM,
185 Buf&: 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
204static void dumpModuleFileInputs(serialization::ModuleFile &Mod,
205 ASTReader &Reader,
206 raw_ostream &OS) {
207 OS << "---- Module Inputs ----\n";
208 Reader.visitInputFiles(MF&: Mod, /*IncludeSystem=*/true, /*Complain=*/false,
209 Visitor: [&](const serialization::InputFile &IF, bool isSystem) {
210 OS << (isSystem ? "system" : "user") << " | ";
211 OS << IF.getFile()->getName() << '\n';
212 });
213}
214
215static 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(Elt: Executable);
221 ArgsWithProgName.append(in_start: Args.begin(), in_end: Args.end());
222 IntrusiveRefCntPtr<DiagnosticsEngine>
223 Diags(CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions));
224 CreateInvocationOptions CIOpts;
225 CIOpts.Diags = Diags;
226 CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed?
227 auto CInvok = createInvocation(Args: ArgsWithProgName, Opts: std::move(CIOpts));
228 if (!CInvok)
229 return true;
230
231 raw_ostream &OS = outs();
232 auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(args&: OS);
233 IndexingOptions IndexOpts;
234 IndexOpts.IndexFunctionLocals = indexLocals;
235 IndexOpts.IndexMacros = !ignoreMacros;
236 IndexOpts.IndexMacrosInPreprocessor = !ignoreMacros;
237 std::unique_ptr<FrontendAction> IndexAction =
238 createIndexingAction(DataConsumer, Opts: IndexOpts);
239
240 auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
241 std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
242 CI: std::move(CInvok), PCHContainerOps, Diags, Action: IndexAction.get()));
243
244 if (!Unit)
245 return true;
246
247 if (dumpModuleImports) {
248 if (auto Reader = Unit->getASTReader()) {
249 Reader->getModuleManager().visit(Visitor: [&](serialization::ModuleFile &Mod) -> bool {
250 OS << "==== Module " << Mod.ModuleName << " ====\n";
251 indexModuleFile(Mod, Reader&: *Reader, DataConsumer&: *DataConsumer, Opts: IndexOpts);
252 dumpModuleFileInputs(Mod, Reader&: *Reader, OS);
253 return true; // skip module dependencies.
254 });
255 }
256 }
257
258 return false;
259}
260
261static bool printSourceSymbolsFromModule(StringRef modulePath,
262 StringRef format) {
263 FileSystemOptions FileSystemOpts;
264 auto pchContOps = std::make_shared<PCHContainerOperations>();
265 // Register the support for object-file-wrapped Clang modules.
266 pchContOps->registerReader(Reader: std::make_unique<ObjectFilePCHContainerReader>());
267 auto pchRdr = pchContOps->getReaderOrNull(Format: format);
268 if (!pchRdr) {
269 errs() << "unknown module format: " << format << '\n';
270 return true;
271 }
272
273 auto HSOpts = std::make_shared<HeaderSearchOptions>();
274
275 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
276 CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions());
277 std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
278 Filename: std::string(modulePath), PCHContainerRdr: *pchRdr, ToLoad: ASTUnit::LoadASTOnly, Diags,
279 FileSystemOpts, HSOpts, /*LangOpts=*/nullptr,
280 /*OnlyLocalDecls=*/true, CaptureDiagnostics: CaptureDiagsKind::None,
281 /*AllowASTWithCompilerErrors=*/true,
282 /*UserFilesAreVolatile=*/false);
283 if (!AU) {
284 errs() << "failed to create TU for: " << modulePath << '\n';
285 return true;
286 }
287
288 PrintIndexDataConsumer DataConsumer(outs());
289 IndexingOptions IndexOpts;
290 indexASTUnit(Unit&: *AU, DataConsumer, Opts: IndexOpts);
291
292 return false;
293}
294
295//===----------------------------------------------------------------------===//
296// Helper Utils
297//===----------------------------------------------------------------------===//
298
299static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
300 OS << getSymbolKindString(K: SymInfo.Kind);
301 if (SymInfo.SubKind != SymbolSubKind::None)
302 OS << '/' << getSymbolSubKindString(K: SymInfo.SubKind);
303 if (SymInfo.Properties) {
304 OS << '(';
305 printSymbolProperties(Props: SymInfo.Properties, OS);
306 OS << ')';
307 }
308 OS << '/' << getSymbolLanguageString(K: SymInfo.Lang);
309}
310
311static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
312 raw_ostream &OS) {
313 if (printSymbolName(D, LO: Ctx.getLangOpts(), OS)) {
314 OS << "<no-name>";
315 }
316 OS << " | ";
317
318 SmallString<256> USRBuf;
319 if (generateUSRForDecl(D, Buf&: USRBuf)) {
320 OS << "<no-usr>";
321 } else {
322 OS << USRBuf;
323 }
324}
325
326static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) {
327 assert(Mod);
328 OS << Mod->getFullModuleName() << " | ";
329 generateFullUSRForModule(Mod, OS);
330}
331
332//===----------------------------------------------------------------------===//
333// Command line processing.
334//===----------------------------------------------------------------------===//
335
336int indextest_core_main(int argc, const char **argv) {
337 sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]);
338 PrettyStackTraceProgram X(argc, argv);
339 void *MainAddr = (void*) (intptr_t) indextest_core_main;
340 std::string Executable = llvm::sys::fs::getMainExecutable(argv0: argv[0], MainExecAddr: MainAddr);
341
342 assert(argv[1] == StringRef("core"));
343 ++argv;
344 --argc;
345
346 std::vector<const char *> CompArgs;
347 const char **DoubleDash = std::find(first: argv, last: argv + argc, val: StringRef("--"));
348 if (DoubleDash != argv + argc) {
349 CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc);
350 argc = DoubleDash - argv;
351 }
352
353 cl::HideUnrelatedOptions(Category&: options::IndexTestCoreCategory);
354 cl::ParseCommandLineOptions(argc, argv, Overview: "index-test-core");
355
356 if (options::Action == ActionType::None) {
357 errs() << "error: action required; pass '-help' for options\n";
358 return 1;
359 }
360
361 if (options::Action == ActionType::PrintSourceSymbols) {
362 if (!options::ModuleFilePath.empty()) {
363 return printSourceSymbolsFromModule(modulePath: options::ModuleFilePath,
364 format: options::ModuleFormat);
365 }
366 if (CompArgs.empty()) {
367 errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
368 return 1;
369 }
370 return printSourceSymbols(Executable: Executable.c_str(), Args: CompArgs,
371 dumpModuleImports: options::DumpModuleImports,
372 indexLocals: options::IncludeLocals, ignoreMacros: options::IgnoreMacros);
373 }
374
375 return 0;
376}
377
378//===----------------------------------------------------------------------===//
379// Utility functions
380//===----------------------------------------------------------------------===//
381
382int indextest_perform_shell_execution(const char *command_line) {
383 BumpPtrAllocator Alloc;
384 llvm::StringSaver Saver(Alloc);
385 SmallVector<const char *, 4> Args;
386 llvm::cl::TokenizeGNUCommandLine(Source: command_line, Saver, NewArgv&: Args);
387 auto Program = llvm::sys::findProgramByName(Name: Args[0]);
388 if (std::error_code ec = Program.getError()) {
389 llvm::errs() << "command not found: " << Args[0] << "\n";
390 return ec.value();
391 }
392 SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end());
393 return llvm::sys::ExecuteAndWait(Program: *Program, Args: execArgs);
394}
395