Coverage Report

Created: 2020-09-22 08:39

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/AST/CommentParser.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
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/AST/CommentParser.h"
10
#include "clang/AST/CommentCommandTraits.h"
11
#include "clang/AST/CommentDiagnostic.h"
12
#include "clang/AST/CommentSema.h"
13
#include "clang/Basic/CharInfo.h"
14
#include "clang/Basic/SourceManager.h"
15
#include "llvm/Support/ErrorHandling.h"
16
17
namespace clang {
18
19
650
static inline bool isWhitespace(llvm::StringRef S) {
20
1.40k
  for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; 
++I759
) {
21
867
    if (!isWhitespace(*I))
22
108
      return false;
23
867
  }
24
542
  return true;
25
650
}
26
27
namespace comments {
28
29
/// Re-lexes a sequence of tok::text tokens.
30
class TextTokenRetokenizer {
31
  llvm::BumpPtrAllocator &Allocator;
32
  Parser &P;
33
34
  /// This flag is set when there are no more tokens we can fetch from lexer.
35
  bool NoMoreInterestingTokens;
36
37
  /// Token buffer: tokens we have processed and lookahead.
38
  SmallVector<Token, 16> Toks;
39
40
  /// A position in \c Toks.
41
  struct Position {
42
    const char *BufferStart;
43
    const char *BufferEnd;
44
    const char *BufferPtr;
45
    SourceLocation BufferStartLoc;
46
    unsigned CurToken;
47
  };
48
49
  /// Current position in Toks.
50
  Position Pos;
51
52
17.9k
  bool isEnd() const {
53
17.9k
    return Pos.CurToken >= Toks.size();
54
17.9k
  }
55
56
  /// Sets up the buffer pointers to point to current token.
57
814
  void setupBuffer() {
58
814
    assert(!isEnd());
59
814
    const Token &Tok = Toks[Pos.CurToken];
60
61
814
    Pos.BufferStart = Tok.getText().begin();
62
814
    Pos.BufferEnd = Tok.getText().end();
63
814
    Pos.BufferPtr = Pos.BufferStart;
64
814
    Pos.BufferStartLoc = Tok.getLocation();
65
814
  }
66
67
1.90k
  SourceLocation getSourceLocation() const {
68
1.90k
    const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
69
1.90k
    return Pos.BufferStartLoc.getLocWithOffset(CharNo);
70
1.90k
  }
71
72
5.71k
  char peek() const {
73
5.71k
    assert(!isEnd());
74
5.71k
    assert(Pos.BufferPtr != Pos.BufferEnd);
75
5.71k
    return *Pos.BufferPtr;
76
5.71k
  }
77
78
3.36k
  void consumeChar() {
79
3.36k
    assert(!isEnd());
80
3.36k
    assert(Pos.BufferPtr != Pos.BufferEnd);
81
3.36k
    Pos.BufferPtr++;
82
3.36k
    if (Pos.BufferPtr == Pos.BufferEnd) {
83
141
      Pos.CurToken++;
84
141
      if (isEnd() && 
!addToken()140
)
85
85
        return;
86
87
56
      assert(!isEnd());
88
56
      setupBuffer();
89
56
    }
90
3.36k
  }
91
92
  /// Add a token.
93
  /// Returns true on success, false if there are no interesting tokens to
94
  /// fetch from lexer.
95
922
  bool addToken() {
96
922
    if (NoMoreInterestingTokens)
97
3
      return false;
98
99
919
    if (P.Tok.is(tok::newline)) {
100
      // If we see a single newline token between text tokens, skip it.
101
142
      Token Newline = P.Tok;
102
142
      P.consumeToken();
103
142
      if (P.Tok.isNot(tok::text)) {
104
93
        P.putBack(Newline);
105
93
        NoMoreInterestingTokens = true;
106
93
        return false;
107
93
      }
108
826
    }
109
826
    if (P.Tok.isNot(tok::text)) {
110
13
      NoMoreInterestingTokens = true;
111
13
      return false;
112
13
    }
113
114
813
    Toks.push_back(P.Tok);
115
813
    P.consumeToken();
116
813
    if (Toks.size() == 1)
117
758
      setupBuffer();
118
813
    return true;
119
813
  }
120
121
1.25k
  void consumeWhitespace() {
122
2.53k
    while (!isEnd()) {
123
2.52k
      if (isWhitespace(peek()))
124
1.28k
        consumeChar();
125
1.23k
      else
126
1.23k
        break;
127
2.52k
    }
128
1.25k
  }
129
130
  void formTokenWithChars(Token &Result,
131
                          SourceLocation Loc,
132
                          const char *TokBegin,
133
                          unsigned TokLength,
134
1.44k
                          StringRef Text) {
135
1.44k
    Result.setLocation(Loc);
136
1.44k
    Result.setKind(tok::text);
137
1.44k
    Result.setLength(TokLength);
138
1.44k
#ifndef NDEBUG
139
1.44k
    Result.TextPtr = "<UNSET>";
140
1.44k
    Result.IntVal = 7;
141
1.44k
#endif
142
1.44k
    Result.setText(Text);
143
1.44k
  }
144
145
public:
146
  TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
147
782
      Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
148
782
    Pos.CurToken = 0;
149
782
    addToken();
150
782
  }
151
152
  /// Extract a word -- sequence of non-whitespace characters.
153
782
  bool lexWord(Token &Tok) {
154
782
    if (isEnd())
155
24
      return false;
156
157
758
    Position SavedPos = Pos;
158
159
758
    consumeWhitespace();
160
758
    SmallString<32> WordText;
161
758
    const char *WordBegin = Pos.BufferPtr;
162
758
    SourceLocation Loc = getSourceLocation();
163
2.51k
    while (!isEnd()) {
164
2.43k
      const char C = peek();
165
2.43k
      if (!isWhitespace(C)) {
166
1.76k
        WordText.push_back(C);
167
1.76k
        consumeChar();
168
1.76k
      } else
169
676
        break;
170
2.43k
    }
171
758
    const unsigned Length = WordText.size();
172
758
    if (Length == 0) {
173
11
      Pos = SavedPos;
174
11
      return false;
175
11
    }
176
177
747
    char *TextPtr = Allocator.Allocate<char>(Length + 1);
178
179
747
    memcpy(TextPtr, WordText.c_str(), Length + 1);
180
747
    StringRef Text = StringRef(TextPtr, Length);
181
182
747
    formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
183
747
    return true;
184
747
  }
185
186
497
  bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
187
497
    if (isEnd())
188
2
      return false;
189
190
495
    Position SavedPos = Pos;
191
192
495
    consumeWhitespace();
193
495
    SmallString<32> WordText;
194
495
    const char *WordBegin = Pos.BufferPtr;
195
495
    SourceLocation Loc = getSourceLocation();
196
495
    bool Error = false;
197
495
    if (!isEnd()) {
198
492
      const char C = peek();
199
492
      if (C == OpenDelim) {
200
54
        WordText.push_back(C);
201
54
        consumeChar();
202
54
      } else
203
438
        Error = true;
204
492
    }
205
495
    char C = '\0';
206
708
    while (!Error && 
!isEnd()270
) {
207
267
      C = peek();
208
267
      WordText.push_back(C);
209
267
      consumeChar();
210
267
      if (C == CloseDelim)
211
54
        break;
212
267
    }
213
495
    if (!Error && 
C != CloseDelim57
)
214
3
      Error = true;
215
216
495
    if (Error) {
217
441
      Pos = SavedPos;
218
441
      return false;
219
441
    }
220
221
54
    const unsigned Length = WordText.size();
222
54
    char *TextPtr = Allocator.Allocate<char>(Length + 1);
223
224
54
    memcpy(TextPtr, WordText.c_str(), Length + 1);
225
54
    StringRef Text = StringRef(TextPtr, Length);
226
227
54
    formTokenWithChars(Tok, Loc, WordBegin,
228
54
                       Pos.BufferPtr - WordBegin, Text);
229
54
    return true;
230
54
  }
