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 "llvm/ADT/Hashing.h" 14 #include "llvm/ADT/StringExtras.h" 15 #include "llvm/Support/SHA1.h" 16 17 using clang::comments::FullComment; 18 19 namespace clang { 20 namespace doc { 21 namespace serialize { 22 23 SymbolID hashUSR(llvm::StringRef USR) { 24 return llvm::SHA1::hash(arrayRefFromStringRef(USR)); 25 } 26 27 template <typename T> 28 static void 29 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, 30 const T *D, bool &IsAnonymousNamespace); 31 32 // A function to extract the appropriate relative path for a given info's 33 // documentation. The path returned is a composite of the parent namespaces. 34 // 35 // Example: Given the below, the directory path for class C info will be 36 // <root>/A/B 37 // 38 // namespace A { 39 // namespace B { 40 // 41 // class C {}; 42 // 43 // } 44 // } 45 llvm::SmallString<128> 46 getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) { 47 llvm::SmallString<128> Path; 48 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R) 49 llvm::sys::path::append(Path, R->Name); 50 return Path; 51 } 52 53 llvm::SmallString<128> getInfoRelativePath(const Decl *D) { 54 llvm::SmallVector<Reference, 4> Namespaces; 55 // The third arg in populateParentNamespaces is a boolean passed by reference, 56 // its value is not relevant in here so it's not used anywhere besides the 57 // function call 58 bool B = true; 59 populateParentNamespaces(Namespaces, D, B); 60 return getInfoRelativePath(Namespaces); 61 } 62 63 class ClangDocCommentVisitor 64 : public ConstCommentVisitor<ClangDocCommentVisitor> { 65 public: 66 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} 67 68 void parseComment(const comments::Comment *C); 69 70 void visitTextComment(const TextComment *C); 71 void visitInlineCommandComment(const InlineCommandComment *C); 72 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 73 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 74 void visitBlockCommandComment(const BlockCommandComment *C); 75 void visitParamCommandComment(const ParamCommandComment *C); 76 void visitTParamCommandComment(const TParamCommandComment *C); 77 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 78 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 79 void visitVerbatimLineComment(const VerbatimLineComment *C); 80 81 private: 82 std::string getCommandName(unsigned CommandID) const; 83 bool isWhitespaceOnly(StringRef S) const; 84 85 CommentInfo &CurrentCI; 86 }; 87 88 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) { 89 CurrentCI.Kind = C->getCommentKindName(); 90 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C); 91 for (comments::Comment *Child : 92 llvm::make_range(C->child_begin(), C->child_end())) { 93 CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>()); 94 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back()); 95 Visitor.parseComment(Child); 96 } 97 } 98 99 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { 100 if (!isWhitespaceOnly(C->getText())) 101 CurrentCI.Text = C->getText(); 102 } 103 104 void ClangDocCommentVisitor::visitInlineCommandComment( 105 const InlineCommandComment *C) { 106 CurrentCI.Name = getCommandName(C->getCommandID()); 107 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I) 108 CurrentCI.Args.push_back(C->getArgText(I)); 109 } 110 111 void ClangDocCommentVisitor::visitHTMLStartTagComment( 112 const HTMLStartTagComment *C) { 113 CurrentCI.Name = C->getTagName(); 114 CurrentCI.SelfClosing = C->isSelfClosing(); 115 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) { 116 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I); 117 CurrentCI.AttrKeys.push_back(Attr.Name); 118 CurrentCI.AttrValues.push_back(Attr.Value); 119 } 120 } 121 122 void ClangDocCommentVisitor::visitHTMLEndTagComment( 123 const HTMLEndTagComment *C) { 124 CurrentCI.Name = C->getTagName(); 125 CurrentCI.SelfClosing = true; 126 } 127 128 void ClangDocCommentVisitor::visitBlockCommandComment( 129 const BlockCommandComment *C) { 130 CurrentCI.Name = getCommandName(C->getCommandID()); 131 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I) 132 CurrentCI.Args.push_back(C->getArgText(I)); 133 } 134 135 void ClangDocCommentVisitor::visitParamCommandComment( 136 const ParamCommandComment *C) { 137 CurrentCI.Direction = 138 ParamCommandComment::getDirectionAsString(C->getDirection()); 139 CurrentCI.Explicit = C->isDirectionExplicit(); 140 if (C->hasParamName()) 141 CurrentCI.ParamName = C->getParamNameAsWritten(); 142 } 143 144 void ClangDocCommentVisitor::visitTParamCommandComment( 145 const TParamCommandComment *C) { 146 if (C->hasParamName()) 147 CurrentCI.ParamName = C->getParamNameAsWritten(); 148 } 149 150 void ClangDocCommentVisitor::visitVerbatimBlockComment( 151 const VerbatimBlockComment *C) { 152 CurrentCI.Name = getCommandName(C->getCommandID()); 153 CurrentCI.CloseName = C->getCloseName(); 154 } 155 156 void ClangDocCommentVisitor::visitVerbatimBlockLineComment( 157 const VerbatimBlockLineComment *C) { 158 if (!isWhitespaceOnly(C->getText())) 159 CurrentCI.Text = C->getText(); 160 } 161 162 void ClangDocCommentVisitor::visitVerbatimLineComment( 163 const VerbatimLineComment *C) { 164 if (!isWhitespaceOnly(C->getText())) 165 CurrentCI.Text = C->getText(); 166 } 167 168 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const { 169 return std::all_of(S.begin(), S.end(), isspace); 170 } 171 172 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { 173 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); 174 if (Info) 175 return Info->Name; 176 // TODO: Add parsing for \file command. 177 return "<not a builtin command>"; 178 } 179 180 // Serializing functions. 181 182 template <typename T> static std::string serialize(T &I) { 183 SmallString<2048> Buffer; 184 llvm::BitstreamWriter Stream(Buffer); 185 ClangDocBitcodeWriter Writer(Stream); 186 Writer.emitBlock(I); 187 return Buffer.str().str(); 188 } 189 190 std::string serialize(std::unique_ptr<Info> &I) { 191 switch (I->IT) { 192 case InfoType::IT_namespace: 193 return serialize(*static_cast<NamespaceInfo *>(I.get())); 194 case InfoType::IT_record: 195 return serialize(*static_cast<RecordInfo *>(I.get())); 196 case InfoType::IT_enum: 197 return serialize(*static_cast<EnumInfo *>(I.get())); 198 case InfoType::IT_function: 199 return serialize(*static_cast<FunctionInfo *>(I.get())); 200 default: 201 return ""; 202 } 203 } 204 205 static void parseFullComment(const FullComment *C, CommentInfo &CI) { 206 ClangDocCommentVisitor Visitor(CI); 207 Visitor.parseComment(C); 208 } 209 210 static SymbolID getUSRForDecl(const Decl *D) { 211 llvm::SmallString<128> USR; 212 if (index::generateUSRForDecl(D, USR)) 213 return SymbolID(); 214 return hashUSR(USR); 215 } 216 217 static RecordDecl *getDeclForType(const QualType &T) { 218 if (const RecordDecl *D = T->getAsRecordDecl()) 219 return D->getDefinition(); 220 return nullptr; 221 } 222 223 static bool isPublic(const clang::AccessSpecifier AS, 224 const clang::Linkage Link) { 225 if (AS == clang::AccessSpecifier::AS_private) 226 return false; 227 else if ((Link == clang::Linkage::ModuleLinkage) || 228 (Link == clang::Linkage::ExternalLinkage)) 229 return true; 230 return false; // otherwise, linkage is some form of internal linkage 231 } 232 233 static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace, 234 const NamedDecl *D) { 235 bool IsAnonymousNamespace = false; 236 if (const auto *N = dyn_cast<NamespaceDecl>(D)) 237 IsAnonymousNamespace = N->isAnonymousNamespace(); 238 return !PublicOnly || 239 (!IsInAnonymousNamespace && !IsAnonymousNamespace && 240 isPublic(D->getAccessUnsafe(), D->getLinkageInternal())); 241 } 242 243 // There are two uses for this function. 244 // 1) Getting the resulting mode of inheritance of a record. 245 // Example: class A {}; class B : private A {}; class C : public B {}; 246 // It's explicit that C is publicly inherited from C and B is privately 247 // inherited from A. It's not explicit but C is also privately inherited from 248 // A. This is the AS that this function calculates. FirstAS is the 249 // inheritance mode of `class C : B` and SecondAS is the inheritance mode of 250 // `class B : A`. 251 // 2) Getting the inheritance mode of an inherited attribute / method. 252 // Example : class A { public: int M; }; class B : private A {}; 253 // Class B is inherited from class A, which has a public attribute. This 254 // attribute is now part of the derived class B but it's not public. This 255 // will be private because the inheritance is private. This is the AS that 256 // this function calculates. FirstAS is the inheritance mode and SecondAS is 257 // the AS of the attribute / method. 258 static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, 259 AccessSpecifier SecondAS) { 260 if (FirstAS == AccessSpecifier::AS_none || 261 SecondAS == AccessSpecifier::AS_none) 262 return AccessSpecifier::AS_none; 263 if (FirstAS == AccessSpecifier::AS_private || 264 SecondAS == AccessSpecifier::AS_private) 265 return AccessSpecifier::AS_private; 266 if (FirstAS == AccessSpecifier::AS_protected || 267 SecondAS == AccessSpecifier::AS_protected) 268 return AccessSpecifier::AS_protected; 269 return AccessSpecifier::AS_public; 270 } 271 272 // The Access parameter is only provided when parsing the field of an inherited 273 // record, the access specification of the field depends on the inheritance mode 274 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, 275 AccessSpecifier Access = AccessSpecifier::AS_public) { 276 for (const FieldDecl *F : D->fields()) { 277 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) 278 continue; 279 if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) { 280 // Use getAccessUnsafe so that we just get the default AS_none if it's not 281 // valid, as opposed to an assert. 282 if (const auto *N = dyn_cast<EnumDecl>(T)) { 283 I.Members.emplace_back( 284 getUSRForDecl(T), N->getNameAsString(), InfoType::IT_enum, 285 getInfoRelativePath(N), F->getNameAsString(), 286 getFinalAccessSpecifier(Access, N->getAccessUnsafe())); 287 continue; 288 } else if (const auto *N = dyn_cast<RecordDecl>(T)) { 289 I.Members.emplace_back( 290 getUSRForDecl(T), N->getNameAsString(), InfoType::IT_record, 291 getInfoRelativePath(N), F->getNameAsString(), 292 getFinalAccessSpecifier(Access, N->getAccessUnsafe())); 293 continue; 294 } 295 } 296 I.Members.emplace_back( 297 F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(), 298 getFinalAccessSpecifier(Access, F->getAccessUnsafe())); 299 } 300 } 301 302 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { 303 for (const EnumConstantDecl *E : D->enumerators()) 304 I.Members.emplace_back(E->getNameAsString()); 305 } 306 307 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { 308 for (const ParmVarDecl *P : D->parameters()) { 309 if (const auto *T = getDeclForType(P->getOriginalType())) { 310 if (const auto *N = dyn_cast<EnumDecl>(T)) { 311 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), 312 InfoType::IT_enum, getInfoRelativePath(N), 313 P->getNameAsString()); 314 continue; 315 } else if (const auto *N = dyn_cast<RecordDecl>(T)) { 316 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), 317 InfoType::IT_record, getInfoRelativePath(N), 318 P->getNameAsString()); 319 continue; 320 } 321 } 322 I.Params.emplace_back(P->getOriginalType().getAsString(), 323 P->getNameAsString()); 324 } 325 } 326 327 // TODO: Remove the serialization of Parents and VirtualParents, this 328 // information is also extracted in the other definition of parseBases. 329 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { 330 // Don't parse bases if this isn't a definition. 331 if (!D->isThisDeclarationADefinition()) 332 return; 333 for (const CXXBaseSpecifier &B : D->bases()) { 334 if (B.isVirtual()) 335 continue; 336 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { 337 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); 338 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(), 339 InfoType::IT_record); 340 } else if (const RecordDecl *P = getDeclForType(B.getType())) 341 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), 342 InfoType::IT_record, getInfoRelativePath(P)); 343 else 344 I.Parents.emplace_back(B.getType().getAsString()); 345 } 346 for (const CXXBaseSpecifier &B : D->vbases()) { 347 if (const auto *P = getDeclForType(B.getType())) 348 I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(), 349 InfoType::IT_record, 350 getInfoRelativePath(P)); 351 else 352 I.VirtualParents.emplace_back(B.getType().getAsString()); 353 } 354 } 355 356 template <typename T> 357 static void 358 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, 359 const T *D, bool &IsInAnonymousNamespace) { 360 const auto *DC = cast<DeclContext>(D); 361 while ((DC = DC->getParent())) { 362 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) { 363 std::string Namespace; 364 if (N->isAnonymousNamespace()) { 365 Namespace = "@nonymous_namespace"; 366 IsInAnonymousNamespace = true; 367 } else 368 Namespace = N->getNameAsString(); 369 Namespaces.emplace_back(getUSRForDecl(N), Namespace, 370 InfoType::IT_namespace); 371 } else if (const auto *N = dyn_cast<RecordDecl>(DC)) 372 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 373 InfoType::IT_record); 374 else if (const auto *N = dyn_cast<FunctionDecl>(DC)) 375 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 376 InfoType::IT_function); 377 else if (const auto *N = dyn_cast<EnumDecl>(DC)) 378 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 379 InfoType::IT_enum); 380 } 381 // The global namespace should be added to the list of namespaces if the decl 382 // corresponds to a Record and if it doesn't have any namespace (because this 383 // means it's in the global namespace). Also if its outermost namespace is a 384 // record because that record matches the previous condition mentioned. 385 if ((Namespaces.empty() && isa<RecordDecl>(D)) || 386 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record)) 387 Namespaces.emplace_back(SymbolID(), "GlobalNamespace", 388 InfoType::IT_namespace); 389 } 390 391 template <typename T> 392 static void populateInfo(Info &I, const T *D, const FullComment *C, 393 bool &IsInAnonymousNamespace) { 394 I.USR = getUSRForDecl(D); 395 I.Name = D->getNameAsString(); 396 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace); 397 if (C) { 398 I.Description.emplace_back(); 399 parseFullComment(C, I.Description.back()); 400 } 401 } 402 403 template <typename T> 404 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, 405 int LineNumber, StringRef Filename, 406 bool IsFileInRootDir, 407 bool &IsInAnonymousNamespace) { 408 populateInfo(I, D, C, IsInAnonymousNamespace); 409 if (D->isThisDeclarationADefinition()) 410 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir); 411 else 412 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir); 413 } 414 415 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, 416 const FullComment *FC, int LineNumber, 417 StringRef Filename, bool IsFileInRootDir, 418 bool &IsInAnonymousNamespace) { 419 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir, 420 IsInAnonymousNamespace); 421 if (const auto *T = getDeclForType(D->getReturnType())) { 422 if (isa<EnumDecl>(T)) 423 I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), 424 InfoType::IT_enum, getInfoRelativePath(T)); 425 else if (isa<RecordDecl>(T)) 426 I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), 427 InfoType::IT_record, getInfoRelativePath(T)); 428 } else { 429 I.ReturnType = TypeInfo(D->getReturnType().getAsString()); 430 } 431 parseParameters(I, D); 432 } 433 434 static void 435 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, 436 bool PublicOnly, bool IsParent, 437 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) { 438 // Don't parse bases if this isn't a definition. 439 if (!D->isThisDeclarationADefinition()) 440 return; 441 for (const CXXBaseSpecifier &B : D->bases()) { 442 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) { 443 if (const CXXRecordDecl *Base = 444 cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) { 445 // Initialized without USR and name, this will be set in the following 446 // if-else stmt. 447 BaseRecordInfo BI( 448 {}, "", getInfoRelativePath(Base), B.isVirtual(), 449 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()), 450 IsParent); 451 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { 452 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); 453 BI.USR = getUSRForDecl(D); 454 BI.Name = B.getType().getAsString(); 455 } else { 456 BI.USR = getUSRForDecl(Base); 457 BI.Name = Base->getNameAsString(); 458 } 459 parseFields(BI, Base, PublicOnly, BI.Access); 460 for (const auto &Decl : Base->decls()) 461 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) { 462 // Don't serialize private methods 463 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private || 464 !MD->isUserProvided()) 465 continue; 466 FunctionInfo FI; 467 FI.IsMethod = true; 468 // The seventh arg in populateFunctionInfo is a boolean passed by 469 // reference, its value is not relevant in here so it's not used 470 // anywhere besides the function call. 471 bool IsInAnonymousNamespace; 472 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{}, 473 /*FileName=*/{}, IsFileInRootDir, 474 IsInAnonymousNamespace); 475 FI.Access = 476 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); 477 BI.ChildFunctions.emplace_back(std::move(FI)); 478 } 479 I.Bases.emplace_back(std::move(BI)); 480 // Call this function recursively to get the inherited classes of 481 // this base; these new bases will also get stored in the original 482 // RecordInfo: I. 483 parseBases(I, Base, IsFileInRootDir, PublicOnly, false, 484 I.Bases.back().Access); 485 } 486 } 487 } 488 } 489 490 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 491 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, 492 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 493 auto I = std::make_unique<NamespaceInfo>(); 494 bool IsInAnonymousNamespace = false; 495 populateInfo(*I, D, FC, IsInAnonymousNamespace); 496 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 497 return {}; 498 499 I->Name = D->isAnonymousNamespace() 500 ? llvm::SmallString<16>("@nonymous_namespace") 501 : I->Name; 502 I->Path = getInfoRelativePath(I->Namespace); 503 if (I->Namespace.empty() && I->USR == SymbolID()) 504 return {std::unique_ptr<Info>{std::move(I)}, nullptr}; 505 506 auto ParentI = std::make_unique<NamespaceInfo>(); 507 ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR; 508 ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace, 509 getInfoRelativePath(I->Namespace)); 510 if (I->Namespace.empty()) 511 ParentI->Path = getInfoRelativePath(ParentI->Namespace); 512 return {std::unique_ptr<Info>{std::move(I)}, 513 std::unique_ptr<Info>{std::move(ParentI)}}; 514 } 515 516 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 517 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, 518 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 519 auto I = std::make_unique<RecordInfo>(); 520 bool IsInAnonymousNamespace = false; 521 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir, 522 IsInAnonymousNamespace); 523 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 524 return {}; 525 526 I->TagType = D->getTagKind(); 527 parseFields(*I, D, PublicOnly); 528 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) { 529 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) { 530 I->Name = TD->getNameAsString(); 531 I->IsTypeDef = true; 532 } 533 // TODO: remove first call to parseBases, that function should be deleted 534 parseBases(*I, C); 535 parseBases(*I, C, IsFileInRootDir, PublicOnly, true); 536 } 537 I->Path = getInfoRelativePath(I->Namespace); 538 539 switch (I->Namespace[0].RefType) { 540 case InfoType::IT_namespace: { 541 auto ParentI = std::make_unique<NamespaceInfo>(); 542 ParentI->USR = I->Namespace[0].USR; 543 ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, 544 getInfoRelativePath(I->Namespace)); 545 return {std::unique_ptr<Info>{std::move(I)}, 546 std::unique_ptr<Info>{std::move(ParentI)}}; 547 } 548 case InfoType::IT_record: { 549 auto ParentI = std::make_unique<RecordInfo>(); 550 ParentI->USR = I->Namespace[0].USR; 551 ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, 552 getInfoRelativePath(I->Namespace)); 553 return {std::unique_ptr<Info>{std::move(I)}, 554 std::unique_ptr<Info>{std::move(ParentI)}}; 555 } 556 default: 557 llvm_unreachable("Invalid reference type for parent namespace"); 558 } 559 } 560 561 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 562 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, 563 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 564 FunctionInfo Func; 565 bool IsInAnonymousNamespace = false; 566 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, 567 IsInAnonymousNamespace); 568 Func.Access = clang::AccessSpecifier::AS_none; 569 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 570 return {}; 571 572 // Wrap in enclosing scope 573 auto ParentI = std::make_unique<NamespaceInfo>(); 574 if (!Func.Namespace.empty()) 575 ParentI->USR = Func.Namespace[0].USR; 576 else 577 ParentI->USR = SymbolID(); 578 if (Func.Namespace.empty()) 579 ParentI->Path = getInfoRelativePath(ParentI->Namespace); 580 ParentI->ChildFunctions.emplace_back(std::move(Func)); 581 // Info is wrapped in its parent scope so it's returned in the second position 582 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; 583 } 584 585 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 586 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, 587 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 588 FunctionInfo Func; 589 bool IsInAnonymousNamespace = false; 590 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, 591 IsInAnonymousNamespace); 592 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 593 return {}; 594 595 Func.IsMethod = true; 596 597 const NamedDecl *Parent = nullptr; 598 if (const auto *SD = 599 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent())) 600 Parent = SD->getSpecializedTemplate(); 601 else 602 Parent = D->getParent(); 603 604 SymbolID ParentUSR = getUSRForDecl(Parent); 605 Func.Parent = 606 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record}; 607 Func.Access = D->getAccess(); 608 609 // Wrap in enclosing scope 610 auto ParentI = std::make_unique<RecordInfo>(); 611 ParentI->USR = ParentUSR; 612 ParentI->ChildFunctions.emplace_back(std::move(Func)); 613 // Info is wrapped in its parent scope so it's returned in the second position 614 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; 615 } 616 617 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> 618 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, 619 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { 620 EnumInfo Enum; 621 bool IsInAnonymousNamespace = false; 622 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir, 623 IsInAnonymousNamespace); 624 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) 625 return {}; 626 627 Enum.Scoped = D->isScoped(); 628 parseEnumerators(Enum, D); 629 630 // Put in global namespace 631 if (Enum.Namespace.empty()) { 632 auto ParentI = std::make_unique<NamespaceInfo>(); 633 ParentI->USR = SymbolID(); 634 ParentI->ChildEnums.emplace_back(std::move(Enum)); 635 ParentI->Path = getInfoRelativePath(ParentI->Namespace); 636 // Info is wrapped in its parent scope so it's returned in the second 637 // position 638 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; 639 } 640 641 // Wrap in enclosing scope 642 switch (Enum.Namespace[0].RefType) { 643 case InfoType::IT_namespace: { 644 auto ParentI = std::make_unique<NamespaceInfo>(); 645 ParentI->USR = Enum.Namespace[0].USR; 646 ParentI->ChildEnums.emplace_back(std::move(Enum)); 647 // Info is wrapped in its parent scope so it's returned in the second 648 // position 649 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; 650 } 651 case InfoType::IT_record: { 652 auto ParentI = std::make_unique<RecordInfo>(); 653 ParentI->USR = Enum.Namespace[0].USR; 654 ParentI->ChildEnums.emplace_back(std::move(Enum)); 655 // Info is wrapped in its parent scope so it's returned in the second 656 // position 657 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; 658 } 659 default: 660 llvm_unreachable("Invalid reference type for parent namespace"); 661 } 662 } 663 664 } // namespace serialize 665 } // namespace doc 666 } // namespace clang 667