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