231
232
  /// Put back tokens that we didn't consume.
233
782
  void putBackLeftoverTokens() {
234
782
    if (isEnd())
235
95
      return;
236
237
687
    bool HavePartialTok = false;
238
687
    Token PartialTok;
239
687
    if (Pos.BufferPtr != Pos.BufferStart) {
240
647
      formTokenWithChars(PartialTok, getSourceLocation(),
241
647
                         Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
242
647
                         StringRef(Pos.BufferPtr,
243
647
                                   Pos.BufferEnd - Pos.BufferPtr));
244
647
      HavePartialTok = true;
245
647
      Pos.CurToken++;
246
647
    }
247
248
687
    P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
249
687
    Pos.CurToken = Toks.size();
250
251
687
    if (HavePartialTok)
252
647
      P.putBack(PartialTok);
253
687
  }
254
};
255
256
Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
257
               const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
258
               const CommandTraits &Traits):
259
    L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
260
1.68k
    Traits(Traits) {
261
1.68k
  consumeToken();
262
1.68k
}
263
264
void Parser::parseParamCommandArgs(ParamCommandComment *PC,
265
497
                                   TextTokenRetokenizer &Retokenizer) {
266
497
  Token Arg;
267
  // Check if argument looks like direction specification: [dir]
268
  // e.g., [in], [out], [in,out]
269
497
  if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
270
54
    S.actOnParamCommandDirectionArg(PC,
271
54
                                    Arg.getLocation(),
272
54
                                    Arg.getEndLocation(),
273
54
                                    Arg.getText());
274
275
497
  if (Retokenizer.lexWord(Arg))
276
492
    S.actOnParamCommandParamNameArg(PC,
277
492
                                    Arg.getLocation(),
278
492
                                    Arg.getEndLocation(),
279
492
                                    Arg.getText());
280
497
}
281
282
void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
283
201
                                    TextTokenRetokenizer &Retokenizer) {
284
201
  Token Arg;
285
201
  if (Retokenizer.lexWord(Arg))
286
194
    S.actOnTParamCommandParamNameArg(TPC,
287
194
                                     Arg.getLocation(),
288
194
                                     Arg.getEndLocation(),
289
194
                                     Arg.getText());
290
201
}
291
292
void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
293
                                   TextTokenRetokenizer &Retokenizer,
