llvm-project

Форк
0
832 строки · 31.7 Кб
1
//===-- Serialize.cpp - ClangDoc Serializer ---------------------*- 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
#include "Serialize.h"
10
#include "BitcodeWriter.h"
11
#include "clang/AST/Comment.h"
12
#include "clang/Index/USRGeneration.h"
13
#include "clang/Lex/Lexer.h"
14
#include "llvm/ADT/Hashing.h"
15
#include "llvm/ADT/StringExtras.h"
16
#include "llvm/Support/SHA1.h"
17

18
using clang::comments::FullComment;
19

20
namespace clang {
21
namespace doc {
22
namespace serialize {
23

24
SymbolID hashUSR(llvm::StringRef USR) {
25
  return llvm::SHA1::hash(arrayRefFromStringRef(USR));
26
}
27

28
template <typename T>
29
static void
30
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
31
                         const T *D, bool &IsAnonymousNamespace);
32

33
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
34

35
// A function to extract the appropriate relative path for a given info's
36
// documentation. The path returned is a composite of the parent namespaces.
37
//
38
// Example: Given the below, the directory path for class C info will be
39
// <root>/A/B
40
//
41
// namespace A {
42
// namespace B {
43
//
44
// class C {};
45
//
46
// }
47
// }
48
llvm::SmallString<128>
49
getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
50
  llvm::SmallString<128> Path;
51
  for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
52
    llvm::sys::path::append(Path, R->Name);
53
  return Path;
54
}
55

56
llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
57
  llvm::SmallVector<Reference, 4> Namespaces;
58
  // The third arg in populateParentNamespaces is a boolean passed by reference,
59
  // its value is not relevant in here so it's not used anywhere besides the
60
  // function call
61
  bool B = true;
62
  populateParentNamespaces(Namespaces, D, B);
63
  return getInfoRelativePath(Namespaces);
64
}
65

66
class ClangDocCommentVisitor
67
    : public ConstCommentVisitor<ClangDocCommentVisitor> {
68
public:
69
  ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
70

71
  void parseComment(const comments::Comment *C);
72

73
  void visitTextComment(const TextComment *C);
74
  void visitInlineCommandComment(const InlineCommandComment *C);
75
  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
76
  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
77
  void visitBlockCommandComment(const BlockCommandComment *C);
78
  void visitParamCommandComment(const ParamCommandComment *C);
79
  void visitTParamCommandComment(const TParamCommandComment *C);
80
  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
81
  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
82
  void visitVerbatimLineComment(const VerbatimLineComment *C);
83

84
private:
85
  std::string getCommandName(unsigned CommandID) const;
86
  bool isWhitespaceOnly(StringRef S) const;
87

88
  CommentInfo &CurrentCI;
89
};
90

91
void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
92
  CurrentCI.Kind = C->getCommentKindName();
93
  ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
94
  for (comments::Comment *Child :
95
       llvm::make_range(C->child_begin(), C->child_end())) {
96
    CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
97
    ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
98
    Visitor.parseComment(Child);
99
  }
100
}
101

102
void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
103
  if (!isWhitespaceOnly(C->getText()))
104
    CurrentCI.Text = C->getText();
105
}
106

107
void ClangDocCommentVisitor::visitInlineCommandComment(
108
    const InlineCommandComment *C) {
109
  CurrentCI.Name = getCommandName(C->getCommandID());
110
  for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
111
    CurrentCI.Args.push_back(C->getArgText(I));
112
}
113

114
void ClangDocCommentVisitor::visitHTMLStartTagComment(
115
    const HTMLStartTagComment *C) {
116
  CurrentCI.Name = C->getTagName();
117
  CurrentCI.SelfClosing = C->isSelfClosing();
118
  for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
119
    const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
120
    CurrentCI.AttrKeys.push_back(Attr.Name);
121
    CurrentCI.AttrValues.push_back(Attr.Value);
122
  }
123
}
124

