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