294
0
                                   unsigned NumArgs) {
295
0
  typedef BlockCommandComment::Argument Argument;
296
0
  Argument *Args =
297
0
      new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
298
0
  unsigned ParsedArgs = 0;
299
0
  Token Arg;
300
0
  while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
301
0
    Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
302
0
                                            Arg.getEndLocation()),
303
0
                                Arg.getText());
304
0
    ParsedArgs++;
305
0
  }
306
307
0
  S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
308
0
}
309
310
1.70k
BlockCommandComment *Parser::parseBlockCommand() {
311
1.70k
  assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
312
313
1.70k
  ParamCommandComment *PC = nullptr;
314
1.70k
  TParamCommandComment *TPC = nullptr;
315
1.70k
  BlockCommandComment *BC = nullptr;
316
1.70k
  const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
317
1.70k
  CommandMarkerKind CommandMarker =
318
1.57k
      Tok.is(tok::backslash_command) ? CMK_Backslash : 
CMK_At136
;
319
1.70k
  if (Info->IsParamCommand) {
320
501
    PC = S.actOnParamCommandStart(Tok.getLocation(),
321
501
                                  Tok.getEndLocation(),
322
501
                                  Tok.getCommandID(),
323
501
                                  CommandMarker);
324
1.20k
  } else if (Info->IsTParamCommand) {
325
202
    TPC = S.actOnTParamCommandStart(Tok.getLocation(),
326
202
                                    Tok.getEndLocation(),
327
202
                                    Tok.getCommandID(),
328
202
                                    CommandMarker);
329
1.00k
  } else {
330
1.00k
    BC = S.actOnBlockCommandStart(Tok.getLocation(),
331
1.00k
                                  Tok.getEndLocation(),
332
1.00k
                                  Tok.getCommandID(),
333
1.00k
                                  CommandMarker);
334
1.00k
  }
335
1.70k
  consumeToken();
336
337
1.70k
  if (isTokBlockCommand()) {
338
    // Block command ahead.  We can't nest block commands, so pretend that this
339
    // command has an empty argument.
340
177
    ParagraphComment *Paragraph = S.actOnParagraphComment(None);
341
177
    if (PC) {
342
4
      S.actOnParamCommandFinish(PC, Paragraph);
343
4
      return PC;
344
173
    } else if (TPC) {
345
1
      S.actOnTParamCommandFinish(TPC, Paragraph);
346
1
      return TPC;
347
172
    } else {
348
172
      S.actOnBlockCommandFinish(BC, Paragraph);
349
172
      return BC;
350
172
    }
351
1.53k
  }
352
353
1.53k
  if (PC || 
TPC1.03k
||
Info->NumArgs > 0832
) {
354
    // In order to parse command arguments we need to retokenize a few
355
    // following text tokens.
356
698
    TextTokenRetokenizer Retokenizer(Allocator, *this);
357
358
698
    if (PC)
359
497
      parseParamCommandArgs(PC, Retokenizer);
360
201
    else if (TPC)
361
201
      parseTParamCommandArgs(TPC, Retokenizer);
362
0
    else
363
0
      parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
364
365
698
    Retokenizer.putBackLeftoverTokens();
366
698
  }
367
368
  // If there's a block command ahead, we will attach an empty paragraph to
369
  // this command.
370
1.53k
  bool EmptyParagraph = false;
371
1.53k
  if (isTokBlockCommand())
372
3
    EmptyParagraph = true;
373
1.52k
  else if (Tok.is(tok::newline)) {
374
135
    Token PrevTok = Tok;
375
135
    consumeToken();
376
135
    EmptyParagraph = isTokBlockCommand();
377
135
    putBack(PrevTok);
378
135
  }
379
380
1.53k
  ParagraphComment *Paragraph;
381
1.53k
  if (EmptyParagraph)
382
12
    Paragraph = S.actOnParagraphComment(None);
383
1.51k
  else {
384
1.51k
    BlockContentComment *Block = parseParagraphOrBlockCommand();
385
    // Since we have checked for a block command, we should have parsed a
386
    // paragraph.
387
1.51k
    Paragraph = cast<ParagraphComment>(Block);
388
1.51k
  }
389
390
1.53k
  if (PC) {
391
497
    S.actOnParamCommandFinish(PC, Paragraph);
392
497
    return PC;
393
1.03k
  } else if (TPC) {
394
201
    S.actOnTParamCommandFinish(TPC, Paragraph);
395
201
    return TPC;
396
832
  } else {
397
832
    S.actOnBlockCommandFinish(BC, Paragraph);
398
832
    return BC;
399
832
  }
400
1.53k
}
401
402
84
InlineCommandComment *Parser::parseInlineCommand() {
403
84
  assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
404
405
84
  const Token CommandTok = Tok;
406
84
  consumeToken();
407
408
84
  TextTokenRetokenizer Retokenizer(Allocator, *this);
409
410
84
  Token ArgTok;
411
84
  bool ArgTokValid = Retokenizer.lexWord(ArgTok);
412
413
84
  InlineCommandComment *IC;
414
84
  if (ArgTokValid) {
415
61
    IC = S.actOnInlineCommand(CommandTok.getLocation(),
416
61
                              CommandTok.getEndLocation(),
417
61
                              CommandTok.getCommandID(),
418
61
                              ArgTok.getLocation(),
419
61
                              ArgTok.getEndLocation(),
420
61
                              ArgTok.getText());
421
23
  } else {
422
23
    IC = S.actOnInlineCommand(CommandTok.getLocation(),
423
23
                              CommandTok.getEndLocation(),
424
23
                              CommandTok.getCommandID());
425
426
23
    Diag(CommandTok.getEndLocation().getLocWithOffset(1),
427
23
         diag::warn_doc_inline_contents_no_argument)
428
23
        << CommandTok.is(tok::at_command)
429
23
        << Traits.getCommandInfo(CommandTok.getCommandID())->Name
430
23
        << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
431
23
  }
432
433
84
  Retokenizer.putBackLeftoverTokens();
434
435
84
  return IC;
436
84
}
437
438
123
HTMLStartTagComment *Parser::parseHTMLStartTag() {
439
123
  assert(Tok.is(tok::html_start_tag));
440
123
  HTMLStartTagComment *HST =
441
123
      S.actOnHTMLStartTagStart(Tok.getLocation(),
442
123
                               Tok.getHTMLTagStartName());
443
123
  consumeToken();
444
445
123
  SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
446
178
  while (true) {
447
178
    switch (Tok.getKind()) {
448
49
    case tok::html_ident: {
449
49
      Token Ident = Tok;
450
49
      consumeToken();
451
49
      if (Tok.isNot(tok::html_equals)) {
452
19
        Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
453
19
                                                       Ident.getHTMLIdent()));
454
19
        continue;
455
19
      }
456
30
      Token Equals = Tok;
457
30
      consumeToken();
458
30
      if (Tok.isNot(tok::html_quoted_string)) {
459
9
        Diag(Tok.getLocation(),
460
9
             diag::warn_doc_html_start_tag_expected_quoted_string)
461
9
          << SourceRange(Equals.getLocation());
462
9
        Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
463
9
                                                       Ident.getHTMLIdent()));
464
12
        while (Tok.is(tok::html_equals) ||
465
9
               Tok.is(tok::html_quoted_string))
466
3
          consumeToken();
467
9
        continue;
468
9
      }