125
void ClangDocCommentVisitor::visitHTMLEndTagComment(
126
    const HTMLEndTagComment *C) {
127
  CurrentCI.Name = C->getTagName();
128
  CurrentCI.SelfClosing = true;
129
}
130

131
void ClangDocCommentVisitor::visitBlockCommandComment(
132
    const BlockCommandComment *C) {
133
  CurrentCI.Name = getCommandName(C->getCommandID());
134
  for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
135
    CurrentCI.Args.push_back(C->getArgText(I));
136
}
137

138
void ClangDocCommentVisitor::visitParamCommandComment(
139
    const ParamCommandComment *C) {
140
  CurrentCI.Direction =
141
      ParamCommandComment::getDirectionAsString(C->getDirection());
142
  CurrentCI.Explicit = C->isDirectionExplicit();
143
  if (C->hasParamName())
144
    CurrentCI.ParamName = C->getParamNameAsWritten();
145
}
146

147
void ClangDocCommentVisitor::visitTParamCommandComment(
148
    const TParamCommandComment *C) {
149
  if (C->hasParamName())
150
    CurrentCI.ParamName = C->getParamNameAsWritten();
151
}
152

153
void ClangDocCommentVisitor::visitVerbatimBlockComment(
154
    const VerbatimBlockComment *C) {
155
  CurrentCI.Name = getCommandName(C->getCommandID());
156
  CurrentCI.CloseName = C->getCloseName();
157
}
158

159
void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
160
    const VerbatimBlockLineComment *C) {
161
  if (!isWhitespaceOnly(C->getText()))
162
    CurrentCI.Text = C->getText();
163
}
164

165
void ClangDocCommentVisitor::visitVerbatimLineComment(
166
    const VerbatimLineComment *C) {
167
  if (!isWhitespaceOnly(C->getText()))
168
    CurrentCI.Text = C->getText();
169
}
170

171
bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
172
  return llvm::all_of(S, isspace);
173
}
174

175
std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
176
  const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
177
  if (Info)
178
    return Info->Name;
179
  // TODO: Add parsing for \file command.
180
  return "<not a builtin command>";
181
}
182

183
// Serializing functions.
184

185
std::string getSourceCode(const Decl *D, const SourceRange &R) {
186
  return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
187
                              D->getASTContext().getSourceManager(),
188
                              D->getASTContext().getLangOpts())
189
      .str();
190
}
191

192
template <typename T> static std::string serialize(T &I) {
193
  SmallString<2048> Buffer;
194
  llvm::BitstreamWriter Stream(Buffer);
195
  ClangDocBitcodeWriter Writer(Stream);
196
  Writer.emitBlock(I);
197
  return Buffer.str().str();
198
}
199

200
std::string serialize(std::unique_ptr<Info> &I) {
201
  switch (I->IT) {
202
  case InfoType::IT_namespace:
203
    return serialize(*static_cast<NamespaceInfo *>(I.get()));
204
  case InfoType::IT_record:
205
    return serialize(*static_cast<RecordInfo *>(I.get()));
206
  case InfoType::IT_enum:
207
    return serialize(*static_cast<EnumInfo *>(I.get()));
208
  case InfoType::IT_function:
209
    return serialize(*static_cast<FunctionInfo *>(I.get()));
210
  default:
211
    return "";
212
  }
213
}
214

215
static void parseFullComment(const FullComment *C, CommentInfo &CI) {
216
  ClangDocCommentVisitor Visitor(CI);
217
  Visitor.parseComment(C);
218
}
219

220
static SymbolID getUSRForDecl(const Decl *D) {
221
  llvm::SmallString<128> USR;
222
  if (index::generateUSRForDecl(D, USR))
223
    return SymbolID();
224
  return hashUSR(USR);
225
}
226

227
static TagDecl *getTagDeclForType(const QualType &T) {
228
  if (const TagDecl *D = T->getAsTagDecl())
229
    return D->getDefinition();
230
  return nullptr;
231
}
232

233
static RecordDecl *getRecordDeclForType(const QualType &T) {
234
  if (const RecordDecl *D = T->getAsRecordDecl())
235
    return D->getDefinition();
236
  return nullptr;
237
}
238

