clang-tools 10.0.0

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/Support/Error.h"
24 #include "llvm/Support/Path.h"
25
26 namespace clang {
27 namespace doc {
28
29 namespace {
30
31 const SymbolID EmptySID = SymbolID();
32
33 template <typename T>
34 llvm::Expected<std::unique_ptr<Info>>
35 reduce(std::vector<std::unique_ptr<Info>> &Values) {
36 if (Values.empty())
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);
44 }
45
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;
53 }
54 return -1;
55 }
56
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;
64 }
65 Children[mergeIdx].merge(std::move(ChildToMerge));
66 }
67 }
68
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;
76 }
77 Children[mergeIdx].merge(std::move(ChildToMerge));
78 }
79 }
80
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;
88 }
89 Children[mergeIdx].merge(std::move(ChildToMerge));
90 }
91 }
92
93 } // namespace
94
95 // Dispatch function.
96 llvm::Expected<std::unique_ptr<Info>>
97 mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
98 if (Values.empty())
99 return llvm::createStringError(llvm::inconvertibleErrorCode(),
100 "no info values to merge");
101
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");
114 }
115 }
116
117 bool Reference::mergeable(const Reference &Other) {
118 return RefType == Other.RefType && USR == Other.USR;
119 }
120
121 void Reference::merge(Reference &&Other) {
122 assert(mergeable(Other));
123 if (Name.empty())
124 Name = Other.Name;
125 if (Path.empty())
126 Path = Other.Path;
127 if (!IsInGlobalNamespace)
128 IsInGlobalNamespace = Other.IsInGlobalNamespace;
129 }
130
131 void Info::mergeBase(Info &&Other) {
132 assert(mergeable(Other));
133 if (USR == EmptySID)
134 USR = Other.USR;
135 if (Name == "")
136 Name = Other.Name;
137 if (Path == "")
138 Path = Other.Path;
139 if (Namespace.empty())
140 Namespace = std::move(Other.Namespace);
141 // Unconditionally extend the description, since each decl may have a comment.
142 std::move(Other.Description.begin(), Other.Description.end(),
143 std::back_inserter(Description));
144 std::sort(Description.begin(), Description.end());
145 auto Last = std::unique(Description.begin(), Description.end());
146 Description.erase(Last, Description.end());
147 }
148
149 bool Info::mergeable(const Info &Other) {
150 return IT == Other.IT && USR == Other.USR;
151 }
152
153 void SymbolInfo::merge(SymbolInfo &&Other) {
154 assert(mergeable(Other));
155 if (!DefLoc)
156 DefLoc = std::move(Other.DefLoc);
157 // Unconditionally extend the list of locations, since we want all of them.
158 std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
159 std::sort(Loc.begin(), Loc.end());
160 auto Last = std::unique(Loc.begin(), Loc.end());
161 Loc.erase(Last, Loc.end());
162 mergeBase(std::move(Other));
163 }
164
165 void NamespaceInfo::merge(NamespaceInfo &&Other) {
166 assert(mergeable(Other));
167 // Reduce children if necessary.
168 reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces));
169 reduceChildren(ChildRecords, std::move(Other.ChildRecords));
170 reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
171 reduceChildren(ChildEnums, std::move(Other.ChildEnums));
172 mergeBase(std::move(Other));
173 }
174
175 void RecordInfo::merge(RecordInfo &&Other) {
176 assert(mergeable(Other));
177 if (!TagType)
178 TagType = Other.TagType;
179 if (Members.empty())
180 Members = std::move(Other.Members);
181 if (Bases.empty())
182 Bases = std::move(Other.Bases);
183 if (Parents.empty())
184 Parents = std::move(Other.Parents);
185 if (VirtualParents.empty())
186 VirtualParents = std::move(Other.VirtualParents);
187 // Reduce children if necessary.
188 reduceChildren(ChildRecords, std::move(Other.ChildRecords));
189 reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
190 reduceChildren(ChildEnums, std::move(Other.ChildEnums));
191 SymbolInfo::merge(std::move(Other));
192 }
193
194 void EnumInfo::merge(EnumInfo &&Other) {
195 assert(mergeable(Other));
196 if (!Scoped)
197 Scoped = Other.Scoped;
198 if (Members.empty())
199 Members = std::move(Other.Members);
200 SymbolInfo::merge(std::move(Other));
201 }
202
203 void FunctionInfo::merge(FunctionInfo &&Other) {
204 assert(mergeable(Other));
205 if (!IsMethod)
206 IsMethod = Other.IsMethod;
207 if (!Access)
208 Access = Other.Access;
209 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
210 ReturnType = std::move(Other.ReturnType);
211 if (Parent.USR == EmptySID && Parent.Name == "")
212 Parent = std::move(Other.Parent);
213 if (Params.empty())
214 Params = std::move(Other.Params);
215 SymbolInfo::merge(std::move(Other));
216 }
217
218 llvm::SmallString<16> Info::extractName() const {
219 if (!Name.empty())
220 return Name;
221
222 switch (IT) {
223 case InfoType::IT_namespace:
224 // Cover the case where the project contains a base namespace called
225 // 'GlobalNamespace' (i.e. a namespace at the same level as the global
226 // namespace, which would conflict with the hard-coded global namespace name
227 // below.)
228 if (Name == "GlobalNamespace" && Namespace.empty())
229 return llvm::SmallString<16>("@GlobalNamespace");
230 // The case of anonymous namespaces is taken care of in serialization,
231 // so here we can safely assume an unnamed namespace is the global
232 // one.
233 return llvm::SmallString<16>("GlobalNamespace");
234 case InfoType::IT_record:
235 return llvm::SmallString<16>("@nonymous_record_" +
236 toHex(llvm::toStringRef(USR)));
237 case InfoType::IT_enum:
238 return llvm::SmallString<16>("@nonymous_enum_" +
239 toHex(llvm::toStringRef(USR)));
240 case InfoType::IT_function:
241 return llvm::SmallString<16>("@nonymous_function_" +
242 toHex(llvm::toStringRef(USR)));
243 case InfoType::IT_default:
244 return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
245 }
246 llvm_unreachable("Invalid InfoType.");
247 return llvm::SmallString<16>("");
248 }
249
250 // Order is based on the Name attribute: case insensitive order
251 bool Index::operator<(const Index &Other) const {
252 // Loop through each character of both strings
253 for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
254 // Compare them after converting both to lower case
255 int D = tolower(Name[I]) - tolower(Other.Name[I]);
256 if (D == 0)
257 continue;
258 return D < 0;
259 }
260 // If both strings have the size it means they would be equal if changed to
261 // lower case. In here, lower case will be smaller than upper case
262 // Example: string < stRing = true
263 // This is the opposite of how operator < handles strings
264 if (Name.size() == Other.Name.size())
265 return Name > Other.Name;
266 // If they are not the same size; the shorter string is smaller
267 return Name.size() < Other.Name.size();
268 }
269
270 void Index::sort() {
271 std::sort(Children.begin(), Children.end());
272 for (auto &C : Children)
273 C.sort();
274 }
275
276 ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
277 StringRef ProjectName, bool PublicOnly,
278 StringRef OutDirectory, StringRef SourceRoot,
279 StringRef RepositoryUrl,
280 std::vector<std::string> UserStylesheets,
281 std::vector<std::string> JsScripts)
282 : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),
283 OutDirectory(OutDirectory), UserStylesheets(UserStylesheets),
284 JsScripts(JsScripts) {
285 llvm::SmallString<128> SourceRootDir(SourceRoot);
286 if (SourceRoot.empty())
287 // If no SourceRoot was provided the current path is used as the default
288 llvm::sys::fs::current_path(SourceRootDir);
289 this->SourceRoot = SourceRootDir.str();
290 if (!RepositoryUrl.empty()) {
291 this->RepositoryUrl = RepositoryUrl;
292 if (!RepositoryUrl.empty() && RepositoryUrl.find("http://") != 0 &&
293 RepositoryUrl.find("https://") != 0)
294 this->RepositoryUrl->insert(0, "https://");
295 }
296 }
297
298 } // namespace doc
299 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
Representation.h
clang::doc::InfoType::IT_default
clang::doc::EmptySID
static const SymbolID EmptySID
Definition: BitcodeWriter.cpp:17
clang::doc::InfoType::IT_function
clang::doc::Reference
Definition: Representation.h:115
Parent
const Node * Parent
Definition: ExtractFunction.cpp:148
clang::doc::ClangDocContext::RepositoryUrl
llvm::Optional< std::string > RepositoryUrl
Definition: Representation.h:429
clang::doc::Reference::Name
SmallString< 16 > Name
Definition: Representation.h:139
SourceRoot
static llvm::cl::opt< std::string > SourceRoot("source-root", llvm::cl::desc(R"( Directory where processed files are stored. Links to definition locations will only be generated if the file is in this dir.)"), llvm::cl::cat(ClangDocCategory))
RepositoryUrl
static llvm::cl::opt< std::string > RepositoryUrl("repository", llvm::cl::desc(R"( URL of repository that hosts code. Used for links to definition locations.)"), llvm::cl::cat(ClangDocCategory))
clang::doc::InfoType::IT_enum
clang::doc::InfoType::IT_record
clang::doc::InfoType::IT_namespace
clang::doc::Info::extractName
llvm::SmallString< 16 > extractName() const
Definition: Representation.cpp:218
clang::doc::Index
Definition: Representation.h:389
clang::doc::ClangDocContext::ClangDocContext
ClangDocContext()=default
clang::doc::NamespaceInfo::merge
void merge(NamespaceInfo &&I)
Definition: Representation.cpp:165
PublicOnly
static llvm::cl::opt< bool > PublicOnly("public", llvm::cl::desc("Document only public declarations."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
clang::doc::EnumInfo::merge
void merge(EnumInfo &&I)
Definition: Representation.cpp:194
clang::doc::Reference::merge
void merge(Reference &&I)
Definition: Representation.cpp:121
ProjectName
static llvm::cl::opt< std::string > ProjectName("project-name", llvm::cl::desc("Name of project."), llvm::cl::cat(ClangDocCategory))
clang::doc::FunctionInfo
Definition: Representation.h:307
clang::doc::SymbolInfo
Definition: Representation.h:292
clang::doc::Reference::USR
SymbolID USR
Definition: Representation.h:138
clang::doc::Reference::RefType
InfoType RefType
Definition: Representation.h:140
clang::doc::Info::mergeable
bool mergeable(const Info &Other)
Definition: Representation.cpp:149
OutDirectory
static llvm::cl::opt< std::string > OutDirectory("output", llvm::cl::desc("Directory for outputting generated files."), llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory))
UserStylesheets
static llvm::cl::list< std::string > UserStylesheets("stylesheets", llvm::cl::CommaSeparated, llvm::cl::desc("CSS stylesheets to extend the default styles."), llvm::cl::cat(ClangDocCategory))
clang::doc::NamespaceInfo
Definition: Representation.h:271
clang::doc::RecordInfo
Definition: Representation.h:327
clang::doc::Index::operator<
bool operator<(const Index &Other) const
Definition: Representation.cpp:251
clang::doc::Reference::mergeable
bool mergeable(const Reference &Other)
Definition: Representation.cpp:117
clang::doc::mergeInfos
llvm::Expected< std::unique_ptr< Info > > mergeInfos(std::vector< std::unique_ptr< Info >> &Values)
Definition: Representation.cpp:97
clang::doc::Info
A base struct for Infos.
Definition: Representation.h:237
ReturnType
std::string ReturnType
Definition: CodeComplete.cpp:395
clang::doc::Reference::IsInGlobalNamespace
bool IsInGlobalNamespace
Definition: Representation.h:148
clang::doc::Index::sort
void sort()
Definition: Representation.cpp:270
clang::doc::SymbolInfo::merge
void merge(SymbolInfo &&I)
Definition: Representation.cpp:153
clang::doc::EnumInfo
Definition: Representation.h:378
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::doc::FunctionInfo::merge
void merge(FunctionInfo &&I)
Definition: Representation.cpp:203
clang::doc::Info::IT
const InfoType IT
Definition: Representation.h:252
clang::doc::Info::USR
SymbolID USR
Definition: Representation.h:250
Description
const char * Description
Definition: Dexp.cpp:257
clang::doc::SymbolID
std::array< uint8_t, 20 > SymbolID
Definition: Representation.h:30
clang::doc::RecordInfo::merge
void merge(RecordInfo &&I)
Definition: Representation.cpp:175
clang::doc::Reference::Path
llvm::SmallString< 128 > Path
Definition: Representation.h:145
clang::doc::Info::mergeBase
void mergeBase(Info &&I)
Definition: Representation.cpp:131