llvm-project

Форк
0
/
CommentLexer.cpp 
862 строки · 25.6 Кб
1
//===--- CommentLexer.cpp -------------------------------------------------===//
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/CommentLexer.h"
10
#include "clang/AST/CommentCommandTraits.h"
11
#include "clang/AST/CommentDiagnostic.h"
12
#include "clang/Basic/CharInfo.h"
13
#include "llvm/ADT/StringExtras.h"
14
#include "llvm/ADT/StringSwitch.h"
15
#include "llvm/Support/ConvertUTF.h"
16
#include "llvm/Support/ErrorHandling.h"
17

18
namespace clang {
19
namespace comments {
20

21
void Token::dump(const Lexer &L, const SourceManager &SM) const {
22
  llvm::errs() << "comments::Token Kind=" << Kind << " ";
23
  Loc.print(llvm::errs(), SM);
24
  llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
25
}
26

27
static inline bool isHTMLNamedCharacterReferenceCharacter(char C) {
28
  return isLetter(C);
29
}
30

31
static inline bool isHTMLDecimalCharacterReferenceCharacter(char C) {
32
  return isDigit(C);
33
}
34

35
static inline bool isHTMLHexCharacterReferenceCharacter(char C) {
36
  return isHexDigit(C);
37
}
38

39
static inline StringRef convertCodePointToUTF8(
40
                                      llvm::BumpPtrAllocator &Allocator,
41
                                      unsigned CodePoint) {
42
  char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
43
  char *ResolvedPtr = Resolved;
44
  if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
45
    return StringRef(Resolved, ResolvedPtr - Resolved);
46
  else
47
    return StringRef();
48
}
49

50
namespace {
51

52
#include "clang/AST/CommentHTMLTags.inc"
53
#include "clang/AST/CommentHTMLNamedCharacterReferences.inc"
54

55
} // end anonymous namespace
56

57
StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
58
  // Fast path, first check a few most widely used named character references.
59
  return llvm::StringSwitch<StringRef>(Name)
60
      .Case("amp", "&")
61
      .Case("lt", "<")
62
      .Case("gt", ">")
63
      .Case("quot", "\"")
64
      .Case("apos", "\'")
65
      // Slow path.
66
      .Default(translateHTMLNamedCharacterReferenceToUTF8(Name));
67
}
68

69
StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
70
  unsigned CodePoint = 0;
71
  for (unsigned i = 0, e = Name.size(); i != e; ++i) {
72
    assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
73
    CodePoint *= 10;
74
    CodePoint += Name[i] - '0';
75
  }
76
  return convertCodePointToUTF8(Allocator, CodePoint);
77
}
78

79
StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
80
  unsigned CodePoint = 0;
81
  for (unsigned i = 0, e = Name.size(); i != e; ++i) {
82
    CodePoint *= 16;
83
    const char C = Name[i];
84
    assert(isHTMLHexCharacterReferenceCharacter(C));
85
    CodePoint += llvm::hexDigitValue(C);
86
  }
87
  return convertCodePointToUTF8(Allocator, CodePoint);
88
}
89

90
void Lexer::skipLineStartingDecorations() {
91
  // This function should be called only for C comments
92
  assert(CommentState == LCS_InsideCComment);
93

94
  if (BufferPtr == CommentEnd)
95
    return;
96

97
  const char *NewBufferPtr = BufferPtr;
98
  while (isHorizontalWhitespace(*NewBufferPtr))
99
    if (++NewBufferPtr == CommentEnd)
100
      return;
101
  if (*NewBufferPtr == '*')
102
    BufferPtr = NewBufferPtr + 1;
103
}
104

105
namespace {
106
/// Returns pointer to the first newline character in the string.
107
const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
108
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
109
    if (isVerticalWhitespace(*BufferPtr))
110
      return BufferPtr;
111
  }
112
  return BufferEnd;
113
}
114

115
const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
116
  if (BufferPtr == BufferEnd)
117
    return BufferPtr;
118

119
  if (*BufferPtr == '\n')
120
    BufferPtr++;
121
  else {
122
    assert(*BufferPtr == '\r');
123
    BufferPtr++;
124
    if (BufferPtr != BufferEnd && *BufferPtr == '\n')
125
      BufferPtr++;
126
  }
