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