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
) {
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
)
57 void reduceChildren(std::vector
<Reference
> &Children
,
58 std::vector
<Reference
> &&ChildrenToMerge
) {
59 for (auto &ChildToMerge
: ChildrenToMerge
) {
60 int mergeIdx
= getChildIndexIfExists(Children
, ChildToMerge
);
62 Children
.push_back(std::move(ChildToMerge
));
65 Children
[mergeIdx
].merge(std::move(ChildToMerge
));
69 void reduceChildren(std::vector
<FunctionInfo
> &Children
,
70 std::vector
<FunctionInfo
> &&ChildrenToMerge
) {
71 for (auto &ChildToMerge
: ChildrenToMerge
) {
72 int mergeIdx
= getChildIndexIfExists(Children
, ChildToMerge
);
74 Children
.push_back(std::move(ChildToMerge
));
77 Children
[mergeIdx
].merge(std::move(ChildToMerge
));
81 void reduceChildren(std::vector
<EnumInfo
> &Children
,
82 std::vector
<EnumInfo
> &&ChildrenToMerge
) {
83 for (auto &ChildToMerge
: ChildrenToMerge
) {
84 int mergeIdx
= getChildIndexIfExists(Children
, ChildToMerge
);
86 Children
.push_back(std::move(ChildToMerge
));
89 Children
[mergeIdx
].merge(std::move(ChildToMerge
));
96 llvm::Expected
<std::unique_ptr
<Info
>>
97 mergeInfos(std::vector
<std::unique_ptr
<Info
>> &Values
) {
99 return llvm::createStringError(llvm::inconvertibleErrorCode(),
100 "no info values to merge");
102 switch (Values
[0]->IT
) {
103 case InfoType::IT_namespace
:
104 return reduce
<NamespaceInfo
>(Values
);
105 case InfoType::IT_record
:
106 return reduce
<RecordInfo
>(Values
);
107 case InfoType::IT_enum
:
108 return reduce
<EnumInfo
>(Values
);
109 case InfoType::IT_function
:
110 return reduce
<FunctionInfo
>(Values
);
112 return llvm::createStringError(llvm::inconvertibleErrorCode(),
113 "unexpected info type");
117 static llvm::SmallString
<64>
118 calculateRelativeFilePath(const InfoType
&Type
, const StringRef
&Path
,
119 const StringRef
&Name
, const StringRef
&CurrentPath
) {
120 llvm::SmallString
<64> FilePath
;
122 if (CurrentPath
!= Path
) {
123 // iterate back to the top
124 for (llvm::sys::path::const_iterator I
=
125 llvm::sys::path::begin(CurrentPath
);
126 I
!= llvm::sys::path::end(CurrentPath
); ++I
)
127 llvm::sys::path::append(FilePath
, "..");
128 llvm::sys::path::append(FilePath
, Path
);
131 // Namespace references have a Path to the parent namespace, but
132 // the file is actually in the subdirectory for the namespace.
133 if (Type
== doc::InfoType::IT_namespace
)
134 llvm::sys::path::append(FilePath
, Name
);
136 return llvm::sys::path::relative_path(FilePath
);
139 llvm::SmallString
<64>
140 Reference::getRelativeFilePath(const StringRef
&CurrentPath
) const {
141 return calculateRelativeFilePath(RefType
, Path
, Name
, CurrentPath
);
144 llvm::SmallString
<16> Reference::getFileBaseName() const {
145 if (RefType
== InfoType::IT_namespace
)
146 return llvm::SmallString
<16>("index");
151 llvm::SmallString
<64>
152 Info::getRelativeFilePath(const StringRef
&CurrentPath
) const {
153 return calculateRelativeFilePath(IT
, Path
, extractName(), CurrentPath
);
156 llvm::SmallString
<16> Info::getFileBaseName() const {
157 if (IT
== InfoType::IT_namespace
)
158 return llvm::SmallString
<16>("index");
160 return extractName();
163 bool Reference::mergeable(const Reference
&Other
) {
164 return RefType
== Other
.RefType
&& USR
== Other
.USR
;
167 void Reference::merge(Reference
&&Other
) {
168 assert(mergeable(Other
));
173 if (!IsInGlobalNamespace
)
174 IsInGlobalNamespace
= Other
.IsInGlobalNamespace
;
177 void Info::mergeBase(Info
&&Other
) {
178 assert(mergeable(Other
));
185 if (Namespace
.empty())
186 Namespace
= std::move(Other
.Namespace
);
187 // Unconditionally extend the description, since each decl may have a comment.
188 std::move(Other
.Description
.begin(), Other
.Description
.end(),
189 std::back_inserter(Description
));
190 llvm::sort(Description
);
191 auto Last
= std::unique(Description
.begin(), Description
.end());
192 Description
.erase(Last
, Description
.end());
195 bool Info::mergeable(const Info
&Other
) {
196 return IT
== Other
.IT
&& USR
== Other
.USR
;
199 void SymbolInfo::merge(SymbolInfo
&&Other
) {
200 assert(mergeable(Other
));
202 DefLoc
= std::move(Other
.DefLoc
);
203 // Unconditionally extend the list of locations, since we want all of them.
204 std::move(Other
.Loc
.begin(), Other
.Loc
.end(), std::back_inserter(Loc
));
206 auto Last
= std::unique(Loc
.begin(), Loc
.end());
207 Loc
.erase(Last
, Loc
.end());
208 mergeBase(std::move(Other
));
211 void NamespaceInfo::merge(NamespaceInfo
&&Other
) {
212 assert(mergeable(Other
));
213 // Reduce children if necessary.
214 reduceChildren(ChildNamespaces
, std::move(Other
.ChildNamespaces
));
215 reduceChildren(ChildRecords
, std::move(Other
.ChildRecords
));
216 reduceChildren(ChildFunctions
, std::move(Other
.ChildFunctions
));
217 reduceChildren(ChildEnums
, std::move(Other
.ChildEnums
));
218 mergeBase(std::move(Other
));
221 void RecordInfo::merge(RecordInfo
&&Other
) {
222 assert(mergeable(Other
));
224 TagType
= Other
.TagType
;
226 Members
= std::move(Other
.Members
);
228 Bases
= std::move(Other
.Bases
);
230 Parents
= std::move(Other
.Parents
);
231 if (VirtualParents
.empty())
232 VirtualParents
= std::move(Other
.VirtualParents
);
233 // Reduce children if necessary.
234 reduceChildren(ChildRecords
, std::move(Other
.ChildRecords
));
235 reduceChildren(ChildFunctions
, std::move(Other
.ChildFunctions
));
236 reduceChildren(ChildEnums
, std::move(Other
.ChildEnums
));
237 SymbolInfo::merge(std::move(Other
));
240 void EnumInfo::merge(EnumInfo
&&Other
) {
241 assert(mergeable(Other
));
243 Scoped
= Other
.Scoped
;
245 Members
= std::move(Other
.Members
);
246 SymbolInfo::merge(std::move(Other
));
249 void FunctionInfo::merge(FunctionInfo
&&Other
) {
250 assert(mergeable(Other
));
252 IsMethod
= Other
.IsMethod
;
254 Access
= Other
.Access
;
255 if (ReturnType
.Type
.USR
== EmptySID
&& ReturnType
.Type
.Name
== "")
256 ReturnType
= std::move(Other
.ReturnType
);
257 if (Parent
.USR
== EmptySID
&& Parent
.Name
== "")
258 Parent
= std::move(Other
.Parent
);
260 Params
= std::move(Other
.Params
);
261 SymbolInfo::merge(std::move(Other
));
264 llvm::SmallString
<16> Info::extractName() const {
269 case InfoType::IT_namespace
:
270 // Cover the case where the project contains a base namespace called
271 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
272 // namespace, which would conflict with the hard-coded global namespace name
274 if (Name
== "GlobalNamespace" && Namespace
.empty())
275 return llvm::SmallString
<16>("@GlobalNamespace");
276 // The case of anonymous namespaces is taken care of in serialization,
277 // so here we can safely assume an unnamed namespace is the global
279 return llvm::SmallString
<16>("GlobalNamespace");
280 case InfoType::IT_record
:
281 return llvm::SmallString
<16>("@nonymous_record_" +
282 toHex(llvm::toStringRef(USR
)));
283 case InfoType::IT_enum
:
284 return llvm::SmallString
<16>("@nonymous_enum_" +
285 toHex(llvm::toStringRef(USR
)));
286 case InfoType::IT_function
:
287 return llvm::SmallString
<16>("@nonymous_function_" +
288 toHex(llvm::toStringRef(USR
)));
289 case InfoType::IT_default
:
290 return llvm::SmallString
<16>("@nonymous_" + toHex(llvm::toStringRef(USR
)));
292 llvm_unreachable("Invalid InfoType.");
293 return llvm::SmallString
<16>("");
296 // Order is based on the Name attribute: case insensitive order
297 bool Index::operator<(const Index
&Other
) const {
298 // Loop through each character of both strings
299 for (unsigned I
= 0; I
< Name
.size() && I
< Other
.Name
.size(); ++I
) {
300 // Compare them after converting both to lower case
301 int D
= tolower(Name
[I
]) - tolower(Other
.Name
[I
]);
306 // If both strings have the size it means they would be equal if changed to
307 // lower case. In here, lower case will be smaller than upper case
308 // Example: string < stRing = true
309 // This is the opposite of how operator < handles strings
310 if (Name
.size() == Other
.Name
.size())
311 return Name
> Other
.Name
;
312 // If they are not the same size; the shorter string is smaller
313 return Name
.size() < Other
.Name
.size();
317 llvm::sort(Children
);
318 for (auto &C
: Children
)
322 ClangDocContext::ClangDocContext(tooling::ExecutionContext
*ECtx
,
323 StringRef ProjectName
, bool PublicOnly
,
324 StringRef OutDirectory
, StringRef SourceRoot
,
325 StringRef RepositoryUrl
,
326 std::vector
<std::string
> UserStylesheets
,
327 std::vector
<std::string
> JsScripts
)
328 : ECtx(ECtx
), ProjectName(ProjectName
), PublicOnly(PublicOnly
),
329 OutDirectory(OutDirectory
), UserStylesheets(UserStylesheets
),
330 JsScripts(JsScripts
) {
331 llvm::SmallString
<128> SourceRootDir(SourceRoot
);
332 if (SourceRoot
.empty())
333 // If no SourceRoot was provided the current path is used as the default
334 llvm::sys::fs::current_path(SourceRootDir
);
335 this->SourceRoot
= std::string(SourceRootDir
.str());
336 if (!RepositoryUrl
.empty()) {
337 this->RepositoryUrl
= std::string(RepositoryUrl
);
338 if (!RepositoryUrl
.empty() && RepositoryUrl
.find("http://") != 0 &&
339 RepositoryUrl
.find("https://") != 0)
340 this->RepositoryUrl
->insert(0, "https://");