1 //===-- core_main.cpp - Core Index Tool testbed ---------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
11 #include "clang/Frontend/ASTUnit.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Frontend/CompilerInvocation.h"
14 #include "clang/Frontend/FrontendAction.h"
15 #include "clang/Index/IndexingAction.h"
16 #include "clang/Index/IndexDataConsumer.h"
17 #include "clang/Index/USRGeneration.h"
18 #include "clang/Index/CodegenNameGenerator.h"
19 #include "clang/Serialization/ASTReader.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/Signals.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include "llvm/Support/PrettyStackTrace.h"
24 
25 using namespace clang;
26 using namespace clang::index;
27 using namespace llvm;
28 
29 extern "C" int indextest_core_main(int argc, const char **argv);
30 
31 namespace {
32 
33 enum class ActionType {
34   None,
35   PrintSourceSymbols,
36 };
37 
38 namespace options {
39 
40 static cl::OptionCategory IndexTestCoreCategory("index-test-core options");
41 
42 static cl::opt<ActionType>
43 Action(cl::desc("Action:"), cl::init(ActionType::None),
44        cl::values(
45           clEnumValN(ActionType::PrintSourceSymbols,
46                      "print-source-symbols", "Print symbols from source")),
47        cl::cat(IndexTestCoreCategory));
48 
49 static cl::extrahelp MoreHelp(
50   "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
51   "invocation\n"
52 );
53 
54 static cl::opt<bool>
55 DumpModuleImports("dump-imported-module-files",
56                cl::desc("Print symbols and input files from imported modules"));
57 
58 static cl::opt<bool>
59 IncludeLocals("include-locals", cl::desc("Print local symbols"));
60 
61 static cl::opt<std::string>
62 ModuleFilePath("module-file",
63                cl::desc("Path to module file to print symbols from"));
64 static cl::opt<std::string>
65   ModuleFormat("fmodule-format", cl::init("raw"),
66         cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
67 
68 }
69 } // anonymous namespace
70 
71 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS);
72 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
73                                   raw_ostream &OS);
74 
75 namespace {
76 
77 class PrintIndexDataConsumer : public IndexDataConsumer {
78   raw_ostream &OS;
79   std::unique_ptr<CodegenNameGenerator> CGNameGen;
80 
81 public:
82   PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) {
83   }
84 
85   void initialize(ASTContext &Ctx) override {
86     CGNameGen.reset(new CodegenNameGenerator(Ctx));
87   }
88 
89   bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles,
90                            ArrayRef<SymbolRelation> Relations,
91                            FileID FID, unsigned Offset,
92                            ASTNodeInfo ASTNode) override {
93     ASTContext &Ctx = D->getASTContext();
94     SourceManager &SM = Ctx.getSourceManager();
95 
96     unsigned Line = SM.getLineNumber(FID, Offset);
97     unsigned Col = SM.getColumnNumber(FID, Offset);
98     OS << Line << ':' << Col << " | ";
99 
100     printSymbolInfo(getSymbolInfo(D), OS);
101     OS << " | ";
102 
103     printSymbolNameAndUSR(D, Ctx, OS);
104     OS << " | ";
105 
106     if (CGNameGen->writeName(D, OS))
107       OS << "<no-cgname>";
108     OS << " | ";
109 
110     printSymbolRoles(Roles, OS);
111     OS << " | ";
112 
113     OS << "rel: " << Relations.size() << '\n';
114 
115     for (auto &SymRel : Relations) {
116       OS << '\t';
117       printSymbolRoles(SymRel.Roles, OS);
118       OS << " | ";
119       printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS);
120       OS << '\n';
121     }
122 
123     return true;
124   }
125 
126   bool handleModuleOccurence(const ImportDecl *ImportD, SymbolRoleSet Roles,
127                              FileID FID, unsigned Offset) override {
128     ASTContext &Ctx = ImportD->getASTContext();
129     SourceManager &SM = Ctx.getSourceManager();
130 
131     unsigned Line = SM.getLineNumber(FID, Offset);
132     unsigned Col = SM.getColumnNumber(FID, Offset);
133     OS << Line << ':' << Col << " | ";
134 
135     printSymbolInfo(getSymbolInfo(ImportD), OS);
136     OS << " | ";
137 
138     OS << ImportD->getImportedModule()->getFullModuleName() << " | ";
139 
140     printSymbolRoles(Roles, OS);
141     OS << " |\n";
142 
143     return true;
144   }
145 };
146 
147 } // anonymous namespace
148 
149 //===----------------------------------------------------------------------===//
150 // Print Source Symbols
151 //===----------------------------------------------------------------------===//
152 
153 static void dumpModuleFileInputs(serialization::ModuleFile &Mod,
154                                  ASTReader &Reader,
155                                  raw_ostream &OS) {
156   OS << "---- Module Inputs ----\n";
157   Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false,
158                         [&](const serialization::InputFile &IF, bool isSystem) {
159     OS << (isSystem ? "system" : "user") << " | ";
160     OS << IF.getFile()->getName() << '\n';
161   });
162 }
163 
164 static bool printSourceSymbols(ArrayRef<const char *> Args,
165                                bool dumpModuleImports,
166                                bool indexLocals) {
167   SmallVector<const char *, 4> ArgsWithProgName;
168   ArgsWithProgName.push_back("clang");
169   ArgsWithProgName.append(Args.begin(), Args.end());
170   IntrusiveRefCntPtr<DiagnosticsEngine>
171     Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
172   auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags);
173   if (!CInvok)
174     return true;
175 
176   raw_ostream &OS = outs();
177   auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS);
178   IndexingOptions IndexOpts;
179   IndexOpts.IndexFunctionLocals = indexLocals;
180   std::unique_ptr<FrontendAction> IndexAction;
181   IndexAction = createIndexingAction(DataConsumer, IndexOpts,
182                                      /*WrappedAction=*/nullptr);
183 
184   auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
185   std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
186       std::move(CInvok), PCHContainerOps, Diags, IndexAction.get()));
187 
188   if (!Unit)
189     return true;
190 
191   if (dumpModuleImports) {
192     if (auto Reader = Unit->getASTReader()) {
193       Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool {
194         OS << "==== Module " << Mod.ModuleName << " ====\n";
195         indexModuleFile(Mod, *Reader, DataConsumer, IndexOpts);
196         dumpModuleFileInputs(Mod, *Reader, OS);
197         return true; // skip module dependencies.
198       });
199     }
200   }
201 
202   return false;
203 }
204 
205 static bool printSourceSymbolsFromModule(StringRef modulePath,
206                                          StringRef format) {
207   FileSystemOptions FileSystemOpts;
208   auto pchContOps = std::make_shared<PCHContainerOperations>();
209   // Register the support for object-file-wrapped Clang modules.
210   pchContOps->registerReader(llvm::make_unique<ObjectFilePCHContainerReader>());
211   auto pchRdr = pchContOps->getReaderOrNull(format);
212   if (!pchRdr) {
213     errs() << "unknown module format: " << format << '\n';
214     return true;
215   }
216 
217   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
218       CompilerInstance::createDiagnostics(new DiagnosticOptions());
219   std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
220       modulePath, *pchRdr, Diags,
221       FileSystemOpts, /*UseDebugInfo=*/false,
222       /*OnlyLocalDecls=*/true, None,
223       /*CaptureDiagnostics=*/false,
224       /*AllowPCHWithCompilerErrors=*/true,
225       /*UserFilesAreVolatile=*/false);
226   if (!AU) {
227     errs() << "failed to create TU for: " << modulePath << '\n';
228     return true;
229   }
230 
231   auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(outs());
232   IndexingOptions IndexOpts;
233   indexASTUnit(*AU, DataConsumer, IndexOpts);
234 
235   return false;
236 }
237 
238 //===----------------------------------------------------------------------===//
239 // Helper Utils
240 //===----------------------------------------------------------------------===//
241 
242 static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
243   OS << getSymbolKindString(SymInfo.Kind);
244   if (SymInfo.SubKind != SymbolSubKind::None)
245     OS << '/' << getSymbolSubKindString(SymInfo.SubKind);
246   if (SymInfo.Properties) {
247     OS << '(';
248     printSymbolProperties(SymInfo.Properties, OS);
249     OS << ')';
250   }
251   OS << '/' << getSymbolLanguageString(SymInfo.Lang);
252 }
253 
254 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
255                                   raw_ostream &OS) {
256   if (printSymbolName(D, Ctx.getLangOpts(), OS)) {
257     OS << "<no-name>";
258   }
259   OS << " | ";
260 
261   SmallString<256> USRBuf;
262   if (generateUSRForDecl(D, USRBuf)) {
263     OS << "<no-usr>";
264   } else {
265     OS << USRBuf;
266   }
267 }
268 
269 //===----------------------------------------------------------------------===//
270 // Command line processing.
271 //===----------------------------------------------------------------------===//
272 
273 int indextest_core_main(int argc, const char **argv) {
274   sys::PrintStackTraceOnErrorSignal(argv[0]);
275   PrettyStackTraceProgram X(argc, argv);
276 
277   assert(argv[1] == StringRef("core"));
278   ++argv;
279   --argc;
280 
281   std::vector<const char *> CompArgs;
282   const char **DoubleDash = std::find(argv, argv + argc, StringRef("--"));
283   if (DoubleDash != argv + argc) {
284     CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc);
285     argc = DoubleDash - argv;
286   }
287 
288   cl::HideUnrelatedOptions(options::IndexTestCoreCategory);
289   cl::ParseCommandLineOptions(argc, argv, "index-test-core");
290 
291   if (options::Action == ActionType::None) {
292     errs() << "error: action required; pass '-help' for options\n";
293     return 1;
294   }
295 
296   if (options::Action == ActionType::PrintSourceSymbols) {
297     if (!options::ModuleFilePath.empty()) {
298       return printSourceSymbolsFromModule(options::ModuleFilePath,
299                                           options::ModuleFormat);
300     }
301     if (CompArgs.empty()) {
302       errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
303       return 1;
304     }
305     return printSourceSymbols(CompArgs, options::DumpModuleImports, options::IncludeLocals);
306   }
307 
308   return 0;
309 }
310