127
  return BufferPtr;
128
}
129

130
const char *skipNamedCharacterReference(const char *BufferPtr,
131
                                        const char *BufferEnd) {
132
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
133
    if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
134
      return BufferPtr;
135
  }
136
  return BufferEnd;
137
}
138

139
const char *skipDecimalCharacterReference(const char *BufferPtr,
140
                                          const char *BufferEnd) {
141
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
142
    if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
143
      return BufferPtr;
144
  }
145
  return BufferEnd;
146
}
147

148
const char *skipHexCharacterReference(const char *BufferPtr,
149
                                      const char *BufferEnd) {
150
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
151
    if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
152
      return BufferPtr;
153
  }
154
  return BufferEnd;
155
}
156

157
bool isHTMLIdentifierStartingCharacter(char C) {
158
  return isLetter(C);
159
}
160

161
bool isHTMLIdentifierCharacter(char C) {
162
  return isAlphanumeric(C);
163
}
164

165
const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
166
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
167
    if (!isHTMLIdentifierCharacter(*BufferPtr))
168
      return BufferPtr;
169
  }
170
  return BufferEnd;
171
}
172

173
/// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
174
/// string allowed.
175
///
176
/// Returns pointer to closing quote.
177
const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
178
{
179
  const char Quote = *BufferPtr;
180
  assert(Quote == '\"' || Quote == '\'');
181

182
  BufferPtr++;
183
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
184
    const char C = *BufferPtr;
185
    if (C == Quote && BufferPtr[-1] != '\\')
186
      return BufferPtr;
187
  }
188
  return BufferEnd;
189
}
190

191
const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
192
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
193
    if (!isWhitespace(*BufferPtr))
194
      return BufferPtr;
195
  }
196
  return BufferEnd;
197
}
198

199
bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
200
  return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
201
}
202

203
bool isCommandNameStartCharacter(char C) {
204
  return isLetter(C);
205
}
206

207
bool isCommandNameCharacter(char C) {
208
  return isAlphanumeric(C);
209
}
210

211
const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
212
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
213
    if (!isCommandNameCharacter(*BufferPtr))
214
      return BufferPtr;
215
  }
216
  return BufferEnd;
217
}
218

219
/// Return the one past end pointer for BCPL comments.
220
/// Handles newlines escaped with backslash or trigraph for backslahs.
221
const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
222
  const char *CurPtr = BufferPtr;
223
  while (CurPtr != BufferEnd) {
224
    while (!isVerticalWhitespace(*CurPtr)) {
225
      CurPtr++;
226
      if (CurPtr == BufferEnd)
227
        return BufferEnd;
228
    }
229
    // We found a newline, check if it is escaped.
230
    const char *EscapePtr = CurPtr - 1;
231
    while(isHorizontalWhitespace(*EscapePtr))
232
      EscapePtr--;
233

234
    if (*EscapePtr == '\\' ||
235
        (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
236
         EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
237
      // We found an escaped newline.
238
      CurPtr = skipNewline(CurPtr, BufferEnd);
239
    } else
240
      return CurPtr; // Not an escaped newline.
241
  }
242
  return BufferEnd;
243
}
244

245
/// Return the one past end pointer for C comments.
246
/// Very dumb, does not handle escaped newlines or trigraphs.
247
const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
248
  for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
249
    if (*BufferPtr == '*') {
250
      assert(BufferPtr + 1 != BufferEnd);
251
      if (*(BufferPtr + 1) == '/')
252
        return BufferPtr;
253
    }
254
  }
255
  llvm_unreachable("buffer end hit before '*/' was seen");
256
}
257

258
} // end anonymous namespace
259

260
void Lexer::formTokenWithChars(Token &Result, const char *TokEnd,
261
                               tok::TokenKind Kind) {
262
  const unsigned TokLen = TokEnd - BufferPtr;
263
  Result.setLocation(getSourceLocation(BufferPtr));
264
  Result.setKind(Kind);
265
  Result.setLength(TokLen);
266
#ifndef NDEBUG
267
  Result.TextPtr = "<UNSET>";
268
  Result.IntVal = 7;
269
#endif
270
  BufferPtr = TokEnd;
271
}
272

