clang-tools 23.0.0git
Representation.cpp
Go to the documentation of this file.
1///===-- Representation.cpp - ClangDoc Representation -----------*- 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// This file defines the merging of different types of infos. The data in the
10// calling Info is preserved during a merge unless that field is empty or
11// default. In that case, the data from the parameter Info is used to replace
12// the empty or default data.
13//
14// For most fields, the first decl seen provides the data. Exceptions to this
15// include the location and description fields, which are collections of data on
16// all decls related to a given definition. All other fields are ignored in new
17// decls unless the first seen decl didn't, for whatever reason, incorporate
18// data on that field (e.g. a forward declared class wouldn't have information
19// on members on the forward declaration, but would have the class name).
20//
21//===----------------------------------------------------------------------===//
22#include "Representation.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/StringMap.h"
25#include "llvm/Support/Error.h"
26#include "llvm/Support/Path.h"
27
28namespace clang {
29namespace doc {
30
31// Thread local arenas usable in each thread pool
32thread_local llvm::BumpPtrAllocator TransientArena;
33thread_local llvm::BumpPtrAllocator PersistentArena;
34
36 static ConcurrentStringPool GlobalPool;
37 return GlobalPool;
38}
39
40CommentKind stringToCommentKind(llvm::StringRef KindStr) {
41 static const llvm::StringMap<CommentKind> KindMap = {
42 {"FullComment", CommentKind::CK_FullComment},
43 {"ParagraphComment", CommentKind::CK_ParagraphComment},
44 {"TextComment", CommentKind::CK_TextComment},
45 {"InlineCommandComment", CommentKind::CK_InlineCommandComment},
46 {"HTMLStartTagComment", CommentKind::CK_HTMLStartTagComment},
47 {"HTMLEndTagComment", CommentKind::CK_HTMLEndTagComment},
48 {"BlockCommandComment", CommentKind::CK_BlockCommandComment},
49 {"ParamCommandComment", CommentKind::CK_ParamCommandComment},
50 {"TParamCommandComment", CommentKind::CK_TParamCommandComment},
51 {"VerbatimBlockComment", CommentKind::CK_VerbatimBlockComment},
52 {"VerbatimBlockLineComment", CommentKind::CK_VerbatimBlockLineComment},
53 {"VerbatimLineComment", CommentKind::CK_VerbatimLineComment},
54 };
55
56 auto It = KindMap.find(KindStr);
57 if (It != KindMap.end()) {
58 return It->second;
59 }
61}
62
63llvm::StringRef commentKindToString(CommentKind Kind) {
64 switch (Kind) {
66 return "FullComment";
68 return "ParagraphComment";
70 return "TextComment";
72 return "InlineCommandComment";
74 return "HTMLStartTagComment";
76 return "HTMLEndTagComment";
78 return "BlockCommandComment";
80 return "ParamCommandComment";
82 return "TParamCommandComment";
84 return "VerbatimBlockComment";
86 return "VerbatimBlockLineComment";
88 return "VerbatimLineComment";
90 return "Unknown";
91 }
92 llvm_unreachable("Unhandled CommentKind");
93}
94
96
97template <typename T>
98static llvm::Expected<Info *> reduce(SmallVectorImpl<Info *> &Values) {
99 if (Values.empty() || !Values[0])
100 return llvm::createStringError(llvm::inconvertibleErrorCode(),
101 "no value to reduce");
102 T *Merged = allocateTransient<T>(Values[0]->USR);
103 for (auto &I : Values)
104 Merged->merge(std::move(*static_cast<T *>(I)));
105 return Merged;
106}
107
108template <typename T>
109static void reduceChildren(DocList<T> &Children, DocList<T> &&ChildrenToMerge) {
110 while (!ChildrenToMerge.empty()) {
111 T *Ptr = ChildrenToMerge.front().Ptr;
112 ChildrenToMerge.pop_front();
113
114 auto It = llvm::find_if(
115 Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
116
117 if (It == Children.end()) {
118 InfoNode<T> *NewNode = allocateListNodePersistent<T>(Ptr->USR);
119 NewNode->Ptr->merge(std::move(*Ptr));
120 Children.push_back(*NewNode);
121 } else {
122 It->Ptr->merge(std::move(*Ptr));
123 }
124 }
125}
126
127template <>
129 DocList<Reference> &&ChildrenToMerge) {
130 while (!ChildrenToMerge.empty()) {
131 Reference *Ptr = ChildrenToMerge.front().Ptr;
132 ChildrenToMerge.pop_front();
133
134 auto It = llvm::find_if(
135 Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
136 if (It == Children.end()) {
138 NewNode->Ptr->USR = Ptr->USR;
139 NewNode->Ptr->RefType = Ptr->RefType;
140 NewNode->Ptr->merge(std::move(*Ptr));
141 Children.push_back(*NewNode);
142 } else {
143 It->Ptr->merge(std::move(*Ptr));
144 }
145 }
146}
147
148template <typename T>
149static void mergeUnkeyed(DocList<T> &Target, DocList<T> &&Source) {
150 while (!Source.empty()) {
151 T *Ptr = Source.front().Ptr;
152 Source.pop_front();
153
154 if (!llvm::any_of(Target,
155 [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
156 Target.push_back(*allocateListNodePersistent<T>(*Ptr));
157 }
158 }
159}
160
161template <>
163 DocList<CommentInfo> &&Source) {
164 while (!Source.empty()) {
165 CommentInfo *Ptr = Source.front().Ptr;
166 Source.pop_front();
167
168 if (llvm::none_of(Target,
169 [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
170 Target.push_back(
172 }
173 }
174}
175
176llvm::Error mergeSingleInfo(doc::Info *&Reduced, doc::Info *NewInfo,
177 llvm::BumpPtrAllocator &Arena) {
178 if (!Reduced) {
179 switch (NewInfo->IT) {
181 Reduced = allocatePtr<NamespaceInfo>(Arena, NewInfo->USR);
182 break;
184 Reduced = allocatePtr<RecordInfo>(Arena, NewInfo->USR);
185 break;
187 Reduced = allocatePtr<EnumInfo>(Arena, NewInfo->USR);
188 break;
190 Reduced = allocatePtr<FunctionInfo>(Arena, NewInfo->USR);
191 break;
193 Reduced = allocatePtr<TypedefInfo>(Arena, NewInfo->USR);
194 break;
196 Reduced = allocatePtr<ConceptInfo>(Arena, NewInfo->USR);
197 break;
199 Reduced = allocatePtr<VarInfo>(Arena, NewInfo->USR);
200 break;
202 Reduced = allocatePtr<FriendInfo>(Arena, NewInfo->USR);
203 break;
204 default:
205 return llvm::createStringError(llvm::inconvertibleErrorCode(),
206 "unknown info type");
207 }
208 }
209
210 if (Reduced->IT != NewInfo->IT)
211 return llvm::createStringError(llvm::inconvertibleErrorCode(),
212 "info types mismatch");
213
214 switch (Reduced->IT) {
216 static_cast<NamespaceInfo *>(Reduced)->merge(
217 std::move(*static_cast<NamespaceInfo *>(NewInfo)));
218 break;
220 static_cast<RecordInfo *>(Reduced)->merge(
221 std::move(*static_cast<RecordInfo *>(NewInfo)));
222 break;
224 static_cast<EnumInfo *>(Reduced)->merge(
225 std::move(*static_cast<EnumInfo *>(NewInfo)));
226 break;
228 static_cast<FunctionInfo *>(Reduced)->merge(
229 std::move(*static_cast<FunctionInfo *>(NewInfo)));
230 break;
232 static_cast<TypedefInfo *>(Reduced)->merge(
233 std::move(*static_cast<TypedefInfo *>(NewInfo)));
234 break;
236 static_cast<ConceptInfo *>(Reduced)->merge(
237 std::move(*static_cast<ConceptInfo *>(NewInfo)));
238 break;
240 static_cast<VarInfo *>(Reduced)->merge(
241 std::move(*static_cast<VarInfo *>(NewInfo)));
242 break;
244 static_cast<FriendInfo *>(Reduced)->merge(
245 std::move(*static_cast<FriendInfo *>(NewInfo)));
246 break;
247 default:
248 return llvm::createStringError(llvm::inconvertibleErrorCode(),
249 "unknown info type");
250 }
251
252 return llvm::Error::success();
253}
254
255// Dispatch function.
256llvm::Expected<Info *> mergeInfos(SmallVectorImpl<Info *> &Values) {
257 if (Values.empty() || !Values[0])
258 return llvm::createStringError(llvm::inconvertibleErrorCode(),
259 "no info values to merge");
260
261 switch (Values[0]->IT) {
263 return reduce<NamespaceInfo>(Values);
265 return reduce<RecordInfo>(Values);
267 return reduce<EnumInfo>(Values);
269 return reduce<FunctionInfo>(Values);
271 return reduce<TypedefInfo>(Values);
273 return reduce<ConceptInfo>(Values);
275 return reduce<VarInfo>(Values);
277 return reduce<FriendInfo>(Values);
279 return llvm::createStringError(llvm::inconvertibleErrorCode(),
280 "unexpected info type");
281 }
282 llvm_unreachable("unhandled enumerator");
283}
284
286 const TemplateSpecializationInfo &Other, llvm::BumpPtrAllocator &Arena)
288 Params = allocateArray(Other.Params, Arena);
289}
290
292 llvm::BumpPtrAllocator &Arena) {
293 Params = allocateArray(Other.Params, Arena);
294 if (Other.Specialization)
296 Constraints = allocateArray(Other.Constraints, Arena);
297}
298
299bool CommentInfo::operator==(const CommentInfo &Other) const {
300 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
302 auto SecondCI =
303 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
304 Other.ParamName, Other.CloseName, Other.SelfClosing,
305 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
306
307 if (FirstCI != SecondCI || Children.size() != Other.Children.size())
308 return false;
309
310 return llvm::equal(Children, Other.Children);
311}
312
313bool CommentInfo::operator<(const CommentInfo &Other) const {
314 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
316 auto SecondCI =
317 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
318 Other.ParamName, Other.CloseName, Other.SelfClosing,
319 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
320
321 if (FirstCI < SecondCI)
322 return true;
323
324 if (FirstCI == SecondCI) {
325 return std::lexicographical_compare(Children.begin(), Children.end(),
326 Other.Children.begin(),
327 Other.Children.end());
328 }
329
330 return false;
331}
332
334 llvm::BumpPtrAllocator &Arena) {
335 Kind = Other.Kind;
336 Direction = Other.Direction;
337 Name = Other.Name;
338 ParamName = Other.ParamName;
339 CloseName = Other.CloseName;
340 SelfClosing = Other.SelfClosing;
341 Explicit = Other.Explicit;
342 Text = Other.Text;
343 AttrKeys = allocateArray(Other.AttrKeys, Arena);
344 AttrValues = allocateArray(Other.AttrValues, Arena);
345 Args = allocateArray(Other.Args, Arena);
346 if (!Other.Children.empty()) {
347 CommentInfo *NewArray = Arena.Allocate<CommentInfo>(Other.Children.size());
348 for (size_t Idx = 0; Idx < Other.Children.size(); ++Idx) {
349 new (NewArray + Idx) CommentInfo(Other.Children[Idx], Arena);
350 }
351 Children = llvm::ArrayRef<CommentInfo>(NewArray, Other.Children.size());
352 }
353}
354
355static llvm::SmallString<64>
356calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
357 const StringRef &Name, const StringRef &CurrentPath) {
358 llvm::SmallString<64> FilePath;
359
360 if (CurrentPath != Path) {
361 // iterate back to the top
362 for (llvm::sys::path::const_iterator I =
363 llvm::sys::path::begin(CurrentPath);
364 I != llvm::sys::path::end(CurrentPath); ++I)
365 llvm::sys::path::append(FilePath, "..");
366 llvm::sys::path::append(FilePath, Path);
367 }
368
369 // Namespace references have a Path to the parent namespace, but
370 // the file is actually in the subdirectory for the namespace.
371 if (Type == doc::InfoType::IT_namespace)
372 llvm::sys::path::append(FilePath, Name);
373
374 return llvm::sys::path::relative_path(FilePath);
375}
376
377StringRef Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
378 return internString(
380}
381
382StringRef Reference::getFileBaseName() const {
384 return "index";
385
386 return Name;
387}
388
389StringRef Info::getRelativeFilePath(const StringRef &CurrentPath) const {
390 return internString(
391 calculateRelativeFilePath(IT, Path, extractName(), CurrentPath));
392}
393
394StringRef Info::getFileBaseName() const {
396 return "index";
397
398 return extractName();
399}
400
401bool Reference::mergeable(const Reference &Other) {
402 return RefType == Other.RefType && USR == Other.USR;
403}
404
406 assert(mergeable(Other));
407 assert(RefType != InfoType::IT_default &&
408 "Merging reference with default InfoType");
409 if (Name.empty())
410 Name = Other.Name;
411 if (Path.empty())
412 Path = Other.Path;
413 if (QualName.empty())
414 QualName = Other.QualName;
415 if (DocumentationFileName.empty())
416 DocumentationFileName = Other.DocumentationFileName;
417}
418
420 return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;
421}
422
424 assert(mergeable(Other));
425 Ref.merge(std::move(Other.Ref));
426 SymbolInfo::merge(std::move(Other));
427}
428
429FriendInfo::FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena)
430 : SymbolInfo(Other, Arena) {
431 Ref = Other.Ref;
432 if (Other.Template)
433 Template.emplace(*Other.Template, Arena);
434 if (Other.ReturnType)
435 ReturnType = Other.ReturnType;
436 if (!Other.Params.empty())
437 Params = allocateArray(Other.Params, Arena);
438 IsClass = Other.IsClass;
439}
440
441Info::Info(const Info &Other, llvm::BumpPtrAllocator &Arena)
442 : Path(Other.Path), Name(Other.Name),
444 ParentUSR(Other.ParentUSR), IT(Other.IT) {
445 Namespace = allocateArray(Other.Namespace, Arena);
446 if (!Other.Description.empty()) {
447 for (const auto &Desc : Other.Description) {
448 CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
449 Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
450 }
451 }
452}
453
454void Info::mergeBase(Info &&Other) {
455 assert(mergeable(Other));
456 assert(IT != InfoType::IT_default && "Merging info with default InfoType");
457 if (USR == EmptySID)
458 USR = Other.USR;
459 if (Name == "")
460 Name = Other.Name;
461 if (Path == "")
462 Path = Other.Path;
463 if (Namespace.empty() && !Other.Namespace.empty())
464 Namespace = allocateArray(Other.Namespace, PersistentArena);
465 // Unconditionally extend the description, since each decl may have a comment.
466 mergeUnkeyed(Description, std::move(Other.Description));
467 if (ParentUSR == EmptySID)
468 ParentUSR = Other.ParentUSR;
469 if (DocumentationFileName.empty())
470 DocumentationFileName = Other.DocumentationFileName;
471}
472
473bool Info::mergeable(const Info &Other) {
474 return IT == Other.IT && USR == Other.USR;
475}
476
477SymbolInfo::SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena)
478 : Info(Other, Arena), DefLoc(Other.DefLoc), MangledName(Other.MangledName),
479 IsStatic(Other.IsStatic) {
480 if (!Other.Loc.empty()) {
481 for (const auto &L : Other.Loc) {
482 Location *NewL = allocatePtr<Location>(Arena, L);
483 Loc.push_back(*allocateListNode<Location>(Arena, NewL));
484 }
485 }
486}
487
488void SymbolInfo::merge(SymbolInfo &&Other) {
489 assert(mergeable(Other));
490 if (!DefLoc)
491 DefLoc = std::move(Other.DefLoc);
492 if (MangledName.empty())
493 MangledName = std::move(Other.MangledName);
494 // Unconditionally extend the list of locations, since we want all of them.
495 mergeUnkeyed(Loc, std::move(Other.Loc));
496 mergeBase(std::move(Other));
497 if (!IsStatic)
498 IsStatic = Other.IsStatic;
499}
500
503
505 assert(mergeable(Other));
506 // Reduce children if necessary.
507 reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
508 reduceChildren(Children.Records, std::move(Other.Children.Records));
509 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
510 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
511 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
512 reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
513 reduceChildren(Children.Variables, std::move(Other.Children.Variables));
514 mergeBase(std::move(Other));
515}
516
519
520// FIXME: This constructor is currently unsafe for cross-arena copies of
521// populated records. Because a default copy of ScopeChildren will shallow-copy
522// the intrusive pointers, leading to a use-after-free when the TransientArena
523// is reset. Subsequent patches will address this by deep-copying children
524// individually via reduceChildren.
525RecordInfo::RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena)
526 : SymbolInfo(Other, Arena), TagType(Other.TagType),
527 IsTypeDef(Other.IsTypeDef) {
528 Members = deepCopyArray(Other.Members, Arena);
529 Parents = allocateArray(Other.Parents, Arena);
531 Bases = deepCopyArray(Other.Bases, Arena);
532 Friends = deepCopyArray(Other.Friends, Arena);
533}
534
536 llvm::BumpPtrAllocator &Arena)
537 : FieldTypeInfo(Other), Access(Other.Access), IsStatic(Other.IsStatic) {
538 if (!Other.Description.empty()) {
539 for (const auto &Desc : Other.Description) {
540 CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
541 Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
542 }
543 }
544}
545
547 assert(mergeable(Other));
548 if (!llvm::to_underlying(TagType))
549 TagType = Other.TagType;
550 IsTypeDef = IsTypeDef || Other.IsTypeDef;
551 if (Members.empty() && !Other.Members.empty())
552 Members = deepCopyArray(Other.Members, PersistentArena);
553 if (Bases.empty() && !Other.Bases.empty())
554 Bases = deepCopyArray(Other.Bases, PersistentArena);
555 if (Parents.empty() && !Other.Parents.empty())
556 Parents = allocateArray(Other.Parents, PersistentArena);
557 if (VirtualParents.empty() && !Other.VirtualParents.empty())
558 VirtualParents = allocateArray(Other.VirtualParents, PersistentArena);
559 if (Friends.empty() && !Other.Friends.empty())
560 Friends = deepCopyArray(Other.Friends, PersistentArena);
561 // Reduce children if necessary.
562 reduceChildren(Children.Records, std::move(Other.Children.Records));
563 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
564 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
565 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
566 if (!Template && Other.Template)
567 Template = TemplateInfo(*Other.Template, PersistentArena);
568 SymbolInfo::merge(std::move(Other));
569}
570
572 llvm::BumpPtrAllocator &Arena)
573 : Name(Other.Name), Value(Other.Value), ValueExpr(Other.ValueExpr) {
574 if (!Other.Description.empty()) {
575 for (const auto &Desc : Other.Description) {
576 CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
577 Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
578 }
579 }
580}
581
583 assert(mergeable(Other));
584 if (!Scoped)
585 Scoped = Other.Scoped;
586 if (!BaseType && Other.BaseType)
587 BaseType = std::move(Other.BaseType);
588 if (Members.empty() && !Other.Members.empty())
589 Members = deepCopyArray(Other.Members, PersistentArena);
590 SymbolInfo::merge(std::move(Other));
591}
592
594 assert(mergeable(Other));
595 if (!IsMethod)
596 IsMethod = Other.IsMethod;
597 if (!Access)
598 Access = Other.Access;
599 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
600 ReturnType = std::move(Other.ReturnType);
601 if (Parent.USR == EmptySID && Parent.Name == "")
602 Parent = std::move(Other.Parent);
603 if (Params.empty() && !Other.Params.empty())
604 Params = allocateArray(Other.Params, PersistentArena);
605 if (!Template && Other.Template)
606 Template = TemplateInfo(*Other.Template, PersistentArena);
607 SymbolInfo::merge(std::move(Other));
608}
609
611 assert(mergeable(Other));
612 if (!IsUsing)
613 IsUsing = Other.IsUsing;
614 if (Underlying.Type.Name == "")
615 Underlying = Other.Underlying;
616 if (!Template && Other.Template)
617 Template = TemplateInfo(*Other.Template, PersistentArena);
618 SymbolInfo::merge(std::move(Other));
619}
620
622 assert(mergeable(Other));
623 if (!IsType)
624 IsType = Other.IsType;
625 if (ConstraintExpression.empty())
626 ConstraintExpression = std::move(Other.ConstraintExpression);
627 if (Template.Constraints.empty() && !Other.Template.Constraints.empty())
628 Template.Constraints =
629 allocateArray(Other.Template.Constraints, PersistentArena);
630 if (Template.Params.empty() && !Other.Template.Params.empty())
631 Template.Params = allocateArray(Other.Template.Params, PersistentArena);
632 SymbolInfo::merge(std::move(Other));
633}
634
635void VarInfo::merge(VarInfo &&Other) {
636 assert(mergeable(Other));
637 if (!IsStatic)
638 IsStatic = Other.IsStatic;
639 if (Type.Type.USR == EmptySID && Type.Type.Name == "")
640 Type = std::move(Other.Type);
641 SymbolInfo::merge(std::move(Other));
642}
643
645
647 llvm::BumpPtrAllocator &Arena)
648 : RecordInfo(Other, Arena), Access(Other.Access),
649 IsVirtual(Other.IsVirtual), IsParent(Other.IsParent) {}
650
652 bool IsVirtual, AccessSpecifier Access,
653 bool IsParent)
656
657StringRef Info::extractName() const {
658 if (!Name.empty())
659 return Name;
660
661 switch (IT) {
663 // Cover the case where the project contains a base namespace called
664 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
665 // namespace, which would conflict with the hard-coded global namespace name
666 // below.)
667 if (Name == "GlobalNamespace" && Namespace.empty())
668 return "@GlobalNamespace";
669 // The case of anonymous namespaces is taken care of in serialization,
670 // so here we can safely assume an unnamed namespace is the global
671 // one.
672 return "GlobalNamespace";
674 return internString("@nonymous_record_" + toHex(llvm::toStringRef(USR)));
676 return internString("@nonymous_enum_" + toHex(llvm::toStringRef(USR)));
678 return internString("@nonymous_typedef_" + toHex(llvm::toStringRef(USR)));
680 return internString("@nonymous_function_" + toHex(llvm::toStringRef(USR)));
682 return internString("@nonymous_concept_" + toHex(llvm::toStringRef(USR)));
684 return internString("@nonymous_variable_" + toHex(llvm::toStringRef(USR)));
686 return internString("@nonymous_friend_" + toHex(llvm::toStringRef(USR)));
688 return internString("@nonymous_" + toHex(llvm::toStringRef(USR)));
689 }
690 llvm_unreachable("Invalid InfoType.");
691 return "";
692}
693
694// Order is based on the Name attribute: case insensitive order
695bool Index::operator<(const Index &Other) const {
696 // Start with case-insensitive (e.g., 'apple' < 'Zebra').
697 // This prevents 'Zebra' from appearing before 'apple' due to ASCII values,
698 // where uppercase letters have a lower numeric value than lowercase.
699 int Cmp = Name.compare_insensitive(Other.Name);
700 if (Cmp != 0)
701 return Cmp < 0;
702
703 // If names are identical, we fall back to standard string comparison where
704 // uppercase precedes lowercase (e.g., 'Apple' < 'apple').
705 return Name < Other.Name;
706}
707
708std::vector<const Index *> Index::getSortedChildren() const {
709 std::vector<const Index *> SortedChildren;
710 SortedChildren.reserve(Children.size());
711 for (const auto &[_, C] : Children)
712 SortedChildren.push_back(&C);
713 llvm::sort(SortedChildren,
714 [](const Index *A, const Index *B) { return *A < *B; });
715 return SortedChildren;
716}
717
719 for (auto &[_, C] : Children)
720 C.sort();
721}
722
723ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
724 StringRef ProjectName, bool PublicOnly,
725 StringRef OutDirectory, StringRef SourceRoot,
726 StringRef RepositoryUrl,
727 StringRef RepositoryLinePrefix, StringRef Base,
728 std::vector<std::string> UserStylesheets,
729 clang::DiagnosticsEngine &Diags,
735 llvm::SmallString<128> SourceRootDir(SourceRoot);
736 if (SourceRoot.empty())
737 // If no SourceRoot was provided the current path is used as the default
738 llvm::sys::fs::current_path(SourceRootDir);
739 this->SourceRoot = std::string(SourceRootDir);
740 if (!RepositoryUrl.empty()) {
741 this->RepositoryUrl = std::string(RepositoryUrl);
742 if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
743 !RepositoryUrl.starts_with("https://"))
744 this->RepositoryUrl->insert(0, "https://");
745
746 if (!RepositoryLinePrefix.empty())
747 this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);
748 }
749}
750
752 Namespaces.sort();
753 Records.sort();
754 Functions.sort();
755 Enums.sort();
756 Typedefs.sort();
757 Concepts.sort();
758 Variables.sort();
759}
760} // namespace doc
761} // namespace clang
T * allocatePtr(llvm::BumpPtrAllocator &Alloc, Args &&...args)
void reduceChildren< Reference >(DocList< Reference > &Children, DocList< Reference > &&ChildrenToMerge)
static void mergeUnkeyed(DocList< T > &Target, DocList< T > &&Source)
static llvm::SmallString< 64 > calculateRelativeFilePath(const InfoType &Type, const StringRef &Path, const StringRef &Name, const StringRef &CurrentPath)
llvm::simple_ilist< InfoNode< T > > DocList
thread_local llvm::BumpPtrAllocator PersistentArena
T * allocateTransient(Args &&...args)
void mergeUnkeyed< CommentInfo >(DocList< CommentInfo > &Target, DocList< CommentInfo > &&Source)
ConcurrentStringPool & getGlobalStringPool()
CommentKind stringToCommentKind(llvm::StringRef KindStr)
llvm::ArrayRef< T > deepCopyArray(llvm::ArrayRef< T > V, llvm::BumpPtrAllocator &Alloc)
InfoNode< T > * allocateListNodePersistent(Args &&...args)
InfoNode< T > * allocateListNode(llvm::BumpPtrAllocator &Alloc, Args &&...args)
thread_local llvm::BumpPtrAllocator TransientArena
StringRef internString(const Twine &T)
llvm::ArrayRef< T > allocateArray(llvm::SmallVectorImpl< T > &V, llvm::BumpPtrAllocator &Alloc)
llvm::Expected< Info * > mergeInfos(SmallVectorImpl< Info * > &Values)
static void reduceChildren(DocList< T > &Children, DocList< T > &&ChildrenToMerge)
static llvm::Expected< Info * > reduce(SmallVectorImpl< Info * > &Values)
std::array< uint8_t, 20 > SymbolID
llvm::StringRef commentKindToString(CommentKind Kind)
llvm::Error mergeSingleInfo(doc::Info *&Reduced, doc::Info *NewInfo, llvm::BumpPtrAllocator &Arena)
const SymbolID EmptySID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::optional< std::string > RepositoryUrl
std::vector< std::string > UserStylesheets
ClangDocContext(tooling::ExecutionContext *ECtx, StringRef ProjectName, bool PublicOnly, StringRef OutDirectory, StringRef SourceRoot, StringRef RepositoryUrl, StringRef RepositoryCodeLinePrefix, StringRef Base, std::vector< std::string > UserStylesheets, clang::DiagnosticsEngine &Diags, OutputFormatTy Format, bool FTimeTrace=false)
tooling::ExecutionContext * ECtx
clang::DiagnosticsEngine & Diags
std::optional< std::string > RepositoryLinePrefix
ArrayRef< StringRef > Args
bool operator<(const CommentInfo &Other) const
bool operator==(const CommentInfo &Other) const
ArrayRef< CommentInfo > Children
ArrayRef< StringRef > AttrKeys
ArrayRef< StringRef > AttrValues
void merge(ConceptInfo &&I)
llvm::ArrayRef< EnumValueInfo > Members
void merge(EnumInfo &&I)
std::optional< TypeInfo > BaseType
DocList< CommentInfo > Description
Comment description of this field.
EnumValueInfo(StringRef Name=StringRef(), StringRef Value=StringRef("0"), StringRef ValueExpr=StringRef())
std::optional< TypeInfo > ReturnType
void merge(FriendInfo &&Other)
llvm::ArrayRef< FieldTypeInfo > Params
std::optional< TemplateInfo > Template
bool mergeable(const FriendInfo &Other)
FunctionInfo(SymbolID USR=SymbolID())
llvm::ArrayRef< FieldTypeInfo > Params
void merge(FunctionInfo &&I)
std::optional< TemplateInfo > Template
bool operator<(const Index &Other) const
llvm::StringMap< Index > Children
std::vector< const Index * > getSortedChildren() const
A base struct for Infos.
StringRef getRelativeFilePath(const StringRef &CurrentPath) const
Returns the file path for this Info relative to CurrentPath.
Info(InfoType IT=InfoType::IT_default, SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
bool mergeable(const Info &Other)
DocList< CommentInfo > Description
llvm::ArrayRef< Reference > Namespace
StringRef extractName() const
StringRef DocumentationFileName
void mergeBase(Info &&I)
StringRef getFileBaseName() const
Returns the basename that should be used for this Info.
DocList< CommentInfo > Description
NamespaceInfo(SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
void merge(NamespaceInfo &&I)
llvm::ArrayRef< BaseRecordInfo > Bases
llvm::ArrayRef< FriendInfo > Friends
RecordInfo(SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
llvm::ArrayRef< Reference > VirtualParents
std::optional< TemplateInfo > Template
llvm::ArrayRef< Reference > Parents
llvm::ArrayRef< MemberTypeInfo > Members
void merge(RecordInfo &&I)
void merge(Reference &&I)
Reference(SymbolID USR=SymbolID(), StringRef Name=StringRef(), InfoType IT=InfoType::IT_default)
bool mergeable(const Reference &Other)
StringRef getFileBaseName() const
Returns the basename that should be used for this Reference.
StringRef getRelativeFilePath(const StringRef &CurrentPath) const
Returns the path for this Reference relative to CurrentPath.
DocList< Reference > Records
DocList< EnumInfo > Enums
DocList< ConceptInfo > Concepts
DocList< VarInfo > Variables
DocList< FunctionInfo > Functions
DocList< Reference > Namespaces
DocList< TypedefInfo > Typedefs
SymbolInfo(InfoType IT, SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
DocList< Location > Loc
std::optional< Location > DefLoc
llvm::ArrayRef< TemplateParamInfo > Params
llvm::ArrayRef< ConstraintInfo > Constraints
std::optional< TemplateSpecializationInfo > Specialization
llvm::ArrayRef< TemplateParamInfo > Params
void merge(TypedefInfo &&I)
std::optional< TemplateInfo > Template
TypedefInfo(SymbolID USR=SymbolID())
void merge(VarInfo &&I)