239
TypeInfo getTypeInfoForType(const QualType &T) {
240
  const TagDecl *TD = getTagDeclForType(T);
241
  if (!TD)
242
    return TypeInfo(Reference(SymbolID(), T.getAsString()));
243

244
  InfoType IT;
245
  if (dyn_cast<EnumDecl>(TD)) {
246
    IT = InfoType::IT_enum;
247
  } else if (dyn_cast<RecordDecl>(TD)) {
248
    IT = InfoType::IT_record;
249
  } else {
250
    IT = InfoType::IT_default;
251
  }
252
  return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
253
                            T.getAsString(), getInfoRelativePath(TD)));
254
}
255

256
static bool isPublic(const clang::AccessSpecifier AS,
257
                     const clang::Linkage Link) {
258
  if (AS == clang::AccessSpecifier::AS_private)
259
    return false;
260
  else if ((Link == clang::Linkage::Module) ||
261
           (Link == clang::Linkage::External))
262
    return true;
263
  return false; // otherwise, linkage is some form of internal linkage
264
}
265

266
static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
267
                                const NamedDecl *D) {
268
  bool IsAnonymousNamespace = false;
269
  if (const auto *N = dyn_cast<NamespaceDecl>(D))
270
    IsAnonymousNamespace = N->isAnonymousNamespace();
271
  return !PublicOnly ||
272
         (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
273
          isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
274
}
275

276
// The InsertChild functions insert the given info into the given scope using
277
// the method appropriate for that type. Some types are moved into the
278
// appropriate vector, while other types have Reference objects generated to
279
// refer to them.
280
//
281
// See MakeAndInsertIntoParent().
282
static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
283
  Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
284
                                Info.Name, getInfoRelativePath(Info.Namespace));
285
}
286

287
static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
288
  Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
289
                             Info.Name, getInfoRelativePath(Info.Namespace));
290
}
291

292
static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
293
  Scope.Enums.push_back(std::move(Info));
294
}
295

296
static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
297
  Scope.Functions.push_back(std::move(Info));
298
}
299

300
static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
301
  Scope.Typedefs.push_back(std::move(Info));
302
}
303

304
// Creates a parent of the correct type for the given child and inserts it into
305
// that parent.
306
//
307
// This is complicated by the fact that namespaces and records are inserted by
308
// reference (constructing a "Reference" object with that namespace/record's
309
// info), while everything else is inserted by moving it directly into the child
310
// vectors.
311
//
312
// For namespaces and records, explicitly specify a const& template parameter
313
// when invoking this function:
314
//   MakeAndInsertIntoParent<const Record&>(...);
315
// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
316
// parameter. Since each variant is used once, it's not worth having a more
317
// elaborate system to automatically deduce this information.
318
template <typename ChildType>
319
std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
320
  if (Child.Namespace.empty()) {
321
    // Insert into unnamed parent namespace.
322
    auto ParentNS = std::make_unique<NamespaceInfo>();
323
    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
324
    return ParentNS;
325
  }
326

327
  switch (Child.Namespace[0].RefType) {
328
  case InfoType::IT_namespace: {
329
    auto ParentNS = std::make_unique<NamespaceInfo>();
330
    ParentNS->USR = Child.Namespace[0].USR;
331
    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
332
    return ParentNS;
333
  }
334
  case InfoType::IT_record: {
335
    auto ParentRec = std::make_unique<RecordInfo>();
336
    ParentRec->USR = Child.Namespace[0].USR;
337
    InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
338
    return ParentRec;
339
  }
340
  default:
341
    llvm_unreachable("Invalid reference type for parent namespace");
342
  }
343
}
344

