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/Frontend/ASTUnit.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Frontend/CompilerInvocation.h"
14 #include "clang/Frontend/FrontendAction.h"
15 #include "clang/Frontend/Utils.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 "clang/Serialization/ObjectFilePCHContainerReader.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/VirtualFileSystem.h"
29 #include "llvm/Support/raw_ostream.h"
31 using namespace clang
;
32 using namespace clang::index
;
35 extern "C" int indextest_core_main(int argc
, const char **argv
);
36 extern "C" int indextest_perform_shell_execution(const char *command_line
);
40 enum class ActionType
{
47 static cl::OptionCategory
IndexTestCoreCategory("index-test-core options");
49 static cl::opt
<ActionType
>
50 Action(cl::desc("Action:"), cl::init(ActionType::None
),
52 clEnumValN(ActionType::PrintSourceSymbols
,
53 "print-source-symbols", "Print symbols from source")),
54 cl::cat(IndexTestCoreCategory
));
56 static cl::extrahelp
MoreHelp(
57 "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
62 DumpModuleImports("dump-imported-module-files",
63 cl::desc("Print symbols and input files from imported modules"));
66 IncludeLocals("include-locals", cl::desc("Print local symbols"));
68 static cl::opt
<bool> IgnoreMacros("ignore-macros",
69 cl::desc("Skip indexing macros"));
71 static cl::opt
<std::string
>
72 ModuleFilePath("module-file",
73 cl::desc("Path to module file to print symbols from"));
74 static cl::opt
<std::string
>
75 ModuleFormat("fmodule-format", cl::init("raw"),
76 cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
79 } // anonymous namespace
81 static void printSymbolInfo(SymbolInfo SymInfo
, raw_ostream
&OS
);
82 static void printSymbolNameAndUSR(const Decl
*D
, ASTContext
&Ctx
,
84 static void printSymbolNameAndUSR(const clang::Module
*Mod
, raw_ostream
&OS
);
88 class PrintIndexDataConsumer
: public IndexDataConsumer
{
90 std::unique_ptr
<ASTNameGenerator
> ASTNameGen
;
91 std::shared_ptr
<Preprocessor
> PP
;
94 PrintIndexDataConsumer(raw_ostream
&OS
) : OS(OS
) {
97 void initialize(ASTContext
&Ctx
) override
{
98 ASTNameGen
.reset(new ASTNameGenerator(Ctx
));
101 void setPreprocessor(std::shared_ptr
<Preprocessor
> PP
) override
{
102 this->PP
= std::move(PP
);
105 bool handleDeclOccurrence(const Decl
*D
, SymbolRoleSet Roles
,
106 ArrayRef
<SymbolRelation
> Relations
,
107 SourceLocation Loc
, ASTNodeInfo ASTNode
) override
{
108 ASTContext
&Ctx
= D
->getASTContext();
109 SourceManager
&SM
= Ctx
.getSourceManager();
111 Loc
= SM
.getFileLoc(Loc
);
112 FileID FID
= SM
.getFileID(Loc
);
113 unsigned Line
= SM
.getLineNumber(FID
, SM
.getFileOffset(Loc
));
114 unsigned Col
= SM
.getColumnNumber(FID
, SM
.getFileOffset(Loc
));
115 OS
<< Line
<< ':' << Col
<< " | ";
117 printSymbolInfo(getSymbolInfo(D
), OS
);
120 printSymbolNameAndUSR(D
, Ctx
, OS
);
123 if (ASTNameGen
->writeName(D
, OS
))
127 printSymbolRoles(Roles
, OS
);
130 OS
<< "rel: " << Relations
.size() << '\n';
132 for (auto &SymRel
: Relations
) {
134 printSymbolRoles(SymRel
.Roles
, OS
);
136 printSymbolNameAndUSR(SymRel
.RelatedSymbol
, Ctx
, OS
);
143 bool handleModuleOccurrence(const ImportDecl
*ImportD
,
144 const clang::Module
*Mod
, SymbolRoleSet Roles
,
145 SourceLocation Loc
) override
{
146 ASTContext
&Ctx
= ImportD
->getASTContext();
147 SourceManager
&SM
= Ctx
.getSourceManager();
149 Loc
= SM
.getFileLoc(Loc
);
150 FileID FID
= SM
.getFileID(Loc
);
151 unsigned Line
= SM
.getLineNumber(FID
, SM
.getFileOffset(Loc
));
152 unsigned Col
= SM
.getColumnNumber(FID
, SM
.getFileOffset(Loc
));
153 OS
<< Line
<< ':' << Col
<< " | ";
155 printSymbolInfo(getSymbolInfo(ImportD
), OS
);
158 printSymbolNameAndUSR(Mod
, OS
);
161 printSymbolRoles(Roles
, OS
);
167 bool handleMacroOccurrence(const IdentifierInfo
*Name
, const MacroInfo
*MI
,
168 SymbolRoleSet Roles
, SourceLocation Loc
) override
{
170 SourceManager
&SM
= PP
->getSourceManager();
172 Loc
= SM
.getFileLoc(Loc
);
173 FileID FID
= SM
.getFileID(Loc
);
174 unsigned Line
= SM
.getLineNumber(FID
, SM
.getFileOffset(Loc
));
175 unsigned Col
= SM
.getColumnNumber(FID
, SM
.getFileOffset(Loc
));
176 OS
<< Line
<< ':' << Col
<< " | ";
178 printSymbolInfo(getSymbolInfoForMacro(*MI
), OS
);
181 OS
<< Name
->getName();
184 SmallString
<256> USRBuf
;
185 if (generateUSRForMacro(Name
->getName(), MI
->getDefinitionLoc(), SM
,
193 printSymbolRoles(Roles
, OS
);
199 } // anonymous namespace
201 //===----------------------------------------------------------------------===//
202 // Print Source Symbols
203 //===----------------------------------------------------------------------===//
205 static void dumpModuleFileInputs(serialization::ModuleFile
&Mod
,
208 OS
<< "---- Module Inputs ----\n";
209 Reader
.visitInputFiles(Mod
, /*IncludeSystem=*/true, /*Complain=*/false,
210 [&](const serialization::InputFile
&IF
, bool isSystem
) {
211 OS
<< (isSystem
? "system" : "user") << " | ";
212 OS
<< IF
.getFile()->getName() << '\n';
216 static bool printSourceSymbols(const char *Executable
,
217 ArrayRef
<const char *> Args
,
218 bool dumpModuleImports
, bool indexLocals
,
220 SmallVector
<const char *, 4> ArgsWithProgName
;
221 ArgsWithProgName
.push_back(Executable
);
222 ArgsWithProgName
.append(Args
.begin(), Args
.end());
223 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags(
224 CompilerInstance::createDiagnostics(*llvm::vfs::getRealFileSystem(),
225 new DiagnosticOptions
));
226 CreateInvocationOptions CIOpts
;
227 CIOpts
.Diags
= Diags
;
228 CIOpts
.ProbePrecompiled
= true; // FIXME: historical default. Needed?
229 auto CInvok
= createInvocation(ArgsWithProgName
, std::move(CIOpts
));
233 raw_ostream
&OS
= outs();
234 auto DataConsumer
= std::make_shared
<PrintIndexDataConsumer
>(OS
);
235 IndexingOptions IndexOpts
;
236 IndexOpts
.IndexFunctionLocals
= indexLocals
;
237 IndexOpts
.IndexMacros
= !ignoreMacros
;
238 IndexOpts
.IndexMacrosInPreprocessor
= !ignoreMacros
;
239 std::unique_ptr
<FrontendAction
> IndexAction
=
240 createIndexingAction(DataConsumer
, IndexOpts
);
242 auto PCHContainerOps
= std::make_shared
<PCHContainerOperations
>();
243 std::unique_ptr
<ASTUnit
> Unit(ASTUnit::LoadFromCompilerInvocationAction(
244 std::move(CInvok
), PCHContainerOps
, Diags
, IndexAction
.get()));
249 if (dumpModuleImports
) {
250 if (auto Reader
= Unit
->getASTReader()) {
251 Reader
->getModuleManager().visit([&](serialization::ModuleFile
&Mod
) -> bool {
252 OS
<< "==== Module " << Mod
.ModuleName
<< " ====\n";
253 indexModuleFile(Mod
, *Reader
, *DataConsumer
, IndexOpts
);
254 dumpModuleFileInputs(Mod
, *Reader
, OS
);
255 return true; // skip module dependencies.
263 static bool printSourceSymbolsFromModule(StringRef modulePath
,
265 FileSystemOptions FileSystemOpts
;
266 auto pchContOps
= std::make_shared
<PCHContainerOperations
>();
267 // Register the support for object-file-wrapped Clang modules.
268 pchContOps
->registerReader(std::make_unique
<ObjectFilePCHContainerReader
>());
269 auto pchRdr
= pchContOps
->getReaderOrNull(format
);
271 errs() << "unknown module format: " << format
<< '\n';
275 auto HSOpts
= std::make_shared
<HeaderSearchOptions
>();
277 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
=
278 CompilerInstance::createDiagnostics(*llvm::vfs::getRealFileSystem(),
279 new DiagnosticOptions());
280 std::unique_ptr
<ASTUnit
> AU
=
281 ASTUnit::LoadFromASTFile(modulePath
, *pchRdr
, ASTUnit::LoadASTOnly
, Diags
,
282 FileSystemOpts
, HSOpts
, /*LangOpts=*/nullptr,
283 /*OnlyLocalDecls=*/true, CaptureDiagsKind::None
,
284 /*AllowASTWithCompilerErrors=*/true,
285 /*UserFilesAreVolatile=*/false);
287 errs() << "failed to create TU for: " << modulePath
<< '\n';
291 PrintIndexDataConsumer
DataConsumer(outs());
292 IndexingOptions IndexOpts
;
293 indexASTUnit(*AU
, DataConsumer
, IndexOpts
);
298 //===----------------------------------------------------------------------===//
300 //===----------------------------------------------------------------------===//
302 static void printSymbolInfo(SymbolInfo SymInfo
, raw_ostream
&OS
) {
303 OS
<< getSymbolKindString(SymInfo
.Kind
);
304 if (SymInfo
.SubKind
!= SymbolSubKind::None
)
305 OS
<< '/' << getSymbolSubKindString(SymInfo
.SubKind
);
306 if (SymInfo
.Properties
) {
308 printSymbolProperties(SymInfo
.Properties
, OS
);
311 OS
<< '/' << getSymbolLanguageString(SymInfo
.Lang
);
314 static void printSymbolNameAndUSR(const Decl
*D
, ASTContext
&Ctx
,
316 if (printSymbolName(D
, Ctx
.getLangOpts(), OS
)) {
321 SmallString
<256> USRBuf
;
322 if (generateUSRForDecl(D
, USRBuf
)) {
329 static void printSymbolNameAndUSR(const clang::Module
*Mod
, raw_ostream
&OS
) {
331 OS
<< Mod
->getFullModuleName() << " | ";
332 generateFullUSRForModule(Mod
, OS
);
335 //===----------------------------------------------------------------------===//
336 // Command line processing.
337 //===----------------------------------------------------------------------===//
339 int indextest_core_main(int argc
, const char **argv
) {
340 sys::PrintStackTraceOnErrorSignal(argv
[0]);
341 PrettyStackTraceProgram
X(argc
, argv
);
342 void *MainAddr
= (void*) (intptr_t) indextest_core_main
;
343 std::string Executable
= llvm::sys::fs::getMainExecutable(argv
[0], MainAddr
);
345 assert(argv
[1] == StringRef("core"));
349 std::vector
<const char *> CompArgs
;
350 const char **DoubleDash
= std::find(argv
, argv
+ argc
, StringRef("--"));
351 if (DoubleDash
!= argv
+ argc
) {
352 CompArgs
= std::vector
<const char *>(DoubleDash
+ 1, argv
+ argc
);
353 argc
= DoubleDash
- argv
;
356 cl::HideUnrelatedOptions(options::IndexTestCoreCategory
);
357 cl::ParseCommandLineOptions(argc
, argv
, "index-test-core");
359 if (options::Action
== ActionType::None
) {
360 errs() << "error: action required; pass '-help' for options\n";
364 if (options::Action
== ActionType::PrintSourceSymbols
) {
365 if (!options::ModuleFilePath
.empty()) {
366 return printSourceSymbolsFromModule(options::ModuleFilePath
,
367 options::ModuleFormat
);
369 if (CompArgs
.empty()) {
370 errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n";
373 return printSourceSymbols(Executable
.c_str(), CompArgs
,
374 options::DumpModuleImports
,
375 options::IncludeLocals
, options::IgnoreMacros
);
381 //===----------------------------------------------------------------------===//
383 //===----------------------------------------------------------------------===//
385 int indextest_perform_shell_execution(const char *command_line
) {
386 BumpPtrAllocator Alloc
;
387 llvm::StringSaver
Saver(Alloc
);
388 SmallVector
<const char *, 4> Args
;
389 llvm::cl::TokenizeGNUCommandLine(command_line
, Saver
, Args
);
390 auto Program
= llvm::sys::findProgramByName(Args
[0]);
391 if (std::error_code ec
= Program
.getError()) {
392 llvm::errs() << "command not found: " << Args
[0] << "\n";
395 SmallVector
<StringRef
, 8> execArgs(Args
.begin(), Args
.end());
396 return llvm::sys::ExecuteAndWait(*Program
, execArgs
);