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
31using namespace clang;
32using namespace clang::index;
33using namespace llvm;
34
35extern "C" int indextest_core_main(int argc, const char **argv);
36extern "C" int indextest_perform_shell_execution(const char *command_line);
37
38namespace {
39
40enum class ActionType {
41 None,
42 PrintSourceSymbols,
43};
44
45namespace options {
46
47static cl::OptionCategory IndexTestCoreCategory("index-test-core options");
48
49static cl::opt<ActionType>
50Action(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
56static cl::extrahelp MoreHelp(
57 "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
58 "invocation\n"
59);
60
61static cl::opt<bool>
62DumpModuleImports("dump-imported-module-files",
63 cl::desc("Print symbols and input files from imported modules"));
64
65static cl::opt<bool>
66IncludeLocals("include-locals", cl::desc("Print local symbols"));
67
68static cl::opt<bool> IgnoreMacros("ignore-macros",
69 cl::desc("Skip indexing macros"));
70
71static cl::opt<std::string>
72ModuleFilePath("module-file",
73 cl::desc("Path to module file to print symbols from"));
74static 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
81static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS);
82static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
83 raw_ostream &OS);
84static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS);
85
86namespace {
87
88class PrintIndexDataConsumer : public IndexDataConsumer {
89 raw_ostream &OS;
90 std::unique_ptr<ASTNameGenerator> ASTNameGen;
91 std::shared_ptr<Preprocessor> PP;
92
93public:
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
205static 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
216static 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
264static 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
304static 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
316static 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
331static 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
341int 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
387int 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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/tools/c-index-test/core_main.cpp