1 //===-- Serializer.cpp - ClangDoc Serializer --------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "Serialize.h" 11 #include "BitcodeWriter.h" 12 #include "clang/AST/Comment.h" 13 #include "clang/Index/USRGeneration.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 class ClangDocCommentVisitor 29 : public ConstCommentVisitor<ClangDocCommentVisitor> { 30 public: 31 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} 32 33 void parseComment(const comments::Comment *C); 34 35 void visitTextComment(const TextComment *C); 36 void visitInlineCommandComment(const InlineCommandComment *C); 37 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 38 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 39 void visitBlockCommandComment(const BlockCommandComment *C); 40 void visitParamCommandComment(const ParamCommandComment *C); 41 void visitTParamCommandComment(const TParamCommandComment *C); 42 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 43 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 44 void visitVerbatimLineComment(const VerbatimLineComment *C); 45 46 private: 47 std::string getCommandName(unsigned CommandID) const; 48 bool isWhitespaceOnly(StringRef S) const; 49 50 CommentInfo &CurrentCI; 51 }; 52 53 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) { 54 CurrentCI.Kind = C->getCommentKindName(); 55 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C); 56 for (comments::Comment *Child : 57 llvm::make_range(C->child_begin(), C->child_end())) { 58 CurrentCI.Children.emplace_back(llvm::make_unique<CommentInfo>()); 59 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back()); 60 Visitor.parseComment(Child); 61 } 62 } 63 64 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { 65 if (!isWhitespaceOnly(C->getText())) 66 CurrentCI.Text = C->getText(); 67 } 68 69 void ClangDocCommentVisitor::visitInlineCommandComment( 70 const InlineCommandComment *C) { 71 CurrentCI.Name = getCommandName(C->getCommandID()); 72 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I) 73 CurrentCI.Args.push_back(C->getArgText(I)); 74 } 75 76 void ClangDocCommentVisitor::visitHTMLStartTagComment( 77 const HTMLStartTagComment *C) { 78 CurrentCI.Name = C->getTagName(); 79 CurrentCI.SelfClosing = C->isSelfClosing(); 80 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) { 81 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I); 82 CurrentCI.AttrKeys.push_back(Attr.Name); 83 CurrentCI.AttrValues.push_back(Attr.Value); 84 } 85 } 86 87 void ClangDocCommentVisitor::visitHTMLEndTagComment( 88 const HTMLEndTagComment *C) { 89 CurrentCI.Name = C->getTagName(); 90 CurrentCI.SelfClosing = true; 91 } 92 93 void ClangDocCommentVisitor::visitBlockCommandComment( 94 const BlockCommandComment *C) { 95 CurrentCI.Name = getCommandName(C->getCommandID()); 96 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I) 97 CurrentCI.Args.push_back(C->getArgText(I)); 98 } 99 100 void ClangDocCommentVisitor::visitParamCommandComment( 101 const ParamCommandComment *C) { 102 CurrentCI.Direction = 103 ParamCommandComment::getDirectionAsString(C->getDirection()); 104 CurrentCI.Explicit = C->isDirectionExplicit(); 105 if (C->hasParamName()) 106 CurrentCI.ParamName = C->getParamNameAsWritten(); 107 } 108 109 void ClangDocCommentVisitor::visitTParamCommandComment( 110 const TParamCommandComment *C) { 111 if (C->hasParamName()) 112 CurrentCI.ParamName = C->getParamNameAsWritten(); 113 } 114 115 void ClangDocCommentVisitor::visitVerbatimBlockComment( 116 const VerbatimBlockComment *C) { 117 CurrentCI.Name = getCommandName(C->getCommandID()); 118 CurrentCI.CloseName = C->getCloseName(); 119 } 120 121 void ClangDocCommentVisitor::visitVerbatimBlockLineComment( 122 const VerbatimBlockLineComment *C) { 123 if (!isWhitespaceOnly(C->getText())) 124 CurrentCI.Text = C->getText(); 125 } 126 127 void ClangDocCommentVisitor::visitVerbatimLineComment( 128 const VerbatimLineComment *C) { 129 if (!isWhitespaceOnly(C->getText())) 130 CurrentCI.Text = C->getText(); 131 } 132 133 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const { 134 return std::all_of(S.begin(), S.end(), isspace); 135 } 136 137 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { 138 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); 139 if (Info) 140 return Info->Name; 141 // TODO: Add parsing for \file command. 142 return "<not a builtin command>"; 143 } 144 145 // Serializing functions. 146 147 template <typename T> static std::string serialize(T &I) { 148 SmallString<2048> Buffer; 149 llvm::BitstreamWriter Stream(Buffer); 150 ClangDocBitcodeWriter Writer(Stream); 151 Writer.emitBlock(I); 152 return Buffer.str().str(); 153 } 154 155 static void parseFullComment(const FullComment *C, CommentInfo &CI) { 156 ClangDocCommentVisitor Visitor(CI); 157 Visitor.parseComment(C); 158 } 159 160 static SymbolID getUSRForDecl(const Decl *D) { 161 llvm::SmallString<128> USR; 162 if (index::generateUSRForDecl(D, USR)) 163 return SymbolID(); 164 return hashUSR(USR); 165 } 166 167 static RecordDecl *getDeclForType(const QualType &T) { 168 auto *Ty = T->getAs<RecordType>(); 169 if (!Ty) 170 return nullptr; 171 return Ty->getDecl()->getDefinition(); 172 } 173 174 static void parseFields(RecordInfo &I, const RecordDecl *D) { 175 for (const FieldDecl *F : D->fields()) { 176 // FIXME: Set Access to the appropriate value. 177 SymbolID Type; 178 std::string Name; 179 InfoType RefType; 180 if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) { 181 Type = getUSRForDecl(T); 182 if (dyn_cast<EnumDecl>(T)) 183 RefType = InfoType::IT_enum; 184 else if (dyn_cast<RecordDecl>(T)) 185 RefType = InfoType::IT_record; 186 I.Members.emplace_back(Type, RefType, F->getQualifiedNameAsString()); 187 } else { 188 Name = F->getTypeSourceInfo()->getType().getAsString(); 189 I.Members.emplace_back(Name, F->getQualifiedNameAsString()); 190 } 191 } 192 } 193 194 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { 195 for (const EnumConstantDecl *E : D->enumerators()) 196 I.Members.emplace_back(E->getNameAsString()); 197 } 198 199 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { 200 for (const ParmVarDecl *P : D->parameters()) { 201 SymbolID Type; 202 std::string Name; 203 InfoType RefType; 204 if (const auto *T = getDeclForType(P->getOriginalType())) { 205 Type = getUSRForDecl(T); 206 if (dyn_cast<EnumDecl>(T)) 207 RefType = InfoType::IT_enum; 208 else if (dyn_cast<RecordDecl>(T)) 209 RefType = InfoType::IT_record; 210 I.Params.emplace_back(Type, RefType, P->getQualifiedNameAsString()); 211 } else { 212 Name = P->getOriginalType().getAsString(); 213 I.Params.emplace_back(Name, P->getQualifiedNameAsString()); 214 } 215 } 216 } 217 218 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { 219 for (const CXXBaseSpecifier &B : D->bases()) { 220 if (B.isVirtual()) 221 continue; 222 if (const auto *P = getDeclForType(B.getType())) 223 I.Parents.emplace_back(getUSRForDecl(P), InfoType::IT_record); 224 else 225 I.Parents.emplace_back(B.getType().getAsString()); 226 } 227 for (const CXXBaseSpecifier &B : D->vbases()) { 228 if (const auto *P = getDeclForType(B.getType())) 229 I.VirtualParents.emplace_back(getUSRForDecl(P), InfoType::IT_record); 230 else 231 I.VirtualParents.emplace_back(B.getType().getAsString()); 232 } 233 } 234 235 template <typename T> 236 static void 237 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, 238 const T *D) { 239 const auto *DC = dyn_cast<DeclContext>(D); 240 while ((DC = DC->getParent())) { 241 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) 242 Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_namespace); 243 else if (const auto *N = dyn_cast<RecordDecl>(DC)) 244 Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_record); 245 else if (const auto *N = dyn_cast<FunctionDecl>(DC)) 246 Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_function); 247 else if (const auto *N = dyn_cast<EnumDecl>(DC)) 248 Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_enum); 249 } 250 } 251 252 template <typename T> 253 static void populateInfo(Info &I, const T *D, const FullComment *C) { 254 I.USR = getUSRForDecl(D); 255 I.Name = D->getNameAsString(); 256 populateParentNamespaces(I.Namespace, D); 257 if (C) { 258 I.Description.emplace_back(); 259 parseFullComment(C, I.Description.back()); 260 } 261 } 262 263 template <typename T> 264 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, 265 int LineNumber, StringRef Filename) { 266 populateInfo(I, D, C); 267 if (D->isThisDeclarationADefinition()) 268 I.DefLoc.emplace(LineNumber, Filename); 269 else 270 I.Loc.emplace_back(LineNumber, Filename); 271 } 272 273 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, 274 const FullComment *FC, int LineNumber, 275 StringRef Filename) { 276 populateSymbolInfo(I, D, FC, LineNumber, Filename); 277 if (const auto *T = getDeclForType(D->getReturnType())) { 278 I.ReturnType.Type.USR = getUSRForDecl(T); 279 if (dyn_cast<EnumDecl>(T)) 280 I.ReturnType.Type.RefType = InfoType::IT_enum; 281 else if (dyn_cast<RecordDecl>(T)) 282 I.ReturnType.Type.RefType = InfoType::IT_record; 283 } else { 284 I.ReturnType.Type.UnresolvedName = D->getReturnType().getAsString(); 285 } 286 parseParameters(I, D); 287 } 288 289 std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, 290 int LineNumber, llvm::StringRef File) { 291 NamespaceInfo I; 292 populateInfo(I, D, FC); 293 return serialize(I); 294 } 295 296 std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, 297 llvm::StringRef File) { 298 RecordInfo I; 299 populateSymbolInfo(I, D, FC, LineNumber, File); 300 I.TagType = D->getTagKind(); 301 parseFields(I, D); 302 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) 303 parseBases(I, C); 304 return serialize(I); 305 } 306 307 std::string emitInfo(const FunctionDecl *D, const FullComment *FC, 308 int LineNumber, llvm::StringRef File) { 309 FunctionInfo I; 310 populateFunctionInfo(I, D, FC, LineNumber, File); 311 I.Access = clang::AccessSpecifier::AS_none; 312 return serialize(I); 313 } 314 315 std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, 316 int LineNumber, llvm::StringRef File) { 317 FunctionInfo I; 318 populateFunctionInfo(I, D, FC, LineNumber, File); 319 I.IsMethod = true; 320 I.Parent = Reference(getUSRForDecl(D->getParent()), InfoType::IT_record); 321 I.Access = D->getAccess(); 322 return serialize(I); 323 } 324 325 std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, 326 llvm::StringRef File) { 327 EnumInfo I; 328 populateSymbolInfo(I, D, FC, LineNumber, File); 329 I.Scoped = D->isScoped(); 330 parseEnumerators(I, D); 331 return serialize(I); 332 } 333 334 } // namespace serialize 335 } // namespace doc 336 } // namespace clang 337