273
const char *Lexer::skipTextToken() {
274
  const char *TokenPtr = BufferPtr;
275
  assert(TokenPtr < CommentEnd);
276
  StringRef TokStartSymbols = ParseCommands ? "\n\r\\@\"&<" : "\n\r";
277

278
again:
279
  size_t End =
280
      StringRef(TokenPtr, CommentEnd - TokenPtr).find_first_of(TokStartSymbols);
281
  if (End == StringRef::npos)
282
    return CommentEnd;
283

284
  // Doxygen doesn't recognize any commands in a one-line double quotation.
285
  // If we don't find an ending quotation mark, we pretend it never began.
286
  if (*(TokenPtr + End) == '\"') {
287
    TokenPtr += End + 1;
288
    End = StringRef(TokenPtr, CommentEnd - TokenPtr).find_first_of("\n\r\"");
289
    if (End != StringRef::npos && *(TokenPtr + End) == '\"')
290
      TokenPtr += End + 1;
291
    goto again;
292
  }
293
  return TokenPtr + End;
294
}
295

296
void Lexer::lexCommentText(Token &T) {
297
  assert(CommentState == LCS_InsideBCPLComment ||
298
         CommentState == LCS_InsideCComment);
299

300
  // Handles lexing non-command text, i.e. text and newline.
301
  auto HandleNonCommandToken = [&]() -> void {
302
    assert(State == LS_Normal);
303

304
    const char *TokenPtr = BufferPtr;
305
    assert(TokenPtr < CommentEnd);
306
    switch (*TokenPtr) {
307
      case '\n':
308
      case '\r':
309
          TokenPtr = skipNewline(TokenPtr, CommentEnd);
310
          formTokenWithChars(T, TokenPtr, tok::newline);
311

312
          if (CommentState == LCS_InsideCComment)
313
            skipLineStartingDecorations();
314
          return;
315

316
      default:
317
        return formTextToken(T, skipTextToken());
318
    }
319
  };
320

321
  if (!ParseCommands)
322
    return HandleNonCommandToken();
323

324
  switch (State) {
325
  case LS_Normal:
326
    break;
327
  case LS_VerbatimBlockFirstLine:
328
    lexVerbatimBlockFirstLine(T);
329
    return;
330
  case LS_VerbatimBlockBody:
331
    lexVerbatimBlockBody(T);
332
    return;
333
  case LS_VerbatimLineText:
334
    lexVerbatimLineText(T);
335
    return;
336
  case LS_HTMLStartTag:
337
    lexHTMLStartTag(T);
338
    return;
339
  case LS_HTMLEndTag:
340
    lexHTMLEndTag(T);
341
    return;
342
  }
343

344
  assert(State == LS_Normal);
345
  const char *TokenPtr = BufferPtr;
346
  assert(TokenPtr < CommentEnd);
347
  switch(*TokenPtr) {
348
    case '\\':
349
    case '@': {
350
      // Commands that start with a backslash and commands that start with
351
      // 'at' have equivalent semantics.  But we keep information about the
352
      // exact syntax in AST for comments.
353
      tok::TokenKind CommandKind =
354
          (*TokenPtr == '@') ? tok::at_command : tok::backslash_command;
355
      TokenPtr++;
356
      if (TokenPtr == CommentEnd) {
357
        formTextToken(T, TokenPtr);
358
        return;
359
      }
360
      char C = *TokenPtr;
361
      switch (C) {
362
      default:
363
        break;
364

365
      case '\\': case '@': case '&': case '$':
366
      case '#':  case '<': case '>': case '%':
367
      case '\"': case '.': case ':':
368
        // This is one of \\ \@ \& \$ etc escape sequences.
369
        TokenPtr++;
370
        if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
371
          // This is the \:: escape sequence.
372
          TokenPtr++;
373
        }
374
        StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
375
        formTokenWithChars(T, TokenPtr, tok::text);
376
        T.setText(UnescapedText);
377
        return;
378
      }
379

380
      // Don't make zero-length commands.
381
      if (!isCommandNameStartCharacter(*TokenPtr)) {
382
        formTextToken(T, TokenPtr);
383
        return;
384
      }
385

386
      TokenPtr = skipCommandName(TokenPtr, CommentEnd);
387
      unsigned Length = TokenPtr - (BufferPtr + 1);
388

389
      // Hardcoded support for lexing LaTeX formula commands
390
      // \f$ \f( \f) \f[ \f] \f{ \f} as a single command.
391
      if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
392
        C = *TokenPtr;
393
        if (C == '$' || C == '(' || C == ')' || C == '[' || C == ']' ||
394
            C == '{' || C == '}') {
395
          TokenPtr++;
396
          Length++;
397
        }
398
      }
399

400
      StringRef CommandName(BufferPtr + 1, Length);
401

402
      const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName);
