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