1//===- ExtractAPI/Serialization/SymbolGraphSerializer.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/// \file
10/// This file implements the SymbolGraphSerializer.
11///
12//===----------------------------------------------------------------------===//
13
14#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
15#include "clang/Basic/SourceLocation.h"
16#include "clang/Basic/Version.h"
17#include "clang/ExtractAPI/API.h"
18#include "clang/ExtractAPI/DeclarationFragments.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/STLFunctionalExtras.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/Support/Casting.h"
23#include "llvm/Support/Compiler.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/VersionTuple.h"
26#include "llvm/Support/raw_ostream.h"
27#include <iterator>
28#include <optional>
29#include <type_traits>
30
31using namespace clang;
32using namespace clang::extractapi;
33using namespace llvm;
34
35namespace {
36
37/// Helper function to inject a JSON object \p Obj into another object \p Paren
38/// at position \p Key.
39void serializeObject(Object &Paren, StringRef Key,
40 std::optional<Object> &&Obj) {
41 if (Obj)
42 Paren[Key] = std::move(*Obj);
43}
44
45/// Helper function to inject a JSON array \p Array into object \p Paren at
46/// position \p Key.
47void serializeArray(Object &Paren, StringRef Key,
48 std::optional<Array> &&Array) {
49 if (Array)
50 Paren[Key] = std::move(*Array);
51}
52
53/// Helper function to inject a JSON array composed of the values in \p C into
54/// object \p Paren at position \p Key.
55template <typename ContainerTy>
56void serializeArray(Object &Paren, StringRef Key, ContainerTy &&C) {
57 Paren[Key] = Array(C);
58}
59
60/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
61/// format.
62///
63/// A semantic version object contains three numeric fields, representing the
64/// \c major, \c minor, and \c patch parts of the version tuple.
65/// For example version tuple 1.0.3 is serialized as:
66/// \code
67/// {
68/// "major" : 1,
69/// "minor" : 0,
70/// "patch" : 3
71/// }
72/// \endcode
73///
74/// \returns \c std::nullopt if the version \p V is empty, or an \c Object
75/// containing the semantic version representation of \p V.
76std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
77 if (V.empty())
78 return std::nullopt;
79
80 Object Version;
81 Version["major"] = V.getMajor();
82 Version["minor"] = V.getMinor().value_or(u: 0);
83 Version["patch"] = V.getSubminor().value_or(u: 0);
84 return Version;
85}
86
87/// Serialize the OS information in the Symbol Graph platform property.
88///
89/// The OS information in Symbol Graph contains the \c name of the OS, and an
90/// optional \c minimumVersion semantic version field.
91Object serializeOperatingSystem(const Triple &T) {
92 Object OS;
93 OS["name"] = T.getOSTypeName(Kind: T.getOS());
94 serializeObject(Paren&: OS, Key: "minimumVersion",
95 Obj: serializeSemanticVersion(V: T.getMinimumSupportedOSVersion()));
96 return OS;
97}
98
99/// Serialize the platform information in the Symbol Graph module section.
100///
101/// The platform object describes a target platform triple in corresponding
102/// three fields: \c architecture, \c vendor, and \c operatingSystem.
103Object serializePlatform(const Triple &T) {
104 Object Platform;
105 Platform["architecture"] = T.getArchName();
106 Platform["vendor"] = T.getVendorName();
107
108 if (!T.getEnvironmentName().empty())
109 Platform["environment"] = T.getEnvironmentName();
110
111 Platform["operatingSystem"] = serializeOperatingSystem(T);
112 return Platform;
113}
114
115/// Serialize a source position.
116Object serializeSourcePosition(const PresumedLoc &Loc) {
117 assert(Loc.isValid() && "invalid source position");
118
119 Object SourcePosition;
120 SourcePosition["line"] = Loc.getLine() - 1;
121 SourcePosition["character"] = Loc.getColumn() - 1;
122
123 return SourcePosition;
124}
125
126/// Serialize a source location in file.
127///
128/// \param Loc The presumed location to serialize.
129/// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
130/// Defaults to false.
131Object serializeSourceLocation(const PresumedLoc &Loc,
132 bool IncludeFileURI = false) {
133 Object SourceLocation;
134 serializeObject(Paren&: SourceLocation, Key: "position", Obj: serializeSourcePosition(Loc));
135
136 if (IncludeFileURI) {
137 std::string FileURI = "file://";
138 // Normalize file path to use forward slashes for the URI.
139 FileURI += sys::path::convert_to_slash(path: Loc.getFilename());
140 SourceLocation["uri"] = FileURI;
141 }
142
143 return SourceLocation;
144}
145
146/// Serialize a source range with begin and end locations.
147Object serializeSourceRange(const PresumedLoc &BeginLoc,
148 const PresumedLoc &EndLoc) {
149 Object SourceRange;
150 serializeObject(Paren&: SourceRange, Key: "start", Obj: serializeSourcePosition(Loc: BeginLoc));
151 serializeObject(Paren&: SourceRange, Key: "end", Obj: serializeSourcePosition(Loc: EndLoc));
152 return SourceRange;
153}
154
155/// Serialize the availability attributes of a symbol.
156///
157/// Availability information contains the introduced, deprecated, and obsoleted
158/// versions of the symbol as semantic versions, if not default.
159/// Availability information also contains flags to indicate if the symbol is
160/// unconditionally unavailable or deprecated,
161/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)).
162///
163/// \returns \c std::nullopt if the symbol has default availability attributes,
164/// or an \c Array containing an object with the formatted availability
165/// information.
166std::optional<Array> serializeAvailability(const AvailabilityInfo &Avail) {
167 if (Avail.isDefault())
168 return std::nullopt;
169
170 Array AvailabilityArray;
171
172 if (Avail.isUnconditionallyDeprecated()) {
173 Object UnconditionallyDeprecated;
174 UnconditionallyDeprecated["domain"] = "*";
175 UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
176 AvailabilityArray.emplace_back(A: std::move(UnconditionallyDeprecated));
177 }
178
179 if (Avail.Domain.str() != "") {
180 Object Availability;
181 Availability["domain"] = Avail.Domain;
182
183 if (Avail.isUnavailable()) {
184 Availability["isUnconditionallyUnavailable"] = true;
185 } else {
186 serializeObject(Paren&: Availability, Key: "introduced",
187 Obj: serializeSemanticVersion(V: Avail.Introduced));
188 serializeObject(Paren&: Availability, Key: "deprecated",
189 Obj: serializeSemanticVersion(V: Avail.Deprecated));
190 serializeObject(Paren&: Availability, Key: "obsoleted",
191 Obj: serializeSemanticVersion(V: Avail.Obsoleted));
192 }
193
194 AvailabilityArray.emplace_back(A: std::move(Availability));
195 }
196
197 return AvailabilityArray;
198}
199
200/// Get the language name string for interface language references.
201StringRef getLanguageName(Language Lang) {
202 switch (Lang) {
203 case Language::C:
204 return "c";
205 case Language::ObjC:
206 return "objective-c";
207 case Language::CXX:
208 return "c++";
209 case Language::ObjCXX:
210 return "objective-c++";
211
212 // Unsupported language currently
213 case Language::OpenCL:
214 case Language::OpenCLCXX:
215 case Language::CUDA:
216 case Language::HIP:
217 case Language::HLSL:
218
219 // Languages that the frontend cannot parse and compile
220 case Language::Unknown:
221 case Language::Asm:
222 case Language::LLVM_IR:
223 case Language::CIR:
224 llvm_unreachable("Unsupported language kind");
225 }
226
227 llvm_unreachable("Unhandled language kind");
228}
229
230/// Serialize the identifier object as specified by the Symbol Graph format.
231///
232/// The identifier property of a symbol contains the USR for precise and unique
233/// references, and the interface language name.
234Object serializeIdentifier(const APIRecord &Record, Language Lang) {
235 Object Identifier;
236 Identifier["precise"] = Record.USR;
237 Identifier["interfaceLanguage"] = getLanguageName(Lang);
238
239 return Identifier;
240}
241
242/// Serialize the documentation comments attached to a symbol, as specified by
243/// the Symbol Graph format.
244///
245/// The Symbol Graph \c docComment object contains an array of lines. Each line
246/// represents one line of striped documentation comment, with source range
247/// information.
248/// e.g.
249/// \code
250/// /// This is a documentation comment
251/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.
252/// /// with multiple lines.
253/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.
254/// \endcode
255///
256/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
257/// the formatted lines.
258std::optional<Object> serializeDocComment(const DocComment &Comment) {
259 if (Comment.empty())
260 return std::nullopt;
261
262 Object DocComment;
263
264 Array LinesArray;
265 for (const auto &CommentLine : Comment) {
266 Object Line;
267 Line["text"] = CommentLine.Text;
268 serializeObject(Paren&: Line, Key: "range",
269 Obj: serializeSourceRange(BeginLoc: CommentLine.Begin, EndLoc: CommentLine.End));
270 LinesArray.emplace_back(A: std::move(Line));
271 }
272
273 serializeArray(Paren&: DocComment, Key: "lines", C: std::move(LinesArray));
274
275 return DocComment;
276}
277
278/// Serialize the declaration fragments of a symbol.
279///
280/// The Symbol Graph declaration fragments is an array of tagged important
281/// parts of a symbol's declaration. The fragments sequence can be joined to
282/// form spans of declaration text, with attached information useful for
283/// purposes like syntax-highlighting etc. For example:
284/// \code
285/// const int pi; -> "declarationFragments" : [
286/// {
287/// "kind" : "keyword",
288/// "spelling" : "const"
289/// },
290/// {
291/// "kind" : "text",
292/// "spelling" : " "
293/// },
294/// {
295/// "kind" : "typeIdentifier",
296/// "preciseIdentifier" : "c:I",
297/// "spelling" : "int"
298/// },
299/// {
300/// "kind" : "text",
301/// "spelling" : " "
302/// },
303/// {
304/// "kind" : "identifier",
305/// "spelling" : "pi"
306/// }
307/// ]
308/// \endcode
309///
310/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
311/// formatted declaration fragments array.
312std::optional<Array>
313serializeDeclarationFragments(const DeclarationFragments &DF) {
314 if (DF.getFragments().empty())
315 return std::nullopt;
316
317 Array Fragments;
318 for (const auto &F : DF.getFragments()) {
319 Object Fragment;
320 Fragment["spelling"] = F.Spelling;
321 Fragment["kind"] = DeclarationFragments::getFragmentKindString(Kind: F.Kind);
322 if (!F.PreciseIdentifier.empty())
323 Fragment["preciseIdentifier"] = F.PreciseIdentifier;
324 Fragments.emplace_back(A: std::move(Fragment));
325 }
326
327 return Fragments;
328}
329
330/// Serialize the \c names field of a symbol as specified by the Symbol Graph
331/// format.
332///
333/// The Symbol Graph names field contains multiple representations of a symbol
334/// that can be used for different applications:
335/// - \c title : The simple declared name of the symbol;
336/// - \c subHeading : An array of declaration fragments that provides tags,
337/// and potentially more tokens (for example the \c +/- symbol for
338/// Objective-C methods). Can be used as sub-headings for documentation.
339Object serializeNames(const APIRecord *Record) {
340 Object Names;
341 Names["title"] = Record->Name;
342
343 serializeArray(Paren&: Names, Key: "subHeading",
344 Array: serializeDeclarationFragments(DF: Record->SubHeading));
345 DeclarationFragments NavigatorFragments;
346 NavigatorFragments.append(Spelling: Record->Name,
347 Kind: DeclarationFragments::FragmentKind::Identifier,
348 /*PreciseIdentifier*/ "");
349 serializeArray(Paren&: Names, Key: "navigator",
350 Array: serializeDeclarationFragments(DF: NavigatorFragments));
351
352 return Names;
353}
354
355Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
356 auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
357 return (getLanguageName(Lang) + "." + S).str();
358 };
359
360 Object Kind;
361 switch (RK) {
362 case APIRecord::RK_Unknown:
363 Kind["identifier"] = AddLangPrefix("unknown");
364 Kind["displayName"] = "Unknown";
365 break;
366 case APIRecord::RK_Namespace:
367 Kind["identifier"] = AddLangPrefix("namespace");
368 Kind["displayName"] = "Namespace";
369 break;
370 case APIRecord::RK_GlobalFunction:
371 Kind["identifier"] = AddLangPrefix("func");
372 Kind["displayName"] = "Function";
373 break;
374 case APIRecord::RK_GlobalFunctionTemplate:
375 Kind["identifier"] = AddLangPrefix("func");
376 Kind["displayName"] = "Function Template";
377 break;
378 case APIRecord::RK_GlobalFunctionTemplateSpecialization:
379 Kind["identifier"] = AddLangPrefix("func");
380 Kind["displayName"] = "Function Template Specialization";
381 break;
382 case APIRecord::RK_GlobalVariableTemplate:
383 Kind["identifier"] = AddLangPrefix("var");
384 Kind["displayName"] = "Global Variable Template";
385 break;
386 case APIRecord::RK_GlobalVariableTemplateSpecialization:
387 Kind["identifier"] = AddLangPrefix("var");
388 Kind["displayName"] = "Global Variable Template Specialization";
389 break;
390 case APIRecord::RK_GlobalVariableTemplatePartialSpecialization:
391 Kind["identifier"] = AddLangPrefix("var");
392 Kind["displayName"] = "Global Variable Template Partial Specialization";
393 break;
394 case APIRecord::RK_GlobalVariable:
395 Kind["identifier"] = AddLangPrefix("var");
396 Kind["displayName"] = "Global Variable";
397 break;
398 case APIRecord::RK_EnumConstant:
399 Kind["identifier"] = AddLangPrefix("enum.case");
400 Kind["displayName"] = "Enumeration Case";
401 break;
402 case APIRecord::RK_Enum:
403 Kind["identifier"] = AddLangPrefix("enum");
404 Kind["displayName"] = "Enumeration";
405 break;
406 case APIRecord::RK_StructField:
407 Kind["identifier"] = AddLangPrefix("property");
408 Kind["displayName"] = "Instance Property";
409 break;
410 case APIRecord::RK_Struct:
411 Kind["identifier"] = AddLangPrefix("struct");
412 Kind["displayName"] = "Structure";
413 break;
414 case APIRecord::RK_UnionField:
415 Kind["identifier"] = AddLangPrefix("property");
416 Kind["displayName"] = "Instance Property";
417 break;
418 case APIRecord::RK_Union:
419 Kind["identifier"] = AddLangPrefix("union");
420 Kind["displayName"] = "Union";
421 break;
422 case APIRecord::RK_CXXField:
423 Kind["identifier"] = AddLangPrefix("property");
424 Kind["displayName"] = "Instance Property";
425 break;
426 case APIRecord::RK_StaticField:
427 Kind["identifier"] = AddLangPrefix("type.property");
428 Kind["displayName"] = "Type Property";
429 break;
430 case APIRecord::RK_ClassTemplate:
431 case APIRecord::RK_ClassTemplateSpecialization:
432 case APIRecord::RK_ClassTemplatePartialSpecialization:
433 case APIRecord::RK_CXXClass:
434 Kind["identifier"] = AddLangPrefix("class");
435 Kind["displayName"] = "Class";
436 break;
437 case APIRecord::RK_CXXMethodTemplate:
438 Kind["identifier"] = AddLangPrefix("method");
439 Kind["displayName"] = "Method Template";
440 break;
441 case APIRecord::RK_CXXMethodTemplateSpecialization:
442 Kind["identifier"] = AddLangPrefix("method");
443 Kind["displayName"] = "Method Template Specialization";
444 break;
445 case APIRecord::RK_CXXFieldTemplate:
446 Kind["identifier"] = AddLangPrefix("property");
447 Kind["displayName"] = "Template Property";
448 break;
449 case APIRecord::RK_Concept:
450 Kind["identifier"] = AddLangPrefix("concept");
451 Kind["displayName"] = "Concept";
452 break;
453 case APIRecord::RK_CXXStaticMethod:
454 Kind["identifier"] = AddLangPrefix("type.method");
455 Kind["displayName"] = "Static Method";
456 break;
457 case APIRecord::RK_CXXInstanceMethod:
458 Kind["identifier"] = AddLangPrefix("method");
459 Kind["displayName"] = "Instance Method";
460 break;
461 case APIRecord::RK_CXXConstructorMethod:
462 Kind["identifier"] = AddLangPrefix("method");
463 Kind["displayName"] = "Constructor";
464 break;
465 case APIRecord::RK_CXXDestructorMethod:
466 Kind["identifier"] = AddLangPrefix("method");
467 Kind["displayName"] = "Destructor";
468 break;
469 case APIRecord::RK_ObjCIvar:
470 Kind["identifier"] = AddLangPrefix("ivar");
471 Kind["displayName"] = "Instance Variable";
472 break;
473 case APIRecord::RK_ObjCInstanceMethod:
474 Kind["identifier"] = AddLangPrefix("method");
475 Kind["displayName"] = "Instance Method";
476 break;
477 case APIRecord::RK_ObjCClassMethod:
478 Kind["identifier"] = AddLangPrefix("type.method");
479 Kind["displayName"] = "Type Method";
480 break;
481 case APIRecord::RK_ObjCInstanceProperty:
482 Kind["identifier"] = AddLangPrefix("property");
483 Kind["displayName"] = "Instance Property";
484 break;
485 case APIRecord::RK_ObjCClassProperty:
486 Kind["identifier"] = AddLangPrefix("type.property");
487 Kind["displayName"] = "Type Property";
488 break;
489 case APIRecord::RK_ObjCInterface:
490 Kind["identifier"] = AddLangPrefix("class");
491 Kind["displayName"] = "Class";
492 break;
493 case APIRecord::RK_ObjCCategory:
494 Kind["identifier"] = AddLangPrefix("class.extension");
495 Kind["displayName"] = "Class Extension";
496 break;
497 case APIRecord::RK_ObjCProtocol:
498 Kind["identifier"] = AddLangPrefix("protocol");
499 Kind["displayName"] = "Protocol";
500 break;
501 case APIRecord::RK_MacroDefinition:
502 Kind["identifier"] = AddLangPrefix("macro");
503 Kind["displayName"] = "Macro";
504 break;
505 case APIRecord::RK_Typedef:
506 Kind["identifier"] = AddLangPrefix("typealias");
507 Kind["displayName"] = "Type Alias";
508 break;
509 default:
510 llvm_unreachable("API Record with uninstantiable kind");
511 }
512
513 return Kind;
514}
515
516/// Serialize the symbol kind information.
517///
518/// The Symbol Graph symbol kind property contains a shorthand \c identifier
519/// which is prefixed by the source language name, useful for tooling to parse
520/// the kind, and a \c displayName for rendering human-readable names.
521Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
522 return serializeSymbolKind(RK: Record.KindForDisplay, Lang);
523}
524
525/// Serialize the function signature field, as specified by the
526/// Symbol Graph format.
527///
528/// The Symbol Graph function signature property contains two arrays.
529/// - The \c returns array is the declaration fragments of the return type;
530/// - The \c parameters array contains names and declaration fragments of the
531/// parameters.
532template <typename RecordTy>
533void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
534 const auto &FS = Record.Signature;
535 if (FS.empty())
536 return;
537
538 Object Signature;
539 serializeArray(Signature, "returns",
540 serializeDeclarationFragments(FS.getReturnType()));
541
542 Array Parameters;
543 for (const auto &P : FS.getParameters()) {
544 Object Parameter;
545 Parameter["name"] = P.Name;
546 serializeArray(Parameter, "declarationFragments",
547 serializeDeclarationFragments(P.Fragments));
548 Parameters.emplace_back(A: std::move(Parameter));
549 }
550
551 if (!Parameters.empty())
552 Signature["parameters"] = std::move(Parameters);
553
554 serializeObject(Paren, Key: "functionSignature", Obj: std::move(Signature));
555}
556
557template <typename RecordTy>
558void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
559 const auto &Template = Record.Templ;
560 if (Template.empty())
561 return;
562
563 Object Generics;
564 Array GenericParameters;
565 for (const auto &Param : Template.getParameters()) {
566 Object Parameter;
567 Parameter["name"] = Param.Name;
568 Parameter["index"] = Param.Index;
569 Parameter["depth"] = Param.Depth;
570 GenericParameters.emplace_back(A: std::move(Parameter));
571 }
572 if (!GenericParameters.empty())
573 Generics["parameters"] = std::move(GenericParameters);
574
575 Array GenericConstraints;
576 for (const auto &Constr : Template.getConstraints()) {
577 Object Constraint;
578 Constraint["kind"] = Constr.Kind;
579 Constraint["lhs"] = Constr.LHS;
580 Constraint["rhs"] = Constr.RHS;
581 GenericConstraints.emplace_back(A: std::move(Constraint));
582 }
583
584 if (!GenericConstraints.empty())
585 Generics["constraints"] = std::move(GenericConstraints);
586
587 serializeObject(Paren, Key: "swiftGenerics", Obj: Generics);
588}
589
590Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents,
591 Language Lang) {
592 Array ParentContexts;
593
594 for (const auto &Parent : Parents) {
595 Object Elem;
596 Elem["usr"] = Parent.USR;
597 Elem["name"] = Parent.Name;
598 if (Parent.Record)
599 Elem["kind"] = serializeSymbolKind(RK: Parent.Record->KindForDisplay,
600 Lang)["identifier"];
601 else
602 Elem["kind"] =
603 serializeSymbolKind(RK: APIRecord::RK_Unknown, Lang)["identifier"];
604 ParentContexts.emplace_back(A: std::move(Elem));
605 }
606
607 return ParentContexts;
608}
609
610/// Walk the records parent information in reverse to generate a hierarchy
611/// suitable for serialization.
612SmallVector<SymbolReference, 8>
613generateHierarchyFromRecord(const APIRecord *Record) {
614 SmallVector<SymbolReference, 8> ReverseHierarchy;
615 for (const auto *Current = Record; Current != nullptr;
616 Current = Current->Parent.Record)
617 ReverseHierarchy.emplace_back(Args&: Current);
618
619 return SmallVector<SymbolReference, 8>(
620 std::make_move_iterator(i: ReverseHierarchy.rbegin()),
621 std::make_move_iterator(i: ReverseHierarchy.rend()));
622}
623
624SymbolReference getHierarchyReference(const APIRecord *Record,
625 const APISet &API) {
626 // If the parent is a category extended from internal module then we need to
627 // pretend this belongs to the associated interface.
628 if (auto *CategoryRecord = dyn_cast_or_null<ObjCCategoryRecord>(Val: Record)) {
629 return CategoryRecord->Interface;
630 // FIXME: TODO generate path components correctly for categories extending
631 // an external module.
632 }
633
634 return SymbolReference(Record);
635}
636
637} // namespace
638
639Object *ExtendedModule::addSymbol(Object &&Symbol) {
640 Symbols.emplace_back(A: std::move(Symbol));
641 return Symbols.back().getAsObject();
642}
643
644void ExtendedModule::addRelationship(Object &&Relationship) {
645 Relationships.emplace_back(A: std::move(Relationship));
646}
647
648/// Defines the format version emitted by SymbolGraphSerializer.
649const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
650
651Object SymbolGraphSerializer::serializeMetadata() const {
652 Object Metadata;
653 serializeObject(Paren&: Metadata, Key: "formatVersion",
654 Obj: serializeSemanticVersion(V: FormatVersion));
655 Metadata["generator"] = clang::getClangFullVersion();
656 return Metadata;
657}
658
659Object
660SymbolGraphSerializer::serializeModuleObject(StringRef ModuleName) const {
661 Object Module;
662 Module["name"] = ModuleName;
663 serializeObject(Paren&: Module, Key: "platform", Obj: serializePlatform(T: API.getTarget()));
664 return Module;
665}
666
667bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
668 if (!Record)
669 return true;
670
671 // Skip unconditionally unavailable symbols
672 if (Record->Availability.isUnconditionallyUnavailable())
673 return true;
674
675 // Filter out symbols prefixed with an underscored as they are understood to
676 // be symbols clients should not use.
677 if (Record->Name.starts_with(Prefix: "_"))
678 return true;
679
680 // Skip explicitly ignored symbols.
681 if (IgnoresList.shouldIgnore(SymbolName: Record->Name))
682 return true;
683
684 return false;
685}
686
687ExtendedModule &SymbolGraphSerializer::getModuleForCurrentSymbol() {
688 if (!ForceEmitToMainModule && ModuleForCurrentSymbol)
689 return *ModuleForCurrentSymbol;
690
691 return MainModule;
692}
693
694Array SymbolGraphSerializer::serializePathComponents(
695 const APIRecord *Record) const {
696 return Array(map_range(C: Hierarchy, F: [](auto Elt) { return Elt.Name; }));
697}
698
699StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
700 switch (Kind) {
701 case RelationshipKind::MemberOf:
702 return "memberOf";
703 case RelationshipKind::InheritsFrom:
704 return "inheritsFrom";
705 case RelationshipKind::ConformsTo:
706 return "conformsTo";
707 case RelationshipKind::ExtensionTo:
708 return "extensionTo";
709 }
710 llvm_unreachable("Unhandled relationship kind");
711}
712
713void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
714 const SymbolReference &Source,
715 const SymbolReference &Target,
716 ExtendedModule &Into) {
717 Object Relationship;
718 SmallString<64> TestRelLabel;
719 if (EmitSymbolLabelsForTesting) {
720 llvm::raw_svector_ostream OS(TestRelLabel);
721 OS << SymbolGraphSerializer::getRelationshipString(Kind) << " $ "
722 << Source.USR << " $ ";
723 if (Target.USR.empty())
724 OS << Target.Name;
725 else
726 OS << Target.USR;
727 Relationship["!testRelLabel"] = TestRelLabel;
728 }
729 Relationship["source"] = Source.USR;
730 Relationship["target"] = Target.USR;
731 Relationship["targetFallback"] = Target.Name;
732 Relationship["kind"] = SymbolGraphSerializer::getRelationshipString(Kind);
733
734 if (ForceEmitToMainModule)
735 MainModule.addRelationship(Relationship: std::move(Relationship));
736 else
737 Into.addRelationship(Relationship: std::move(Relationship));
738}
739
740StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {
741 switch (Kind) {
742 case ConstraintKind::Conformance:
743 return "conformance";
744 case ConstraintKind::ConditionalConformance:
745 return "conditionalConformance";
746 }
747 llvm_unreachable("Unhandled constraint kind");
748}
749
750void SymbolGraphSerializer::serializeAPIRecord(const APIRecord *Record) {
751 Object Obj;
752
753 // If we need symbol labels for testing emit the USR as the value and the key
754 // starts with '!'' to ensure it ends up at the top of the object.
755 if (EmitSymbolLabelsForTesting)
756 Obj["!testLabel"] = Record->USR;
757
758 serializeObject(Paren&: Obj, Key: "identifier",
759 Obj: serializeIdentifier(Record: *Record, Lang: API.getLanguage()));
760 serializeObject(Paren&: Obj, Key: "kind", Obj: serializeSymbolKind(Record: *Record, Lang: API.getLanguage()));
761 serializeObject(Paren&: Obj, Key: "names", Obj: serializeNames(Record));
762 serializeObject(
763 Paren&: Obj, Key: "location",
764 Obj: serializeSourceLocation(Loc: Record->Location, /*IncludeFileURI=*/true));
765 serializeArray(Paren&: Obj, Key: "availability",
766 Array: serializeAvailability(Avail: Record->Availability));
767 serializeObject(Paren&: Obj, Key: "docComment", Obj: serializeDocComment(Comment: Record->Comment));
768 serializeArray(Paren&: Obj, Key: "declarationFragments",
769 Array: serializeDeclarationFragments(DF: Record->Declaration));
770
771 Obj["pathComponents"] = serializePathComponents(Record);
772 Obj["accessLevel"] = Record->Access.getAccess();
773
774 ExtendedModule &Module = getModuleForCurrentSymbol();
775 // If the hierarchy has at least one parent and child.
776 if (Hierarchy.size() >= 2)
777 serializeRelationship(Kind: MemberOf, Source: Hierarchy.back(),
778 Target: Hierarchy[Hierarchy.size() - 2], Into&: Module);
779
780 CurrentSymbol = Module.addSymbol(Symbol: std::move(Obj));
781}
782
783bool SymbolGraphSerializer::traverseAPIRecord(const APIRecord *Record) {
784 if (!Record)
785 return true;
786 if (shouldSkip(Record))
787 return true;
788 Hierarchy.push_back(Elt: getHierarchyReference(Record, API));
789 // Defer traversal mechanics to APISetVisitor base implementation
790 auto RetVal = Base::traverseAPIRecord(Record);
791 Hierarchy.pop_back();
792 return RetVal;
793}
794
795bool SymbolGraphSerializer::visitAPIRecord(const APIRecord *Record) {
796 serializeAPIRecord(Record);
797 return true;
798}
799
800bool SymbolGraphSerializer::visitGlobalFunctionRecord(
801 const GlobalFunctionRecord *Record) {
802 if (!CurrentSymbol)
803 return true;
804
805 serializeFunctionSignatureMixin(Paren&: *CurrentSymbol, Record: *Record);
806 return true;
807}
808
809bool SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord *Record) {
810 if (!CurrentSymbol)
811 return true;
812
813 for (const auto &Base : Record->Bases)
814 serializeRelationship(Kind: RelationshipKind::InheritsFrom, Source: Record, Target: Base,
815 Into&: getModuleForCurrentSymbol());
816 return true;
817}
818
819bool SymbolGraphSerializer::visitClassTemplateRecord(
820 const ClassTemplateRecord *Record) {
821 if (!CurrentSymbol)
822 return true;
823
824 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
825 return true;
826}
827
828bool SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
829 const ClassTemplatePartialSpecializationRecord *Record) {
830 if (!CurrentSymbol)
831 return true;
832
833 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
834 return true;
835}
836
837bool SymbolGraphSerializer::visitCXXMethodRecord(
838 const CXXMethodRecord *Record) {
839 if (!CurrentSymbol)
840 return true;
841
842 serializeFunctionSignatureMixin(Paren&: *CurrentSymbol, Record: *Record);
843 return true;
844}
845
846bool SymbolGraphSerializer::visitCXXMethodTemplateRecord(
847 const CXXMethodTemplateRecord *Record) {
848 if (!CurrentSymbol)
849 return true;
850
851 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
852 return true;
853}
854
855bool SymbolGraphSerializer::visitCXXFieldTemplateRecord(
856 const CXXFieldTemplateRecord *Record) {
857 if (!CurrentSymbol)
858 return true;
859
860 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
861 return true;
862}
863
864bool SymbolGraphSerializer::visitConceptRecord(const ConceptRecord *Record) {
865 if (!CurrentSymbol)
866 return true;
867
868 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
869 return true;
870}
871
872bool SymbolGraphSerializer::visitGlobalVariableTemplateRecord(
873 const GlobalVariableTemplateRecord *Record) {
874 if (!CurrentSymbol)
875 return true;
876
877 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
878 return true;
879}
880
881bool SymbolGraphSerializer::
882 visitGlobalVariableTemplatePartialSpecializationRecord(
883 const GlobalVariableTemplatePartialSpecializationRecord *Record) {
884 if (!CurrentSymbol)
885 return true;
886
887 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
888 return true;
889}
890
891bool SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(
892 const GlobalFunctionTemplateRecord *Record) {
893 if (!CurrentSymbol)
894 return true;
895
896 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
897 return true;
898}
899
900bool SymbolGraphSerializer::visitObjCContainerRecord(
901 const ObjCContainerRecord *Record) {
902 if (!CurrentSymbol)
903 return true;
904
905 for (const auto &Protocol : Record->Protocols)
906 serializeRelationship(Kind: ConformsTo, Source: Record, Target: Protocol,
907 Into&: getModuleForCurrentSymbol());
908
909 return true;
910}
911
912bool SymbolGraphSerializer::visitObjCInterfaceRecord(
913 const ObjCInterfaceRecord *Record) {
914 if (!CurrentSymbol)
915 return true;
916
917 if (!Record->SuperClass.empty())
918 serializeRelationship(Kind: InheritsFrom, Source: Record, Target: Record->SuperClass,
919 Into&: getModuleForCurrentSymbol());
920 return true;
921}
922
923bool SymbolGraphSerializer::traverseObjCCategoryRecord(
924 const ObjCCategoryRecord *Record) {
925 if (SkipSymbolsInCategoriesToExternalTypes &&
926 !API.findRecordForUSR(USR: Record->Interface.USR))
927 return true;
928
929 auto *CurrentModule = ModuleForCurrentSymbol;
930 if (auto ModuleExtendedByRecord = Record->getExtendedExternalModule())
931 ModuleForCurrentSymbol = &ExtendedModules[*ModuleExtendedByRecord];
932
933 if (!walkUpFromObjCCategoryRecord(Record))
934 return false;
935
936 bool RetVal = traverseRecordContext(Context: Record);
937 ModuleForCurrentSymbol = CurrentModule;
938 return RetVal;
939}
940
941bool SymbolGraphSerializer::walkUpFromObjCCategoryRecord(
942 const ObjCCategoryRecord *Record) {
943 return visitObjCCategoryRecord(Record);
944}
945
946bool SymbolGraphSerializer::visitObjCCategoryRecord(
947 const ObjCCategoryRecord *Record) {
948 // If we need to create a record for the category in the future do so here,
949 // otherwise everything is set up to pretend that the category is in fact the
950 // interface it extends.
951 for (const auto &Protocol : Record->Protocols)
952 serializeRelationship(Kind: ConformsTo, Source: Record->Interface, Target: Protocol,
953 Into&: getModuleForCurrentSymbol());
954
955 return true;
956}
957
958bool SymbolGraphSerializer::visitObjCMethodRecord(
959 const ObjCMethodRecord *Record) {
960 if (!CurrentSymbol)
961 return true;
962
963 serializeFunctionSignatureMixin(Paren&: *CurrentSymbol, Record: *Record);
964 return true;
965}
966
967bool SymbolGraphSerializer::visitObjCInstanceVariableRecord(
968 const ObjCInstanceVariableRecord *Record) {
969 // FIXME: serialize ivar access control here.
970 return true;
971}
972
973bool SymbolGraphSerializer::walkUpFromTypedefRecord(
974 const TypedefRecord *Record) {
975 // Short-circuit walking up the class hierarchy and handle creating typedef
976 // symbol objects manually as there are additional symbol dropping rules to
977 // respect.
978 return visitTypedefRecord(Record);
979}
980
981bool SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord *Record) {
982 // Typedefs of anonymous types have their entries unified with the underlying
983 // type.
984 bool ShouldDrop = Record->UnderlyingType.Name.empty();
985 // enums declared with `NS_OPTION` have a named enum and a named typedef, with
986 // the same name
987 ShouldDrop |= (Record->UnderlyingType.Name == Record->Name);
988 if (ShouldDrop)
989 return true;
990
991 // Create the symbol record if the other symbol droppping rules permit it.
992 serializeAPIRecord(Record);
993 if (!CurrentSymbol)
994 return true;
995
996 (*CurrentSymbol)["type"] = Record->UnderlyingType.USR;
997
998 return true;
999}
1000
1001void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
1002 switch (Record->getKind()) {
1003 // dispatch to the relevant walkUpFromMethod
1004#define CONCRETE_RECORD(CLASS, BASE, KIND) \
1005 case APIRecord::KIND: { \
1006 walkUpFrom##CLASS(static_cast<const CLASS *>(Record)); \
1007 break; \
1008 }
1009#include "clang/ExtractAPI/APIRecords.inc"
1010 // otherwise fallback on the only behavior we can implement safely.
1011 case APIRecord::RK_Unknown:
1012 visitAPIRecord(Record);
1013 break;
1014 default:
1015 llvm_unreachable("API Record with uninstantiable kind");
1016 }
1017}
1018
1019Object SymbolGraphSerializer::serializeGraph(StringRef ModuleName,
1020 ExtendedModule &&EM) {
1021 Object Root;
1022 serializeObject(Paren&: Root, Key: "metadata", Obj: serializeMetadata());
1023 serializeObject(Paren&: Root, Key: "module", Obj: serializeModuleObject(ModuleName));
1024
1025 Root["symbols"] = std::move(EM.Symbols);
1026 Root["relationships"] = std::move(EM.Relationships);
1027
1028 return Root;
1029}
1030
1031void SymbolGraphSerializer::serializeGraphToStream(
1032 raw_ostream &OS, SymbolGraphSerializerOption Options, StringRef ModuleName,
1033 ExtendedModule &&EM) {
1034 Object Root = serializeGraph(ModuleName, EM: std::move(EM));
1035 if (Options.Compact)
1036 OS << formatv(Fmt: "{0}", Vals: json::Value(std::move(Root))) << "\n";
1037 else
1038 OS << formatv(Fmt: "{0:2}", Vals: json::Value(std::move(Root))) << "\n";
1039}
1040
1041void SymbolGraphSerializer::serializeMainSymbolGraph(
1042 raw_ostream &OS, const APISet &API, const APIIgnoresList &IgnoresList,
1043 SymbolGraphSerializerOption Options) {
1044 SymbolGraphSerializer Serializer(
1045 API, IgnoresList, Options.EmitSymbolLabelsForTesting,
1046 /*ForceEmitToMainModule=*/true,
1047 /*SkipSymbolsInCategoriesToExternalTypes=*/true);
1048
1049 Serializer.traverseAPISet();
1050 Serializer.serializeGraphToStream(OS, Options, ModuleName: API.ProductName,
1051 EM: std::move(Serializer.MainModule));
1052 // FIXME: TODO handle extended modules here
1053}
1054
1055void SymbolGraphSerializer::serializeWithExtensionGraphs(
1056 raw_ostream &MainOutput, const APISet &API,
1057 const APIIgnoresList &IgnoresList,
1058 llvm::function_ref<std::unique_ptr<llvm::raw_pwrite_stream>(Twine BaseName)>
1059 CreateOutputStream,
1060 SymbolGraphSerializerOption Options) {
1061 SymbolGraphSerializer Serializer(API, IgnoresList,
1062 Options.EmitSymbolLabelsForTesting);
1063 Serializer.traverseAPISet();
1064
1065 Serializer.serializeGraphToStream(OS&: MainOutput, Options, ModuleName: API.ProductName,
1066 EM: std::move(Serializer.MainModule));
1067
1068 for (auto &ExtensionSGF : Serializer.ExtendedModules) {
1069 if (auto ExtensionOS =
1070 CreateOutputStream(API.ProductName + "@" + ExtensionSGF.getKey()))
1071 Serializer.serializeGraphToStream(OS&: *ExtensionOS, Options, ModuleName: API.ProductName,
1072 EM: std::move(ExtensionSGF.getValue()));
1073 }
1074}
1075
1076std::optional<Object>
1077SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
1078 const APISet &API) {
1079 APIRecord *Record = API.findRecordForUSR(USR);
1080 if (!Record)
1081 return {};
1082
1083 Object Root;
1084 APIIgnoresList EmptyIgnores;
1085 SymbolGraphSerializer Serializer(API, EmptyIgnores,
1086 /*EmitSymbolLabelsForTesting*/ false,
1087 /*ForceEmitToMainModule*/ true);
1088
1089 // Set up serializer parent chain
1090 Serializer.Hierarchy = generateHierarchyFromRecord(Record);
1091
1092 Serializer.serializeSingleRecord(Record);
1093 serializeObject(Paren&: Root, Key: "symbolGraph",
1094 Obj: Serializer.serializeGraph(ModuleName: API.ProductName,
1095 EM: std::move(Serializer.MainModule)));
1096
1097 Language Lang = API.getLanguage();
1098 serializeArray(Paren&: Root, Key: "parentContexts",
1099 C: generateParentContexts(Parents: Serializer.Hierarchy, Lang));
1100
1101 Array RelatedSymbols;
1102
1103 for (const auto &Fragment : Record->Declaration.getFragments()) {
1104 // If we don't have a USR there isn't much we can do.
1105 if (Fragment.PreciseIdentifier.empty())
1106 continue;
1107
1108 APIRecord *RelatedRecord = API.findRecordForUSR(USR: Fragment.PreciseIdentifier);
1109
1110 // If we can't find the record let's skip.
1111 if (!RelatedRecord)
1112 continue;
1113
1114 Object RelatedSymbol;
1115 RelatedSymbol["usr"] = RelatedRecord->USR;
1116 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
1117 RelatedSymbol["accessLevel"] = RelatedRecord->Access.getAccess();
1118 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
1119 RelatedSymbol["moduleName"] = API.ProductName;
1120 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
1121
1122 serializeArray(Paren&: RelatedSymbol, Key: "parentContexts",
1123 C: generateParentContexts(
1124 Parents: generateHierarchyFromRecord(Record: RelatedRecord), Lang));
1125
1126 RelatedSymbols.push_back(E: std::move(RelatedSymbol));
1127 }
1128
1129 serializeArray(Paren&: Root, Key: "relatedSymbols", C&: RelatedSymbols);
1130 return Root;
1131}
1132

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp