Coverage Report

Created: 2020-02-15 09:57

/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
617
static inline bool isWhitespace(llvm::StringRef S) {
20
1.34k
  for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; 
++I724
) {
21
826
    if (!isWhitespace(*I))
22
102
      return false;
23
826
  }
24
617
  
return true515
;
25
617
}
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
16.9k
  bool isEnd() const {
53
16.9k
    return Pos.CurToken >= Toks.size();
54
16.9k
  }
55
56
  /// Sets up the buffer pointers to point to current token.
57
776
  void setupBuffer() {
58
776
    assert(!isEnd());
59
776
    const Token &Tok = Toks[Pos.CurToken];
60
776
61
776
    Pos.BufferStart = Tok.getText().begin();
62
776
    Pos.BufferEnd = Tok.getText().end();
63
776
    Pos.BufferPtr = Pos.BufferStart;
64
776
    Pos.BufferStartLoc = Tok.getLocation();
65
776
  }
66
67
1.80k
  SourceLocation getSourceLocation() const {
68
1.80k
    const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
69
1.80k
    return Pos.BufferStartLoc.getLocWithOffset(CharNo);
70
1.80k
  }
71
72
5.39k
  char peek() const {
73
5.39k
    assert(!isEnd());
74
5.39k
    assert(Pos.BufferPtr != Pos.BufferEnd);
75
5.39k
    return *Pos.BufferPtr;
76
5.39k
  }
77
78
3.15k
  void consumeChar() {
79
3.15k
    assert(!isEnd());
80
3.15k
    assert(Pos.BufferPtr != Pos.BufferEnd);
81
3.15k
    Pos.BufferPtr++;
82
3.15k
    if (Pos.BufferPtr == Pos.BufferEnd) {
83
135
      Pos.CurToken++;
84
135
      if (isEnd() && 
!addToken()134
)
85
81
        return;
86
54
87
54
      assert(!isEnd());
88
54
      setupBuffer();
89
54
    }
90
3.15k
  }
91
92
  /// Add a token.
93
  /// Returns true on success, false if there are no interesting tokens to
94
  /// fetch from lexer.
95
880
  bool addToken() {
96
880
    if (NoMoreInterestingTokens)
97
3
      return false;
98
877
99
877
    if (P.Tok.is(tok::newline)) {
100
136
      // If we see a single newline token between text tokens, skip it.
101
136
      Token Newline = P.Tok;
102
136
      P.consumeToken();
103
136
      if (P.Tok.isNot(tok::text)) {
104
89
        P.putBack(Newline);
105
89
        NoMoreInterestingTokens = true;
106
89
        return false;
107
89
      }
108
788
    }
109
788
    if (P.Tok.isNot(tok::text)) {
110
13
      NoMoreInterestingTokens = true;
111
13
      return false;
112
13
    }
113
775
114
775
    Toks.push_back(P.Tok);
115
775
    P.consumeToken();
116
775
    if (Toks.size() == 1)
117
722
      setupBuffer();
118
775
    return true;
119
775
  }
120
121
1.19k
  void consumeWhitespace() {
122
2.41k
    while (!isEnd()) {
123
2.40k
      if (isWhitespace(peek()))
124
1.22k
        consumeChar();
125
1.17k
      else
126
1.17k
        break;
127
2.40k
    }
128
1.19k
  }
129
130
  void formTokenWithChars(Token &Result,
131
                          SourceLocation Loc,
132
                          const char *TokBegin,
133
                          unsigned TokLength,
134
1.37k
                          StringRef Text) {
135
1.37k
    Result.setLocation(Loc);
136
1.37k
    Result.setKind(tok::text);
137
1.37k
    Result.setLength(TokLength);
138
1.37k
#ifndef NDEBUG
139
1.37k
    Result.TextPtr = "<UNSET>";
140
1.37k
    Result.IntVal = 7;
141
1.37k
#endif
142
1.37k
    Result.setText(Text);
143
1.37k
  }
144
145
public:
146
  TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
147
746
      Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
148
746
    Pos.CurToken = 0;
149
746
    addToken();
