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/StringMap.h"
24#include "llvm/Support/Error.h"
25#include "llvm/Support/Path.h"
26
27namespace clang {
28namespace doc {
29
30// Thread local arenas usable in each thread pool
31thread_local llvm::BumpPtrAllocator TransientArena;
32thread_local llvm::BumpPtrAllocator PersistentArena;
33
35 static ConcurrentStringPool GlobalPool;
36 return GlobalPool;
37}
38
39CommentKind stringToCommentKind(llvm::StringRef KindStr) {
40 static const llvm::StringMap<CommentKind> KindMap = {
41 {"FullComment", CommentKind::CK_FullComment},
42 {"ParagraphComment", CommentKind::CK_ParagraphComment},
43 {"TextComment", CommentKind::CK_TextComment},
44 {"InlineCommandComment", CommentKind::CK_InlineCommandComment},
45 {"HTMLStartTagComment", CommentKind::CK_HTMLStartTagComment},
46 {"HTMLEndTagComment", CommentKind::CK_HTMLEndTagComment},
47 {"BlockCommandComment", CommentKind::CK_BlockCommandComment},
48 {"ParamCommandComment", CommentKind::CK_ParamCommandComment},
49 {"TParamCommandComment", CommentKind::CK_TParamCommandComment},
50 {"VerbatimBlockComment", CommentKind::CK_VerbatimBlockComment},
51 {"VerbatimBlockLineComment", CommentKind::CK_VerbatimBlockLineComment},
52 {"VerbatimLineComment", CommentKind::CK_VerbatimLineComment},
53 };
54
55 auto It = KindMap.find(KindStr);
56 if (It != KindMap.end()) {
57 return It->second;
58 }
60}
61
62llvm::StringRef commentKindToString(CommentKind Kind) {
63 switch (Kind) {
65 return "FullComment";
67 return "ParagraphComment";
69 return "TextComment";
71 return "InlineCommandComment";
73 return "HTMLStartTagComment";
75 return "HTMLEndTagComment";
77 return "BlockCommandComment";
79 return "ParamCommandComment";
81 return "TParamCommandComment";
83 return "VerbatimBlockComment";
85 return "VerbatimBlockLineComment";
87 return "VerbatimLineComment";
89 return "Unknown";
90 }
91 llvm_unreachable("Unhandled CommentKind");
92}
93
94const SymbolID EmptySID = SymbolID();
95
96template <typename T>
97static llvm::Expected<OwnedPtr<Info>> reduce(OwningPtrArray<Info> &Values) {
98 if (Values.empty() || !Values[0])
99 return llvm::createStringError(llvm::inconvertibleErrorCode(),
100 "no value to reduce");
101 OwnedPtr<Info> Merged = allocatePtr<T>(Values[0]->USR);
102 T *Tmp = static_cast<T *>(getPtr(Merged));
103 for (auto &I : Values)
104 Tmp->merge(std::move(*static_cast<T *>(getPtr(I))));
105 return std::move(Merged);
106}
107
108// Return the index of the matching child in the vector, or -1 if merge is not
109// necessary.
110template <typename T>
111static int getChildIndexIfExists(OwningVec<T> &Children, T &ChildToMerge) {
112 for (unsigned long I = 0; I < Children.size(); I++) {
113 if (ChildToMerge.USR == Children[I].USR)
114 return I;
115 }
116 return -1;
117}
118
119template <typename T>
120static void reduceChildren(llvm::simple_ilist<T> &Children,
121 llvm::simple_ilist<T> &&ChildrenToMerge) {
122 while (!ChildrenToMerge.empty()) {
123 T *ChildToMerge = &ChildrenToMerge.front();
124 ChildrenToMerge.pop_front();
125
126 auto It = llvm::find_if(
127 Children, [&](const T &C) { return C.USR == ChildToMerge->USR; });
128 if (It == Children.end()) {
129 Children.push_back(*ChildToMerge);
130 } else {
131 It->merge(std::move(*ChildToMerge));
132 }
133 }
134}
135
136template <typename T>
137static void reduceChildren(OwningVec<T> &Children,
138 OwningVec<T> &&ChildrenToMerge) {
139 for (auto &ChildToMerge : ChildrenToMerge) {
140 int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);
141 if (MergeIdx == -1) {
142 Children.push_back(std::move(ChildToMerge));
143 continue;
144 }
145 Children[MergeIdx].merge(std::move(ChildToMerge));
146 }
147}
148
149// Dispatch function.
150llvm::Expected<OwnedPtr<Info>> mergeInfos(OwningPtrArray<Info> &Values) {
151 if (Values.empty() || !Values[0])
152 return llvm::createStringError(llvm::inconvertibleErrorCode(),
153 "no info values to merge");
154
155 switch (Values[0]->IT) {
157 return reduce<NamespaceInfo>(Values);
159 return reduce<RecordInfo>(Values);
161 return reduce<EnumInfo>(Values);
163 return reduce<FunctionInfo>(Values);
165 return reduce<TypedefInfo>(Values);
167 return reduce<ConceptInfo>(Values);
169 return reduce<VarInfo>(Values);
171 return reduce<FriendInfo>(Values);
173 return llvm::createStringError(llvm::inconvertibleErrorCode(),
174 "unexpected info type");
175 }
176 llvm_unreachable("unhandled enumerator");
177}
178
179bool CommentInfo::operator==(const CommentInfo &Other) const {
180 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
182 auto SecondCI =
183 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
184 Other.ParamName, Other.CloseName, Other.SelfClosing,
185 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
186
187 if (FirstCI != SecondCI || Children.size() != Other.Children.size())
188 return false;
189
190 return std::equal(Children.begin(), Children.end(), Other.Children.begin(),
191 llvm::deref<std::equal_to<>>{});
192}
193
194bool CommentInfo::operator<(const CommentInfo &Other) const {
195 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
197 auto SecondCI =
198 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
199 Other.ParamName, Other.CloseName, Other.SelfClosing,
200 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
201
202 if (FirstCI < SecondCI)
203 return true;
204
205 if (FirstCI == SecondCI) {
206 return std::lexicographical_compare(
207 Children.begin(), Children.end(), Other.Children.begin(),
208 Other.Children.end(), llvm::deref<std::less<>>());
209 }
210
211 return false;
212}
213
214static llvm::SmallString<64>
215calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
216 const StringRef &Name, const StringRef &CurrentPath) {
217 llvm::SmallString<64> FilePath;
218
219 if (CurrentPath != Path) {
220 // iterate back to the top
221 for (llvm::sys::path::const_iterator I =
222 llvm::sys::path::begin(CurrentPath);
223 I != llvm::sys::path::end(CurrentPath); ++I)
224 llvm::sys::path::append(FilePath, "..");
225 llvm::sys::path::append(FilePath, Path);
226 }
227
228 // Namespace references have a Path to the parent namespace, but
229 // the file is actually in the subdirectory for the namespace.
230 if (Type == doc::InfoType::IT_namespace)
231 llvm::sys::path::append(FilePath, Name);
232
233 return llvm::sys::path::relative_path(FilePath);
234}
235
236StringRef Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
237 return internString(
239}
240
241StringRef Reference::getFileBaseName() const {
243 return "index";
244
245 return Name;
246}
247
248StringRef Info::getRelativeFilePath(const StringRef &CurrentPath) const {
249 return internString(
250 calculateRelativeFilePath(IT, Path, extractName(), CurrentPath));
251}
252
253StringRef Info::getFileBaseName() const {
255 return "index";
256
257 return extractName();
258}
259
260bool Reference::mergeable(const Reference &Other) {
261 return RefType == Other.RefType && USR == Other.USR;
262}
263
265 assert(mergeable(Other));
266 if (Name.empty())
267 Name = Other.Name;
268 if (Path.empty())
269 Path = Other.Path;
270 if (DocumentationFileName.empty())
271 DocumentationFileName = Other.DocumentationFileName;
272}
273
275 return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;
276}
277
279 assert(mergeable(Other));
280 Ref.merge(std::move(Other.Ref));
281 SymbolInfo::merge(std::move(Other));
282}
283
284void Info::mergeBase(Info &&Other) {
285 assert(mergeable(Other));
286 if (USR == EmptySID)
287 USR = Other.USR;
288 if (Name == "")
289 Name = Other.Name;
290 if (Path == "")
291 Path = Other.Path;
292 if (Namespace.empty())
293 Namespace = std::move(Other.Namespace);
294 // Unconditionally extend the description, since each decl may have a comment.
295 std::move(Other.Description.begin(), Other.Description.end(),
296 std::back_inserter(Description));
297 llvm::sort(Description);
298 auto Last = llvm::unique(Description);
299 Description.erase(Last, Description.end());
300 if (ParentUSR == EmptySID)
301 ParentUSR = Other.ParentUSR;
302 if (DocumentationFileName.empty())
303 DocumentationFileName = Other.DocumentationFileName;
304}
305
306bool Info::mergeable(const Info &Other) {
307 return IT == Other.IT && USR == Other.USR;
308}
309
310void SymbolInfo::merge(SymbolInfo &&Other) {
311 assert(mergeable(Other));
312 if (!DefLoc)
313 DefLoc = std::move(Other.DefLoc);
314 // Unconditionally extend the list of locations, since we want all of them.
315 std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
316 llvm::sort(Loc);
317 auto *Last = llvm::unique(Loc);
318 Loc.erase(Last, Loc.end());
319 mergeBase(std::move(Other));
320 if (MangledName.empty())
321 MangledName = std::move(Other.MangledName);
322}
323
326
328 assert(mergeable(Other));
329 // Reduce children if necessary.
330 reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
331 reduceChildren(Children.Records, std::move(Other.Children.Records));
332 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
333 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
334 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
335 reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
336 reduceChildren(Children.Variables, std::move(Other.Children.Variables));
337 mergeBase(std::move(Other));
338}
339
342
344 assert(mergeable(Other));
345 if (!llvm::to_underlying(TagType))
346 TagType = Other.TagType;
347 IsTypeDef = IsTypeDef || Other.IsTypeDef;
348 if (Members.empty())
349 Members = std::move(Other.Members);
350 if (Bases.empty())
351 Bases = std::move(Other.Bases);
352 if (Parents.empty())
353 Parents = std::move(Other.Parents);
354 if (VirtualParents.empty())
355 VirtualParents = std::move(Other.VirtualParents);
356 if (Friends.empty())
357 Friends = std::move(Other.Friends);
358 // Reduce children if necessary.
359 reduceChildren(Children.Records, std::move(Other.Children.Records));
360 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
361 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
362 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
363 SymbolInfo::merge(std::move(Other));
364 if (!Template)
365 Template = Other.Template;
366}
367
369 assert(mergeable(Other));
370 if (!Scoped)
371 Scoped = Other.Scoped;
372 if (Members.empty())
373 Members = std::move(Other.Members);
374 SymbolInfo::merge(std::move(Other));
375}
376
378 assert(mergeable(Other));
379 if (!IsMethod)
380 IsMethod = Other.IsMethod;
381 if (!Access)
382 Access = Other.Access;
383 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
384 ReturnType = std::move(Other.ReturnType);
385 if (Parent.USR == EmptySID && Parent.Name == "")
386 Parent = std::move(Other.Parent);
387 if (Params.empty())
388 Params = std::move(Other.Params);
389 SymbolInfo::merge(std::move(Other));
390 if (!Template)
391 Template = Other.Template;
392}
393
395 assert(mergeable(Other));
396 if (!IsUsing)
397 IsUsing = Other.IsUsing;
398 if (Underlying.Type.Name == "")
399 Underlying = Other.Underlying;
400 if (!Template)
401 Template = Other.Template;
402 SymbolInfo::merge(std::move(Other));
403}
404
406 assert(mergeable(Other));
407 if (!IsType)
408 IsType = Other.IsType;
409 if (ConstraintExpression.empty())
410 ConstraintExpression = std::move(Other.ConstraintExpression);
411 if (Template.Constraints.empty())
412 Template.Constraints = std::move(Other.Template.Constraints);
413 if (Template.Params.empty())
414 Template.Params = std::move(Other.Template.Params);
415 SymbolInfo::merge(std::move(Other));
416}
417
418void VarInfo::merge(VarInfo &&Other) {
419 assert(mergeable(Other));
420 if (!IsStatic)
421 IsStatic = Other.IsStatic;
422 if (Type.Type.USR == EmptySID && Type.Type.Name == "")
423 Type = std::move(Other.Type);
424 SymbolInfo::merge(std::move(Other));
425}
426
428
430 bool IsVirtual, AccessSpecifier Access,
431 bool IsParent)
434
435StringRef Info::extractName() const {
436 if (!Name.empty())
437 return Name;
438
439 switch (IT) {
441 // Cover the case where the project contains a base namespace called
442 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
443 // namespace, which would conflict with the hard-coded global namespace name
444 // below.)
445 if (Name == "GlobalNamespace" && Namespace.empty())
446 return "@GlobalNamespace";
447 // The case of anonymous namespaces is taken care of in serialization,
448 // so here we can safely assume an unnamed namespace is the global
449 // one.
450 return "GlobalNamespace";
452 return internString("@nonymous_record_" + toHex(llvm::toStringRef(USR)));
454 return internString("@nonymous_enum_" + toHex(llvm::toStringRef(USR)));
456 return internString("@nonymous_typedef_" + toHex(llvm::toStringRef(USR)));
458 return internString("@nonymous_function_" + toHex(llvm::toStringRef(USR)));
460 return internString("@nonymous_concept_" + toHex(llvm::toStringRef(USR)));
462 return internString("@nonymous_variable_" + toHex(llvm::toStringRef(USR)));
464 return internString("@nonymous_friend_" + toHex(llvm::toStringRef(USR)));
466 return internString("@nonymous_" + toHex(llvm::toStringRef(USR)));
467 }
468 llvm_unreachable("Invalid InfoType.");
469 return "";
470}
471
472// Order is based on the Name attribute: case insensitive order
473bool Index::operator<(const Index &Other) const {
474 // Start with case-insensitive (e.g., 'apple' < 'Zebra').
475 // This prevents 'Zebra' from appearing before 'apple' due to ASCII values,
476 // where uppercase letters have a lower numeric value than lowercase.
477 int Cmp = Name.compare_insensitive(Other.Name);
478 if (Cmp != 0)
479 return Cmp < 0;
480
481 // If names are identical, we fall back to standard string comparison where
482 // uppercase precedes lowercase (e.g., 'Apple' < 'apple').
483 return Name < Other.Name;
484}
485
487 OwningVec<const Index *> SortedChildren;
488 SortedChildren.reserve(Children.size());
489 for (const auto &[_, C] : Children)
490 SortedChildren.push_back(&C);
491 llvm::sort(SortedChildren,
492 [](const Index *A, const Index *B) { return *A < *B; });
493 return SortedChildren;
494}
495
497 for (auto &[_, C] : Children)
498 C.sort();
499}
500
501ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
502 StringRef ProjectName, bool PublicOnly,
503 StringRef OutDirectory, StringRef SourceRoot,
504 StringRef RepositoryUrl,
505 StringRef RepositoryLinePrefix, StringRef Base,
506 std::vector<std::string> UserStylesheets,
507 clang::DiagnosticsEngine &Diags,
513 llvm::SmallString<128> SourceRootDir(SourceRoot);
514 if (SourceRoot.empty())
515 // If no SourceRoot was provided the current path is used as the default
516 llvm::sys::fs::current_path(SourceRootDir);
517 this->SourceRoot = std::string(SourceRootDir);
518 if (!RepositoryUrl.empty()) {
519 this->RepositoryUrl = std::string(RepositoryUrl);
520 if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
521 !RepositoryUrl.starts_with("https://"))
522 this->RepositoryUrl->insert(0, "https://");
523
524 if (!RepositoryLinePrefix.empty())
525 this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);
526 }
527}
528
530 Namespaces.sort();
531 llvm::sort(Records);
532 llvm::sort(Functions);
533 llvm::sort(Enums);
534 llvm::sort(Typedefs);
535 llvm::sort(Concepts);
536 llvm::sort(Variables);
537}
538} // namespace doc
539} // namespace clang
llvm::Expected< OwnedPtr< Info > > mergeInfos(OwningPtrArray< Info > &Values)
static llvm::SmallString< 64 > calculateRelativeFilePath(const InfoType &Type, const StringRef &Path, const StringRef &Name, const StringRef &CurrentPath)
std::unique_ptr< T > OwnedPtr
thread_local llvm::BumpPtrAllocator PersistentArena
static void reduceChildren(llvm::simple_ilist< T > &Children, llvm::simple_ilist< T > &&ChildrenToMerge)
T * getPtr(const OwnedPtr< T > &O)
static int getChildIndexIfExists(OwningVec< T > &Children, T &ChildToMerge)
ConcurrentStringPool & getGlobalStringPool()
CommentKind stringToCommentKind(llvm::StringRef KindStr)
std::vector< OwnedPtr< T > > OwningPtrArray
thread_local llvm::BumpPtrAllocator TransientArena
StringRef internString(const Twine &T)
static llvm::Expected< OwnedPtr< Info > > reduce(OwningPtrArray< Info > &Values)
std::vector< T > OwningVec
OwnedPtr< T > allocatePtr(Args &&...args)
std::array< uint8_t, 20 > SymbolID
llvm::StringRef commentKindToString(CommentKind Kind)
static 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
llvm::ArrayRef< StringRef > Args
llvm::ArrayRef< StringRef > AttrValues
bool operator<(const CommentInfo &Other) const
bool operator==(const CommentInfo &Other) const
llvm::ArrayRef< StringRef > AttrKeys
OwningPtrVec< CommentInfo > Children
void merge(ConceptInfo &&I)
llvm::SmallVector< EnumValueInfo, 4 > Members
void merge(EnumInfo &&I)
void merge(FriendInfo &&Other)
bool mergeable(const FriendInfo &Other)
FunctionInfo(SymbolID USR=SymbolID())
llvm::SmallVector< FieldTypeInfo, 4 > Params
void merge(FunctionInfo &&I)
std::optional< TemplateInfo > Template
OwningVec< const Index * > getSortedChildren() const
bool operator<(const Index &Other) const
llvm::StringMap< Index > Children
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)
OwningVec< CommentInfo > Description
StringRef extractName() const
StringRef DocumentationFileName
void mergeBase(Info &&I)
StringRef getFileBaseName() const
Returns the basename that should be used for this Info.
llvm::SmallVector< Reference, 4 > Namespace
NamespaceInfo(SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
void merge(NamespaceInfo &&I)
OwningVec< BaseRecordInfo > Bases
llvm::SmallVector< MemberTypeInfo, 4 > Members
RecordInfo(SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
std::optional< TemplateInfo > Template
llvm::SmallVector< Reference, 4 > VirtualParents
llvm::SmallVector< Reference, 4 > Parents
void merge(RecordInfo &&I)
OwningVec< FriendInfo > Friends
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.
OwningVec< FunctionInfo > Functions
OwningVec< TypedefInfo > Typedefs
OwningVec< EnumInfo > Enums
OwningVec< Reference > Records
OwningVec< ConceptInfo > Concepts
OwningVec< VarInfo > Variables
llvm::simple_ilist< Reference > Namespaces
SymbolInfo(InfoType IT, SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
llvm::SmallVector< Location, 2 > Loc
std::optional< Location > DefLoc
void merge(TypedefInfo &&I)
std::optional< TemplateInfo > Template
TypedefInfo(SymbolID USR=SymbolID())
void merge(VarInfo &&I)