Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Index/CommentToXML.cpp
35232 views
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
9
#include "clang/Index/CommentToXML.h"
10
#include "clang/AST/ASTContext.h"
11
#include "clang/AST/Attr.h"
12
#include "clang/AST/Comment.h"
13
#include "clang/AST/CommentVisitor.h"
14
#include "clang/Basic/FileManager.h"
15
#include "clang/Basic/IdentifierTable.h"
16
#include "clang/Basic/SourceManager.h"
17
#include "clang/Format/Format.h"
18
#include "clang/Index/USRGeneration.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;
91
SmallVector<const BlockCommandComment *, 4> Returns;
92
SmallVector<const ParamCommandComment *, 8> Params;
93
SmallVector<const TParamCommandComment *, 4> TParams;
94
llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95
SmallVector<const BlockContentComment *, 8> MiscBlocks;
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,
223
SmallVectorImpl<char> &Str,
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,
534
SmallVectorImpl<char> &Str,
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 ParagraphKind,
550
StringRef PrependBodyText);
551
552
void visitBlockCommandComment(const BlockCommandComment *C);
553
void visitParamCommandComment(const ParamCommandComment *C);
554
void visitTParamCommandComment(const TParamCommandComment *C);
555
void visitVerbatimBlockComment(const VerbatimBlockComment *C);
556
void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
557
void visitVerbatimLineComment(const VerbatimLineComment *C);
558
559
void visitFullComment(const FullComment *C);
560
561
// Helpers.
562
void appendToResultWithXMLEscaping(StringRef S);
563
void appendToResultWithCDATAEscaping(StringRef S);
564
565
void formatTextOfDeclaration(const DeclInfo *DI,
566
SmallString<128> &Declaration);
567
568
private:
569
const FullComment *FC;
570
571
/// Output stream for XML.
572
llvm::raw_svector_ostream Result;
573
574
const CommandTraits &Traits;
575
const SourceManager &SM;
576
};
577
578
void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
579
SmallVectorImpl<char> &Str) {
580
ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
581
const LangOptions &LangOpts = Context.getLangOpts();
582
llvm::raw_svector_ostream OS(Str);
583
PrintingPolicy PPolicy(LangOpts);
584
PPolicy.PolishForDeclaration = true;
585
PPolicy.TerseOutput = true;
586
PPolicy.ConstantsAsWritten = true;
587
ThisDecl->CurrentDecl->print(OS, PPolicy,
588
/*Indentation*/0, /*PrintInstantiation*/false);
589
}
590
591
void CommentASTToXMLConverter::formatTextOfDeclaration(
592
const DeclInfo *DI, SmallString<128> &Declaration) {
593
// Formatting API expects null terminated input string.
594
StringRef StringDecl(Declaration.c_str(), Declaration.size());
595
596
// Formatter specific code.
597
unsigned Offset = 0;
598
unsigned Length = Declaration.size();
599
600
format::FormatStyle Style = format::getLLVMStyle();
601
Style.FixNamespaceComments = false;
602
tooling::Replacements Replaces =
603
reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
604
auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
605
if (static_cast<bool>(FormattedStringDecl)) {
606
Declaration = *FormattedStringDecl;
607
}
608
}
609
610
} // end unnamed namespace
611
612
void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
613
appendToResultWithXMLEscaping(C->getText());
614
}
615
616
void CommentASTToXMLConverter::visitInlineCommandComment(
617
const InlineCommandComment *C) {
618
// Nothing to render if no arguments supplied.
619
if (C->getNumArgs() == 0)
620
return;
621
622
// Nothing to render if argument is empty.
623
StringRef Arg0 = C->getArgText(0);
624
if (Arg0.empty())
625
return;
626
627
switch (C->getRenderKind()) {
628
case InlineCommandRenderKind::Normal:
629
for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
630
appendToResultWithXMLEscaping(C->getArgText(i));
631
Result << " ";
632
}
633
return;
634
case InlineCommandRenderKind::Bold:
635
assert(C->getNumArgs() == 1);
636
Result << "<bold>";
637
appendToResultWithXMLEscaping(Arg0);
638
Result << "</bold>";
639
return;
640
case InlineCommandRenderKind::Monospaced:
641
assert(C->getNumArgs() == 1);
642
Result << "<monospaced>";
643
appendToResultWithXMLEscaping(Arg0);
644
Result << "</monospaced>";
645
return;
646
case InlineCommandRenderKind::Emphasized:
647
assert(C->getNumArgs() == 1);
648
Result << "<emphasized>";
649
appendToResultWithXMLEscaping(Arg0);
650
Result << "</emphasized>";
651
return;
652
case InlineCommandRenderKind::Anchor:
653
assert(C->getNumArgs() == 1);
654
Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
655
return;
656
}
657
}
658
659
void CommentASTToXMLConverter::visitHTMLStartTagComment(
660
const HTMLStartTagComment *C) {
661
Result << "<rawHTML";
662
if (C->isMalformed())
663
Result << " isMalformed=\"1\"";
664
Result << ">";
665
{
666
SmallString<32> Tag;
667
{
668
llvm::raw_svector_ostream TagOS(Tag);
669
printHTMLStartTagComment(C, TagOS);
670
}
671
appendToResultWithCDATAEscaping(Tag);
672
}
673
Result << "</rawHTML>";
674
}
675
676
void
677
CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
678
Result << "<rawHTML";
679
if (C->isMalformed())
680
Result << " isMalformed=\"1\"";
681
Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
682
}
683
684
void CommentASTToXMLConverter::visitParagraphComment(
685
const ParagraphComment *C) {
686
appendParagraphCommentWithKind(C, StringRef(), StringRef());
687
}
688
689
void CommentASTToXMLConverter::appendParagraphCommentWithKind(
690
const ParagraphComment *C, StringRef ParagraphKind,
691
StringRef PrependBodyText) {
692
if (C->isWhitespace() && PrependBodyText.empty())
693
return;
694
695
if (ParagraphKind.empty())
696
Result << "<Para>";
697
else
698
Result << "<Para kind=\"" << ParagraphKind << "\">";
699
700
if (!PrependBodyText.empty())
701
Result << PrependBodyText << " ";
702
703
for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); I != E;
704
++I) {
705
visit(*I);
706
}
707
Result << "</Para>";
708
}
709
710
void CommentASTToXMLConverter::visitBlockCommandComment(
711
const BlockCommandComment *C) {
712
StringRef ParagraphKind;
713
StringRef ExceptionType;
714
715
const unsigned CommandID = C->getCommandID();
716
const CommandInfo *Info = Traits.getCommandInfo(CommandID);
717
if (Info->IsThrowsCommand && C->getNumArgs() > 0) {
718
ExceptionType = C->getArgText(0);
719
}
720
721
switch (CommandID) {
722
case CommandTraits::KCI_attention:
723
case CommandTraits::KCI_author:
724
case CommandTraits::KCI_authors:
725
case CommandTraits::KCI_bug:
726
case CommandTraits::KCI_copyright:
727
case CommandTraits::KCI_date:
728
case CommandTraits::KCI_invariant:
729
case CommandTraits::KCI_note:
730
case CommandTraits::KCI_post:
731
case CommandTraits::KCI_pre:
732
case CommandTraits::KCI_remark:
733
case CommandTraits::KCI_remarks:
734
case CommandTraits::KCI_sa:
735
case CommandTraits::KCI_see:
736
case CommandTraits::KCI_since:
737
case CommandTraits::KCI_todo:
738
case CommandTraits::KCI_version:
739
case CommandTraits::KCI_warning:
740
ParagraphKind = C->getCommandName(Traits);
741
break;
742
default:
743
break;
744
}
745
746
appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind,
747
ExceptionType);
748
}
749
750
void CommentASTToXMLConverter::visitParamCommandComment(
751
const ParamCommandComment *C) {
752
Result << "<Parameter><Name>";
753
appendToResultWithXMLEscaping(C->isParamIndexValid()
754
? C->getParamName(FC)
755
: C->getParamNameAsWritten());
756
Result << "</Name>";
757
758
if (C->isParamIndexValid()) {
759
if (C->isVarArgParam())
760
Result << "<IsVarArg />";
761
else
762
Result << "<Index>" << C->getParamIndex() << "</Index>";
763
}
764
765
Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
766
switch (C->getDirection()) {
767
case ParamCommandPassDirection::In:
768
Result << "in";
769
break;
770
case ParamCommandPassDirection::Out:
771
Result << "out";
772
break;
773
case ParamCommandPassDirection::InOut:
774
Result << "in,out";
775
break;
776
}
777
Result << "</Direction><Discussion>";
778
visit(C->getParagraph());
779
Result << "</Discussion></Parameter>";
780
}
781
782
void CommentASTToXMLConverter::visitTParamCommandComment(
783
const TParamCommandComment *C) {
784
Result << "<Parameter><Name>";
785
appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
786
: C->getParamNameAsWritten());
787
Result << "</Name>";
788
789
if (C->isPositionValid() && C->getDepth() == 1) {
790
Result << "<Index>" << C->getIndex(0) << "</Index>";
791
}
792
793
Result << "<Discussion>";
794
visit(C->getParagraph());
795
Result << "</Discussion></Parameter>";
796
}
797
798
void CommentASTToXMLConverter::visitVerbatimBlockComment(
799
const VerbatimBlockComment *C) {
800
unsigned NumLines = C->getNumLines();
801
if (NumLines == 0)
802
return;
803
804
switch (C->getCommandID()) {
805
case CommandTraits::KCI_code:
806
Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
807
break;
808
default:
809
Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
810
break;
811
}
812
for (unsigned i = 0; i != NumLines; ++i) {
813
appendToResultWithXMLEscaping(C->getText(i));
814
if (i + 1 != NumLines)
815
Result << '\n';
816
}
817
Result << "</Verbatim>";
818
}
819
820
void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
821
const VerbatimBlockLineComment *C) {
822
llvm_unreachable("should not see this AST node");
823
}
824
825
void CommentASTToXMLConverter::visitVerbatimLineComment(
826
const VerbatimLineComment *C) {
827
Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
828
appendToResultWithXMLEscaping(C->getText());
829
Result << "</Verbatim>";
830
}
831
832
void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
833
FullCommentParts Parts(C, Traits);
834
835
const DeclInfo *DI = C->getDeclInfo();
836
StringRef RootEndTag;
837
if (DI) {
838
switch (DI->getKind()) {
839
case DeclInfo::OtherKind:
840
RootEndTag = "</Other>";
841
Result << "<Other";
842
break;
843
case DeclInfo::FunctionKind:
844
RootEndTag = "</Function>";
845
Result << "<Function";
846
switch (DI->TemplateKind) {
847
case DeclInfo::NotTemplate:
848
break;
849
case DeclInfo::Template:
850
Result << " templateKind=\"template\"";
851
break;
852
case DeclInfo::TemplateSpecialization:
853
Result << " templateKind=\"specialization\"";
854
break;
855
case DeclInfo::TemplatePartialSpecialization:
856
llvm_unreachable("partial specializations of functions "
857
"are not allowed in C++");
858
}
859
if (DI->IsInstanceMethod)
860
Result << " isInstanceMethod=\"1\"";
861
if (DI->IsClassMethod)
862
Result << " isClassMethod=\"1\"";
863
break;
864
case DeclInfo::ClassKind:
865
RootEndTag = "</Class>";
866
Result << "<Class";
867
switch (DI->TemplateKind) {
868
case DeclInfo::NotTemplate:
869
break;
870
case DeclInfo::Template:
871
Result << " templateKind=\"template\"";
872
break;
873
case DeclInfo::TemplateSpecialization:
874
Result << " templateKind=\"specialization\"";
875
break;
876
case DeclInfo::TemplatePartialSpecialization:
877
Result << " templateKind=\"partialSpecialization\"";
878
break;
879
}
880
break;
881
case DeclInfo::VariableKind:
882
RootEndTag = "</Variable>";
883
Result << "<Variable";
884
break;
885
case DeclInfo::NamespaceKind:
886
RootEndTag = "</Namespace>";
887
Result << "<Namespace";
888
break;
889
case DeclInfo::TypedefKind:
890
RootEndTag = "</Typedef>";
891
Result << "<Typedef";
892
break;
893
case DeclInfo::EnumKind:
894
RootEndTag = "</Enum>";
895
Result << "<Enum";
896
break;
897
}
898
899
{
900
// Print line and column number.
901
SourceLocation Loc = DI->CurrentDecl->getLocation();
902
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
903
FileID FID = LocInfo.first;
904
unsigned FileOffset = LocInfo.second;
905
906
if (FID.isValid()) {
907
if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID)) {
908
Result << " file=\"";
909
appendToResultWithXMLEscaping(FE->getName());
910
Result << "\"";
911
}
912
Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
913
<< "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
914
<< "\"";
915
}
916
}
917
918
// Finish the root tag.
919
Result << ">";
920
921
bool FoundName = false;
922
if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
923
if (DeclarationName DeclName = ND->getDeclName()) {
924
Result << "<Name>";
925
std::string Name = DeclName.getAsString();
926
appendToResultWithXMLEscaping(Name);
927
FoundName = true;
928
Result << "</Name>";
929
}
930
}
931
if (!FoundName)
932
Result << "<Name>&lt;anonymous&gt;</Name>";
933
934
{
935
// Print USR.
936
SmallString<128> USR;
937
generateUSRForDecl(DI->CommentDecl, USR);
938
if (!USR.empty()) {
939
Result << "<USR>";
940
appendToResultWithXMLEscaping(USR);
941
Result << "</USR>";
942
}
943
}
944
} else {
945
// No DeclInfo -- just emit some root tag and name tag.
946
RootEndTag = "</Other>";
947
Result << "<Other><Name>unknown</Name>";
948
}
949
950
if (Parts.Headerfile) {
951
Result << "<Headerfile>";
952
visit(Parts.Headerfile);
953
Result << "</Headerfile>";
954
}
955
956
{
957
// Pretty-print the declaration.
958
Result << "<Declaration>";
959
SmallString<128> Declaration;
960
getSourceTextOfDeclaration(DI, Declaration);
961
formatTextOfDeclaration(DI, Declaration);
962
appendToResultWithXMLEscaping(Declaration);
963
Result << "</Declaration>";
964
}
965
966
bool FirstParagraphIsBrief = false;
967
if (Parts.Brief) {
968
Result << "<Abstract>";
969
visit(Parts.Brief);
970
Result << "</Abstract>";
971
} else if (Parts.FirstParagraph) {
972
Result << "<Abstract>";
973
visit(Parts.FirstParagraph);
974
Result << "</Abstract>";
975
FirstParagraphIsBrief = true;
976
}
977
978
if (Parts.TParams.size() != 0) {
979
Result << "<TemplateParameters>";
980
for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
981
visit(Parts.TParams[i]);
982
Result << "</TemplateParameters>";
983
}
984
985
if (Parts.Params.size() != 0) {
986
Result << "<Parameters>";
987
for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
988
visit(Parts.Params[i]);
989
Result << "</Parameters>";
990
}
991
992
if (Parts.Exceptions.size() != 0) {
993
Result << "<Exceptions>";
994
for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
995
visit(Parts.Exceptions[i]);
996
Result << "</Exceptions>";
997
}
998
999
if (Parts.Returns.size() != 0) {
1000
Result << "<ResultDiscussion>";
1001
for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
1002
visit(Parts.Returns[i]);
1003
Result << "</ResultDiscussion>";
1004
}
1005
1006
if (DI->CommentDecl->hasAttrs()) {
1007
const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1008
for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1009
const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1010
if (!AA) {
1011
if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1012
if (DA->getMessage().empty())
1013
Result << "<Deprecated/>";
1014
else {
1015
Result << "<Deprecated>";
1016
appendToResultWithXMLEscaping(DA->getMessage());
1017
Result << "</Deprecated>";
1018
}
1019
}
1020
else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1021
if (UA->getMessage().empty())
1022
Result << "<Unavailable/>";
1023
else {
1024
Result << "<Unavailable>";
1025
appendToResultWithXMLEscaping(UA->getMessage());
1026
Result << "</Unavailable>";
1027
}
1028
}
1029
continue;
1030
}
1031
1032
// 'availability' attribute.
1033
Result << "<Availability";
1034
StringRef Distribution;
1035
if (AA->getPlatform()) {
1036
Distribution = AvailabilityAttr::getPrettyPlatformName(
1037
AA->getPlatform()->getName());
1038
if (Distribution.empty())
1039
Distribution = AA->getPlatform()->getName();
1040
}
1041
Result << " distribution=\"" << Distribution << "\">";
1042
VersionTuple IntroducedInVersion = AA->getIntroduced();
1043
if (!IntroducedInVersion.empty()) {
1044
Result << "<IntroducedInVersion>"
1045
<< IntroducedInVersion.getAsString()
1046
<< "</IntroducedInVersion>";
1047
}
1048
VersionTuple DeprecatedInVersion = AA->getDeprecated();
1049
if (!DeprecatedInVersion.empty()) {
1050
Result << "<DeprecatedInVersion>"
1051
<< DeprecatedInVersion.getAsString()
1052
<< "</DeprecatedInVersion>";
1053
}
1054
VersionTuple RemovedAfterVersion = AA->getObsoleted();
1055
if (!RemovedAfterVersion.empty()) {
1056
Result << "<RemovedAfterVersion>"
1057
<< RemovedAfterVersion.getAsString()
1058
<< "</RemovedAfterVersion>";
1059
}
1060
StringRef DeprecationSummary = AA->getMessage();
1061
if (!DeprecationSummary.empty()) {
1062
Result << "<DeprecationSummary>";
1063
appendToResultWithXMLEscaping(DeprecationSummary);
1064
Result << "</DeprecationSummary>";
1065
}
1066
if (AA->getUnavailable())
1067
Result << "<Unavailable/>";
1068
1069
IdentifierInfo *Environment = AA->getEnvironment();
1070
if (Environment) {
1071
Result << "<Environment>" << Environment->getName() << "</Environment>";
1072
}
1073
Result << "</Availability>";
1074
}
1075
}
1076
1077
{
1078
bool StartTagEmitted = false;
1079
for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1080
const Comment *C = Parts.MiscBlocks[i];
1081
if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1082
continue;
1083
if (!StartTagEmitted) {
1084
Result << "<Discussion>";
1085
StartTagEmitted = true;
1086
}
1087
visit(C);
1088
}
1089
if (StartTagEmitted)
1090
Result << "</Discussion>";
1091
}
1092
1093
Result << RootEndTag;
1094
}
1095
1096
void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1097
for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1098
const char C = *I;
1099
switch (C) {
1100
case '&':
1101
Result << "&amp;";
1102
break;
1103
case '<':
1104
Result << "&lt;";
1105
break;
1106
case '>':
1107
Result << "&gt;";
1108
break;
1109
case '"':
1110
Result << "&quot;";
1111
break;
1112
case '\'':
1113
Result << "&apos;";
1114
break;
1115
default:
1116
Result << C;
1117
break;
1118
}
1119
}
1120
}
1121
1122
void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1123
if (S.empty())
1124
return;
1125
1126
Result << "<![CDATA[";
1127
while (!S.empty()) {
1128
size_t Pos = S.find("]]>");
1129
if (Pos == 0) {
1130
Result << "]]]]><![CDATA[>";
1131
S = S.drop_front(3);
1132
continue;
1133
}
1134
if (Pos == StringRef::npos)
1135
Pos = S.size();
1136
1137
Result << S.substr(0, Pos);
1138
1139
S = S.drop_front(Pos);
1140
}
1141
Result << "]]>";
1142
}
1143
1144
CommentToXMLConverter::CommentToXMLConverter() {}
1145
CommentToXMLConverter::~CommentToXMLConverter() {}
1146
1147
void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1148
SmallVectorImpl<char> &HTML,
1149
const ASTContext &Context) {
1150
CommentASTToHTMLConverter Converter(FC, HTML,
1151
Context.getCommentCommandTraits());
1152
Converter.visit(FC);
1153
}
1154
1155
void CommentToXMLConverter::convertHTMLTagNodeToText(
1156
const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1157
const ASTContext &Context) {
1158
CommentASTToHTMLConverter Converter(nullptr, Text,
1159
Context.getCommentCommandTraits());
1160
Converter.visit(HTC);
1161
}
1162
1163
void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1164
SmallVectorImpl<char> &XML,
1165
const ASTContext &Context) {
1166
CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1167
Context.getSourceManager());
1168
Converter.visit(FC);
1169
}
1170
1171