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(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("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(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
204static 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
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(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  CIOpts.ProbePrecompiled = true; // FIXME: historical default. Needed?
227  auto CInvok = createInvocation(ArgsWithProgName, std::move(CIOpts));
228  if (!CInvok)
229    return true;
230
231  raw_ostream &OS = outs();
232  auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(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, IndexOpts);
239
240  auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
241  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
242      std::move(CInvok), PCHContainerOps, Diags, IndexAction.get()));
243
244  if (!Unit)
245    return true;
246
247  if (dumpModuleImports) {
248    if (auto Reader = Unit->getASTReader()) {
249      Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool {
250        OS << "==== Module " << Mod.ModuleName << " ====\n";
251        indexModuleFile(Mod, *Reader, *DataConsumer, IndexOpts);
252        dumpModuleFileInputs(Mod, *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(std::make_unique<ObjectFilePCHContainerReader>());
267  auto pchRdr = pchContOps->getReaderOrNull(format);
268  if (!pchRdr) {
269    errs() << "unknown module format: " << format << '\n';
270    return true;
271  }
272
273  IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
274      CompilerInstance::createDiagnostics(new DiagnosticOptions());
275  std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
276      std::string(modulePath), *pchRdr, ASTUnit::LoadASTOnly, Diags,
277      FileSystemOpts, /*UseDebugInfo=*/false,
278      /*OnlyLocalDecls=*/true, CaptureDiagsKind::None,
279      /*AllowASTWithCompilerErrors=*/true,
280      /*UserFilesAreVolatile=*/false);
281  if (!AU) {
282    errs() << "failed to create TU for: " << modulePath << '\n';
283    return true;
284  }
285
286  PrintIndexDataConsumer DataConsumer(outs());
287  IndexingOptions IndexOpts;
288  indexASTUnit(*AU, DataConsumer, IndexOpts);
289
290  return false;
291}
292
293//===----------------------------------------------------------------------===//
294// Helper Utils
295//===----------------------------------------------------------------------===//
296
297static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
298  OS << getSymbolKindString(SymInfo.Kind);
299  if (SymInfo.SubKind != SymbolSubKind::None)
300    OS << '/' << getSymbolSubKindString(SymInfo.SubKind);
301  if (SymInfo.Properties) {
302    OS << '(';
303    printSymbolProperties(SymInfo.Properties, OS);
304    OS << ')';
305  }
306  OS << '/' << getSymbolLanguageString(SymInfo.Lang);
307}
308
309static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
310                                  raw_ostream &OS) {
311  if (printSymbolName(D, Ctx.getLangOpts(), OS)) {
312    OS << "<no-name>";
313  }
314  OS << " | ";
315
316  SmallString<256> USRBuf;
317  if (generateUSRForDecl(D, USRBuf)) {
318    OS << "<no-usr>";
319  } else {
320    OS << USRBuf;
321  }
322}
323
324static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) {
325  assert(Mod);
326  OS << Mod->getFullModuleName() << " | ";
327  generateFullUSRForModule(Mod, OS);
328}
329
330//===----------------------------------------------------------------------===//
331// Command line processing.
332//===----------------------------------------------------------------------===//
333
334int indextest_core_main(int argc, const char **argv) {
335  sys::PrintStackTraceOnErrorSignal(argv[0]);
336  PrettyStackTraceProgram X(argc, argv);
337  void *MainAddr = (void*) (intptr_t) indextest_core_main;
338  std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
339
340  assert(argv[1] == StringRef("core"));
341  ++argv;
342  --argc;
343
344  std::vector<const char *> CompArgs;
345  const char **DoubleDash = std::find(argv, argv + argc, StringRef("--"));
346  if (DoubleDash != argv + argc) {
347    CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc);
348    argc = DoubleDash - argv;
349  }
350
351  cl::HideUnrelatedOptions(options::IndexTestCoreCategory);
352  cl::ParseCommandLineOptions(argc, argv, "index-test-core");
353
354  if (options::Action == ActionType::None) {
355    errs() << "error: action required; pass '-help' for options\n";
356    return 1;
357  }
358
359  if (options::Action == ActionType::PrintSourceSymbols) {
360    if (!options::ModuleFilePath.empty()) {
361      return printSourceSymbolsFromModule(options::ModuleFilePath,
362                                          options::ModuleFormat);
363    }
364    if (CompArgs.empty()) {
365      errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
366      return 1;
367    }
368    return printSourceSymbols(Executable.c_str(), CompArgs,
369                              options::DumpModuleImports,
370                              options::IncludeLocals, options::IgnoreMacros);
371  }
372
373  return 0;
374}
375
376//===----------------------------------------------------------------------===//
377// Utility functions
378//===----------------------------------------------------------------------===//
379
380int indextest_perform_shell_execution(const char *command_line) {
381  BumpPtrAllocator Alloc;
382  llvm::StringSaver Saver(Alloc);
383  SmallVector<const char *, 4> Args;
384  llvm::cl::TokenizeGNUCommandLine(command_line, Saver, Args);
385  auto Program = llvm::sys::findProgramByName(Args[0]);
386  if (std::error_code ec = Program.getError()) {
387    llvm::errs() << "command not found: " << Args[0] << "\n";
388    return ec.value();
389  }
390  SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end());
391  return llvm::sys::ExecuteAndWait(*Program, execArgs);
392}
393