1//===--- Marshalling.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#include "Marshalling.h"
10#include "Headers.h"
11#include "Index.pb.h"
12#include "Protocol.h"
13#include "index/Index.h"
14#include "index/Ref.h"
15#include "index/Serialization.h"
16#include "index/Symbol.h"
17#include "index/SymbolID.h"
18#include "index/SymbolLocation.h"
19#include "index/SymbolOrigin.h"
20#include "support/Logger.h"
21#include "clang/Index/IndexSymbol.h"
22#include "llvm/ADT/DenseSet.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/Support/Error.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/Path.h"
29#include "llvm/Support/StringSaver.h"
30
31namespace clang {
32namespace clangd {
33namespace remote {
34
35using llvm::sys::path::append;
36using llvm::sys::path::convert_to_slash;
37using llvm::sys::path::is_absolute;
38using llvm::sys::path::replace_path_prefix;
39using llvm::sys::path::Style;
40
41namespace {
42
43template <typename IDRange>
44llvm::Expected<llvm::DenseSet<SymbolID>> getIDs(IDRange IDs) {
45 llvm::DenseSet<SymbolID> Result;
46 for (const auto &ID : IDs) {
47 auto SID = SymbolID::fromStr(StringRef(ID));
48 if (!SID)
49 return SID.takeError();
50 Result.insert(V: *SID);
51 }
52 return Result;
53}
54
55} // namespace
56
57Marshaller::Marshaller(llvm::StringRef RemoteIndexRoot,
58 llvm::StringRef LocalIndexRoot)
59 : Strings(Arena) {
60 llvm::StringRef PosixSeparator = get_separator(style: Style::posix);
61 if (!RemoteIndexRoot.empty()) {
62 assert(is_absolute(RemoteIndexRoot));
63 this->RemoteIndexRoot = convert_to_slash(path: RemoteIndexRoot, style: Style::windows);
64 llvm::StringRef Path(this->RemoteIndexRoot);
65 if (!is_separator(value: this->RemoteIndexRoot.back(), style: Style::posix))
66 this->RemoteIndexRoot += PosixSeparator;
67 }
68 if (!LocalIndexRoot.empty()) {
69 assert(is_absolute(LocalIndexRoot));
70 this->LocalIndexRoot = convert_to_slash(path: LocalIndexRoot, style: Style::windows);
71 llvm::StringRef Path(this->LocalIndexRoot);
72 if (!is_separator(value: this->LocalIndexRoot.back(), style: Style::posix))
73 this->LocalIndexRoot += PosixSeparator;
74 }
75 assert(!RemoteIndexRoot.empty() || !LocalIndexRoot.empty());
76}
77
78llvm::Expected<clangd::LookupRequest>
79Marshaller::fromProtobuf(const LookupRequest *Message) {
80 clangd::LookupRequest Req;
81 auto IDs = getIDs(Message->ids());
82 if (!IDs)
83 return IDs.takeError();
84 Req.IDs = std::move(*IDs);
85 return Req;
86}
87
88llvm::Expected<clangd::FuzzyFindRequest>
89Marshaller::fromProtobuf(const FuzzyFindRequest *Message) {
90 assert(!RemoteIndexRoot.empty());
91 clangd::FuzzyFindRequest Result;
92 Result.Query = Message->query();
93 for (const auto &Scope : Message->scopes())
94 Result.Scopes.push_back(Scope);
95 Result.AnyScope = Message->any_scope();
96 if (Message->limit())
97 Result.Limit = Message->limit();
98 Result.RestrictForCodeCompletion = Message->restricted_for_code_completion();
99 for (const auto &Path : Message->proximity_paths()) {
100 llvm::SmallString<256> LocalPath = llvm::StringRef(RemoteIndexRoot);
101 append(LocalPath, Path);
102 // FuzzyFindRequest requires proximity paths to have platform-native format
103 // in order for SymbolIndex to process the query correctly.
104 llvm::sys::path::native(LocalPath);
105 Result.ProximityPaths.push_back(std::string(LocalPath));
106 }
107 for (const auto &Type : Message->preferred_types())
108 Result.ProximityPaths.push_back(Type);
109 return Result;
110}
111
112llvm::Expected<clangd::RefsRequest>
113Marshaller::fromProtobuf(const RefsRequest *Message) {
114 clangd::RefsRequest Req;
115 auto IDs = getIDs(Message->ids());
116 if (!IDs)
117 return IDs.takeError();
118 Req.IDs = std::move(*IDs);
119 if (Message->has_filter())
120 Req.Filter = static_cast<clangd::RefKind>(Message->filter());
121 else
122 Req.Filter = clangd::RefKind::All;
123 if (Message->limit())
124 Req.Limit = Message->limit();
125 Req.WantContainer = Message->want_container();
126 return Req;
127}
128
129llvm::Expected<clangd::ContainedRefsRequest>
130Marshaller::fromProtobuf(const ContainedRefsRequest *Message) {
131 clangd::ContainedRefsRequest Req;
132 if (!Message->has_id())
133 return error(Fmt: "ContainedRefsRequest requires an id.");
134 auto ID = SymbolID::fromStr(Message->id());
135 if (!ID)
136 return ID.takeError();
137 Req.ID = *ID;
138 if (Message->has_limit())
139 Req.Limit = Message->limit();
140 return Req;
141}
142
143llvm::Expected<clangd::RelationsRequest>
144Marshaller::fromProtobuf(const RelationsRequest *Message) {
145 clangd::RelationsRequest Req;
146 auto IDs = getIDs(Message->subjects());
147 if (!IDs)
148 return IDs.takeError();
149 Req.Subjects = std::move(*IDs);
150 if (!Message->has_predicate())
151 return error(Fmt: "RelationsRequest requires RelationKind predicate.");
152 Req.Predicate = static_cast<RelationKind>(Message->predicate());
153 if (Message->limit())
154 Req.Limit = Message->limit();
155 return Req;
156}
157
158llvm::Expected<clangd::Symbol> Marshaller::fromProtobuf(const Symbol &Message) {
159 if (!Message.has_info() || !Message.has_canonical_declaration())
160 return error(Fmt: "Missing info or declaration.");
161 clangd::Symbol Result;
162 auto ID = SymbolID::fromStr(Message.id());
163 if (!ID)
164 return ID.takeError();
165 Result.ID = *ID;
166 Result.SymInfo = fromProtobuf(Message.info());
167 Result.Name = Message.name();
168 Result.Scope = Message.scope();
169 if (Message.has_definition()) {
170 auto Definition = fromProtobuf(Message.definition());
171 if (Definition)
172 Result.Definition = *Definition;
173 }
174 auto Declaration = fromProtobuf(Message.canonical_declaration());
175 if (!Declaration)
176 return Declaration.takeError();
177 Result.CanonicalDeclaration = *Declaration;
178 Result.References = Message.references();
179 // Overwrite symbol origin: it's coming from remote index.
180 Result.Origin = clangd::SymbolOrigin::Remote;
181 Result.Signature = Message.signature();
182 Result.TemplateSpecializationArgs = Message.template_specialization_args();
183 Result.CompletionSnippetSuffix = Message.completion_snippet_suffix();
184 Result.Documentation = Message.documentation();
185 Result.ReturnType = Message.return_type();
186 Result.Type = Message.type();
187 for (const auto &Header : Message.headers()) {
188 auto SerializedHeader = fromProtobuf(Header);
189 if (!SerializedHeader)
190 return SerializedHeader.takeError();
191 Result.IncludeHeaders.push_back(*SerializedHeader);
192 }
193 Result.Flags = static_cast<clangd::Symbol::SymbolFlag>(Message.flags());
194 return Result;
195}
196
197llvm::Expected<clangd::Ref> Marshaller::fromProtobuf(const Ref &Message) {
198 if (!Message.has_location())
199 return error(Fmt: "Missing location.");
200 clangd::Ref Result;
201 auto Location = fromProtobuf(Message.location());
202 if (!Location)
203 return Location.takeError();
204 Result.Location = *Location;
205 Result.Kind = static_cast<RefKind>(Message.kind());
206 auto ContainerID = SymbolID::fromStr(Message.container());
207 if (ContainerID)
208 Result.Container = *ContainerID;
209 return Result;
210}
211
212llvm::Expected<clangd::ContainedRefsResult>
213Marshaller::fromProtobuf(const ContainedRef &Message) {
214 clangd::ContainedRefsResult Result;
215 if (!Message.has_location())
216 return error(Fmt: "ContainedRef must have a location.");
217 if (!Message.has_kind())
218 return error(Fmt: "ContainedRef must have a kind.");
219 if (!Message.has_symbol())
220 return error(Fmt: "ContainedRef must have a symbol.");
221 auto Location = fromProtobuf(Message.location());
222 if (!Location)
223 return Location.takeError();
224 Result.Location = *Location;
225 Result.Kind = static_cast<RefKind>(Message.kind());
226 auto Symbol = SymbolID::fromStr(Message.symbol());
227 if (!Symbol)
228 return Symbol.takeError();
229 Result.Symbol = *Symbol;
230 return Result;
231}
232
233llvm::Expected<std::pair<clangd::SymbolID, clangd::Symbol>>
234Marshaller::fromProtobuf(const Relation &Message) {
235 auto SubjectID = SymbolID::fromStr(Message.subject_id());
236 if (!SubjectID)
237 return SubjectID.takeError();
238 if (!Message.has_object())
239 return error(Fmt: "Missing Object.");
240 auto Object = fromProtobuf(Message.object());
241 if (!Object)
242 return Object.takeError();
243 return std::make_pair(*SubjectID, *Object);
244}
245
246LookupRequest Marshaller::toProtobuf(const clangd::LookupRequest &From) {
247 LookupRequest RPCRequest;
248 for (const auto &SymbolID : From.IDs)
249 RPCRequest.add_ids(SymbolID.str());
250 return RPCRequest;
251}
252
253FuzzyFindRequest Marshaller::toProtobuf(const clangd::FuzzyFindRequest &From) {
254 assert(!LocalIndexRoot.empty());
255 FuzzyFindRequest RPCRequest;
256 RPCRequest.set_query(From.Query);
257 for (const auto &Scope : From.Scopes)
258 RPCRequest.add_scopes(Scope);
259 RPCRequest.set_any_scope(From.AnyScope);
260 if (From.Limit)
261 RPCRequest.set_limit(*From.Limit);
262 RPCRequest.set_restricted_for_code_completion(From.RestrictForCodeCompletion);
263 for (const auto &Path : From.ProximityPaths) {
264 llvm::SmallString<256> RelativePath = llvm::StringRef(Path);
265 if (replace_path_prefix(Path&: RelativePath, OldPrefix: LocalIndexRoot, NewPrefix: ""))
266 RPCRequest.add_proximity_paths(
267 convert_to_slash(path: RelativePath, style: Style::windows));
268 }
269 for (const auto &Type : From.PreferredTypes)
270 RPCRequest.add_preferred_types(Type);
271 return RPCRequest;
272}
273
274RefsRequest Marshaller::toProtobuf(const clangd::RefsRequest &From) {
275 RefsRequest RPCRequest;
276 for (const auto &ID : From.IDs)
277 RPCRequest.add_ids(ID.str());
278 RPCRequest.set_filter(static_cast<uint32_t>(From.Filter));
279 if (From.Limit)
280 RPCRequest.set_limit(*From.Limit);
281 RPCRequest.set_want_container(From.WantContainer);
282 return RPCRequest;
283}
284
285ContainedRefsRequest
286Marshaller::toProtobuf(const clangd::ContainedRefsRequest &From) {
287 ContainedRefsRequest RPCRequest;
288 RPCRequest.set_id(From.ID.str());
289 if (From.Limit)
290 RPCRequest.set_limit(*From.Limit);
291 return RPCRequest;
292}
293
294RelationsRequest Marshaller::toProtobuf(const clangd::RelationsRequest &From) {
295 RelationsRequest RPCRequest;
296 for (const auto &ID : From.Subjects)
297 RPCRequest.add_subjects(ID.str());
298 RPCRequest.set_predicate(static_cast<uint32_t>(From.Predicate));
299 if (From.Limit)
300 RPCRequest.set_limit(*From.Limit);
301 return RPCRequest;
302}
303
304llvm::Expected<Symbol> Marshaller::toProtobuf(const clangd::Symbol &From) {
305 Symbol Result;
306 Result.set_id(From.ID.str());
307 *Result.mutable_info() = toProtobuf(Info: From.SymInfo);
308 Result.set_name(From.Name.str());
309 if (*From.Definition.FileURI) {
310 auto Definition = toProtobuf(Location: From.Definition);
311 if (!Definition)
312 return Definition.takeError();
313 *Result.mutable_definition() = *Definition;
314 }
315 Result.set_scope(From.Scope.str());
316 auto Declaration = toProtobuf(Location: From.CanonicalDeclaration);
317 if (!Declaration)
318 return Declaration.takeError();
319 *Result.mutable_canonical_declaration() = *Declaration;
320 Result.set_references(From.References);
321 Result.set_signature(From.Signature.str());
322 Result.set_template_specialization_args(
323 From.TemplateSpecializationArgs.str());
324 Result.set_completion_snippet_suffix(From.CompletionSnippetSuffix.str());
325 Result.set_documentation(From.Documentation.str());
326 Result.set_return_type(From.ReturnType.str());
327 Result.set_type(From.Type.str());
328 for (const auto &Header : From.IncludeHeaders) {
329 auto Serialized = toProtobuf(Header);
330 if (!Serialized)
331 return Serialized.takeError();
332 auto *NextHeader = Result.add_headers();
333 *NextHeader = *Serialized;
334 }
335 Result.set_flags(static_cast<uint32_t>(From.Flags));
336 return Result;
337}
338
339llvm::Expected<Ref> Marshaller::toProtobuf(const clangd::Ref &From) {
340 Ref Result;
341 Result.set_kind(static_cast<uint32_t>(From.Kind));
342 auto Location = toProtobuf(Location: From.Location);
343 if (!Location)
344 return Location.takeError();
345 *Result.mutable_location() = *Location;
346 Result.set_container(From.Container.str());
347 return Result;
348}
349
350llvm::Expected<ContainedRef>
351Marshaller::toProtobuf(const clangd::ContainedRefsResult &From) {
352 ContainedRef Result;
353 auto Location = toProtobuf(Location: From.Location);
354 if (!Location)
355 return Location.takeError();
356 *Result.mutable_location() = *Location;
357 Result.set_kind(static_cast<uint32_t>(From.Kind));
358 *Result.mutable_symbol() = From.Symbol.str();
359 return Result;
360}
361
362llvm::Expected<Relation> Marshaller::toProtobuf(const clangd::SymbolID &Subject,
363 const clangd::Symbol &Object) {
364 Relation Result;
365 *Result.mutable_subject_id() = Subject.str();
366 auto SerializedObject = toProtobuf(From: Object);
367 if (!SerializedObject)
368 return SerializedObject.takeError();
369 *Result.mutable_object() = *SerializedObject;
370 return Result;
371}
372
373llvm::Expected<std::string>
374Marshaller::relativePathToURI(llvm::StringRef RelativePath) {
375 assert(!LocalIndexRoot.empty());
376 assert(RelativePath == convert_to_slash(RelativePath));
377 if (RelativePath.empty())
378 return error(Fmt: "Empty relative path.");
379 if (is_absolute(path: RelativePath, style: Style::posix))
380 return error(Fmt: "RelativePath '{0}' is absolute.", Vals&: RelativePath);
381 llvm::SmallString<256> FullPath = llvm::StringRef(LocalIndexRoot);
382 append(path&: FullPath, a: RelativePath);
383 auto Result = URI::createFile(AbsolutePath: FullPath);
384 return Result.toString();
385}
386
387llvm::Expected<std::string> Marshaller::uriToRelativePath(llvm::StringRef URI) {
388 assert(!RemoteIndexRoot.empty());
389 auto ParsedURI = URI::parse(Uri: URI);
390 if (!ParsedURI)
391 return ParsedURI.takeError();
392 if (ParsedURI->scheme() != "file")
393 return error(Fmt: "Can not use URI schemes other than file, given: '{0}'.", Vals&: URI);
394 llvm::SmallString<256> Result = ParsedURI->body();
395 llvm::StringRef Path(Result);
396 // Check for Windows paths (URI=file:///X:/path => Body=/X:/path)
397 if (is_absolute(path: Path.substr(Start: 1), style: Style::windows))
398 Result = Path.drop_front();
399 if (!replace_path_prefix(Path&: Result, OldPrefix: RemoteIndexRoot, NewPrefix: ""))
400 return error(Fmt: "File path '{0}' doesn't start with '{1}'.", Vals: Result.str(),
401 Vals&: RemoteIndexRoot);
402 assert(Result == convert_to_slash(Result, Style::windows));
403 return std::string(Result);
404}
405
406clangd::SymbolLocation::Position
407Marshaller::fromProtobuf(const Position &Message) {
408 clangd::SymbolLocation::Position Result;
409 Result.setColumn(static_cast<uint32_t>(Message.column()));
410 Result.setLine(static_cast<uint32_t>(Message.line()));
411 return Result;
412}
413
414Position
415Marshaller::toProtobuf(const clangd::SymbolLocation::Position &Position) {
416 remote::Position Result;
417 Result.set_column(Position.column());
418 Result.set_line(Position.line());
419 return Result;
420}
421
422clang::index::SymbolInfo Marshaller::fromProtobuf(const SymbolInfo &Message) {
423 clang::index::SymbolInfo Result;
424 Result.Kind = static_cast<clang::index::SymbolKind>(Message.kind());
425 Result.SubKind = static_cast<clang::index::SymbolSubKind>(Message.subkind());
426 Result.Lang = static_cast<clang::index::SymbolLanguage>(Message.language());
427 Result.Properties =
428 static_cast<clang::index::SymbolPropertySet>(Message.properties());
429 return Result;
430}
431
432SymbolInfo Marshaller::toProtobuf(const clang::index::SymbolInfo &Info) {
433 SymbolInfo Result;
434 Result.set_kind(static_cast<uint32_t>(Info.Kind));
435 Result.set_subkind(static_cast<uint32_t>(Info.SubKind));
436 Result.set_language(static_cast<uint32_t>(Info.Lang));
437 Result.set_properties(static_cast<uint32_t>(Info.Properties));
438 return Result;
439}
440
441llvm::Expected<clangd::SymbolLocation>
442Marshaller::fromProtobuf(const SymbolLocation &Message) {
443 clangd::SymbolLocation Location;
444 auto URIString = relativePathToURI(RelativePath: Message.file_path());
445 if (!URIString)
446 return URIString.takeError();
447 Location.FileURI = Strings.save(*URIString).begin();
448 Location.Start = fromProtobuf(Message.start());
449 Location.End = fromProtobuf(Message.end());
450 return Location;
451}
452
453llvm::Expected<SymbolLocation>
454Marshaller::toProtobuf(const clangd::SymbolLocation &Location) {
455 remote::SymbolLocation Result;
456 auto RelativePath = uriToRelativePath(URI: Location.FileURI);
457 if (!RelativePath)
458 return RelativePath.takeError();
459 *Result.mutable_file_path() = *RelativePath;
460 *Result.mutable_start() = toProtobuf(Position: Location.Start);
461 *Result.mutable_end() = toProtobuf(Position: Location.End);
462 return Result;
463}
464
465llvm::Expected<HeaderWithReferences> Marshaller::toProtobuf(
466 const clangd::Symbol::IncludeHeaderWithReferences &IncludeHeader) {
467 HeaderWithReferences Result;
468 Result.set_references(IncludeHeader.References);
469 Result.set_supported_directives(IncludeHeader.SupportedDirectives);
470 const std::string Header = IncludeHeader.IncludeHeader.str();
471 if (isLiteralInclude(Include: Header)) {
472 Result.set_header(Header);
473 return Result;
474 }
475 auto RelativePath = uriToRelativePath(URI: Header);
476 if (!RelativePath)
477 return RelativePath.takeError();
478 Result.set_header(*RelativePath);
479 return Result;
480}
481
482llvm::Expected<clangd::Symbol::IncludeHeaderWithReferences>
483Marshaller::fromProtobuf(const HeaderWithReferences &Message) {
484 std::string Header = Message.header();
485 if (!isLiteralInclude(Include: Header)) {
486 auto URIString = relativePathToURI(RelativePath: Header);
487 if (!URIString)
488 return URIString.takeError();
489 Header = *URIString;
490 }
491 auto Directives = clangd::Symbol::IncludeDirective::Include;
492 if (Message.has_supported_directives())
493 Directives = static_cast<clangd::Symbol::IncludeDirective>(
494 Message.supported_directives());
495 return clangd::Symbol::IncludeHeaderWithReferences{
496 Strings.save(S: Header), Message.references(), Directives};
497}
498
499} // namespace remote
500} // namespace clangd
501} // namespace clang
502

source code of clang-tools-extra/clangd/index/remote/marshalling/Marshalling.cpp