]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/llvm/tools/clang/lib/AST/CommentLexer.cpp
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / llvm / tools / clang / lib / AST / CommentLexer.cpp
1 #include "clang/AST/CommentLexer.h"
2 #include "clang/AST/CommentCommandTraits.h"
3 #include "clang/AST/CommentDiagnostic.h"
4 #include "clang/Basic/CharInfo.h"
5 #include "llvm/ADT/StringExtras.h"
6 #include "llvm/ADT/StringSwitch.h"
7 #include "llvm/Support/ConvertUTF.h"
8 #include "llvm/Support/ErrorHandling.h"
9
10 namespace clang {
11 namespace comments {
12
13 void Token::dump(const Lexer &L, const SourceManager &SM) const {
14   llvm::errs() << "comments::Token Kind=" << Kind << " ";
15   Loc.dump(SM);
16   llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
17 }
18
19 static inline bool isHTMLNamedCharacterReferenceCharacter(char C) {
20   return isLetter(C);
21 }
22
23 static inline bool isHTMLDecimalCharacterReferenceCharacter(char C) {
24   return isDigit(C);
25 }
26
27 static inline bool isHTMLHexCharacterReferenceCharacter(char C) {
28   return isHexDigit(C);
29 }
30
31 static inline StringRef convertCodePointToUTF8(
32                                       llvm::BumpPtrAllocator &Allocator,
33                                       unsigned CodePoint) {
34   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
35   char *ResolvedPtr = Resolved;
36   if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
37     return StringRef(Resolved, ResolvedPtr - Resolved);
38   else
39     return StringRef();
40 }
41
42 namespace {
43
44 #include "clang/AST/CommentHTMLTags.inc"
45 #include "clang/AST/CommentHTMLNamedCharacterReferences.inc"
46
47 } // unnamed namespace
48
49 StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
50   // Fast path, first check a few most widely used named character references.
51   return llvm::StringSwitch<StringRef>(Name)
52       .Case("amp", "&")
53       .Case("lt", "<")
54       .Case("gt", ">")
55       .Case("quot", "\"")
56       .Case("apos", "\'")
57       // Slow path.
58       .Default(translateHTMLNamedCharacterReferenceToUTF8(Name));
59 }
60
61 StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
62   unsigned CodePoint = 0;
63   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
64     assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
65     CodePoint *= 10;
66     CodePoint += Name[i] - '0';
67   }
68   return convertCodePointToUTF8(Allocator, CodePoint);
69 }
70
71 StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
72   unsigned CodePoint = 0;
73   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
74     CodePoint *= 16;
75     const char C = Name[i];
76     assert(isHTMLHexCharacterReferenceCharacter(C));
77     CodePoint += llvm::hexDigitValue(C);
78   }
79   return convertCodePointToUTF8(Allocator, CodePoint);
80 }
81
82 void Lexer::skipLineStartingDecorations() {
83   // This function should be called only for C comments
84   assert(CommentState == LCS_InsideCComment);
85
86   if (BufferPtr == CommentEnd)
87     return;
88
89   switch (*BufferPtr) {
90   case ' ':
91   case '\t':
92   case '\f':
93   case '\v': {
94     const char *NewBufferPtr = BufferPtr;
95     NewBufferPtr++;
96     if (NewBufferPtr == CommentEnd)
97       return;
98
99     char C = *NewBufferPtr;
100     while (isHorizontalWhitespace(C)) {
101       NewBufferPtr++;
102       if (NewBufferPtr == CommentEnd)
103         return;
104       C = *NewBufferPtr;
105     }
106     if (C == '*')
107       BufferPtr = NewBufferPtr + 1;
108     break;
109   }
110   case '*':
111     BufferPtr++;
112     break;
113   }
114 }
115
116 namespace {
117 /// Returns pointer to the first newline character in the string.
118 const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
119   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
120     if (isVerticalWhitespace(*BufferPtr))
121       return BufferPtr;
122   }
123   return BufferEnd;
124 }
125
126 const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
127   if (BufferPtr == BufferEnd)
128     return BufferPtr;
129
130   if (*BufferPtr == '\n')
131     BufferPtr++;
132   else {
133     assert(*BufferPtr == '\r');
134     BufferPtr++;
135     if (BufferPtr != BufferEnd && *BufferPtr == '\n')
136       BufferPtr++;
137   }
138   return BufferPtr;
139 }
140
141 const char *skipNamedCharacterReference(const char *BufferPtr,
142                                         const char *BufferEnd) {
143   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
144     if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
145       return BufferPtr;
146   }
147   return BufferEnd;
148 }
149
150 const char *skipDecimalCharacterReference(const char *BufferPtr,
151                                           const char *BufferEnd) {
152   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
153     if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
154       return BufferPtr;
155   }
156   return BufferEnd;
157 }
158
159 const char *skipHexCharacterReference(const char *BufferPtr,
160                                           const char *BufferEnd) {
161   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
162     if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
163       return BufferPtr;
164   }
165   return BufferEnd;
166 }
167
168 bool isHTMLIdentifierStartingCharacter(char C) {
169   return isLetter(C);
170 }
171
172 bool isHTMLIdentifierCharacter(char C) {
173   return isAlphanumeric(C);
174 }
175
176 const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
177   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
178     if (!isHTMLIdentifierCharacter(*BufferPtr))
179       return BufferPtr;
180   }
181   return BufferEnd;
182 }
183
184 /// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
185 /// string allowed.
186 ///
187 /// Returns pointer to closing quote.
188 const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
189 {
190   const char Quote = *BufferPtr;
191   assert(Quote == '\"' || Quote == '\'');
192
193   BufferPtr++;
194   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
195     const char C = *BufferPtr;
196     if (C == Quote && BufferPtr[-1] != '\\')
197       return BufferPtr;
198   }
199   return BufferEnd;
200 }
201
202 const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
203   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
204     if (!isWhitespace(*BufferPtr))
205       return BufferPtr;
206   }
207   return BufferEnd;
208 }
209
210 bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
211   return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
212 }
213
214 bool isCommandNameStartCharacter(char C) {
215   return isLetter(C);
216 }
217
218 bool isCommandNameCharacter(char C) {
219   return isAlphanumeric(C);
220 }
221
222 const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
223   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
224     if (!isCommandNameCharacter(*BufferPtr))
225       return BufferPtr;
226   }
227   return BufferEnd;
228 }
229
230 /// Return the one past end pointer for BCPL comments.
231 /// Handles newlines escaped with backslash or trigraph for backslahs.
232 const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
233   const char *CurPtr = BufferPtr;
234   while (CurPtr != BufferEnd) {
235     while (!isVerticalWhitespace(*CurPtr)) {
236       CurPtr++;
237       if (CurPtr == BufferEnd)
238         return BufferEnd;
239     }
240     // We found a newline, check if it is escaped.
241     const char *EscapePtr = CurPtr - 1;
242     while(isHorizontalWhitespace(*EscapePtr))
243       EscapePtr--;
244
245     if (*EscapePtr == '\\' ||
246         (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
247          EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
248       // We found an escaped newline.
249       CurPtr = skipNewline(CurPtr, BufferEnd);
250     } else
251       return CurPtr; // Not an escaped newline.
252   }
253   return BufferEnd;
254 }
255
256 /// Return the one past end pointer for C comments.
257 /// Very dumb, does not handle escaped newlines or trigraphs.
258 const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
259   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
260     if (*BufferPtr == '*') {
261       assert(BufferPtr + 1 != BufferEnd);
262       if (*(BufferPtr + 1) == '/')
263         return BufferPtr;
264     }
265   }
266   llvm_unreachable("buffer end hit before '*/' was seen");
267 }
268 } // unnamed namespace
269
270 void Lexer::lexCommentText(Token &T) {
271   assert(CommentState == LCS_InsideBCPLComment ||
272          CommentState == LCS_InsideCComment);
273
274   switch (State) {
275   case LS_Normal:
276     break;
277   case LS_VerbatimBlockFirstLine:
278     lexVerbatimBlockFirstLine(T);
279     return;
280   case LS_VerbatimBlockBody:
281     lexVerbatimBlockBody(T);
282     return;
283   case LS_VerbatimLineText:
284     lexVerbatimLineText(T);
285     return;
286   case LS_HTMLStartTag:
287     lexHTMLStartTag(T);
288     return;
289   case LS_HTMLEndTag:
290     lexHTMLEndTag(T);
291     return;
292   }
293
294   assert(State == LS_Normal);
295
296   const char *TokenPtr = BufferPtr;
297   assert(TokenPtr < CommentEnd);
298   while (TokenPtr != CommentEnd) {
299     switch(*TokenPtr) {
300       case '\\':
301       case '@': {
302         // Commands that start with a backslash and commands that start with
303         // 'at' have equivalent semantics.  But we keep information about the
304         // exact syntax in AST for comments.
305         tok::TokenKind CommandKind =
306             (*TokenPtr == '@') ? tok::at_command : tok::backslash_command;
307         TokenPtr++;
308         if (TokenPtr == CommentEnd) {
309           formTextToken(T, TokenPtr);
310           return;
311         }
312         char C = *TokenPtr;
313         switch (C) {
314         default:
315           break;
316
317         case '\\': case '@': case '&': case '$':
318         case '#':  case '<': case '>': case '%':
319         case '\"': case '.': case ':':
320           // This is one of \\ \@ \& \$ etc escape sequences.
321           TokenPtr++;
322           if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
323             // This is the \:: escape sequence.
324             TokenPtr++;
325           }
326           StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
327           formTokenWithChars(T, TokenPtr, tok::text);
328           T.setText(UnescapedText);
329           return;
330         }
331
332         // Don't make zero-length commands.
333         if (!isCommandNameStartCharacter(*TokenPtr)) {
334           formTextToken(T, TokenPtr);
335           return;
336         }
337
338         TokenPtr = skipCommandName(TokenPtr, CommentEnd);
339         unsigned Length = TokenPtr - (BufferPtr + 1);
340
341         // Hardcoded support for lexing LaTeX formula commands
342         // \f$ \f[ \f] \f{ \f} as a single command.
343         if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
344           C = *TokenPtr;
345           if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') {
346             TokenPtr++;
347             Length++;
348           }
349         }
350
351         const StringRef CommandName(BufferPtr + 1, Length);
352
353         const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName);
354         if (!Info) {
355           formTokenWithChars(T, TokenPtr, tok::unknown_command);
356           T.setUnknownCommandName(CommandName);
357           Diag(T.getLocation(), diag::warn_unknown_comment_command_name);
358           return;
359         }
360         if (Info->IsVerbatimBlockCommand) {
361           setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info);
362           return;
363         }
364         if (Info->IsVerbatimLineCommand) {
365           setupAndLexVerbatimLine(T, TokenPtr, Info);
366           return;
367         }
368         formTokenWithChars(T, TokenPtr, CommandKind);
369         T.setCommandID(Info->getID());
370         return;
371       }
372
373       case '&':
374         lexHTMLCharacterReference(T);
375         return;
376
377       case '<': {
378         TokenPtr++;
379         if (TokenPtr == CommentEnd) {
380           formTextToken(T, TokenPtr);
381           return;
382         }
383         const char C = *TokenPtr;
384         if (isHTMLIdentifierStartingCharacter(C))
385           setupAndLexHTMLStartTag(T);
386         else if (C == '/')
387           setupAndLexHTMLEndTag(T);
388         else
389           formTextToken(T, TokenPtr);
390
391         return;
392       }
393
394       case '\n':
395       case '\r':
396         TokenPtr = skipNewline(TokenPtr, CommentEnd);
397         formTokenWithChars(T, TokenPtr, tok::newline);
398
399         if (CommentState == LCS_InsideCComment)
400           skipLineStartingDecorations();
401         return;
402
403       default: {
404         size_t End = StringRef(TokenPtr, CommentEnd - TokenPtr).
405                          find_first_of("\n\r\\@&<");
406         if (End != StringRef::npos)
407           TokenPtr += End;
408         else
409           TokenPtr = CommentEnd;
410         formTextToken(T, TokenPtr);
411         return;
412       }
413     }
414   }
415 }
416
417 void Lexer::setupAndLexVerbatimBlock(Token &T,
418                                      const char *TextBegin,
419                                      char Marker, const CommandInfo *Info) {
420   assert(Info->IsVerbatimBlockCommand);
421
422   VerbatimBlockEndCommandName.clear();
423   VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
424   VerbatimBlockEndCommandName.append(Info->EndCommandName);
425
426   formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
427   T.setVerbatimBlockID(Info->getID());
428
429   // If there is a newline following the verbatim opening command, skip the
430   // newline so that we don't create an tok::verbatim_block_line with empty
431   // text content.
432   if (BufferPtr != CommentEnd &&
433       isVerticalWhitespace(*BufferPtr)) {
434     BufferPtr = skipNewline(BufferPtr, CommentEnd);
435     State = LS_VerbatimBlockBody;
436     return;
437   }
438
439   State = LS_VerbatimBlockFirstLine;
440 }
441
442 void Lexer::lexVerbatimBlockFirstLine(Token &T) {
443 again:
444   assert(BufferPtr < CommentEnd);
445
446   // FIXME: It would be better to scan the text once, finding either the block
447   // end command or newline.
448   //
449   // Extract current line.
450   const char *Newline = findNewline(BufferPtr, CommentEnd);
451   StringRef Line(BufferPtr, Newline - BufferPtr);
452
453   // Look for end command in current line.
454   size_t Pos = Line.find(VerbatimBlockEndCommandName);
455   const char *TextEnd;
456   const char *NextLine;
457   if (Pos == StringRef::npos) {
458     // Current line is completely verbatim.
459     TextEnd = Newline;
460     NextLine = skipNewline(Newline, CommentEnd);
461   } else if (Pos == 0) {
462     // Current line contains just an end command.
463     const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
464     StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
465     formTokenWithChars(T, End, tok::verbatim_block_end);
466     T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID());
467     State = LS_Normal;
468     return;
469   } else {
470     // There is some text, followed by end command.  Extract text first.
471     TextEnd = BufferPtr + Pos;
472     NextLine = TextEnd;
473     // If there is only whitespace before end command, skip whitespace.
474     if (isWhitespace(BufferPtr, TextEnd)) {
475       BufferPtr = TextEnd;
476       goto again;
477     }
478   }
479
480   StringRef Text(BufferPtr, TextEnd - BufferPtr);
481   formTokenWithChars(T, NextLine, tok::verbatim_block_line);
482   T.setVerbatimBlockText(Text);
483
484   State = LS_VerbatimBlockBody;
485 }
486
487 void Lexer::lexVerbatimBlockBody(Token &T) {
488   assert(State == LS_VerbatimBlockBody);
489
490   if (CommentState == LCS_InsideCComment)
491     skipLineStartingDecorations();
492
493   lexVerbatimBlockFirstLine(T);
494 }
495
496 void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin,
497                                     const CommandInfo *Info) {
498   assert(Info->IsVerbatimLineCommand);
499   formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
500   T.setVerbatimLineID(Info->getID());
501
502   State = LS_VerbatimLineText;
503 }
504
505 void Lexer::lexVerbatimLineText(Token &T) {
506   assert(State == LS_VerbatimLineText);
507
508   // Extract current line.
509   const char *Newline = findNewline(BufferPtr, CommentEnd);
510   const StringRef Text(BufferPtr, Newline - BufferPtr);
511   formTokenWithChars(T, Newline, tok::verbatim_line_text);
512   T.setVerbatimLineText(Text);
513
514   State = LS_Normal;
515 }
516
517 void Lexer::lexHTMLCharacterReference(Token &T) {
518   const char *TokenPtr = BufferPtr;
519   assert(*TokenPtr == '&');
520   TokenPtr++;
521   if (TokenPtr == CommentEnd) {
522     formTextToken(T, TokenPtr);
523     return;
524   }
525   const char *NamePtr;
526   bool isNamed = false;
527   bool isDecimal = false;
528   char C = *TokenPtr;
529   if (isHTMLNamedCharacterReferenceCharacter(C)) {
530     NamePtr = TokenPtr;
531     TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
532     isNamed = true;
533   } else if (C == '#') {
534     TokenPtr++;
535     if (TokenPtr == CommentEnd) {
536       formTextToken(T, TokenPtr);
537       return;
538     }
539     C = *TokenPtr;
540     if (isHTMLDecimalCharacterReferenceCharacter(C)) {
541       NamePtr = TokenPtr;
542       TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
543       isDecimal = true;
544     } else if (C == 'x' || C == 'X') {
545       TokenPtr++;
546       NamePtr = TokenPtr;
547       TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
548     } else {
549       formTextToken(T, TokenPtr);
550       return;
551     }
552   } else {
553     formTextToken(T, TokenPtr);
554     return;
555   }
556   if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
557       *TokenPtr != ';') {
558     formTextToken(T, TokenPtr);
559     return;
560   }
561   StringRef Name(NamePtr, TokenPtr - NamePtr);
562   TokenPtr++; // Skip semicolon.
563   StringRef Resolved;
564   if (isNamed)
565     Resolved = resolveHTMLNamedCharacterReference(Name);
566   else if (isDecimal)
567     Resolved = resolveHTMLDecimalCharacterReference(Name);
568   else
569     Resolved = resolveHTMLHexCharacterReference(Name);
570
571   if (Resolved.empty()) {
572     formTextToken(T, TokenPtr);
573     return;
574   }
575   formTokenWithChars(T, TokenPtr, tok::text);
576   T.setText(Resolved);
577   return;
578 }
579
580 void Lexer::setupAndLexHTMLStartTag(Token &T) {
581   assert(BufferPtr[0] == '<' &&
582          isHTMLIdentifierStartingCharacter(BufferPtr[1]));
583   const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
584   StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
585   if (!isHTMLTagName(Name)) {
586     formTextToken(T, TagNameEnd);
587     return;
588   }
589
590   formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
591   T.setHTMLTagStartName(Name);
592
593   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
594
595   const char C = *BufferPtr;
596   if (BufferPtr != CommentEnd &&
597       (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
598     State = LS_HTMLStartTag;
599 }
600
601 void Lexer::lexHTMLStartTag(Token &T) {
602   assert(State == LS_HTMLStartTag);
603
604   const char *TokenPtr = BufferPtr;
605   char C = *TokenPtr;
606   if (isHTMLIdentifierCharacter(C)) {
607     TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
608     StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
609     formTokenWithChars(T, TokenPtr, tok::html_ident);
610     T.setHTMLIdent(Ident);
611   } else {
612     switch (C) {
613     case '=':
614       TokenPtr++;
615       formTokenWithChars(T, TokenPtr, tok::html_equals);
616       break;
617     case '\"':
618     case '\'': {
619       const char *OpenQuote = TokenPtr;
620       TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
621       const char *ClosingQuote = TokenPtr;
622       if (TokenPtr != CommentEnd) // Skip closing quote.
623         TokenPtr++;
624       formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
625       T.setHTMLQuotedString(StringRef(OpenQuote + 1,
626                                       ClosingQuote - (OpenQuote + 1)));
627       break;
628     }
629     case '>':
630       TokenPtr++;
631       formTokenWithChars(T, TokenPtr, tok::html_greater);
632       State = LS_Normal;
633       return;
634     case '/':
635       TokenPtr++;
636       if (TokenPtr != CommentEnd && *TokenPtr == '>') {
637         TokenPtr++;
638         formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
639       } else
640         formTextToken(T, TokenPtr);
641
642       State = LS_Normal;
643       return;
644     }
645   }
646
647   // Now look ahead and return to normal state if we don't see any HTML tokens
648   // ahead.
649   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
650   if (BufferPtr == CommentEnd) {
651     State = LS_Normal;
652     return;
653   }
654
655   C = *BufferPtr;
656   if (!isHTMLIdentifierStartingCharacter(C) &&
657       C != '=' && C != '\"' && C != '\'' && C != '>') {
658     State = LS_Normal;
659     return;
660   }
661 }
662
663 void Lexer::setupAndLexHTMLEndTag(Token &T) {
664   assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
665
666   const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
667   const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
668   StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin);
669   if (!isHTMLTagName(Name)) {
670     formTextToken(T, TagNameEnd);
671     return;
672   }
673
674   const char *End = skipWhitespace(TagNameEnd, CommentEnd);
675
676   formTokenWithChars(T, End, tok::html_end_tag);
677   T.setHTMLTagEndName(Name);
678
679   if (BufferPtr != CommentEnd && *BufferPtr == '>')
680     State = LS_HTMLEndTag;
681 }
682
683 void Lexer::lexHTMLEndTag(Token &T) {
684   assert(BufferPtr != CommentEnd && *BufferPtr == '>');
685
686   formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
687   State = LS_Normal;
688 }
689
690 Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags,
691              const CommandTraits &Traits,
692              SourceLocation FileLoc,
693              const char *BufferStart, const char *BufferEnd):
694     Allocator(Allocator), Diags(Diags), Traits(Traits),
695     BufferStart(BufferStart), BufferEnd(BufferEnd),
696     FileLoc(FileLoc), BufferPtr(BufferStart),
697     CommentState(LCS_BeforeComment), State(LS_Normal) {
698 }
699
700 void Lexer::lex(Token &T) {
701 again:
702   switch (CommentState) {
703   case LCS_BeforeComment:
704     if (BufferPtr == BufferEnd) {
705       formTokenWithChars(T, BufferPtr, tok::eof);
706       return;
707     }
708
709     assert(*BufferPtr == '/');
710     BufferPtr++; // Skip first slash.
711     switch(*BufferPtr) {
712     case '/': { // BCPL comment.
713       BufferPtr++; // Skip second slash.
714
715       if (BufferPtr != BufferEnd) {
716         // Skip Doxygen magic marker, if it is present.
717         // It might be missing because of a typo //< or /*<, or because we
718         // merged this non-Doxygen comment into a bunch of Doxygen comments
719         // around it: /** ... */ /* ... */ /** ... */
720         const char C = *BufferPtr;
721         if (C == '/' || C == '!')
722           BufferPtr++;
723       }
724
725       // Skip less-than symbol that marks trailing comments.
726       // Skip it even if the comment is not a Doxygen one, because //< and /*<
727       // are frequent typos.
728       if (BufferPtr != BufferEnd && *BufferPtr == '<')
729         BufferPtr++;
730
731       CommentState = LCS_InsideBCPLComment;
732       if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
733         State = LS_Normal;
734       CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
735       goto again;
736     }
737     case '*': { // C comment.
738       BufferPtr++; // Skip star.
739
740       // Skip Doxygen magic marker.
741       const char C = *BufferPtr;
742       if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
743         BufferPtr++;
744
745       // Skip less-than symbol that marks trailing comments.
746       if (BufferPtr != BufferEnd && *BufferPtr == '<')
747         BufferPtr++;
748
749       CommentState = LCS_InsideCComment;
750       State = LS_Normal;
751       CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
752       goto again;
753     }
754     default:
755       llvm_unreachable("second character of comment should be '/' or '*'");
756     }
757
758   case LCS_BetweenComments: {
759     // Consecutive comments are extracted only if there is only whitespace
760     // between them.  So we can search for the start of the next comment.
761     const char *EndWhitespace = BufferPtr;
762     while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
763       EndWhitespace++;
764
765     // Turn any whitespace between comments (and there is only whitespace
766     // between them -- guaranteed by comment extraction) into a newline.  We
767     // have two newlines between C comments in total (first one was synthesized
768     // after a comment).
769     formTokenWithChars(T, EndWhitespace, tok::newline);
770
771     CommentState = LCS_BeforeComment;
772     break;
773   }
774
775   case LCS_InsideBCPLComment:
776   case LCS_InsideCComment:
777     if (BufferPtr != CommentEnd) {
778       lexCommentText(T);
779       break;
780     } else {
781       // Skip C comment closing sequence.
782       if (CommentState == LCS_InsideCComment) {
783         assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
784         BufferPtr += 2;
785         assert(BufferPtr <= BufferEnd);
786
787         // Synthenize newline just after the C comment, regardless if there is
788         // actually a newline.
789         formTokenWithChars(T, BufferPtr, tok::newline);
790
791         CommentState = LCS_BetweenComments;
792         break;
793       } else {
794         // Don't synthesized a newline after BCPL comment.
795         CommentState = LCS_BetweenComments;
796         goto again;
797       }
798     }
799   }
800 }
801
802 StringRef Lexer::getSpelling(const Token &Tok,
803                              const SourceManager &SourceMgr,
804                              bool *Invalid) const {
805   SourceLocation Loc = Tok.getLocation();
806   std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
807
808   bool InvalidTemp = false;
809   StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
810   if (InvalidTemp) {
811     *Invalid = true;
812     return StringRef();
813   }
814
815   const char *Begin = File.data() + LocInfo.second;
816   return StringRef(Begin, Tok.getLength());
817 }
818
819 } // end namespace comments
820 } // end namespace clang
821