clang  12.0.0git
CommentToXML.cpp
Go to the documentation of this file.
1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Attr.h"
12 #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()) {
106  case Comment::NoCommentKind:
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  llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
194  llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
195 }
196 
197 void printHTMLStartTagComment(const HTMLStartTagComment *C,
198  llvm::raw_svector_ostream &Result) {
199  Result << "<" << C->getTagName();
200 
201  if (C->getNumAttrs() != 0) {
202  for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
203  Result << " ";
205  Result << Attr.Name;
206  if (!Attr.Value.empty())
207  Result << "=\"" << Attr.Value << "\"";
208  }
209  }
210 
211  if (!C->isSelfClosing())
212  Result << ">";
213  else
214  Result << "/>";
215 }
216 
217 class CommentASTToHTMLConverter :
218  public ConstCommentVisitor<CommentASTToHTMLConverter> {
219 public:
220  /// \param Str accumulator for HTML.
221  CommentASTToHTMLConverter(const FullComment *FC,
223  const CommandTraits &Traits) :
224  FC(FC), Result(Str), Traits(Traits)
225  { }
226 
227  // Inline content.
228  void visitTextComment(const TextComment *C);
229  void visitInlineCommandComment(const InlineCommandComment *C);
230  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
231  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
232 
233  // Block content.
234  void visitParagraphComment(const ParagraphComment *C);
235  void visitBlockCommandComment(const BlockCommandComment *C);
236  void visitParamCommandComment(const ParamCommandComment *C);
237  void visitTParamCommandComment(const TParamCommandComment *C);
238  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
239  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
240  void visitVerbatimLineComment(const VerbatimLineComment *C);
241 
242  void visitFullComment(const FullComment *C);
243 
244  // Helpers.
245 
246  /// Convert a paragraph that is not a block by itself (an argument to some
247  /// command).
248  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
249 
250  void appendToResultWithHTMLEscaping(StringRef S);
251 
252 private:
253  const FullComment *FC;
254  /// Output stream for HTML.
255  llvm::raw_svector_ostream Result;
256 
257  const CommandTraits &Traits;
258 };
259 } // end unnamed namespace
260 
261 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
262  appendToResultWithHTMLEscaping(C->getText());
263 }
264 
265 void CommentASTToHTMLConverter::visitInlineCommandComment(
266  const InlineCommandComment *C) {
267  // Nothing to render if no arguments supplied.
268  if (C->getNumArgs() == 0)
269  return;
270 
271  // Nothing to render if argument is empty.
272  StringRef Arg0 = C->getArgText(0);
273  if (Arg0.empty())
274  return;
275 
276  switch (C->getRenderKind()) {
278  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
279  appendToResultWithHTMLEscaping(C->getArgText(i));
280  Result << " ";
281  }
282  return;
283 
285  assert(C->getNumArgs() == 1);
286  Result << "<b>";
287  appendToResultWithHTMLEscaping(Arg0);
288  Result << "</b>";
289  return;
291  assert(C->getNumArgs() == 1);
292  Result << "<tt>";
293  appendToResultWithHTMLEscaping(Arg0);
294  Result<< "</tt>";
295  return;
297  assert(C->getNumArgs() == 1);
298  Result << "<em>";
299  appendToResultWithHTMLEscaping(Arg0);
300  Result << "</em>";
301  return;
303  assert(C->getNumArgs() == 1);
304  Result << "<span id=\"" << Arg0 << "\"></span>";
305  return;
306  }
307 }
308 
309 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
310  const HTMLStartTagComment *C) {
311  printHTMLStartTagComment(C, Result);
312 }
313 
314 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
315  const HTMLEndTagComment *C) {
316  Result << "</" << C->getTagName() << ">";
317 }
318 
319 void CommentASTToHTMLConverter::visitParagraphComment(
320  const ParagraphComment *C) {
321  if (C->isWhitespace())
322  return;
323 
324  Result << "<p>";
325  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
326  I != E; ++I) {
327  visit(*I);
328  }
329  Result << "</p>";
330 }
331 
332 void CommentASTToHTMLConverter::visitBlockCommandComment(
333  const BlockCommandComment *C) {
334  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
335  if (Info->IsBriefCommand) {
336  Result << "<p class=\"para-brief\">";
337  visitNonStandaloneParagraphComment(C->getParagraph());
338  Result << "</p>";
339  return;
340  }
341  if (Info->IsReturnsCommand) {
342  Result << "<p class=\"para-returns\">"
343  "<span class=\"word-returns\">Returns</span> ";
344  visitNonStandaloneParagraphComment(C->getParagraph());
345  Result << "</p>";
346  return;
347  }
348  // We don't know anything about this command. Just render the paragraph.
349  visit(C->getParagraph());
350 }
351 
352 void CommentASTToHTMLConverter::visitParamCommandComment(
353  const ParamCommandComment *C) {
354  if (C->isParamIndexValid()) {
355  if (C->isVarArgParam()) {
356  Result << "<dt class=\"param-name-index-vararg\">";
357  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
358  } else {
359  Result << "<dt class=\"param-name-index-"
360  << C->getParamIndex()
361  << "\">";
362  appendToResultWithHTMLEscaping(C->getParamName(FC));
363  }
364  } else {
365  Result << "<dt class=\"param-name-index-invalid\">";
366  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
367  }
368  Result << "</dt>";
369 
370  if (C->isParamIndexValid()) {
371  if (C->isVarArgParam())
372  Result << "<dd class=\"param-descr-index-vararg\">";
373  else
374  Result << "<dd class=\"param-descr-index-"
375  << C->getParamIndex()
376  << "\">";
377  } else
378  Result << "<dd class=\"param-descr-index-invalid\">";
379 
380  visitNonStandaloneParagraphComment(C->getParagraph());
381  Result << "</dd>";
382 }
383 
384 void CommentASTToHTMLConverter::visitTParamCommandComment(
385  const TParamCommandComment *C) {
386  if (C->isPositionValid()) {
387  if (C->getDepth() == 1)
388  Result << "<dt class=\"tparam-name-index-"
389  << C->getIndex(0)
390  << "\">";
391  else
392  Result << "<dt class=\"tparam-name-index-other\">";
393  appendToResultWithHTMLEscaping(C->getParamName(FC));
394  } else {
395  Result << "<dt class=\"tparam-name-index-invalid\">";
396  appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
397  }
398 
399  Result << "</dt>";
400 
401  if (C->isPositionValid()) {
402  if (C->getDepth() == 1)
403  Result << "<dd class=\"tparam-descr-index-"
404  << C->getIndex(0)
405  << "\">";
406  else
407  Result << "<dd class=\"tparam-descr-index-other\">";
408  } else
409  Result << "<dd class=\"tparam-descr-index-invalid\">";
410 
411  visitNonStandaloneParagraphComment(C->getParagraph());
412  Result << "</dd>";
413 }
414 
415 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
416  const VerbatimBlockComment *C) {
417  unsigned NumLines = C->getNumLines();
418  if (NumLines == 0)
419  return;
420 
421  Result << "<pre>";
422  for (unsigned i = 0; i != NumLines; ++i) {
423  appendToResultWithHTMLEscaping(C->getText(i));
424  if (i + 1 != NumLines)
425  Result << '\n';
426  }
427  Result << "</pre>";
428 }
429 
430 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
431  const VerbatimBlockLineComment *C) {
432  llvm_unreachable("should not see this AST node");
433 }
434 
435 void CommentASTToHTMLConverter::visitVerbatimLineComment(
436  const VerbatimLineComment *C) {
437  Result << "<pre>";
438  appendToResultWithHTMLEscaping(C->getText());
439  Result << "</pre>";
440 }
441 
442 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
443  FullCommentParts Parts(C, Traits);
444 
445  bool FirstParagraphIsBrief = false;
446  if (Parts.Headerfile)
447  visit(Parts.Headerfile);
448  if (Parts.Brief)
449  visit(Parts.Brief);
450  else if (Parts.FirstParagraph) {
451  Result << "<p class=\"para-brief\">";
452  visitNonStandaloneParagraphComment(Parts.FirstParagraph);
453  Result << "</p>";
454  FirstParagraphIsBrief = true;
455  }
456 
457  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
458  const Comment *C = Parts.MiscBlocks[i];
459  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
460  continue;
461  visit(C);
462  }
463 
464  if (Parts.TParams.size() != 0) {
465  Result << "<dl>";
466  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
467  visit(Parts.TParams[i]);
468  Result << "</dl>";
469  }
470 
471  if (Parts.Params.size() != 0) {
472  Result << "<dl>";
473  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
474  visit(Parts.Params[i]);
475  Result << "</dl>";
476  }
477 
478  if (Parts.Returns.size() != 0) {
479  Result << "<div class=\"result-discussion\">";
480  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
481  visit(Parts.Returns[i]);
482  Result << "</div>";
483  }
484 
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  FC(FC), Result(Str), Traits(Traits), SM(SM) { }
537 
538  // Inline content.
539  void visitTextComment(const TextComment *C);
540  void visitInlineCommandComment(const InlineCommandComment *C);
541  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
542  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543 
544  // Block content.
545  void visitParagraphComment(const ParagraphComment *C);
546 
547  void appendParagraphCommentWithKind(const ParagraphComment *C,
548  StringRef Kind);
549 
550  void visitBlockCommandComment(const BlockCommandComment *C);
551  void visitParamCommandComment(const ParamCommandComment *C);
552  void visitTParamCommandComment(const TParamCommandComment *C);
553  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
554  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
555  void visitVerbatimLineComment(const VerbatimLineComment *C);
556 
557  void visitFullComment(const FullComment *C);
558 
559  // Helpers.
560  void appendToResultWithXMLEscaping(StringRef S);
561  void appendToResultWithCDATAEscaping(StringRef S);
562 
563  void formatTextOfDeclaration(const DeclInfo *DI,
564  SmallString<128> &Declaration);
565 
566 private:
567  const FullComment *FC;
568 
569  /// Output stream for XML.
570  llvm::raw_svector_ostream Result;
571 
572  const CommandTraits &Traits;
573  const SourceManager &SM;
574 };
575 
576 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
577  SmallVectorImpl<char> &Str) {
578  ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
579  const LangOptions &LangOpts = Context.getLangOpts();
580  llvm::raw_svector_ostream OS(Str);
581  PrintingPolicy PPolicy(LangOpts);
582  PPolicy.PolishForDeclaration = true;
583  PPolicy.TerseOutput = true;
584  PPolicy.ConstantsAsWritten = true;
585  ThisDecl->CurrentDecl->print(OS, PPolicy,
586  /*Indentation*/0, /*PrintInstantiation*/false);
587 }
588 
589 void CommentASTToXMLConverter::formatTextOfDeclaration(
590  const DeclInfo *DI, SmallString<128> &Declaration) {
591  // Formatting API expects null terminated input string.
592  StringRef StringDecl(Declaration.c_str(), Declaration.size());
593 
594  // Formatter specific code.
595  unsigned Offset = 0;
596  unsigned Length = Declaration.size();
597 
599  Style.FixNamespaceComments = false;
600  tooling::Replacements Replaces =
601  reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
602  auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
603  if (static_cast<bool>(FormattedStringDecl)) {
604  Declaration = *FormattedStringDecl;
605  }
606 }
607 
608 } // end unnamed namespace
609 
610 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
611  appendToResultWithXMLEscaping(C->getText());
612 }
613 
614 void CommentASTToXMLConverter::visitInlineCommandComment(
615  const InlineCommandComment *C) {
616  // Nothing to render if no arguments supplied.
617  if (C->getNumArgs() == 0)
618  return;
619 
620  // Nothing to render if argument is empty.
621  StringRef Arg0 = C->getArgText(0);
622  if (Arg0.empty())
623  return;
624 
625  switch (C->getRenderKind()) {
626  case InlineCommandComment::RenderNormal:
627  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
628  appendToResultWithXMLEscaping(C->getArgText(i));
629  Result << " ";
630  }
631  return;
632  case InlineCommandComment::RenderBold:
633  assert(C->getNumArgs() == 1);
634  Result << "<bold>";
635  appendToResultWithXMLEscaping(Arg0);
636  Result << "</bold>";
637  return;
638  case InlineCommandComment::RenderMonospaced:
639  assert(C->getNumArgs() == 1);
640  Result << "<monospaced>";
641  appendToResultWithXMLEscaping(Arg0);
642  Result << "</monospaced>";
643  return;
644  case InlineCommandComment::RenderEmphasized:
645  assert(C->getNumArgs() == 1);
646  Result << "<emphasized>";
647  appendToResultWithXMLEscaping(Arg0);
648  Result << "</emphasized>";
649  return;
650  case InlineCommandComment::RenderAnchor:
651  assert(C->getNumArgs() == 1);
652  Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
653  return;
654  }
655 }
656 
657 void CommentASTToXMLConverter::visitHTMLStartTagComment(
658  const HTMLStartTagComment *C) {
659  Result << "<rawHTML";
660  if (C->isMalformed())
661  Result << " isMalformed=\"1\"";
662  Result << ">";
663  {
664  SmallString<32> Tag;
665  {
666  llvm::raw_svector_ostream TagOS(Tag);
667  printHTMLStartTagComment(C, TagOS);
668  }
669  appendToResultWithCDATAEscaping(Tag);
670  }
671  Result << "</rawHTML>";
672 }
673 
674 void
675 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
676  Result << "<rawHTML";
677  if (C->isMalformed())
678  Result << " isMalformed=\"1\"";
679  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
680 }
681 
682 void
683 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
684  appendParagraphCommentWithKind(C, StringRef());
685 }
686 
687 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
688  const ParagraphComment *C,
689  StringRef ParagraphKind) {
690  if (C->isWhitespace())
691  return;
692 
693  if (ParagraphKind.empty())
694  Result << "<Para>";
695  else
696  Result << "<Para kind=\"" << ParagraphKind << "\">";
697 
698  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
699  I != E; ++I) {
700  visit(*I);
701  }
702  Result << "</Para>";
703 }
704 
705 void CommentASTToXMLConverter::visitBlockCommandComment(
706  const BlockCommandComment *C) {
707  StringRef ParagraphKind;
708 
709  switch (C->getCommandID()) {
710  case CommandTraits::KCI_attention:
711  case CommandTraits::KCI_author:
712  case CommandTraits::KCI_authors:
713  case CommandTraits::KCI_bug:
714  case CommandTraits::KCI_copyright:
715  case CommandTraits::KCI_date:
716  case CommandTraits::KCI_invariant:
717  case CommandTraits::KCI_note:
718  case CommandTraits::KCI_post:
719  case CommandTraits::KCI_pre:
720  case CommandTraits::KCI_remark:
721  case CommandTraits::KCI_remarks:
722  case CommandTraits::KCI_sa:
723  case CommandTraits::KCI_see:
724  case CommandTraits::KCI_since:
725  case CommandTraits::KCI_todo:
726  case CommandTraits::KCI_version:
727  case CommandTraits::KCI_warning:
728  ParagraphKind = C->getCommandName(Traits);
729  break;
730  default:
731  break;
732  }
733 
734  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
735 }
736 
737 void CommentASTToXMLConverter::visitParamCommandComment(
738  const ParamCommandComment *C) {
739  Result << "<Parameter><Name>";
740  appendToResultWithXMLEscaping(C->isParamIndexValid()
741  ? C->getParamName(FC)
742  : C->getParamNameAsWritten());
743  Result << "</Name>";
744 
745  if (C->isParamIndexValid()) {
746  if (C->isVarArgParam())
747  Result << "<IsVarArg />";
748  else
749  Result << "<Index>" << C->getParamIndex() << "</Index>";
750  }
751 
752  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
753  switch (C->getDirection()) {
754  case ParamCommandComment::In:
755  Result << "in";
756  break;
757  case ParamCommandComment::Out:
758  Result << "out";
759  break;
760  case ParamCommandComment::InOut:
761  Result << "in,out";
762  break;
763  }
764  Result << "</Direction><Discussion>";
765  visit(C->getParagraph());
766  Result << "</Discussion></Parameter>";
767 }
768 
769 void CommentASTToXMLConverter::visitTParamCommandComment(
770  const TParamCommandComment *C) {
771  Result << "<Parameter><Name>";
772  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
773  : C->getParamNameAsWritten());
774  Result << "</Name>";
775 
776  if (C->isPositionValid() && C->getDepth() == 1) {
777  Result << "<Index>" << C->getIndex(0) << "</Index>";
778  }
779 
780  Result << "<Discussion>";
781  visit(C->getParagraph());
782  Result << "</Discussion></Parameter>";
783 }
784 
785 void CommentASTToXMLConverter::visitVerbatimBlockComment(
786  const VerbatimBlockComment *C) {
787  unsigned NumLines = C->getNumLines();
788  if (NumLines == 0)
789  return;
790 
791  switch (C->getCommandID()) {
792  case CommandTraits::KCI_code:
793  Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
794  break;
795  default:
796  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
797  break;
798  }
799  for (unsigned i = 0; i != NumLines; ++i) {
800  appendToResultWithXMLEscaping(C->getText(i));
801  if (i + 1 != NumLines)
802  Result << '\n';
803  }
804  Result << "</Verbatim>";
805 }
806 
807 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
808  const VerbatimBlockLineComment *C) {
809  llvm_unreachable("should not see this AST node");
810 }
811 
812 void CommentASTToXMLConverter::visitVerbatimLineComment(
813  const VerbatimLineComment *C) {
814  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
815  appendToResultWithXMLEscaping(C->getText());
816  Result << "</Verbatim>";
817 }
818 
819 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
820  FullCommentParts Parts(C, Traits);
821 
822  const DeclInfo *DI = C->getDeclInfo();
823  StringRef RootEndTag;
824  if (DI) {
825  switch (DI->getKind()) {
826  case DeclInfo::OtherKind:
827  RootEndTag = "</Other>";
828  Result << "<Other";
829  break;
830  case DeclInfo::FunctionKind:
831  RootEndTag = "</Function>";
832  Result << "<Function";
833  switch (DI->TemplateKind) {
834  case DeclInfo::NotTemplate:
835  break;
836  case DeclInfo::Template:
837  Result << " templateKind=\"template\"";
838  break;
839  case DeclInfo::TemplateSpecialization:
840  Result << " templateKind=\"specialization\"";
841  break;
842  case DeclInfo::TemplatePartialSpecialization:
843  llvm_unreachable("partial specializations of functions "
844  "are not allowed in C++");
845  }
846  if (DI->IsInstanceMethod)
847  Result << " isInstanceMethod=\"1\"";
848  if (DI->IsClassMethod)
849  Result << " isClassMethod=\"1\"";
850  break;
851  case DeclInfo::ClassKind:
852  RootEndTag = "</Class>";
853  Result << "<Class";
854  switch (DI->TemplateKind) {
855  case DeclInfo::NotTemplate:
856  break;
857  case DeclInfo::Template:
858  Result << " templateKind=\"template\"";
859  break;
860  case DeclInfo::TemplateSpecialization:
861  Result << " templateKind=\"specialization\"";
862  break;
863  case DeclInfo::TemplatePartialSpecialization:
864  Result << " templateKind=\"partialSpecialization\"";
865  break;
866  }
867  break;
868  case DeclInfo::VariableKind:
869  RootEndTag = "</Variable>";
870  Result << "<Variable";
871  break;
872  case DeclInfo::NamespaceKind:
873  RootEndTag = "</Namespace>";
874  Result << "<Namespace";
875  break;
876  case DeclInfo::TypedefKind:
877  RootEndTag = "</Typedef>";
878  Result << "<Typedef";
879  break;
880  case DeclInfo::EnumKind:
881  RootEndTag = "</Enum>";
882  Result << "<Enum";
883  break;
884  }
885 
886  {
887  // Print line and column number.
889  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
890  FileID FID = LocInfo.first;
891  unsigned FileOffset = LocInfo.second;
892 
893  if (FID.isValid()) {
894  if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
895  Result << " file=\"";
896  appendToResultWithXMLEscaping(FE->getName());
897  Result << "\"";
898  }
899  Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
900  << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
901  << "\"";
902  }
903  }
904 
905  // Finish the root tag.
906  Result << ">";
907 
908  bool FoundName = false;
909  if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
910  if (DeclarationName DeclName = ND->getDeclName()) {
911  Result << "<Name>";
912  std::string Name = DeclName.getAsString();
913  appendToResultWithXMLEscaping(Name);
914  FoundName = true;
915  Result << "</Name>";
916  }
917  }
918  if (!FoundName)
919  Result << "<Name>&lt;anonymous&gt;</Name>";
920 
921  {
922  // Print USR.
923  SmallString<128> USR;
925  if (!USR.empty()) {
926  Result << "<USR>";
927  appendToResultWithXMLEscaping(USR);
928  Result << "</USR>";
929  }
930  }
931  } else {
932  // No DeclInfo -- just emit some root tag and name tag.
933  RootEndTag = "</Other>";
934  Result << "<Other><Name>unknown</Name>";
935  }
936 
937  if (Parts.Headerfile) {
938  Result << "<Headerfile>";
939  visit(Parts.Headerfile);
940  Result << "</Headerfile>";
941  }
942 
943  {
944  // Pretty-print the declaration.
945  Result << "<Declaration>";
946  SmallString<128> Declaration;
947  getSourceTextOfDeclaration(DI, Declaration);
948  formatTextOfDeclaration(DI, Declaration);
949  appendToResultWithXMLEscaping(Declaration);
950  Result << "</Declaration>";
951  }
952 
953  bool FirstParagraphIsBrief = false;
954  if (Parts.Brief) {
955  Result << "<Abstract>";
956  visit(Parts.Brief);
957  Result << "</Abstract>";
958  } else if (Parts.FirstParagraph) {
959  Result << "<Abstract>";
960  visit(Parts.FirstParagraph);
961  Result << "</Abstract>";
962  FirstParagraphIsBrief = true;
963  }
964 
965  if (Parts.TParams.size() != 0) {
966  Result << "<TemplateParameters>";
967  for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
968  visit(Parts.TParams[i]);
969  Result << "</TemplateParameters>";
970  }
971 
972  if (Parts.Params.size() != 0) {
973  Result << "<Parameters>";
974  for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
975  visit(Parts.Params[i]);
976  Result << "</Parameters>";
977  }
978 
979  if (Parts.Exceptions.size() != 0) {
980  Result << "<Exceptions>";
981  for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
982  visit(Parts.Exceptions[i]);
983  Result << "</Exceptions>";
984  }
985 
986  if (Parts.Returns.size() != 0) {
987  Result << "<ResultDiscussion>";
988  for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
989  visit(Parts.Returns[i]);
990  Result << "</ResultDiscussion>";
991  }
992 
993  if (DI->CommentDecl->hasAttrs()) {
994  const AttrVec &Attrs = DI->CommentDecl->getAttrs();
995  for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
996  const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
997  if (!AA) {
998  if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
999  if (DA->getMessage().empty())
1000  Result << "<Deprecated/>";
1001  else {
1002  Result << "<Deprecated>";
1003  appendToResultWithXMLEscaping(DA->getMessage());
1004  Result << "</Deprecated>";
1005  }
1006  }
1007  else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1008  if (UA->getMessage().empty())
1009  Result << "<Unavailable/>";
1010  else {
1011  Result << "<Unavailable>";
1012  appendToResultWithXMLEscaping(UA->getMessage());
1013  Result << "</Unavailable>";
1014  }
1015  }
1016  continue;
1017  }
1018 
1019  // 'availability' attribute.
1020  Result << "<Availability";
1021  StringRef Distribution;
1022  if (AA->getPlatform()) {
1023  Distribution = AvailabilityAttr::getPrettyPlatformName(
1024  AA->getPlatform()->getName());
1025  if (Distribution.empty())
1026  Distribution = AA->getPlatform()->getName();
1027  }
1028  Result << " distribution=\"" << Distribution << "\">";
1029  VersionTuple IntroducedInVersion = AA->getIntroduced();
1030  if (!IntroducedInVersion.empty()) {
1031  Result << "<IntroducedInVersion>"
1032  << IntroducedInVersion.getAsString()
1033  << "</IntroducedInVersion>";
1034  }
1035  VersionTuple DeprecatedInVersion = AA->getDeprecated();
1036  if (!DeprecatedInVersion.empty()) {
1037  Result << "<DeprecatedInVersion>"
1038  << DeprecatedInVersion.getAsString()
1039  << "</DeprecatedInVersion>";
1040  }
1041  VersionTuple RemovedAfterVersion = AA->getObsoleted();
1042  if (!RemovedAfterVersion.empty()) {
1043  Result << "<RemovedAfterVersion>"
1044  << RemovedAfterVersion.getAsString()
1045  << "</RemovedAfterVersion>";
1046  }
1047  StringRef DeprecationSummary = AA->getMessage();
1048  if (!DeprecationSummary.empty()) {
1049  Result << "<DeprecationSummary>";
1050  appendToResultWithXMLEscaping(DeprecationSummary);
1051  Result << "</DeprecationSummary>";
1052  }
1053  if (AA->getUnavailable())
1054  Result << "<Unavailable/>";
1055  Result << "</Availability>";
1056  }
1057  }
1058 
1059  {
1060  bool StartTagEmitted = false;
1061  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1062  const Comment *C = Parts.MiscBlocks[i];
1063  if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1064  continue;
1065  if (!StartTagEmitted) {
1066  Result << "<Discussion>";
1067  StartTagEmitted = true;
1068  }
1069  visit(C);
1070  }
1071  if (StartTagEmitted)
1072  Result << "</Discussion>";
1073  }
1074 
1075  Result << RootEndTag;
1076 }
1077 
1078 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1079  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1080  const char C = *I;
1081  switch (C) {
1082  case '&':
1083  Result << "&amp;";
1084  break;
1085  case '<':
1086  Result << "&lt;";
1087  break;
1088  case '>':
1089  Result << "&gt;";
1090  break;
1091  case '"':
1092  Result << "&quot;";
1093  break;
1094  case '\'':
1095  Result << "&apos;";
1096  break;
1097  default:
1098  Result << C;
1099  break;
1100  }
1101  }
1102 }
1103 
1104 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1105  if (S.empty())
1106  return;
1107 
1108  Result << "<![CDATA[";
1109  while (!S.empty()) {
1110  size_t Pos = S.find("]]>");
1111  if (Pos == 0) {
1112  Result << "]]]]><![CDATA[>";
1113  S = S.drop_front(3);
1114  continue;
1115  }
1116  if (Pos == StringRef::npos)
1117  Pos = S.size();
1118 
1119  Result << S.substr(0, Pos);
1120 
1121  S = S.drop_front(Pos);
1122  }
1123  Result << "]]>";
1124 }
1125 
1126 CommentToXMLConverter::CommentToXMLConverter() {}
1127 CommentToXMLConverter::~CommentToXMLConverter() {}
1128 
1129 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1130  SmallVectorImpl<char> &HTML,
1131  const ASTContext &Context) {
1132  CommentASTToHTMLConverter Converter(FC, HTML,
1133  Context.getCommentCommandTraits());
1134  Converter.visit(FC);
1135 }
1136 
1137 void CommentToXMLConverter::convertHTMLTagNodeToText(
1139  const ASTContext &Context) {
1140  CommentASTToHTMLConverter Converter(nullptr, Text,
1141  Context.getCommentCommandTraits());
1142  Converter.visit(HTC);
1143 }
1144 
1145 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1146  SmallVectorImpl<char> &XML,
1147  const ASTContext &Context) {
1148  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1149  Context.getSourceManager());
1150  Converter.visit(FC);
1151 }
Defines the clang::ASTContext interface.
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:776
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:983
Defines the clang::FileManager interface and associated types.
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:842
Defines the SourceManager interface.
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:371
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:209
StringRef getArgText(unsigned Idx) const
Definition: Comment.h:360
Describes how types, statements, expressions, and declarations should be printed. ...
Definition: PrettyPrinter.h:49
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:851
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:689
StringRef getCommandName(const CommandTraits &Traits) const
Definition: Comment.h:654
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:174
child_iterator child_end() const
Definition: Comment.h:1115
RenderKind getRenderKind() const
Definition: Comment.h:352
FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language=FormatStyle::LanguageKind::LK_Cpp)
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:807
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:993
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:54
A command with word-like arguments that is considered inline content.
Definition: Comment.h:297
A line of text contained in a verbatim block.
Definition: Comment.h:863
A verbatim line command.
Definition: Comment.h:943
Any part of the comment.
Definition: Comment.h:52
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:785
#define UINT_MAX
Definition: limits.h:56
A verbatim block command (e.
Definition: Comment.h:891
StringRef getText() const LLVM_READONLY
Definition: Comment.h:281
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1064
bool isValid() const
A source range independent of the SourceManager.
Definition: Replacement.h:44
unsigned Offset
Definition: Format.cpp:2020
unsigned IsBriefCommand
True if this command is introducing a brief documentation paragraph ( an alias).
child_iterator child_begin() const
Definition: Comment.h:1111
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:812
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:596
CommentKind getCommentKind() const
Definition: Comment.h:204
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:749
#define SM(sm)
Definition: Cuda.cpp:62
An opening HTML tag with attributes.
Definition: Comment.h:413
AttrVec & getAttrs()
Definition: DeclBase.h:495
bool hasAttrs() const
Definition: DeclBase.h:489
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:414
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:772
Cached information about one file (either on disk or in the virtual file system). ...
Definition: FileManager.h:79
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:2659
Block content (contains inline content).
Definition: Comment.h:530
Comment *const * child_iterator
Definition: Comment.h:222
StringRef getParamNameAsWritten() const
Definition: Comment.h:834
PassDirection getDirection() const LLVM_READONLY
Definition: Comment.h:745
A closing HTML tag.
Definition: Comment.h:507
The FormatStyle is used to configure the formatting to follow specific guidelines.
Definition: Format.h:54
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:799
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Dataflow Directional Tag Classes.
The name of a declaration.
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:980
A single paragraph that contains inline content.
Definition: Comment.h:546
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1081
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class...
Definition: Comment.h:1072
StringRef getText(unsigned LineIdx) const
Definition: Comment.h:935
StringRef getTagName() const LLVM_READONLY
Definition: Comment.h:395
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
unsigned IsReturnsCommand
True if this command is \returns or an alias.
SourceManager & getSourceManager()
Definition: ASTContext.h:619
Abstract class for opening and closing HTML tags.
Definition: Comment.h:371
const Attribute & getAttr(unsigned Idx) const
Definition: Comment.h:476
StringRef getParamNameAsWritten() const
Definition: Comment.h:764
child_iterator child_end() const
Definition: Comment.h:576
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1077
Doxygen \param command.
Definition: Comment.h:711
const DeclInfo * getDeclInfo() const LLVM_READONLY
Definition: Comment.h:1123
StringRef Text
Definition: Format.cpp:2019
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:223
StringRef getParamName(const FullComment *FC) const
Definition: Comment.cpp:378
child_iterator child_begin() const
Definition: Comment.h:572
const LangOptions & getLangOpts() const
Definition: ASTContext.h:664
This class handles loading and caching of source files into memory.
Attr - This represents one attribute.
Definition: Attr.h:46
SourceLocation getLocation() const
Definition: DeclBase.h:430
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1091
const FormatStyle & Style