10a4f3f43SEugene Zelenko //===--- CommentLexer.cpp -------------------------------------------------===//
20a4f3f43SEugene Zelenko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60a4f3f43SEugene Zelenko //
70a4f3f43SEugene Zelenko //===----------------------------------------------------------------------===//
80a4f3f43SEugene Zelenko 
95188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h"
10ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
116738e430SFariborz Jahanian #include "clang/AST/CommentDiagnostic.h"
12bcef3411SDmitri Gribenko #include "clang/Basic/CharInfo.h"
13b2e5482bSDmitri Gribenko #include "llvm/ADT/StringExtras.h"
145188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h"
159feeef40SDmitri Gribenko #include "llvm/Support/ConvertUTF.h"
165188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h"
175188c4b9SDmitri Gribenko 
185188c4b9SDmitri Gribenko namespace clang {
195188c4b9SDmitri Gribenko namespace comments {
205188c4b9SDmitri Gribenko 
dump(const Lexer & L,const SourceManager & SM) const215188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const {
225188c4b9SDmitri Gribenko   llvm::errs() << "comments::Token Kind=" << Kind << " ";
233124ce72SStephen Kelly   Loc.print(llvm::errs(), SM);
245188c4b9SDmitri Gribenko   llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
255188c4b9SDmitri Gribenko }
265188c4b9SDmitri Gribenko 
isHTMLNamedCharacterReferenceCharacter(char C)2774f4d028SDmitri Gribenko static inline bool isHTMLNamedCharacterReferenceCharacter(char C) {
28bcef3411SDmitri Gribenko   return isLetter(C);
294586df76SDmitri Gribenko }
304586df76SDmitri Gribenko 
isHTMLDecimalCharacterReferenceCharacter(char C)3174f4d028SDmitri Gribenko static inline bool isHTMLDecimalCharacterReferenceCharacter(char C) {
32bcef3411SDmitri Gribenko   return isDigit(C);
334586df76SDmitri Gribenko }
344586df76SDmitri Gribenko 
isHTMLHexCharacterReferenceCharacter(char C)3574f4d028SDmitri Gribenko static inline bool isHTMLHexCharacterReferenceCharacter(char C) {
36bcef3411SDmitri Gribenko   return isHexDigit(C);
374586df76SDmitri Gribenko }
38107618a6SDmitri Gribenko 
convertCodePointToUTF8(llvm::BumpPtrAllocator & Allocator,unsigned CodePoint)3974f4d028SDmitri Gribenko static inline StringRef convertCodePointToUTF8(
4074f4d028SDmitri Gribenko                                       llvm::BumpPtrAllocator &Allocator,
4128800da1SDmitri Gribenko                                       unsigned CodePoint) {
424586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
434586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
449feeef40SDmitri Gribenko   if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
454586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
464586df76SDmitri Gribenko   else
474586df76SDmitri Gribenko     return StringRef();
484586df76SDmitri Gribenko }
494586df76SDmitri Gribenko 
5074f4d028SDmitri Gribenko namespace {
5174f4d028SDmitri Gribenko 
5228800da1SDmitri Gribenko #include "clang/AST/CommentHTMLTags.inc"
5328800da1SDmitri Gribenko #include "clang/AST/CommentHTMLNamedCharacterReferences.inc"
5428800da1SDmitri Gribenko 
550a4f3f43SEugene Zelenko } // end anonymous namespace
567b3ae190SFariborz Jahanian 
resolveHTMLNamedCharacterReference(StringRef Name) const577b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
5828800da1SDmitri Gribenko   // Fast path, first check a few most widely used named character references.
597b3ae190SFariborz Jahanian   return llvm::StringSwitch<StringRef>(Name)
607b3ae190SFariborz Jahanian       .Case("amp", "&")
617b3ae190SFariborz Jahanian       .Case("lt", "<")
627b3ae190SFariborz Jahanian       .Case("gt", ">")
637b3ae190SFariborz Jahanian       .Case("quot", "\"")
647b3ae190SFariborz Jahanian       .Case("apos", "\'")
6528800da1SDmitri Gribenko       // Slow path.
6628800da1SDmitri Gribenko       .Default(translateHTMLNamedCharacterReferenceToUTF8(Name));
677b3ae190SFariborz Jahanian }
687b3ae190SFariborz Jahanian 
resolveHTMLDecimalCharacterReference(StringRef Name) const697b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
704586df76SDmitri Gribenko   unsigned CodePoint = 0;
714586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
727b3ae190SFariborz Jahanian     assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
737b3ae190SFariborz Jahanian     CodePoint *= 10;
747b3ae190SFariborz Jahanian     CodePoint += Name[i] - '0';
754586df76SDmitri Gribenko   }
7628800da1SDmitri Gribenko   return convertCodePointToUTF8(Allocator, CodePoint);
7728800da1SDmitri Gribenko }
784586df76SDmitri Gribenko 
resolveHTMLHexCharacterReference(StringRef Name) const7928800da1SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
8028800da1SDmitri Gribenko   unsigned CodePoint = 0;
8128800da1SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
8228800da1SDmitri Gribenko     CodePoint *= 16;
8328800da1SDmitri Gribenko     const char C = Name[i];
8428800da1SDmitri Gribenko     assert(isHTMLHexCharacterReferenceCharacter(C));
8528800da1SDmitri Gribenko     CodePoint += llvm::hexDigitValue(C);
8628800da1SDmitri Gribenko   }
8728800da1SDmitri Gribenko   return convertCodePointToUTF8(Allocator, CodePoint);
884586df76SDmitri Gribenko }
894586df76SDmitri Gribenko 
skipLineStartingDecorations()905188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() {
915188c4b9SDmitri Gribenko   // This function should be called only for C comments
925188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideCComment);
935188c4b9SDmitri Gribenko 
945188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd)
955188c4b9SDmitri Gribenko     return;
965188c4b9SDmitri Gribenko 
975188c4b9SDmitri Gribenko   const char *NewBufferPtr = BufferPtr;
98bd0a970fSAaron Puchert   while (isHorizontalWhitespace(*NewBufferPtr))
99bd0a970fSAaron Puchert     if (++NewBufferPtr == CommentEnd)
1005188c4b9SDmitri Gribenko       return;
101bd0a970fSAaron Puchert   if (*NewBufferPtr == '*')
1025188c4b9SDmitri Gribenko     BufferPtr = NewBufferPtr + 1;
1035188c4b9SDmitri Gribenko }
1045188c4b9SDmitri Gribenko 
1055188c4b9SDmitri Gribenko namespace {
106ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string.
findNewline(const char * BufferPtr,const char * BufferEnd)1075188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
1085188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
109bcef3411SDmitri Gribenko     if (isVerticalWhitespace(*BufferPtr))
1105188c4b9SDmitri Gribenko       return BufferPtr;
1115188c4b9SDmitri Gribenko   }
1125188c4b9SDmitri Gribenko   return BufferEnd;
1135188c4b9SDmitri Gribenko }
1145188c4b9SDmitri Gribenko 
skipNewline(const char * BufferPtr,const char * BufferEnd)1155188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
1165188c4b9SDmitri Gribenko   if (BufferPtr == BufferEnd)
1175188c4b9SDmitri Gribenko     return BufferPtr;
1185188c4b9SDmitri Gribenko 
1195188c4b9SDmitri Gribenko   if (*BufferPtr == '\n')
1205188c4b9SDmitri Gribenko     BufferPtr++;
1215188c4b9SDmitri Gribenko   else {
1225188c4b9SDmitri Gribenko     assert(*BufferPtr == '\r');
1235188c4b9SDmitri Gribenko     BufferPtr++;
1245188c4b9SDmitri Gribenko     if (BufferPtr != BufferEnd && *BufferPtr == '\n')
1255188c4b9SDmitri Gribenko       BufferPtr++;
1265188c4b9SDmitri Gribenko   }
1275188c4b9SDmitri Gribenko   return BufferPtr;
1285188c4b9SDmitri Gribenko }
1295188c4b9SDmitri Gribenko 
skipNamedCharacterReference(const char * BufferPtr,const char * BufferEnd)1304586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr,
1314586df76SDmitri Gribenko                                         const char *BufferEnd) {
1324586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1334586df76SDmitri Gribenko     if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
1344586df76SDmitri Gribenko       return BufferPtr;
1354586df76SDmitri Gribenko   }
1364586df76SDmitri Gribenko   return BufferEnd;
1374586df76SDmitri Gribenko }
1384586df76SDmitri Gribenko 
skipDecimalCharacterReference(const char * BufferPtr,const char * BufferEnd)1394586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr,
1404586df76SDmitri Gribenko                                           const char *BufferEnd) {
1414586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1424586df76SDmitri Gribenko     if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
1434586df76SDmitri Gribenko       return BufferPtr;
1444586df76SDmitri Gribenko   }
1454586df76SDmitri Gribenko   return BufferEnd;
1464586df76SDmitri Gribenko }
1474586df76SDmitri Gribenko 
skipHexCharacterReference(const char * BufferPtr,const char * BufferEnd)1484586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr,
1494586df76SDmitri Gribenko                                       const char *BufferEnd) {
1504586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1514586df76SDmitri Gribenko     if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
1524586df76SDmitri Gribenko       return BufferPtr;
1534586df76SDmitri Gribenko   }
1544586df76SDmitri Gribenko   return BufferEnd;
1554586df76SDmitri Gribenko }
1564586df76SDmitri Gribenko 
isHTMLIdentifierStartingCharacter(char C)15717709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) {
158bcef3411SDmitri Gribenko   return isLetter(C);
15917709ae8SDmitri Gribenko }
16017709ae8SDmitri Gribenko 
isHTMLIdentifierCharacter(char C)1615188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) {
162bcef3411SDmitri Gribenko   return isAlphanumeric(C);
1635188c4b9SDmitri Gribenko }
1645188c4b9SDmitri Gribenko 
skipHTMLIdentifier(const char * BufferPtr,const char * BufferEnd)1655188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
1665188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1675188c4b9SDmitri Gribenko     if (!isHTMLIdentifierCharacter(*BufferPtr))
1685188c4b9SDmitri Gribenko       return BufferPtr;
1695188c4b9SDmitri Gribenko   }
1705188c4b9SDmitri Gribenko   return BufferEnd;
1715188c4b9SDmitri Gribenko }
1725188c4b9SDmitri Gribenko 
1735188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
1745188c4b9SDmitri Gribenko /// string allowed.
1755188c4b9SDmitri Gribenko ///
1765188c4b9SDmitri Gribenko /// Returns pointer to closing quote.
skipHTMLQuotedString(const char * BufferPtr,const char * BufferEnd)1775188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
1785188c4b9SDmitri Gribenko {
1795188c4b9SDmitri Gribenko   const char Quote = *BufferPtr;
1805188c4b9SDmitri Gribenko   assert(Quote == '\"' || Quote == '\'');
1815188c4b9SDmitri Gribenko 
1825188c4b9SDmitri Gribenko   BufferPtr++;
1835188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1845188c4b9SDmitri Gribenko     const char C = *BufferPtr;
1855188c4b9SDmitri Gribenko     if (C == Quote && BufferPtr[-1] != '\\')
1865188c4b9SDmitri Gribenko       return BufferPtr;
1875188c4b9SDmitri Gribenko   }
1885188c4b9SDmitri Gribenko   return BufferEnd;
1895188c4b9SDmitri Gribenko }
1905188c4b9SDmitri Gribenko 
skipWhitespace(const char * BufferPtr,const char * BufferEnd)1915188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
1925188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1935188c4b9SDmitri Gribenko     if (!isWhitespace(*BufferPtr))
1945188c4b9SDmitri Gribenko       return BufferPtr;
1955188c4b9SDmitri Gribenko   }
1965188c4b9SDmitri Gribenko   return BufferEnd;
1975188c4b9SDmitri Gribenko }
1985188c4b9SDmitri Gribenko 
isWhitespace(const char * BufferPtr,const char * BufferEnd)199e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
200e4a3997dSDmitri Gribenko   return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
201e4a3997dSDmitri Gribenko }
202e4a3997dSDmitri Gribenko 
isCommandNameStartCharacter(char C)203ad45ad6fSDmitri Gribenko bool isCommandNameStartCharacter(char C) {
204bcef3411SDmitri Gribenko   return isLetter(C);
205ad45ad6fSDmitri Gribenko }
206ad45ad6fSDmitri Gribenko 
isCommandNameCharacter(char C)2075188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) {
208bcef3411SDmitri Gribenko   return isAlphanumeric(C);
2095188c4b9SDmitri Gribenko }
2105188c4b9SDmitri Gribenko 
skipCommandName(const char * BufferPtr,const char * BufferEnd)2115188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
2125188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2135188c4b9SDmitri Gribenko     if (!isCommandNameCharacter(*BufferPtr))
2145188c4b9SDmitri Gribenko       return BufferPtr;
2155188c4b9SDmitri Gribenko   }
2165188c4b9SDmitri Gribenko   return BufferEnd;
2175188c4b9SDmitri Gribenko }
2185188c4b9SDmitri Gribenko 
2195188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments.
2205188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs.
findBCPLCommentEnd(const char * BufferPtr,const char * BufferEnd)2215188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
2225188c4b9SDmitri Gribenko   const char *CurPtr = BufferPtr;
2235188c4b9SDmitri Gribenko   while (CurPtr != BufferEnd) {
224bcef3411SDmitri Gribenko     while (!isVerticalWhitespace(*CurPtr)) {
2255188c4b9SDmitri Gribenko       CurPtr++;
2265188c4b9SDmitri Gribenko       if (CurPtr == BufferEnd)
2275188c4b9SDmitri Gribenko         return BufferEnd;
2285188c4b9SDmitri Gribenko     }
2295188c4b9SDmitri Gribenko     // We found a newline, check if it is escaped.
2305188c4b9SDmitri Gribenko     const char *EscapePtr = CurPtr - 1;
2315188c4b9SDmitri Gribenko     while(isHorizontalWhitespace(*EscapePtr))
2325188c4b9SDmitri Gribenko       EscapePtr--;
2335188c4b9SDmitri Gribenko 
2345188c4b9SDmitri Gribenko     if (*EscapePtr == '\\' ||
2355188c4b9SDmitri Gribenko         (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
2365188c4b9SDmitri Gribenko          EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
2375188c4b9SDmitri Gribenko       // We found an escaped newline.
2385188c4b9SDmitri Gribenko       CurPtr = skipNewline(CurPtr, BufferEnd);
2395188c4b9SDmitri Gribenko     } else
2405188c4b9SDmitri Gribenko       return CurPtr; // Not an escaped newline.
2415188c4b9SDmitri Gribenko   }
2425188c4b9SDmitri Gribenko   return BufferEnd;
2435188c4b9SDmitri Gribenko }
2445188c4b9SDmitri Gribenko 
2455188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments.
2465188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs.
findCCommentEnd(const char * BufferPtr,const char * BufferEnd)2475188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
2485188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2495188c4b9SDmitri Gribenko     if (*BufferPtr == '*') {
2505188c4b9SDmitri Gribenko       assert(BufferPtr + 1 != BufferEnd);
2515188c4b9SDmitri Gribenko       if (*(BufferPtr + 1) == '/')
2525188c4b9SDmitri Gribenko         return BufferPtr;
2535188c4b9SDmitri Gribenko     }
2545188c4b9SDmitri Gribenko   }
2555188c4b9SDmitri Gribenko   llvm_unreachable("buffer end hit before '*/' was seen");
2565188c4b9SDmitri Gribenko }
2576c7a1666SFariborz Jahanian 
2580a4f3f43SEugene Zelenko } // end anonymous namespace
2595188c4b9SDmitri Gribenko 
formTokenWithChars(Token & Result,const char * TokEnd,tok::TokenKind Kind)2603ffab05fSAlp Toker void Lexer::formTokenWithChars(Token &Result, const char *TokEnd,
2613ffab05fSAlp Toker                                tok::TokenKind Kind) {
2623ffab05fSAlp Toker   const unsigned TokLen = TokEnd - BufferPtr;
2633ffab05fSAlp Toker   Result.setLocation(getSourceLocation(BufferPtr));
2643ffab05fSAlp Toker   Result.setKind(Kind);
2653ffab05fSAlp Toker   Result.setLength(TokLen);
2663ffab05fSAlp Toker #ifndef NDEBUG
2673ffab05fSAlp Toker   Result.TextPtr = "<UNSET>";
2683ffab05fSAlp Toker   Result.IntVal = 7;
2693ffab05fSAlp Toker #endif
2703ffab05fSAlp Toker   BufferPtr = TokEnd;
2713ffab05fSAlp Toker }
2723ffab05fSAlp Toker 
skipTextToken()273*9f0fa654SAaron Puchert const char *Lexer::skipTextToken() {
274*9f0fa654SAaron Puchert   const char *TokenPtr = BufferPtr;
275*9f0fa654SAaron Puchert   assert(TokenPtr < CommentEnd);
276*9f0fa654SAaron Puchert   StringRef TokStartSymbols = ParseCommands ? "\n\r\\@\"&<" : "\n\r";
277*9f0fa654SAaron Puchert 
278*9f0fa654SAaron Puchert again:
279*9f0fa654SAaron Puchert   size_t End =
280*9f0fa654SAaron Puchert       StringRef(TokenPtr, CommentEnd - TokenPtr).find_first_of(TokStartSymbols);
281*9f0fa654SAaron Puchert   if (End == StringRef::npos)
282*9f0fa654SAaron Puchert     return CommentEnd;
283*9f0fa654SAaron Puchert 
284*9f0fa654SAaron Puchert   // Doxygen doesn't recognize any commands in a one-line double quotation.
285*9f0fa654SAaron Puchert   // If we don't find an ending quotation mark, we pretend it never began.
286*9f0fa654SAaron Puchert   if (*(TokenPtr + End) == '\"') {
287*9f0fa654SAaron Puchert     TokenPtr += End + 1;
288*9f0fa654SAaron Puchert     End = StringRef(TokenPtr, CommentEnd - TokenPtr).find_first_of("\n\r\"");
289*9f0fa654SAaron Puchert     if (End != StringRef::npos && *(TokenPtr + End) == '\"')
290*9f0fa654SAaron Puchert       TokenPtr += End + 1;
291*9f0fa654SAaron Puchert     goto again;
292*9f0fa654SAaron Puchert   }
293*9f0fa654SAaron Puchert   return TokenPtr + End;
294*9f0fa654SAaron Puchert }
295*9f0fa654SAaron Puchert 
lexCommentText(Token & T)2965188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) {
2975188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideBCPLComment ||
2985188c4b9SDmitri Gribenko          CommentState == LCS_InsideCComment);
2995188c4b9SDmitri Gribenko 
3001ff7c32fSIlya Biryukov   // Handles lexing non-command text, i.e. text and newline.
3011ff7c32fSIlya Biryukov   auto HandleNonCommandToken = [&]() -> void {
3021ff7c32fSIlya Biryukov     assert(State == LS_Normal);
3031ff7c32fSIlya Biryukov 
3041ff7c32fSIlya Biryukov     const char *TokenPtr = BufferPtr;
3051ff7c32fSIlya Biryukov     assert(TokenPtr < CommentEnd);
3061ff7c32fSIlya Biryukov     switch (*TokenPtr) {
3071ff7c32fSIlya Biryukov       case '\n':
3081ff7c32fSIlya Biryukov       case '\r':
3091ff7c32fSIlya Biryukov           TokenPtr = skipNewline(TokenPtr, CommentEnd);
3101ff7c32fSIlya Biryukov           formTokenWithChars(T, TokenPtr, tok::newline);
3111ff7c32fSIlya Biryukov 
3121ff7c32fSIlya Biryukov           if (CommentState == LCS_InsideCComment)
3131ff7c32fSIlya Biryukov             skipLineStartingDecorations();
3141ff7c32fSIlya Biryukov           return;
3151ff7c32fSIlya Biryukov 
316*9f0fa654SAaron Puchert       default:
317*9f0fa654SAaron Puchert         return formTextToken(T, skipTextToken());
3181ff7c32fSIlya Biryukov     }
3191ff7c32fSIlya Biryukov   };
3201ff7c32fSIlya Biryukov 
3211ff7c32fSIlya Biryukov   if (!ParseCommands)
3221ff7c32fSIlya Biryukov     return HandleNonCommandToken();
3231ff7c32fSIlya Biryukov 
3245188c4b9SDmitri Gribenko   switch (State) {
3255188c4b9SDmitri Gribenko   case LS_Normal:
3265188c4b9SDmitri Gribenko     break;
3275188c4b9SDmitri Gribenko   case LS_VerbatimBlockFirstLine:
3285188c4b9SDmitri Gribenko     lexVerbatimBlockFirstLine(T);
3295188c4b9SDmitri Gribenko     return;
3305188c4b9SDmitri Gribenko   case LS_VerbatimBlockBody:
3315188c4b9SDmitri Gribenko     lexVerbatimBlockBody(T);
3325188c4b9SDmitri Gribenko     return;
3331669f702SDmitri Gribenko   case LS_VerbatimLineText:
3341669f702SDmitri Gribenko     lexVerbatimLineText(T);
3351669f702SDmitri Gribenko     return;
336e00ffc7bSDmitri Gribenko   case LS_HTMLStartTag:
337e00ffc7bSDmitri Gribenko     lexHTMLStartTag(T);
3385188c4b9SDmitri Gribenko     return;
339e00ffc7bSDmitri Gribenko   case LS_HTMLEndTag:
340e00ffc7bSDmitri Gribenko     lexHTMLEndTag(T);
341ec92531cSDmitri Gribenko     return;
3425188c4b9SDmitri Gribenko   }
3435188c4b9SDmitri Gribenko 
3445188c4b9SDmitri Gribenko   assert(State == LS_Normal);
3455188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
3465188c4b9SDmitri Gribenko   assert(TokenPtr < CommentEnd);
3475188c4b9SDmitri Gribenko   switch(*TokenPtr) {
3485188c4b9SDmitri Gribenko     case '\\':
3495188c4b9SDmitri Gribenko     case '@': {
350bcf7f4d3SDmitri Gribenko       // Commands that start with a backslash and commands that start with
351bcf7f4d3SDmitri Gribenko       // 'at' have equivalent semantics.  But we keep information about the
352bcf7f4d3SDmitri Gribenko       // exact syntax in AST for comments.
353bcf7f4d3SDmitri Gribenko       tok::TokenKind CommandKind =
354bcf7f4d3SDmitri Gribenko           (*TokenPtr == '@') ? tok::at_command : tok::backslash_command;
3555188c4b9SDmitri Gribenko       TokenPtr++;
3565188c4b9SDmitri Gribenko       if (TokenPtr == CommentEnd) {
3574586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
3585188c4b9SDmitri Gribenko         return;
3595188c4b9SDmitri Gribenko       }
3605188c4b9SDmitri Gribenko       char C = *TokenPtr;
3615188c4b9SDmitri Gribenko       switch (C) {
3625188c4b9SDmitri Gribenko       default:
3635188c4b9SDmitri Gribenko         break;
3645188c4b9SDmitri Gribenko 
3655188c4b9SDmitri Gribenko       case '\\': case '@': case '&': case '$':
3665188c4b9SDmitri Gribenko       case '#':  case '<': case '>': case '%':
3675188c4b9SDmitri Gribenko       case '\"': case '.': case ':':
3685188c4b9SDmitri Gribenko         // This is one of \\ \@ \& \$ etc escape sequences.
3695188c4b9SDmitri Gribenko         TokenPtr++;
3705188c4b9SDmitri Gribenko         if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
3715188c4b9SDmitri Gribenko           // This is the \:: escape sequence.
3725188c4b9SDmitri Gribenko           TokenPtr++;
3735188c4b9SDmitri Gribenko         }
37460ddd8a1SDmitri Gribenko         StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
3755188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::text);
37660ddd8a1SDmitri Gribenko         T.setText(UnescapedText);
3775188c4b9SDmitri Gribenko         return;
3785188c4b9SDmitri Gribenko       }
3795188c4b9SDmitri Gribenko 
3805188c4b9SDmitri Gribenko       // Don't make zero-length commands.
381ad45ad6fSDmitri Gribenko       if (!isCommandNameStartCharacter(*TokenPtr)) {
3824586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
3835188c4b9SDmitri Gribenko         return;
3845188c4b9SDmitri Gribenko       }
3855188c4b9SDmitri Gribenko 
3865188c4b9SDmitri Gribenko       TokenPtr = skipCommandName(TokenPtr, CommentEnd);
3875188c4b9SDmitri Gribenko       unsigned Length = TokenPtr - (BufferPtr + 1);
3885188c4b9SDmitri Gribenko 
3895188c4b9SDmitri Gribenko       // Hardcoded support for lexing LaTeX formula commands
390196554d4SAaron Puchert       // \f$ \f( \f) \f[ \f] \f{ \f} as a single command.
3915188c4b9SDmitri Gribenko       if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
3925188c4b9SDmitri Gribenko         C = *TokenPtr;
393196554d4SAaron Puchert         if (C == '$' || C == '(' || C == ')' || C == '[' || C == ']' ||
394196554d4SAaron Puchert             C == '{' || C == '}') {
3955188c4b9SDmitri Gribenko           TokenPtr++;
3965188c4b9SDmitri Gribenko           Length++;
3975188c4b9SDmitri Gribenko         }
3985188c4b9SDmitri Gribenko       }
3995188c4b9SDmitri Gribenko 
400bf3e3270SCraig Topper       StringRef CommandName(BufferPtr + 1, Length);
4015188c4b9SDmitri Gribenko 
4027acbf00fSDmitri Gribenko       const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName);
4037acbf00fSDmitri Gribenko       if (!Info) {
404ebb262f7SFariborz Jahanian         if ((Info = Traits.getTypoCorrectCommandInfo(CommandName))) {
4056c7a1666SFariborz Jahanian           StringRef CorrectedName = Info->Name;
4062dece574SBenjamin Kramer           SourceLocation Loc = getSourceLocation(BufferPtr);
40749082374SErik Verbruggen           SourceLocation EndLoc = getSourceLocation(TokenPtr);
40849082374SErik Verbruggen           SourceRange FullRange = SourceRange(Loc, EndLoc);
40949082374SErik Verbruggen           SourceRange CommandRange(Loc.getLocWithOffset(1), EndLoc);
4102dece574SBenjamin Kramer           Diag(Loc, diag::warn_correct_comment_command_name)
41149082374SErik Verbruggen             << FullRange << CommandName << CorrectedName
4126c7a1666SFariborz Jahanian             << FixItHint::CreateReplacement(CommandRange, CorrectedName);
4136c7a1666SFariborz Jahanian         } else {
4142dece574SBenjamin Kramer           formTokenWithChars(T, TokenPtr, tok::unknown_command);
4152dece574SBenjamin Kramer           T.setUnknownCommandName(CommandName);
41649082374SErik Verbruggen           Diag(T.getLocation(), diag::warn_unknown_comment_command_name)
41749082374SErik Verbruggen               << SourceRange(T.getLocation(), T.getEndLocation());
4185188c4b9SDmitri Gribenko           return;
4195188c4b9SDmitri Gribenko         }
4206c7a1666SFariborz Jahanian       }
4217acbf00fSDmitri Gribenko       if (Info->IsVerbatimBlockCommand) {
4227acbf00fSDmitri Gribenko         setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info);
4237acbf00fSDmitri Gribenko         return;
4247acbf00fSDmitri Gribenko       }
4257acbf00fSDmitri Gribenko       if (Info->IsVerbatimLineCommand) {
4267acbf00fSDmitri Gribenko         setupAndLexVerbatimLine(T, TokenPtr, Info);
4275188c4b9SDmitri Gribenko         return;
4285188c4b9SDmitri Gribenko       }
429bcf7f4d3SDmitri Gribenko       formTokenWithChars(T, TokenPtr, CommandKind);
4307acbf00fSDmitri Gribenko       T.setCommandID(Info->getID());
4315188c4b9SDmitri Gribenko       return;
4325188c4b9SDmitri Gribenko     }
4335188c4b9SDmitri Gribenko 
4344586df76SDmitri Gribenko     case '&':
4354586df76SDmitri Gribenko       lexHTMLCharacterReference(T);
4364586df76SDmitri Gribenko       return;
4374586df76SDmitri Gribenko 
4385188c4b9SDmitri Gribenko     case '<': {
4395188c4b9SDmitri Gribenko       TokenPtr++;
4405188c4b9SDmitri Gribenko       if (TokenPtr == CommentEnd) {
4414586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
4425188c4b9SDmitri Gribenko         return;
4435188c4b9SDmitri Gribenko       }
4445188c4b9SDmitri Gribenko       const char C = *TokenPtr;
44517709ae8SDmitri Gribenko       if (isHTMLIdentifierStartingCharacter(C))
446e00ffc7bSDmitri Gribenko         setupAndLexHTMLStartTag(T);
4475188c4b9SDmitri Gribenko       else if (C == '/')
448e00ffc7bSDmitri Gribenko         setupAndLexHTMLEndTag(T);
4494586df76SDmitri Gribenko       else
4504586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
4515188c4b9SDmitri Gribenko       return;
4525188c4b9SDmitri Gribenko     }
4535188c4b9SDmitri Gribenko 
4541ff7c32fSIlya Biryukov     default:
4551ff7c32fSIlya Biryukov       return HandleNonCommandToken();
4565188c4b9SDmitri Gribenko   }
4575188c4b9SDmitri Gribenko }
4585188c4b9SDmitri Gribenko 
setupAndLexVerbatimBlock(Token & T,const char * TextBegin,char Marker,const CommandInfo * Info)4595188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T,
4605188c4b9SDmitri Gribenko                                      const char *TextBegin,
4617acbf00fSDmitri Gribenko                                      char Marker, const CommandInfo *Info) {
4627acbf00fSDmitri Gribenko   assert(Info->IsVerbatimBlockCommand);
4637acbf00fSDmitri Gribenko 
4645188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.clear();
4655188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
4667acbf00fSDmitri Gribenko   VerbatimBlockEndCommandName.append(Info->EndCommandName);
4675188c4b9SDmitri Gribenko 
4685188c4b9SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
4697acbf00fSDmitri Gribenko   T.setVerbatimBlockID(Info->getID());
4705188c4b9SDmitri Gribenko 
471ec92531cSDmitri Gribenko   // If there is a newline following the verbatim opening command, skip the
472ec92531cSDmitri Gribenko   // newline so that we don't create an tok::verbatim_block_line with empty
473ec92531cSDmitri Gribenko   // text content.
474bcef3411SDmitri Gribenko   if (BufferPtr != CommentEnd &&
475bcef3411SDmitri Gribenko       isVerticalWhitespace(*BufferPtr)) {
476ec92531cSDmitri Gribenko     BufferPtr = skipNewline(BufferPtr, CommentEnd);
477ec92531cSDmitri Gribenko     State = LS_VerbatimBlockBody;
478ec92531cSDmitri Gribenko     return;
479ec92531cSDmitri Gribenko   }
480ec92531cSDmitri Gribenko 
4815188c4b9SDmitri Gribenko   State = LS_VerbatimBlockFirstLine;
4825188c4b9SDmitri Gribenko }
4835188c4b9SDmitri Gribenko 
lexVerbatimBlockFirstLine(Token & T)4845188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) {
485e4a3997dSDmitri Gribenko again:
4865188c4b9SDmitri Gribenko   assert(BufferPtr < CommentEnd);
4875188c4b9SDmitri Gribenko 
4885188c4b9SDmitri Gribenko   // FIXME: It would be better to scan the text once, finding either the block
4895188c4b9SDmitri Gribenko   // end command or newline.
4905188c4b9SDmitri Gribenko   //
4915188c4b9SDmitri Gribenko   // Extract current line.
4925188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
4935188c4b9SDmitri Gribenko   StringRef Line(BufferPtr, Newline - BufferPtr);
4945188c4b9SDmitri Gribenko 
4955188c4b9SDmitri Gribenko   // Look for end command in current line.
4965188c4b9SDmitri Gribenko   size_t Pos = Line.find(VerbatimBlockEndCommandName);
497ec92531cSDmitri Gribenko   const char *TextEnd;
4985188c4b9SDmitri Gribenko   const char *NextLine;
4995188c4b9SDmitri Gribenko   if (Pos == StringRef::npos) {
5005188c4b9SDmitri Gribenko     // Current line is completely verbatim.
501ec92531cSDmitri Gribenko     TextEnd = Newline;
5025188c4b9SDmitri Gribenko     NextLine = skipNewline(Newline, CommentEnd);
5035188c4b9SDmitri Gribenko   } else if (Pos == 0) {
5045188c4b9SDmitri Gribenko     // Current line contains just an end command.
5055188c4b9SDmitri Gribenko     const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
50660ddd8a1SDmitri Gribenko     StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
5075188c4b9SDmitri Gribenko     formTokenWithChars(T, End, tok::verbatim_block_end);
5087acbf00fSDmitri Gribenko     T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID());
5095188c4b9SDmitri Gribenko     State = LS_Normal;
5105188c4b9SDmitri Gribenko     return;
5115188c4b9SDmitri Gribenko   } else {
5125188c4b9SDmitri Gribenko     // There is some text, followed by end command.  Extract text first.
513ec92531cSDmitri Gribenko     TextEnd = BufferPtr + Pos;
514ec92531cSDmitri Gribenko     NextLine = TextEnd;
515e4a3997dSDmitri Gribenko     // If there is only whitespace before end command, skip whitespace.
516e4a3997dSDmitri Gribenko     if (isWhitespace(BufferPtr, TextEnd)) {
517e4a3997dSDmitri Gribenko       BufferPtr = TextEnd;
518e4a3997dSDmitri Gribenko       goto again;
519e4a3997dSDmitri Gribenko     }
5205188c4b9SDmitri Gribenko   }
5215188c4b9SDmitri Gribenko 
522ec92531cSDmitri Gribenko   StringRef Text(BufferPtr, TextEnd - BufferPtr);
5235188c4b9SDmitri Gribenko   formTokenWithChars(T, NextLine, tok::verbatim_block_line);
52460ddd8a1SDmitri Gribenko   T.setVerbatimBlockText(Text);
5255188c4b9SDmitri Gribenko 
5265188c4b9SDmitri Gribenko   State = LS_VerbatimBlockBody;
5275188c4b9SDmitri Gribenko }
5285188c4b9SDmitri Gribenko 
lexVerbatimBlockBody(Token & T)5295188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) {
5305188c4b9SDmitri Gribenko   assert(State == LS_VerbatimBlockBody);
5315188c4b9SDmitri Gribenko 
5325188c4b9SDmitri Gribenko   if (CommentState == LCS_InsideCComment)
5335188c4b9SDmitri Gribenko     skipLineStartingDecorations();
5345188c4b9SDmitri Gribenko 
5358b720625SDmitri Gribenko   if (BufferPtr == CommentEnd) {
5368b720625SDmitri Gribenko     formTokenWithChars(T, BufferPtr, tok::verbatim_block_line);
5378b720625SDmitri Gribenko     T.setVerbatimBlockText("");
5388b720625SDmitri Gribenko     return;
5398b720625SDmitri Gribenko   }
5408b720625SDmitri Gribenko 
5415188c4b9SDmitri Gribenko   lexVerbatimBlockFirstLine(T);
5425188c4b9SDmitri Gribenko }
5435188c4b9SDmitri Gribenko 
setupAndLexVerbatimLine(Token & T,const char * TextBegin,const CommandInfo * Info)5447acbf00fSDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin,
5457acbf00fSDmitri Gribenko                                     const CommandInfo *Info) {
5467acbf00fSDmitri Gribenko   assert(Info->IsVerbatimLineCommand);
5471669f702SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
5487acbf00fSDmitri Gribenko   T.setVerbatimLineID(Info->getID());
5491669f702SDmitri Gribenko 
5501669f702SDmitri Gribenko   State = LS_VerbatimLineText;
5511669f702SDmitri Gribenko }
5521669f702SDmitri Gribenko 
lexVerbatimLineText(Token & T)5531669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) {
5541669f702SDmitri Gribenko   assert(State == LS_VerbatimLineText);
5551669f702SDmitri Gribenko 
5565188c4b9SDmitri Gribenko   // Extract current line.
5575188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
558bf3e3270SCraig Topper   StringRef Text(BufferPtr, Newline - BufferPtr);
5591669f702SDmitri Gribenko   formTokenWithChars(T, Newline, tok::verbatim_line_text);
5605188c4b9SDmitri Gribenko   T.setVerbatimLineText(Text);
5611669f702SDmitri Gribenko 
5621669f702SDmitri Gribenko   State = LS_Normal;
5635188c4b9SDmitri Gribenko }
5645188c4b9SDmitri Gribenko 
lexHTMLCharacterReference(Token & T)5654586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) {
5664586df76SDmitri Gribenko   const char *TokenPtr = BufferPtr;
5674586df76SDmitri Gribenko   assert(*TokenPtr == '&');
5684586df76SDmitri Gribenko   TokenPtr++;
5694586df76SDmitri Gribenko   if (TokenPtr == CommentEnd) {
5704586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5714586df76SDmitri Gribenko     return;
5724586df76SDmitri Gribenko   }
5734586df76SDmitri Gribenko   const char *NamePtr;
5744586df76SDmitri Gribenko   bool isNamed = false;
5754586df76SDmitri Gribenko   bool isDecimal = false;
5764586df76SDmitri Gribenko   char C = *TokenPtr;
5774586df76SDmitri Gribenko   if (isHTMLNamedCharacterReferenceCharacter(C)) {
5784586df76SDmitri Gribenko     NamePtr = TokenPtr;
5794586df76SDmitri Gribenko     TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
5804586df76SDmitri Gribenko     isNamed = true;
5814586df76SDmitri Gribenko   } else if (C == '#') {
5824586df76SDmitri Gribenko     TokenPtr++;
5834586df76SDmitri Gribenko     if (TokenPtr == CommentEnd) {
5844586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
5854586df76SDmitri Gribenko       return;
5864586df76SDmitri Gribenko     }
5874586df76SDmitri Gribenko     C = *TokenPtr;
5884586df76SDmitri Gribenko     if (isHTMLDecimalCharacterReferenceCharacter(C)) {
5894586df76SDmitri Gribenko       NamePtr = TokenPtr;
5904586df76SDmitri Gribenko       TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
5914586df76SDmitri Gribenko       isDecimal = true;
5924586df76SDmitri Gribenko     } else if (C == 'x' || C == 'X') {
5934586df76SDmitri Gribenko       TokenPtr++;
5944586df76SDmitri Gribenko       NamePtr = TokenPtr;
5954586df76SDmitri Gribenko       TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
5964586df76SDmitri Gribenko     } else {
5974586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
5984586df76SDmitri Gribenko       return;
5994586df76SDmitri Gribenko     }
6004586df76SDmitri Gribenko   } else {
6014586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
6024586df76SDmitri Gribenko     return;
6034586df76SDmitri Gribenko   }
6044586df76SDmitri Gribenko   if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
6054586df76SDmitri Gribenko       *TokenPtr != ';') {
6064586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
6074586df76SDmitri Gribenko     return;
6084586df76SDmitri Gribenko   }
6094586df76SDmitri Gribenko   StringRef Name(NamePtr, TokenPtr - NamePtr);
6104586df76SDmitri Gribenko   TokenPtr++; // Skip semicolon.
6114586df76SDmitri Gribenko   StringRef Resolved;
61228800da1SDmitri Gribenko   if (isNamed)
6134586df76SDmitri Gribenko     Resolved = resolveHTMLNamedCharacterReference(Name);
6144586df76SDmitri Gribenko   else if (isDecimal)
6154586df76SDmitri Gribenko     Resolved = resolveHTMLDecimalCharacterReference(Name);
6164586df76SDmitri Gribenko   else
6174586df76SDmitri Gribenko     Resolved = resolveHTMLHexCharacterReference(Name);
6184586df76SDmitri Gribenko 
6194586df76SDmitri Gribenko   if (Resolved.empty()) {
6204586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
6214586df76SDmitri Gribenko     return;
6224586df76SDmitri Gribenko   }
6234586df76SDmitri Gribenko   formTokenWithChars(T, TokenPtr, tok::text);
6244586df76SDmitri Gribenko   T.setText(Resolved);
6254586df76SDmitri Gribenko }
6264586df76SDmitri Gribenko 
setupAndLexHTMLStartTag(Token & T)627e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) {
62817709ae8SDmitri Gribenko   assert(BufferPtr[0] == '<' &&
62917709ae8SDmitri Gribenko          isHTMLIdentifierStartingCharacter(BufferPtr[1]));
6305188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
63160ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
632107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
633107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
634107618a6SDmitri Gribenko     return;
635107618a6SDmitri Gribenko   }
636107618a6SDmitri Gribenko 
637e00ffc7bSDmitri Gribenko   formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
638e00ffc7bSDmitri Gribenko   T.setHTMLTagStartName(Name);
6395188c4b9SDmitri Gribenko 
6405188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
6415188c4b9SDmitri Gribenko 
64217709ae8SDmitri Gribenko   const char C = *BufferPtr;
64317709ae8SDmitri Gribenko   if (BufferPtr != CommentEnd &&
644f26054f0SDmitri Gribenko       (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
645e00ffc7bSDmitri Gribenko     State = LS_HTMLStartTag;
6465188c4b9SDmitri Gribenko }
6475188c4b9SDmitri Gribenko 
lexHTMLStartTag(Token & T)648e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) {
649e00ffc7bSDmitri Gribenko   assert(State == LS_HTMLStartTag);
6505188c4b9SDmitri Gribenko 
6515188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
6525188c4b9SDmitri Gribenko   char C = *TokenPtr;
6535188c4b9SDmitri Gribenko   if (isHTMLIdentifierCharacter(C)) {
6545188c4b9SDmitri Gribenko     TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
65560ddd8a1SDmitri Gribenko     StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
6565188c4b9SDmitri Gribenko     formTokenWithChars(T, TokenPtr, tok::html_ident);
65760ddd8a1SDmitri Gribenko     T.setHTMLIdent(Ident);
6585188c4b9SDmitri Gribenko   } else {
6595188c4b9SDmitri Gribenko     switch (C) {
6605188c4b9SDmitri Gribenko     case '=':
6615188c4b9SDmitri Gribenko       TokenPtr++;
6625188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_equals);
6635188c4b9SDmitri Gribenko       break;
6645188c4b9SDmitri Gribenko     case '\"':
6655188c4b9SDmitri Gribenko     case '\'': {
6665188c4b9SDmitri Gribenko       const char *OpenQuote = TokenPtr;
6675188c4b9SDmitri Gribenko       TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
6685188c4b9SDmitri Gribenko       const char *ClosingQuote = TokenPtr;
6695188c4b9SDmitri Gribenko       if (TokenPtr != CommentEnd) // Skip closing quote.
6705188c4b9SDmitri Gribenko         TokenPtr++;
6715188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
6725188c4b9SDmitri Gribenko       T.setHTMLQuotedString(StringRef(OpenQuote + 1,
6735188c4b9SDmitri Gribenko                                       ClosingQuote - (OpenQuote + 1)));
6745188c4b9SDmitri Gribenko       break;
6755188c4b9SDmitri Gribenko     }
6765188c4b9SDmitri Gribenko     case '>':
6775188c4b9SDmitri Gribenko       TokenPtr++;
6785188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_greater);
67917709ae8SDmitri Gribenko       State = LS_Normal;
68017709ae8SDmitri Gribenko       return;
681f26054f0SDmitri Gribenko     case '/':
682f26054f0SDmitri Gribenko       TokenPtr++;
683f26054f0SDmitri Gribenko       if (TokenPtr != CommentEnd && *TokenPtr == '>') {
684f26054f0SDmitri Gribenko         TokenPtr++;
685f26054f0SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
6864586df76SDmitri Gribenko       } else
6874586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
6884586df76SDmitri Gribenko 
689f26054f0SDmitri Gribenko       State = LS_Normal;
690f26054f0SDmitri Gribenko       return;
6915188c4b9SDmitri Gribenko     }
6925188c4b9SDmitri Gribenko   }
6935188c4b9SDmitri Gribenko 
6945188c4b9SDmitri Gribenko   // Now look ahead and return to normal state if we don't see any HTML tokens
6955188c4b9SDmitri Gribenko   // ahead.
6965188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
6975188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd) {
6985188c4b9SDmitri Gribenko     State = LS_Normal;
6995188c4b9SDmitri Gribenko     return;
7005188c4b9SDmitri Gribenko   }
7015188c4b9SDmitri Gribenko 
7025188c4b9SDmitri Gribenko   C = *BufferPtr;
70317709ae8SDmitri Gribenko   if (!isHTMLIdentifierStartingCharacter(C) &&
7045188c4b9SDmitri Gribenko       C != '=' && C != '\"' && C != '\'' && C != '>') {
7055188c4b9SDmitri Gribenko     State = LS_Normal;
7065188c4b9SDmitri Gribenko     return;
7075188c4b9SDmitri Gribenko   }
7085188c4b9SDmitri Gribenko }
7095188c4b9SDmitri Gribenko 
setupAndLexHTMLEndTag(Token & T)710e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) {
7115188c4b9SDmitri Gribenko   assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
7125188c4b9SDmitri Gribenko 
7135188c4b9SDmitri Gribenko   const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
7145188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
715107618a6SDmitri Gribenko   StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin);
716107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
717107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
718107618a6SDmitri Gribenko     return;
719107618a6SDmitri Gribenko   }
7205188c4b9SDmitri Gribenko 
7215188c4b9SDmitri Gribenko   const char *End = skipWhitespace(TagNameEnd, CommentEnd);
7225188c4b9SDmitri Gribenko 
723e00ffc7bSDmitri Gribenko   formTokenWithChars(T, End, tok::html_end_tag);
724107618a6SDmitri Gribenko   T.setHTMLTagEndName(Name);
725ec92531cSDmitri Gribenko 
726ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd && *BufferPtr == '>')
727e00ffc7bSDmitri Gribenko     State = LS_HTMLEndTag;
728ec92531cSDmitri Gribenko }
729ec92531cSDmitri Gribenko 
lexHTMLEndTag(Token & T)730e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) {
731ec92531cSDmitri Gribenko   assert(BufferPtr != CommentEnd && *BufferPtr == '>');
732ec92531cSDmitri Gribenko 
733ec92531cSDmitri Gribenko   formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
734ec92531cSDmitri Gribenko   State = LS_Normal;
7355188c4b9SDmitri Gribenko }
7365188c4b9SDmitri Gribenko 
Lexer(llvm::BumpPtrAllocator & Allocator,DiagnosticsEngine & Diags,const CommandTraits & Traits,SourceLocation FileLoc,const char * BufferStart,const char * BufferEnd,bool ParseCommands)7375b637078SFariborz Jahanian Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags,
7381ff7c32fSIlya Biryukov              const CommandTraits &Traits, SourceLocation FileLoc,
739b698ad00SNathan James              const char *BufferStart, const char *BufferEnd, bool ParseCommands)
7401ff7c32fSIlya Biryukov     : Allocator(Allocator), Diags(Diags), Traits(Traits),
741b698ad00SNathan James       BufferStart(BufferStart), BufferEnd(BufferEnd), BufferPtr(BufferStart),
742b698ad00SNathan James       FileLoc(FileLoc), ParseCommands(ParseCommands),
743b698ad00SNathan James       CommentState(LCS_BeforeComment), State(LS_Normal) {}
7445188c4b9SDmitri Gribenko 
lex(Token & T)7455188c4b9SDmitri Gribenko void Lexer::lex(Token &T) {
7465188c4b9SDmitri Gribenko again:
7475188c4b9SDmitri Gribenko   switch (CommentState) {
7485188c4b9SDmitri Gribenko   case LCS_BeforeComment:
7495188c4b9SDmitri Gribenko     if (BufferPtr == BufferEnd) {
7505188c4b9SDmitri Gribenko       formTokenWithChars(T, BufferPtr, tok::eof);
7515188c4b9SDmitri Gribenko       return;
7525188c4b9SDmitri Gribenko     }
7535188c4b9SDmitri Gribenko 
7545188c4b9SDmitri Gribenko     assert(*BufferPtr == '/');
7555188c4b9SDmitri Gribenko     BufferPtr++; // Skip first slash.
7565188c4b9SDmitri Gribenko     switch(*BufferPtr) {
7575188c4b9SDmitri Gribenko     case '/': { // BCPL comment.
7585188c4b9SDmitri Gribenko       BufferPtr++; // Skip second slash.
7595188c4b9SDmitri Gribenko 
7605188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd) {
7615188c4b9SDmitri Gribenko         // Skip Doxygen magic marker, if it is present.
7625188c4b9SDmitri Gribenko         // It might be missing because of a typo //< or /*<, or because we
7635188c4b9SDmitri Gribenko         // merged this non-Doxygen comment into a bunch of Doxygen comments
7645188c4b9SDmitri Gribenko         // around it: /** ... */ /* ... */ /** ... */
7655188c4b9SDmitri Gribenko         const char C = *BufferPtr;
7665188c4b9SDmitri Gribenko         if (C == '/' || C == '!')
7675188c4b9SDmitri Gribenko           BufferPtr++;
7685188c4b9SDmitri Gribenko       }
7695188c4b9SDmitri Gribenko 
7705188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7715188c4b9SDmitri Gribenko       // Skip it even if the comment is not a Doxygen one, because //< and /*<
7725188c4b9SDmitri Gribenko       // are frequent typos.
7735188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7745188c4b9SDmitri Gribenko         BufferPtr++;
7755188c4b9SDmitri Gribenko 
7765188c4b9SDmitri Gribenko       CommentState = LCS_InsideBCPLComment;
777ec92531cSDmitri Gribenko       if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
7785188c4b9SDmitri Gribenko         State = LS_Normal;
7795188c4b9SDmitri Gribenko       CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
7805188c4b9SDmitri Gribenko       goto again;
7815188c4b9SDmitri Gribenko     }
7825188c4b9SDmitri Gribenko     case '*': { // C comment.
7835188c4b9SDmitri Gribenko       BufferPtr++; // Skip star.
7845188c4b9SDmitri Gribenko 
7855188c4b9SDmitri Gribenko       // Skip Doxygen magic marker.
7865188c4b9SDmitri Gribenko       const char C = *BufferPtr;
7875188c4b9SDmitri Gribenko       if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
7885188c4b9SDmitri Gribenko         BufferPtr++;
7895188c4b9SDmitri Gribenko 
7905188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7915188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7925188c4b9SDmitri Gribenko         BufferPtr++;
7935188c4b9SDmitri Gribenko 
7945188c4b9SDmitri Gribenko       CommentState = LCS_InsideCComment;
7955188c4b9SDmitri Gribenko       State = LS_Normal;
7965188c4b9SDmitri Gribenko       CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
7975188c4b9SDmitri Gribenko       goto again;
7985188c4b9SDmitri Gribenko     }
7995188c4b9SDmitri Gribenko     default:
8005188c4b9SDmitri Gribenko       llvm_unreachable("second character of comment should be '/' or '*'");
8015188c4b9SDmitri Gribenko     }
8025188c4b9SDmitri Gribenko 
8035188c4b9SDmitri Gribenko   case LCS_BetweenComments: {
8045188c4b9SDmitri Gribenko     // Consecutive comments are extracted only if there is only whitespace
8055188c4b9SDmitri Gribenko     // between them.  So we can search for the start of the next comment.
8065188c4b9SDmitri Gribenko     const char *EndWhitespace = BufferPtr;
8075188c4b9SDmitri Gribenko     while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
8085188c4b9SDmitri Gribenko       EndWhitespace++;
8095188c4b9SDmitri Gribenko 
8105188c4b9SDmitri Gribenko     // Turn any whitespace between comments (and there is only whitespace
81117709ae8SDmitri Gribenko     // between them -- guaranteed by comment extraction) into a newline.  We
81217709ae8SDmitri Gribenko     // have two newlines between C comments in total (first one was synthesized
81317709ae8SDmitri Gribenko     // after a comment).
8145188c4b9SDmitri Gribenko     formTokenWithChars(T, EndWhitespace, tok::newline);
8155188c4b9SDmitri Gribenko 
8165188c4b9SDmitri Gribenko     CommentState = LCS_BeforeComment;
8175188c4b9SDmitri Gribenko     break;
8185188c4b9SDmitri Gribenko   }
8195188c4b9SDmitri Gribenko 
8205188c4b9SDmitri Gribenko   case LCS_InsideBCPLComment:
8215188c4b9SDmitri Gribenko   case LCS_InsideCComment:
8225188c4b9SDmitri Gribenko     if (BufferPtr != CommentEnd) {
8235188c4b9SDmitri Gribenko       lexCommentText(T);
8245188c4b9SDmitri Gribenko       break;
8255188c4b9SDmitri Gribenko     } else {
8265188c4b9SDmitri Gribenko       // Skip C comment closing sequence.
8275188c4b9SDmitri Gribenko       if (CommentState == LCS_InsideCComment) {
8285188c4b9SDmitri Gribenko         assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
8295188c4b9SDmitri Gribenko         BufferPtr += 2;
8305188c4b9SDmitri Gribenko         assert(BufferPtr <= BufferEnd);
8315188c4b9SDmitri Gribenko 
8325188c4b9SDmitri Gribenko         // Synthenize newline just after the C comment, regardless if there is
8335188c4b9SDmitri Gribenko         // actually a newline.
8345188c4b9SDmitri Gribenko         formTokenWithChars(T, BufferPtr, tok::newline);
8355188c4b9SDmitri Gribenko 
8365188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
8375188c4b9SDmitri Gribenko         break;
8385188c4b9SDmitri Gribenko       } else {
8395188c4b9SDmitri Gribenko         // Don't synthesized a newline after BCPL comment.
8405188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
8415188c4b9SDmitri Gribenko         goto again;
8425188c4b9SDmitri Gribenko       }
8435188c4b9SDmitri Gribenko     }
8445188c4b9SDmitri Gribenko   }
8455188c4b9SDmitri Gribenko }
8465188c4b9SDmitri Gribenko 
getSpelling(const Token & Tok,const SourceManager & SourceMgr) const8475188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok,
8484b8b7f24SSimon Pilgrim                              const SourceManager &SourceMgr) const {
8495188c4b9SDmitri Gribenko   SourceLocation Loc = Tok.getLocation();
8505188c4b9SDmitri Gribenko   std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
8515188c4b9SDmitri Gribenko 
8525188c4b9SDmitri Gribenko   bool InvalidTemp = false;
8535188c4b9SDmitri Gribenko   StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
8544b8b7f24SSimon Pilgrim   if (InvalidTemp)
8555188c4b9SDmitri Gribenko     return StringRef();
8565188c4b9SDmitri Gribenko 
8575188c4b9SDmitri Gribenko   const char *Begin = File.data() + LocInfo.second;
8585188c4b9SDmitri Gribenko   return StringRef(Begin, Tok.getLength());
8595188c4b9SDmitri Gribenko }
8605188c4b9SDmitri Gribenko 
8615188c4b9SDmitri Gribenko } // end namespace comments
8625188c4b9SDmitri Gribenko } // end namespace clang
863