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
32llvm::BumpPtrAllocator &getTransientArena() {
33 thread_local llvm::BumpPtrAllocator TransientArena;
34 return TransientArena;
35}
36
37llvm::BumpPtrAllocator &getPersistentArena() {
38 thread_local llvm::BumpPtrAllocator PersistentArena;
39 return PersistentArena;
40}
41
43 static ConcurrentStringPool GlobalPool;
44 return GlobalPool;
45}
46
47CommentKind stringToCommentKind(llvm::StringRef KindStr) {
48 static const llvm::StringMap<CommentKind> KindMap = {
49 {"FullComment", CommentKind::CK_FullComment},
50 {"ParagraphComment", CommentKind::CK_ParagraphComment},
51 {"TextComment", CommentKind::CK_TextComment},
52 {"InlineCommandComment", CommentKind::CK_InlineCommandComment},
53 {"HTMLStartTagComment", CommentKind::CK_HTMLStartTagComment},
54 {"HTMLEndTagComment", CommentKind::CK_HTMLEndTagComment},
55 {"BlockCommandComment", CommentKind::CK_BlockCommandComment},
56 {"ParamCommandComment", CommentKind::CK_ParamCommandComment},
57 {"TParamCommandComment", CommentKind::CK_TParamCommandComment},
58 {"VerbatimBlockComment", CommentKind::CK_VerbatimBlockComment},
59 {"VerbatimBlockLineComment", CommentKind::CK_VerbatimBlockLineComment},
60 {"VerbatimLineComment", CommentKind::CK_VerbatimLineComment},
61 };
62
63 auto It = KindMap.find(KindStr);
64 if (It != KindMap.end()) {
65 return It->second;
66 }
68}
69
70llvm::StringRef commentKindToString(CommentKind Kind) {
71 switch (Kind) {
73 return "FullComment";
75 return "ParagraphComment";
77 return "TextComment";
79 return "InlineCommandComment";
81 return "HTMLStartTagComment";
83 return "HTMLEndTagComment";
85 return "BlockCommandComment";
87 return "ParamCommandComment";
89 return "TParamCommandComment";
91 return "VerbatimBlockComment";
93 return "VerbatimBlockLineComment";
95 return "VerbatimLineComment";
97 return "Unknown";
98 }
99 llvm_unreachable("Unhandled CommentKind");
100}
101
103
104template <typename T>
105static llvm::Expected<Info *> reduce(SmallVectorImpl<Info *> &Values) {
106 if (Values.empty() || !Values[0])
107 return llvm::createStringError(llvm::inconvertibleErrorCode(),
108 "no value to reduce");
109 T *Merged = allocateTransient<T>(Values[0]->USR);
110 for (auto &I : Values)
111 Merged->merge(std::move(*cast<T>(I)));
112 return Merged;
113}
114
115template <typename T>
116static void reduceChildren(DocList<T> &Children, DocList<T> &&ChildrenToMerge) {
117 while (!ChildrenToMerge.empty()) {
118 T *Ptr = ChildrenToMerge.front().Ptr;
119 ChildrenToMerge.pop_front();
120
121 auto It = llvm::find_if(
122 Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
123
124 if (It == Children.end()) {
125 InfoNode<T> *NewNode = allocateListNodePersistent<T>(Ptr->USR);
126 NewNode->Ptr->merge(std::move(*Ptr));
127 Children.push_back(*NewNode);
128 } else {
129 It->Ptr->merge(std::move(*Ptr));
130 }
131 }
132}
133
134template <>
136 DocList<Reference> &&ChildrenToMerge) {
137 while (!ChildrenToMerge.empty()) {
138 Reference *Ptr = ChildrenToMerge.front().Ptr;
139 ChildrenToMerge.pop_front();
140
141 auto It = llvm::find_if(
142 Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
143 if (It == Children.end()) {
145 NewNode->Ptr->USR = Ptr->USR;
146 NewNode->Ptr->RefType = Ptr->RefType;
147 NewNode->Ptr->merge(std::move(*Ptr));
148 Children.push_back(*NewNode);
149 } else {
150 It->Ptr->merge(std::move(*Ptr));
151 }
152 }
153}
154
155template <typename T>
156static void mergeUnkeyed(DocList<T> &Target, DocList<T> &&Source) {
157 while (!Source.empty()) {
158 T *Ptr = Source.front().Ptr;
159 Source.pop_front();
160
161 if (!llvm::any_of(Target,
162 [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
163 Target.push_back(*allocateListNodePersistent<T>(*Ptr));
164 }
165 }
166}
167
168template <>
170 DocList<CommentInfo> &&Source) {
171 while (!Source.empty()) {
172 CommentInfo *Ptr = Source.front().Ptr;
173 Source.pop_front();
174
175 if (llvm::none_of(Target,
176 [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
177 Target.push_back(
179 }
180 }
181}
182
183template <typename T>
184static llvm::Error mergeTypedInfo(Info *&Reduced, Info *NewInfo,
185 llvm::BumpPtrAllocator &Arena) {
186 if (!Reduced)
187 Reduced = allocatePtr<T>(Arena, NewInfo->USR);
188 cast<T>(Reduced)->merge(std::move(*cast<T>(NewInfo)));
189 return llvm::Error::success();
190}
191
192llvm::Error mergeSingleInfo(doc::Info *&Reduced, doc::Info *NewInfo,
193 llvm::BumpPtrAllocator &Arena) {
194 if (Reduced && Reduced->IT != NewInfo->IT)
195 return llvm::createStringError(llvm::inconvertibleErrorCode(),
196 "info types mismatch");
197
198 switch (NewInfo->IT) {
200 return mergeTypedInfo<NamespaceInfo>(Reduced, NewInfo, Arena);
202 return mergeTypedInfo<RecordInfo>(Reduced, NewInfo, Arena);
204 return mergeTypedInfo<EnumInfo>(Reduced, NewInfo, Arena);
206 return mergeTypedInfo<FunctionInfo>(Reduced, NewInfo, Arena);
208 return mergeTypedInfo<TypedefInfo>(Reduced, NewInfo, Arena);
210 return mergeTypedInfo<ConceptInfo>(Reduced, NewInfo, Arena);
212 return mergeTypedInfo<VarInfo>(Reduced, NewInfo, Arena);
214 return mergeTypedInfo<FriendInfo>(Reduced, NewInfo, Arena);
215 default:
216 return llvm::createStringError(llvm::inconvertibleErrorCode(),
217 "unknown info type");
218 }
219
220 return llvm::Error::success();
221}
222
223// Dispatch function.
224llvm::Expected<Info *> mergeInfos(SmallVectorImpl<Info *> &Values) {
225 if (Values.empty() || !Values[0])
226 return llvm::createStringError(llvm::inconvertibleErrorCode(),
227 "no info values to merge");
228
229 switch (Values[0]->IT) {
231 return reduce<NamespaceInfo>(Values);
233 return reduce<RecordInfo>(Values);
235 return reduce<EnumInfo>(Values);
237 return reduce<FunctionInfo>(Values);
239 return reduce<TypedefInfo>(Values);
241 return reduce<ConceptInfo>(Values);
243 return reduce<VarInfo>(Values);
245 return reduce<FriendInfo>(Values);
247 return llvm::createStringError(llvm::inconvertibleErrorCode(),
248 "unexpected info type");
249 }
250 llvm_unreachable("unhandled enumerator");
251}
252
254 const TemplateSpecializationInfo &Other, llvm::BumpPtrAllocator &Arena)
256 Params = allocateArray(Other.Params, Arena);
257}
258
260 llvm::BumpPtrAllocator &Arena) {
261 Params = allocateArray(Other.Params, Arena);
262 if (Other.Specialization)
264 Constraints = allocateArray(Other.Constraints, Arena);
265}
266
267bool CommentInfo::operator==(const CommentInfo &Other) const {
268 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
270 auto SecondCI =
271 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
272 Other.ParamName, Other.CloseName, Other.SelfClosing,
273 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
274
275 if (FirstCI != SecondCI || Children.size() != Other.Children.size())
276 return false;
277
278 return llvm::equal(Children, Other.Children);
279}
280
281bool CommentInfo::operator<(const CommentInfo &Other) const {
282 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
284 auto SecondCI =
285 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
286 Other.ParamName, Other.CloseName, Other.SelfClosing,
287 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
288
289 if (FirstCI < SecondCI)
290 return true;
291
292 if (FirstCI == SecondCI) {
293 return std::lexicographical_compare(Children.begin(), Children.end(),
294 Other.Children.begin(),
295 Other.Children.end());
296 }
297
298 return false;
299}
300
302 llvm::BumpPtrAllocator &Arena) {
303 Kind = Other.Kind;
304 Direction = Other.Direction;
305 Name = Other.Name;
306 ParamName = Other.ParamName;
307 CloseName = Other.CloseName;
308 SelfClosing = Other.SelfClosing;
309 Explicit = Other.Explicit;
310 Text = Other.Text;
311 AttrKeys = allocateArray(Other.AttrKeys, Arena);
312 AttrValues = allocateArray(Other.AttrValues, Arena);
313 Args = allocateArray(Other.Args, Arena);
314 if (!Other.Children.empty()) {
315 CommentInfo *NewArray = Arena.Allocate<CommentInfo>(Other.Children.size());
316 for (size_t Idx = 0; Idx < Other.Children.size(); ++Idx) {
317 new (NewArray + Idx) CommentInfo(Other.Children[Idx], Arena);
318 }
319 Children = llvm::ArrayRef<CommentInfo>(NewArray, Other.Children.size());
320 }
321}
322
323static llvm::SmallString<64>
324calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
325 const StringRef &Name, const StringRef &CurrentPath) {
326 llvm::SmallString<64> FilePath;
327
328 if (CurrentPath != Path) {
329 // iterate back to the top
330 for (llvm::sys::path::const_iterator I =
331 llvm::sys::path::begin(CurrentPath);
332 I != llvm::sys::path::end(CurrentPath); ++I)
333 llvm::sys::path::append(FilePath, "..");
334 llvm::sys::path::append(FilePath, Path);
335 }
336
337 // Namespace references have a Path to the parent namespace, but
338 // the file is actually in the subdirectory for the namespace.
339 if (Type == doc::InfoType::IT_namespace)
340 llvm::sys::path::append(FilePath, Name);
341
342 return llvm::sys::path::relative_path(FilePath);
343}
344
345StringRef Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
346 return internString(
348}
349
350StringRef Reference::getFileBaseName() const {
352 return "index";
353
354 return Name;
355}
356
357StringRef Info::getRelativeFilePath(const StringRef &CurrentPath) const {
358 return internString(
359 calculateRelativeFilePath(IT, Path, extractName(), CurrentPath));
360}
361
362StringRef Info::getFileBaseName() const {
364 return "index";
365
366 return extractName();
367}
368
369bool Reference::mergeable(const Reference &Other) {
370 return RefType == Other.RefType && USR == Other.USR;
371}
372
374 assert(mergeable(Other));
375 assert(RefType != InfoType::IT_default &&
376 "Merging reference with default InfoType");
377 if (Name.empty())
378 Name = Other.Name;
379 if (Path.empty())
380 Path = Other.Path;
381 if (QualName.empty())
382 QualName = Other.QualName;
383 if (DocumentationFileName.empty())
384 DocumentationFileName = Other.DocumentationFileName;
385}
386
388 return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;
389}
390
392 assert(mergeable(Other));
393 Ref.merge(std::move(Other.Ref));
394 SymbolInfo::merge(std::move(Other));
395}
396
397FriendInfo::FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena)
398 : SymbolInfo(Other, Arena) {
399 Ref = Other.Ref;
400 if (Other.Template)
401 Template.emplace(*Other.Template, Arena);
402 if (Other.ReturnType)
403 ReturnType = Other.ReturnType;
404 if (!Other.Params.empty())
405 Params = allocateArray(Other.Params, Arena);
406 IsClass = Other.IsClass;
407}
408
409Info::Info(const Info &Other, llvm::BumpPtrAllocator &Arena)
410 : Path(Other.Path), Name(Other.Name),
412 ParentUSR(Other.ParentUSR), IT(Other.IT) {
413 Namespace = allocateArray(Other.Namespace, Arena);
414 if (!Other.Description.empty()) {
415 for (const auto &Desc : Other.Description) {
416 CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
417 Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
418 }
419 }
420}
421
422void Info::mergeBase(Info &&Other) {
423 assert(mergeable(Other));
424 assert(IT != InfoType::IT_default && "Merging info with default InfoType");
425 if (USR == EmptySID)
426 USR = Other.USR;
427 if (Name == "")
428 Name = Other.Name;
429 if (Path == "")
430 Path = Other.Path;
431 if (Namespace.empty() && !Other.Namespace.empty())
432 Namespace = allocateArray(Other.Namespace, getPersistentArena());
433 // Unconditionally extend the description, since each decl may have a comment.
434 mergeUnkeyed(Description, std::move(Other.Description));
435 if (ParentUSR == EmptySID)
436 ParentUSR = Other.ParentUSR;
437 if (DocumentationFileName.empty())
438 DocumentationFileName = Other.DocumentationFileName;
439}
440
441bool Info::mergeable(const Info &Other) {
442 return IT == Other.IT && USR == Other.USR;
443}
444
445SymbolInfo::SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena)
446 : Info(Other, Arena), DefLoc(Other.DefLoc), MangledName(Other.MangledName),
447 IsStatic(Other.IsStatic) {
448 if (!Other.Loc.empty()) {
449 for (const auto &L : Other.Loc) {
450 Location *NewL = allocatePtr<Location>(Arena, L);
451 Loc.push_back(*allocateListNode<Location>(Arena, NewL));
452 }
453 }
454}
455
456void SymbolInfo::merge(SymbolInfo &&Other) {
457 assert(mergeable(Other));
458 if (!DefLoc)
459 DefLoc = std::move(Other.DefLoc);
460 if (MangledName.empty())
461 MangledName = std::move(Other.MangledName);
462 // Unconditionally extend the list of locations, since we want all of them.
463 mergeUnkeyed(Loc, std::move(Other.Loc));
464 mergeBase(std::move(Other));
465 if (!IsStatic)
466 IsStatic = Other.IsStatic;
467}
468
471
473 assert(mergeable(Other));
474 // Reduce children if necessary.
475 reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
476 reduceChildren(Children.Records, std::move(Other.Children.Records));
477 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
478 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
479 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
480 reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
481 reduceChildren(Children.Variables, std::move(Other.Children.Variables));
482 mergeBase(std::move(Other));
483}
484
487
488// FIXME: This constructor is currently unsafe for cross-arena copies of
489// populated records. Because a default copy of ScopeChildren will shallow-copy
490// the intrusive pointers, leading to a use-after-free when the TransientArena
491// is reset. Subsequent patches will address this by deep-copying children
492// individually via reduceChildren.
493RecordInfo::RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena)
494 : SymbolInfo(Other, Arena), TagType(Other.TagType),
495 IsTypeDef(Other.IsTypeDef) {
496 Members = deepCopyArray(Other.Members, Arena);
497 Parents = allocateArray(Other.Parents, Arena);
499 Bases = deepCopyArray(Other.Bases, Arena);
500 Friends = deepCopyArray(Other.Friends, Arena);
501}
502
504 llvm::BumpPtrAllocator &Arena)
505 : FieldTypeInfo(Other), Access(Other.Access), IsStatic(Other.IsStatic) {
506 if (!Other.Description.empty()) {
507 for (const auto &Desc : Other.Description) {
508 CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
509 Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
510 }
511 }
512}
513
515 assert(mergeable(Other));
516 if (!llvm::to_underlying(TagType))
517 TagType = Other.TagType;
518 IsTypeDef = IsTypeDef || Other.IsTypeDef;
519 if (Members.empty() && !Other.Members.empty())
520 Members = deepCopyArray(Other.Members, getPersistentArena());
521 if (Bases.empty() && !Other.Bases.empty())
522 Bases = deepCopyArray(Other.Bases, getPersistentArena());
523 if (Parents.empty() && !Other.Parents.empty())
524 Parents = allocateArray(Other.Parents, getPersistentArena());
525 if (VirtualParents.empty() && !Other.VirtualParents.empty())
526 VirtualParents = allocateArray(Other.VirtualParents, getPersistentArena());
527 if (Friends.empty() && !Other.Friends.empty())
528 Friends = deepCopyArray(Other.Friends, getPersistentArena());
529 // Reduce children if necessary.
530 reduceChildren(Children.Records, std::move(Other.Children.Records));
531 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
532 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
533 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
534 if (!Template && Other.Template)
535 Template = TemplateInfo(*Other.Template, getPersistentArena());
536 SymbolInfo::merge(std::move(Other));
537}
538
540 llvm::BumpPtrAllocator &Arena)
541 : Name(Other.Name), Value(Other.Value), ValueExpr(Other.ValueExpr) {
542 if (!Other.Description.empty()) {
543 for (const auto &Desc : Other.Description) {
544 CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
545 Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
546 }
547 }
548}
549
551 assert(mergeable(Other));
552 if (!Scoped)
553 Scoped = Other.Scoped;
554 if (!BaseType && Other.BaseType)
555 BaseType = std::move(Other.BaseType);
556 if (Members.empty() && !Other.Members.empty())
557 Members = deepCopyArray(Other.Members, getPersistentArena());
558 SymbolInfo::merge(std::move(Other));
559}
560
562 assert(mergeable(Other));
563 if (!IsMethod)
564 IsMethod = Other.IsMethod;
565 if (!Access)
566 Access = Other.Access;
567 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
568 ReturnType = std::move(Other.ReturnType);
569 if (Parent.USR == EmptySID && Parent.Name == "")
570 Parent = std::move(Other.Parent);
571 if (Params.empty() && !Other.Params.empty())
572 Params = allocateArray(Other.Params, getPersistentArena());
573 if (!Template && Other.Template)
574 Template = TemplateInfo(*Other.Template, getPersistentArena());
575 SymbolInfo::merge(std::move(Other));
576}
577
579 assert(mergeable(Other));
580 if (!IsUsing)
581 IsUsing = Other.IsUsing;
582 if (Underlying.Type.Name == "")
583 Underlying = Other.Underlying;
584 if (!Template && Other.Template)
585 Template = TemplateInfo(*Other.Template, getPersistentArena());
586 SymbolInfo::merge(std::move(Other));
587}
588
590 assert(mergeable(Other));
591 if (!IsType)
592 IsType = Other.IsType;
593 if (ConstraintExpression.empty())
594 ConstraintExpression = std::move(Other.ConstraintExpression);
595 if (Template.Constraints.empty() && !Other.Template.Constraints.empty())
596 Template.Constraints =
597 allocateArray(Other.Template.Constraints, getPersistentArena());
598 if (Template.Params.empty() && !Other.Template.Params.empty())
599 Template.Params =
600 allocateArray(Other.Template.Params, getPersistentArena());
601 SymbolInfo::merge(std::move(Other));
602}
603
604void VarInfo::merge(VarInfo &&Other) {
605 assert(mergeable(Other));
606 if (!IsStatic)
607 IsStatic = Other.IsStatic;
608 if (Type.Type.USR == EmptySID && Type.Type.Name == "")
609 Type = std::move(Other.Type);
610 SymbolInfo::merge(std::move(Other));
611}
612
614
616 llvm::BumpPtrAllocator &Arena)
617 : RecordInfo(Other, Arena), Access(Other.Access),
618 IsVirtual(Other.IsVirtual), IsParent(Other.IsParent) {}
619
621 bool IsVirtual, AccessSpecifier Access,
622 bool IsParent)
625
626StringRef Info::extractName() const {
627 if (!Name.empty())
628 return Name;
629
630 switch (IT) {
632 // Cover the case where the project contains a base namespace called
633 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
634 // namespace, which would conflict with the hard-coded global namespace name
635 // below.)
636 if (Name == "GlobalNamespace" && Namespace.empty())
637 return "@GlobalNamespace";
638 // The case of anonymous namespaces is taken care of in serialization,
639 // so here we can safely assume an unnamed namespace is the global
640 // one.
641 return "GlobalNamespace";
643 return internString("@nonymous_record_" + toHex(llvm::toStringRef(USR)));
645 return internString("@nonymous_enum_" + toHex(llvm::toStringRef(USR)));
647 return internString("@nonymous_typedef_" + toHex(llvm::toStringRef(USR)));
649 return internString("@nonymous_function_" + toHex(llvm::toStringRef(USR)));
651 return internString("@nonymous_concept_" + toHex(llvm::toStringRef(USR)));
653 return internString("@nonymous_variable_" + toHex(llvm::toStringRef(USR)));
655 return internString("@nonymous_friend_" + toHex(llvm::toStringRef(USR)));
657 return internString("@nonymous_" + toHex(llvm::toStringRef(USR)));
658 }
659 llvm_unreachable("Invalid InfoType.");
660 return "";
661}
662
663// Order is based on the Name attribute: case insensitive order
664bool Index::operator<(const Index &Other) const {
665 // Start with case-insensitive (e.g., 'apple' < 'Zebra').
666 // This prevents 'Zebra' from appearing before 'apple' due to ASCII values,
667 // where uppercase letters have a lower numeric value than lowercase.
668 int Cmp = Name.compare_insensitive(Other.Name);
669 if (Cmp != 0)
670 return Cmp < 0;
671
672 // If names are identical, we fall back to standard string comparison where
673 // uppercase precedes lowercase (e.g., 'Apple' < 'apple').
674 return Name < Other.Name;
675}
676
677std::vector<const Index *> Index::getSortedChildren() const {
678 std::vector<const Index *> SortedChildren;
679 SortedChildren.reserve(Children.size());
680 for (const auto &[_, C] : Children)
681 SortedChildren.push_back(&C);
682 llvm::sort(SortedChildren,
683 [](const Index *A, const Index *B) { return *A < *B; });
684 return SortedChildren;
685}
686
688 for (auto &[_, C] : Children)
689 C.sort();
690}
691
693 tooling::ExecutionContext *ECtx, StringRef ProjectName, bool PublicOnly,
694 StringRef OutDirectory, StringRef SourceRoot, StringRef RepositoryUrl,
695 StringRef RepositoryLinePrefix, StringRef Base,
696 std::vector<std::string> UserStylesheets, clang::DiagnosticsEngine &Diags,
702 llvm::SmallString<128> SourceRootDir(SourceRoot);
703 if (SourceRoot.empty())
704 // If no SourceRoot was provided the current path is used as the default
705 llvm::sys::fs::current_path(SourceRootDir);
706 this->SourceRoot = std::string(SourceRootDir);
707 if (!RepositoryUrl.empty()) {
708 this->RepositoryUrl = std::string(RepositoryUrl);
709 if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
710 !RepositoryUrl.starts_with("https://"))
711 this->RepositoryUrl->insert(0, "https://");
712
713 if (!RepositoryLinePrefix.empty())
714 this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);
715 }
716}
717
719 Namespaces.sort();
720 Records.sort();
721 Functions.sort();
722 Enums.sort();
723 Typedefs.sort();
724 Concepts.sort();
725 Variables.sort();
726}
727} // namespace doc
728} // namespace clang
static llvm::Error mergeTypedInfo(Info *&Reduced, Info *NewInfo, llvm::BumpPtrAllocator &Arena)
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
llvm::BumpPtrAllocator & getPersistentArena()
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)
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)
llvm::BumpPtrAllocator & getTransientArena()
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
tooling::ExecutionContext * ECtx
clang::DiagnosticsEngine & Diags
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, bool Pretty=false)
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)