[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang-tools-extra / clang-doc / Representation.cpp
blobdeb508d9e92bb46ffb3c631d104624eb760d5e56
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.
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"
26 namespace clang {
27 namespace doc {
29 namespace {
31 const SymbolID EmptySID = SymbolID();
33 template <typename T>
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
47 // necessary.
48 template <typename T>
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)
52 return I;
54 return -1;
57 void reduceChildren(std::vector<Reference> &Children,
58 std::vector<Reference> &&ChildrenToMerge) {
59 for (auto &ChildToMerge : ChildrenToMerge) {
60 int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
61 if (mergeIdx == -1) {
62 Children.push_back(std::move(ChildToMerge));
63 continue;
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);
73 if (mergeIdx == -1) {
74 Children.push_back(std::move(ChildToMerge));
75 continue;
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);
85 if (mergeIdx == -1) {
86 Children.push_back(std::move(ChildToMerge));
87 continue;
89 Children[mergeIdx].merge(std::move(ChildToMerge));
93 } // namespace
95 // Dispatch function.
96 llvm::Expected<std::unique_ptr<Info>>
97 mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
98 if (Values.empty() || !Values[0])
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);
111 default:
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");
148 return Name;
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));
169 if (Name.empty())
170 Name = Other.Name;
171 if (Path.empty())
172 Path = Other.Path;
173 if (!IsInGlobalNamespace)
174 IsInGlobalNamespace = Other.IsInGlobalNamespace;
177 void Info::mergeBase(Info &&Other) {
178 assert(mergeable(Other));
179 if (USR == EmptySID)
180 USR = Other.USR;
181 if (Name == "")
182 Name = Other.Name;
183 if (Path == "")
184 Path = Other.Path;
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));
201 if (!DefLoc)
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));
205 llvm::sort(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));
223 if (!TagType)
224 TagType = Other.TagType;
225 IsTypeDef = IsTypeDef || Other.IsTypeDef;
226 if (Members.empty())
227 Members = std::move(Other.Members);
228 if (Bases.empty())
229 Bases = std::move(Other.Bases);
230 if (Parents.empty())
231 Parents = std::move(Other.Parents);
232 if (VirtualParents.empty())
233 VirtualParents = std::move(Other.VirtualParents);
234 // Reduce children if necessary.
235 reduceChildren(ChildRecords, std::move(Other.ChildRecords));
236 reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
237 reduceChildren(ChildEnums, std::move(Other.ChildEnums));
238 SymbolInfo::merge(std::move(Other));
241 void EnumInfo::merge(EnumInfo &&Other) {
242 assert(mergeable(Other));
243 if (!Scoped)
244 Scoped = Other.Scoped;
245 if (Members.empty())
246 Members = std::move(Other.Members);
247 SymbolInfo::merge(std::move(Other));
250 void FunctionInfo::merge(FunctionInfo &&Other) {
251 assert(mergeable(Other));
252 if (!IsMethod)
253 IsMethod = Other.IsMethod;
254 if (!Access)
255 Access = Other.Access;
256 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
257 ReturnType = std::move(Other.ReturnType);
258 if (Parent.USR == EmptySID && Parent.Name == "")
259 Parent = std::move(Other.Parent);
260 if (Params.empty())
261 Params = std::move(Other.Params);
262 SymbolInfo::merge(std::move(Other));
265 llvm::SmallString<16> Info::extractName() const {
266 if (!Name.empty())
267 return Name;
269 switch (IT) {
270 case InfoType::IT_namespace:
271 // Cover the case where the project contains a base namespace called
272 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
273 // namespace, which would conflict with the hard-coded global namespace name
274 // below.)
275 if (Name == "GlobalNamespace" && Namespace.empty())
276 return llvm::SmallString<16>("@GlobalNamespace");
277 // The case of anonymous namespaces is taken care of in serialization,
278 // so here we can safely assume an unnamed namespace is the global
279 // one.
280 return llvm::SmallString<16>("GlobalNamespace");
281 case InfoType::IT_record:
282 return llvm::SmallString<16>("@nonymous_record_" +
283 toHex(llvm::toStringRef(USR)));
284 case InfoType::IT_enum:
285 return llvm::SmallString<16>("@nonymous_enum_" +
286 toHex(llvm::toStringRef(USR)));
287 case InfoType::IT_function:
288 return llvm::SmallString<16>("@nonymous_function_" +
289 toHex(llvm::toStringRef(USR)));
290 case InfoType::IT_default:
291 return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
293 llvm_unreachable("Invalid InfoType.");
294 return llvm::SmallString<16>("");
297 // Order is based on the Name attribute: case insensitive order
298 bool Index::operator<(const Index &Other) const {
299 // Loop through each character of both strings
300 for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
301 // Compare them after converting both to lower case
302 int D = tolower(Name[I]) - tolower(Other.Name[I]);
303 if (D == 0)
304 continue;
305 return D < 0;
307 // If both strings have the size it means they would be equal if changed to
308 // lower case. In here, lower case will be smaller than upper case
309 // Example: string < stRing = true
310 // This is the opposite of how operator < handles strings
311 if (Name.size() == Other.Name.size())
312 return Name > Other.Name;
313 // If they are not the same size; the shorter string is smaller
314 return Name.size() < Other.Name.size();
317 void Index::sort() {
318 llvm::sort(Children);
319 for (auto &C : Children)
320 C.sort();
323 ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
324 StringRef ProjectName, bool PublicOnly,
325 StringRef OutDirectory, StringRef SourceRoot,
326 StringRef RepositoryUrl,
327 std::vector<std::string> UserStylesheets,
328 std::vector<std::string> JsScripts)
329 : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),
330 OutDirectory(OutDirectory), UserStylesheets(UserStylesheets),
331 JsScripts(JsScripts) {
332 llvm::SmallString<128> SourceRootDir(SourceRoot);
333 if (SourceRoot.empty())
334 // If no SourceRoot was provided the current path is used as the default
335 llvm::sys::fs::current_path(SourceRootDir);
336 this->SourceRoot = std::string(SourceRootDir.str());
337 if (!RepositoryUrl.empty()) {
338 this->RepositoryUrl = std::string(RepositoryUrl);
339 if (!RepositoryUrl.empty() && RepositoryUrl.find("http://") != 0 &&
340 RepositoryUrl.find("https://") != 0)
341 this->RepositoryUrl->insert(0, "https://");
345 } // namespace doc
346 } // namespace clang