xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/c-index-test/core_main.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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/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 "llvm/Support/CommandLine.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/PrettyStackTrace.h"
24 #include "llvm/Support/Program.h"
25 #include "llvm/Support/Signals.h"
26 #include "llvm/Support/StringSaver.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 using namespace clang;
30 using namespace clang::index;
31 using namespace llvm;
32 
33 extern "C" int indextest_core_main(int argc, const char **argv);
34 extern "C" int indextest_perform_shell_execution(const char *command_line);
35 
36 namespace {
37 
38 enum class ActionType {
39   None,
40   PrintSourceSymbols,
41 };
42 
43 namespace options {
44 
45 static cl::OptionCategory IndexTestCoreCategory("index-test-core options");
46 
47 static cl::opt<ActionType>
48 Action(cl::desc("Action:"), cl::init(ActionType::None),
49        cl::values(
50           clEnumValN(ActionType::PrintSourceSymbols,
51                      "print-source-symbols", "Print symbols from source")),
52        cl::cat(IndexTestCoreCategory));
53 
54 static cl::extrahelp MoreHelp(
55   "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
56   "invocation\n"
57 );
58 
59 static cl::opt<bool>
60 DumpModuleImports("dump-imported-module-files",
61                cl::desc("Print symbols and input files from imported modules"));
62 
63 static cl::opt<bool>
64 IncludeLocals("include-locals", cl::desc("Print local symbols"));
65 
66 static cl::opt<bool> IgnoreMacros("ignore-macros",
67                                   cl::desc("Skip indexing macros"));
68 
69 static cl::opt<std::string>
70 ModuleFilePath("module-file",
71                cl::desc("Path to module file to print symbols from"));
72 static cl::opt<std::string>
73   ModuleFormat("fmodule-format", cl::init("raw"),
74         cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
75 
76 }
77 } // anonymous namespace
78 
79 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS);
80 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
81                                   raw_ostream &OS);
82 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS);
83 
84 namespace {
85 
86 class PrintIndexDataConsumer : public IndexDataConsumer {
87   raw_ostream &OS;
88   std::unique_ptr<ASTNameGenerator> ASTNameGen;
89   std::shared_ptr<Preprocessor> PP;
90 
91 public:
PrintIndexDataConsumer(raw_ostream & OS)92   PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) {
93   }
94 
initialize(ASTContext & Ctx)95   void initialize(ASTContext &Ctx) override {
96     ASTNameGen.reset(new ASTNameGenerator(Ctx));
97   }
98 
setPreprocessor(std::shared_ptr<Preprocessor> PP)99   void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
100     this->PP = std::move(PP);
101   }
102 
handleDeclOccurrence(const Decl * D,SymbolRoleSet Roles,ArrayRef<SymbolRelation> Relations,SourceLocation Loc,ASTNodeInfo ASTNode)103   bool handleDeclOccurrence(const Decl *D, SymbolRoleSet Roles,
104                             ArrayRef<SymbolRelation> Relations,
105                             SourceLocation Loc, ASTNodeInfo ASTNode) override {
106     ASTContext &Ctx = D->getASTContext();
107     SourceManager &SM = Ctx.getSourceManager();
108 
109     Loc = SM.getFileLoc(Loc);
110     FileID FID = SM.getFileID(Loc);
111     unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc));
112     unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc));
113     OS << Line << ':' << Col << " | ";
114 
115     printSymbolInfo(getSymbolInfo(D), OS);
116     OS << " | ";
117 
118     printSymbolNameAndUSR(D, Ctx, OS);
119     OS << " | ";
120 
121     if (ASTNameGen->writeName(D, OS))
122       OS << "<no-cgname>";
123     OS << " | ";
124 
125     printSymbolRoles(Roles, OS);
126     OS << " | ";
127 
128     OS << "rel: " << Relations.size() << '\n';
129 
130     for (auto &SymRel : Relations) {
131       OS << '\t';
132       printSymbolRoles(SymRel.Roles, OS);
133       OS << " | ";
134       printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS);
135       OS << '\n';
136     }
137 
138     return true;
139   }
140 
handleModuleOccurrence(const ImportDecl * ImportD,const clang::Module * Mod,SymbolRoleSet Roles,SourceLocation Loc)141   bool handleModuleOccurrence(const ImportDecl *ImportD,
142                               const clang::Module *Mod, SymbolRoleSet Roles,
143                               SourceLocation Loc) override {
144     ASTContext &Ctx = ImportD->getASTContext();
145     SourceManager &SM = Ctx.getSourceManager();
146 
147     Loc = SM.getFileLoc(Loc);
148     FileID FID = SM.getFileID(Loc);
149     unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc));
150     unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc));
151     OS << Line << ':' << Col << " | ";
152 
153     printSymbolInfo(getSymbolInfo(ImportD), OS);
154     OS << " | ";
155 
156     printSymbolNameAndUSR(Mod, OS);
157     OS << " | ";
158 
159     printSymbolRoles(Roles, OS);
160     OS << " |\n";
161 
162     return true;
163   }
164 
handleMacroOccurrence(const IdentifierInfo * Name,const MacroInfo * MI,SymbolRoleSet Roles,SourceLocation Loc)165   bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI,
166                              SymbolRoleSet Roles, SourceLocation Loc) override {
167     assert(PP);
168     SourceManager &SM = PP->getSourceManager();
169 
170     Loc = SM.getFileLoc(Loc);
171     FileID FID = SM.getFileID(Loc);
172     unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc));
173     unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc));
174     OS << Line << ':' << Col << " | ";
175 
176     printSymbolInfo(getSymbolInfoForMacro(*MI), OS);
177     OS << " | ";
178 
179     OS << Name->getName();
180     OS << " | ";
181 
182     SmallString<256> USRBuf;
183     if (generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM,
184                             USRBuf)) {
185       OS << "<no-usr>";
186     } else {
187       OS << USRBuf;
188     }
189     OS << " | ";
190 
191     printSymbolRoles(Roles, OS);
192     OS << " |\n";
193     return true;
194   }
195 };
196 
197 } // anonymous namespace
198 
199 //===----------------------------------------------------------------------===//
200 // Print Source Symbols
201 //===----------------------------------------------------------------------===//
202 
dumpModuleFileInputs(serialization::ModuleFile & Mod,ASTReader & Reader,raw_ostream & OS)203 static void dumpModuleFileInputs(serialization::ModuleFile &Mod,
204                                  ASTReader &Reader,
205                                  raw_ostream &OS) {
206   OS << "---- Module Inputs ----\n";
207   Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false,
208                         [&](const serialization::InputFile &IF, bool isSystem) {
209     OS << (isSystem ? "system" : "user") << " | ";
210     OS << IF.getFile()->getName() << '\n';
211   });
212 }
213 
printSourceSymbols(const char * Executable,ArrayRef<const char * > Args,bool dumpModuleImports,bool indexLocals,bool ignoreMacros)214 static bool printSourceSymbols(const char *Executable,
215                                ArrayRef<const char *> Args,
216                                bool dumpModuleImports, bool indexLocals,
217                                bool ignoreMacros) {
218   SmallVector<const char *, 4> ArgsWithProgName;
219   ArgsWithProgName.push_back(Executable);
220   ArgsWithProgName.append(Args.begin(), Args.end());
221   IntrusiveRefCntPtr<DiagnosticsEngine>
222     Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
223   auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags);
224   if (!CInvok)
225     return true;
226 
227   raw_ostream &OS = outs();
228   auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS);
229   IndexingOptions IndexOpts;
230   IndexOpts.IndexFunctionLocals = indexLocals;
231   IndexOpts.IndexMacros = !ignoreMacros;
232   IndexOpts.IndexMacrosInPreprocessor = !ignoreMacros;
233   std::unique_ptr<FrontendAction> IndexAction =
234       createIndexingAction(DataConsumer, IndexOpts);
235 
236   auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
237   std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
238       std::move(CInvok), PCHContainerOps, Diags, IndexAction.get()));
239 
240   if (!Unit)
241     return true;
242 
243   if (dumpModuleImports) {
244     if (auto Reader = Unit->getASTReader()) {
245       Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool {
246         OS << "==== Module " << Mod.ModuleName << " ====\n";
247         indexModuleFile(Mod, *Reader, *DataConsumer, IndexOpts);
248         dumpModuleFileInputs(Mod, *Reader, OS);
249         return true; // skip module dependencies.
250       });
251     }
252   }
253 
254   return false;
255 }
256 
printSourceSymbolsFromModule(StringRef modulePath,StringRef format)257 static bool printSourceSymbolsFromModule(StringRef modulePath,
258                                          StringRef format) {
259   FileSystemOptions FileSystemOpts;
260   auto pchContOps = std::make_shared<PCHContainerOperations>();
261   // Register the support for object-file-wrapped Clang modules.
262   pchContOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
263   auto pchRdr = pchContOps->getReaderOrNull(format);
264   if (!pchRdr) {
265     errs() << "unknown module format: " << format << '\n';
266     return true;
267   }
268 
269   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
270       CompilerInstance::createDiagnostics(new DiagnosticOptions());
271   std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
272       std::string(modulePath), *pchRdr, ASTUnit::LoadASTOnly, Diags,
273       FileSystemOpts, /*UseDebugInfo=*/false,
274       /*OnlyLocalDecls=*/true, CaptureDiagsKind::None,
275       /*AllowASTWithCompilerErrors=*/true,
276       /*UserFilesAreVolatile=*/false);
277   if (!AU) {
278     errs() << "failed to create TU for: " << modulePath << '\n';
279     return true;
280   }
281 
282   PrintIndexDataConsumer DataConsumer(outs());
283   IndexingOptions IndexOpts;
284   indexASTUnit(*AU, DataConsumer, IndexOpts);
285 
286   return false;
287 }
288 
289 //===----------------------------------------------------------------------===//
290 // Helper Utils
291 //===----------------------------------------------------------------------===//
292 
printSymbolInfo(SymbolInfo SymInfo,raw_ostream & OS)293 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
294   OS << getSymbolKindString(SymInfo.Kind);
295   if (SymInfo.SubKind != SymbolSubKind::None)
296     OS << '/' << getSymbolSubKindString(SymInfo.SubKind);
297   if (SymInfo.Properties) {
298     OS << '(';
299     printSymbolProperties(SymInfo.Properties, OS);
300     OS << ')';
301   }
302   OS << '/' << getSymbolLanguageString(SymInfo.Lang);
303 }
304 
printSymbolNameAndUSR(const Decl * D,ASTContext & Ctx,raw_ostream & OS)305 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
306                                   raw_ostream &OS) {
307   if (printSymbolName(D, Ctx.getLangOpts(), OS)) {
308     OS << "<no-name>";
309   }
310   OS << " | ";
311 
312   SmallString<256> USRBuf;
313   if (generateUSRForDecl(D, USRBuf)) {
314     OS << "<no-usr>";
315   } else {
316     OS << USRBuf;
317   }
318 }
319 
printSymbolNameAndUSR(const clang::Module * Mod,raw_ostream & OS)320 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) {
321   assert(Mod);
322   OS << Mod->getFullModuleName() << " | ";
323   generateFullUSRForModule(Mod, OS);
324 }
325 
326 //===----------------------------------------------------------------------===//
327 // Command line processing.
328 //===----------------------------------------------------------------------===//
329 
indextest_core_main(int argc,const char ** argv)330 int indextest_core_main(int argc, const char **argv) {
331   sys::PrintStackTraceOnErrorSignal(argv[0]);
332   PrettyStackTraceProgram X(argc, argv);
333   void *MainAddr = (void*) (intptr_t) indextest_core_main;
334   std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
335 
336   assert(argv[1] == StringRef("core"));
337   ++argv;
338   --argc;
339 
340   std::vector<const char *> CompArgs;
341   const char **DoubleDash = std::find(argv, argv + argc, StringRef("--"));
342   if (DoubleDash != argv + argc) {
343     CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc);
344     argc = DoubleDash - argv;
345   }
346 
347   cl::HideUnrelatedOptions(options::IndexTestCoreCategory);
348   cl::ParseCommandLineOptions(argc, argv, "index-test-core");
349 
350   if (options::Action == ActionType::None) {
351     errs() << "error: action required; pass '-help' for options\n";
352     return 1;
353   }
354 
355   if (options::Action == ActionType::PrintSourceSymbols) {
356     if (!options::ModuleFilePath.empty()) {
357       return printSourceSymbolsFromModule(options::ModuleFilePath,
358                                           options::ModuleFormat);
359     }
360     if (CompArgs.empty()) {
361       errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
362       return 1;
363     }
364     return printSourceSymbols(Executable.c_str(), CompArgs,
365                               options::DumpModuleImports,
366                               options::IncludeLocals, options::IgnoreMacros);
367   }
368 
369   return 0;
370 }
371 
372 //===----------------------------------------------------------------------===//
373 // Utility functions
374 //===----------------------------------------------------------------------===//
375 
indextest_perform_shell_execution(const char * command_line)376 int indextest_perform_shell_execution(const char *command_line) {
377   BumpPtrAllocator Alloc;
378   llvm::StringSaver Saver(Alloc);
379   SmallVector<const char *, 4> Args;
380   llvm::cl::TokenizeGNUCommandLine(command_line, Saver, Args);
381   auto Program = llvm::sys::findProgramByName(Args[0]);
382   if (std::error_code ec = Program.getError()) {
383     llvm::errs() << "command not found: " << Args[0] << "\n";
384     return ec.value();
385   }
386   SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end());
387   return llvm::sys::ExecuteAndWait(*Program, execArgs);
388 }
389