1 ///===-- Representation.cpp - ClangDoc Representation -----------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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.
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).
21 //===----------------------------------------------------------------------===//
22 #include "Representation.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/Path.h"
31 const SymbolID EmptySID
= SymbolID();
34 llvm::Expected
<std::unique_ptr
<Info
>>
35 reduce(std::vector
<std::unique_ptr
<Info
>> &Values
) {
36 if (Values
.empty() || !Values
[0])
37 return llvm::createStringError(llvm::inconvertibleErrorCode(),
38 "no value to reduce");
39 std::unique_ptr
<Info
> Merged
= std::make_unique
<T
>(Values
[0]->USR
);
40 T
*Tmp
= static_cast<T
*>(Merged
.get());
41 for (auto &I
: Values
)
42 Tmp
->merge(std::move(*static_cast<T
*>(I
.get())));
43 return std::move(Merged
);
46 // Return the index of the matching child in the vector, or -1 if merge is not
49 int getChildIndexIfExists(std::vector
<T
> &Children
, T
&ChildToMerge
) {
50 for (unsigned long I
= 0; I
< Children
.size(); I
++) {
51 if (ChildToMerge
.USR
== Children
[I
].USR
)
58 void reduceChildren(std::vector
<T
> &Children
,
59 std::vector
<T
> &&ChildrenToMerge
) {
60 for (auto &ChildToMerge
: ChildrenToMerge
) {
61 int MergeIdx
= getChildIndexIfExists(Children
, ChildToMerge
);
63 Children
.push_back(std::move(ChildToMerge
));
66 Children
[MergeIdx
].merge(std::move(ChildToMerge
));
73 llvm::Expected
<std::unique_ptr
<Info
>>
74 mergeInfos(std::vector
<std::unique_ptr
<Info
>> &Values
) {
75 if (Values
.empty() || !Values
[0])
76 return llvm::createStringError(llvm::inconvertibleErrorCode(),
77 "no info values to merge");
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
);
88 case InfoType::IT_typedef
:
89 return reduce
<TypedefInfo
>(Values
);
91 return llvm::createStringError(llvm::inconvertibleErrorCode(),
92 "unexpected info type");
96 bool CommentInfo::operator==(const CommentInfo
&Other
) const {
97 auto FirstCI
= std::tie(Kind
, Text
, Name
, Direction
, ParamName
, CloseName
,
98 SelfClosing
, Explicit
, AttrKeys
, AttrValues
, Args
);
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
);
104 if (FirstCI
!= SecondCI
|| Children
.size() != Other
.Children
.size())
107 return std::equal(Children
.begin(), Children
.end(), Other
.Children
.begin(),
108 llvm::deref
<std::equal_to
<>>{});
111 bool CommentInfo::operator<(const CommentInfo
&Other
) const {
112 auto FirstCI
= std::tie(Kind
, Text
, Name
, Direction
, ParamName
, CloseName
,
113 SelfClosing
, Explicit
, AttrKeys
, AttrValues
, Args
);
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
);
119 if (FirstCI
< SecondCI
)
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
<>>());
131 static llvm::SmallString
<64>
132 calculateRelativeFilePath(const InfoType
&Type
, const StringRef
&Path
,
133 const StringRef
&Name
, const StringRef
&CurrentPath
) {
134 llvm::SmallString
<64> FilePath
;
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
);
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
);
150 return llvm::sys::path::relative_path(FilePath
);
153 llvm::SmallString
<64>
154 Reference::getRelativeFilePath(const StringRef
&CurrentPath
) const {
155 return calculateRelativeFilePath(RefType
, Path
, Name
, CurrentPath
);
158 llvm::SmallString
<16> Reference::getFileBaseName() const {
159 if (RefType
== InfoType::IT_namespace
)
160 return llvm::SmallString
<16>("index");
165 llvm::SmallString
<64>
166 Info::getRelativeFilePath(const StringRef
&CurrentPath
) const {
167 return calculateRelativeFilePath(IT
, Path
, extractName(), CurrentPath
);
170 llvm::SmallString
<16> Info::getFileBaseName() const {
171 if (IT
== InfoType::IT_namespace
)
172 return llvm::SmallString
<16>("index");
174 return extractName();
177 bool Reference::mergeable(const Reference
&Other
) {
178 return RefType
== Other
.RefType
&& USR
== Other
.USR
;
181 void Reference::merge(Reference
&&Other
) {
182 assert(mergeable(Other
));
189 void Info::mergeBase(Info
&&Other
) {
190 assert(mergeable(Other
));
197 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
));
202 llvm::sort(Description
);
203 auto Last
= std::unique(Description
.begin(), Description
.end());
204 Description
.erase(Last
, Description
.end());
207 bool Info::mergeable(const Info
&Other
) {
208 return IT
== Other
.IT
&& USR
== Other
.USR
;
211 void SymbolInfo::merge(SymbolInfo
&&Other
) {
212 assert(mergeable(Other
));
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
));
218 auto Last
= std::unique(Loc
.begin(), Loc
.end());
219 Loc
.erase(Last
, Loc
.end());
220 mergeBase(std::move(Other
));
223 NamespaceInfo::NamespaceInfo(SymbolID USR
, StringRef Name
, StringRef Path
)
224 : Info(InfoType::IT_namespace
, USR
, Name
, Path
) {}
226 void NamespaceInfo::merge(NamespaceInfo
&&Other
) {
227 assert(mergeable(Other
));
228 // Reduce children if necessary.
229 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
));
234 mergeBase(std::move(Other
));
237 RecordInfo::RecordInfo(SymbolID USR
, StringRef Name
, StringRef Path
)
238 : SymbolInfo(InfoType::IT_record
, USR
, Name
, Path
) {}
240 void RecordInfo::merge(RecordInfo
&&Other
) {
241 assert(mergeable(Other
));
243 TagType
= Other
.TagType
;
244 IsTypeDef
= IsTypeDef
|| Other
.IsTypeDef
;
246 Members
= std::move(Other
.Members
);
248 Bases
= std::move(Other
.Bases
);
250 Parents
= std::move(Other
.Parents
);
251 if (VirtualParents
.empty())
252 VirtualParents
= std::move(Other
.VirtualParents
);
253 // Reduce children if necessary.
254 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
));
258 SymbolInfo::merge(std::move(Other
));
260 Template
= Other
.Template
;
263 void EnumInfo::merge(EnumInfo
&&Other
) {
264 assert(mergeable(Other
));
266 Scoped
= Other
.Scoped
;
268 Members
= std::move(Other
.Members
);
269 SymbolInfo::merge(std::move(Other
));
272 void FunctionInfo::merge(FunctionInfo
&&Other
) {
273 assert(mergeable(Other
));
275 IsMethod
= Other
.IsMethod
;
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
);
283 Params
= std::move(Other
.Params
);
284 SymbolInfo::merge(std::move(Other
));
286 Template
= Other
.Template
;
289 void TypedefInfo::merge(TypedefInfo
&&Other
) {
290 assert(mergeable(Other
));
292 IsUsing
= Other
.IsUsing
;
293 if (Underlying
.Type
.Name
== "")
294 Underlying
= Other
.Underlying
;
295 SymbolInfo::merge(std::move(Other
));
298 BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
300 BaseRecordInfo::BaseRecordInfo(SymbolID USR
, StringRef Name
, StringRef Path
,
301 bool IsVirtual
, AccessSpecifier Access
,
303 : RecordInfo(USR
, Name
, Path
), IsVirtual(IsVirtual
), Access(Access
),
304 IsParent(IsParent
) {}
306 llvm::SmallString
<16> Info::extractName() const {
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
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
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
)));
328 case InfoType::IT_typedef
:
329 return llvm::SmallString
<16>("@nonymous_typedef_" +
330 toHex(llvm::toStringRef(USR
)));
331 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
)));
337 llvm_unreachable("Invalid InfoType.");
338 return llvm::SmallString
<16>("");
341 // Order is based on the Name attribute: case insensitive order
342 bool 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
]);
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();
362 llvm::sort(Children
);
363 for (auto &C
: Children
)
367 ClangDocContext::ClangDocContext(tooling::ExecutionContext
*ECtx
,
368 StringRef ProjectName
, bool PublicOnly
,
369 StringRef OutDirectory
, StringRef SourceRoot
,
370 StringRef RepositoryUrl
,
371 std::vector
<std::string
> UserStylesheets
,
372 std::vector
<std::string
> JsScripts
)
373 : ECtx(ECtx
), ProjectName(ProjectName
), PublicOnly(PublicOnly
),
374 OutDirectory(OutDirectory
), UserStylesheets(UserStylesheets
),
375 JsScripts(JsScripts
) {
376 llvm::SmallString
<128> SourceRootDir(SourceRoot
);
377 if (SourceRoot
.empty())
378 // If no SourceRoot was provided the current path is used as the default
379 llvm::sys::fs::current_path(SourceRootDir
);
380 this->SourceRoot
= std::string(SourceRootDir
.str());
381 if (!RepositoryUrl
.empty()) {
382 this->RepositoryUrl
= std::string(RepositoryUrl
);
383 if (!RepositoryUrl
.empty() && RepositoryUrl
.find("http://") != 0 &&
384 RepositoryUrl
.find("https://") != 0)
385 this->RepositoryUrl
->insert(0, "https://");