345
// There are two uses for this function.
346
// 1) Getting the resulting mode of inheritance of a record.
347
//    Example: class A {}; class B : private A {}; class C : public B {};
348
//    It's explicit that C is publicly inherited from C and B is privately
349
//    inherited from A. It's not explicit but C is also privately inherited from
350
//    A. This is the AS that this function calculates. FirstAS is the
351
//    inheritance mode of `class C : B` and SecondAS is the inheritance mode of
352
//    `class B : A`.
353
// 2) Getting the inheritance mode of an inherited attribute / method.
354
//    Example : class A { public: int M; }; class B : private A {};
355
//    Class B is inherited from class A, which has a public attribute. This
356
//    attribute is now part of the derived class B but it's not public. This
357
//    will be private because the inheritance is private. This is the AS that
358
//    this function calculates. FirstAS is the inheritance mode and SecondAS is
359
//    the AS of the attribute / method.
360
static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
361
                                               AccessSpecifier SecondAS) {
362
  if (FirstAS == AccessSpecifier::AS_none ||
363
      SecondAS == AccessSpecifier::AS_none)
364
    return AccessSpecifier::AS_none;
365
  if (FirstAS == AccessSpecifier::AS_private ||
366
      SecondAS == AccessSpecifier::AS_private)
367
    return AccessSpecifier::AS_private;
368
  if (FirstAS == AccessSpecifier::AS_protected ||
369
      SecondAS == AccessSpecifier::AS_protected)
370
    return AccessSpecifier::AS_protected;
371
  return AccessSpecifier::AS_public;
372
}
373

374
// The Access parameter is only provided when parsing the field of an inherited
375
// record, the access specification of the field depends on the inheritance mode
376
static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
377
                        AccessSpecifier Access = AccessSpecifier::AS_public) {
378
  for (const FieldDecl *F : D->fields()) {
379
    if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
380
      continue;
381

382
    // Use getAccessUnsafe so that we just get the default AS_none if it's not
383
    // valid, as opposed to an assert.
384
    MemberTypeInfo &NewMember = I.Members.emplace_back(
385
        getTypeInfoForType(F->getTypeSourceInfo()->getType()),
386
        F->getNameAsString(),
387
        getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
388
    populateMemberTypeInfo(NewMember, F);
389
  }
390
}
391

392
static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
393
  for (const EnumConstantDecl *E : D->enumerators()) {
394
    std::string ValueExpr;
395
    if (const Expr *InitExpr = E->getInitExpr())
396
      ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
397

398
    SmallString<16> ValueStr;
399
    E->getInitVal().toString(ValueStr);
400
    I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
401
  }
402
}
403

404
static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
405
  for (const ParmVarDecl *P : D->parameters()) {
406
    FieldTypeInfo &FieldInfo = I.Params.emplace_back(
407
        getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
408
    FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
409
  }
410
}
411

412
// TODO: Remove the serialization of Parents and VirtualParents, this
413
// information is also extracted in the other definition of parseBases.
414
static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
415
  // Don't parse bases if this isn't a definition.
416
  if (!D->isThisDeclarationADefinition())
417
    return;
418
  for (const CXXBaseSpecifier &B : D->bases()) {
419
    if (B.isVirtual())
420
      continue;
421
    if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
422
      const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
423
      I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
424
                             InfoType::IT_record, B.getType().getAsString());
425
    } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
426
      I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
427
                             InfoType::IT_record, P->getQualifiedNameAsString(),
428
                             getInfoRelativePath(P));
429
    else
430
      I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
431
  }
432
  for (const CXXBaseSpecifier &B : D->vbases()) {
433
    if (const RecordDecl *P = getRecordDeclForType(B.getType()))
434
      I.VirtualParents.emplace_back(
435
          getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
436
          P->getQualifiedNameAsString(), getInfoRelativePath(P));
437
    else
438
      I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
439
  }
440
}
441

442
template <typename T>
443
static void
444
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
445
                         const T *D, bool &IsInAnonymousNamespace) {
446
  const DeclContext *DC = D->getDeclContext();
447
  do {
448
    if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
449
      std::string Namespace;
450
      if (N->isAnonymousNamespace()) {
451
        Namespace = "@nonymous_namespace";
452
        IsInAnonymousNamespace = true;
453
      } else
454
        Namespace = N->getNameAsString();
455
      Namespaces.emplace_back(getUSRForDecl(N), Namespace,
456
                              InfoType::IT_namespace,
457
                              N->getQualifiedNameAsString());
458
    } else if (const auto *N = dyn_cast<RecordDecl>(DC))
459
      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
460
                              InfoType::IT_record,
461
                              N->getQualifiedNameAsString());