403
      if (!Info) {
404
        if ((Info = Traits.getTypoCorrectCommandInfo(CommandName))) {
405
          StringRef CorrectedName = Info->Name;
406
          SourceLocation Loc = getSourceLocation(BufferPtr);
407
          SourceLocation EndLoc = getSourceLocation(TokenPtr);
408
          SourceRange FullRange = SourceRange(Loc, EndLoc);
409
          SourceRange CommandRange(Loc.getLocWithOffset(1), EndLoc);
410
          Diag(Loc, diag::warn_correct_comment_command_name)
411
            << FullRange << CommandName << CorrectedName
412
            << FixItHint::CreateReplacement(CommandRange, CorrectedName);
413
        } else {
414
          formTokenWithChars(T, TokenPtr, tok::unknown_command);
415
          T.setUnknownCommandName(CommandName);
416
          Diag(T.getLocation(), diag::warn_unknown_comment_command_name)
417
              << SourceRange(T.getLocation(), T.getEndLocation());
418
          return;
419
        }
420
      }
421
      if (Info->IsVerbatimBlockCommand) {
422
        setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info);
423
        return;
424
      }
425
      if (Info->IsVerbatimLineCommand) {
426
        setupAndLexVerbatimLine(T, TokenPtr, Info);
427
        return;
428
      }
429
      formTokenWithChars(T, TokenPtr, CommandKind);
430
      T.setCommandID(Info->getID());
431
      return;
432
    }
433

434
    case '&':
435
      lexHTMLCharacterReference(T);
436
      return;
437

438
    case '<': {
439
      TokenPtr++;
440
      if (TokenPtr == CommentEnd) {
441
        formTextToken(T, TokenPtr);
442
        return;
443
      }
444
      const char C = *TokenPtr;
445
      if (isHTMLIdentifierStartingCharacter(C))
446
        setupAndLexHTMLStartTag(T);
447
      else if (C == '/')
448
        setupAndLexHTMLEndTag(T);
449
      else
450
        formTextToken(T, TokenPtr);
451
      return;
452
    }
453

454
    default:
455
      return HandleNonCommandToken();
456
  }
457
}
458

459
void Lexer::setupAndLexVerbatimBlock(Token &T,
460
                                     const char *TextBegin,
461
                                     char Marker, const CommandInfo *Info) {
462
  assert(Info->IsVerbatimBlockCommand);
463

464
  VerbatimBlockEndCommandName.clear();
465
  VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
466
  VerbatimBlockEndCommandName.append(Info->EndCommandName);
467

468
  formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
469
  T.setVerbatimBlockID(Info->getID());
470

471
  // If there is a newline following the verbatim opening command, skip the
472
  // newline so that we don't create an tok::verbatim_block_line with empty
473
  // text content.
474
  if (BufferPtr != CommentEnd &&
475
      isVerticalWhitespace(*BufferPtr)) {
476
    BufferPtr = skipNewline(BufferPtr, CommentEnd);
477
    State = LS_VerbatimBlockBody;
478
    return;
479
  }
480

481
  State = LS_VerbatimBlockFirstLine;
482
}
483

