1 //===-- core_main.cpp - Core Index Tool testbed ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
30 using namespace clang
;
31 using namespace clang::index
;
34 extern "C" int indextest_core_main(int argc
, const char **argv
);
35 extern "C" int indextest_perform_shell_execution(const char *command_line
);
39 enum class ActionType
{
46 static cl::OptionCategory
IndexTestCoreCategory("index-test-core options");
48 static cl::opt
<ActionType
>
49 Action(cl::desc("Action:"), cl::init(ActionType::None
),
51 clEnumValN(ActionType::PrintSourceSymbols
,
52 "print-source-symbols", "Print symbols from source")),
53 cl::cat(IndexTestCoreCategory
));
55 static cl::extrahelp
MoreHelp(
56 "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
61 DumpModuleImports("dump-imported-module-files",
62 cl::desc("Print symbols and input files from imported modules"));
65 IncludeLocals("include-locals", cl::desc("Print local symbols"));
67 static cl::opt
<bool> IgnoreMacros("ignore-macros",
68 cl::desc("Skip indexing macros"));
70 static cl::opt
<std::string
>
71 ModuleFilePath("module-file",
72 cl::desc("Path to module file to print symbols from"));
73 static cl::opt
<std::string
>
74 ModuleFormat("fmodule-format", cl::init("raw"),
75 cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
78 } // anonymous namespace
80 static void printSymbolInfo(SymbolInfo SymInfo
, raw_ostream
&OS
);
81 static void printSymbolNameAndUSR(const Decl
*D
, ASTContext
&Ctx
,
83 static void printSymbolNameAndUSR(const clang::Module
*Mod
, raw_ostream
&OS
);
87 class PrintIndexDataConsumer
: public IndexDataConsumer
{
89 std::unique_ptr
<ASTNameGenerator
> ASTNameGen
;
90 std::shared_ptr
<Preprocessor
> PP
;
93 PrintIndexDataConsumer(raw_ostream
&OS
) : OS(OS
) {
96 void initialize(ASTContext
&Ctx
) override
{
97 ASTNameGen
.reset(new ASTNameGenerator(Ctx
));
100 void setPreprocessor(std::shared_ptr
<Preprocessor
> PP
) override
{
101 this->PP
= std::move(PP
);
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();
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
<< " | ";
116 printSymbolInfo(getSymbolInfo(D
), OS
);
119 printSymbolNameAndUSR(D
, Ctx
, OS
);
122 if (ASTNameGen
->writeName(D
, OS
))
126 printSymbolRoles(Roles
, OS
);
129 OS
<< "rel: " << Relations
.size() << '\n';
131 for (auto &SymRel
: Relations
) {
133 printSymbolRoles(SymRel
.Roles
, OS
);
135 printSymbolNameAndUSR(SymRel
.RelatedSymbol
, Ctx
, OS
);
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();
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
<< " | ";
154 printSymbolInfo(getSymbolInfo(ImportD
), OS
);
157 printSymbolNameAndUSR(Mod
, OS
);
160 printSymbolRoles(Roles
, OS
);
166 bool handleMacroOccurrence(const IdentifierInfo
*Name
, const MacroInfo
*MI
,
167 SymbolRoleSet Roles
, SourceLocation Loc
) override
{
169 SourceManager
&SM
= PP
->getSourceManager();
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
<< " | ";
177 printSymbolInfo(getSymbolInfoForMacro(*MI
), OS
);
180 OS
<< Name
->getName();
183 SmallString
<256> USRBuf
;
184 if (generateUSRForMacro(Name
->getName(), MI
->getDefinitionLoc(), SM
,
192 printSymbolRoles(Roles
, OS
);
198 } // anonymous namespace
200 //===----------------------------------------------------------------------===//
201 // Print Source Symbols
202 //===----------------------------------------------------------------------===//
204 static void dumpModuleFileInputs(serialization::ModuleFile
&Mod
,
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';
215 static bool printSourceSymbols(const char *Executable
,
216 ArrayRef
<const char *> Args
,
217 bool dumpModuleImports
, bool indexLocals
,
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
));
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
);
240 auto PCHContainerOps
= std::make_shared
<PCHContainerOperations
>();
241 std::unique_ptr
<ASTUnit
> Unit(ASTUnit::LoadFromCompilerInvocationAction(
242 std::move(CInvok
), PCHContainerOps
, Diags
, IndexAction
.get()));
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.
261 static bool printSourceSymbolsFromModule(StringRef modulePath
,
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
);
269 errs() << "unknown module format: " << format
<< '\n';
273 auto HSOpts
= std::make_shared
<HeaderSearchOptions
>();
275 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
=
276 CompilerInstance::createDiagnostics(new DiagnosticOptions());
277 std::unique_ptr
<ASTUnit
> AU
= ASTUnit::LoadFromASTFile(
278 std::string(modulePath
), *pchRdr
, ASTUnit::LoadASTOnly
, Diags
,
279 FileSystemOpts
, HSOpts
, /*UseDebugInfo=*/false,
280 /*OnlyLocalDecls=*/true, CaptureDiagsKind::None
,
281 /*AllowASTWithCompilerErrors=*/true,
282 /*UserFilesAreVolatile=*/false);
284 errs() << "failed to create TU for: " << modulePath
<< '\n';
288 PrintIndexDataConsumer
DataConsumer(outs());
289 IndexingOptions IndexOpts
;
290 indexASTUnit(*AU
, DataConsumer
, IndexOpts
);
295 //===----------------------------------------------------------------------===//
297 //===----------------------------------------------------------------------===//
299 static void printSymbolInfo(SymbolInfo SymInfo
, raw_ostream
&OS
) {
300 OS
<< getSymbolKindString(SymInfo
.Kind
);
301 if (SymInfo
.SubKind
!= SymbolSubKind::None
)
302 OS
<< '/' << getSymbolSubKindString(SymInfo
.SubKind
);
303 if (SymInfo
.Properties
) {
305 printSymbolProperties(SymInfo
.Properties
, OS
);
308 OS
<< '/' << getSymbolLanguageString(SymInfo
.Lang
);
311 static void printSymbolNameAndUSR(const Decl
*D
, ASTContext
&Ctx
,
313 if (printSymbolName(D
, Ctx
.getLangOpts(), OS
)) {
318 SmallString
<256> USRBuf
;
319 if (generateUSRForDecl(D
, USRBuf
)) {
326 static void printSymbolNameAndUSR(const clang::Module
*Mod
, raw_ostream
&OS
) {
328 OS
<< Mod
->getFullModuleName() << " | ";
329 generateFullUSRForModule(Mod
, OS
);
332 //===----------------------------------------------------------------------===//
333 // Command line processing.
334 //===----------------------------------------------------------------------===//
336 int indextest_core_main(int argc
, const char **argv
) {
337 sys::PrintStackTraceOnErrorSignal(argv
[0]);
338 PrettyStackTraceProgram
X(argc
, argv
);
339 void *MainAddr
= (void*) (intptr_t) indextest_core_main
;
340 std::string Executable
= llvm::sys::fs::getMainExecutable(argv
[0], MainAddr
);
342 assert(argv
[1] == StringRef("core"));
346 std::vector
<const char *> CompArgs
;
347 const char **DoubleDash
= std::find(argv
, argv
+ argc
, StringRef("--"));
348 if (DoubleDash
!= argv
+ argc
) {
349 CompArgs
= std::vector
<const char *>(DoubleDash
+ 1, argv
+ argc
);
350 argc
= DoubleDash
- argv
;
353 cl::HideUnrelatedOptions(options::IndexTestCoreCategory
);
354 cl::ParseCommandLineOptions(argc
, argv
, "index-test-core");
356 if (options::Action
== ActionType::None
) {
357 errs() << "error: action required; pass '-help' for options\n";
361 if (options::Action
== ActionType::PrintSourceSymbols
) {
362 if (!options::ModuleFilePath
.empty()) {
363 return printSourceSymbolsFromModule(options::ModuleFilePath
,
364 options::ModuleFormat
);
366 if (CompArgs
.empty()) {
367 errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
370 return printSourceSymbols(Executable
.c_str(), CompArgs
,
371 options::DumpModuleImports
,
372 options::IncludeLocals
, options::IgnoreMacros
);
378 //===----------------------------------------------------------------------===//
380 //===----------------------------------------------------------------------===//
382 int indextest_perform_shell_execution(const char *command_line
) {
383 BumpPtrAllocator Alloc
;
384 llvm::StringSaver
Saver(Alloc
);
385 SmallVector
<const char *, 4> Args
;
386 llvm::cl::TokenizeGNUCommandLine(command_line
, Saver
, Args
);
387 auto Program
= llvm::sys::findProgramByName(Args
[0]);
388 if (std::error_code ec
= Program
.getError()) {
389 llvm::errs() << "command not found: " << Args
[0] << "\n";
392 SmallVector
<StringRef
, 8> execArgs(Args
.begin(), Args
.end());
393 return llvm::sys::ExecuteAndWait(*Program
, execArgs
);