462
    else if (const auto *N = dyn_cast<FunctionDecl>(DC))
463
      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
464
                              InfoType::IT_function,
465
                              N->getQualifiedNameAsString());
466
    else if (const auto *N = dyn_cast<EnumDecl>(DC))
467
      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
468
                              InfoType::IT_enum, N->getQualifiedNameAsString());
469
  } while ((DC = DC->getParent()));
470
  // The global namespace should be added to the list of namespaces if the decl
471
  // corresponds to a Record and if it doesn't have any namespace (because this
472
  // means it's in the global namespace). Also if its outermost namespace is a
473
  // record because that record matches the previous condition mentioned.
474
  if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
475
      (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
476
    Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
477
                            InfoType::IT_namespace);
478
}
479

480
void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
481
                                const clang::Decl *D) {
482
  if (const TemplateParameterList *ParamList =
483
          D->getDescribedTemplateParams()) {
484
    if (!TemplateInfo) {
485
      TemplateInfo.emplace();
486
    }
487
    for (const NamedDecl *ND : *ParamList) {
488
      TemplateInfo->Params.emplace_back(
489
          getSourceCode(ND, ND->getSourceRange()));
490
    }
491
  }
492
}
493

494
TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
495
                                         const TemplateArgument &Arg) {
496
  // The TemplateArgument's pretty printing handles all the normal cases
497
  // well enough for our requirements.
498
  std::string Str;
499
  llvm::raw_string_ostream Stream(Str);
500
  Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
501
  return TemplateParamInfo(Str);
502
}
503

504
template <typename T>
505
static void populateInfo(Info &I, const T *D, const FullComment *C,
506
                         bool &IsInAnonymousNamespace) {
507
  I.USR = getUSRForDecl(D);
508
  I.Name = D->getNameAsString();
509
  populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
510
  if (C) {
511
    I.Description.emplace_back();
512
    parseFullComment(C, I.Description.back());
513
  }
514
}
515

516
template <typename T>
517
static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
518
                               int LineNumber, StringRef Filename,
519
                               bool IsFileInRootDir,
520
                               bool &IsInAnonymousNamespace) {
521
  populateInfo(I, D, C, IsInAnonymousNamespace);
522
  if (D->isThisDeclarationADefinition())
523
    I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
524
  else
525
    I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
526
}
527

528
static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
529
                                 const FullComment *FC, int LineNumber,
530
                                 StringRef Filename, bool IsFileInRootDir,
531
                                 bool &IsInAnonymousNamespace) {
532
  populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
533
                     IsInAnonymousNamespace);
534
  I.ReturnType = getTypeInfoForType(D->getReturnType());
535
  parseParameters(I, D);
536

537
  PopulateTemplateParameters(I.Template, D);
538

539
  // Handle function template specializations.
540
  if (const FunctionTemplateSpecializationInfo *FTSI =
541
          D->getTemplateSpecializationInfo()) {
542
    if (!I.Template)
543
      I.Template.emplace();
544
    I.Template->Specialization.emplace();
545
    auto &Specialization = *I.Template->Specialization;
546

547
    Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
548

549
    // Template parameters to the specialization.
550
    if (FTSI->TemplateArguments) {
551
      for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
552
        Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
553
      }
554
    }
555
  }
556
}
557

558
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
559
  assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
560

561
  ASTContext& Context = D->getASTContext();
562
  // TODO investigate whether we can use ASTContext::getCommentForDecl instead
563
  // of this logic. See also similar code in Mapper.cpp.
564
  RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
565
  if (!Comment)
566
    return;
567

568
  Comment->setAttached();
569
  if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
570
    I.Description.emplace_back();
