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