CXComment.cpp revision 6cd4420945489e7331012103048f162288a6c7d8
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 "clang-c/Index.h"
15#include "CXString.h"
16#include "CXComment.h"
17#include "CXCursor.h"
18#include "CXTranslationUnit.h"
19
20#include "clang/AST/CommentVisitor.h"
21#include "clang/AST/Decl.h"
22#include "clang/Frontend/ASTUnit.h"
23
24#include "llvm/ADT/StringSwitch.h"
25#include "llvm/Support/ErrorHandling.h"
26#include "llvm/Support/raw_ostream.h"
27
28#include <climits>
29
30using namespace clang;
31using namespace clang::cxstring;
32using namespace clang::comments;
33using namespace clang::cxcomment;
34
35extern "C" {
36
37enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
38  const Comment *C = getASTNode(CXC);
39  if (!C)
40    return CXComment_Null;
41
42  switch (C->getCommentKind()) {
43  case Comment::NoCommentKind:
44    return CXComment_Null;
45
46  case Comment::TextCommentKind:
47    return CXComment_Text;
48
49  case Comment::InlineCommandCommentKind:
50    return CXComment_InlineCommand;
51
52  case Comment::HTMLStartTagCommentKind:
53    return CXComment_HTMLStartTag;
54
55  case Comment::HTMLEndTagCommentKind:
56    return CXComment_HTMLEndTag;
57
58  case Comment::ParagraphCommentKind:
59    return CXComment_Paragraph;
60
61  case Comment::BlockCommandCommentKind:
62    return CXComment_BlockCommand;
63
64  case Comment::ParamCommandCommentKind:
65    return CXComment_ParamCommand;
66
67  case Comment::TParamCommandCommentKind:
68    return CXComment_TParamCommand;
69
70  case Comment::VerbatimBlockCommentKind:
71    return CXComment_VerbatimBlockCommand;
72
73  case Comment::VerbatimBlockLineCommentKind:
74    return CXComment_VerbatimBlockLine;
75
76  case Comment::VerbatimLineCommentKind:
77    return CXComment_VerbatimLine;
78
79  case Comment::FullCommentKind:
80    return CXComment_FullComment;
81  }
82  llvm_unreachable("unknown CommentKind");
83}
84
85unsigned clang_Comment_getNumChildren(CXComment CXC) {
86  const Comment *C = getASTNode(CXC);
87  if (!C)
88    return 0;
89
90  return C->child_count();
91}
92
93CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
94  const Comment *C = getASTNode(CXC);
95  if (!C || ChildIdx >= C->child_count())
96    return createCXComment(NULL);
97
98  return createCXComment(*(C->child_begin() + ChildIdx));
99}
100
101unsigned clang_Comment_isWhitespace(CXComment CXC) {
102  const Comment *C = getASTNode(CXC);
103  if (!C)
104    return false;
105
106  if (const TextComment *TC = dyn_cast<TextComment>(C))
107    return TC->isWhitespace();
108
109  if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
110    return PC->isWhitespace();
111
112  return false;
113}
114
115unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
116  const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
117  if (!ICC)
118    return false;
119
120  return ICC->hasTrailingNewline();
121}
122
123CXString clang_TextComment_getText(CXComment CXC) {
124  const TextComment *TC = getASTNodeAs<TextComment>(CXC);
125  if (!TC)
126    return createCXString((const char *) 0);
127
128  return createCXString(TC->getText(), /*DupString=*/ false);
129}
130
131CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
132  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
133  if (!ICC)
134    return createCXString((const char *) 0);
135
136  return createCXString(ICC->getCommandName(), /*DupString=*/ false);
137}
138
139enum CXCommentInlineCommandRenderKind
140clang_InlineCommandComment_getRenderKind(CXComment CXC) {
141  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
142  if (!ICC)
143    return CXCommentInlineCommandRenderKind_Normal;
144
145  switch (ICC->getRenderKind()) {
146  case InlineCommandComment::RenderNormal:
147    return CXCommentInlineCommandRenderKind_Normal;
148
149  case InlineCommandComment::RenderBold:
150    return CXCommentInlineCommandRenderKind_Bold;
151
152  case InlineCommandComment::RenderMonospaced:
153    return CXCommentInlineCommandRenderKind_Monospaced;
154
155  case InlineCommandComment::RenderEmphasized:
156    return CXCommentInlineCommandRenderKind_Emphasized;
157  }
158  llvm_unreachable("unknown InlineCommandComment::RenderKind");
159}
160
161unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
162  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
163  if (!ICC)
164    return 0;
165
166  return ICC->getNumArgs();
167}
168
169CXString clang_InlineCommandComment_getArgText(CXComment CXC,
170                                               unsigned ArgIdx) {
171  const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
172  if (!ICC || ArgIdx >= ICC->getNumArgs())
173    return createCXString((const char *) 0);
174
175  return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
176}
177
178CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
179  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
180  if (!HTC)
181    return createCXString((const char *) 0);
182
183  return createCXString(HTC->getTagName(), /*DupString=*/ false);
184}
185
186unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
187  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
188  if (!HST)
189    return false;
190
191  return HST->isSelfClosing();
192}
193
194unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
195  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
196  if (!HST)
197    return 0;
198
199  return HST->getNumAttrs();
200}
201
202CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
203  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
204  if (!HST || AttrIdx >= HST->getNumAttrs())
205    return createCXString((const char *) 0);
206
207  return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
208}
209
210CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
211  const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
212  if (!HST || AttrIdx >= HST->getNumAttrs())
213    return createCXString((const char *) 0);
214
215  return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
216}
217
218CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
219  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
220  if (!BCC)
221    return createCXString((const char *) 0);
222
223  return createCXString(BCC->getCommandName(), /*DupString=*/ false);
224}
225
226unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
227  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
228  if (!BCC)
229    return 0;
230
231  return BCC->getNumArgs();
232}
233
234CXString clang_BlockCommandComment_getArgText(CXComment CXC,
235                                              unsigned ArgIdx) {
236  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
237  if (!BCC || ArgIdx >= BCC->getNumArgs())
238    return createCXString((const char *) 0);
239
240  return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
241}
242
243CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
244  const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
245  if (!BCC)
246    return createCXComment(NULL);
247
248  return createCXComment(BCC->getParagraph());
249}
250
251CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
252  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
253  if (!PCC || !PCC->hasParamName())
254    return createCXString((const char *) 0);
255
256  return createCXString(PCC->getParamName(), /*DupString=*/ false);
257}
258
259unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
260  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
261  if (!PCC)
262    return false;
263
264  return PCC->isParamIndexValid();
265}
266
267unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
268  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
269  if (!PCC || !PCC->isParamIndexValid())
270    return ParamCommandComment::InvalidParamIndex;
271
272  return PCC->getParamIndex();
273}
274
275unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
276  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
277  if (!PCC)
278    return false;
279
280  return PCC->isDirectionExplicit();
281}
282
283enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
284                                                            CXComment CXC) {
285  const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
286  if (!PCC)
287    return CXCommentParamPassDirection_In;
288
289  switch (PCC->getDirection()) {
290  case ParamCommandComment::In:
291    return CXCommentParamPassDirection_In;
292
293  case ParamCommandComment::Out:
294    return CXCommentParamPassDirection_Out;
295
296  case ParamCommandComment::InOut:
297    return CXCommentParamPassDirection_InOut;
298  }
299  llvm_unreachable("unknown ParamCommandComment::PassDirection");
300}
301
302CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
303  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
304  if (!TPCC || !TPCC->hasParamName())
305    return createCXString((const char *) 0);
306
307  return createCXString(TPCC->getParamName(), /*DupString=*/ false);
308}
309
310unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
311  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
312  if (!TPCC)
313    return false;
314
315  return TPCC->isPositionValid();
316}
317
318unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
319  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
320  if (!TPCC || !TPCC->isPositionValid())
321    return 0;
322
323  return TPCC->getDepth();
324}
325
326unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
327  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
328  if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
329    return 0;
330
331  return TPCC->getIndex(Depth);
332}
333
334CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
335  const VerbatimBlockLineComment *VBL =
336      getASTNodeAs<VerbatimBlockLineComment>(CXC);
337  if (!VBL)
338    return createCXString((const char *) 0);
339
340  return createCXString(VBL->getText(), /*DupString=*/ false);
341}
342
343CXString clang_VerbatimLineComment_getText(CXComment CXC) {
344  const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
345  if (!VLC)
346    return createCXString((const char *) 0);
347
348  return createCXString(VLC->getText(), /*DupString=*/ false);
349}
350
351} // end extern "C"
352
353//===----------------------------------------------------------------------===//
354// Helpers for converting comment AST to HTML.
355//===----------------------------------------------------------------------===//
356
357namespace {
358
359/// This comparison will sort parameters with valid index by index and
360/// invalid (unresolved) parameters last.
361class ParamCommandCommentCompareIndex {
362public:
363  bool operator()(const ParamCommandComment *LHS,
364                  const ParamCommandComment *RHS) const {
365    unsigned LHSIndex = UINT_MAX;
366    unsigned RHSIndex = UINT_MAX;
367    if (LHS->isParamIndexValid())
368      LHSIndex = LHS->getParamIndex();
369    if (RHS->isParamIndexValid())
370      RHSIndex = RHS->getParamIndex();
371
372    return LHSIndex < RHSIndex;
373  }
374};
375
376/// This comparison will sort template parameters in the following order:
377/// \li real template parameters (depth = 1) in index order;
378/// \li all other names (depth > 1);
379/// \li unresolved names.
380class TParamCommandCommentComparePosition {
381public:
382  bool operator()(const TParamCommandComment *LHS,
383                  const TParamCommandComment *RHS) const {
384    // Sort unresolved names last.
385    if (!LHS->isPositionValid())
386      return false;
387    if (!RHS->isPositionValid())
388      return true;
389
390    if (LHS->getDepth() > 1)
391      return false;
392    if (RHS->getDepth() > 1)
393      return true;
394
395    // Sort template parameters in index order.
396    if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
397      return LHS->getIndex(0) < RHS->getIndex(0);
398
399    // Leave all other names in source order.
400    return true;
401  }
402};
403
404/// Separate parts of a FullComment.
405struct FullCommentParts {
406  /// Take a full comment apart and initialize members accordingly.
407  FullCommentParts(const FullComment *C);
408
409  const BlockContentComment *Brief;
410  const ParagraphComment *FirstParagraph;
411  const BlockCommandComment *Returns;
412  SmallVector<const ParamCommandComment *, 8> Params;
413  SmallVector<const TParamCommandComment *, 4> TParams;
414  SmallVector<const BlockContentComment *, 8> MiscBlocks;
415};
416
417FullCommentParts::FullCommentParts(const FullComment *C) :
418    Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
419  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
420       I != E; ++I) {
421    const Comment *Child = *I;
422    if (!Child)
423      continue;
424    switch (Child->getCommentKind()) {
425    case Comment::NoCommentKind:
426      continue;
427
428    case Comment::ParagraphCommentKind: {
429      const ParagraphComment *PC = cast<ParagraphComment>(Child);
430      if (PC->isWhitespace())
431        break;
432      if (!FirstParagraph)
433        FirstParagraph = PC;
434
435      MiscBlocks.push_back(PC);
436      break;
437    }
438
439    case Comment::BlockCommandCommentKind: {
440      const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
441      StringRef CommandName = BCC->getCommandName();
442      if (!Brief && (CommandName == "brief" || CommandName == "short")) {
443        Brief = BCC;
444        break;
445      }
446      if (!Returns && (CommandName == "returns" || CommandName == "return")) {
447        Returns = BCC;
448        break;
449      }
450      MiscBlocks.push_back(BCC);
451      break;
452    }
453
454    case Comment::ParamCommandCommentKind: {
455      const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
456      if (!PCC->hasParamName())
457        break;
458
459      if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
460        break;
461
462      Params.push_back(PCC);
463      break;
464    }
465
466    case Comment::TParamCommandCommentKind: {
467      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
468      if (!TPCC->hasParamName())
469        break;
470
471      if (!TPCC->hasNonWhitespaceParagraph())
472        break;
473
474      TParams.push_back(TPCC);
475      break;
476    }
477
478    case Comment::VerbatimBlockCommentKind:
479    case Comment::VerbatimLineCommentKind:
480      MiscBlocks.push_back(cast<BlockCommandComment>(Child));
481      break;
482
483    case Comment::TextCommentKind:
484    case Comment::InlineCommandCommentKind:
485    case Comment::HTMLStartTagCommentKind:
486    case Comment::HTMLEndTagCommentKind:
487    case Comment::VerbatimBlockLineCommentKind:
488    case Comment::FullCommentKind:
489      llvm_unreachable("AST node of this kind can't be a child of "
490                       "a FullComment");
491    }
492  }
493
494  // Sort params in order they are declared in the function prototype.
495  // Unresolved parameters are put at the end of the list in the same order
496  // they were seen in the comment.
497  std::stable_sort(Params.begin(), Params.end(),
498                   ParamCommandCommentCompareIndex());
499
500  std::stable_sort(TParams.begin(), TParams.end(),
501                   TParamCommandCommentComparePosition());
502}
503
504void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
505                              llvm::raw_svector_ostream &Result) {
506  Result << "<" << C->getTagName();
507
508  if (C->getNumAttrs() != 0) {
509    for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
510      Result << " ";
511      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
512      Result << Attr.Name;
513      if (!Attr.Value.empty())
514        Result << "=\"" << Attr.Value << "\"";
515    }
516  }
517
518  if (!C->isSelfClosing())
519    Result << ">";
520  else
521    Result << "/>";
522}
523
524class CommentASTToHTMLConverter :
525    public ConstCommentVisitor<CommentASTToHTMLConverter> {
526public:
527  /// \param Str accumulator for HTML.
528  CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
529
530  // Inline content.
531  void visitTextComment(const TextComment *C);
532  void visitInlineCommandComment(const InlineCommandComment *C);
533  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
534  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
535
536  // Block content.
537  void visitParagraphComment(const ParagraphComment *C);
538  void visitBlockCommandComment(const BlockCommandComment *C);
539  void visitParamCommandComment(const ParamCommandComment *C);
540  void visitTParamCommandComment(const TParamCommandComment *C);
541  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
542  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
543  void visitVerbatimLineComment(const VerbatimLineComment *C);
544
545  void visitFullComment(const FullComment *C);
546
547  // Helpers.
548
549  /// Convert a paragraph that is not a block by itself (an argument to some
550  /// command).
551  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
552
553  void appendToResultWithHTMLEscaping(StringRef S);
554
555private:
556  /// Output stream for HTML.
557  llvm::raw_svector_ostream Result;
558};
559} // end unnamed namespace
560
561void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
562  appendToResultWithHTMLEscaping(C->getText());
563}
564
565void CommentASTToHTMLConverter::visitInlineCommandComment(
566                                  const InlineCommandComment *C) {
567  // Nothing to render if no arguments supplied.
568  if (C->getNumArgs() == 0)
569    return;
570
571  // Nothing to render if argument is empty.
572  StringRef Arg0 = C->getArgText(0);
573  if (Arg0.empty())
574    return;
575
576  switch (C->getRenderKind()) {
577  case InlineCommandComment::RenderNormal:
578    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
579      appendToResultWithHTMLEscaping(C->getArgText(i));
580      Result << " ";
581    }
582    return;
583
584  case InlineCommandComment::RenderBold:
585    assert(C->getNumArgs() == 1);
586    Result << "<b>";
587    appendToResultWithHTMLEscaping(Arg0);
588    Result << "</b>";
589    return;
590  case InlineCommandComment::RenderMonospaced:
591    assert(C->getNumArgs() == 1);
592    Result << "<tt>";
593    appendToResultWithHTMLEscaping(Arg0);
594    Result<< "</tt>";
595    return;
596  case InlineCommandComment::RenderEmphasized:
597    assert(C->getNumArgs() == 1);
598    Result << "<em>";
599    appendToResultWithHTMLEscaping(Arg0);
600    Result << "</em>";
601    return;
602  }
603}
604
605void CommentASTToHTMLConverter::visitHTMLStartTagComment(
606                                  const HTMLStartTagComment *C) {
607  PrintHTMLStartTagComment(C, Result);
608}
609
610void CommentASTToHTMLConverter::visitHTMLEndTagComment(
611                                  const HTMLEndTagComment *C) {
612  Result << "</" << C->getTagName() << ">";
613}
614
615void CommentASTToHTMLConverter::visitParagraphComment(
616                                  const ParagraphComment *C) {
617  if (C->isWhitespace())
618    return;
619
620  Result << "<p>";
621  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
622       I != E; ++I) {
623    visit(*I);
624  }
625  Result << "</p>";
626}
627
628void CommentASTToHTMLConverter::visitBlockCommandComment(
629                                  const BlockCommandComment *C) {
630  StringRef CommandName = C->getCommandName();
631  if (CommandName == "brief" || CommandName == "short") {
632    Result << "<p class=\"para-brief\">";
633    visitNonStandaloneParagraphComment(C->getParagraph());
634    Result << "</p>";
635    return;
636  }
637  if (CommandName == "returns" || CommandName == "return" ||
638      CommandName == "result") {
639    Result << "<p class=\"para-returns\">"
640              "<span class=\"word-returns\">Returns</span> ";
641    visitNonStandaloneParagraphComment(C->getParagraph());
642    Result << "</p>";
643    return;
644  }
645  // We don't know anything about this command.  Just render the paragraph.
646  visit(C->getParagraph());
647}
648
649void CommentASTToHTMLConverter::visitParamCommandComment(
650                                  const ParamCommandComment *C) {
651  if (C->isParamIndexValid()) {
652    Result << "<dt class=\"param-name-index-"
653           << C->getParamIndex()
654           << "\">";
655  } else
656    Result << "<dt class=\"param-name-index-invalid\">";
657
658  appendToResultWithHTMLEscaping(C->getParamName());
659  Result << "</dt>";
660
661  if (C->isParamIndexValid()) {
662    Result << "<dd class=\"param-descr-index-"
663           << C->getParamIndex()
664           << "\">";
665  } else
666    Result << "<dd class=\"param-descr-index-invalid\">";
667
668  visitNonStandaloneParagraphComment(C->getParagraph());
669  Result << "</dd>";
670}
671
672void CommentASTToHTMLConverter::visitTParamCommandComment(
673                                  const TParamCommandComment *C) {
674  if (C->isPositionValid()) {
675    if (C->getDepth() == 1)
676      Result << "<dt class=\"tparam-name-index-"
677             << C->getIndex(0)
678             << "\">";
679    else
680      Result << "<dt class=\"tparam-name-index-other\">";
681  } else
682    Result << "<dt class=\"tparam-name-index-invalid\">";
683
684  appendToResultWithHTMLEscaping(C->getParamName());
685  Result << "</dt>";
686
687  if (C->isPositionValid()) {
688    if (C->getDepth() == 1)
689      Result << "<dd class=\"tparam-descr-index-"
690             << C->getIndex(0)
691             << "\">";
692    else
693      Result << "<dd class=\"tparam-descr-index-other\">";
694  } else
695    Result << "<dd class=\"tparam-descr-index-invalid\">";
696
697  visitNonStandaloneParagraphComment(C->getParagraph());
698  Result << "</dd>";
699}
700
701void CommentASTToHTMLConverter::visitVerbatimBlockComment(
702                                  const VerbatimBlockComment *C) {
703  unsigned NumLines = C->getNumLines();
704  if (NumLines == 0)
705    return;
706
707  Result << "<pre>";
708  for (unsigned i = 0; i != NumLines; ++i) {
709    appendToResultWithHTMLEscaping(C->getText(i));
710    if (i + 1 != NumLines)
711      Result << '\n';
712  }
713  Result << "</pre>";
714}
715
716void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
717                                  const VerbatimBlockLineComment *C) {
718  llvm_unreachable("should not see this AST node");
719}
720
721void CommentASTToHTMLConverter::visitVerbatimLineComment(
722                                  const VerbatimLineComment *C) {
723  Result << "<pre>";
724  appendToResultWithHTMLEscaping(C->getText());
725  Result << "</pre>";
726}
727
728void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
729  FullCommentParts Parts(C);
730
731  bool FirstParagraphIsBrief = false;
732  if (Parts.Brief)
733    visit(Parts.Brief);
734  else if (Parts.FirstParagraph) {
735    Result << "<p class=\"para-brief\">";
736    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
737    Result << "</p>";
738    FirstParagraphIsBrief = true;
739  }
740
741  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
742    const Comment *C = Parts.MiscBlocks[i];
743    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
744      continue;
745    visit(C);
746  }
747
748  if (Parts.TParams.size() != 0) {
749    Result << "<dl>";
750    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
751      visit(Parts.TParams[i]);
752    Result << "</dl>";
753  }
754
755  if (Parts.Params.size() != 0) {
756    Result << "<dl>";
757    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
758      visit(Parts.Params[i]);
759    Result << "</dl>";
760  }
761
762  if (Parts.Returns)
763    visit(Parts.Returns);
764
765  Result.flush();
766}
767
768void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
769                                  const ParagraphComment *C) {
770  if (!C)
771    return;
772
773  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
774       I != E; ++I) {
775    visit(*I);
776  }
777}
778
779void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
780  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
781    const char C = *I;
782    switch (C) {
783      case '&':
784        Result << "&amp;";
785        break;
786      case '<':
787        Result << "&lt;";
788        break;
789      case '>':
790        Result << "&gt;";
791        break;
792      case '"':
793        Result << "&quot;";
794        break;
795      case '\'':
796        Result << "&#39;";
797        break;
798      case '/':
799        Result << "&#47;";
800        break;
801      default:
802        Result << C;
803        break;
804    }
805  }
806}
807
808extern "C" {
809
810CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
811  const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
812  if (!HTC)
813    return createCXString((const char *) 0);
814
815  SmallString<128> HTML;
816  CommentASTToHTMLConverter Converter(HTML);
817  Converter.visit(HTC);
818  return createCXString(HTML.str(), /* DupString = */ true);
819}
820
821CXString clang_FullComment_getAsHTML(CXComment CXC) {
822  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
823  if (!FC)
824    return createCXString((const char *) 0);
825
826  SmallString<1024> HTML;
827  CommentASTToHTMLConverter Converter(HTML);
828  Converter.visit(FC);
829  return createCXString(HTML.str(), /* DupString = */ true);
830}
831
832} // end extern "C"
833
834namespace {
835class CommentASTToXMLConverter :
836    public ConstCommentVisitor<CommentASTToXMLConverter> {
837public:
838  /// \param Str accumulator for XML.
839  CommentASTToXMLConverter(const SourceManager &SM,
840                           SmallVectorImpl<char> &Str) :
841    SM(SM), Result(Str) { }
842
843  // Inline content.
844  void visitTextComment(const TextComment *C);
845  void visitInlineCommandComment(const InlineCommandComment *C);
846  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
847  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
848
849  // Block content.
850  void visitParagraphComment(const ParagraphComment *C);
851  void visitBlockCommandComment(const BlockCommandComment *C);
852  void visitParamCommandComment(const ParamCommandComment *C);
853  void visitTParamCommandComment(const TParamCommandComment *C);
854  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
855  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
856  void visitVerbatimLineComment(const VerbatimLineComment *C);
857
858  void visitFullComment(const FullComment *C);
859
860  // Helpers.
861  void appendToResultWithXMLEscaping(StringRef S);
862
863private:
864  const SourceManager &SM;
865  /// Output stream for XML.
866  llvm::raw_svector_ostream Result;
867};
868} // end unnamed namespace
869
870void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
871  appendToResultWithXMLEscaping(C->getText());
872}
873
874void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
875  // Nothing to render if no arguments supplied.
876  if (C->getNumArgs() == 0)
877    return;
878
879  // Nothing to render if argument is empty.
880  StringRef Arg0 = C->getArgText(0);
881  if (Arg0.empty())
882    return;
883
884  switch (C->getRenderKind()) {
885  case InlineCommandComment::RenderNormal:
886    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
887      appendToResultWithXMLEscaping(C->getArgText(i));
888      Result << " ";
889    }
890    return;
891  case InlineCommandComment::RenderBold:
892    assert(C->getNumArgs() == 1);
893    Result << "<bold>";
894    appendToResultWithXMLEscaping(Arg0);
895    Result << "</bold>";
896    return;
897  case InlineCommandComment::RenderMonospaced:
898    assert(C->getNumArgs() == 1);
899    Result << "<monospaced>";
900    appendToResultWithXMLEscaping(Arg0);
901    Result << "</monospaced>";
902    return;
903  case InlineCommandComment::RenderEmphasized:
904    assert(C->getNumArgs() == 1);
905    Result << "<emphasized>";
906    appendToResultWithXMLEscaping(Arg0);
907    Result << "</emphasized>";
908    return;
909  }
910}
911
912void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
913  Result << "<rawHTML><![CDATA[";
914  PrintHTMLStartTagComment(C, Result);
915  Result << "]]></rawHTML>";
916}
917
918void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
919  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
920}
921
922void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
923  if (C->isWhitespace())
924    return;
925
926  Result << "<Para>";
927  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
928       I != E; ++I) {
929    visit(*I);
930  }
931  Result << "</Para>";
932}
933
934void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
935  visit(C->getParagraph());
936}
937
938void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
939  Result << "<Parameter><Name>";
940  appendToResultWithXMLEscaping(C->getParamName());
941  Result << "</Name>";
942
943  if (C->isParamIndexValid())
944    Result << "<Index>" << C->getParamIndex() << "</Index>";
945
946  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
947  switch (C->getDirection()) {
948  case ParamCommandComment::In:
949    Result << "in";
950    break;
951  case ParamCommandComment::Out:
952    Result << "out";
953    break;
954  case ParamCommandComment::InOut:
955    Result << "in,out";
956    break;
957  }
958  Result << "</Direction><Discussion>";
959  visit(C->getParagraph());
960  Result << "</Discussion></Parameter>";
961}
962
963void CommentASTToXMLConverter::visitTParamCommandComment(
964                                  const TParamCommandComment *C) {
965  Result << "<Parameter><Name>";
966  appendToResultWithXMLEscaping(C->getParamName());
967  Result << "</Name>";
968
969  if (C->isPositionValid() && C->getDepth() == 1) {
970    Result << "<Index>" << C->getIndex(0) << "</Index>";
971  }
972
973  Result << "<Discussion>";
974  visit(C->getParagraph());
975  Result << "</Discussion></Parameter>";
976}
977
978void CommentASTToXMLConverter::visitVerbatimBlockComment(
979                                  const VerbatimBlockComment *C) {
980  unsigned NumLines = C->getNumLines();
981  if (NumLines == 0)
982    return;
983
984  Result << llvm::StringSwitch<const char *>(C->getCommandName())
985      .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
986      .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
987  for (unsigned i = 0; i != NumLines; ++i) {
988    appendToResultWithXMLEscaping(C->getText(i));
989    if (i + 1 != NumLines)
990      Result << '\n';
991  }
992  Result << "</Verbatim>";
993}
994
995void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
996                                  const VerbatimBlockLineComment *C) {
997  llvm_unreachable("should not see this AST node");
998}
999
1000void CommentASTToXMLConverter::visitVerbatimLineComment(
1001                                  const VerbatimLineComment *C) {
1002  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1003  appendToResultWithXMLEscaping(C->getText());
1004  Result << "</Verbatim>";
1005}
1006
1007void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1008  FullCommentParts Parts(C);
1009
1010  const DeclInfo *DI = C->getDeclInfo();
1011  StringRef RootEndTag;
1012  if (DI) {
1013    switch (DI->getKind()) {
1014    case DeclInfo::OtherKind:
1015      RootEndTag = "</Other>";
1016      Result << "<Other";
1017      break;
1018    case DeclInfo::FunctionKind:
1019      RootEndTag = "</Function>";
1020      Result << "<Function";
1021      switch (DI->TemplateKind) {
1022      case DeclInfo::NotTemplate:
1023        break;
1024      case DeclInfo::Template:
1025        Result << " templateKind=\"template\"";
1026        break;
1027      case DeclInfo::TemplateSpecialization:
1028        Result << " templateKind=\"specialization\"";
1029        break;
1030      case DeclInfo::TemplatePartialSpecialization:
1031        llvm_unreachable("partial specializations of functions "
1032                         "are not allowed in C++");
1033      }
1034      if (DI->IsInstanceMethod)
1035        Result << " isInstanceMethod=\"1\"";
1036      if (DI->IsClassMethod)
1037        Result << " isClassMethod=\"1\"";
1038      break;
1039    case DeclInfo::ClassKind:
1040      RootEndTag = "</Class>";
1041      Result << "<Class";
1042      switch (DI->TemplateKind) {
1043      case DeclInfo::NotTemplate:
1044        break;
1045      case DeclInfo::Template:
1046        Result << " templateKind=\"template\"";
1047        break;
1048      case DeclInfo::TemplateSpecialization:
1049        Result << " templateKind=\"specialization\"";
1050        break;
1051      case DeclInfo::TemplatePartialSpecialization:
1052        Result << " templateKind=\"partialSpecialization\"";
1053        break;
1054      }
1055      break;
1056    case DeclInfo::VariableKind:
1057      RootEndTag = "</Variable>";
1058      Result << "<Variable";
1059      break;
1060    case DeclInfo::NamespaceKind:
1061      RootEndTag = "</Namespace>";
1062      Result << "<Namespace";
1063      break;
1064    case DeclInfo::TypedefKind:
1065      RootEndTag = "</Typedef>";
1066      Result << "<Typedef";
1067      break;
1068    case DeclInfo::EnumKind:
1069      RootEndTag = "</Enum>";
1070      Result << "<Enum";
1071      break;
1072    }
1073
1074    {
1075      // Print line and column number.
1076      SourceLocation Loc = DI->ThisDecl->getLocation();
1077      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1078      FileID FID = LocInfo.first;
1079      unsigned FileOffset = LocInfo.second;
1080
1081      if (!FID.isInvalid()) {
1082        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1083          Result << " file=\"";
1084          appendToResultWithXMLEscaping(FE->getName());
1085          Result << "\"";
1086        }
1087        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1088               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1089               << "\"";
1090      }
1091    }
1092
1093    // Finish the root tag.
1094    Result << ">";
1095
1096    bool FoundName = false;
1097    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1098      if (DeclarationName DeclName = ND->getDeclName()) {
1099        Result << "<Name>";
1100        std::string Name = DeclName.getAsString();
1101        appendToResultWithXMLEscaping(Name);
1102        FoundName = true;
1103        Result << "</Name>";
1104      }
1105    }
1106    if (!FoundName)
1107      Result << "<Name>&lt;anonymous&gt;</Name>";
1108
1109    {
1110      // Print USR.
1111      SmallString<128> USR;
1112      cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1113      if (!USR.empty()) {
1114        Result << "<USR>";
1115        appendToResultWithXMLEscaping(USR);
1116        Result << "</USR>";
1117      }
1118    }
1119  } else {
1120    // No DeclInfo -- just emit some root tag and name tag.
1121    RootEndTag = "</Other>";
1122    Result << "<Other><Name>unknown</Name>";
1123  }
1124
1125  bool FirstParagraphIsBrief = false;
1126  if (Parts.Brief) {
1127    Result << "<Abstract>";
1128    visit(Parts.Brief);
1129    Result << "</Abstract>";
1130  } else if (Parts.FirstParagraph) {
1131    Result << "<Abstract>";
1132    visit(Parts.FirstParagraph);
1133    Result << "</Abstract>";
1134    FirstParagraphIsBrief = true;
1135  }
1136
1137  if (Parts.TParams.size() != 0) {
1138    Result << "<TemplateParameters>";
1139    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1140      visit(Parts.TParams[i]);
1141    Result << "</TemplateParameters>";
1142  }
1143
1144  if (Parts.Params.size() != 0) {
1145    Result << "<Parameters>";
1146    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1147      visit(Parts.Params[i]);
1148    Result << "</Parameters>";
1149  }
1150
1151  if (Parts.Returns) {
1152    Result << "<ResultDiscussion>";
1153    visit(Parts.Returns);
1154    Result << "</ResultDiscussion>";
1155  }
1156
1157  {
1158    bool StartTagEmitted = false;
1159    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1160      const Comment *C = Parts.MiscBlocks[i];
1161      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1162        continue;
1163      if (!StartTagEmitted) {
1164        Result << "<Discussion>";
1165        StartTagEmitted = true;
1166      }
1167      visit(C);
1168    }
1169    if (StartTagEmitted)
1170      Result << "</Discussion>";
1171  }
1172
1173  Result << RootEndTag;
1174
1175  Result.flush();
1176}
1177
1178void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1179  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1180    const char C = *I;
1181    switch (C) {
1182      case '&':
1183        Result << "&amp;";
1184        break;
1185      case '<':
1186        Result << "&lt;";
1187        break;
1188      case '>':
1189        Result << "&gt;";
1190        break;
1191      case '"':
1192        Result << "&quot;";
1193        break;
1194      case '\'':
1195        Result << "&apos;";
1196        break;
1197      default:
1198        Result << C;
1199        break;
1200    }
1201  }
1202}
1203
1204extern "C" {
1205
1206CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
1207  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1208  if (!FC)
1209    return createCXString((const char *) 0);
1210
1211  SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1212
1213  SmallString<1024> XML;
1214  CommentASTToXMLConverter Converter(SM, XML);
1215  Converter.visit(FC);
1216  return createCXString(XML.str(), /* DupString = */ true);
1217}
1218
1219} // end extern "C"
1220
1221