571
    parseFullComment(fc, I.Description.back());
572
  }
573
}
574

575
static void
576
parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
577
           bool PublicOnly, bool IsParent,
578
           AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
579
  // Don't parse bases if this isn't a definition.
580
  if (!D->isThisDeclarationADefinition())
581
    return;
582
  for (const CXXBaseSpecifier &B : D->bases()) {
583
    if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
584
      if (const CXXRecordDecl *Base =
585
              cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
586
        // Initialized without USR and name, this will be set in the following
587
        // if-else stmt.
588
        BaseRecordInfo BI(
589
            {}, "", getInfoRelativePath(Base), B.isVirtual(),
590
            getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
591
            IsParent);
592
        if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
593
          const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
594
          BI.USR = getUSRForDecl(D);
595
          BI.Name = B.getType().getAsString();
596
        } else {
597
          BI.USR = getUSRForDecl(Base);
598
          BI.Name = Base->getNameAsString();
599
        }
600
        parseFields(BI, Base, PublicOnly, BI.Access);
601
        for (const auto &Decl : Base->decls())
602
          if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
603
            // Don't serialize private methods
604
            if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
605
                !MD->isUserProvided())
606
              continue;
607
            FunctionInfo FI;
608
            FI.IsMethod = true;
609
            // The seventh arg in populateFunctionInfo is a boolean passed by
610
            // reference, its value is not relevant in here so it's not used
611
            // anywhere besides the function call.
612
            bool IsInAnonymousNamespace;
613
            populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
614
                                 /*FileName=*/{}, IsFileInRootDir,
615
                                 IsInAnonymousNamespace);
616
            FI.Access =
617
                getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
618
            BI.Children.Functions.emplace_back(std::move(FI));
619
          }
620
        I.Bases.emplace_back(std::move(BI));
621
        // Call this function recursively to get the inherited classes of
622
        // this base; these new bases will also get stored in the original
623
        // RecordInfo: I.
624
        parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
625
                   I.Bases.back().Access);
626
      }
627
    }
628
  }
629
}
630

631
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
632
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
633
         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
634
  auto I = std::make_unique<NamespaceInfo>();
635
  bool IsInAnonymousNamespace = false;
636
  populateInfo(*I, D, FC, IsInAnonymousNamespace);
637
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
638
    return {};
639

640
  I->Name = D->isAnonymousNamespace()
641
                ? llvm::SmallString<16>("@nonymous_namespace")
642
                : I->Name;
643
  I->Path = getInfoRelativePath(I->Namespace);
644
  if (I->Namespace.empty() && I->USR == SymbolID())
645
    return {std::unique_ptr<Info>{std::move(I)}, nullptr};
646

647
  // Namespaces are inserted into the parent by reference, so we need to return
648
  // both the parent and the record itself.
649
  return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
650
}
651

652
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
653
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
654
         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
655
  auto I = std::make_unique<RecordInfo>();
656
  bool IsInAnonymousNamespace = false;
657
  populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
658
                     IsInAnonymousNamespace);
659
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
660
    return {};
661

662
  I->TagType = D->getTagKind();
663
  parseFields(*I, D, PublicOnly);
664
  if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
665
    if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
666
      I->Name = TD->getNameAsString();
667
      I->IsTypeDef = true;
668
    }
669
    // TODO: remove first call to parseBases, that function should be deleted
670
    parseBases(*I, C);
671
    parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
672
  }
673
  I->Path = getInfoRelativePath(I->Namespace);
674

675
  PopulateTemplateParameters(I->Template, D);
676

677
  // Full and partial specializations.
678
  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
679
    if (!I->Template)
680
      I->Template.emplace();
681
    I->Template->Specialization.emplace();
682
    auto &Specialization = *I->Template->Specialization;
683

684
    // What this is a specialization of.
685
    auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
686
    if (SpecOf.is<ClassTemplateDecl *>()) {
687
      Specialization.SpecializationOf =
688
          getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());
689
    } else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {
690
      Specialization.SpecializationOf =
691
          getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());