150
746
  }
151
152
  /// Extract a word -- sequence of non-whitespace characters.
153
746
  bool lexWord(Token &Tok) {
154
746
    if (isEnd())
155
24
      return false;
156
722
157
722
    Position SavedPos = Pos;
158
722
159
722
    consumeWhitespace();
160
722
    SmallString<32> WordText;
161
722
    const char *WordBegin = Pos.BufferPtr;
162
722
    SourceLocation Loc = getSourceLocation();
163
2.36k
    while (!isEnd()) {
164
2.28k
      const char C = peek();
165
2.28k
      if (!isWhitespace(C)) {
166
1.64k
        WordText.push_back(C);
167
1.64k
        consumeChar();
168
1.64k
      } else
169
644
        break;
170
2.28k
    }
171
722
    const unsigned Length = WordText.size();
172
722
    if (Length == 0) {
173
11
      Pos = SavedPos;
174
11
      return false;
175
11
    }
176
711
177
711
    char *TextPtr = Allocator.Allocate<char>(Length + 1);
178
711
179
711
    memcpy(TextPtr, WordText.c_str(), Length + 1);
180
711
    StringRef Text = StringRef(TextPtr, Length);
181
711
182
711
    formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
183
711
    return true;
184
711
  }
185
186
471
  bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
187
471
    if (isEnd())
188
2
      return false;
189
469
190
469
    Position SavedPos = Pos;
191
469
192
469
    consumeWhitespace();
193
469
    SmallString<32> WordText;
194
469
    const char *WordBegin = Pos.BufferPtr;
195
469
    SourceLocation Loc = getSourceLocation();
196
469
    bool Error = false;
197
469
    if (!isEnd()) {
198
466
      const char C = peek();
199
466
      if (C == OpenDelim) {
200
48
        WordText.push_back(C);
201
48
        consumeChar();
202
48
      } else
203
418
        Error = true;
204
466
    }
205
469
    char C = '\0';
206
662
    while (!Error && 
!isEnd()244
) {
207
241
      C = peek();
208
241
      WordText.push_back(C);
209
241
      consumeChar();
210
241
      if (C == CloseDelim)
211
48
        break;
212
241
    }
213
469
    if (!Error && 
C != CloseDelim51
)
214
3
      Error = true;
215
469
216
469
    if (Error) {
217
421
      Pos = SavedPos;
218
421
      return false;
219
421
    }
220
48
221
48
    const unsigned Length = WordText.size();
222
48
    char *TextPtr = Allocator.Allocate<char>(Length + 1);
223
48
224
48
    memcpy(TextPtr, WordText.c_str(), Length + 1);
225
48
    StringRef Text = StringRef(TextPtr, Length);
226
48
227
48
    formTokenWithChars(Tok, Loc, WordBegin,
228
48
                       Pos.BufferPtr - WordBegin, Text);
229
48
    return true;
230
48
  }
231
232
  /// Put back tokens that we didn't consume.
233
746
  void putBackLeftoverTokens() {
234
746
    if (isEnd())
235
91
      return;
236
655
237
655
    bool HavePartialTok = false;
238
655
    Token PartialTok;
239
655
    if (Pos.BufferPtr != Pos.BufferStart) {
240
617
      formTokenWithChars(PartialTok, getSourceLocation(),
241
617
                         Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
242
617
                         StringRef(Pos.BufferPtr,
243
617
                                   Pos.BufferEnd - Pos.BufferPtr));
244
617
      HavePartialTok = true;
245
617
      Pos.CurToken++;
246
617
    }
247
655
248
655
    P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
249
655
    Pos.CurToken = Toks.size();
250
655
251
655
    if (HavePartialTok)
252
617
      P.putBack(PartialTok);
253
655
  }
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.61k
    Traits(Traits) {
261
1.61k
  consumeToken();
262
1.61k
}
263
264
void Parser::parseParamCommandArgs(ParamCommandComment *PC,
265
471
                                   TextTokenRetokenizer &Retokenizer) {
266
471
  Token Arg;
267
471
  // Check if argument looks like direction specification: [dir]
268
471
  // e.g., [in], [out], [in,out]
269
471
  if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
270
48
    S.actOnParamCommandDirectionArg(PC,
271
48
                                    Arg.getLocation(),
272
48
                                    Arg.getEndLocation(),
273
48
                                    Arg.getText());
274
471
275
471
  if (Retokenizer.lexWord(Arg))
276
466
    S.actOnParamCommandParamNameArg(PC,
277
466
                                    Arg.getLocation(),
278
466
                                    Arg.getEndLocation(),
279
466
                                    Arg.getText());
280
471
}
281
282
void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
283
198
                                    TextTokenRetokenizer &Retokenizer) {
284
198
  Token Arg;
285
198
  if (Retokenizer.lexWord(Arg))
286
191
    S.actOnTParamCommandParamNameArg(TPC,
287
191
                                     Arg.getLocation(),
288
191
                                     Arg.getEndLocation(),
289
191
                                     Arg.getText());
290
198
}
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
0
307
0
  S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
