1 //===-- YAMLSerialization.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 // A YAML index file is a sequence of tagged entries. 10 // Each entry either encodes a Symbol or the list of references to a symbol 11 // (a "ref bundle"). 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "Index.h" 16 #include "Serialization.h" 17 #include "SymbolLocation.h" 18 #include "SymbolOrigin.h" 19 #include "Trace.h" 20 #include "dex/Dex.h" 21 #include "llvm/ADT/Optional.h" 22 #include "llvm/ADT/SmallVector.h" 23 #include "llvm/ADT/StringRef.h" 24 #include "llvm/Support/Allocator.h" 25 #include "llvm/Support/Errc.h" 26 #include "llvm/Support/MemoryBuffer.h" 27 #include "llvm/Support/StringSaver.h" 28 #include "llvm/Support/YAMLTraits.h" 29 #include "llvm/Support/raw_ostream.h" 30 #include <cstdint> 31 32 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) 33 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref) 34 35 namespace { 36 using RefBundle = 37 std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>; 38 // This is a pale imitation of std::variant<Symbol, RefBundle> 39 struct VariantEntry { 40 llvm::Optional<clang::clangd::Symbol> Symbol; 41 llvm::Optional<RefBundle> Refs; 42 }; 43 // A class helps YAML to serialize the 32-bit encoded position (Line&Column), 44 // as YAMLIO can't directly map bitfields. 45 struct YPosition { 46 uint32_t Line; 47 uint32_t Column; 48 }; 49 50 } // namespace 51 namespace llvm { 52 namespace yaml { 53 54 using clang::clangd::Ref; 55 using clang::clangd::RefKind; 56 using clang::clangd::Symbol; 57 using clang::clangd::SymbolID; 58 using clang::clangd::SymbolLocation; 59 using clang::clangd::SymbolOrigin; 60 using clang::index::SymbolInfo; 61 using clang::index::SymbolKind; 62 using clang::index::SymbolLanguage; 63 64 // Helper to (de)serialize the SymbolID. We serialize it as a hex string. 65 struct NormalizedSymbolID { 66 NormalizedSymbolID(IO &) {} 67 NormalizedSymbolID(IO &, const SymbolID &ID) { 68 llvm::raw_string_ostream OS(HexString); 69 OS << ID; 70 } 71 72 SymbolID denormalize(IO &I) { 73 auto ID = SymbolID::fromStr(HexString); 74 if (!ID) { 75 I.setError(llvm::toString(ID.takeError())); 76 return SymbolID(); 77 } 78 return *ID; 79 } 80 81 std::string HexString; 82 }; 83 84 struct NormalizedSymbolFlag { 85 NormalizedSymbolFlag(IO &) {} 86 NormalizedSymbolFlag(IO &, Symbol::SymbolFlag F) { 87 Flag = static_cast<uint8_t>(F); 88 } 89 90 Symbol::SymbolFlag denormalize(IO &) { 91 return static_cast<Symbol::SymbolFlag>(Flag); 92 } 93 94 uint8_t Flag = 0; 95 }; 96 97 struct NormalizedSymbolOrigin { 98 NormalizedSymbolOrigin(IO &) {} 99 NormalizedSymbolOrigin(IO &, SymbolOrigin O) { 100 Origin = static_cast<uint8_t>(O); 101 } 102 103 SymbolOrigin denormalize(IO &) { return static_cast<SymbolOrigin>(Origin); } 104 105 uint8_t Origin = 0; 106 }; 107 108 template <> struct MappingTraits<YPosition> { 109 static void mapping(IO &IO, YPosition &Value) { 110 IO.mapRequired("Line", Value.Line); 111 IO.mapRequired("Column", Value.Column); 112 } 113 }; 114 115 struct NormalizedPosition { 116 using Position = clang::clangd::SymbolLocation::Position; 117 NormalizedPosition(IO &) {} 118 NormalizedPosition(IO &, const Position &Pos) { 119 P.Line = Pos.line(); 120 P.Column = Pos.column(); 121 } 122 123 Position denormalize(IO &) { 124 Position Pos; 125 Pos.setLine(P.Line); 126 Pos.setColumn(P.Column); 127 return Pos; 128 } 129 YPosition P; 130 }; 131 132 struct NormalizedFileURI { 133 NormalizedFileURI(IO &) {} 134 NormalizedFileURI(IO &, const char *FileURI) { URI = FileURI; } 135 136 const char *denormalize(IO &IO) { 137 assert(IO.getContext() && 138 "Expecting an UniqueStringSaver to allocate data"); 139 return static_cast<llvm::UniqueStringSaver *>(IO.getContext()) 140 ->save(URI) 141 .data(); 142 } 143 144 std::string URI; 145 }; 146 147 template <> struct MappingTraits<SymbolLocation> { 148 static void mapping(IO &IO, SymbolLocation &Value) { 149 MappingNormalization<NormalizedFileURI, const char *> NFile(IO, 150 Value.FileURI); 151 IO.mapRequired("FileURI", NFile->URI); 152 MappingNormalization<NormalizedPosition, SymbolLocation::Position> NStart( 153 IO, Value.Start); 154 IO.mapRequired("Start", NStart->P); 155 MappingNormalization<NormalizedPosition, SymbolLocation::Position> NEnd( 156 IO, Value.End); 157 IO.mapRequired("End", NEnd->P); 158 } 159 }; 160 161 template <> struct MappingTraits<SymbolInfo> { 162 static void mapping(IO &io, SymbolInfo &SymInfo) { 163 // FIXME: expose other fields? 164 io.mapRequired("Kind", SymInfo.Kind); 165 io.mapRequired("Lang", SymInfo.Lang); 166 } 167 }; 168 169 template <> 170 struct MappingTraits<clang::clangd::Symbol::IncludeHeaderWithReferences> { 171 static void mapping(IO &io, 172 clang::clangd::Symbol::IncludeHeaderWithReferences &Inc) { 173 io.mapRequired("Header", Inc.IncludeHeader); 174 io.mapRequired("References", Inc.References); 175 } 176 }; 177 178 template <> struct MappingTraits<Symbol> { 179 static void mapping(IO &IO, Symbol &Sym) { 180 MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, Sym.ID); 181 MappingNormalization<NormalizedSymbolFlag, Symbol::SymbolFlag> NSymbolFlag( 182 IO, Sym.Flags); 183 MappingNormalization<NormalizedSymbolOrigin, SymbolOrigin> NSymbolOrigin( 184 IO, Sym.Origin); 185 IO.mapRequired("ID", NSymbolID->HexString); 186 IO.mapRequired("Name", Sym.Name); 187 IO.mapRequired("Scope", Sym.Scope); 188 IO.mapRequired("SymInfo", Sym.SymInfo); 189 IO.mapOptional("CanonicalDeclaration", Sym.CanonicalDeclaration, 190 SymbolLocation()); 191 IO.mapOptional("Definition", Sym.Definition, SymbolLocation()); 192 IO.mapOptional("References", Sym.References, 0u); 193 IO.mapOptional("Origin", NSymbolOrigin->Origin); 194 IO.mapOptional("Flags", NSymbolFlag->Flag); 195 IO.mapOptional("Signature", Sym.Signature); 196 IO.mapOptional("TemplateSpecializationArgs", 197 Sym.TemplateSpecializationArgs); 198 IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); 199 IO.mapOptional("Documentation", Sym.Documentation); 200 IO.mapOptional("ReturnType", Sym.ReturnType); 201 IO.mapOptional("Type", Sym.Type); 202 IO.mapOptional("IncludeHeaders", Sym.IncludeHeaders); 203 } 204 }; 205 206 template <> struct ScalarEnumerationTraits<SymbolLanguage> { 207 static void enumeration(IO &IO, SymbolLanguage &Value) { 208 IO.enumCase(Value, "C", SymbolLanguage::C); 209 IO.enumCase(Value, "Cpp", SymbolLanguage::CXX); 210 IO.enumCase(Value, "ObjC", SymbolLanguage::ObjC); 211 IO.enumCase(Value, "Swift", SymbolLanguage::Swift); 212 } 213 }; 214 215 template <> struct ScalarEnumerationTraits<SymbolKind> { 216 static void enumeration(IO &IO, SymbolKind &Value) { 217 #define DEFINE_ENUM(name) IO.enumCase(Value, #name, SymbolKind::name) 218 219 DEFINE_ENUM(Unknown); 220 DEFINE_ENUM(Function); 221 DEFINE_ENUM(Module); 222 DEFINE_ENUM(Namespace); 223 DEFINE_ENUM(NamespaceAlias); 224 DEFINE_ENUM(Macro); 225 DEFINE_ENUM(Enum); 226 DEFINE_ENUM(Struct); 227 DEFINE_ENUM(Class); 228 DEFINE_ENUM(Protocol); 229 DEFINE_ENUM(Extension); 230 DEFINE_ENUM(Union); 231 DEFINE_ENUM(TypeAlias); 232 DEFINE_ENUM(Function); 233 DEFINE_ENUM(Variable); 234 DEFINE_ENUM(Field); 235 DEFINE_ENUM(EnumConstant); 236 DEFINE_ENUM(InstanceMethod); 237 DEFINE_ENUM(ClassMethod); 238 DEFINE_ENUM(StaticMethod); 239 DEFINE_ENUM(InstanceProperty); 240 DEFINE_ENUM(ClassProperty); 241 DEFINE_ENUM(StaticProperty); 242 DEFINE_ENUM(Constructor); 243 DEFINE_ENUM(Destructor); 244 DEFINE_ENUM(ConversionFunction); 245 DEFINE_ENUM(Parameter); 246 DEFINE_ENUM(Using); 247 248 #undef DEFINE_ENUM 249 } 250 }; 251 252 template <> struct MappingTraits<RefBundle> { 253 static void mapping(IO &IO, RefBundle &Refs) { 254 MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, 255 Refs.first); 256 IO.mapRequired("ID", NSymbolID->HexString); 257 IO.mapRequired("References", Refs.second); 258 } 259 }; 260 261 struct NormalizedRefKind { 262 NormalizedRefKind(IO &) {} 263 NormalizedRefKind(IO &, RefKind O) { Kind = static_cast<uint8_t>(O); } 264 265 RefKind denormalize(IO &) { return static_cast<RefKind>(Kind); } 266 267 uint8_t Kind = 0; 268 }; 269 270 template <> struct MappingTraits<Ref> { 271 static void mapping(IO &IO, Ref &R) { 272 MappingNormalization<NormalizedRefKind, RefKind> NKind(IO, R.Kind); 273 IO.mapRequired("Kind", NKind->Kind); 274 IO.mapRequired("Location", R.Location); 275 } 276 }; 277 278 template <> struct MappingTraits<VariantEntry> { 279 static void mapping(IO &IO, VariantEntry &Variant) { 280 if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) { 281 if (!IO.outputting()) 282 Variant.Symbol.emplace(); 283 MappingTraits<Symbol>::mapping(IO, *Variant.Symbol); 284 } else if (IO.mapTag("!Refs", Variant.Refs.hasValue())) { 285 if (!IO.outputting()) 286 Variant.Refs.emplace(); 287 MappingTraits<RefBundle>::mapping(IO, *Variant.Refs); 288 } 289 } 290 }; 291 292 } // namespace yaml 293 } // namespace llvm 294 295 namespace clang { 296 namespace clangd { 297 298 void writeYAML(const IndexFileOut &O, llvm::raw_ostream &OS) { 299 llvm::yaml::Output Yout(OS); 300 for (const auto &Sym : *O.Symbols) { 301 VariantEntry Entry; 302 Entry.Symbol = Sym; 303 Yout << Entry; 304 } 305 if (O.Refs) 306 for (auto &Sym : *O.Refs) { 307 VariantEntry Entry; 308 Entry.Refs = Sym; 309 Yout << Entry; 310 } 311 } 312 313 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data) { 314 SymbolSlab::Builder Symbols; 315 RefSlab::Builder Refs; 316 llvm::BumpPtrAllocator 317 Arena; // store the underlying data of Position::FileURI. 318 llvm::UniqueStringSaver Strings(Arena); 319 llvm::yaml::Input Yin(Data, &Strings); 320 while (Yin.setCurrentDocument()) { 321 llvm::yaml::EmptyContext Ctx; 322 VariantEntry Variant; 323 yamlize(Yin, Variant, true, Ctx); 324 if (Yin.error()) 325 return llvm::errorCodeToError(Yin.error()); 326 327 if (Variant.Symbol) 328 Symbols.insert(*Variant.Symbol); 329 if (Variant.Refs) 330 for (const auto &Ref : Variant.Refs->second) 331 Refs.insert(Variant.Refs->first, Ref); 332 Yin.nextDocument(); 333 } 334 335 IndexFileIn Result; 336 Result.Symbols.emplace(std::move(Symbols).build()); 337 Result.Refs.emplace(std::move(Refs).build()); 338 return std::move(Result); 339 } 340 341 std::string toYAML(const Symbol &S) { 342 std::string Buf; 343 { 344 llvm::raw_string_ostream OS(Buf); 345 llvm::yaml::Output Yout(OS); 346 Symbol Sym = S; // copy: Yout<< requires mutability. 347 Yout << Sym; 348 } 349 return Buf; 350 } 351 352 std::string toYAML(const std::pair<SymbolID, llvm::ArrayRef<Ref>> &Data) { 353 RefBundle Refs = {Data.first, Data.second}; 354 std::string Buf; 355 { 356 llvm::raw_string_ostream OS(Buf); 357 llvm::yaml::Output Yout(OS); 358 Yout << Refs; 359 } 360 return Buf; 361 } 362 363 } // namespace clangd 364 } // namespace clang 365