blob: d08afbb96218907dd4c1be8ff8ffbe6a7bc96fcc [file] [log] [blame]
Julie Hockettd0f9a872018-06-04 17:22:201///===-- Representation.cpp - ClangDoc Representation -----------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:563// 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
Julie Hockettd0f9a872018-06-04 17:22:206//
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/Support/Error.h"
Julie Hockettd900ef02019-06-28 19:07:5624#include "llvm/Support/Path.h"
Julie Hockettd0f9a872018-06-04 17:22:2025
26namespace clang {
27namespace doc {
28
Julie Hockett8899c292018-08-02 20:10:1729namespace {
30
31const SymbolID EmptySID = SymbolID();
Julie Hockettd0f9a872018-06-04 17:22:2032
33template <typename T>
Julie Hockett8899c292018-08-02 20:10:1734llvm::Expected<std::unique_ptr<Info>>
35reduce(std::vector<std::unique_ptr<Info>> &Values) {
Paul Kirth30360d82022-07-22 17:34:5836 if (Values.empty() || !Values[0])
Fangrui Song18038062019-08-28 02:56:0337 return llvm::createStringError(llvm::inconvertibleErrorCode(),
38 "no value to reduce");
Jonas Devlieghere1c705d92019-08-14 23:52:2339 std::unique_ptr<Info> Merged = std::make_unique<T>(Values[0]->USR);
Julie Hockettd0f9a872018-06-04 17:22:2040 T *Tmp = static_cast<T *>(Merged.get());
41 for (auto &I : Values)
42 Tmp->merge(std::move(*static_cast<T *>(I.get())));
Julie Hockett8899c292018-08-02 20:10:1743 return std::move(Merged);
Julie Hockettd0f9a872018-06-04 17:22:2044}
45
Julie Hockett8899c292018-08-02 20:10:1746// Return the index of the matching child in the vector, or -1 if merge is not
47// necessary.
48template <typename T>
49int getChildIndexIfExists(std::vector<T> &Children, T &ChildToMerge) {
50 for (unsigned long I = 0; I < Children.size(); I++) {
51 if (ChildToMerge.USR == Children[I].USR)
52 return I;
53 }
54 return -1;
55}
56
Paul Kirth9470de62022-07-19 22:55:4157template <typename T>
58void reduceChildren(std::vector<T> &Children,
59 std::vector<T> &&ChildrenToMerge) {
Julie Hockett8899c292018-08-02 20:10:1760 for (auto &ChildToMerge : ChildrenToMerge) {
Paul Kirth9470de62022-07-19 22:55:4161 int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);
62 if (MergeIdx == -1) {
Julie Hockett8899c292018-08-02 20:10:1763 Children.push_back(std::move(ChildToMerge));
Diego Astiazarane27f7782019-08-12 18:42:4664 continue;
65 }
Paul Kirth9470de62022-07-19 22:55:4166 Children[MergeIdx].merge(std::move(ChildToMerge));
Brett Wilson21fb70c2022-10-14 21:28:4367 }
68}
69
Julie Hockett8899c292018-08-02 20:10:1770} // namespace
71
Julie Hockettd0f9a872018-06-04 17:22:2072// Dispatch function.
73llvm::Expected<std::unique_ptr<Info>>
74mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
Paul Kirth30360d82022-07-22 17:34:5875 if (Values.empty() || !Values[0])
Fangrui Song18038062019-08-28 02:56:0376 return llvm::createStringError(llvm::inconvertibleErrorCode(),
77 "no info values to merge");
Julie Hockettd0f9a872018-06-04 17:22:2078
79 switch (Values[0]->IT) {
80 case InfoType::IT_namespace:
81 return reduce<NamespaceInfo>(Values);
82 case InfoType::IT_record:
83 return reduce<RecordInfo>(Values);
84 case InfoType::IT_enum:
85 return reduce<EnumInfo>(Values);
86 case InfoType::IT_function:
87 return reduce<FunctionInfo>(Values);
Brett Wilson21fb70c2022-10-14 21:28:4388 case InfoType::IT_typedef:
89 return reduce<TypedefInfo>(Values);
Julie Hockettd0f9a872018-06-04 17:22:2090 default:
Fangrui Song18038062019-08-28 02:56:0391 return llvm::createStringError(llvm::inconvertibleErrorCode(),
92 "unexpected info type");
Julie Hockettd0f9a872018-06-04 17:22:2093 }
94}
95
Jens Massbergf88c6b92023-01-13 10:37:1396bool CommentInfo::operator==(const CommentInfo &Other) const {
97 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
98 SelfClosing, Explicit, AttrKeys, AttrValues, Args);
99 auto SecondCI =
100 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
101 Other.ParamName, Other.CloseName, Other.SelfClosing,
102 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
103
104 if (FirstCI != SecondCI || Children.size() != Other.Children.size())
105 return false;
106
107 return std::equal(Children.begin(), Children.end(), Other.Children.begin(),
108 llvm::deref<std::equal_to<>>{});
109}
110
111bool CommentInfo::operator<(const CommentInfo &Other) const {
112 auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
113 SelfClosing, Explicit, AttrKeys, AttrValues, Args);
114 auto SecondCI =
115 std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
116 Other.ParamName, Other.CloseName, Other.SelfClosing,
117 Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
118
119 if (FirstCI < SecondCI)
120 return true;
121
122 if (FirstCI == SecondCI) {
123 return std::lexicographical_compare(
124 Children.begin(), Children.end(), Other.Children.begin(),
125 Other.Children.end(), llvm::deref<std::less<>>());
126 }
127
128 return false;
129}
130
Petr Hosek7003f642020-03-07 01:33:56131static llvm::SmallString<64>
132calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
133 const StringRef &Name, const StringRef &CurrentPath) {
134 llvm::SmallString<64> FilePath;
135
136 if (CurrentPath != Path) {
137 // iterate back to the top
138 for (llvm::sys::path::const_iterator I =
139 llvm::sys::path::begin(CurrentPath);
140 I != llvm::sys::path::end(CurrentPath); ++I)
141 llvm::sys::path::append(FilePath, "..");
142 llvm::sys::path::append(FilePath, Path);
143 }
144
145 // Namespace references have a Path to the parent namespace, but
146 // the file is actually in the subdirectory for the namespace.
147 if (Type == doc::InfoType::IT_namespace)
148 llvm::sys::path::append(FilePath, Name);
149
150 return llvm::sys::path::relative_path(FilePath);
151}
152
153llvm::SmallString<64>
154Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
155 return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
156}
157
158llvm::SmallString<16> Reference::getFileBaseName() const {
159 if (RefType == InfoType::IT_namespace)
160 return llvm::SmallString<16>("index");
161
162 return Name;
163}
164
165llvm::SmallString<64>
166Info::getRelativeFilePath(const StringRef &CurrentPath) const {
167 return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
168}
169
170llvm::SmallString<16> Info::getFileBaseName() const {
171 if (IT == InfoType::IT_namespace)
172 return llvm::SmallString<16>("index");
173
174 return extractName();
175}
176
Diego Astiazarane27f7782019-08-12 18:42:46177bool Reference::mergeable(const Reference &Other) {
178 return RefType == Other.RefType && USR == Other.USR;
179}
180
181void Reference::merge(Reference &&Other) {
182 assert(mergeable(Other));
183 if (Name.empty())
184 Name = Other.Name;
185 if (Path.empty())
186 Path = Other.Path;
Diego Astiazarane27f7782019-08-12 18:42:46187}
188
Julie Hockettd0f9a872018-06-04 17:22:20189void Info::mergeBase(Info &&Other) {
190 assert(mergeable(Other));
191 if (USR == EmptySID)
192 USR = Other.USR;
193 if (Name == "")
194 Name = Other.Name;
Julie Hockett2c1c9a22019-07-12 18:32:00195 if (Path == "")
196 Path = Other.Path;
Julie Hockettd0f9a872018-06-04 17:22:20197 if (Namespace.empty())
198 Namespace = std::move(Other.Namespace);
199 // Unconditionally extend the description, since each decl may have a comment.
200 std::move(Other.Description.begin(), Other.Description.end(),
201 std::back_inserter(Description));
Benjamin Kramer4065e922020-03-28 18:19:55202 llvm::sort(Description);
Julie Hockett93a290f2019-06-28 18:17:58203 auto Last = std::unique(Description.begin(), Description.end());
204 Description.erase(Last, Description.end());
Julie Hockettd0f9a872018-06-04 17:22:20205}
206
207bool Info::mergeable(const Info &Other) {
Julie Hockett8899c292018-08-02 20:10:17208 return IT == Other.IT && USR == Other.USR;
Julie Hockettd0f9a872018-06-04 17:22:20209}
210
211void SymbolInfo::merge(SymbolInfo &&Other) {
212 assert(mergeable(Other));
213 if (!DefLoc)
214 DefLoc = std::move(Other.DefLoc);
215 // Unconditionally extend the list of locations, since we want all of them.
216 std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
Benjamin Kramer4065e922020-03-28 18:19:55217 llvm::sort(Loc);
Julie Hockett93a290f2019-06-28 18:17:58218 auto Last = std::unique(Loc.begin(), Loc.end());
219 Loc.erase(Last, Loc.end());
Julie Hockettd0f9a872018-06-04 17:22:20220 mergeBase(std::move(Other));
221}
222
Jens Massbergf88c6b92023-01-13 10:37:13223NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
224 : Info(InfoType::IT_namespace, USR, Name, Path) {}
225
Julie Hockettd0f9a872018-06-04 17:22:20226void NamespaceInfo::merge(NamespaceInfo &&Other) {
227 assert(mergeable(Other));
Julie Hockett8899c292018-08-02 20:10:17228 // Reduce children if necessary.
Brett Wilson21fb70c2022-10-14 21:28:43229 reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
230 reduceChildren(Children.Records, std::move(Other.Children.Records));
231 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
232 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
233 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
Julie Hockettd0f9a872018-06-04 17:22:20234 mergeBase(std::move(Other));
235}
236
Jens Massbergf88c6b92023-01-13 10:37:13237RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
238 : SymbolInfo(InfoType::IT_record, USR, Name, Path) {}
239
Julie Hockettd0f9a872018-06-04 17:22:20240void RecordInfo::merge(RecordInfo &&Other) {
241 assert(mergeable(Other));
Vlad Serebrennikovedd690b2023-11-03 17:45:39242 if (!llvm::to_underlying(TagType))
Julie Hockettd0f9a872018-06-04 17:22:20243 TagType = Other.TagType;
Brett Wilson68266822022-08-12 18:38:21244 IsTypeDef = IsTypeDef || Other.IsTypeDef;
Julie Hockettd0f9a872018-06-04 17:22:20245 if (Members.empty())
246 Members = std::move(Other.Members);
Diego Astiazaranba3d5952019-08-16 00:10:49247 if (Bases.empty())
248 Bases = std::move(Other.Bases);
Julie Hockettd0f9a872018-06-04 17:22:20249 if (Parents.empty())
250 Parents = std::move(Other.Parents);
251 if (VirtualParents.empty())
252 VirtualParents = std::move(Other.VirtualParents);
Julie Hockett8899c292018-08-02 20:10:17253 // Reduce children if necessary.
Brett Wilson21fb70c2022-10-14 21:28:43254 reduceChildren(Children.Records, std::move(Other.Children.Records));
255 reduceChildren(Children.Functions, std::move(Other.Children.Functions));
256 reduceChildren(Children.Enums, std::move(Other.Children.Enums));
257 reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
Julie Hockettd0f9a872018-06-04 17:22:20258 SymbolInfo::merge(std::move(Other));
Brett Wilson4a68bab2022-12-07 18:26:04259 if (!Template)
260 Template = Other.Template;
Julie Hockettd0f9a872018-06-04 17:22:20261}
262
263void EnumInfo::merge(EnumInfo &&Other) {
264 assert(mergeable(Other));
265 if (!Scoped)
266 Scoped = Other.Scoped;
267 if (Members.empty())
268 Members = std::move(Other.Members);
269 SymbolInfo::merge(std::move(Other));
270}
271
272void FunctionInfo::merge(FunctionInfo &&Other) {
273 assert(mergeable(Other));
274 if (!IsMethod)
275 IsMethod = Other.IsMethod;
276 if (!Access)
277 Access = Other.Access;
278 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
279 ReturnType = std::move(Other.ReturnType);
280 if (Parent.USR == EmptySID && Parent.Name == "")
281 Parent = std::move(Other.Parent);
282 if (Params.empty())
283 Params = std::move(Other.Params);
284 SymbolInfo::merge(std::move(Other));
Brett Wilson4a68bab2022-12-07 18:26:04285 if (!Template)
286 Template = Other.Template;
Julie Hockettd0f9a872018-06-04 17:22:20287}
288
Brett Wilson21fb70c2022-10-14 21:28:43289void TypedefInfo::merge(TypedefInfo &&Other) {
290 assert(mergeable(Other));
291 if (!IsUsing)
292 IsUsing = Other.IsUsing;
293 if (Underlying.Type.Name == "")
294 Underlying = Other.Underlying;
295 SymbolInfo::merge(std::move(Other));
296}
297
Jens Massbergf88c6b92023-01-13 10:37:13298BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
299
300BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,
301 bool IsVirtual, AccessSpecifier Access,
302 bool IsParent)
303 : RecordInfo(USR, Name, Path), IsVirtual(IsVirtual), Access(Access),
304 IsParent(IsParent) {}
305
Diego Astiazaran7dfe0bc2019-08-06 18:31:46306llvm::SmallString<16> Info::extractName() const {
Julie Hockettd900ef02019-06-28 19:07:56307 if (!Name.empty())
308 return Name;
309
310 switch (IT) {
311 case InfoType::IT_namespace:
312 // Cover the case where the project contains a base namespace called
313 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
314 // namespace, which would conflict with the hard-coded global namespace name
315 // below.)
316 if (Name == "GlobalNamespace" && Namespace.empty())
317 return llvm::SmallString<16>("@GlobalNamespace");
318 // The case of anonymous namespaces is taken care of in serialization,
319 // so here we can safely assume an unnamed namespace is the global
320 // one.
321 return llvm::SmallString<16>("GlobalNamespace");
322 case InfoType::IT_record:
323 return llvm::SmallString<16>("@nonymous_record_" +
324 toHex(llvm::toStringRef(USR)));
325 case InfoType::IT_enum:
326 return llvm::SmallString<16>("@nonymous_enum_" +
327 toHex(llvm::toStringRef(USR)));
Brett Wilson21fb70c2022-10-14 21:28:43328 case InfoType::IT_typedef:
329 return llvm::SmallString<16>("@nonymous_typedef_" +
330 toHex(llvm::toStringRef(USR)));
Julie Hockettd900ef02019-06-28 19:07:56331 case InfoType::IT_function:
332 return llvm::SmallString<16>("@nonymous_function_" +
333 toHex(llvm::toStringRef(USR)));
334 case InfoType::IT_default:
335 return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
336 }
337 llvm_unreachable("Invalid InfoType.");
338 return llvm::SmallString<16>("");
339}
340
Diego Astiazaranb7bb9fb2019-08-15 23:32:12341// Order is based on the Name attribute: case insensitive order
342bool Index::operator<(const Index &Other) const {
343 // Loop through each character of both strings
344 for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
345 // Compare them after converting both to lower case
346 int D = tolower(Name[I]) - tolower(Other.Name[I]);
347 if (D == 0)
348 continue;
349 return D < 0;
350 }
351 // If both strings have the size it means they would be equal if changed to
352 // lower case. In here, lower case will be smaller than upper case
353 // Example: string < stRing = true
354 // This is the opposite of how operator < handles strings
355 if (Name.size() == Other.Name.size())
356 return Name > Other.Name;
357 // If they are not the same size; the shorter string is smaller
358 return Name.size() < Other.Name.size();
359}
360
Diego Astiazaran7dfe0bc2019-08-06 18:31:46361void Index::sort() {
Benjamin Kramer4065e922020-03-28 18:19:55362 llvm::sort(Children);
Diego Astiazaran7dfe0bc2019-08-06 18:31:46363 for (auto &C : Children)
364 C.sort();
365}
366
Diego Astiazaran665e9672019-08-09 17:49:41367ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
Diego Astiazaran3550da72019-08-16 18:38:11368 StringRef ProjectName, bool PublicOnly,
369 StringRef OutDirectory, StringRef SourceRoot,
370 StringRef RepositoryUrl,
PeterChou1f14ad742024-06-27 20:22:01371 std::vector<std::string> UserStylesheets)
Diego Astiazaran3550da72019-08-16 18:38:11372 : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),
PeterChou1f14ad742024-06-27 20:22:01373 OutDirectory(OutDirectory), UserStylesheets(UserStylesheets) {
Diego Astiazaran77dc05b2019-08-15 23:20:42374 llvm::SmallString<128> SourceRootDir(SourceRoot);
375 if (SourceRoot.empty())
376 // If no SourceRoot was provided the current path is used as the default
377 llvm::sys::fs::current_path(SourceRootDir);
Kazu Hirata2b00d442024-01-27 07:46:24378 this->SourceRoot = std::string(SourceRootDir);
Diego Astiazaran665e9672019-08-09 17:49:41379 if (!RepositoryUrl.empty()) {
Benjamin Krameradcd0262020-01-28 19:23:46380 this->RepositoryUrl = std::string(RepositoryUrl);
Nicolas van Kempen5232cec2024-04-19 21:00:13381 if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
382 !RepositoryUrl.starts_with("https://"))
Diego Astiazaran665e9672019-08-09 17:49:41383 this->RepositoryUrl->insert(0, "https://");
384 }
385}
386
Julie Hockettd0f9a872018-06-04 17:22:20387} // namespace doc
388} // namespace clang