308
0
}
309
310
1.63k
BlockCommandComment *Parser::parseBlockCommand() {
311
1.63k
  assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
312
1.63k
313
1.63k
  ParamCommandComment *PC = nullptr;
314
1.63k
  TParamCommandComment *TPC = nullptr;
315
1.63k
  BlockCommandComment *BC = nullptr;
316
1.63k
  const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
317
1.63k
  CommandMarkerKind CommandMarker =
318
1.63k
      Tok.is(tok::backslash_command) ? 
CMK_Backslash1.51k
:
CMK_At124
;
319
1.63k
  if (Info->IsParamCommand) {
320
475
    PC = S.actOnParamCommandStart(Tok.getLocation(),
321
475
                                  Tok.getEndLocation(),
322
475
                                  Tok.getCommandID(),
323
475
                                  CommandMarker);
324
1.16k
  } else if (Info->IsTParamCommand) {
325
199
    TPC = S.actOnTParamCommandStart(Tok.getLocation(),
326
199
                                    Tok.getEndLocation(),
327
199
                                    Tok.getCommandID(),
328
199
                                    CommandMarker);
329
963
  } else {
330
963
    BC = S.actOnBlockCommandStart(Tok.getLocation(),
331
963
                                  Tok.getEndLocation(),
332
963
                                  Tok.getCommandID(),
333
963
                                  CommandMarker);
334
963
  }
335
1.63k
  consumeToken();
336
1.63k
337
1.63k
  if (isTokBlockCommand()) {
338
165
    // Block command ahead.  We can't nest block commands, so pretend that this
339
165
    // command has an empty argument.
340
165
    ParagraphComment *Paragraph = S.actOnParagraphComment(None);
341
165
    if (PC) {
342
4
      S.actOnParamCommandFinish(PC, Paragraph);
343
4
      return PC;
344
161
    } else if (TPC) {
345
1
      S.actOnTParamCommandFinish(TPC, Paragraph);
346
1
      return TPC;
347
160
    } else {
348
160
      S.actOnBlockCommandFinish(BC, Paragraph);
349
160
      return BC;
350
160
    }
351
1.47k
  }
352
1.47k
353
1.47k
  if (PC || 
TPC1.00k
||
Info->NumArgs > 0803
) {
354
669
    // In order to parse command arguments we need to retokenize a few
355
669
    // following text tokens.
356
669
    TextTokenRetokenizer Retokenizer(Allocator, *this);
357
669
358
669
    if (PC)
359
471
      parseParamCommandArgs(PC, Retokenizer);
360
198
    else if (TPC)
361
198
      parseTParamCommandArgs(TPC, Retokenizer);
362
0
    else
363
0
      parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
364
669
365
669
    Retokenizer.putBackLeftoverTokens();
366
669
  }
367
1.47k
368
1.47k
  // If there's a block command ahead, we will attach an empty paragraph to
369
1.47k
  // this command.
370
1.47k
  bool EmptyParagraph = false;
371
1.47k
  if (isTokBlockCommand())
372
3
    EmptyParagraph = true;
373
1.46k
  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
1.47k
380
1.47k
  ParagraphComment *Paragraph;
381
1.47k
  if (EmptyParagraph)
382
12
    Paragraph = S.actOnParagraphComment(None);
383
1.46k
  else {
384
1.46k
    BlockContentComment *Block = parseParagraphOrBlockCommand();
385
1.46k
    // Since we have checked for a block command, we should have parsed a
386
1.46k
    // paragraph.
387
1.46k
    Paragraph = cast<ParagraphComment>(Block);
388
1.46k
  }
389
1.47k
390
1.47k
  if (PC) {
391
471
    S.actOnParamCommandFinish(PC, Paragraph);
392
471
    return PC;
393
1.00k
  } else if (TPC) {
394
198
    S.actOnTParamCommandFinish(TPC, Paragraph);
395
198
    return TPC;
396
803
  } else {
397
803
    S.actOnBlockCommandFinish(BC, Paragraph);
398
803
    return BC;
399
803
  }
400
1.47k
}
401
402
77
InlineCommandComment *Parser::parseInlineCommand() {
403
77
  assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
404
77
405
77
  const Token CommandTok = Tok;
406
77
  consumeToken();
407
77
408
77
  TextTokenRetokenizer Retokenizer(Allocator, *this);
409
77
410
77
  Token ArgTok;
411
77
  bool ArgTokValid = Retokenizer.lexWord(ArgTok);
412
77
413
77
  InlineCommandComment *IC;
414
77
  if (ArgTokValid) {
415
54
    IC = S.actOnInlineCommand(CommandTok.getLocation(),
416
54
                              CommandTok.getEndLocation(),
417
54
                              CommandTok.getCommandID(),
418
54
                              ArgTok.getLocation(),
419
54
                              ArgTok.getEndLocation(),
420
54
                              ArgTok.getText());
421
54
  } else {
422
23
    IC = S.actOnInlineCommand(CommandTok.getLocation(),
423
23
                              CommandTok.getEndLocation(),
424
23
                              CommandTok.getCommandID());
425
23
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
77
433
77
  Retokenizer.putBackLeftoverTokens();
434
77
435
77
  return IC;
436
77
}
437
438
119
HTMLStartTagComment *Parser::parseHTMLStartTag() {
439
119
  assert(Tok.is(tok::html_start_tag));
440
119
  HTMLStartTagComment *HST =
441
119
      S.actOnHTMLStartTagStart(Tok.getLocation(),
442
119
                               Tok.getHTMLTagStartName());
443
119
  consumeToken();
444
119
445
119
  SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
446
174
  while (true) {
447
174
    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
12
               
Tok.is(tok::html_quoted_string)9
)
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
21
480
87
    case tok::html_greater:
481
87
      S.actOnHTMLStartTagFinish(HST,
482
87
                                S.copyArray(llvm::makeArrayRef(Attrs)),
483
87
                                Tok.getLocation(),
484
87
                                /* IsSelfClosing = */ false);
485
87
      consumeToken();
486
87
      return HST;
487
21
488
21
    case tok::html_slash_greater:
489
10
      S.actOnHTMLStartTagFinish(HST,
490
10
                                S.copyArray(llvm::makeArrayRef(Attrs)),
491
10
                                Tok.getLocation(),
492
10
                                /* IsSelfClosing = */ true);
493
10
      consumeToken();
494
10
      return HST;
495
21
496
21
    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
18
             
Tok.is(tok::html_quoted_string)12
)
502
9
        consumeToken();
503
9
      if (Tok.is(tok::html_ident) ||
504
9
          Tok.is(tok::html_greater) ||
505
9
          
Tok.is(tok::html_slash_greater)3
)
506
6
        continue;
507
3
508
3
      S.actOnHTMLStartTagFinish(HST,
509
3
                                S.copyArray(llvm::makeArrayRef(Attrs)),
510
3
                                SourceLocation(),
511
3
                                /* IsSelfClosing = */ false);
512
3
      return HST;
513
3
514
19
    default:
515
19
      // 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
174
    }
