clang-tools 22.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<std::unique_ptr<Info>>
89reduce(std::vector<std::unique_ptr<Info>> &Values) {
90 if (Values.empty() || !Values[0])
91 return llvm::createStringError(llvm::inconvertibleErrorCode(),
92 "no value to reduce");
93 std::unique_ptr<Info> Merged = std::make_unique<T>(Values[0]->USR);
94 T *Tmp = static_cast<T *>(Merged.get());
95 for (auto &I : Values)
96 Tmp->merge(std::move(*static_cast<T *>(I.get())));
97 return std::move(Merged);
98}
99
100// Return the index of the matching child in the vector, or -1 if merge is not
101// necessary.
102template <typename T>
103static int getChildIndexIfExists(std::vector<T> &Children, T &ChildToMerge) {
104 for (unsigned long I = 0; I < Children.size(); I++) {
105 if (ChildToMerge.USR == Children[I].USR)
106 return I;
107 }
108 return -1;
109}
110
111template <typename T>
112static void reduceChildren(std::vector<T> &Children,
113 std::vector<T> &&ChildrenToMerge) {
114 for (auto &ChildToMerge : ChildrenToMerge) {
115 int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);
116 if (MergeIdx == -1) {
117 Children.push_back(std::move(ChildToMerge));
118 continue;
119 }
120 Children[MergeIdx].merge(std::move(ChildToMerge));
121 }
122}
123
124// Dispatch function.
125llvm::Expected<std::unique_ptr<Info>>
126mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
127 if (Values.empty() || !Values[0])
128 return llvm::createStringError(llvm::inconvertibleErrorCode(),
129 "no info values to merge");
130
131 switch (Values[0]->IT) {
133 return reduce<NamespaceInfo>(Values);
135 return reduce<RecordInfo>(Values);
137 return reduce<EnumInfo>(Values);
139 return reduce<FunctionInfo>(Values);
141 return reduce<TypedefInfo>(Values);
143 return reduce<ConceptInfo>(Values);
145 return reduce<VarInfo>(Values);
147 return reduce<FriendInfo>(Values);
149 return llvm::createStringError(llvm::inconvertibleErrorCode(),
150 "unexpected info type");
151 }
152 llvm_unreachable("unhandled enumerator");
153}
154
155bool CommentInfo::operator==(const CommentInfo &Other) const {
156 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
158 auto SecondCI =
159 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
160 Other.ParamName, Other.CloseName, Other.SelfClosing,
161 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
162
163 if (FirstCI != SecondCI || Children.size() != Other.Children.size())
164 return false;
165
166 return std::equal(Children.begin(), Children.end(), Other.Children.begin(),
167 llvm::deref<std::equal_to<>>{});
168}
169
170bool CommentInfo::operator<(const CommentInfo &Other) const {
171 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
173 auto SecondCI =
174 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
175 Other.ParamName, Other.CloseName, Other.SelfClosing,
176 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
177
178 if (FirstCI < SecondCI)
179 return true;
180
181 if (FirstCI == SecondCI) {
182 return std::lexicographical_compare(
183 Children.begin(), Children.end(), Other.Children.begin(),
184 Other.Children.end(), llvm::deref<std::less<>>());
185 }
186
187 return false;
188}
189
190static llvm::SmallString<64>
191calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
192 const StringRef &Name, const StringRef &CurrentPath) {
193 llvm::SmallString<64> FilePath;
194
195 if (CurrentPath != Path) {
196 // iterate back to the top
197 for (llvm::sys::path::const_iterator I =
198 llvm::sys::path::begin(CurrentPath);
199 I != llvm::sys::path::end(CurrentPath); ++I)
200 llvm::sys::path::append(FilePath, "..");
201 llvm::sys::path::append(FilePath, Path);
202 }
203
204 // Namespace references have a Path to the parent namespace, but
205 // the file is actually in the subdirectory for the namespace.
206 if (Type == doc::InfoType::IT_namespace)
207 llvm::sys::path::append(FilePath, Name);
208
209 return llvm::sys::path::relative_path(FilePath);
210}
211
212llvm::SmallString<64>
213Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
214 return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
215}
216
217llvm::SmallString<16> Reference::getFileBaseName() const {
219 return llvm::SmallString<16>("index");
220
221 return Name;
222}
223
224llvm::SmallString<64>
225Info::getRelativeFilePath(const StringRef &CurrentPath) const {
226 return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
227}
228
229llvm::SmallString<16> Info::getFileBaseName() const {
231 return llvm::SmallString<16>("index");
232
233 return extractName();
234}
235
236bool Reference::mergeable(const Reference &Other) {
237 return RefType == Other.RefType && USR == Other.USR;
238}
239
241 assert(mergeable(Other));
242 if (Name.empty())
243 Name = Other.Name;
244 if (Path.empty())
245 Path = Other.Path;
246 if (DocumentationFileName.empty())
247 DocumentationFileName = Other.DocumentationFileName;
248}
249
251 return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;
252}
253
255 assert(mergeable(Other));
256 Ref.merge(std::move(Other.Ref));
257 SymbolInfo::merge(std::move(Other));
258}
259
260void Info::mergeBase(Info &&Other) {
261 assert(mergeable(Other));
262 if (USR == EmptySID)
263 USR = Other.USR;
264 if (Name == "")
265 Name = Other.Name;
266 if (Path == "")
267 Path = Other.Path;
268 if (Namespace.empty())
269 Namespace = std::move(Other.Namespace);
270 // Unconditionally extend the description, since each decl may have a comment.
271 std::move(Other.Description.begin(), Other.Description.end(),
272 std::back_inserter(Description));
273 llvm::sort(Description);
274 auto Last = llvm::unique(Description);
275 Description.erase(Last, Description.end());
276 if (ParentUSR == EmptySID)
277 ParentUSR = Other.ParentUSR;
278 if (DocumentationFileName.empty())
279 DocumentationFileName = Other.DocumentationFileName;
280}
281
282bool Info::mergeable(const Info &Other) {
283 return IT == Other.IT && USR == Other.USR;
284}
285
286void SymbolInfo::merge(SymbolInfo &&Other) {
287 assert(mergeable(Other));
288 if (!DefLoc)
289 DefLoc = std::move(Other.DefLoc);
290 // Unconditionally extend the list of locations, since we want all of them.
291 std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
292 llvm::sort(Loc);
293 auto *Last = llvm::unique(Loc);
294 Loc.erase(Last, Loc.end());
295 mergeBase(std::move(Other));
296 if (MangledName.empty())
297 MangledName = std::move(Other.MangledName);
298}
299
302
304 assert(mergeable(Other));
305 // Reduce children if necessary.
306 reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
307 reduceChildren(Children.Records, std::move(Other.Children.Records));
308 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
309 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
310 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
311 reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
312 reduceChildren(Children.Variables, std::move(Other.Children.Variables));
313 mergeBase(std::move(Other));
314}
315
318
320 assert(mergeable(Other));
321 if (!llvm::to_underlying(TagType))
322 TagType = Other.TagType;
323 IsTypeDef = IsTypeDef || Other.IsTypeDef;
324 if (Members.empty())
325 Members = std::move(Other.Members);
326 if (Bases.empty())
327 Bases = std::move(Other.Bases);
328 if (Parents.empty())
329 Parents = std::move(Other.Parents);
330 if (VirtualParents.empty())
331 VirtualParents = std::move(Other.VirtualParents);
332 if (Friends.empty())
333 Friends = std::move(Other.Friends);
334 // Reduce children if necessary.
335 reduceChildren(Children.Records, std::move(Other.Children.Records));
336 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
337 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
338 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
339 SymbolInfo::merge(std::move(Other));
340 if (!Template)
341 Template = Other.Template;
342}
343
345 assert(mergeable(Other));
346 if (!Scoped)
347 Scoped = Other.Scoped;
348 if (Members.empty())
349 Members = std::move(Other.Members);
350 SymbolInfo::merge(std::move(Other));
351}
352
354 assert(mergeable(Other));
355 if (!IsMethod)
356 IsMethod = Other.IsMethod;
357 if (!Access)
358 Access = Other.Access;
359 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
360 ReturnType = std::move(Other.ReturnType);
361 if (Parent.USR == EmptySID && Parent.Name == "")
362 Parent = std::move(Other.Parent);
363 if (Params.empty())
364 Params = std::move(Other.Params);
365 SymbolInfo::merge(std::move(Other));
366 if (!Template)
367 Template = Other.Template;
368}
369
371 assert(mergeable(Other));
372 if (!IsUsing)
373 IsUsing = Other.IsUsing;
374 if (Underlying.Type.Name == "")
375 Underlying = Other.Underlying;
376 if (!Template)
377 Template = Other.Template;
378 SymbolInfo::merge(std::move(Other));
379}
380
382 assert(mergeable(Other));
383 if (!IsType)
384 IsType = Other.IsType;
385 if (ConstraintExpression.empty())
386 ConstraintExpression = std::move(Other.ConstraintExpression);
387 if (Template.Constraints.empty())
388 Template.Constraints = std::move(Other.Template.Constraints);
389 if (Template.Params.empty())
390 Template.Params = std::move(Other.Template.Params);
391 SymbolInfo::merge(std::move(Other));
392}
393
394void VarInfo::merge(VarInfo &&Other) {
395 assert(mergeable(Other));
396 if (!IsStatic)
397 IsStatic = Other.IsStatic;
398 if (Type.Type.USR == EmptySID && Type.Type.Name == "")
399 Type = std::move(Other.Type);
400 SymbolInfo::merge(std::move(Other));
401}
402
404
406 bool IsVirtual, AccessSpecifier Access,
407 bool IsParent)
410
411llvm::SmallString<16> Info::extractName() const {
412 if (!Name.empty())
413 return Name;
414
415 switch (IT) {
417 // Cover the case where the project contains a base namespace called
418 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
419 // namespace, which would conflict with the hard-coded global namespace name
420 // below.)
421 if (Name == "GlobalNamespace" && Namespace.empty())
422 return llvm::SmallString<16>("@GlobalNamespace");
423 // The case of anonymous namespaces is taken care of in serialization,
424 // so here we can safely assume an unnamed namespace is the global
425 // one.
426 return llvm::SmallString<16>("GlobalNamespace");
428 return llvm::SmallString<16>("@nonymous_record_" +
429 toHex(llvm::toStringRef(USR)));
431 return llvm::SmallString<16>("@nonymous_enum_" +
432 toHex(llvm::toStringRef(USR)));
434 return llvm::SmallString<16>("@nonymous_typedef_" +
435 toHex(llvm::toStringRef(USR)));
437 return llvm::SmallString<16>("@nonymous_function_" +
438 toHex(llvm::toStringRef(USR)));
440 return llvm::SmallString<16>("@nonymous_concept_" +
441 toHex(llvm::toStringRef(USR)));
443 return llvm::SmallString<16>("@nonymous_variable_" +
444 toHex(llvm::toStringRef(USR)));
446 return llvm::SmallString<16>("@nonymous_friend_" +
447 toHex(llvm::toStringRef(USR)));
449 return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
450 }
451 llvm_unreachable("Invalid InfoType.");
452 return llvm::SmallString<16>("");
453}
454
455// Order is based on the Name attribute: case insensitive order
456bool Index::operator<(const Index &Other) const {
457 // Loop through each character of both strings
458 for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
459 // Compare them after converting both to lower case
460 int D = tolower(Name[I]) - tolower(Other.Name[I]);
461 if (D == 0)
462 continue;
463 return D < 0;
464 }
465 // If both strings have the size it means they would be equal if changed to
466 // lower case. In here, lower case will be smaller than upper case
467 // Example: string < stRing = true
468 // This is the opposite of how operator < handles strings
469 if (Name.size() == Other.Name.size())
470 return Name > Other.Name;
471 // If they are not the same size; the shorter string is smaller
472 return Name.size() < Other.Name.size();
473}
474
476 llvm::sort(Children);
477 for (auto &C : Children)
478 C.sort();
479}
480
481ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
482 StringRef ProjectName, bool PublicOnly,
483 StringRef OutDirectory, StringRef SourceRoot,
484 StringRef RepositoryUrl,
485 StringRef RepositoryLinePrefix, StringRef Base,
486 std::vector<std::string> UserStylesheets,
487 clang::DiagnosticsEngine &Diags,
488 bool FTimeTrace)
492 llvm::SmallString<128> SourceRootDir(SourceRoot);
493 if (SourceRoot.empty())
494 // If no SourceRoot was provided the current path is used as the default
495 llvm::sys::fs::current_path(SourceRootDir);
496 this->SourceRoot = std::string(SourceRootDir);
497 if (!RepositoryUrl.empty()) {
498 this->RepositoryUrl = std::string(RepositoryUrl);
499 if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
500 !RepositoryUrl.starts_with("https://"))
501 this->RepositoryUrl->insert(0, "https://");
502
503 if (!RepositoryLinePrefix.empty())
504 this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);
505 }
506}
507
509 llvm::sort(Namespaces);
510 llvm::sort(Records);
511 llvm::sort(Functions);
512 llvm::sort(Enums);
513 llvm::sort(Typedefs);
514 llvm::sort(Concepts);
515 llvm::sort(Variables);
516}
517} // namespace doc
518} // namespace clang
llvm::Expected< std::unique_ptr< Info > > mergeInfos(std::vector< std::unique_ptr< Info > > &Values)
static llvm::SmallString< 64 > calculateRelativeFilePath(const InfoType &Type, const StringRef &Path, const StringRef &Name, const StringRef &CurrentPath)
static void reduceChildren(std::vector< T > &Children, std::vector< T > &&ChildrenToMerge)
CommentKind stringToCommentKind(llvm::StringRef KindStr)
std::array< uint8_t, 20 > SymbolID
llvm::StringRef commentKindToString(CommentKind Kind)
static int getChildIndexIfExists(std::vector< T > &Children, T &ChildToMerge)
static llvm::Expected< std::unique_ptr< Info > > reduce(std::vector< std::unique_ptr< Info > > &Values)
static const SymbolID EmptySID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::optional< std::string > RepositoryUrl
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, bool FTimeTrace=false)
std::vector< std::string > UserStylesheets
tooling::ExecutionContext * ECtx
clang::DiagnosticsEngine & Diags
std::optional< std::string > RepositoryLinePrefix
SmallString< 8 > Direction
std::vector< std::unique_ptr< CommentInfo > > Children
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
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
std::vector< Index > Children
bool operator<(const Index &Other) const
SmallString< 16 > DocumentationFileName
Info(InfoType IT=InfoType::IT_default, SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
bool mergeable(const Info &Other)
SmallString< 16 > Name
llvm::SmallString< 16 > getFileBaseName() const
Returns the basename that should be used for this Info.
std::vector< CommentInfo > Description
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)
llvm::SmallVector< MemberTypeInfo, 4 > Members
RecordInfo(SymbolID USR=SymbolID(), StringRef Name=StringRef(), StringRef Path=StringRef())
std::optional< TemplateInfo > Template
std::vector< FriendInfo > Friends
llvm::SmallVector< Reference, 4 > VirtualParents
llvm::SmallVector< Reference, 4 > Parents
void merge(RecordInfo &&I)
std::vector< BaseRecordInfo > Bases
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
std::vector< Reference > Records
std::vector< TypedefInfo > Typedefs
std::vector< FunctionInfo > Functions
std::vector< Reference > Namespaces
std::vector< VarInfo > Variables
std::vector< EnumInfo > Enums
std::vector< ConceptInfo > Concepts
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)