1 //===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
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 // This file defines all libclang APIs related to walking comment AST.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "CXComment.h"
15 #include "CXCursor.h"
16 #include "CXString.h"
17 #include "clang-c/Documentation.h"
18 #include "clang-c/Index.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/Index/CommentToXML.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/Support/ErrorHandling.h"
23 #include <climits>
24 
25 using namespace clang;
26 using namespace clang::comments;
27 using namespace clang::cxcomment;
28 
29 extern "C" {
30 
31 CXComment clang_Cursor_getParsedComment(CXCursor C) {
32   using namespace clang::cxcursor;
33 
34   if (!clang_isDeclaration(C.kind))
35     return createCXComment(nullptr, nullptr);
36 
37   const Decl *D = getCursorDecl(C);
38   const ASTContext &Context = getCursorContext(C);
39   const FullComment *FC = Context.getCommentForDecl(D, /*PP=*/nullptr);
40 
41   return createCXComment(FC, getCursorTU(C));
42 }
43 
44 enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
45   const Comment *C = getASTNode(CXC);
46   if (!C)
47     return CXComment_Null;
48 
49   switch (C->getCommentKind()) {
50   case Comment::NoCommentKind:
51     return CXComment_Null;
52 
53   case Comment::TextCommentKind:
54     return CXComment_Text;
55 
56   case Comment::InlineCommandCommentKind:
57     return CXComment_InlineCommand;
58 
59   case Comment::HTMLStartTagCommentKind:
60     return CXComment_HTMLStartTag;
61 
62   case Comment::HTMLEndTagCommentKind:
63     return CXComment_HTMLEndTag;
64 
65   case Comment::ParagraphCommentKind:
66     return CXComment_Paragraph;
67 
68   case Comment::BlockCommandCommentKind:
69     return CXComment_BlockCommand;
70 
71   case Comment::ParamCommandCommentKind:
72     return CXComment_ParamCommand;
73 
74   case Comment::TParamCommandCommentKind:
75     return CXComment_TParamCommand;
76 
77   case Comment::VerbatimBlockCommentKind:
78     return CXComment_VerbatimBlockCommand;
79 
80   case Comment::VerbatimBlockLineCommentKind:
81     return CXComment_VerbatimBlockLine;
82 
83   case Comment::VerbatimLineCommentKind:
84     return CXComment_VerbatimLine;
85 
86   case Comment::FullCommentKind:
87     return CXComment_FullComment;
88   }
89   llvm_unreachable("unknown CommentKind");
90 }
91 
92 unsigned clang_Comment_getNumChildren(CXComment CXC) {
93   const Comment *C = getASTNode(CXC);
94   if (!C)
95     return 0;
96 
97   return C->child_count();
98 }
99 
100 CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
101   const Comment *C = getASTNode(CXC);
102   if (!C || ChildIdx >= C->child_count())
103     return createCXComment(nullptr, nullptr);
104 
105   return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
106 }
107 
108 unsigned clang_Comment_isWhitespace(CXComment CXC) {
109   const Comment *C = getASTNode(CXC);
110   if (!C)
111     return false;
112 
113   if (const TextComment *TC = dyn_cast<TextComment>(C))
114     return TC->isWhitespace();
115 
116   if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
117     return PC->isWhitespace();
118 
119   return false;
120 }
121 
122 unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
123   const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
124   if (!ICC)
125     return false;
126 
127   return ICC->hasTrailingNewline();
128 }
129 
130 CXString clang_TextComment_getText(CXComment CXC) {
131   const TextComment *TC = getASTNodeAs<TextComment>(CXC);
132   if (!TC)
133     return cxstring::createNull();
134 
135   return cxstring::createRef(TC->getText());
136 }
137 
138 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
139   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
140   if (!ICC)
141     return cxstring::createNull();
142 
143   const CommandTraits &Traits = getCommandTraits(CXC);
144   return cxstring::createRef(ICC->getCommandName(Traits));
145 }
146 
147 enum CXCommentInlineCommandRenderKind
148 clang_InlineCommandComment_getRenderKind(CXComment CXC) {
149   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
150   if (!ICC)
151     return CXCommentInlineCommandRenderKind_Normal;
152 
153   switch (ICC->getRenderKind()) {
154   case InlineCommandComment::RenderNormal:
155     return CXCommentInlineCommandRenderKind_Normal;
156 
157   case InlineCommandComment::RenderBold:
158     return CXCommentInlineCommandRenderKind_Bold;
159 
160   case InlineCommandComment::RenderMonospaced:
161     return CXCommentInlineCommandRenderKind_Monospaced;
162 
163   case InlineCommandComment::RenderEmphasized:
164     return CXCommentInlineCommandRenderKind_Emphasized;
165   }
166   llvm_unreachable("unknown InlineCommandComment::RenderKind");
167 }
168 
169 unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
170   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
171   if (!ICC)
172     return 0;
173 
174   return ICC->getNumArgs();
175 }
176 
177 CXString clang_InlineCommandComment_getArgText(CXComment CXC,
178                                                unsigned ArgIdx) {
179   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
180   if (!ICC || ArgIdx >= ICC->getNumArgs())
181     return cxstring::createNull();
182 
183   return cxstring::createRef(ICC->getArgText(ArgIdx));
184 }
185 
186 CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
187   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
188   if (!HTC)
189     return cxstring::createNull();
190 
191   return cxstring::createRef(HTC->getTagName());
192 }
193 
194 unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
195   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
196   if (!HST)
197     return false;
198 
199   return HST->isSelfClosing();
200 }
201 
202 unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
203   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
204   if (!HST)
205     return 0;
206 
207   return HST->getNumAttrs();
208 }
209 
210 CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
211   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
212   if (!HST || AttrIdx >= HST->getNumAttrs())
213     return cxstring::createNull();
214 
215   return cxstring::createRef(HST->getAttr(AttrIdx).Name);
216 }
217 
218 CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
219   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
220   if (!HST || AttrIdx >= HST->getNumAttrs())
221     return cxstring::createNull();
222 
223   return cxstring::createRef(HST->getAttr(AttrIdx).Value);
224 }
225 
226 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
227   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
228   if (!BCC)
229     return cxstring::createNull();
230 
231   const CommandTraits &Traits = getCommandTraits(CXC);
232   return cxstring::createRef(BCC->getCommandName(Traits));
233 }
234 
235 unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
236   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
237   if (!BCC)
238     return 0;
239 
240   return BCC->getNumArgs();
241 }
242 
243 CXString clang_BlockCommandComment_getArgText(CXComment CXC,
244                                               unsigned ArgIdx) {
245   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
246   if (!BCC || ArgIdx >= BCC->getNumArgs())
247     return cxstring::createNull();
248 
249   return cxstring::createRef(BCC->getArgText(ArgIdx));
250 }
251 
252 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
253   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
254   if (!BCC)
255     return createCXComment(nullptr, nullptr);
256 
257   return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
258 }
259 
260 CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
261   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
262   if (!PCC || !PCC->hasParamName())
263     return cxstring::createNull();
264 
265   return cxstring::createRef(PCC->getParamNameAsWritten());
266 }
267 
268 unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
269   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
270   if (!PCC)
271     return false;
272 
273   return PCC->isParamIndexValid();
274 }
275 
276 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
277   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
278   if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam())
279     return ParamCommandComment::InvalidParamIndex;
280 
281   return PCC->getParamIndex();
282 }
283 
284 unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
285   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
286   if (!PCC)
287     return false;
288 
289   return PCC->isDirectionExplicit();
290 }
291 
292 enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
293                                                             CXComment CXC) {
294   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
295   if (!PCC)
296     return CXCommentParamPassDirection_In;
297 
298   switch (PCC->getDirection()) {
299   case ParamCommandComment::In:
300     return CXCommentParamPassDirection_In;
301 
302   case ParamCommandComment::Out:
303     return CXCommentParamPassDirection_Out;
304 
305   case ParamCommandComment::InOut:
306     return CXCommentParamPassDirection_InOut;
307   }
308   llvm_unreachable("unknown ParamCommandComment::PassDirection");
309 }
310 
311 CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
312   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
313   if (!TPCC || !TPCC->hasParamName())
314     return cxstring::createNull();
315 
316   return cxstring::createRef(TPCC->getParamNameAsWritten());
317 }
318 
319 unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
320   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
321   if (!TPCC)
322     return false;
323 
324   return TPCC->isPositionValid();
325 }
326 
327 unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
328   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
329   if (!TPCC || !TPCC->isPositionValid())
330     return 0;
331 
332   return TPCC->getDepth();
333 }
334 
335 unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
336   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
337   if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
338     return 0;
339 
340   return TPCC->getIndex(Depth);
341 }
342 
343 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
344   const VerbatimBlockLineComment *VBL =
345       getASTNodeAs<VerbatimBlockLineComment>(CXC);
346   if (!VBL)
347     return cxstring::createNull();
348 
349   return cxstring::createRef(VBL->getText());
350 }
351 
352 CXString clang_VerbatimLineComment_getText(CXComment CXC) {
353   const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
354   if (!VLC)
355     return cxstring::createNull();
356 
357   return cxstring::createRef(VLC->getText());
358 }
359 
360 //===----------------------------------------------------------------------===//
361 // Converting comments to XML.
362 //===----------------------------------------------------------------------===//
363 
364 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
365   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
366   if (!HTC)
367     return cxstring::createNull();
368 
369   CXTranslationUnit TU = CXC.TranslationUnit;
370   if (!TU->CommentToXML)
371     TU->CommentToXML = new clang::index::CommentToXMLConverter();
372 
373   SmallString<128> Text;
374   TU->CommentToXML->convertHTMLTagNodeToText(
375       HTC, Text, cxtu::getASTUnit(TU)->getASTContext());
376   return cxstring::createDup(Text.str());
377 }
378 
379 CXString clang_FullComment_getAsHTML(CXComment CXC) {
380   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
381   if (!FC)
382     return cxstring::createNull();
383 
384   CXTranslationUnit TU = CXC.TranslationUnit;
385   if (!TU->CommentToXML)
386     TU->CommentToXML = new clang::index::CommentToXMLConverter();
387 
388   SmallString<1024> HTML;
389   TU->CommentToXML
390       ->convertCommentToHTML(FC, HTML, cxtu::getASTUnit(TU)->getASTContext());
391   return cxstring::createDup(HTML.str());
392 }
393 
394 CXString clang_FullComment_getAsXML(CXComment CXC) {
395   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
396   if (!FC)
397     return cxstring::createNull();
398 
399   CXTranslationUnit TU = CXC.TranslationUnit;
400   if (!TU->CommentToXML)
401     TU->CommentToXML = new clang::index::CommentToXMLConverter();
402 
403   SmallString<1024> XML;
404   TU->CommentToXML
405       ->convertCommentToXML(FC, XML, cxtu::getASTUnit(TU)->getASTContext());
406   return cxstring::createDup(XML.str());
407 }
408 
409 } // end extern "C"
410 
411