469
21
      Attrs.push_back(HTMLStartTagComment::Attribute(
470
21
                              Ident.getLocation(),
471
21
                              Ident.getHTMLIdent(),
472
21
                              Equals.getLocation(),
473
21
                              SourceRange(Tok.getLocation(),
474
21
                                          Tok.getEndLocation()),
475
21
                              Tok.getHTMLQuotedString()));
476
21
      consumeToken();
477
21
      continue;
478
21
    }
479
480
89
    case tok::html_greater:
481
89
      S.actOnHTMLStartTagFinish(HST,
482
89
                                S.copyArray(llvm::makeArrayRef(Attrs)),
483
89
                                Tok.getLocation(),
484
89
                                /* IsSelfClosing = */ false);
485
89
      consumeToken();
486
89
      return HST;
487
488
12
    case tok::html_slash_greater:
489
12
      S.actOnHTMLStartTagFinish(HST,
490
12
                                S.copyArray(llvm::makeArrayRef(Attrs)),
491
12
                                Tok.getLocation(),
492
12
                                /* IsSelfClosing = */ true);
493
12
      consumeToken();
494
12
      return HST;
495
496
9
    case tok::html_equals:
497
9
    case tok::html_quoted_string:
498
9
      Diag(Tok.getLocation(),
499
9
           diag::warn_doc_html_start_tag_expected_ident_or_greater);
