xref: /openbsd-src/gnu/llvm/clang/tools/c-index-test/core_main.cpp (revision d2c5a4743fb945f45b034a3a830a96f7e1bc695d)
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       modulePath, *pchRdr, ASTUnit::LoadASTOnly, Diags,
264       FileSystemOpts, /*UseDebugInfo=*/false,
265       /*OnlyLocalDecls=*/true, None,
266       CaptureDiagsKind::None,
267       /*AllowPCHWithCompilerErrors=*/true,
268       /*UserFilesAreVolatile=*/false);
269   if (!AU) {
270     errs() << "failed to create TU for: " << modulePath << '\n';
271     return true;
272   }
273 
274   PrintIndexDataConsumer DataConsumer(outs());
275   IndexingOptions IndexOpts;
276   indexASTUnit(*AU, DataConsumer, IndexOpts);
277 
278   return false;
279 }
280 
281 //===----------------------------------------------------------------------===//
282 // Helper Utils
283 //===----------------------------------------------------------------------===//
284 
285 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
286   OS << getSymbolKindString(SymInfo.Kind);
287   if (SymInfo.SubKind != SymbolSubKind::None)
288     OS << '/' << getSymbolSubKindString(SymInfo.SubKind);
289   if (SymInfo.Properties) {
290     OS << '(';
291     printSymbolProperties(SymInfo.Properties, OS);
292     OS << ')';
293   }
294   OS << '/' << getSymbolLanguageString(SymInfo.Lang);
295 }
296 
297 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
298                                   raw_ostream &OS) {
299   if (printSymbolName(D, Ctx.getLangOpts(), OS)) {
300     OS << "<no-name>";
301   }
302   OS << " | ";
303 
304   SmallString<256> USRBuf;
305   if (generateUSRForDecl(D, USRBuf)) {
306     OS << "<no-usr>";
307   } else {
308     OS << USRBuf;
309   }
310 }
311 
312 static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) {
313   assert(Mod);
314   OS << Mod->getFullModuleName() << " | ";
315   generateFullUSRForModule(Mod, OS);
316 }
317 
318 //===----------------------------------------------------------------------===//
319 // Command line processing.
320 //===----------------------------------------------------------------------===//
321 
322 int indextest_core_main(int argc, const char **argv) {
323   sys::PrintStackTraceOnErrorSignal(argv[0]);
324   PrettyStackTraceProgram X(argc, argv);
325   void *MainAddr = (void*) (intptr_t) indextest_core_main;
326   std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
327 
328   assert(argv[1] == StringRef("core"));
329   ++argv;
330   --argc;
331 
332   std::vector<const char *> CompArgs;
333   const char **DoubleDash = std::find(argv, argv + argc, StringRef("--"));
334   if (DoubleDash != argv + argc) {
335     CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc);
336     argc = DoubleDash - argv;
337   }
338 
339   cl::HideUnrelatedOptions(options::IndexTestCoreCategory);
340   cl::ParseCommandLineOptions(argc, argv, "index-test-core");
341 
342   if (options::Action == ActionType::None) {
343     errs() << "error: action required; pass '-help' for options\n";
344     return 1;
345   }
346 
347   if (options::Action == ActionType::PrintSourceSymbols) {
348     if (!options::ModuleFilePath.empty()) {
349       return printSourceSymbolsFromModule(options::ModuleFilePath,
350                                           options::ModuleFormat);
351     }
352     if (CompArgs.empty()) {
353       errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
354       return 1;
355     }
356     return printSourceSymbols(Executable.c_str(), CompArgs,
357                               options::DumpModuleImports,
358                               options::IncludeLocals);
359   }
360 
361   return 0;
362 }
363