clang  3.7.0
CommentToXML.cpp
Go to the documentation of this file.
1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
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 
11 #include "SimpleFormatContext.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/Attr.h"
14 #include "clang/AST/Comment.h"
16 #include "clang/Format/Format.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/TinyPtrVector.h"
20 #include "llvm/Support/raw_ostream.h"
21 
22 using namespace clang;
23 using namespace clang::comments;
24 using namespace clang::index;
25 
26 namespace {
27 
28 /// This comparison will sort parameters with valid index by index, then vararg
29 /// parameters, and invalid (unresolved) parameters last.
30 class ParamCommandCommentCompareIndex {
31 public:
32  bool operator()(const ParamCommandComment *LHS,
33  const ParamCommandComment *RHS) const {
34  unsigned LHSIndex = UINT_MAX;
35  unsigned RHSIndex = UINT_MAX;
36 
37  if (LHS->isParamIndexValid()) {
38  if (LHS->isVarArgParam())
39  LHSIndex = UINT_MAX - 1;
40  else
41  LHSIndex = LHS->getParamIndex();
42  }
43  if (RHS->isParamIndexValid()) {
44  if (RHS->isVarArgParam())
45  RHSIndex = UINT_MAX - 1;
46  else
47  RHSIndex = RHS->getParamIndex();
48  }
49  return LHSIndex < RHSIndex;
50  }
51 };
52 
53 /// This comparison will sort template parameters in the following order:
54 /// \li real template parameters (depth = 1) in index order;
55 /// \li all other names (depth > 1);
56 /// \li unresolved names.
57 class TParamCommandCommentComparePosition {
58 public:
59  bool operator()(const TParamCommandComment *LHS,
60  const TParamCommandComment *RHS) const {
61  // Sort unresolved names last.
62  if (!LHS->isPositionValid())
63  return false;
64  if (!RHS->isPositionValid())
65  return true;
66 
67  if (LHS->getDepth() > 1)
68  return false;
69  if (RHS->getDepth() > 1)
70  return true;
71 
72  // Sort template parameters in index order.
73  if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
74  return LHS->getIndex(0) < RHS->getIndex(0);
75 
76  // Leave all other names in source order.
77  return true;
78  }
79 };
80 
81 /// Separate parts of a FullComment.
82 struct FullCommentParts {
83  /// Take a full comment apart and initialize members accordingly.
84  FullCommentParts(const FullComment *C,
85  const CommandTraits &Traits);
86 
87  const BlockContentComment *Brief;
88  const BlockContentComment *Headerfile;
89  const ParagraphComment *FirstParagraph;
93  llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95 };
96 
97 FullCommentParts::FullCommentParts(const FullComment *C,
98  const CommandTraits &Traits) :
99  Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
100  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
101  I != E; ++I) {
102  const Comment *Child = *I;
103  if (!Child)
104  continue;
105  switch (Child->getCommentKind()) {
107  continue;
108 
109  case Comment::ParagraphCommentKind: {
110  const ParagraphComment *PC = cast<ParagraphComment>(Child);
111  if (PC->isWhitespace())
112  break;
113  if (!FirstParagraph)
114  FirstParagraph = PC;
115 
116  MiscBlocks.push_back(PC);
117  break;
118  }
119 
120  case Comment::BlockCommandCommentKind: {
121  const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
122  const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
123  if (!Brief && Info->IsBriefCommand) {
124  Brief = BCC;
125  break;
126  }
127  if (!Headerfile && Info->IsHeaderfileCommand) {
128  Headerfile = BCC;
129  break;
130  }
131  if (Info->IsReturnsCommand) {
132  Returns.push_back(BCC);
133  break;
134  }
135  if (Info->IsThrowsCommand) {
136  Exceptions.push_back(BCC);
137  break;
138  }
139  MiscBlocks.push_back(BCC);
140  break;
141  }
142 
143  case Comment::ParamCommandCommentKind: {
144  const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
145  if (!PCC->hasParamName())
146  break;
147 
148  if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
149  break;
150 
151  Params.push_back(PCC);
152  break;
153  }
154 
155  case Comment::TParamCommandCommentKind: {
156  const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
157  if (!TPCC->hasParamName())
158  break;
159 
160  if (!TPCC->hasNonWhitespaceParagraph())
161  break;
162 
163  TParams.push_back(TPCC);
164  break;
165  }
166 
167  case Comment::VerbatimBlockCommentKind:
168  MiscBlocks.push_back(cast<BlockCommandComment>(Child));
169  break;
170 
171  case Comment::VerbatimLineCommentKind: {
172  const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
173  const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
174  if (!Info->IsDeclarationCommand)
175  MiscBlocks.push_back(VLC);
176  break;
177  }
178 
179  case Comment::TextCommentKind:
180  case Comment::InlineCommandCommentKind:
181  case Comment::HTMLStartTagCommentKind:
182  case Comment::HTMLEndTagCommentKind:
183  case Comment::VerbatimBlockLineCommentKind:
184  case Comment::FullCommentKind:
185  llvm_unreachable("AST node of this kind can't be a child of "
186  "a FullComment");
187  }
188  }
189 
190  // Sort params in order they are declared in the function prototype.
191  // Unresolved parameters are put at the end of the list in the same order
192  // they were seen in the comment.
193  std::stable_sort(Params.begin(), Params.end(),
194  ParamCommandCommentCompareIndex());
195 
196  std::stable_sort(TParams.begin(), TParams.end(),
197  TParamCommandCommentComparePosition());
198 }
199 
200 void printHTMLStartTagComment(const HTMLStartTagComment *C,
201  llvm::raw_svector_ostream &Result) {
202  Result << "<" << C->getTagName();
203 
204  if (C->getNumAttrs() != 0) {
205  for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
206  Result << " ";
208  Result << Attr.Name;
209  if (!Attr.Value.empty())
210  Result << "=\"" << Attr.Value << "\"";
211  }
212  }
213 
214  if (!C->isSelfClosing())
215  Result << ">";
216  else
217  Result << "/>";
218 }
219 
220 class CommentASTToHTMLConverter :
221  public ConstCommentVisitor<CommentASTToHTMLConverter> {
222 public:
223  /// \param Str accumulator for HTML.
224  CommentASTToHTMLConverter(const FullComment *FC,
226  const CommandTraits &Traits) :
227  FC(FC), Result(Str), Traits(Traits)
228  { }
229 
230  // Inline content.
231  void visitTextComment(const TextComment *C);
232  void visitInlineCommandComment(const InlineCommandComment *C);
233  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
234  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
235 
236  // Block content.
237  void visitParagraphComment(const ParagraphComment *C);
238  void visitBlockCommandComment(const BlockCommandComment *C);
239  void visitParamCommandComment(const ParamCommandComment *C);
240  void visitTParamCommandComment(const TParamCommandComment *C);
241  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
242  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
243  void visitVerbatimLineComment(const VerbatimLineComment *C);
244 
245  void visitFullComment(const FullComment *C);
246 
247  // Helpers.
248 
249  /// Convert a paragraph that is not a block by itself (an argument to some
250  /// command).
251  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
252 
253  void appendToResultWithHTMLEscaping(StringRef S);
254 
255 private:
256  const FullComment *FC;
257  /// Output stream for HTML.
258  llvm::raw_svector_ostream Result;
259 
260  const CommandTraits &Traits;
261 };
262 } // end unnamed namespace
263 
264 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
265  appendToResultWithHTMLEscaping(C->getText());
266 }
267 
268 void CommentASTToHTMLConverter::visitInlineCommandComment(
269  const InlineCommandComment *C) {
270  // Nothing to render if no arguments supplied.
271  if (C->getNumArgs() == 0)
272  return;
273 
274  // Nothing to render if argument is empty.
275  StringRef Arg0 = C->getArgText(0);
276  if (Arg0.empty())
277  return;
278 
279  switch (C->getRenderKind()) {
281  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
282  appendToResultWithHTMLEscaping(C->getArgText(i));
283  Result << " ";
284  }
285  return;
286 
288  assert(C->getNumArgs() == 1);
289  Result << "<b>";
290  appendToResultWithHTMLEscaping(Arg0);
291  Result << "</b>";
292  return;
294  assert(C->getNumArgs() == 1);
295  Result << "<tt>";
296  appendToResultWithHTMLEscaping(Arg0);
297  Result<< "</tt>";
298  return;
300  assert(C->getNumArgs() == 1);
301  Result << "<em>";
302  appendToResultWithHTMLEscaping(Arg0);
303  Result << "</em>";
304  return;
305  }
306 }
307 
308 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
309  const HTMLStartTagComment *C) {
310  printHTMLStartTagComment(C, Result);
311 }
312 
313 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
314  const HTMLEndTagComment *C) {
315  Result << "</" << C->getTagName() << ">";
316 }
317 
318 void CommentASTToHTMLConverter::visitParagraphComment(
319  const ParagraphComment *C) {
320  if (C->isWhitespace())
321  return;
322 
323  Result << "<p>";
324  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
325  I != E; ++I) {
326  visit(*I);
327  }
328  Result << "</p>";
329 }
330 
331 void CommentASTToHTMLConverter::visitBlockCommandComment(
332  const BlockCommandComment *C) {
333  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
334  if (Info->IsBriefCommand) {
335  Result << "<p class=\"para-brief\">";
336  visitNonStandaloneParagraphComment(C->getParagraph());
337  Result << "</p>";
338  return;
339  }
340  if (Info->IsReturnsCommand) {
341  Result << "<p class=\"para-returns\">"
342  "<span class=\"word-returns\">Returns</span> ";
343  visitNonStandaloneParagraphComment(C->getParagraph());
344  Result << "</p>";
345  return;
346  }
347  // We don't know anything about this command. Just render the paragraph.
348  visit(C->getParagraph());
349 }
350 
351 void CommentASTToHTMLConverter::visitParamCommandComment(
352  const ParamCommandComment *C) {
353  if (C->isParamIndexValid()) {
354  if (C->isVarArgParam()) {
355  Result << "<dt class=\"param-name-index-vararg\">";
356  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
357  } else {
358  Result << "<dt class=\"param-name-index-"
359  << C->getParamIndex()
360  << "\">";
361  appendToResultWithHTMLEscaping(C->getParamName(FC));
362  }
363  } else {
364  Result << "<dt class=\"param-name-index-invalid\">";
365  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
366  }
367  Result << "</dt>";
368 
369  if (C->isParamIndexValid()) {
370  if (C->isVarArgParam())
371  Result << "<dd class=\"param-descr-index-vararg\">";
372  else
373  Result << "<dd class=\"param-descr-index-"
374  << C->getParamIndex()
375  << "\">";
376  } else
377  Result << "<dd class=\"param-descr-index-invalid\">";
378 
379  visitNonStandaloneParagraphComment(C->getParagraph());
380  Result << "</dd>";
381 }
382 
383 void CommentASTToHTMLConverter::visitTParamCommandComment(
384  const TParamCommandComment *C) {
385  if (C->isPositionValid()) {
386  if (C->getDepth() == 1)
387  Result << "<dt class=\"tparam-name-index-"
388  << C->getIndex(0)
389  << "\">";
390  else
391  Result << "<dt class=\"tparam-name-index-other\">";
392  appendToResultWithHTMLEscaping(C->getParamName(FC));
393  } else {
394  Result << "<dt class=\"tparam-name-index-invalid\">";
395  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
396  }
397 
398  Result << "</dt>";
399 
400  if (C->isPositionValid()) {
401  if (C->getDepth() == 1)
402  Result << "<dd class=\"tparam-descr-index-"
403  << C->getIndex(0)
404  << "\">";
405  else
406  Result << "<dd class=\"tparam-descr-index-other\">";
407  } else
408  Result << "<dd class=\"tparam-descr-index-invalid\">";
409 
410  visitNonStandaloneParagraphComment(C->getParagraph());
411  Result << "</dd>";
412 }
413 
414 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
415  const VerbatimBlockComment *C) {
416  unsigned NumLines = C->getNumLines();
417  if (NumLines == 0)
418  return;
419 
420  Result << "<pre>";
421  for (unsigned i = 0; i != NumLines; ++i) {
422  appendToResultWithHTMLEscaping(C->getText(i));
423  if (i + 1 != NumLines)
424  Result << '\n';
425  }
426  Result << "</pre>";
427 }
428 
429 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
430  const VerbatimBlockLineComment *C) {
431  llvm_unreachable("should not see this AST node");
432 }
433 
434 void CommentASTToHTMLConverter::visitVerbatimLineComment(
435  const VerbatimLineComment *C) {
436  Result << "<pre>";
437  appendToResultWithHTMLEscaping(C->getText());
438  Result << "</pre>";
439 }
440 
441 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
442  FullCommentParts Parts(C, Traits);
443 
444  bool FirstParagraphIsBrief = false;
445  if (Parts.Headerfile)
446  visit(Parts.Headerfile);
447  if (Parts.Brief)
448  visit(Parts.Brief);
449  else if (Parts.FirstParagraph) {
450  Result << "<p class=\"para-brief\">";
451  visitNonStandaloneParagraphComment(Parts.FirstParagraph);
452  Result << "</p>";
453  FirstParagraphIsBrief = true;
454  }
455 
456  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
457  const Comment *C = Parts.MiscBlocks[i];
458  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
459  continue;
460  visit(C);
461  }
462 
463  if (Parts.TParams.size() != 0) {
464  Result << "<dl>";
465  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
466  visit(Parts.TParams[i]);
467  Result << "</dl>";
468  }
469 
470  if (Parts.Params.size() != 0) {
471  Result << "<dl>";
472  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
473  visit(Parts.Params[i]);
474  Result << "</dl>";
475  }
476 
477  if (Parts.Returns.size() != 0) {
478  Result << "<div class=\"result-discussion\">";
479  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
480  visit(Parts.Returns[i]);
481  Result << "</div>";
482  }
483 
484  Result.flush();
485 }
486 
487 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
488  const ParagraphComment *C) {
489  if (!C)
490  return;
491 
492  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
493  I != E; ++I) {
494  visit(*I);
495  }
496 }
497 
498 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
499  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
500  const char C = *I;
501  switch (C) {
502  case '&':
503  Result << "&amp;";
504  break;
505  case '<':
506  Result << "&lt;";
507  break;
508  case '>':
509  Result << "&gt;";
510  break;
511  case '"':
512  Result << "&quot;";
513  break;
514  case '\'':
515  Result << "&#39;";
516  break;
517  case '/':
518  Result << "&#47;";
519  break;
520  default:
521  Result << C;
522  break;
523  }
524  }
525 }
526 
527 namespace {
528 class CommentASTToXMLConverter :
529  public ConstCommentVisitor<CommentASTToXMLConverter> {
530 public:
531  /// \param Str accumulator for XML.
532  CommentASTToXMLConverter(const FullComment *FC,
534  const CommandTraits &Traits,
535  const SourceManager &SM,
536  SimpleFormatContext &SFC,
537  unsigned FUID) :
538  FC(FC), Result(Str), Traits(Traits), SM(SM),
539  FormatRewriterContext(SFC),
540  FormatInMemoryUniqueId(FUID) { }
541 
542  // Inline content.
543  void visitTextComment(const TextComment *C);
544  void visitInlineCommandComment(const InlineCommandComment *C);
545  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
546  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
547 
548  // Block content.
549  void visitParagraphComment(const ParagraphComment *C);
550 
551  void appendParagraphCommentWithKind(const ParagraphComment *C,
552  StringRef Kind);
553 
554  void visitBlockCommandComment(const BlockCommandComment *C);
555  void visitParamCommandComment(const ParamCommandComment *C);
556  void visitTParamCommandComment(const TParamCommandComment *C);
557  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
558  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
559  void visitVerbatimLineComment(const VerbatimLineComment *C);
560 
561  void visitFullComment(const FullComment *C);
562 
563  // Helpers.
564  void appendToResultWithXMLEscaping(StringRef S);
565  void appendToResultWithCDATAEscaping(StringRef S);
566 
567  void formatTextOfDeclaration(const DeclInfo *DI,
568  SmallString<128> &Declaration);
569 
570 private:
571  const FullComment *FC;
572 
573  /// Output stream for XML.
574  llvm::raw_svector_ostream Result;
575 
576  const CommandTraits &Traits;
577  const SourceManager &SM;
578  SimpleFormatContext &FormatRewriterContext;
579  unsigned FormatInMemoryUniqueId;
580 };
581 
582 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
583  SmallVectorImpl<char> &Str) {
585  const LangOptions &LangOpts = Context.getLangOpts();
586  llvm::raw_svector_ostream OS(Str);
587  PrintingPolicy PPolicy(LangOpts);
588  PPolicy.PolishForDeclaration = true;
589  PPolicy.TerseOutput = true;
590  ThisDecl->CurrentDecl->print(OS, PPolicy,
591  /*Indentation*/0, /*PrintInstantiation*/false);
592 }
593 
594 void CommentASTToXMLConverter::formatTextOfDeclaration(
595  const DeclInfo *DI, SmallString<128> &Declaration) {
596  // FIXME. formatting API expects null terminated input string.
597  // There might be more efficient way of doing this.
598  std::string StringDecl = Declaration.str();
599 
600  // Formatter specific code.
601  // Form a unique in memory buffer name.
602  SmallString<128> filename;
603  filename += "xmldecl";
604  filename += llvm::utostr(FormatInMemoryUniqueId);
605  filename += ".xd";
606  FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
607  SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
608  .getLocWithOffset(0);
609  unsigned Length = Declaration.size();
610 
612  format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
613  CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
614  applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
615  Declaration = FormatRewriterContext.getRewrittenText(ID);
616 }
617 
618 } // end unnamed namespace
619 
620 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
621  appendToResultWithXMLEscaping(C->getText());
622 }
623 
624 void CommentASTToXMLConverter::visitInlineCommandComment(
625  const InlineCommandComment *C) {
626  // Nothing to render if no arguments supplied.
627  if (C->getNumArgs() == 0)
628  return;
629 
630  // Nothing to render if argument is empty.
631  StringRef Arg0 = C->getArgText(0);
632  if (Arg0.empty())
633  return;
634 
635  switch (C->getRenderKind()) {
637  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
638  appendToResultWithXMLEscaping(C->getArgText(i));
639  Result << " ";
640  }
641  return;
643  assert(C->getNumArgs() == 1);
644  Result << "<bold>";
645  appendToResultWithXMLEscaping(Arg0);
646  Result << "</bold>";
647  return;
649  assert(C->getNumArgs() == 1);
650  Result << "<monospaced>";
651  appendToResultWithXMLEscaping(Arg0);
652  Result << "</monospaced>";
653  return;
655  assert(C->getNumArgs() == 1);
656  Result << "<emphasized>";
657  appendToResultWithXMLEscaping(Arg0);
658  Result << "</emphasized>";
659  return;
660  }
661 }
662 
663 void CommentASTToXMLConverter::visitHTMLStartTagComment(
664  const HTMLStartTagComment *C) {
665  Result << "<rawHTML";
666  if (C->isMalformed())
667  Result << " isMalformed=\"1\"";
668  Result << ">";
669  {
670  SmallString<32> Tag;
671  {
672  llvm::raw_svector_ostream TagOS(Tag);
673  printHTMLStartTagComment(C, TagOS);
674  }
675  appendToResultWithCDATAEscaping(Tag);
676  }
677  Result << "</rawHTML>";
678 }
679 
680 void
681 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
682  Result << "<rawHTML";
683  if (C->isMalformed())
684  Result << " isMalformed=\"1\"";
685  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
686 }
687 
688 void
689 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
690  appendParagraphCommentWithKind(C, StringRef());
691 }
692 
693 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
694  const ParagraphComment *C,
695  StringRef ParagraphKind) {
696  if (C->isWhitespace())
697  return;
698 
699  if (ParagraphKind.empty())
700  Result << "<Para>";
701  else
702  Result << "<Para kind=\"" << ParagraphKind << "\">";
703 
704  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
705  I != E; ++I) {
706  visit(*I);
707  }
708  Result << "</Para>";
709 }
710 
711 void CommentASTToXMLConverter::visitBlockCommandComment(
712  const BlockCommandComment *C) {
713  StringRef ParagraphKind;
714 
715  switch (C->getCommandID()) {
716  case CommandTraits::KCI_attention:
717  case CommandTraits::KCI_author:
718  case CommandTraits::KCI_authors:
719  case CommandTraits::KCI_bug:
720  case CommandTraits::KCI_copyright:
721  case CommandTraits::KCI_date:
722  case CommandTraits::KCI_invariant:
723  case CommandTraits::KCI_note:
724  case CommandTraits::KCI_post:
725  case CommandTraits::KCI_pre:
726  case CommandTraits::KCI_remark:
727  case CommandTraits::KCI_remarks:
728  case CommandTraits::KCI_sa:
729  case CommandTraits::KCI_see:
730  case CommandTraits::KCI_since:
731  case CommandTraits::KCI_todo:
732  case CommandTraits::KCI_version:
733  case CommandTraits::KCI_warning:
734  ParagraphKind = C->getCommandName(Traits);
735  default:
736  break;
737  }
738 
739  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
740 }
741 
742 void CommentASTToXMLConverter::visitParamCommandComment(
743  const ParamCommandComment *C) {
744  Result << "<Parameter><Name>";
745  appendToResultWithXMLEscaping(C->isParamIndexValid()
746  ? C->getParamName(FC)
747  : C->getParamNameAsWritten());
748  Result << "</Name>";
749 
750  if (C->isParamIndexValid()) {
751  if (C->isVarArgParam())
752  Result << "<IsVarArg />";
753  else
754  Result << "<Index>" << C->getParamIndex() << "</Index>";
755  }
756 
757  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
758  switch (C->getDirection()) {
760  Result << "in";
761  break;
763  Result << "out";
764  break;
766  Result << "in,out";
767  break;
768  }
769  Result << "</Direction><Discussion>";
770  visit(C->getParagraph());
771  Result << "</Discussion></Parameter>";
772 }
773 
774 void CommentASTToXMLConverter::visitTParamCommandComment(
775  const TParamCommandComment *C) {
776  Result << "<Parameter><Name>";
777  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
778  : C->getParamNameAsWritten());
779  Result << "</Name>";
780 
781  if (C->isPositionValid() && C->getDepth() == 1) {
782  Result << "<Index>" << C->getIndex(0) << "</Index>";
783  }
784 
785  Result << "<Discussion>";
786  visit(C->getParagraph());
787  Result << "</Discussion></Parameter>";
788 }
789 
790 void CommentASTToXMLConverter::visitVerbatimBlockComment(
791  const VerbatimBlockComment *C) {
792  unsigned NumLines = C->getNumLines();
793  if (NumLines == 0)
794  return;
795 
796  switch (C->getCommandID()) {
797  case CommandTraits::KCI_code:
798  Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
799  break;
800  default:
801  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
802  break;
803  }
804  for (unsigned i = 0; i != NumLines; ++i) {
805  appendToResultWithXMLEscaping(C->getText(i));
806  if (i + 1 != NumLines)
807  Result << '\n';
808  }
809  Result << "</Verbatim>";
810 }
811 
812 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
813  const VerbatimBlockLineComment *C) {
814  llvm_unreachable("should not see this AST node");
815 }
816 
817 void CommentASTToXMLConverter::visitVerbatimLineComment(
818  const VerbatimLineComment *C) {
819  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
820  appendToResultWithXMLEscaping(C->getText());
821  Result << "</Verbatim>";
822 }
823 
824 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
825  FullCommentParts Parts(C, Traits);
826 
827  const DeclInfo *DI = C->getDeclInfo();
828  StringRef RootEndTag;
829  if (DI) {
830  switch (DI->getKind()) {
831  case DeclInfo::OtherKind:
832  RootEndTag = "</Other>";
833  Result << "<Other";
834  break;
836  RootEndTag = "</Function>";
837  Result << "<Function";
838  switch (DI->TemplateKind) {
840  break;
841  case DeclInfo::Template:
842  Result << " templateKind=\"template\"";
843  break;
845  Result << " templateKind=\"specialization\"";
846  break;
848  llvm_unreachable("partial specializations of functions "
849  "are not allowed in C++");
850  }
851  if (DI->IsInstanceMethod)
852  Result << " isInstanceMethod=\"1\"";
853  if (DI->IsClassMethod)
854  Result << " isClassMethod=\"1\"";
855  break;
856  case DeclInfo::ClassKind:
857  RootEndTag = "</Class>";
858  Result << "<Class";
859  switch (DI->TemplateKind) {
861  break;
862  case DeclInfo::Template:
863  Result << " templateKind=\"template\"";
864  break;
866  Result << " templateKind=\"specialization\"";
867  break;
869  Result << " templateKind=\"partialSpecialization\"";
870  break;
871  }
872  break;
874  RootEndTag = "</Variable>";
875  Result << "<Variable";
876  break;
878  RootEndTag = "</Namespace>";
879  Result << "<Namespace";
880  break;
882  RootEndTag = "</Typedef>";
883  Result << "<Typedef";
884  break;
885  case DeclInfo::EnumKind:
886  RootEndTag = "</Enum>";
887  Result << "<Enum";
888  break;
889  }
890 
891  {
892  // Print line and column number.
894  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
895  FileID FID = LocInfo.first;
896  unsigned FileOffset = LocInfo.second;
897 
898  if (!FID.isInvalid()) {
899  if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
900  Result << " file=\"";
901  appendToResultWithXMLEscaping(FE->getName());
902  Result << "\"";
903  }
904  Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
905  << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
906  << "\"";
907  }
908  }
909 
910  // Finish the root tag.
911  Result << ">";
912 
913  bool FoundName = false;
914  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
915  if (DeclarationName DeclName = ND->getDeclName()) {
916  Result << "<Name>";
917  std::string Name = DeclName.getAsString();
918  appendToResultWithXMLEscaping(Name);
919  FoundName = true;
920  Result << "</Name>";
921  }
922  }
923  if (!FoundName)
924  Result << "<Name>&lt;anonymous&gt;</Name>";
925 
926  {
927  // Print USR.
928  SmallString<128> USR;
930  if (!USR.empty()) {
931  Result << "<USR>";
932  appendToResultWithXMLEscaping(USR);
933  Result << "</USR>";
934  }
935  }
936  } else {
937  // No DeclInfo -- just emit some root tag and name tag.
938  RootEndTag = "</Other>";
939  Result << "<Other><Name>unknown</Name>";
940  }
941 
942  if (Parts.Headerfile) {
943  Result << "<Headerfile>";
944  visit(Parts.Headerfile);
945  Result << "</Headerfile>";
946  }
947 
948  {
949  // Pretty-print the declaration.
950  Result << "<Declaration>";
951  SmallString<128> Declaration;
952  getSourceTextOfDeclaration(DI, Declaration);
953  formatTextOfDeclaration(DI, Declaration);
954  appendToResultWithXMLEscaping(Declaration);
955  Result << "</Declaration>";
956  }
957 
958  bool FirstParagraphIsBrief = false;
959  if (Parts.Brief) {
960  Result << "<Abstract>";
961  visit(Parts.Brief);
962  Result << "</Abstract>";
963  } else if (Parts.FirstParagraph) {
964  Result << "<Abstract>";
965  visit(Parts.FirstParagraph);
966  Result << "</Abstract>";
967  FirstParagraphIsBrief = true;
968  }
969 
970  if (Parts.TParams.size() != 0) {
971  Result << "<TemplateParameters>";
972  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
973  visit(Parts.TParams[i]);
974  Result << "</TemplateParameters>";
975  }
976 
977  if (Parts.Params.size() != 0) {
978  Result << "<Parameters>";
979  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
980  visit(Parts.Params[i]);
981  Result << "</Parameters>";
982  }
983 
984  if (Parts.Exceptions.size() != 0) {
985  Result << "<Exceptions>";
986  for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
987  visit(Parts.Exceptions[i]);
988  Result << "</Exceptions>";
989  }
990 
991  if (Parts.Returns.size() != 0) {
992  Result << "<ResultDiscussion>";
993  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
994  visit(Parts.Returns[i]);
995  Result << "</ResultDiscussion>";
996  }
997 
998  if (DI->CommentDecl->hasAttrs()) {
999  const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1000  for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1001  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1002  if (!AA) {
1003  if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1004  if (DA->getMessage().empty())
1005  Result << "<Deprecated/>";
1006  else {
1007  Result << "<Deprecated>";
1008  appendToResultWithXMLEscaping(DA->getMessage());
1009  Result << "</Deprecated>";
1010  }
1011  }
1012  else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1013  if (UA->getMessage().empty())
1014  Result << "<Unavailable/>";
1015  else {
1016  Result << "<Unavailable>";
1017  appendToResultWithXMLEscaping(UA->getMessage());
1018  Result << "</Unavailable>";
1019  }
1020  }
1021  continue;
1022  }
1023 
1024  // 'availability' attribute.
1025  Result << "<Availability";
1026  StringRef Distribution;
1027  if (AA->getPlatform()) {
1028  Distribution = AvailabilityAttr::getPrettyPlatformName(
1029  AA->getPlatform()->getName());
1030  if (Distribution.empty())
1031  Distribution = AA->getPlatform()->getName();
1032  }
1033  Result << " distribution=\"" << Distribution << "\">";
1034  VersionTuple IntroducedInVersion = AA->getIntroduced();
1035  if (!IntroducedInVersion.empty()) {
1036  Result << "<IntroducedInVersion>"
1037  << IntroducedInVersion.getAsString()
1038  << "</IntroducedInVersion>";
1039  }
1040  VersionTuple DeprecatedInVersion = AA->getDeprecated();
1041  if (!DeprecatedInVersion.empty()) {
1042  Result << "<DeprecatedInVersion>"
1043  << DeprecatedInVersion.getAsString()
1044  << "</DeprecatedInVersion>";
1045  }
1046  VersionTuple RemovedAfterVersion = AA->getObsoleted();
1047  if (!RemovedAfterVersion.empty()) {
1048  Result << "<RemovedAfterVersion>"
1049  << RemovedAfterVersion.getAsString()
1050  << "</RemovedAfterVersion>";
1051  }
1052  StringRef DeprecationSummary = AA->getMessage();
1053  if (!DeprecationSummary.empty()) {
1054  Result << "<DeprecationSummary>";
1055  appendToResultWithXMLEscaping(DeprecationSummary);
1056  Result << "</DeprecationSummary>";
1057  }
1058  if (AA->getUnavailable())
1059  Result << "<Unavailable/>";
1060  Result << "</Availability>";
1061  }
1062  }
1063 
1064  {
1065  bool StartTagEmitted = false;
1066  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1067  const Comment *C = Parts.MiscBlocks[i];
1068  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1069  continue;
1070  if (!StartTagEmitted) {
1071  Result << "<Discussion>";
1072  StartTagEmitted = true;
1073  }
1074  visit(C);
1075  }
1076  if (StartTagEmitted)
1077  Result << "</Discussion>";
1078  }
1079 
1080  Result << RootEndTag;
1081 
1082  Result.flush();
1083 }
1084 
1085 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1086  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1087  const char C = *I;
1088  switch (C) {
1089  case '&':
1090  Result << "&amp;";
1091  break;
1092  case '<':
1093  Result << "&lt;";
1094  break;
1095  case '>':
1096  Result << "&gt;";
1097  break;
1098  case '"':
1099  Result << "&quot;";
1100  break;
1101  case '\'':
1102  Result << "&apos;";
1103  break;
1104  default:
1105  Result << C;
1106  break;
1107  }
1108  }
1109 }
1110 
1111 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1112  if (S.empty())
1113  return;
1114 
1115  Result << "<![CDATA[";
1116  while (!S.empty()) {
1117  size_t Pos = S.find("]]>");
1118  if (Pos == 0) {
1119  Result << "]]]]><![CDATA[>";
1120  S = S.drop_front(3);
1121  continue;
1122  }
1123  if (Pos == StringRef::npos)
1124  Pos = S.size();
1125 
1126  Result << S.substr(0, Pos);
1127 
1128  S = S.drop_front(Pos);
1129  }
1130  Result << "]]>";
1131 }
1132 
1133 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
1135 
1137  SmallVectorImpl<char> &HTML,
1138  const ASTContext &Context) {
1139  CommentASTToHTMLConverter Converter(FC, HTML,
1140  Context.getCommentCommandTraits());
1141  Converter.visit(FC);
1142 }
1143 
1146  const ASTContext &Context) {
1147  CommentASTToHTMLConverter Converter(nullptr, Text,
1148  Context.getCommentCommandTraits());
1149  Converter.visit(HTC);
1150 }
1151 
1153  SmallVectorImpl<char> &XML,
1154  const ASTContext &Context) {
1155  if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
1156  // Create a new format context, or re-create it after some number of
1157  // iterations, so the buffers don't grow too large.
1158  FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
1159  }
1160 
1161  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1162  Context.getSourceManager(), *FormatContext,
1163  FormatInMemoryUniqueId++);
1164  Converter.visit(FC);
1165 }
1166 
Defines the clang::ASTContext interface.
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:782
child_iterator child_begin() const
Definition: Comment.h:1117
const Decl * CommentDecl
Definition: Comment.h:989
Represents a version number in the form major[.minor[.subminor[.build]]].
Definition: VersionTuple.h:26
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:848
Defines a utility class for use of clang-format in libclang.
std::set< Replacement > Replacements
A set of Replacements. FIXME: Change to a vector and deduplicate in the RefactoringTool.
Definition: Replacement.h:141
Describes how types, statements, expressions, and declarations should be printed. ...
Definition: PrettyPrinter.h:35
A small class to be used by libclang clients to format a declaration string in memory. This object is instantiated once and used each time a formatting is needed.
Information about a single command.
const CommandInfo * getCommandInfo(StringRef Name) const
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite)
Apply all replacements in Replaces to the Rewriter Rewrite.
ParagraphComment * getParagraph() const LLVM_READONLY
Definition: Comment.h:695
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:697
StringRef getParamNameAsWritten() const
Definition: Comment.h:770
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:89
StringRef getParamNameAsWritten() const
Definition: Comment.h:840
const Decl * CurrentDecl
Definition: Comment.h:999
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
A command with word-like arguments that is considered inline content.
Definition: Comment.h:303
child_iterator child_end() const
Definition: Comment.h:1121
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
A line of text contained in a verbatim block.
Definition: Comment.h:869
std::string getAsString() const
Retrieve a string representation of the version number.
const LangOptions & getLangOpts() const
Definition: ASTContext.h:533
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:791
#define UINT_MAX
Definition: limits.h:72
bool hasNonWhitespaceParagraph() const
Definition: Comment.h:699
child_iterator child_end() const
Definition: Comment.h:582
unsigned getCommandID() const
Definition: Comment.h:656
void convertCommentToHTML(const comments::FullComment *FC, SmallVectorImpl< char > &HTML, const ASTContext &Context)
StringRef getText() const LLVM_READONLY
Definition: Comment.h:287
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1070
ASTContext * Context
FormatStyle getLLVMStyle()
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:342
ID
Defines the set of possible language-specific address spaces.
Definition: AddressSpaces.h:27
SourceManager & SM
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:755
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:331
An enumeration or scoped enumeration.
Definition: Comment.h:1052
An opening HTML tag with attributes.
Definition: Comment.h:419
unsigned getColumnNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Return the column # for the specified file position.
AttrVec & getAttrs()
Definition: DeclBase.h:431
static CharSourceRange getCharRange(SourceRange R)
Kind
RenderKind getRenderKind() const
Definition: Comment.h:358
Encodes a location in the source. The SourceManager can decode this to get at the full include stack...
CommentKind getCommentKind() const
Definition: Comment.h:204
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:284
void convertHTMLTagNodeToText(const comments::HTMLTagComment *HTC, SmallVectorImpl< char > &Text, const ASTContext &Context)
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:778
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:53
const Attribute & getAttr(unsigned Idx) const
Definition: Comment.h:482
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:857
Comment *const * child_iterator
Definition: Comment.h:228
PassDirection getDirection() const LLVM_READONLY
Definition: Comment.h:751
A closing HTML tag.
Definition: Comment.h:513
Everything else not explicitly mentioned below.
Definition: Comment.h:1018
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:805
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool empty() const
Determine whether this version information is empty (e.g., all version components are zero)...
Definition: VersionTuple.h:64
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Given a SourceLocation, return the spelling line number for the position indicated.
bool hasAttrs() const
Definition: DeclBase.h:427
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:986
A single paragraph that contains inline content.
Definition: Comment.h:552
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1087
StringRef getTagName() const LLVM_READONLY
Definition: Comment.h:401
void print(raw_ostream &Out, unsigned Indentation=0, bool PrintInstantiation=false) const
bool isInvalid() const
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:324
child_iterator child_begin() const
Definition: Comment.h:578
unsigned IsReturnsCommand
True if this command is \returns or an alias.
SourceManager & getSourceManager()
Definition: ASTContext.h:494
StringRef getText(unsigned LineIdx) const
Definition: Comment.h:941
StringRef getArgText(unsigned Idx) const
Definition: Comment.h:366
Doxygen \param command.
Definition: Comment.h:717
const DeclInfo * getDeclInfo() const LLVM_READONLY
Definition: Comment.h:1129
tooling::Replacements reformat(const FormatStyle &Style, SourceManager &SourceMgr, FileID ID, ArrayRef< CharSourceRange > Ranges, bool *IncompleteFormat=nullptr)
Reformats the given Ranges in the file ID.
Definition: Format.cpp:1563
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
SourceLocation getLocation() const
Definition: DeclBase.h:372
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
void convertCommentToXML(const comments::FullComment *FC, SmallVectorImpl< char > &XML, const ASTContext &Context)
This class handles loading and caching of source files into memory.
StringRef getCommandName(const CommandTraits &Traits) const
Definition: Comment.h:660
Attr - This represents one attribute.
Definition: Attr.h:44
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1097