clang-tools 24.0.0git
IndexerMain.cpp
Go to the documentation of this file.
1//===--- IndexerMain.cpp -----------------------------------------*- C++-*-===//
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// clangd-indexer is a tool to gather index data (symbols, xrefs) from source.
10//
11//===----------------------------------------------------------------------===//
12
13#include "ClangdServer.h"
14#include "CompileCommands.h"
15#include "Compiler.h"
16#include "ConfigProvider.h"
17#include "index/IndexAction.h"
18#include "index/Merge.h"
19#include "index/Ref.h"
20#include "index/Serialization.h"
21#include "index/Symbol.h"
23#include "support/Context.h"
24#include "support/Logger.h"
26#include "clang/Tooling/ArgumentsAdjusters.h"
27#include "clang/Tooling/Execution.h"
28#include "clang/Tooling/Tooling.h"
29#include "llvm/Support/CommandLine.h"
30#include "llvm/Support/Path.h"
31#include "llvm/Support/Signals.h"
32#include <memory>
33#include <utility>
34#include <vector>
35
36namespace clang {
37namespace clangd {
38namespace {
39
40static llvm::cl::opt<IndexFileFormat>
41 Format("format", llvm::cl::desc("Format of the index to be written"),
42 llvm::cl::values(clEnumValN(IndexFileFormat::YAML, "yaml",
43 "human-readable YAML format"),
44 clEnumValN(IndexFileFormat::RIFF, "binary",
45 "binary RIFF format")),
46 llvm::cl::init(IndexFileFormat::RIFF));
47
48static llvm::cl::list<std::string> QueryDriverGlobs{
49 "query-driver",
50 llvm::cl::desc(
51 "Comma separated list of globs for white-listing gcc-compatible "
52 "drivers that are safe to execute. Drivers matching any of these globs "
53 "will be used to extract system includes. e.g. "
54 "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),
55 llvm::cl::CommaSeparated,
56};
57
58static llvm::cl::opt<bool> EnableConfig{
59 "enable-config",
61 llvm::cl::init(true),
62};
63
64class IndexActionFactory : public tooling::FrontendActionFactory {
65public:
66 IndexActionFactory(IndexFileIn &Result) : Result(Result) {}
67
68 std::unique_ptr<FrontendAction> create() override {
69 SymbolCollector::Options Opts;
70 Opts.CountReferences = true;
71 Opts.FileFilter = [&](const SourceManager &SM, FileID FID) {
72 const auto F = SM.getFileEntryRefForID(FID);
73 if (!F)
74 return false; // Skip invalid files.
75 auto AbsPath = getCanonicalPath(*F, SM.getFileManager());
76 if (!AbsPath)
77 return false; // Skip files without absolute path.
78 std::lock_guard<std::mutex> Lock(FilesMu);
79 return Files.insert(*AbsPath).second; // Skip already processed files.
80 };
81 return createStaticIndexingAction(Opts, [&](IndexFileIn Result) {
82 {
83 // Merge as we go.
84 std::lock_guard<std::mutex> Lock(SymbolsMu);
85 for (const auto &Sym : *Result.Symbols) {
86 if (const auto *Existing = Symbols.find(Sym.ID))
87 Symbols.insert(mergeSymbol(*Existing, Sym));
88 else
89 Symbols.insert(Sym);
90 }
91 }
92 {
93 std::lock_guard<std::mutex> Lock(RefsMu);
94 for (const auto &Sym : *Result.Refs) {
95 // Deduplication happens during insertion.
96 for (const auto &Ref : Sym.second)
97 Refs.insert(Sym.first, Ref);
98 }
99 }
100 {
101 std::lock_guard<std::mutex> Lock(RelsMu);
102 for (const auto &R : *Result.Relations) {
103 Relations.insert(R);
104 }
105 }
106 // FIXME: Handle Result.Sources?
107 });
108 }
109
110 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
111 FileManager *Files,
112 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
113 DiagnosticConsumer *DiagConsumer) override {
114 disableUnsupportedOptions(*Invocation);
115 return tooling::FrontendActionFactory::runInvocation(
116 std::move(Invocation), Files, std::move(PCHContainerOps), DiagConsumer);
117 }
118
119 // Awkward: we write the result in the destructor, because the executor
120 // takes ownership so it's the easiest way to get our data back out.
121 ~IndexActionFactory() {
122 Result.Symbols = std::move(Symbols).build();
123 Result.Refs = std::move(Refs).build();
124 Result.Relations = std::move(Relations).build();
125 }
126
127private:
128 IndexFileIn &Result;
129 std::mutex FilesMu;
130 llvm::StringSet<> Files;
131 std::mutex SymbolsMu;
132 SymbolSlab::Builder Symbols;
133 std::mutex RefsMu;
134 RefSlab::Builder Refs;
135 std::mutex RelsMu;
136 RelationSlab::Builder Relations;
137};
138
139} // namespace
140} // namespace clangd
141} // namespace clang
142
143int main(int argc, const char **argv) {
144 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
145
146 const char *Overview = R"(
147 Creates an index of symbol information etc in a whole project.
148
149 Example usage for a project using CMake compile commands:
150
151 $ clangd-indexer --executor=all-TUs compile_commands.json > clangd.dex
152
153 Example usage for file sequence index without flags:
154
155 $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > clangd.dex
156
157 Note: only symbols from header files will be indexed.
158 )";
159
160 auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
161 argc, argv, llvm::cl::getGeneralCategory(), Overview);
162
163 if (!Executor) {
164 llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
165 return 1;
166 }
167
169 std::vector<std::unique_ptr<clang::clangd::config::Provider>> ProviderStack;
170 if (clang::clangd::EnableConfig)
171 ProviderStack =
173 auto ConfigProvider =
174 clang::clangd::config::Provider::combine(std::move(ProviderStack));
175 auto ContextProvider =
177 ConfigProvider.get(), /*Callbacks=*/nullptr);
178
179 // Collect symbols found in each translation unit, merging as we go.
181 auto Mangler = std::make_shared<clang::clangd::CommandMangler>(
183 Mangler->SystemIncludeExtractor = clang::clangd::getSystemIncludeExtractor(
184 static_cast<llvm::ArrayRef<std::string>>(
185 clang::clangd::QueryDriverGlobs));
186 auto Err = Executor->get()->execute(
187 std::make_unique<clang::clangd::IndexActionFactory>(Data),
188 clang::tooling::ArgumentsAdjuster(
189 [Mangler = std::move(Mangler),
190 ContextProvider = std::move(ContextProvider)](
191 const std::vector<std::string> &Args, llvm::StringRef File) {
192 // FIXME: If File is relative, it's relative to the compile
193 // command's "directory", not our CWD, but ToolExecutor doesn't
194 // expose "directory" here. We don't have enough information to
195 // correctly determine the absolute path, so we don't apply the
196 // config for relative paths. See
197 // indexer-clangd-config-relative-path.test.
198 // FIXME: WithCfg only lives for this ArgumentsAdjuster call, so
199 // it's visible to Mangler below but not to the parse that follows.
200 // That's harmless today since clangd-indexer doesn't consult
201 // config during the parse, but a real fix would need libTooling
202 // changes to keep the context alive for the whole invocation.
203 std::optional<clang::clangd::WithContext> WithCfg;
204 if (llvm::sys::path::is_absolute(File)) {
205 WithCfg.emplace(ContextProvider(File));
206 }
207
208 clang::tooling::CompileCommand Cmd;
209 Cmd.CommandLine = Args;
210 Mangler->operator()(Cmd, File);
211 return Cmd.CommandLine;
212 }));
213 if (Err) {
214 clang::clangd::elog("{0}", std::move(Err));
215 }
216
217 // Emit collected data.
219 Out.Format = clang::clangd::Format;
220 llvm::outs() << Out;
221 return 0;
222}
int main(int argc, const char **argv)
static std::function< Context(PathRef)> createConfiguredContextProvider(const config::Provider *Provider, ClangdServer::Callbacks *)
Creates a context provider that loads and installs config.
static const char *const EnableConfigFlagDesc
Help text for the –enable-config flag, shared by clangd tools that expose createDefaultProviders() on...
static std::vector< std::unique_ptr< Provider > > createDefaultProviders(const ThreadsafeFS &)
Returns providers for the configuration files that clangd tools read by default: project config (ance...
static std::unique_ptr< Provider > combine(std::vector< std::unique_ptr< Provider > >)
A provider that includes fragments from all the supplied providers.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
SystemIncludeExtractorFn getSystemIncludeExtractor(llvm::ArrayRef< std::string > QueryDriverGlobs)
Symbol mergeSymbol(const Symbol &L, const Symbol &R)
Definition Merge.cpp:266
std::optional< std::string > getCanonicalPath(const FileEntryRef F, FileManager &FileMgr)
Get the canonical path of F.
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(IndexFileIn)> IndexContentsCallback)
void disableUnsupportedOptions(CompilerInvocation &CI)
Clears CI from options that are not supported by clangd, like codegen or plugins.
Definition Compiler.cpp:47
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static CommandMangler detect()