27#include "llvm/ADT/StringRef.h"
28#include "llvm/Support/CommandLine.h"
29#include "llvm/Support/Error.h"
30#include "llvm/Support/FileSystem.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/Support/Parallel.h"
33#include "llvm/Support/Path.h"
34#include "llvm/Support/Signals.h"
35#include "llvm/Support/raw_ostream.h"
44static llvm::cl::OptionCategory RemapCategory(
"clangd-remap options");
46static llvm::cl::opt<std::string> PathMappingsArg{
48 llvm::cl::cat(RemapCategory),
50 "List of path mappings applied to every string in each background "
51 "index shard. Format: /old/path=/new/path[,/old2=/new2,...]"),
55static llvm::cl::opt<std::string> IndexDir{
56 llvm::cl::desc(
"<index-dir>"),
57 llvm::cl::cat(RemapCategory),
62static llvm::cl::opt<unsigned> NumThreads{
64 llvm::cl::cat(RemapCategory),
65 llvm::cl::desc(
"Number of worker threads (0 = all)"),
69static llvm::cl::opt<Logger::Level> LogLevel{
71 llvm::cl::cat(RemapCategory),
72 llvm::cl::desc(
"Verbosity of log messages written to stderr"),
75 clEnumValN(
Logger::Info,
"info",
"High level execution tracing"),
84std::optional<std::string> remapString(llvm::StringRef S,
87 if (S.starts_with(
"file://"))
93 size_t FirstSlash = S.find(
'/');
94 if (FirstSlash == llvm::StringRef::npos)
97 for (
const auto &Mapping : Mappings) {
98 size_t Pos = S.find(Mapping.ClientPath);
99 if (Pos == FirstSlash) {
100 llvm::StringRef After = S.substr(Pos + Mapping.ClientPath.size());
102 if (After.empty() || After.front() ==
'/')
103 return (S.substr(0, Pos) + Mapping.ServerPath + After).str();
111void remapRef(llvm::StringRef &S,
const PathMappings &Mappings,
112 llvm::StringSaver &Saver) {
113 if (
auto R = remapString(S, Mappings))
114 S = Saver.save(std::move(*R));
119void remapOrCopyRef(llvm::StringRef &S,
const PathMappings &Mappings,
120 llvm::StringSaver &Saver) {
121 if (
auto R = remapString(S, Mappings))
122 S = Saver.save(std::move(*R));
127void remapCharURI(
const char *&P,
const PathMappings &Mappings,
128 llvm::StringSaver &Saver) {
129 llvm::StringRef S(P);
130 if (
auto R = remapString(S, Mappings))
131 P = Saver.save(std::move(*R)).data();
134void remapStdStr(std::string &S,
const PathMappings &Mappings) {
135 if (
auto R = remapString(S, Mappings))
139std::vector<std::string> collectShards(llvm::StringRef Dir) {
140 std::vector<std::string> Paths;
142 for (llvm::sys::fs::recursive_directory_iterator It(Dir, EC), End;
143 It != End && !EC; It.increment(EC)) {
144 if (llvm::sys::path::extension(It->path()) ==
".idx")
145 Paths.push_back(It->path());
148 elog(
"Error scanning directory {0}: {1}", Dir, EC.message());
154std::string shardName(llvm::StringRef SourceFilePath) {
155 return (llvm::sys::path::filename(SourceFilePath) +
"." +
156 llvm::toHex(
digest(SourceFilePath)) +
".idx")
166std::string deriveNewFilename(
const IndexFileIn &Data,
167 llvm::StringRef OldFilename,
169 if (!Data.Sources || Data.Sources->empty())
170 return OldFilename.str();
172 for (
const auto &Entry : *Data.Sources) {
175 llvm::consumeError(U.takeError());
180 llvm::consumeError(
Path.takeError());
183 if (shardName(*
Path) == OldFilename) {
184 std::string NewPath = *
Path;
185 remapStdStr(NewPath, Mappings);
186 return shardName(NewPath);
189 return OldFilename.str();
195 llvm::StringSaver &Saver) {
199 for (
const auto &Sym : *Data.Symbols) {
201 remapCharURI(S.CanonicalDeclaration.FileURI, Mappings, Saver);
202 remapCharURI(S.Definition.FileURI, Mappings, Saver);
203 for (
auto &Inc : S.IncludeHeaders)
204 remapRef(Inc.IncludeHeader, Mappings, Saver);
207 Data.Symbols = std::move(Builder).build();
212 for (
const auto &Entry : *Data.Refs) {
213 for (
const auto &R : Entry.second) {
215 remapCharURI(MR.Location.FileURI, Mappings, Saver);
216 Builder.insert(Entry.first, MR);
219 Data.Refs = std::move(Builder).
build();
227 for (
auto &Entry : *Data.Sources) {
229 remapOrCopyRef(IGN.URI, Mappings, Saver);
230 for (
auto &Inc : IGN.DirectIncludes)
231 remapOrCopyRef(Inc, Mappings, Saver);
232 NewSources[IGN.URI] = std::move(IGN);
234 Data.Sources = std::move(NewSources);
238 remapStdStr(Data.Cmd->Directory, Mappings);
239 for (
auto &Arg : Data.Cmd->CommandLine)
240 remapStdStr(Arg, Mappings);
241 remapStdStr(Data.Cmd->Filename, Mappings);
249int main(
int Argc,
const char **Argv) {
252 llvm::sys::PrintStackTraceOnErrorSignal(Argv[0]);
253 llvm::cl::HideUnrelatedOptions(RemapCategory);
254 llvm::cl::ParseCommandLineOptions(Argc, Argv,
255 "clangd-remap: rewrite paths inside "
256 "background-index .idx shards\n");
263 elog(
"Invalid --path-mappings: {0}", Mappings.takeError());
266 if (Mappings->empty()) {
267 elog(
"No path mappings specified.");
272 auto AllShards = collectShards(IndexDir);
273 if (AllShards.empty()) {
274 log(
"No .idx files found in the specified directories.");
278 log(
"Found {0} shard(s) to process.", AllShards.size());
279 for (
const auto &M : *Mappings)
280 log(
" Path mapping: {0}", M);
282 if (NumThreads.getValue() != 0)
283 llvm::parallel::strategy = llvm::hardware_concurrency(NumThreads);
285 std::atomic<unsigned> Errors{0};
286 std::atomic<unsigned> FilesRenamed{0};
287 std::atomic<unsigned> FilesUnchanged{0};
289 llvm::parallelFor(0, AllShards.size(), [&](
size_t I) {
290 const std::string &ShardPath = AllShards[I];
292 auto Buf = llvm::MemoryBuffer::getFile(ShardPath);
294 elog(
"Cannot read {0}: {1}", ShardPath, Buf.getError().message());
301 elog(
"Cannot parse {0}: {1}", ShardPath, Parsed.takeError());
308 llvm::StringRef OldFilename = llvm::sys::path::filename(ShardPath);
309 std::string NewFilename =
310 deriveNewFilename(*Parsed, OldFilename, *Mappings);
313 llvm::BumpPtrAllocator Arena;
314 llvm::StringSaver Saver(Arena);
315 remapIndexData(*Parsed, *Mappings, Saver);
318 llvm::StringRef ParentDir = llvm::sys::path::parent_path(ShardPath);
319 llvm::SmallString<256> NewPath(ParentDir);
320 llvm::sys::path::append(NewPath, NewFilename);
321 if (
auto Err = llvm::writeToOutput(NewPath, [&](llvm::raw_ostream &OS) {
325 return llvm::Error::success();
327 elog(
"Cannot write {0}: {1}", NewPath, std::move(Err));
333 if (NewFilename != OldFilename) {
334 llvm::sys::fs::remove(ShardPath);
340 unsigned Renamed = FilesRenamed.load();
341 unsigned Unchanged = FilesUnchanged.load();
342 log(
"Processed: {0} shard(s), {1} renamed, {2} unchanged, {3} error(s).",
343 Renamed + Unchanged, Renamed, Unchanged, Errors.load());
344 return Errors.load() > 0 ? 1 : 0;
int main(int Argc, const char **Argv)
void elog(const char *Fmt, Ts &&... Vals)
Interface to allow custom logging in clangd.
Only one LoggingSession can be active at a time.
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
RefSlab build() &&
Consumes the builder to finalize the slab.
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
llvm::Expected< IndexFileIn > readIndexFile(llvm::StringRef Data, SymbolOrigin Origin)
std::vector< PathMapping > PathMappings
FileDigest digest(llvm::StringRef Content)
llvm::Expected< PathMappings > parsePathMappings(llvm::StringRef RawPathMappings)
Parse the command line RawPathMappings (e.g.
llvm::StringMap< IncludeGraphNode > IncludeGraph
void log(const char *Fmt, Ts &&... Vals)
std::string Path
A typedef to represent a file path.
void elog(const char *Fmt, Ts &&... Vals)
std::optional< std::string > doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings)
Returns a modified S with the first matching path in Mappings substituted, if applicable.
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents a symbol occurrence in the source file.
The class presents a C++ symbol, e.g.