500
18
      while (Tok.is(tok::html_equals) ||
501
12
             Tok.is(tok::html_quoted_string))
502
9
        consumeToken();
503
9
      if (Tok.is(tok::html_ident) ||
504
9
          Tok.is(tok::html_greater) ||
505
3
          Tok.is(tok::html_slash_greater))
506
6
        continue;
507
508
3
      S.actOnHTMLStartTagFinish(HST,
509
3
                                S.copyArray(llvm::makeArrayRef(Attrs)),
510
3
                                SourceLocation(),
511
3
                                /* IsSelfClosing = */ false);
512
3
      return HST;
513
514
19
    default:
515
      // Not a token from an HTML start tag.  Thus HTML tag prematurely ended.
516
19
      S.actOnHTMLStartTagFinish(HST,
517
19
                                S.copyArray(llvm::makeArrayRef(Attrs)),
518
19
                                SourceLocation(),
519
19
                                /* IsSelfClosing = */ false);
520
19
      bool StartLineInvalid;
521
19
      const unsigned StartLine = SourceMgr.getPresumedLineNumber(
522
19
                                                  HST->getLocation(),
523
19
                                                  &StartLineInvalid);
524
19
      bool EndLineInvalid;
525
19
      const unsigned EndLine = SourceMgr.getPresumedLineNumber(
526
19
                                                  Tok.getLocation(),
527
19
                                                  &EndLineInvalid);
528
19
      if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
529
16
        Diag(Tok.getLocation(),
530
16
             diag::warn_doc_html_start_tag_expected_ident_or_greater)
531
16
          << HST->getSourceRange();
532
3
      else {
533
3
        Diag(Tok.getLocation(),
534
3
             diag::warn_doc_html_start_tag_expected_ident_or_greater);
535
3
        Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
536
3
          << HST->getSourceRange();
537
3
      }
538
19
      return HST;
539
178
    }
540
178
  }