484
void Lexer::lexVerbatimBlockFirstLine(Token &T) {
485
again:
486
  assert(BufferPtr < CommentEnd);
487

488
  // FIXME: It would be better to scan the text once, finding either the block
489
  // end command or newline.
490
  //
491
  // Extract current line.
492
  const char *Newline = findNewline(BufferPtr, CommentEnd);
493
  StringRef Line(BufferPtr, Newline - BufferPtr);
494

495
  // Look for end command in current line.
496
  size_t Pos = Line.find(VerbatimBlockEndCommandName);
497
  const char *TextEnd;
498
  const char *NextLine;
499
  if (Pos == StringRef::npos) {
500
    // Current line is completely verbatim.
501
    TextEnd = Newline;
502
    NextLine = skipNewline(Newline, CommentEnd);
503
  } else if (Pos == 0) {
504
    // Current line contains just an end command.
505
    const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
506
    StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
507
    formTokenWithChars(T, End, tok::verbatim_block_end);
508
    T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID());
509
    State = LS_Normal;
510
    return;
511
  } else {
512
    // There is some text, followed by end command.  Extract text first.
513
    TextEnd = BufferPtr + Pos;
514
    NextLine = TextEnd;
515
    // If there is only whitespace before end command, skip whitespace.
516
    if (isWhitespace(BufferPtr, TextEnd)) {
517
      BufferPtr = TextEnd;
518
      goto again;
519
    }
520
  }
521

522
  StringRef Text(BufferPtr, TextEnd - BufferPtr);
523
  formTokenWithChars(T, NextLine, tok::verbatim_block_line);
524
  T.setVerbatimBlockText(Text);
525

526
  State = LS_VerbatimBlockBody;
527
}
528

529
void Lexer::lexVerbatimBlockBody(Token &T) {
530
  assert(State == LS_VerbatimBlockBody);
531

532
  if (CommentState == LCS_InsideCComment)
533
    skipLineStartingDecorations();
534

535
  if (BufferPtr == CommentEnd) {
536
    formTokenWithChars(T, BufferPtr, tok::verbatim_block_line);
537
    T.setVerbatimBlockText("");
538
    return;
539
  }
540

541
  lexVerbatimBlockFirstLine(T);
542
}
543

544
void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin,
545
                                    const CommandInfo *Info) {
546
  assert(Info->IsVerbatimLineCommand);
547
  formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
548
  T.setVerbatimLineID(Info->getID());
549

550
  State = LS_VerbatimLineText;
551
}
552

553
void Lexer::lexVerbatimLineText(Token &T) {
554
  assert(State == LS_VerbatimLineText);
555

556
  // Extract current line.
557
  const char *Newline = findNewline(BufferPtr, CommentEnd);
558
  StringRef Text(BufferPtr, Newline - BufferPtr);
559
  formTokenWithChars(T, Newline, tok::verbatim_line_text);
560
  T.setVerbatimLineText(Text);
561

562
  State = LS_Normal;
563
}
564

565
void Lexer::lexHTMLCharacterReference(Token &T) {
566
  const char *TokenPtr = BufferPtr;
567
  assert(*TokenPtr == '&');
568
  TokenPtr++;
569
  if (TokenPtr == CommentEnd) {
570
    formTextToken(T, TokenPtr);
571
    return;
572
  }
573
  const char *NamePtr;
574
  bool isNamed = false;
575
  bool isDecimal = false;
576
  char C = *TokenPtr;
577
  if (isHTMLNamedCharacterReferenceCharacter(C)) {
578
    NamePtr = TokenPtr;
579
    TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
580
    isNamed = true;
581
  } else if (C == '#') {
582
    TokenPtr++;
583
    if (TokenPtr == CommentEnd) {
584
      formTextToken(T, TokenPtr);
585
      return;
586
    }
587
    C = *TokenPtr;
588
    if (isHTMLDecimalCharacterReferenceCharacter(C)) {
589
      NamePtr = TokenPtr;
590
      TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
591
      isDecimal = true;
592
    } else if (C == 'x' || C == 'X') {
593
      TokenPtr++;
594
      NamePtr = TokenPtr;
595
      TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
596
    } else {
597
      formTextToken(T, TokenPtr);
598
      return;
599
    }
600
  } else {
601
    formTextToken(T, TokenPtr);
602
    return;
603
  }
604
  if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
605
      *TokenPtr != ';') {
606
    formTextToken(T, TokenPtr);
607
    return;
608
  }
609
  StringRef Name(NamePtr, TokenPtr - NamePtr);
610
  TokenPtr++; // Skip semicolon.
611
  StringRef Resolved;
612
  if (isNamed)
613
    Resolved = resolveHTMLNamedCharacterReference(Name);
614
  else if (isDecimal)
615
    Resolved = resolveHTMLDecimalCharacterReference(Name);
