clang  8.0.0svn
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) {
576  ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
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  PPolicy.ConstantsAsWritten = true;
583  ThisDecl->CurrentDecl->print(OS, PPolicy,
584  /*Indentation*/0, /*PrintInstantiation*/false);
585 }
586 
587 void CommentASTToXMLConverter::formatTextOfDeclaration(
588  const DeclInfo *DI, SmallString<128> &Declaration) {
589  // Formatting API expects null terminated input string.
590  StringRef StringDecl(Declaration.c_str(), Declaration.size());
591 
592  // Formatter specific code.
593  unsigned Offset = 0;
594  unsigned Length = Declaration.size();
595 
597  Style.FixNamespaceComments = false;
598  tooling::Replacements Replaces =
599  reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
600  auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
601  if (static_cast<bool>(FormattedStringDecl)) {
602  Declaration = *FormattedStringDecl;
603  }
604 }
605 
606 } // end unnamed namespace
607 
608 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
609  appendToResultWithXMLEscaping(C->getText());
610 }
611 
612 void CommentASTToXMLConverter::visitInlineCommandComment(
613  const InlineCommandComment *C) {
614  // Nothing to render if no arguments supplied.
615  if (C->getNumArgs() == 0)
616  return;
617 
618  // Nothing to render if argument is empty.
619  StringRef Arg0 = C->getArgText(0);
620  if (Arg0.empty())
621  return;
622 
623  switch (C->getRenderKind()) {
624  case InlineCommandComment::RenderNormal:
625  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
626  appendToResultWithXMLEscaping(C->getArgText(i));
627  Result << " ";
628  }
629  return;
630  case InlineCommandComment::RenderBold:
631  assert(C->getNumArgs() == 1);
632  Result << "<bold>";
633  appendToResultWithXMLEscaping(Arg0);
634  Result << "</bold>";
635  return;
636  case InlineCommandComment::RenderMonospaced:
637  assert(C->getNumArgs() == 1);
638  Result << "<monospaced>";
639  appendToResultWithXMLEscaping(Arg0);
640  Result << "</monospaced>";
641  return;
642  case InlineCommandComment::RenderEmphasized:
643  assert(C->getNumArgs() == 1);
644  Result << "<emphasized>";
645  appendToResultWithXMLEscaping(Arg0);
646  Result << "</emphasized>";
647  return;
648  }
649 }
650 
651 void CommentASTToXMLConverter::visitHTMLStartTagComment(
652  const HTMLStartTagComment *C) {
653  Result << "<rawHTML";
654  if (C->isMalformed())
655  Result << " isMalformed=\"1\"";
656  Result << ">";
657  {
658  SmallString<32> Tag;
659  {
660  llvm::raw_svector_ostream TagOS(Tag);
661  printHTMLStartTagComment(C, TagOS);
662  }
663  appendToResultWithCDATAEscaping(Tag);
664  }
665  Result << "</rawHTML>";
666 }
667 
668 void
669 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
670  Result << "<rawHTML";
671  if (C->isMalformed())
672  Result << " isMalformed=\"1\"";
673  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
674 }
675 
676 void
677 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
678  appendParagraphCommentWithKind(C, StringRef());
679 }
680 
681 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
682  const ParagraphComment *C,
683  StringRef ParagraphKind) {
684  if (C->isWhitespace())
685  return;
686 
687  if (ParagraphKind.empty())
688  Result << "<Para>";
689  else
690  Result << "<Para kind=\"" << ParagraphKind << "\">";
691 
692  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
693  I != E; ++I) {
694  visit(*I);
695  }
696  Result << "</Para>";
697 }
698 
699 void CommentASTToXMLConverter::visitBlockCommandComment(
700  const BlockCommandComment *C) {
701  StringRef ParagraphKind;
702 
703  switch (C->getCommandID()) {
704  case CommandTraits::KCI_attention:
705  case CommandTraits::KCI_author:
706  case CommandTraits::KCI_authors:
707  case CommandTraits::KCI_bug:
708  case CommandTraits::KCI_copyright:
709  case CommandTraits::KCI_date:
710  case CommandTraits::KCI_invariant:
711  case CommandTraits::KCI_note:
712  case CommandTraits::KCI_post:
713  case CommandTraits::KCI_pre:
714  case CommandTraits::KCI_remark:
715  case CommandTraits::KCI_remarks:
716  case CommandTraits::KCI_sa:
717  case CommandTraits::KCI_see:
718  case CommandTraits::KCI_since:
719  case CommandTraits::KCI_todo:
720  case CommandTraits::KCI_version:
721  case CommandTraits::KCI_warning:
722  ParagraphKind = C->getCommandName(Traits);
723  break;
724  default:
725  break;
726  }
727 
728  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
729 }
730 
731 void CommentASTToXMLConverter::visitParamCommandComment(
732  const ParamCommandComment *C) {
733  Result << "<Parameter><Name>";
734  appendToResultWithXMLEscaping(C->isParamIndexValid()
735  ? C->getParamName(FC)
736  : C->getParamNameAsWritten());
737  Result << "</Name>";
738 
739  if (C->isParamIndexValid()) {
740  if (C->isVarArgParam())
741  Result << "<IsVarArg />";
742  else
743  Result << "<Index>" << C->getParamIndex() << "</Index>";
744  }
745 
746  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
747  switch (C->getDirection()) {
748  case ParamCommandComment::In:
749  Result << "in";
750  break;
751  case ParamCommandComment::Out:
752  Result << "out";
753  break;
754  case ParamCommandComment::InOut:
755  Result << "in,out";
756  break;
757  }
758  Result << "</Direction><Discussion>";
759  visit(C->getParagraph());
760  Result << "</Discussion></Parameter>";
761 }
762 
763 void CommentASTToXMLConverter::visitTParamCommandComment(
764  const TParamCommandComment *C) {
765  Result << "<Parameter><Name>";
766  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
767  : C->getParamNameAsWritten());
768  Result << "</Name>";
769 
770  if (C->isPositionValid() && C->getDepth() == 1) {
771  Result << "<Index>" << C->getIndex(0) << "</Index>";
772  }
773 
774  Result << "<Discussion>";
775  visit(C->getParagraph());
776  Result << "</Discussion></Parameter>";
777 }
778 
779 void CommentASTToXMLConverter::visitVerbatimBlockComment(
780  const VerbatimBlockComment *C) {
781  unsigned NumLines = C->getNumLines();
782  if (NumLines == 0)
783  return;
784 
785  switch (C->getCommandID()) {
786  case CommandTraits::KCI_code:
787  Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
788  break;
789  default:
790  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
791  break;
792  }
793  for (unsigned i = 0; i != NumLines; ++i) {
794  appendToResultWithXMLEscaping(C->getText(i));
795  if (i + 1 != NumLines)
796  Result << '\n';
797  }
798  Result << "</Verbatim>";
799 }
800 
801 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
802  const VerbatimBlockLineComment *C) {
803  llvm_unreachable("should not see this AST node");
804 }
805 
806 void CommentASTToXMLConverter::visitVerbatimLineComment(
807  const VerbatimLineComment *C) {
808  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
809  appendToResultWithXMLEscaping(C->getText());
810  Result << "</Verbatim>";
811 }
812 
813 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
814  FullCommentParts Parts(C, Traits);
815 
816  const DeclInfo *DI = C->getDeclInfo();
817  StringRef RootEndTag;
818  if (DI) {
819  switch (DI->getKind()) {
820  case DeclInfo::OtherKind:
821  RootEndTag = "</Other>";
822  Result << "<Other";
823  break;
824  case DeclInfo::FunctionKind:
825  RootEndTag = "</Function>";
826  Result << "<Function";
827  switch (DI->TemplateKind) {
828  case DeclInfo::NotTemplate:
829  break;
830  case DeclInfo::Template:
831  Result << " templateKind=\"template\"";
832  break;
834  Result << " templateKind=\"specialization\"";
835  break;
837  llvm_unreachable("partial specializations of functions "
838  "are not allowed in C++");
839  }
840  if (DI->IsInstanceMethod)
841  Result << " isInstanceMethod=\"1\"";
842  if (DI->IsClassMethod)
843  Result << " isClassMethod=\"1\"";
844  break;
845  case DeclInfo::ClassKind:
846  RootEndTag = "</Class>";
847  Result << "<Class";
848  switch (DI->TemplateKind) {
849  case DeclInfo::NotTemplate:
850  break;
851  case DeclInfo::Template:
852  Result << " templateKind=\"template\"";
853  break;
855  Result << " templateKind=\"specialization\"";
856  break;
858  Result << " templateKind=\"partialSpecialization\"";
859  break;
860  }
861  break;
862  case DeclInfo::VariableKind:
863  RootEndTag = "</Variable>";
864  Result << "<Variable";
865  break;
866  case DeclInfo::NamespaceKind:
867  RootEndTag = "</Namespace>";
868  Result << "<Namespace";
869  break;
870  case DeclInfo::TypedefKind:
871  RootEndTag = "</Typedef>";
872  Result << "<Typedef";
873  break;
874  case DeclInfo::EnumKind:
875  RootEndTag = "</Enum>";
876  Result << "<Enum";
877  break;
878  }
879 
880  {
881  // Print line and column number.
883  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
884  FileID FID = LocInfo.first;
885  unsigned FileOffset = LocInfo.second;
886 
887  if (FID.isValid()) {
888  if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
889  Result << " file=\"";
890  appendToResultWithXMLEscaping(FE->getName());
891  Result << "\"";
892  }
893  Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
894  << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
895  << "\"";
896  }
897  }
898 
899  // Finish the root tag.
900  Result << ">";
901 
902  bool FoundName = false;
903  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
904  if (DeclarationName DeclName = ND->getDeclName()) {
905  Result << "<Name>";
906  std::string Name = DeclName.getAsString();
907  appendToResultWithXMLEscaping(Name);
908  FoundName = true;
909  Result << "</Name>";
910  }
911  }
912  if (!FoundName)
913  Result << "<Name>&lt;anonymous&gt;</Name>";
914 
915  {
916  // Print USR.
917  SmallString<128> USR;
919  if (!USR.empty()) {
920  Result << "<USR>";
921  appendToResultWithXMLEscaping(USR);
922  Result << "</USR>";
923  }
924  }
925  } else {
926  // No DeclInfo -- just emit some root tag and name tag.
927  RootEndTag = "</Other>";
928  Result << "<Other><Name>unknown</Name>";
929  }
930 
931  if (Parts.Headerfile) {
932  Result << "<Headerfile>";
933  visit(Parts.Headerfile);
934  Result << "</Headerfile>";
935  }
936 
937  {
938  // Pretty-print the declaration.
939  Result << "<Declaration>";
941  getSourceTextOfDeclaration(DI, Declaration);
942  formatTextOfDeclaration(DI, Declaration);
943  appendToResultWithXMLEscaping(Declaration);
944  Result << "</Declaration>";
945  }
946 
947  bool FirstParagraphIsBrief = false;
948  if (Parts.Brief) {
949  Result << "<Abstract>";
950  visit(Parts.Brief);
951  Result << "</Abstract>";
952  } else if (Parts.FirstParagraph) {
953  Result << "<Abstract>";
954  visit(Parts.FirstParagraph);
955  Result << "</Abstract>";
956  FirstParagraphIsBrief = true;
957  }
958 
959  if (Parts.TParams.size() != 0) {
960  Result << "<TemplateParameters>";
961  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
962  visit(Parts.TParams[i]);
963  Result << "</TemplateParameters>";
964  }
965 
966  if (Parts.Params.size() != 0) {
967  Result << "<Parameters>";
968  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
969  visit(Parts.Params[i]);
970  Result << "</Parameters>";
971  }
972 
973  if (Parts.Exceptions.size() != 0) {
974  Result << "<Exceptions>";
975  for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
976  visit(Parts.Exceptions[i]);
977  Result << "</Exceptions>";
978  }
979 
980  if (Parts.Returns.size() != 0) {
981  Result << "<ResultDiscussion>";
982  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
983  visit(Parts.Returns[i]);
984  Result << "</ResultDiscussion>";
985  }
986 
987  if (DI->CommentDecl->hasAttrs()) {
988  const AttrVec &Attrs = DI->CommentDecl->getAttrs();
989  for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
990  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
991  if (!AA) {
992  if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
993  if (DA->getMessage().empty())
994  Result << "<Deprecated/>";
995  else {
996  Result << "<Deprecated>";
997  appendToResultWithXMLEscaping(DA->getMessage());
998  Result << "</Deprecated>";
999  }
1000  }
1001  else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1002  if (UA->getMessage().empty())
1003  Result << "<Unavailable/>";
1004  else {
1005  Result << "<Unavailable>";
1006  appendToResultWithXMLEscaping(UA->getMessage());
1007  Result << "</Unavailable>";
1008  }
1009  }
1010  continue;
1011  }
1012 
1013  // 'availability' attribute.
1014  Result << "<Availability";
1015  StringRef Distribution;
1016  if (AA->getPlatform()) {
1017  Distribution = AvailabilityAttr::getPrettyPlatformName(
1018  AA->getPlatform()->getName());
1019  if (Distribution.empty())
1020  Distribution = AA->getPlatform()->getName();
1021  }
1022  Result << " distribution=\"" << Distribution << "\">";
1023  VersionTuple IntroducedInVersion = AA->getIntroduced();
1024  if (!IntroducedInVersion.empty()) {
1025  Result << "<IntroducedInVersion>"
1026  << IntroducedInVersion.getAsString()
1027  << "</IntroducedInVersion>";
1028  }
1029  VersionTuple DeprecatedInVersion = AA->getDeprecated();
1030  if (!DeprecatedInVersion.empty()) {
1031  Result << "<DeprecatedInVersion>"
1032  << DeprecatedInVersion.getAsString()
1033  << "</DeprecatedInVersion>";
1034  }
1035  VersionTuple RemovedAfterVersion = AA->getObsoleted();
1036  if (!RemovedAfterVersion.empty()) {
1037  Result << "<RemovedAfterVersion>"
1038  << RemovedAfterVersion.getAsString()
1039  << "</RemovedAfterVersion>";
1040  }
1041  StringRef DeprecationSummary = AA->getMessage();
1042  if (!DeprecationSummary.empty()) {
1043  Result << "<DeprecationSummary>";
1044  appendToResultWithXMLEscaping(DeprecationSummary);
1045  Result << "</DeprecationSummary>";
1046  }
1047  if (AA->getUnavailable())
1048  Result << "<Unavailable/>";
1049  Result << "</Availability>";
1050  }
1051  }
1052 
1053  {
1054  bool StartTagEmitted = false;
1055  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1056  const Comment *C = Parts.MiscBlocks[i];
1057  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1058  continue;
1059  if (!StartTagEmitted) {
1060  Result << "<Discussion>";
1061  StartTagEmitted = true;
1062  }
1063  visit(C);
1064  }
1065  if (StartTagEmitted)
1066  Result << "</Discussion>";
1067  }
1068 
1069  Result << RootEndTag;
1070 }
1071 
1072 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1073  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1074  const char C = *I;
1075  switch (C) {
1076  case '&':
1077  Result << "&amp;";
1078  break;
1079  case '<':
1080  Result << "&lt;";
1081  break;
1082  case '>':
1083  Result << "&gt;";
1084  break;
1085  case '"':
1086  Result << "&quot;";
1087  break;
1088  case '\'':
1089  Result << "&apos;";
1090  break;
1091  default:
1092  Result << C;
1093  break;
1094  }
1095  }
1096 }
1097 
1098 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1099  if (S.empty())
1100  return;
1101 
1102  Result << "<![CDATA[";
1103  while (!S.empty()) {
1104  size_t Pos = S.find("]]>");
1105  if (Pos == 0) {
1106  Result << "]]]]><![CDATA[>";
1107  S = S.drop_front(3);
1108  continue;
1109  }
1110  if (Pos == StringRef::npos)
1111  Pos = S.size();
1112 
1113  Result << S.substr(0, Pos);
1114 
1115  S = S.drop_front(Pos);
1116  }
1117  Result << "]]>";
1118 }
1119 
1122 
1124  SmallVectorImpl<char> &HTML,
1125  const ASTContext &Context) {
1126  CommentASTToHTMLConverter Converter(FC, HTML,
1127  Context.getCommentCommandTraits());
1128  Converter.visit(FC);
1129 }
1130 
1133  const ASTContext &Context) {
1134  CommentASTToHTMLConverter Converter(nullptr, Text,
1135  Context.getCommentCommandTraits());
1136  Converter.visit(HTC);
1137 }
1138 
1140  SmallVectorImpl<char> &XML,
1141  const ASTContext &Context) {
1142  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1143  Context.getSourceManager());
1144  Converter.visit(FC);
1145 }
Defines the clang::ASTContext interface.
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:777
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:984
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:843
unsigned getColumnNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Return the column # for the specified file position.
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:357
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:210
StringRef getArgText(unsigned Idx) const
Definition: Comment.h:361
Describes how types, statements, expressions, and declarations should be printed. ...
Definition: PrettyPrinter.h:38
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:852
Information about a single command.
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite)
Apply all replacements in Replaces to the Rewriter Rewrite.
ParagraphComment * getParagraph() const LLVM_READONLY
Definition: Comment.h:690
StringRef getCommandName(const CommandTraits &Traits) const
Definition: Comment.h:655
void print(raw_ostream &Out, unsigned Indentation=0, bool PrintInstantiation=false) const
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:154
child_iterator child_end() const
Definition: Comment.h:1116
RenderKind getRenderKind() const
Definition: Comment.h:353
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:994
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:50
A command with word-like arguments that is considered inline content.
Definition: Comment.h:299
A line of text contained in a verbatim block.
Definition: Comment.h:864
A verbatim line command.
Definition: Comment.h:944
Any part of the comment.
Definition: Comment.h:53
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:786
#define UINT_MAX
Definition: limits.h:72
void convertCommentToHTML(const comments::FullComment *FC, SmallVectorImpl< char > &HTML, const ASTContext &Context)
A verbatim block command (e.
Definition: Comment.h:892
StringRef getText() const LLVM_READONLY
Definition: Comment.h:283
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1065
bool isValid() const
A source range independent of the SourceManager.
Definition: Replacement.h:45
FormatStyle getLLVMStyle()
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:622
unsigned Offset
Definition: Format.cpp:1631
unsigned IsBriefCommand
True if this command is introducing a brief documentation paragraph ( an alias).
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
child_iterator child_begin() const
Definition: Comment.h:1112
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:874
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:597
CommentKind getCommentKind() const
Definition: Comment.h:204
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:750
An opening HTML tag with attributes.
Definition: Comment.h:414
const SourceManager & SM
Definition: Format.cpp:1490
AttrVec & getAttrs()
Definition: DeclBase.h:479
bool hasAttrs() const
Definition: DeclBase.h:473
This class provides information about commands that can be used in comments.
Kind
Encodes a location in the source.
Various functions to configurably format source code.
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:376
void convertHTMLTagNodeToText(const comments::HTMLTagComment *HTC, SmallVectorImpl< char > &Text, const ASTContext &Context)
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:773
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:60
const CommandInfo * getCommandInfo(StringRef Name) const
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:2217
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Given a SourceLocation, return the spelling line number for the position indicated.
Block content (contains inline content).
Definition: Comment.h:531
Comment *const * child_iterator
Definition: Comment.h:224
StringRef getParamNameAsWritten() const
Definition: Comment.h:835
PassDirection getDirection() const LLVM_READONLY
Definition: Comment.h:746
A closing HTML tag.
Definition: Comment.h:508
The FormatStyle is used to configure the formatting to follow specific guidelines.
Definition: Format.h:50
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:800
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Dataflow Directional Tag Classes.
bool FixNamespaceComments
If true, clang-format adds missing namespace end comments and fixes invalid existing ones...
Definition: Format.h:1034
The name of a declaration.
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:981
A single paragraph that contains inline content.
Definition: Comment.h:547
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1082
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class...
Definition: Comment.h:1073
StringRef getText(unsigned LineIdx) const
Definition: Comment.h:936
StringRef getTagName() const LLVM_READONLY
Definition: Comment.h:396
unsigned IsReturnsCommand
True if this command is \returns or an alias.
SourceManager & getSourceManager()
Definition: ASTContext.h:661
Abstract class for opening and closing HTML tags.
Definition: Comment.h:372
const Attribute & getAttr(unsigned Idx) const
Definition: Comment.h:477
StringRef getParamNameAsWritten() const
Definition: Comment.h:765
child_iterator child_end() const
Definition: Comment.h:577
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1078
Doxygen \param command.
Definition: Comment.h:712
const DeclInfo * getDeclInfo() const LLVM_READONLY
Definition: Comment.h:1124
StringRef Text
Definition: Format.cpp:1630
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
This represents a decl that may have a name.
Definition: Decl.h:248
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:364
child_iterator child_begin() const
Definition: Comment.h:573
void convertCommentToXML(const comments::FullComment *FC, SmallVectorImpl< char > &XML, const ASTContext &Context)
const LangOptions & getLangOpts() const
Definition: ASTContext.h:706
This class handles loading and caching of source files into memory.
Attr - This represents one attribute.
Definition: Attr.h:43
SourceLocation getLocation() const
Definition: DeclBase.h:418
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1092
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
const FormatStyle & Style