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
30CommentKind stringToCommentKind(llvm::StringRef KindStr) {
31 static const llvm::StringMap<CommentKind> KindMap = {
32 {"FullComment", CommentKind::CK_FullComment},
33 {"ParagraphComment", CommentKind::CK_ParagraphComment},
34 {"TextComment", CommentKind::CK_TextComment},
35 {"InlineCommandComment", CommentKind::CK_InlineCommandComment},
36 {"HTMLStartTagComment", CommentKind::CK_HTMLStartTagComment},
37 {"HTMLEndTagComment", CommentKind::CK_HTMLEndTagComment},
38 {"BlockCommandComment", CommentKind::CK_BlockCommandComment},
39 {"ParamCommandComment", CommentKind::CK_ParamCommandComment},
40 {"TParamCommandComment", CommentKind::CK_TParamCommandComment},
41 {"VerbatimBlockComment", CommentKind::CK_VerbatimBlockComment},
42 {"VerbatimBlockLineComment", CommentKind::CK_VerbatimBlockLineComment},
43 {"VerbatimLineComment", CommentKind::CK_VerbatimLineComment},
44 };
45
46 auto It = KindMap.find(KindStr);
47 if (It != KindMap.end()) {
48 return It->second;
49 }
51}
52
53llvm::StringRef commentKindToString(CommentKind Kind) {
54 switch (Kind) {
56 return "FullComment";
58 return "ParagraphComment";
60 return "TextComment";
62 return "InlineCommandComment";
64 return "HTMLStartTagComment";
66 return "HTMLEndTagComment";
68 return "BlockCommandComment";
70 return "ParamCommandComment";
72 return "TParamCommandComment";
74 return "VerbatimBlockComment";
76 return "VerbatimBlockLineComment";
78 return "VerbatimLineComment";
80 return "Unknown";
81 }
82 llvm_unreachable("Unhandled CommentKind");
83}
84
85const SymbolID EmptySID = SymbolID();
86
87template <typename T>
88static llvm::Expected<OwnedPtr<Info>> reduce(OwningPtrArray<Info> &Values) {
89 if (Values.empty() || !Values[0])
90 return llvm::createStringError(llvm::inconvertibleErrorCode(),
91 "no value to reduce");
92 OwnedPtr<Info> Merged = allocatePtr<T>(Values[0]->USR);
93 T *Tmp = static_cast<T *>(getPtr(Merged));
94 for (auto &I : Values)
95 Tmp->merge(std::move(*static_cast<T *>(getPtr(I))));
96 return std::move(Merged);
97}
98
99// Return the index of the matching child in the vector, or -1 if merge is not
100// necessary.
101template <typename T>
102static int getChildIndexIfExists(OwningVec<T> &Children, T &ChildToMerge) {
103 for (unsigned long I = 0; I < Children.size(); I++) {
104 if (ChildToMerge.USR == Children[I].USR)
105 return I;
106 }
107 return -1;
108}
109
110template <typename T>
111static void reduceChildren(OwningVec<T> &Children,
112 OwningVec<T> &&ChildrenToMerge) {
113 for (auto &ChildToMerge : ChildrenToMerge) {
114 int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);
115 if (MergeIdx == -1) {
116 Children.push_back(std::move(ChildToMerge));
117 continue;
118 }
119 Children[MergeIdx].merge(std::move(ChildToMerge));
120 }
121}
122
123// Dispatch function.
124llvm::Expected<OwnedPtr<Info>> mergeInfos(OwningPtrArray<Info> &Values) {
125 if (Values.empty() || !Values[0])
126 return llvm::createStringError(llvm::inconvertibleErrorCode(),
127 "no info values to merge");
128
129 switch (Values[0]->IT) {
131 return reduce<NamespaceInfo>(Values);
133 return reduce<RecordInfo>(Values);
135 return reduce<EnumInfo>(Values);
137 return reduce<FunctionInfo>(Values);
139 return reduce<TypedefInfo>(Values);
141 return reduce<ConceptInfo>(Values);
143 return reduce<VarInfo>(Values);
145 return reduce<FriendInfo>(Values);
147 return llvm::createStringError(llvm::inconvertibleErrorCode(),
148 "unexpected info type");
149 }
150 llvm_unreachable("unhandled enumerator");
151}
152
153bool CommentInfo::operator==(const CommentInfo &Other) const {
154 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
156 auto SecondCI =
157 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
158 Other.ParamName, Other.CloseName, Other.SelfClosing,
159 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
160
161 if (FirstCI != SecondCI || Children.size() != Other.Children.size())
162 return false;
163
164 return std::equal(Children.begin(), Children.end(), Other.Children.begin(),
165 llvm::deref<std::equal_to<>>{});
166}
167
168bool CommentInfo::operator<(const CommentInfo &Other) const {
169 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
171 auto SecondCI =
172 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
173 Other.ParamName, Other.CloseName, Other.SelfClosing,
174 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
175
176 if (FirstCI < SecondCI)
177 return true;
178
179 if (FirstCI == SecondCI) {
180 return std::lexicographical_compare(
181 Children.begin(), Children.end(), Other.Children.begin(),
182 Other.Children.end(), llvm::deref<std::less<>>());
183 }
184
185 return false;
186}
187
188static llvm::SmallString<64>
189calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
190 const StringRef &Name, const StringRef &CurrentPath) {
191 llvm::SmallString<64> FilePath;
192
193 if (CurrentPath != Path) {
194 // iterate back to the top
195 for (llvm::sys::path::const_iterator I =
196 llvm::sys::path::begin(CurrentPath);
197 I != llvm::sys::path::end(CurrentPath); ++I)
198 llvm::sys::path::append(FilePath, "..");
199 llvm::sys::path::append(FilePath, Path);
200 }
201
202 // Namespace references have a Path to the parent namespace, but
203 // the file is actually in the subdirectory for the namespace.
204 if (Type == doc::InfoType::IT_namespace)
205 llvm::sys::path::append(FilePath, Name);
206
207 return llvm::sys::path::relative_path(FilePath);
208}
209
210llvm::SmallString<64>
211Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
212 return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
213}
214
215llvm::SmallString<16> Reference::getFileBaseName() const {
217 return llvm::SmallString<16>("index");
218
219 return Name;
220}
221
222llvm::SmallString<64>
223Info::getRelativeFilePath(const StringRef &CurrentPath) const {
224 return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
225}
226
227llvm::SmallString<16> Info::getFileBaseName() const {
229 return llvm::SmallString<16>("index");
230
231 return extractName();
232}
233
234bool Reference::mergeable(const Reference &Other) {
235 return RefType == Other.RefType && USR == Other.USR;
236}
237
239 assert(mergeable(Other));
240 if (Name.empty())
241 Name = Other.Name;
242 if (Path.empty())
243 Path = Other.Path;
244 if (DocumentationFileName.empty())
245 DocumentationFileName = Other.DocumentationFileName;
246}
247
249 return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;
250}
251
253 assert(mergeable(Other));
254 Ref.merge(std::move(Other.Ref));
255 SymbolInfo::merge(std::move(Other));
256}
257
258void Info::mergeBase(Info &&Other) {
259 assert(mergeable(Other));
260 if (USR == EmptySID)
261 USR = Other.USR;
262 if (Name == "")
263 Name = Other.Name;
264 if (Path == "")
265 Path = Other.Path;
266 if (Namespace.empty())
267 Namespace = std::move(Other.Namespace);
268 // Unconditionally extend the description, since each decl may have a comment.
269 std::move(Other.Description.begin(), Other.Description.end(),
270 std::back_inserter(Description));
271 llvm::sort(Description);
272 auto Last = llvm::unique(Description);
273 Description.erase(Last, Description.end());
274 if (ParentUSR == EmptySID)
275 ParentUSR = Other.ParentUSR;
276 if (DocumentationFileName.empty())
277 DocumentationFileName = Other.DocumentationFileName;
278}
279
280bool Info::mergeable(const Info &Other) {
281 return IT == Other.IT && USR == Other.USR;
282}
283
284void SymbolInfo::merge(SymbolInfo &&Other) {
285 assert(mergeable(Other));
286 if (!DefLoc)
287 DefLoc = std::move(Other.DefLoc);
288 // Unconditionally extend the list of locations, since we want all of them.
289 std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
290 llvm::sort(Loc);
291 auto *Last = llvm::unique(Loc);
292 Loc.erase(Last, Loc.end());
293 mergeBase(std::move(Other));
294 if (MangledName.empty())
295 MangledName = std::move(Other.MangledName);
296}
297
300
302 assert(mergeable(Other));
303 // Reduce children if necessary.
304 reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
305 reduceChildren(Children.Records, std::move(Other.Children.Records));
306 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
307 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
308 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
309 reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
310 reduceChildren(Children.Variables, std::move(Other.Children.Variables));
311 mergeBase(std::move(Other));
312}
313
316
318 assert(mergeable(Other));
319 if (!llvm::to_underlying(TagType))
320 TagType = Other.TagType;
321 IsTypeDef = IsTypeDef || Other.IsTypeDef;
322 if (Members.empty())
323 Members = std::move(Other.Members);
324 if (Bases.empty())
325 Bases = std::move(Other.Bases);
326 if (Parents.empty())
327 Parents = std::move(Other.Parents);
328 if (VirtualParents.empty())
329 VirtualParents = std::move(Other.VirtualParents);
330 if (Friends.empty())
331 Friends = std::move(Other.Friends);
332 // Reduce children if necessary.
333 reduceChildren(Children.Records, std::move(Other.Children.Records));
334 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
335 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
336 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
337 SymbolInfo::merge(std::move(Other));
338 if (!Template)
339 Template = Other.Template;
340}
341
343 assert(mergeable(Other));
344 if (!Scoped)
345 Scoped = Other.Scoped;
346 if (Members.empty())
347 Members = std::move(Other.Members);
348 SymbolInfo::merge(std::move(Other));
349}
350
352 assert(mergeable(Other));
353 if (!IsMethod)
354 IsMethod = Other.IsMethod;
355 if (!Access)
356 Access = Other.Access;
357 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
358 ReturnType = std::move(Other.ReturnType);
359 if (Parent.USR == EmptySID && Parent.Name == "")
360 Parent = std::move(Other.Parent);
361 if (Params.empty())
362 Params = std::move(Other.Params);
363 SymbolInfo::merge(std::move(Other));
364 if (!Template)
365 Template = Other.Template;
366}
367
369 assert(mergeable(Other));
370 if (!IsUsing)
371 IsUsing = Other.IsUsing;
372 if (Underlying.Type.Name == "")
373 Underlying = Other.Underlying;
374 if (!Template)
375 Template = Other.Template;
376 SymbolInfo::merge(std::move(Other));
377}
378
380 assert(mergeable(Other));
381 if (!IsType)
382 IsType = Other.IsType;
383 if (ConstraintExpression.empty())
384 ConstraintExpression = std::move(Other.ConstraintExpression);
385 if (Template.Constraints.empty())
386 Template.Constraints = std::move(Other.Template.Constraints);
387 if (Template.Params.empty())
388 Template.Params = std::move(Other.Template.Params);
389 SymbolInfo::merge(std::move(Other));
390}
391
392void VarInfo::merge(VarInfo &&Other) {
393 assert(mergeable(Other));
394 if (!IsStatic)
395 IsStatic = Other.IsStatic;
396 if (Type.Type.USR == EmptySID && Type.Type.Name == "")
397 Type = std::move(Other.Type);
398 SymbolInfo::merge(std::move(Other));
399}
400
402
404 bool IsVirtual, AccessSpecifier Access,
405 bool IsParent)
408
409llvm::SmallString<16> Info::extractName() const {
410 if (!Name.empty())
411 return Name;
412
413 switch (IT) {
415 // Cover the case where the project contains a base namespace called
416 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
417 // namespace, which would conflict with the hard-coded global namespace name
418 // below.)
419 if (Name == "GlobalNamespace" && Namespace.empty())
420 return llvm::SmallString<16>("@GlobalNamespace");
421 // The case of anonymous namespaces is taken care of in serialization,
422 // so here we can safely assume an unnamed namespace is the global
423 // one.
424 return llvm::SmallString<16>("GlobalNamespace");
426 return llvm::SmallString<16>("@nonymous_record_" +
427 toHex(llvm::toStringRef(USR)));
429 return llvm::SmallString<16>("@nonymous_enum_" +
430 toHex(llvm::toStringRef(USR)));
432 return llvm::SmallString<16>("@nonymous_typedef_" +
433 toHex(llvm::toStringRef(USR)));
435 return llvm::SmallString<16>("@nonymous_function_" +
436 toHex(llvm::toStringRef(USR)));
438 return llvm::SmallString<16>("@nonymous_concept_" +
439 toHex(llvm::toStringRef(USR)));
441 return llvm::SmallString<16>("@nonymous_variable_" +
442 toHex(llvm::toStringRef(USR)));
444 return llvm::SmallString<16>("@nonymous_friend_" +
445 toHex(llvm::toStringRef(USR)));
447 return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
448 }
449 llvm_unreachable("Invalid InfoType.");
450 return llvm::SmallString<16>("");
451}
452
453// Order is based on the Name attribute: case insensitive order
454bool Index::operator<(const Index &Other) const {
455 // Loop through each character of both strings
456 for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
457 // Compare them after converting both to lower case
458 int D = tolower(Name[I]) - tolower(Other.Name[I]);
459 if (D == 0)
460 continue;
461 return D < 0;
462 }
463 // If both strings have the size it means they would be equal if changed to
464 // lower case. In here, lower case will be smaller than upper case
465 // Example: string < stRing = true
466 // This is the opposite of how operator < handles strings
467 if (Name.size() == Other.Name.size())
468 return Name > Other.Name;
469 // If they are not the same size; the shorter string is smaller
470 return Name.size() < Other.Name.size();
471}
472
474 OwningVec<const Index *> SortedChildren;
475 SortedChildren.reserve(Children.size());
476 for (const auto &[_, C] : Children)
477 SortedChildren.push_back(&C);
478 llvm::sort(SortedChildren,
479 [](const Index *A, const Index *B) { return *A < *B; });
480 return SortedChildren;
481}
482
484 for (auto &[_, C] : Children)
485 C.sort();
486}
487
488ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
489 StringRef ProjectName, bool PublicOnly,
490 StringRef OutDirectory, StringRef SourceRoot,
491 StringRef RepositoryUrl,
492 StringRef RepositoryLinePrefix, StringRef Base,
493 std::vector<std::string> UserStylesheets,
494 clang::DiagnosticsEngine &Diags,
500 llvm::SmallString<128> SourceRootDir(SourceRoot);
501 if (SourceRoot.empty())
502 // If no SourceRoot was provided the current path is used as the default
503 llvm::sys::fs::current_path(SourceRootDir);
504 this->SourceRoot = std::string(SourceRootDir);
505 if (!RepositoryUrl.empty()) {
506 this->RepositoryUrl = std::string(RepositoryUrl);
507 if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
508 !RepositoryUrl.starts_with("https://"))
509 this->RepositoryUrl->insert(0, "https://");
510
511 if (!RepositoryLinePrefix.empty())
512 this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);
513 }
514}
515
517 llvm::sort(Namespaces);
518 llvm::sort(Records);
519 llvm::sort(Functions);
520 llvm::sort(Enums);
521 llvm::sort(Typedefs);
522 llvm::sort(Concepts);
523 llvm::sort(Variables);
524}
525} // namespace doc
526} // 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
T * getPtr(const OwnedPtr< T > &O)
static int getChildIndexIfExists(OwningVec< T > &Children, T &ChildToMerge)
static void reduceChildren(OwningVec< T > &Children, OwningVec< T > &&ChildrenToMerge)
CommentKind stringToCommentKind(llvm::StringRef KindStr)
std::vector< OwnedPtr< T > > OwningPtrArray
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
SmallString< 8 > Direction
bool operator<(const CommentInfo &Other) const
llvm::SmallVector< SmallString< 16 >, 4 > AttrValues
SmallString< 16 > CloseName
bool operator==(const CommentInfo &Other) const
SmallString< 16 > Name
SmallString< 64 > Text
OwningPtrVec< CommentInfo > Children
llvm::SmallVector< SmallString< 16 >, 4 > AttrKeys
llvm::SmallVector< SmallString< 16 >, 4 > Args
SmallString< 16 > ParamName
void merge(ConceptInfo &&I)
SmallString< 16 > ConstraintExpression
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
SmallString< 16 > DocumentationFileName
Info(InfoType IT=InfoType::IT_default, SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
bool mergeable(const Info &Other)
OwningVec< CommentInfo > Description
SmallString< 16 > Name
llvm::SmallString< 16 > getFileBaseName() const
Returns the basename that should be used for this Info.
llvm::SmallString< 128 > Path
void mergeBase(Info &&I)
llvm::SmallString< 16 > extractName() const
llvm::SmallString< 64 > getRelativeFilePath(const StringRef &CurrentPath) const
Returns the file path for this Info relative to CurrentPath.
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)
llvm::SmallString< 128 > Path
llvm::SmallString< 64 > getRelativeFilePath(const StringRef &CurrentPath) const
Returns the path for this Reference relative to CurrentPath.
llvm::SmallString< 16 > getFileBaseName() const
Returns the basename that should be used for this Reference.
SmallString< 16 > DocumentationFileName
SmallString< 16 > Name
OwningVec< FunctionInfo > Functions
OwningVec< TypedefInfo > Typedefs
OwningVec< EnumInfo > Enums
OwningVec< Reference > Records
OwningVec< Reference > Namespaces
OwningVec< ConceptInfo > Concepts
OwningVec< VarInfo > Variables
SymbolInfo(InfoType IT, SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
llvm::SmallVector< Location, 2 > Loc
std::optional< Location > DefLoc
SmallString< 16 > MangledName
void merge(TypedefInfo &&I)
std::optional< TemplateInfo > Template
TypedefInfo(SymbolID USR=SymbolID())
void merge(VarInfo &&I)