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