540
174
  }
541
119
}
542
543
62
HTMLEndTagComment *Parser::parseHTMLEndTag() {
544
62
  assert(Tok.is(tok::html_end_tag));
545
62
  Token TokEndTag = Tok;
546
62
  consumeToken();
547
62
  SourceLocation Loc;
548
62
  if (Tok.is(tok::html_greater)) {
549
61
    Loc = Tok.getLocation();
550
61
    consumeToken();
551
61
  }
552
62
553
62
  return S.actOnHTMLEndTag(TokEndTag.getLocation(),
554
62
                           Loc,
555
62
                           TokEndTag.getHTMLTagEndName());
556
62
}
557
558
4.89k
BlockContentComment *Parser::parseParagraphOrBlockCommand() {
559
4.89k
  SmallVector<InlineContentComment *, 8> Content;
560
4.89k
561
9.86k
  while (true) {
562
9.86k
    switch (Tok.getKind()) {
563
115
    case tok::verbatim_block_begin:
564
115
    case tok::verbatim_line_name:
565
115
    case tok::eof:
566
115
      break; // Block content or EOF ahead, finish this parapgaph.
567
115
568
151
    case tok::unknown_command:
569
151
      Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
570
151
                                              Tok.getEndLocation(),
571
151
                                              Tok.getUnknownCommandName()));
572
151
      consumeToken();
573
151
      continue;
574
115
575
3.16k
    case tok::backslash_command:
576
3.16k
    case tok::at_command: {
577
3.16k
      const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
578
3.16k
      if (Info->IsBlockCommand) {
579
3.07k
        if (Content.size() == 0)
580
1.63k
          return parseBlockCommand();
581
1.43k
        break; // Block command ahead, finish this parapgaph.
582
1.43k
      }
583
92
      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
81
      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
77
      assert(Info->IsInlineCommand);
600
77
      Content.push_back(parseInlineCommand());
601
77
      continue;
602
77
    }
603
77
604
2.38k
    case tok::newline: {
605
2.38k
      consumeToken();
606
2.38k
      if (Tok.is(tok::newline) || 
Tok.is(tok::eof)1.97k
) {
607
1.61k
        consumeToken();
608
1.61k
        break; // Two newlines -- end of paragraph.
609
1.61k
      }
610
763
      // Also allow [tok::newline, tok::text, tok::newline] if the middle
611
763
      // tok::text is just whitespace.
612
763
      if (Tok.is(tok::text) && 
isWhitespace(Tok.getText())617
) {
613
515
        Token WhitespaceTok = Tok;
614
515
        consumeToken();
615
515
        if (Tok.is(tok::newline) || 
Tok.is(tok::eof)425
) {
616
90
          consumeToken();
617
90
          break;
618
90
        }
619
425
        // We have [tok::newline, tok::text, non-newline].  Put back tok::text.
620
425
        putBack(WhitespaceTok);
621
425
      }
622
763
      
if (673
Content.size() > 0673
)
623
656
        Content.back()->addTrailingNewline();
624
673
      continue;
625
763
    }
626
763
627
763
    // Don't deal with HTML tag soup now.
628
763
    case tok::html_start_tag:
629
119
      Content.push_back(parseHTMLStartTag());
630
119
      continue;
631
763
632
763
    case tok::html_end_tag:
633
62
      Content.push_back(parseHTMLEndTag());
634
62
      continue;
635
763
636
3.86k
    case tok::text:
637
3.86k
      Content.push_back(S.actOnText(Tok.getLocation(),
638
3.86k
                                    Tok.getEndLocation(),
639
3.86k
                                    Tok.getText()));
640
3.86k
      consumeToken();
641
3.86k
      continue;
642
763
643
763
    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.26k
    }
