Coverage Report

Created: 2022-01-25 06:29

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