616
  else
617
    Resolved = resolveHTMLHexCharacterReference(Name);
618

619
  if (Resolved.empty()) {
620
    formTextToken(T, TokenPtr);
621
    return;
622
  }
623
  formTokenWithChars(T, TokenPtr, tok::text);
624
  T.setText(Resolved);
625
}
626

627
void Lexer::setupAndLexHTMLStartTag(Token &T) {
628
  assert(BufferPtr[0] == '<' &&
629
         isHTMLIdentifierStartingCharacter(BufferPtr[1]));
630
  const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
631
  StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
632
  if (!isHTMLTagName(Name)) {
633
    formTextToken(T, TagNameEnd);
634
    return;
635
  }
636

637
  formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
638
  T.setHTMLTagStartName(Name);
639

640
  BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
641

642
  const char C = *BufferPtr;
643
  if (BufferPtr != CommentEnd &&
644
      (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
645
    State = LS_HTMLStartTag;
646
}
647

648
void Lexer::lexHTMLStartTag(Token &T) {
649
  assert(State == LS_HTMLStartTag);
650

651
  const char *TokenPtr = BufferPtr;
652
  char C = *TokenPtr;
653
  if (isHTMLIdentifierCharacter(C)) {
654
    TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
655
    StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
656
    formTokenWithChars(T, TokenPtr, tok::html_ident);
657
    T.setHTMLIdent(Ident);
658
  } else {
659
    switch (C) {
660
    case '=':
661
      TokenPtr++;
662
      formTokenWithChars(T, TokenPtr, tok::html_equals);
663
      break;
664
    case '\"':
665
    case '\'': {
666
      const char *OpenQuote = TokenPtr;
667
      TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
668
      const char *ClosingQuote = TokenPtr;
669
      if (TokenPtr != CommentEnd) // Skip closing quote.
670
        TokenPtr++;
671
      formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
672
      T.setHTMLQuotedString(StringRef(OpenQuote + 1,
673
                                      ClosingQuote - (OpenQuote + 1)));
674
      break;
675
    }
676
    case '>':
677
      TokenPtr++;
678
      formTokenWithChars(T, TokenPtr, tok::html_greater);
679
      State = LS_Normal;
680
      return;
681
    case '/':
682
      TokenPtr++;
683
      if (TokenPtr != CommentEnd && *TokenPtr == '>') {
684
        TokenPtr++;
685
        formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
686
      } else
687
        formTextToken(T, TokenPtr);
688

689
      State = LS_Normal;
690
      return;
691
    }
692
  }
693

694
  // Now look ahead and return to normal state if we don't see any HTML tokens
695
  // ahead.
696
  BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
697
  if (BufferPtr == CommentEnd) {
698
    State = LS_Normal;
699
    return;
700
  }
701

702
  C = *BufferPtr;
703
  if (!isHTMLIdentifierStartingCharacter(C) &&
704
      C != '=' && C != '\"' && C != '\'' && C != '>' && C != '/') {
705
    State = LS_Normal;
706
    return;
707
  }
708
}
709

710
void Lexer::setupAndLexHTMLEndTag(Token &T) {
711
  assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
712

713
  const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
714
  const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
715
  StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin);
716
  if (!isHTMLTagName(Name)) {
717
    formTextToken(T, TagNameEnd);
718
    return;
719
  }
720

721
  const char *End = skipWhitespace(TagNameEnd, CommentEnd);
722

723
  formTokenWithChars(T, End, tok::html_end_tag);
724
  T.setHTMLTagEndName(Name);
725

726
  if (BufferPtr != CommentEnd && *BufferPtr == '>')
727
    State = LS_HTMLEndTag;
728
}
729

730
void Lexer::lexHTMLEndTag(Token &T) {
731
  assert(BufferPtr != CommentEnd && *BufferPtr == '>');
732

733
  formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
734
  State = LS_Normal;
735
}
736

737
Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags,
738
             const CommandTraits &Traits, SourceLocation FileLoc,
739
             const char *BufferStart, const char *BufferEnd, bool ParseCommands)
740
    : Allocator(Allocator), Diags(Diags), Traits(Traits),
741
      BufferStart(BufferStart), BufferEnd(BufferEnd), BufferPtr(BufferStart),
