llvm-project

Форк
0
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

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() || !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);
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
template <typename T>
58
void reduceChildren(std::vector<T> &Children,
59
                    std::vector<T> &&ChildrenToMerge) {
60
  for (auto &ChildToMerge : ChildrenToMerge) {
61
    int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);
62
    if (MergeIdx == -1) {
63
      Children.push_back(std::move(ChildToMerge));
64
      continue;
65
    }
66
    Children[MergeIdx].merge(std::move(ChildToMerge));
67
  }
68
}
69

70
} // namespace
71

72
// Dispatch function.
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");
78

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);
90
  default:
91
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
92
                                   "unexpected info type");
93
  }
94
}
95

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);
99
  auto SecondCI =
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);
103

104
  if (FirstCI != SecondCI || Children.size() != Other.Children.size())
105
    return false;
106

107
  return std::equal(Children.begin(), Children.end(), Other.Children.begin(),
108
                    llvm::deref<std::equal_to<>>{});
109
}
110

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);
114
  auto SecondCI =
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);
118

119
  if (FirstCI < SecondCI)
120
    return true;
121

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<>>());
126
  }
127

128
  return false;
129
}
130

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;
135

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);
143
  }
144

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);
149

150
  return llvm::sys::path::relative_path(FilePath);
151
}
152

153
llvm::SmallString<64>
154
Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
155
  return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
156
}
157

158
llvm::SmallString<16> Reference::getFileBaseName() const {
159
  if (RefType == InfoType::IT_namespace)
160
    return llvm::SmallString<16>("index");
161

162
  return Name;
163
}
164

165
llvm::SmallString<64>
166
Info::getRelativeFilePath(const StringRef &CurrentPath) const {
167
  return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
168
}
169

170
llvm::SmallString<16> Info::getFileBaseName() const {
171
  if (IT == InfoType::IT_namespace)
172
    return llvm::SmallString<16>("index");
173

174
  return extractName();
175
}
176

177
bool Reference::mergeable(const Reference &Other) {
178
  return RefType == Other.RefType && USR == Other.USR;
179
}
180

181
void Reference::merge(Reference &&Other) {
182
  assert(mergeable(Other));
183
  if (Name.empty())
184
    Name = Other.Name;
185
  if (Path.empty())
186
    Path = Other.Path;
187
}
188

189
void Info::mergeBase(Info &&Other) {
190
  assert(mergeable(Other));
191
  if (USR == EmptySID)
192
    USR = Other.USR;
193
  if (Name == "")
194
    Name = Other.Name;
195
  if (Path == "")
196
    Path = Other.Path;
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());
205
}
206

207
bool Info::mergeable(const Info &Other) {
208
  return IT == Other.IT && USR == Other.USR;
209
}
210

211
void SymbolInfo::merge(SymbolInfo &&Other) {
212
  assert(mergeable(Other));
213
  if (!DefLoc)
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));
217
  llvm::sort(Loc);
218
  auto Last = std::unique(Loc.begin(), Loc.end());
219
  Loc.erase(Last, Loc.end());
220
  mergeBase(std::move(Other));
221
}
222

223
NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
224
      : Info(InfoType::IT_namespace, USR, Name, Path) {}
225

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));
235
}
236

237
RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
238
    : SymbolInfo(InfoType::IT_record, USR, Name, Path) {}
239

240
void RecordInfo::merge(RecordInfo &&Other) {
241
  assert(mergeable(Other));
242
  if (!llvm::to_underlying(TagType))
243
    TagType = Other.TagType;
244
  IsTypeDef = IsTypeDef || Other.IsTypeDef;
245
  if (Members.empty())
246
    Members = std::move(Other.Members);
247
  if (Bases.empty())
248
    Bases = std::move(Other.Bases);
249
  if (Parents.empty())
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));
259
  if (!Template)
260
    Template = Other.Template;
261
}
262

263
void EnumInfo::merge(EnumInfo &&Other) {
264
  assert(mergeable(Other));
265
  if (!Scoped)
266
    Scoped = Other.Scoped;
267
  if (Members.empty())
268
    Members = std::move(Other.Members);
269
  SymbolInfo::merge(std::move(Other));
270
}
271

272
void FunctionInfo::merge(FunctionInfo &&Other) {
273
  assert(mergeable(Other));
274
  if (!IsMethod)
275
    IsMethod = Other.IsMethod;
276
  if (!Access)
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);
282
  if (Params.empty())
283
    Params = std::move(Other.Params);
284
  SymbolInfo::merge(std::move(Other));
285
  if (!Template)
286
    Template = Other.Template;
287
}
288

289
void TypedefInfo::merge(TypedefInfo &&Other) {
290
  assert(mergeable(Other));
291
  if (!IsUsing)
292
    IsUsing = Other.IsUsing;
293
  if (Underlying.Type.Name == "")
294
    Underlying = Other.Underlying;
295
  SymbolInfo::merge(std::move(Other));
296
}
297

298
BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
299

300
BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,
301
                               bool IsVirtual, AccessSpecifier Access,
302
                               bool IsParent)
303
    : RecordInfo(USR, Name, Path), IsVirtual(IsVirtual), Access(Access),
304
      IsParent(IsParent) {}
305

306
llvm::SmallString<16> Info::extractName() const {
307
  if (!Name.empty())
308
    return Name;
309

310
  switch (IT) {
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
315
    // below.)
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
320
    // one.
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)));
336
  }
337
  llvm_unreachable("Invalid InfoType.");
338
  return llvm::SmallString<16>("");
339
}
340

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]);
347
    if (D == 0)
348
      continue;
349
    return 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
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();
359
}
360

361
void Index::sort() {
362
  llvm::sort(Children);
363
  for (auto &C : Children)
364
    C.sort();
365
}
366

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
    : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),
373
      OutDirectory(OutDirectory), UserStylesheets(UserStylesheets) {
374
  llvm::SmallString<128> SourceRootDir(SourceRoot);
375
  if (SourceRoot.empty())
376
    // If no SourceRoot was provided the current path is used as the default
377
    llvm::sys::fs::current_path(SourceRootDir);
378
  this->SourceRoot = std::string(SourceRootDir);
379
  if (!RepositoryUrl.empty()) {
380
    this->RepositoryUrl = std::string(RepositoryUrl);
381
    if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
382
        !RepositoryUrl.starts_with("https://"))
383
      this->RepositoryUrl->insert(0, "https://");
384
  }
385
}
386

387
} // namespace doc
388
} // namespace clang
389

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.