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 class ClangDocCommentVisitor 28 : public ConstCommentVisitor<ClangDocCommentVisitor> { 29 public: 30 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} 31 32 void parseComment(const comments::Comment *C); 33 34 void visitTextComment(const TextComment *C); 35 void visitInlineCommandComment(const InlineCommandComment *C); 36 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 37 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 38 void visitBlockCommandComment(const BlockCommandComment *C); 39 void visitParamCommandComment(const ParamCommandComment *C); 40 void visitTParamCommandComment(const TParamCommandComment *C); 41 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 42 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 43 void visitVerbatimLineComment(const VerbatimLineComment *C); 44 45 private: 46 std::string getCommandName(unsigned CommandID) const; 47 bool isWhitespaceOnly(StringRef S) const; 48 49 CommentInfo &CurrentCI; 50 }; 51 52 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) { 53 CurrentCI.Kind = C->getCommentKindName(); 54 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C); 55 for (comments::Comment *Child : 56 llvm::make_range(C->child_begin(), C->child_end())) { 57 CurrentCI.Children.emplace_back(llvm::make_unique<CommentInfo>()); 58 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back()); 59 Visitor.parseComment(Child); 60 } 61 } 62 63 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { 64 if (!isWhitespaceOnly(C->getText())) 65 CurrentCI.Text = C->getText(); 66 } 67 68 void ClangDocCommentVisitor::visitInlineCommandComment( 69 const InlineCommandComment *C) { 70 CurrentCI.Name = getCommandName(C->getCommandID()); 71 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I) 72 CurrentCI.Args.push_back(C->getArgText(I)); 73 } 74 75 void ClangDocCommentVisitor::visitHTMLStartTagComment( 76 const HTMLStartTagComment *C) { 77 CurrentCI.Name = C->getTagName(); 78 CurrentCI.SelfClosing = C->isSelfClosing(); 79 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) { 80 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I); 81 CurrentCI.AttrKeys.push_back(Attr.Name); 82 CurrentCI.AttrValues.push_back(Attr.Value); 83 } 84 } 85 86 void ClangDocCommentVisitor::visitHTMLEndTagComment( 87 const HTMLEndTagComment *C) { 88 CurrentCI.Name = C->getTagName(); 89 CurrentCI.SelfClosing = true; 90 } 91 92 void ClangDocCommentVisitor::visitBlockCommandComment( 93 const BlockCommandComment *C) { 94 CurrentCI.Name = getCommandName(C->getCommandID()); 95 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I) 96 CurrentCI.Args.push_back(C->getArgText(I)); 97 } 98 99 void ClangDocCommentVisitor::visitParamCommandComment( 100 const ParamCommandComment *C) { 101 CurrentCI.Direction = 102 ParamCommandComment::getDirectionAsString(C->getDirection()); 103 CurrentCI.Explicit = C->isDirectionExplicit(); 104 if (C->hasParamName()) 105 CurrentCI.ParamName = C->getParamNameAsWritten(); 106 } 107 108 void ClangDocCommentVisitor::visitTParamCommandComment( 109 const TParamCommandComment *C) { 110 if (C->hasParamName()) 111 CurrentCI.ParamName = C->getParamNameAsWritten(); 112 } 113 114 void ClangDocCommentVisitor::visitVerbatimBlockComment( 115 const VerbatimBlockComment *C) { 116 CurrentCI.Name = getCommandName(C->getCommandID()); 117 CurrentCI.CloseName = C->getCloseName(); 118 } 119 120 void ClangDocCommentVisitor::visitVerbatimBlockLineComment( 121 const VerbatimBlockLineComment *C) { 122 if (!isWhitespaceOnly(C->getText())) 123 CurrentCI.Text = C->getText(); 124 } 125 126 void ClangDocCommentVisitor::visitVerbatimLineComment( 127 const VerbatimLineComment *C) { 128 if (!isWhitespaceOnly(C->getText())) 129 CurrentCI.Text = C->getText(); 130 } 131 132 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const { 133 return std::all_of(S.begin(), S.end(), isspace); 134 } 135 136 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { 137 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); 138 if (Info) 139 return Info->Name; 140 // TODO: Add parsing for \file command. 141 return "<not a builtin command>"; 142 } 143 144 // Serializing functions. 145 146 template <typename T> static std::string serialize(T &I) { 147 SmallString<2048> Buffer; 148 llvm::BitstreamWriter Stream(Buffer); 149 ClangDocBitcodeWriter Writer(Stream); 150 Writer.emitBlock(I); 151 return Buffer.str().str(); 152 } 153 154 std::string serialize(std::unique_ptr<Info> &I) { 155 switch (I->IT) { 156 case InfoType::IT_namespace: 157 return serialize(*static_cast<NamespaceInfo *>(I.get())); 158 case InfoType::IT_record: 159 return serialize(*static_cast<RecordInfo *>(I.get())); 160 case InfoType::IT_enum: 161 return serialize(*static_cast<EnumInfo *>(I.get())); 162 case InfoType::IT_function: 163 return serialize(*static_cast<FunctionInfo *>(I.get())); 164 default: 165 return ""; 166 } 167 } 168 169 static void parseFullComment(const FullComment *C, CommentInfo &CI) { 170 ClangDocCommentVisitor Visitor(CI); 171 Visitor.parseComment(C); 172 } 173 174 static SymbolID getUSRForDecl(const Decl *D) { 175 llvm::SmallString<128> USR; 176 if (index::generateUSRForDecl(D, USR)) 177 return SymbolID(); 178 return hashUSR(USR); 179 } 180 181 static RecordDecl *getDeclForType(const QualType &T) { 182 auto *Ty = T->getAs<RecordType>(); 183 if (!Ty) 184 return nullptr; 185 return Ty->getDecl()->getDefinition(); 186 } 187 188 static bool isPublic(const clang::AccessSpecifier AS, 189 const clang::Linkage Link) { 190 if (AS == clang::AccessSpecifier::AS_private) 191 return false; 192 else if ((Link == clang::Linkage::ModuleLinkage) || 193 (Link == clang::Linkage::ExternalLinkage)) 194 return true; 195 return false; // otherwise, linkage is some form of internal linkage 196 } 197 198 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) { 199 for (const FieldDecl *F : D->fields()) { 200 if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal())) 201 continue; 202 if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) { 203 // Use getAccessUnsafe so that we just get the default AS_none if it's not 204 // valid, as opposed to an assert. 205 if (const auto *N = dyn_cast<EnumDecl>(T)) { 206 I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(), 207 InfoType::IT_enum, F->getNameAsString(), 208 N->getAccessUnsafe()); 209 continue; 210 } else if (const auto *N = dyn_cast<RecordDecl>(T)) { 211 I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(), 212 InfoType::IT_record, F->getNameAsString(), 213 N->getAccessUnsafe()); 214 continue; 215 } 216 } 217 I.Members.emplace_back(F->getTypeSourceInfo()->getType().getAsString(), 218 F->getNameAsString(), F->getAccessUnsafe()); 219 } 220 } 221 222 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { 223 for (const EnumConstantDecl *E : D->enumerators()) 224 I.Members.emplace_back(E->getNameAsString()); 225 } 226 227 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { 228 for (const ParmVarDecl *P : D->parameters()) { 229 if (const auto *T = getDeclForType(P->getOriginalType())) { 230 if (const auto *N = dyn_cast<EnumDecl>(T)) { 231 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), 232 InfoType::IT_enum, P->getNameAsString()); 233 continue; 234 } else if (const auto *N = dyn_cast<RecordDecl>(T)) { 235 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), 236 InfoType::IT_record, P->getNameAsString()); 237 continue; 238 } 239 } 240 I.Params.emplace_back(P->getOriginalType().getAsString(), 241 P->getNameAsString()); 242 } 243 } 244 245 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { 246 // Don't parse bases if this isn't a definition. 247 if (!D->isThisDeclarationADefinition()) 248 return; 249 for (const CXXBaseSpecifier &B : D->bases()) { 250 if (B.isVirtual()) 251 continue; 252 if (const auto *P = getDeclForType(B.getType())) 253 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), 254 InfoType::IT_record); 255 else 256 I.Parents.emplace_back(B.getType().getAsString()); 257 } 258 for (const CXXBaseSpecifier &B : D->vbases()) { 259 if (const auto *P = getDeclForType(B.getType())) 260 I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(), 261 InfoType::IT_record); 262 else 263 I.VirtualParents.emplace_back(B.getType().getAsString()); 264 } 265 } 266 267 template <typename T> 268 static void 269 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, 270 const T *D) { 271 const auto *DC = dyn_cast<DeclContext>(D); 272 while ((DC = DC->getParent())) { 273 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) 274 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 275 InfoType::IT_namespace); 276 else if (const auto *N = dyn_cast<RecordDecl>(DC)) 277 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 278 InfoType::IT_record); 279 else if (const auto *N = dyn_cast<FunctionDecl>(DC)) 280 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 281 InfoType::IT_function); 282 else if (const auto *N = dyn_cast<EnumDecl>(DC)) 283 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), 284 InfoType::IT_enum); 285 } 286 } 287 288 template <typename T> 289 static void populateInfo(Info &I, const T *D, const FullComment *C) { 290 I.USR = getUSRForDecl(D); 291 I.Name = D->getNameAsString(); 292 populateParentNamespaces(I.Namespace, D); 293 if (C) { 294 I.Description.emplace_back(); 295 parseFullComment(C, I.Description.back()); 296 } 297 } 298 299 template <typename T> 300 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, 301 int LineNumber, StringRef Filename) { 302 populateInfo(I, D, C); 303 if (D->isThisDeclarationADefinition()) 304 I.DefLoc.emplace(LineNumber, Filename); 305 else 306 I.Loc.emplace_back(LineNumber, Filename); 307 } 308 309 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, 310 const FullComment *FC, int LineNumber, 311 StringRef Filename) { 312 populateSymbolInfo(I, D, FC, LineNumber, Filename); 313 if (const auto *T = getDeclForType(D->getReturnType())) { 314 if (dyn_cast<EnumDecl>(T)) 315 I.ReturnType = 316 TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_enum); 317 else if (dyn_cast<RecordDecl>(T)) 318 I.ReturnType = 319 TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_record); 320 } else { 321 I.ReturnType = TypeInfo(D->getReturnType().getAsString()); 322 } 323 parseParameters(I, D); 324 } 325 326 std::unique_ptr<Info> emitInfo(const NamespaceDecl *D, const FullComment *FC, 327 int LineNumber, llvm::StringRef File, 328 bool PublicOnly) { 329 if (PublicOnly && ((D->isAnonymousNamespace()) || 330 !isPublic(D->getAccess(), D->getLinkageInternal()))) 331 return nullptr; 332 auto I = llvm::make_unique<NamespaceInfo>(); 333 populateInfo(*I, D, FC); 334 return std::unique_ptr<Info>{std::move(I)}; 335 } 336 337 std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC, 338 int LineNumber, llvm::StringRef File, 339 bool PublicOnly) { 340 if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) 341 return nullptr; 342 auto I = llvm::make_unique<RecordInfo>(); 343 populateSymbolInfo(*I, D, FC, LineNumber, File); 344 I->TagType = D->getTagKind(); 345 parseFields(*I, D, PublicOnly); 346 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) 347 parseBases(*I, C); 348 return std::unique_ptr<Info>{std::move(I)}; 349 } 350 351 std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC, 352 int LineNumber, llvm::StringRef File, 353 bool PublicOnly) { 354 if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) 355 return nullptr; 356 FunctionInfo Func; 357 populateFunctionInfo(Func, D, FC, LineNumber, File); 358 Func.Access = clang::AccessSpecifier::AS_none; 359 360 // Wrap in enclosing scope 361 auto I = llvm::make_unique<NamespaceInfo>(); 362 if (!Func.Namespace.empty()) 363 I->USR = Func.Namespace[0].USR; 364 else 365 I->USR = SymbolID(); 366 I->ChildFunctions.emplace_back(std::move(Func)); 367 return std::unique_ptr<Info>{std::move(I)}; 368 } 369 370 std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC, 371 int LineNumber, llvm::StringRef File, 372 bool PublicOnly) { 373 if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) 374 return nullptr; 375 FunctionInfo Func; 376 populateFunctionInfo(Func, D, FC, LineNumber, File); 377 Func.IsMethod = true; 378 379 SymbolID ParentUSR = getUSRForDecl(D->getParent()); 380 Func.Parent = Reference{ParentUSR, D->getParent()->getNameAsString(), 381 InfoType::IT_record}; 382 Func.Access = D->getAccess(); 383 384 // Wrap in enclosing scope 385 auto I = llvm::make_unique<RecordInfo>(); 386 I->USR = ParentUSR; 387 I->ChildFunctions.emplace_back(std::move(Func)); 388 return std::unique_ptr<Info>{std::move(I)}; 389 } 390 391 std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC, 392 int LineNumber, llvm::StringRef File, 393 bool PublicOnly) { 394 if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) 395 return nullptr; 396 EnumInfo Enum; 397 populateSymbolInfo(Enum, D, FC, LineNumber, File); 398 Enum.Scoped = D->isScoped(); 399 parseEnumerators(Enum, D); 400 401 // Wrap in enclosing scope 402 if (!Enum.Namespace.empty()) { 403 switch (Enum.Namespace[0].RefType) { 404 case InfoType::IT_namespace: { 405 auto I = llvm::make_unique<NamespaceInfo>(); 406 I->USR = Enum.Namespace[0].USR; 407 I->ChildEnums.emplace_back(std::move(Enum)); 408 return std::unique_ptr<Info>{std::move(I)}; 409 } 410 case InfoType::IT_record: { 411 auto I = llvm::make_unique<RecordInfo>(); 412 I->USR = Enum.Namespace[0].USR; 413 I->ChildEnums.emplace_back(std::move(Enum)); 414 return std::unique_ptr<Info>{std::move(I)}; 415 } 416 default: 417 break; 418 } 419 } 420 421 // Put in global namespace 422 auto I = llvm::make_unique<NamespaceInfo>(); 423 I->USR = SymbolID(); 424 I->ChildEnums.emplace_back(std::move(Enum)); 425 return std::unique_ptr<Info>{std::move(I)}; 426 } 427 428 } // namespace serialize 429 } // namespace doc 430 } // namespace clang 431