llvm-project
388 строк · 13.7 Кб
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
26namespace clang {
27namespace doc {
28
29namespace {
30
31const SymbolID EmptySID = SymbolID();
32
33template <typename T>
34llvm::Expected<std::unique_ptr<Info>>
35reduce(std::vector<std::unique_ptr<Info>> &Values) {
36if (Values.empty() || !Values[0])
37return llvm::createStringError(llvm::inconvertibleErrorCode(),
38"no value to reduce");
39std::unique_ptr<Info> Merged = std::make_unique<T>(Values[0]->USR);
40T *Tmp = static_cast<T *>(Merged.get());
41for (auto &I : Values)
42Tmp->merge(std::move(*static_cast<T *>(I.get())));
43return std::move(Merged);
44}
45
46// 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) {
50for (unsigned long I = 0; I < Children.size(); I++) {
51if (ChildToMerge.USR == Children[I].USR)
52return I;
53}
54return -1;
55}
56
57template <typename T>
58void reduceChildren(std::vector<T> &Children,
59std::vector<T> &&ChildrenToMerge) {
60for (auto &ChildToMerge : ChildrenToMerge) {
61int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);
62if (MergeIdx == -1) {
63Children.push_back(std::move(ChildToMerge));
64continue;
65}
66Children[MergeIdx].merge(std::move(ChildToMerge));
67}
68}
69
70} // namespace
71
72// Dispatch function.
73llvm::Expected<std::unique_ptr<Info>>
74mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
75if (Values.empty() || !Values[0])
76return llvm::createStringError(llvm::inconvertibleErrorCode(),
77"no info values to merge");
78
79switch (Values[0]->IT) {
80case InfoType::IT_namespace:
81return reduce<NamespaceInfo>(Values);
82case InfoType::IT_record:
83return reduce<RecordInfo>(Values);
84case InfoType::IT_enum:
85return reduce<EnumInfo>(Values);
86case InfoType::IT_function:
87return reduce<FunctionInfo>(Values);
88case InfoType::IT_typedef:
89return reduce<TypedefInfo>(Values);
90default:
91return llvm::createStringError(llvm::inconvertibleErrorCode(),
92"unexpected info type");
93}
94}
95
96bool CommentInfo::operator==(const CommentInfo &Other) const {
97auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
98SelfClosing, Explicit, AttrKeys, AttrValues, Args);
99auto SecondCI =
100std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
101Other.ParamName, Other.CloseName, Other.SelfClosing,
102Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
103
104if (FirstCI != SecondCI || Children.size() != Other.Children.size())
105return false;
106
107return std::equal(Children.begin(), Children.end(), Other.Children.begin(),
108llvm::deref<std::equal_to<>>{});
109}
110
111bool CommentInfo::operator<(const CommentInfo &Other) const {
112auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
113SelfClosing, Explicit, AttrKeys, AttrValues, Args);
114auto SecondCI =
115std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
116Other.ParamName, Other.CloseName, Other.SelfClosing,
117Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
118
119if (FirstCI < SecondCI)
120return true;
121
122if (FirstCI == SecondCI) {
123return std::lexicographical_compare(
124Children.begin(), Children.end(), Other.Children.begin(),
125Other.Children.end(), llvm::deref<std::less<>>());
126}
127
128return false;
129}
130
131static llvm::SmallString<64>
132calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
133const StringRef &Name, const StringRef &CurrentPath) {
134llvm::SmallString<64> FilePath;
135
136if (CurrentPath != Path) {
137// iterate back to the top
138for (llvm::sys::path::const_iterator I =
139llvm::sys::path::begin(CurrentPath);
140I != llvm::sys::path::end(CurrentPath); ++I)
141llvm::sys::path::append(FilePath, "..");
142llvm::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.
147if (Type == doc::InfoType::IT_namespace)
148llvm::sys::path::append(FilePath, Name);
149
150return llvm::sys::path::relative_path(FilePath);
151}
152
153llvm::SmallString<64>
154Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
155return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
156}
157
158llvm::SmallString<16> Reference::getFileBaseName() const {
159if (RefType == InfoType::IT_namespace)
160return llvm::SmallString<16>("index");
161
162return Name;
163}
164
165llvm::SmallString<64>
166Info::getRelativeFilePath(const StringRef &CurrentPath) const {
167return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
168}
169
170llvm::SmallString<16> Info::getFileBaseName() const {
171if (IT == InfoType::IT_namespace)
172return llvm::SmallString<16>("index");
173
174return extractName();
175}
176
177bool Reference::mergeable(const Reference &Other) {
178return RefType == Other.RefType && USR == Other.USR;
179}
180
181void Reference::merge(Reference &&Other) {
182assert(mergeable(Other));
183if (Name.empty())
184Name = Other.Name;
185if (Path.empty())
186Path = Other.Path;
187}
188
189void Info::mergeBase(Info &&Other) {
190assert(mergeable(Other));
191if (USR == EmptySID)
192USR = Other.USR;
193if (Name == "")
194Name = Other.Name;
195if (Path == "")
196Path = Other.Path;
197if (Namespace.empty())
198Namespace = std::move(Other.Namespace);
199// Unconditionally extend the description, since each decl may have a comment.
200std::move(Other.Description.begin(), Other.Description.end(),
201std::back_inserter(Description));
202llvm::sort(Description);
203auto Last = std::unique(Description.begin(), Description.end());
204Description.erase(Last, Description.end());
205}
206
207bool Info::mergeable(const Info &Other) {
208return IT == Other.IT && USR == Other.USR;
209}
210
211void SymbolInfo::merge(SymbolInfo &&Other) {
212assert(mergeable(Other));
213if (!DefLoc)
214DefLoc = std::move(Other.DefLoc);
215// Unconditionally extend the list of locations, since we want all of them.
216std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
217llvm::sort(Loc);
218auto Last = std::unique(Loc.begin(), Loc.end());
219Loc.erase(Last, Loc.end());
220mergeBase(std::move(Other));
221}
222
223NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
224: Info(InfoType::IT_namespace, USR, Name, Path) {}
225
226void NamespaceInfo::merge(NamespaceInfo &&Other) {
227assert(mergeable(Other));
228// Reduce children if necessary.
229reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
230reduceChildren(Children.Records, std::move(Other.Children.Records));
231reduceChildren(Children.Functions, std::move(Other.Children.Functions));
232reduceChildren(Children.Enums, std::move(Other.Children.Enums));
233reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
234mergeBase(std::move(Other));
235}
236
237RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
238: SymbolInfo(InfoType::IT_record, USR, Name, Path) {}
239
240void RecordInfo::merge(RecordInfo &&Other) {
241assert(mergeable(Other));
242if (!llvm::to_underlying(TagType))
243TagType = Other.TagType;
244IsTypeDef = IsTypeDef || Other.IsTypeDef;
245if (Members.empty())
246Members = std::move(Other.Members);
247if (Bases.empty())
248Bases = std::move(Other.Bases);
249if (Parents.empty())
250Parents = std::move(Other.Parents);
251if (VirtualParents.empty())
252VirtualParents = std::move(Other.VirtualParents);
253// Reduce children if necessary.
254reduceChildren(Children.Records, std::move(Other.Children.Records));
255reduceChildren(Children.Functions, std::move(Other.Children.Functions));
256reduceChildren(Children.Enums, std::move(Other.Children.Enums));
257reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
258SymbolInfo::merge(std::move(Other));
259if (!Template)
260Template = Other.Template;
261}
262
263void EnumInfo::merge(EnumInfo &&Other) {
264assert(mergeable(Other));
265if (!Scoped)
266Scoped = Other.Scoped;
267if (Members.empty())
268Members = std::move(Other.Members);
269SymbolInfo::merge(std::move(Other));
270}
271
272void FunctionInfo::merge(FunctionInfo &&Other) {
273assert(mergeable(Other));
274if (!IsMethod)
275IsMethod = Other.IsMethod;
276if (!Access)
277Access = Other.Access;
278if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
279ReturnType = std::move(Other.ReturnType);
280if (Parent.USR == EmptySID && Parent.Name == "")
281Parent = std::move(Other.Parent);
282if (Params.empty())
283Params = std::move(Other.Params);
284SymbolInfo::merge(std::move(Other));
285if (!Template)
286Template = Other.Template;
287}
288
289void TypedefInfo::merge(TypedefInfo &&Other) {
290assert(mergeable(Other));
291if (!IsUsing)
292IsUsing = Other.IsUsing;
293if (Underlying.Type.Name == "")
294Underlying = Other.Underlying;
295SymbolInfo::merge(std::move(Other));
296}
297
298BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
299
300BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,
301bool IsVirtual, AccessSpecifier Access,
302bool IsParent)
303: RecordInfo(USR, Name, Path), IsVirtual(IsVirtual), Access(Access),
304IsParent(IsParent) {}
305
306llvm::SmallString<16> Info::extractName() const {
307if (!Name.empty())
308return Name;
309
310switch (IT) {
311case 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.)
316if (Name == "GlobalNamespace" && Namespace.empty())
317return 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.
321return llvm::SmallString<16>("GlobalNamespace");
322case InfoType::IT_record:
323return llvm::SmallString<16>("@nonymous_record_" +
324toHex(llvm::toStringRef(USR)));
325case InfoType::IT_enum:
326return llvm::SmallString<16>("@nonymous_enum_" +
327toHex(llvm::toStringRef(USR)));
328case InfoType::IT_typedef:
329return llvm::SmallString<16>("@nonymous_typedef_" +
330toHex(llvm::toStringRef(USR)));
331case InfoType::IT_function:
332return llvm::SmallString<16>("@nonymous_function_" +
333toHex(llvm::toStringRef(USR)));
334case InfoType::IT_default:
335return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
336}
337llvm_unreachable("Invalid InfoType.");
338return llvm::SmallString<16>("");
339}
340
341// 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
344for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
345// Compare them after converting both to lower case
346int D = tolower(Name[I]) - tolower(Other.Name[I]);
347if (D == 0)
348continue;
349return 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
355if (Name.size() == Other.Name.size())
356return Name > Other.Name;
357// If they are not the same size; the shorter string is smaller
358return Name.size() < Other.Name.size();
359}
360
361void Index::sort() {
362llvm::sort(Children);
363for (auto &C : Children)
364C.sort();
365}
366
367ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
368StringRef ProjectName, bool PublicOnly,
369StringRef OutDirectory, StringRef SourceRoot,
370StringRef RepositoryUrl,
371std::vector<std::string> UserStylesheets)
372: ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),
373OutDirectory(OutDirectory), UserStylesheets(UserStylesheets) {
374llvm::SmallString<128> SourceRootDir(SourceRoot);
375if (SourceRoot.empty())
376// If no SourceRoot was provided the current path is used as the default
377llvm::sys::fs::current_path(SourceRootDir);
378this->SourceRoot = std::string(SourceRootDir);
379if (!RepositoryUrl.empty()) {
380this->RepositoryUrl = std::string(RepositoryUrl);
381if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
382!RepositoryUrl.starts_with("https://"))
383this->RepositoryUrl->insert(0, "https://");
384}
385}
386
387} // namespace doc
388} // namespace clang
389