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