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, const PrintingPolicy &Policy) { 240 const TagDecl *TD = getTagDeclForType(T); 241 if (!TD) 242 return TypeInfo(Reference(SymbolID(), T.getAsString(Policy))); 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(Policy), 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 auto &LO = F->getLangOpts(); 383 // Use getAccessUnsafe so that we just get the default AS_none if it's not 384 // valid, as opposed to an assert. 385 MemberTypeInfo &NewMember = I.Members.emplace_back( 386 getTypeInfoForType(F->getTypeSourceInfo()->getType(), LO), 387 F->getNameAsString(), 388 getFinalAccessSpecifier(Access, F->getAccessUnsafe())); 389 populateMemberTypeInfo(NewMember, F); 390 } 391 } 392 393 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { 394 for (const EnumConstantDecl *E : D->enumerators()) { 395 std::string ValueExpr; 396 if (const Expr *InitExpr = E->getInitExpr()) 397 ValueExpr = getSourceCode(D, InitExpr->getSourceRange()); 398 SmallString<16> ValueStr; 399 E->getInitVal().toString(ValueStr); 400 I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr); 401 ASTContext &Context = E->getASTContext(); 402 if (RawComment *Comment = 403 E->getASTContext().getRawCommentForDeclNoCache(E)) { 404 CommentInfo CInfo; 405 Comment->setAttached(); 406 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) { 407 EnumValueInfo &Member = I.Members.back(); 408 Member.Description.emplace_back(); 409 parseFullComment(Fc, Member.Description.back()); 410 } 411 } 412 } 413 } 414 415 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { 416 auto &LO = D->getLangOpts(); 417 for (const ParmVarDecl *P : D->parameters()) { 418 FieldTypeInfo &FieldInfo = I.Params.emplace_back( 419 getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString()); 420 FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange()); 421 } 422 } 423 424 // TODO: Remove the serialization of Parents and VirtualParents, this 425 // information is also extracted in the other definition of parseBases. 426 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { 427 // Don't parse bases if this isn't a definition. 428 if (!D->isThisDeclarationADefinition()) 429 return; 430 for (const CXXBaseSpecifier &B : D->bases()) { 431 if (B.isVirtual()) 432 continue; 433 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { 434 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); 435 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(), 436 InfoType::IT_record, B.getType().getAsString()); 437 } else if (const RecordDecl *P = getRecordDeclForType(B.getType())) 438 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), 439 InfoType::IT_record, P->getQualifiedNameAsString(), 440 getInfoRelativePath(P)); 441 else 442 I.Parents.emplace_back(SymbolID(), B.getType().getAsString()); 443 } 444 for (const CXXBaseSpecifier &B : D->vbases()) { 445 if (const RecordDecl *P = getRecordDeclForType(B.getType())) 446 I.VirtualParents.emplace_back( 447 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record, 448 P->getQualifiedNameAsString(), getInfoRelativePath(P)); 449 else 450 I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString()); 451 } 452 } 453 454 template <typename T> 455 static void 456 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, 457 const T *D, bool &IsInAnonymousNamespace) { 458 const DeclContext *DC = D->getDeclContext(); 459 do { 460 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) { 461 std::string Namespace; 462 if (N->isAnonymousNamespace()) { 463 Namespace = "@nonymous_namespace"; 464 IsInAnonymousNamespace = true; 465 } else 466 Namespace = N->getNameAsString(); 467 Namespaces.emplace_back(getUSRForDecl(N), Namespace, 468 InfoType::IT_namespace, 469 N->getQualifiedNameAsString()); 470 } else if (const auto *N = dyn_cast<RecordDecl>(DC)) 471 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 472 InfoType::IT_record, 473 N->getQualifiedNameAsString()); 474 else if (const auto *N = dyn_cast<FunctionDecl>(DC)) 475 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 476 InfoType::IT_function, 477 N->getQualifiedNameAsString()); 478 else if (const auto *N = dyn_cast<EnumDecl>(DC)) 479 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 480 InfoType::IT_enum, N->getQualifiedNameAsString()); 481 } while ((DC = DC->getParent())); 482 // The global namespace should be added to the list of namespaces if the decl 483 // corresponds to a Record and if it doesn't have any namespace (because this 484 // means it's in the global namespace). Also if its outermost namespace is a 485 // record because that record matches the previous condition mentioned. 486 if ((Namespaces.empty() && isa<RecordDecl>(D)) || 487 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record)) 488 Namespaces.emplace_back(SymbolID(), "GlobalNamespace", 489 InfoType::IT_namespace); 490 } 491 492 void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo, 493 const clang::Decl *D) { 494 if (const TemplateParameterList *ParamList = 495 D->getDescribedTemplateParams()) { 496 if (!TemplateInfo) { 497 TemplateInfo.emplace(); 498 } 499 for (const NamedDecl *ND : *ParamList) { 500 TemplateInfo->Params.emplace_back( 501 getSourceCode(ND, ND->getSourceRange())); 502 } 503 } 504 } 505 506 TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D, 507 const TemplateArgument &Arg) { 508 // The TemplateArgument's pretty printing handles all the normal cases 509 // well enough for our requirements. 510 std::string Str; 511 llvm::raw_string_ostream Stream(Str); 512 Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false); 513 return TemplateParamInfo(Str); 514 } 515 516 template <typename T> 517 static void populateInfo(Info &I, const T *D, const FullComment *C, 518 bool &IsInAnonymousNamespace) { 519 I.USR = getUSRForDecl(D); 520 I.Name = D->getNameAsString(); 521 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace); 522 if (C) { 523 I.Description.emplace_back(); 524 parseFullComment(C, I.Description.back()); 525 } 526 } 527 528 template <typename T> 529 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, 530 int LineNumber, StringRef Filename, 531 bool IsFileInRootDir, 532 bool &IsInAnonymousNamespace) { 533 populateInfo(I, D, C, IsInAnonymousNamespace); 534 if (D->isThisDeclarationADefinition()) 535 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir); 536 else 537 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir); 538 } 539 540 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, 541 const FullComment *FC, int LineNumber, 542 StringRef Filename, bool IsFileInRootDir, 543 bool &IsInAnonymousNamespace) { 544 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir, 545 IsInAnonymousNamespace); 546 auto &LO = D->getLangOpts(); 547 I.ReturnType = getTypeInfoForType(D->getReturnType(), LO); 548 parseParameters(I, D); 549 550 PopulateTemplateParameters(I.Template, D); 551 552 // Handle function template specializations. 553 if (const FunctionTemplateSpecializationInfo *FTSI = 554 D->getTemplateSpecializationInfo()) { 555 if (!I.Template) 556 I.Template.emplace(); 557 I.Template->Specialization.emplace(); 558 auto &Specialization = *I.Template->Specialization; 559 560 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate()); 561 562 // Template parameters to the specialization. 563 if (FTSI->TemplateArguments) { 564 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) { 565 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg)); 566 } 567 } 568 } 569 } 570 571 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) { 572 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo"); 573 574 ASTContext& Context = D->getASTContext(); 575 // TODO investigate whether we can use ASTContext::getCommentForDecl instead 576 // of this logic. See also similar code in Mapper.cpp. 577 RawComment *Comment = Context.getRawCommentForDeclNoCache(D); 578 if (!Comment) 579 return; 580 581 Comment->setAttached(); 582 if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) { 583 I.Description.emplace_back(); 584 parseFullComment(fc, I.Description.back()); 585 } 586 } 587 588 static void 589 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, 590 bool PublicOnly, bool IsParent, 591 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) { 592 // Don't parse bases if this isn't a definition. 593 if (!D->isThisDeclarationADefinition()) 594 return; 595 for (const CXXBaseSpecifier &B : D->bases()) { 596 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) { 597 if (const CXXRecordDecl *Base = 598 cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) { 599 // Initialized without USR and name, this will be set in the following 600 // if-else stmt. 601 BaseRecordInfo BI( 602 {}, "", getInfoRelativePath(Base), B.isVirtual(), 603 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()), 604 IsParent); 605 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { 606 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); 607 BI.USR = getUSRForDecl(D); 608 BI.Name = B.getType().getAsString(); 609 } else { 610 BI.USR = getUSRForDecl(Base); 611 BI.Name = Base->getNameAsString(); 612 } 613 parseFields(BI, Base, PublicOnly, BI.Access); 614 for (const auto &Decl : Base->decls()) 615 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) { 616 // Don't serialize private methods 617 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private || 618 !MD->isUserProvided()) 619 continue; 620 FunctionInfo FI; 621 FI.IsMethod = true; 622 // The seventh arg in populateFunctionInfo is a boolean passed by 623 // reference, its value is not relevant in here so it's not used 624 // anywhere besides the function call. 625 bool IsInAnonymousNamespace; 626 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{}, 627 /*FileName=*/{}, IsFileInRootDir, 628 IsInAnonymousNamespace); 629 FI.Access = 630 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); 631 BI.Children.Functions.emplace_back(std::move(FI)); 632 } 633 I.Bases.emplace_back(std::move(BI)); 634 // Call this function recursively to get the inherited classes of 635 // this base; these new bases will also get stored in the original 636 // RecordInfo: I. 637 parseBases(I, Base, IsFileInRootDir, PublicOnly, false, 638 I.Bases.back().Access); 639 } 640 } 641 } 642 } 643 644 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 645 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, 646 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 647 auto I = std::make_unique<NamespaceInfo>(); 648 bool IsInAnonymousNamespace = false; 649 populateInfo(*I, D, FC, IsInAnonymousNamespace); 650 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 651 return {}; 652 653 I->Name = D->isAnonymousNamespace() 654 ? llvm::SmallString<16>("@nonymous_namespace") 655 : I->Name; 656 I->Path = getInfoRelativePath(I->Namespace); 657 if (I->Namespace.empty() && I->USR == SymbolID()) 658 return {std::unique_ptr<Info>{std::move(I)}, nullptr}; 659 660 // Namespaces are inserted into the parent by reference, so we need to return 661 // both the parent and the record itself. 662 return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)}; 663 } 664 665 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 666 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, 667 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 668 auto I = std::make_unique<RecordInfo>(); 669 bool IsInAnonymousNamespace = false; 670 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir, 671 IsInAnonymousNamespace); 672 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 673 return {}; 674 675 I->TagType = D->getTagKind(); 676 parseFields(*I, D, PublicOnly); 677 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) { 678 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) { 679 I->Name = TD->getNameAsString(); 680 I->IsTypeDef = true; 681 } 682 // TODO: remove first call to parseBases, that function should be deleted 683 parseBases(*I, C); 684 parseBases(*I, C, IsFileInRootDir, PublicOnly, true); 685 } 686 I->Path = getInfoRelativePath(I->Namespace); 687 688 PopulateTemplateParameters(I->Template, D); 689 690 // Full and partial specializations. 691 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { 692 if (!I->Template) 693 I->Template.emplace(); 694 I->Template->Specialization.emplace(); 695 auto &Specialization = *I->Template->Specialization; 696 697 // What this is a specialization of. 698 auto SpecOf = CTSD->getSpecializedTemplateOrPartial(); 699 if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf)) 700 Specialization.SpecializationOf = getUSRForDecl(CTD); 701 else if (auto *CTPSD = 702 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf)) 703 Specialization.SpecializationOf = getUSRForDecl(CTPSD); 704 705 // Parameters to the specilization. For partial specializations, get the 706 // parameters "as written" from the ClassTemplatePartialSpecializationDecl 707 // because the non-explicit template parameters will have generated internal 708 // placeholder names rather than the names the user typed that match the 709 // template parameters. 710 if (const ClassTemplatePartialSpecializationDecl *CTPSD = 711 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) { 712 if (const ASTTemplateArgumentListInfo *AsWritten = 713 CTPSD->getTemplateArgsAsWritten()) { 714 for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) { 715 Specialization.Params.emplace_back( 716 getSourceCode(D, (*AsWritten)[i].getSourceRange())); 717 } 718 } 719 } else { 720 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) { 721 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg)); 722 } 723 } 724 } 725 726 // Records are inserted into the parent by reference, so we need to return 727 // both the parent and the record itself. 728 auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I); 729 return {std::move(I), std::move(Parent)}; 730 } 731 732 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 733 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, 734 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 735 FunctionInfo Func; 736 bool IsInAnonymousNamespace = false; 737 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, 738 IsInAnonymousNamespace); 739 Func.Access = clang::AccessSpecifier::AS_none; 740 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 741 return {}; 742 743 // Info is wrapped in its parent scope so is returned in the second position. 744 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))}; 745 } 746 747 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 748 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, 749 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 750 FunctionInfo Func; 751 bool IsInAnonymousNamespace = false; 752 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, 753 IsInAnonymousNamespace); 754 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 755 return {}; 756 757 Func.IsMethod = true; 758 759 const NamedDecl *Parent = nullptr; 760 if (const auto *SD = 761 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent())) 762 Parent = SD->getSpecializedTemplate(); 763 else 764 Parent = D->getParent(); 765 766 SymbolID ParentUSR = getUSRForDecl(Parent); 767 Func.Parent = 768 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record, 769 Parent->getQualifiedNameAsString()}; 770 Func.Access = D->getAccess(); 771 772 // Info is wrapped in its parent scope so is returned in the second position. 773 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))}; 774 } 775 776 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 777 emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, 778 StringRef File, bool IsFileInRootDir, bool PublicOnly) { 779 TypedefInfo Info; 780 781 bool IsInAnonymousNamespace = false; 782 populateInfo(Info, D, FC, IsInAnonymousNamespace); 783 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 784 return {}; 785 786 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir); 787 auto &LO = D->getLangOpts(); 788 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO); 789 if (Info.Underlying.Type.Name.empty()) { 790 // Typedef for an unnamed type. This is like "typedef struct { } Foo;" 791 // The record serializer explicitly checks for this syntax and constructs 792 // a record with that name, so we don't want to emit a duplicate here. 793 return {}; 794 } 795 Info.IsUsing = false; 796 797 // Info is wrapped in its parent scope so is returned in the second position. 798 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))}; 799 } 800 801 // A type alias is a C++ "using" declaration for a type. It gets mapped to a 802 // TypedefInfo with the IsUsing flag set. 803 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 804 emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, 805 StringRef File, bool IsFileInRootDir, bool PublicOnly) { 806 TypedefInfo Info; 807 808 bool IsInAnonymousNamespace = false; 809 populateInfo(Info, D, FC, IsInAnonymousNamespace); 810 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 811 return {}; 812 813 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir); 814 auto &LO = D->getLangOpts(); 815 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO); 816 Info.IsUsing = true; 817 818 // Info is wrapped in its parent scope so is returned in the second position. 819 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))}; 820 } 821 822 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 823 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, 824 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 825 EnumInfo Enum; 826 bool IsInAnonymousNamespace = false; 827 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir, 828 IsInAnonymousNamespace); 829 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 830 return {}; 831 832 Enum.Scoped = D->isScoped(); 833 if (D->isFixed()) { 834 auto Name = D->getIntegerType().getAsString(); 835 Enum.BaseType = TypeInfo(Name, Name); 836 } 837 parseEnumerators(Enum, D); 838 839 // Info is wrapped in its parent scope so is returned in the second position. 840 return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))}; 841 } 842 843 } // namespace serialize 844 } // namespace doc 845 } // namespace clang 846