| //===--- Marshalling.cpp -----------------------------------------*- C++-*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Marshalling.h" |
| #include "Headers.h" |
| #include "Index.pb.h" |
| #include "Protocol.h" |
| #include "index/Index.h" |
| #include "index/Ref.h" |
| #include "index/Serialization.h" |
| #include "index/Symbol.h" |
| #include "index/SymbolID.h" |
| #include "index/SymbolLocation.h" |
| #include "index/SymbolOrigin.h" |
| #include "support/Logger.h" |
| #include "clang/Index/IndexSymbol.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/StringSaver.h" |
| |
| namespace clang { |
| namespace clangd { |
| namespace remote { |
| |
| using llvm::sys::path::append; |
| using llvm::sys::path::convert_to_slash; |
| using llvm::sys::path::is_absolute; |
| using llvm::sys::path::replace_path_prefix; |
| using llvm::sys::path::Style; |
| |
| namespace { |
| |
| template <typename IDRange> |
| llvm::Expected<llvm::DenseSet<SymbolID>> getIDs(IDRange IDs) { |
| llvm::DenseSet<SymbolID> Result; |
| for (const auto &ID : IDs) { |
| auto SID = SymbolID::fromStr(StringRef(ID)); |
| if (!SID) |
| return SID.takeError(); |
| Result.insert(*SID); |
| } |
| return Result; |
| } |
| |
| } // namespace |
| |
| Marshaller::Marshaller(llvm::StringRef RemoteIndexRoot, |
| llvm::StringRef LocalIndexRoot) |
| : Strings(Arena) { |
| llvm::StringRef PosixSeparator = get_separator(Style::posix); |
| if (!RemoteIndexRoot.empty()) { |
| assert(is_absolute(RemoteIndexRoot)); |
| this->RemoteIndexRoot = convert_to_slash(RemoteIndexRoot, Style::windows); |
| llvm::StringRef Path(this->RemoteIndexRoot); |
| if (!is_separator(this->RemoteIndexRoot.back(), Style::posix)) |
| this->RemoteIndexRoot += PosixSeparator; |
| } |
| if (!LocalIndexRoot.empty()) { |
| assert(is_absolute(LocalIndexRoot)); |
| this->LocalIndexRoot = convert_to_slash(LocalIndexRoot, Style::windows); |
| llvm::StringRef Path(this->LocalIndexRoot); |
| if (!is_separator(this->LocalIndexRoot.back(), Style::posix)) |
| this->LocalIndexRoot += PosixSeparator; |
| } |
| assert(!RemoteIndexRoot.empty() || !LocalIndexRoot.empty()); |
| } |
| |
| llvm::Expected<clangd::LookupRequest> |
| Marshaller::fromProtobuf(const LookupRequest *Message) { |
| clangd::LookupRequest Req; |
| auto IDs = getIDs(Message->ids()); |
| if (!IDs) |
| return IDs.takeError(); |
| Req.IDs = std::move(*IDs); |
| return Req; |
| } |
| |
| llvm::Expected<clangd::FuzzyFindRequest> |
| Marshaller::fromProtobuf(const FuzzyFindRequest *Message) { |
| assert(!RemoteIndexRoot.empty()); |
| clangd::FuzzyFindRequest Result; |
| Result.Query = Message->query(); |
| for (const auto &Scope : Message->scopes()) |
| Result.Scopes.push_back(Scope); |
| Result.AnyScope = Message->any_scope(); |
| if (Message->limit()) |
| Result.Limit = Message->limit(); |
| Result.RestrictForCodeCompletion = Message->restricted_for_code_completion(); |
| for (const auto &Path : Message->proximity_paths()) { |
| llvm::SmallString<256> LocalPath = llvm::StringRef(RemoteIndexRoot); |
| append(LocalPath, Path); |
| // FuzzyFindRequest requires proximity paths to have platform-native format |
| // in order for SymbolIndex to process the query correctly. |
| llvm::sys::path::native(LocalPath); |
| Result.ProximityPaths.push_back(std::string(LocalPath)); |
| } |
| for (const auto &Type : Message->preferred_types()) |
| Result.ProximityPaths.push_back(Type); |
| return Result; |
| } |
| |
| llvm::Expected<clangd::RefsRequest> |
| Marshaller::fromProtobuf(const RefsRequest *Message) { |
| clangd::RefsRequest Req; |
| auto IDs = getIDs(Message->ids()); |
| if (!IDs) |
| return IDs.takeError(); |
| Req.IDs = std::move(*IDs); |
| if (Message->has_filter()) |
| Req.Filter = static_cast<clangd::RefKind>(Message->filter()); |
| else |
| Req.Filter = clangd::RefKind::All; |
| if (Message->limit()) |
| Req.Limit = Message->limit(); |
| return Req; |
| } |
| |
| llvm::Expected<clangd::RelationsRequest> |
| Marshaller::fromProtobuf(const RelationsRequest *Message) { |
| clangd::RelationsRequest Req; |
| auto IDs = getIDs(Message->subjects()); |
| if (!IDs) |
| return IDs.takeError(); |
| Req.Subjects = std::move(*IDs); |
| if (!Message->has_predicate()) |
| return error("RelationsRequest requires RelationKind predicate."); |
| Req.Predicate = static_cast<RelationKind>(Message->predicate()); |
| if (Message->limit()) |
| Req.Limit = Message->limit(); |
| return Req; |
| } |
| |
| llvm::Expected<clangd::Symbol> Marshaller::fromProtobuf(const Symbol &Message) { |
| if (!Message.has_info() || !Message.has_canonical_declaration()) |
| return error("Missing info or declaration."); |
| clangd::Symbol Result; |
| auto ID = SymbolID::fromStr(Message.id()); |
| if (!ID) |
| return ID.takeError(); |
| Result.ID = *ID; |
| Result.SymInfo = fromProtobuf(Message.info()); |
| Result.Name = Message.name(); |
| Result.Scope = Message.scope(); |
| if (Message.has_definition()) { |
| auto Definition = fromProtobuf(Message.definition()); |
| if (Definition) |
| Result.Definition = *Definition; |
| } |
| auto Declaration = fromProtobuf(Message.canonical_declaration()); |
| if (!Declaration) |
| return Declaration.takeError(); |
| Result.CanonicalDeclaration = *Declaration; |
| Result.References = Message.references(); |
| // Overwrite symbol origin: it's coming from remote index. |
| Result.Origin = clangd::SymbolOrigin::Remote; |
| Result.Signature = Message.signature(); |
| Result.TemplateSpecializationArgs = Message.template_specialization_args(); |
| Result.CompletionSnippetSuffix = Message.completion_snippet_suffix(); |
| Result.Documentation = Message.documentation(); |
| Result.ReturnType = Message.return_type(); |
| Result.Type = Message.type(); |
| for (const auto &Header : Message.headers()) { |
| auto SerializedHeader = fromProtobuf(Header); |
| if (!SerializedHeader) |
| return SerializedHeader.takeError(); |
| Result.IncludeHeaders.push_back(*SerializedHeader); |
| } |
| Result.Flags = static_cast<clangd::Symbol::SymbolFlag>(Message.flags()); |
| return Result; |
| } |
| |
| llvm::Expected<clangd::Ref> Marshaller::fromProtobuf(const Ref &Message) { |
| if (!Message.has_location()) |
| return error("Missing location."); |
| clangd::Ref Result; |
| auto Location = fromProtobuf(Message.location()); |
| if (!Location) |
| return Location.takeError(); |
| Result.Location = *Location; |
| Result.Kind = static_cast<RefKind>(Message.kind()); |
| return Result; |
| } |
| |
| llvm::Expected<std::pair<clangd::SymbolID, clangd::Symbol>> |
| Marshaller::fromProtobuf(const Relation &Message) { |
| auto SubjectID = SymbolID::fromStr(Message.subject_id()); |
| if (!SubjectID) |
| return SubjectID.takeError(); |
| if (!Message.has_object()) |
| return error("Missing Object."); |
| auto Object = fromProtobuf(Message.object()); |
| if (!Object) |
| return Object.takeError(); |
| return std::make_pair(*SubjectID, *Object); |
| } |
| |
| LookupRequest Marshaller::toProtobuf(const clangd::LookupRequest &From) { |
| LookupRequest RPCRequest; |
| for (const auto &SymbolID : From.IDs) |
| RPCRequest.add_ids(SymbolID.str()); |
| return RPCRequest; |
| } |
| |
| FuzzyFindRequest Marshaller::toProtobuf(const clangd::FuzzyFindRequest &From) { |
| assert(!LocalIndexRoot.empty()); |
| FuzzyFindRequest RPCRequest; |
| RPCRequest.set_query(From.Query); |
| for (const auto &Scope : From.Scopes) |
| RPCRequest.add_scopes(Scope); |
| RPCRequest.set_any_scope(From.AnyScope); |
| if (From.Limit) |
| RPCRequest.set_limit(*From.Limit); |
| RPCRequest.set_restricted_for_code_completion(From.RestrictForCodeCompletion); |
| for (const auto &Path : From.ProximityPaths) { |
| llvm::SmallString<256> RelativePath = llvm::StringRef(Path); |
| if (replace_path_prefix(RelativePath, LocalIndexRoot, "")) |
| RPCRequest.add_proximity_paths( |
| convert_to_slash(RelativePath, Style::windows)); |
| } |
| for (const auto &Type : From.PreferredTypes) |
| RPCRequest.add_preferred_types(Type); |
| return RPCRequest; |
| } |
| |
| RefsRequest Marshaller::toProtobuf(const clangd::RefsRequest &From) { |
| RefsRequest RPCRequest; |
| for (const auto &ID : From.IDs) |
| RPCRequest.add_ids(ID.str()); |
| RPCRequest.set_filter(static_cast<uint32_t>(From.Filter)); |
| if (From.Limit) |
| RPCRequest.set_limit(*From.Limit); |
| return RPCRequest; |
| } |
| |
| RelationsRequest Marshaller::toProtobuf(const clangd::RelationsRequest &From) { |
| RelationsRequest RPCRequest; |
| for (const auto &ID : From.Subjects) |
| RPCRequest.add_subjects(ID.str()); |
| RPCRequest.set_predicate(static_cast<uint32_t>(From.Predicate)); |
| if (From.Limit) |
| RPCRequest.set_limit(*From.Limit); |
| return RPCRequest; |
| } |
| |
| llvm::Expected<Symbol> Marshaller::toProtobuf(const clangd::Symbol &From) { |
| Symbol Result; |
| Result.set_id(From.ID.str()); |
| *Result.mutable_info() = toProtobuf(From.SymInfo); |
| Result.set_name(From.Name.str()); |
| if (*From.Definition.FileURI) { |
| auto Definition = toProtobuf(From.Definition); |
| if (!Definition) |
| return Definition.takeError(); |
| *Result.mutable_definition() = *Definition; |
| } |
| Result.set_scope(From.Scope.str()); |
| auto Declaration = toProtobuf(From.CanonicalDeclaration); |
| if (!Declaration) |
| return Declaration.takeError(); |
| *Result.mutable_canonical_declaration() = *Declaration; |
| Result.set_references(From.References); |
| Result.set_origin(static_cast<uint32_t>(From.Origin)); |
| Result.set_signature(From.Signature.str()); |
| Result.set_template_specialization_args( |
| From.TemplateSpecializationArgs.str()); |
| Result.set_completion_snippet_suffix(From.CompletionSnippetSuffix.str()); |
| Result.set_documentation(From.Documentation.str()); |
| Result.set_return_type(From.ReturnType.str()); |
| Result.set_type(From.Type.str()); |
| for (const auto &Header : From.IncludeHeaders) { |
| auto Serialized = toProtobuf(Header); |
| if (!Serialized) |
| return Serialized.takeError(); |
| auto *NextHeader = Result.add_headers(); |
| *NextHeader = *Serialized; |
| } |
| Result.set_flags(static_cast<uint32_t>(From.Flags)); |
| return Result; |
| } |
| |
| llvm::Expected<Ref> Marshaller::toProtobuf(const clangd::Ref &From) { |
| Ref Result; |
| Result.set_kind(static_cast<uint32_t>(From.Kind)); |
| auto Location = toProtobuf(From.Location); |
| if (!Location) |
| return Location.takeError(); |
| *Result.mutable_location() = *Location; |
| return Result; |
| } |
| |
| llvm::Expected<Relation> Marshaller::toProtobuf(const clangd::SymbolID &Subject, |
| const clangd::Symbol &Object) { |
| Relation Result; |
| *Result.mutable_subject_id() = Subject.str(); |
| auto SerializedObject = toProtobuf(Object); |
| if (!SerializedObject) |
| return SerializedObject.takeError(); |
| *Result.mutable_object() = *SerializedObject; |
| return Result; |
| } |
| |
| llvm::Expected<std::string> |
| Marshaller::relativePathToURI(llvm::StringRef RelativePath) { |
| assert(!LocalIndexRoot.empty()); |
| assert(RelativePath == convert_to_slash(RelativePath)); |
| if (RelativePath.empty()) |
| return error("Empty relative path."); |
| if (is_absolute(RelativePath, Style::posix)) |
| return error("RelativePath '{0}' is absolute.", RelativePath); |
| llvm::SmallString<256> FullPath = llvm::StringRef(LocalIndexRoot); |
| append(FullPath, RelativePath); |
| auto Result = URI::createFile(FullPath); |
| return Result.toString(); |
| } |
| |
| llvm::Expected<std::string> Marshaller::uriToRelativePath(llvm::StringRef URI) { |
| assert(!RemoteIndexRoot.empty()); |
| auto ParsedURI = URI::parse(URI); |
| if (!ParsedURI) |
| return ParsedURI.takeError(); |
| if (ParsedURI->scheme() != "file") |
| return error("Can not use URI schemes other than file, given: '{0}'.", URI); |
| llvm::SmallString<256> Result = ParsedURI->body(); |
| llvm::StringRef Path(Result); |
| // Check for Windows paths (URI=file:///X:/path => Body=/X:/path) |
| if (is_absolute(Path.substr(1), Style::windows)) |
| Result = Path.drop_front(); |
| if (!replace_path_prefix(Result, RemoteIndexRoot, "")) |
| return error("File path '{0}' doesn't start with '{1}'.", Result.str(), |
| RemoteIndexRoot); |
| assert(Result == convert_to_slash(Result, Style::windows)); |
| return std::string(Result); |
| } |
| |
| clangd::SymbolLocation::Position |
| Marshaller::fromProtobuf(const Position &Message) { |
| clangd::SymbolLocation::Position Result; |
| Result.setColumn(static_cast<uint32_t>(Message.column())); |
| Result.setLine(static_cast<uint32_t>(Message.line())); |
| return Result; |
| } |
| |
| Position |
| Marshaller::toProtobuf(const clangd::SymbolLocation::Position &Position) { |
| remote::Position Result; |
| Result.set_column(Position.column()); |
| Result.set_line(Position.line()); |
| return Result; |
| } |
| |
| clang::index::SymbolInfo Marshaller::fromProtobuf(const SymbolInfo &Message) { |
| clang::index::SymbolInfo Result; |
| Result.Kind = static_cast<clang::index::SymbolKind>(Message.kind()); |
| Result.SubKind = static_cast<clang::index::SymbolSubKind>(Message.subkind()); |
| Result.Lang = static_cast<clang::index::SymbolLanguage>(Message.language()); |
| Result.Properties = |
| static_cast<clang::index::SymbolPropertySet>(Message.properties()); |
| return Result; |
| } |
| |
| SymbolInfo Marshaller::toProtobuf(const clang::index::SymbolInfo &Info) { |
| SymbolInfo Result; |
| Result.set_kind(static_cast<uint32_t>(Info.Kind)); |
| Result.set_subkind(static_cast<uint32_t>(Info.SubKind)); |
| Result.set_language(static_cast<uint32_t>(Info.Lang)); |
| Result.set_properties(static_cast<uint32_t>(Info.Properties)); |
| return Result; |
| } |
| |
| llvm::Expected<clangd::SymbolLocation> |
| Marshaller::fromProtobuf(const SymbolLocation &Message) { |
| clangd::SymbolLocation Location; |
| auto URIString = relativePathToURI(Message.file_path()); |
| if (!URIString) |
| return URIString.takeError(); |
| Location.FileURI = Strings.save(*URIString).begin(); |
| Location.Start = fromProtobuf(Message.start()); |
| Location.End = fromProtobuf(Message.end()); |
| return Location; |
| } |
| |
| llvm::Expected<SymbolLocation> |
| Marshaller::toProtobuf(const clangd::SymbolLocation &Location) { |
| remote::SymbolLocation Result; |
| auto RelativePath = uriToRelativePath(Location.FileURI); |
| if (!RelativePath) |
| return RelativePath.takeError(); |
| *Result.mutable_file_path() = *RelativePath; |
| *Result.mutable_start() = toProtobuf(Location.Start); |
| *Result.mutable_end() = toProtobuf(Location.End); |
| return Result; |
| } |
| |
| llvm::Expected<HeaderWithReferences> Marshaller::toProtobuf( |
| const clangd::Symbol::IncludeHeaderWithReferences &IncludeHeader) { |
| HeaderWithReferences Result; |
| Result.set_references(IncludeHeader.References); |
| const std::string Header = IncludeHeader.IncludeHeader.str(); |
| if (isLiteralInclude(Header)) { |
| Result.set_header(Header); |
| return Result; |
| } |
| auto RelativePath = uriToRelativePath(Header); |
| if (!RelativePath) |
| return RelativePath.takeError(); |
| Result.set_header(*RelativePath); |
| return Result; |
| } |
| |
| llvm::Expected<clangd::Symbol::IncludeHeaderWithReferences> |
| Marshaller::fromProtobuf(const HeaderWithReferences &Message) { |
| std::string Header = Message.header(); |
| if (!isLiteralInclude(Header)) { |
| auto URIString = relativePathToURI(Header); |
| if (!URIString) |
| return URIString.takeError(); |
| Header = *URIString; |
| } |
| return clangd::Symbol::IncludeHeaderWithReferences{Strings.save(Header), |
| Message.references()}; |
| } |
| |
| } // namespace remote |
| } // namespace clangd |
| } // namespace clang |