692
    }
693

694
    // Parameters to the specilization. For partial specializations, get the
695
    // parameters "as written" from the ClassTemplatePartialSpecializationDecl
696
    // because the non-explicit template parameters will have generated internal
697
    // placeholder names rather than the names the user typed that match the
698
    // template parameters.
699
    if (const ClassTemplatePartialSpecializationDecl *CTPSD =
700
            dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
701
      if (const ASTTemplateArgumentListInfo *AsWritten =
702
              CTPSD->getTemplateArgsAsWritten()) {
703
        for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
704
          Specialization.Params.emplace_back(
705
              getSourceCode(D, (*AsWritten)[i].getSourceRange()));
706
        }
707
      }
708
    } else {
709
      for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
710
        Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
711
      }
712
    }
713
  }
714

715
  // Records are inserted into the parent by reference, so we need to return
716
  // both the parent and the record itself.
717
  auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
718
  return {std::move(I), std::move(Parent)};
719
}
720

721
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
722
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
723
         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
724
  FunctionInfo Func;
725
  bool IsInAnonymousNamespace = false;
726
  populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
727
                       IsInAnonymousNamespace);
728
  Func.Access = clang::AccessSpecifier::AS_none;
729
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
730
    return {};
731

732
  // Info is wrapped in its parent scope so is returned in the second position.
733
  return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
734
}
735

736
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
737
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
738
         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
739
  FunctionInfo Func;
740
  bool IsInAnonymousNamespace = false;
741
  populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
742
                       IsInAnonymousNamespace);
743
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
744
    return {};
745

746
  Func.IsMethod = true;
747

748
  const NamedDecl *Parent = nullptr;
749
  if (const auto *SD =
750
          dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
751
    Parent = SD->getSpecializedTemplate();
752
  else
753
    Parent = D->getParent();
754

755
  SymbolID ParentUSR = getUSRForDecl(Parent);
756
  Func.Parent =
757
      Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
758
                Parent->getQualifiedNameAsString()};
759
  Func.Access = D->getAccess();
760

761
  // Info is wrapped in its parent scope so is returned in the second position.
762
  return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
763
}
764

765
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
766
emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
767
         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
768
  TypedefInfo Info;
769

770
  bool IsInAnonymousNamespace = false;
771
  populateInfo(Info, D, FC, IsInAnonymousNamespace);
772
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
773
    return {};
774

775
  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
776
  Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
777
  if (Info.Underlying.Type.Name.empty()) {
778
    // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
779
    // The record serializer explicitly checks for this syntax and constructs
780
    // a record with that name, so we don't want to emit a duplicate here.
781
    return {};
782
  }
783
  Info.IsUsing = false;
784

785
  // Info is wrapped in its parent scope so is returned in the second position.
786
  return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
787
}
788

789
// A type alias is a C++ "using" declaration for a type. It gets mapped to a
790
// TypedefInfo with the IsUsing flag set.
791
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
792
emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
793
         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
794
  TypedefInfo Info;
795

796
  bool IsInAnonymousNamespace = false;
797
  populateInfo(Info, D, FC, IsInAnonymousNamespace);
798
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
799
    return {};
800

801
  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
802
  Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
803
  Info.IsUsing = true;
804

805
  // Info is wrapped in its parent scope so is returned in the second position.
806
  return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
807
}
808

809
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
810
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
811
         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
812
  EnumInfo Enum;
813
  bool IsInAnonymousNamespace = false;
814
  populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
815
                     IsInAnonymousNamespace);
816
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
817
    return {};
818

819
  Enum.Scoped = D->isScoped();
820
  if (D->isFixed()) {
821
    auto Name = D->getIntegerType().getAsString();
822
    Enum.BaseType = TypeInfo(Name, Name);
823
  }
824
  parseEnumerators(Enum, D);
825

826
  // Info is wrapped in its parent scope so is returned in the second position.
827
  return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
828
}
829

830
} // namespace serialize
831
} // namespace doc
832
} // namespace clang
833

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

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

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

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