653
3.26k
    break;
654
3.26k
  }
655
4.89k
656
4.89k
  
return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)))3.26k
;
657
4.89k
}
658
659
35
VerbatimBlockComment *Parser::parseVerbatimBlock() {
660
35
  assert(Tok.is(tok::verbatim_block_begin));
661
35
662
35
  VerbatimBlockComment *VB =
663
35
      S.actOnVerbatimBlockStart(Tok.getLocation(),
664
35
                                Tok.getVerbatimBlockID());
665
35
  consumeToken();
666
35
667
35
  // Don't create an empty line if verbatim opening command is followed
668
35
  // by a newline.
669
35
  if (Tok.is(tok::newline))
670
11
    consumeToken();
671
35
672
35
  SmallVector<VerbatimBlockLineComment *, 8> Lines;
673
90
  while (Tok.is(tok::verbatim_block_line) ||
674
90
         
Tok.is(tok::newline)42
) {
675
55
    VerbatimBlockLineComment *Line;
676
55
    if (Tok.is(tok::verbatim_block_line)) {
677
48
      Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
678
48
                                      Tok.getVerbatimBlockText());
679
48
      consumeToken();
680
48
      if (Tok.is(tok::newline)) {
681
21
        consumeToken();
682
21
      }
683
48
    } else {
684
7
      // Empty line, just a tok::newline.
685
7
      Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
686
7
      consumeToken();
687
7
    }
688
55
    Lines.push_back(Line);
689
55
  }