541
123
}
542
543
64
HTMLEndTagComment *Parser::parseHTMLEndTag() {
544
64
  assert(Tok.is(tok::html_end_tag));
545
64
  Token TokEndTag = Tok;
546
64
  consumeToken();
547
64
  SourceLocation Loc;
548
64
  if (Tok.is(tok::html_greater)) {
549
63
    Loc = Tok.getLocation();
550
63
    consumeToken();
551
63
  }
552
553
64
  return S.actOnHTMLEndTag(TokEndTag.getLocation(),
554
64
                           Loc,
555
64
                           TokEndTag.getHTMLTagEndName());
556
64
}
557
558
5.10k
BlockContentComment *Parser::parseParagraphOrBlockCommand() {
559
5.10k
  SmallVector<InlineContentComment *, 8> Content;
560
561
10.2k
  while (true) {
562
10.2k
    switch (Tok.getKind()) {
563
127
    case tok::verbatim_block_begin:
564
127
    case tok::verbatim_line_name:
565
127
    case tok::eof:
566
127
      break; // Block content or EOF ahead, finish this parapgaph.
567
568
153
    case tok::unknown_command:
569
153
      Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
570
153
                                              Tok.getEndLocation(),
571
153
                                              Tok.getUnknownCommandName()));
572
153
      consumeToken();
573
153
      continue;
574
575
3.30k
    case tok::backslash_command:
576
3.30k
    case tok::at_command: {
577
3.30k
      const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
578
3.30k
      if (Info->IsBlockCommand) {
579
3.20k
        if (Content.size() == 0)
580
1.70k
          return parseBlockCommand();
581
1.49k
        break; // Block command ahead, finish this parapgaph.
582
1.49k
      }
583
99
      if (Info->IsVerbatimBlockEndCommand) {
584
11
        Diag(Tok.getLocation(),
585
11
             diag::warn_verbatim_block_end_without_start)
586
11
          << Tok.is(tok::at_command)
587
11
          << Info->Name
588
11
          << SourceRange(Tok.getLocation(), Tok.getEndLocation());
589
11
        consumeToken();
590
11
        continue;
591
11
      }
592
88
      if (Info->IsUnknownCommand) {
593
4
        Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
594
4
                                                Tok.getEndLocation(),
595
4
                                                Info->getID()));
596
4
        consumeToken();
597
4
        continue;
598
4
      }
599
84
      assert(Info->IsInlineCommand);
600
84
      Content.push_back(parseInlineCommand());
601
84
      continue;
602
84
    }
603
604
2.47k
    case tok::newline: {
605
2.47k
      consumeToken();
606
2.47k
      if (Tok.is(tok::newline) || 
Tok.is(tok::eof)2.05k
) {
607
1.67k
        consumeToken();
608
1.67k
        break; // Two newlines -- end of paragraph.
609
1.67k
      }
610
      // Also allow [tok::newline, tok::text, tok::newline] if the middle
611
      // tok::text is just whitespace.
612
799
      if (Tok.is(tok::text) && 
isWhitespace(Tok.getText())650
) {
613
542
        Token WhitespaceTok = Tok;
614
542
        consumeToken();
615
542
        if (Tok.is(tok::newline) || 
Tok.is(tok::eof)440
) {
616
102
          consumeToken();
617
102
          break;
618
102
        }
619
        // We have [tok::newline, tok::text, non-newline].  Put back tok::text.
620
440
        putBack(WhitespaceTok);
621
440
      }
622
697
      if (Content.size() > 0)
623
680
        Content.back()->addTrailingNewline();
624
697
      continue;
625
799
    }
626
627
    // Don't deal with HTML tag soup now.
628
123
    case tok::html_start_tag:
629
123
      Content.push_back(parseHTMLStartTag());
630
123
      continue;
631
632
64
    case tok::html_end_tag:
633
64
      Content.push_back(parseHTMLEndTag());
634
64
      continue;
635
636
4.03k
    case tok::text:
637
4.03k
      Content.push_back(S.actOnText(Tok.getLocation(),
638
4.03k
                                    Tok.getEndLocation(),
639
4.03k
                                    Tok.getText()));
640
4.03k
      consumeToken();
641
4.03k
      continue;
642
643
0
    case tok::verbatim_block_line:
644
0
    case tok::verbatim_block_end:
645
0
    case tok::verbatim_line_text:
646
0
    case tok::html_ident:
647
0
    case tok::html_equals:
648
0
    case tok::html_quoted_string:
649
0
    case tok::html_greater:
650
0
    case tok::html_slash_greater:
651
0
      llvm_unreachable("should not see this token");
652
3.39k
    }
653
3.39k
    break;
654
3.39k
  }
655
656
3.39k
  return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
