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