690
35
691
35
  if (Tok.is(tok::verbatim_block_end)) {
692
28
    const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
693
28
    S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
694
28
                               Info->Name,
695
28
                               S.copyArray(llvm::makeArrayRef(Lines)));
696
28
    consumeToken();
697
28
  } else {
698
7
    // Unterminated \\verbatim block
699
7
    S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
700
7
                               S.copyArray(llvm::makeArrayRef(Lines)));
701
7
  }
702
35
703
35
  return VB;
704
35
}
705
706
89
VerbatimLineComment *Parser::parseVerbatimLine() {
707
89
  assert(Tok.is(tok::verbatim_line_name));
708
89
709
89
  Token NameTok = Tok;
710
89
  consumeToken();
711
89
712
89
  SourceLocation TextBegin;
713
89
  StringRef Text;
714
89
  // Next token might not be a tok::verbatim_line_text if verbatim line
715
89
  // starting command comes just before a newline or comment end.
716
89
  if (Tok.is(tok::verbatim_line_text)) {
717
87
    TextBegin = Tok.getLocation();
718
87
    Text = Tok.getVerbatimLineText();
719
87
  } else {
720
2
    TextBegin = NameTok.getEndLocation();
721
2
    Text = "";
722
2
  }
723
89
724
89
  VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
725
89
                                                NameTok.getVerbatimLineID(),
726
89
                                                TextBegin,
727
89
                                                Text);
728
89
  consumeToken();
729
89
  return VL;
730
89
}
731
732
3.56k
BlockContentComment *Parser::parseBlockContent() {
733
3.56k
  switch (Tok.getKind()) {
734
3.43k
  case tok::text:
735
3.43k
  case tok::unknown_command:
736
3.43k
  case tok::backslash_command:
737
3.43k
  case tok::at_command:
738
3.43k
  case tok::html_start_tag:
739
3.43k
  case tok::html_end_tag:
740
3.43k
    return parseParagraphOrBlockCommand();
741
3.43k
742
3.43k
  case tok::verbatim_block_begin:
743
35
    return parseVerbatimBlock();
744
3.43k
745
3.43k
  case tok::verbatim_line_name:
746
89
    return parseVerbatimLine();
747
3.43k
748
3.43k
  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.61k
FullComment *Parser::parseFullComment() {
764
1.61k
  // Skip newlines at the beginning of the comment.
765
1.90k
  while (Tok.is(tok::newline))
766
292
    consumeToken();
767
1.61k
768
1.61k
  SmallVector<BlockContentComment *, 8> Blocks;
769
5.17k
  while (Tok.isNot(tok::eof)) {
770
3.56k
    Blocks.push_back(parseBlockContent());
771
3.56k
772
3.56k
    // Skip extra newlines after paragraph end.
773
4.01k
    while (Tok.is(tok::newline))
774
453
      consumeToken();
775
3.56k
  }
776
1.61k
  return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
777
1.61k
}
778
779
} // end namespace comments
780
} // end namespace clang