657
5.10k
}
658
659
37
VerbatimBlockComment *Parser::parseVerbatimBlock() {
660
37
  assert(Tok.is(tok::verbatim_block_begin));
661
662
37
  VerbatimBlockComment *VB =
663
37
      S.actOnVerbatimBlockStart(Tok.getLocation(),
664
37
                                Tok.getVerbatimBlockID());
665
37
  consumeToken();
666
667
  // Don't create an empty line if verbatim opening command is followed
668
  // by a newline.
669
37
  if (Tok.is(tok::newline))
670
13
    consumeToken();
671
672
37
  SmallVector<VerbatimBlockLineComment *, 8> Lines;
673
94
  while (Tok.is(tok::verbatim_block_line) ||
674
57
         
Tok.is(tok::newline)44
) {
675
57
    VerbatimBlockLineComment *Line;
676
57
    if (Tok.is(tok::verbatim_block_line)) {
677
50
      Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
678
50
                                      Tok.getVerbatimBlockText());
679
50
      consumeToken();
680
50
      if (Tok.is(tok::newline)) {
681
23
        consumeToken();
682
23
      }
683
7
    } else {
684
      // Empty line, just a tok::newline.
685
7
      Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
686
7
      consumeToken();
687
7
    }
688
57
    Lines.push_back(Line);
689
57
  }
690
691
37
  if (Tok.is(tok::verbatim_block_end)) {
692
30
    const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
693
30
    S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
694
30
                               Info->Name,
695
30
                               S.copyArray(llvm::makeArrayRef(Lines)));
696
30
    consumeToken();
697
7
  } else {
698
    // Unterminated \\verbatim block
699
7
    S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
700
7
                               S.copyArray(llvm::makeArrayRef(Lines)));
701
7
  }
702
703
37
  return VB;
704
37
}
705
706
104
VerbatimLineComment *Parser::parseVerbatimLine() {
707
104
  assert(Tok.is(tok::verbatim_line_name));
708
709
104
  Token NameTok = Tok;
710
104
  consumeToken();
711
712
104
  SourceLocation TextBegin;
713
104
  StringRef Text;
714
  // Next token might not be a tok::verbatim_line_text if verbatim line
715
  // starting command comes just before a newline or comment end.
716
104
  if (Tok.is(tok::verbatim_line_text)) {
717
102
    TextBegin = Tok.getLocation();
718
102
    Text = Tok.getVerbatimLineText();
719
2
  } else {
720
2
    TextBegin = NameTok.getEndLocation();
721
2
    Text = "";
722
2
  }
723
724
104
  VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
725
104
                                                NameTok.getVerbatimLineID(),
726
104
                                                TextBegin,
727
104
                                                Text);
728
104
  consumeToken();
729
104
  return VL;
730
104
}
731
732
3.72k
BlockContentComment *Parser::parseBlockContent() {
733
3.72k
  switch (Tok.getKind()) {
734
3.58k
  case tok::text:
735
3.58k
  case tok::unknown_command:
736
3.58k
  case tok::backslash_command:
737
3.58k
  case tok::at_command:
738
3.58k
  case tok::html_start_tag:
739
3.58k
  case tok::html_end_tag:
740
3.58k
    return parseParagraphOrBlockCommand();
741
742
37
  case tok::verbatim_block_begin:
743
37
    return parseVerbatimBlock();
744
745
104
  case tok::verbatim_line_name:
746
104
    return parseVerbatimLine();
747
748
0
  case tok::eof:
749
0
  case tok::newline:
750
0
  case tok::verbatim_block_line:
751
0
  case tok::verbatim_block_end:
752
0
  case tok::verbatim_line_text:
753
0
  case tok::html_ident:
754
0
  case tok::html_equals:
755
0
  case tok::html_quoted_string:
756
0
  case tok::html_greater:
757
0
  case tok::html_slash_greater:
758
0
    llvm_unreachable("should not see this token");
759
0
  }
760
0
  llvm_unreachable("bogus token kind");
761
0
}
762
763
1.68k
FullComment *Parser::parseFullComment() {
764
  // Skip newlines at the beginning of the comment.
765
1.99k
  while (Tok.is(tok::newline))
766
315
    consumeToken();
767
768
1.68k
  SmallVector<BlockContentComment *, 8> Blocks;
769
5.40k
  while (Tok.isNot(tok::eof)) {
770
3.72k
    Blocks.push_back(parseBlockContent());
771
772
    // Skip extra newlines after paragraph end.
773
4.23k
    while (Tok.is(tok::newline))
774
509
      consumeToken();
775
3.72k
  }
776
1.68k
  return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
777
1.68k
}
778
779
} // end namespace comments
780
} // end namespace clang