742
      FileLoc(FileLoc), ParseCommands(ParseCommands),
743
      CommentState(LCS_BeforeComment), State(LS_Normal) {}
744

745
void Lexer::lex(Token &T) {
746
again:
747
  switch (CommentState) {
748
  case LCS_BeforeComment:
749
    if (BufferPtr == BufferEnd) {
750
      formTokenWithChars(T, BufferPtr, tok::eof);
751
      return;
752
    }
753

754
    assert(*BufferPtr == '/');
755
    BufferPtr++; // Skip first slash.
756
    switch(*BufferPtr) {
757
    case '/': { // BCPL comment.
758
      BufferPtr++; // Skip second slash.
759

760
      if (BufferPtr != BufferEnd) {
761
        // Skip Doxygen magic marker, if it is present.
762
        // It might be missing because of a typo //< or /*<, or because we
763
        // merged this non-Doxygen comment into a bunch of Doxygen comments
764
        // around it: /** ... */ /* ... */ /** ... */
765
        const char C = *BufferPtr;
766
        if (C == '/' || C == '!')
767
          BufferPtr++;
768
      }
769

770
      // Skip less-than symbol that marks trailing comments.
771
      // Skip it even if the comment is not a Doxygen one, because //< and /*<
772
      // are frequent typos.
773
      if (BufferPtr != BufferEnd && *BufferPtr == '<')
774
        BufferPtr++;
775

776
      CommentState = LCS_InsideBCPLComment;
777
      if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
778
        State = LS_Normal;
779
      CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
780
      goto again;
781
    }
782
    case '*': { // C comment.
783
      BufferPtr++; // Skip star.
784

785
      // Skip Doxygen magic marker.
786
      const char C = *BufferPtr;
787
      if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
788
        BufferPtr++;
789

790
      // Skip less-than symbol that marks trailing comments.
791
      if (BufferPtr != BufferEnd && *BufferPtr == '<')
792
        BufferPtr++;
793

794
      CommentState = LCS_InsideCComment;
795
      State = LS_Normal;
796
      CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
797
      goto again;
798
    }
799
    default:
800
      llvm_unreachable("second character of comment should be '/' or '*'");
801
    }
802

803
  case LCS_BetweenComments: {
804
    // Consecutive comments are extracted only if there is only whitespace
805
    // between them.  So we can search for the start of the next comment.
806
    const char *EndWhitespace = BufferPtr;
807
    while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
808
      EndWhitespace++;
809

810
    // Turn any whitespace between comments (and there is only whitespace
811
    // between them -- guaranteed by comment extraction) into a newline.  We
812
    // have two newlines between C comments in total (first one was synthesized
813
    // after a comment).
814
    formTokenWithChars(T, EndWhitespace, tok::newline);
815

816
    CommentState = LCS_BeforeComment;
817
    break;
818
  }
819

820
  case LCS_InsideBCPLComment:
821
  case LCS_InsideCComment:
822
    if (BufferPtr != CommentEnd) {
823
      lexCommentText(T);
824
      break;
825
    } else {
826
      // Skip C comment closing sequence.
827
      if (CommentState == LCS_InsideCComment) {
828
        assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
829
        BufferPtr += 2;
830
        assert(BufferPtr <= BufferEnd);
831

832
        // Synthenize newline just after the C comment, regardless if there is
833
        // actually a newline.
834
        formTokenWithChars(T, BufferPtr, tok::newline);
835

836
        CommentState = LCS_BetweenComments;
837
        break;
838
      } else {
839
        // Don't synthesized a newline after BCPL comment.
840
        CommentState = LCS_BetweenComments;
841
        goto again;
842
      }
843
    }
844
  }
845
}
846

847
StringRef Lexer::getSpelling(const Token &Tok,
848
                             const SourceManager &SourceMgr) const {
849
  SourceLocation Loc = Tok.getLocation();
850
  std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
851

852
  bool InvalidTemp = false;
853
  StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
854
  if (InvalidTemp)
855
    return StringRef();
856

857
  const char *Begin = File.data() + LocInfo.second;
858
  return StringRef(Begin, Tok.getLength());
859
}
860

861
} // end namespace comments
862
} // end namespace clang
863

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.