LCOV - code coverage report
Current view: top level - gcc/rust/lex - rust-lex.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 89.7 % 1342 1204
Test Date: 2024-04-20 14:03:02 Functions: 92.5 % 53 49
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : // Copyright (C) 2020-2024 Free Software Foundation, Inc.
       2                 :             : 
       3                 :             : // This file is part of GCC.
       4                 :             : 
       5                 :             : // GCC is free software; you can redistribute it and/or modify it under
       6                 :             : // the terms of the GNU General Public License as published by the Free
       7                 :             : // Software Foundation; either version 3, or (at your option) any later
       8                 :             : // version.
       9                 :             : 
      10                 :             : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11                 :             : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12                 :             : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13                 :             : // for more details.
      14                 :             : 
      15                 :             : // You should have received a copy of the GNU General Public License
      16                 :             : // along with GCC; see the file COPYING3.  If not see
      17                 :             : // <http://www.gnu.org/licenses/>.
      18                 :             : 
      19                 :             : #include "rust-codepoint.h"
      20                 :             : #include "rust-system.h"
      21                 :             : #include "rust-lex.h"
      22                 :             : #include "rust-diagnostics.h"
      23                 :             : #include "rust-linemap.h"
      24                 :             : #include "rust-session-manager.h"
      25                 :             : #include "safe-ctype.h"
      26                 :             : #include "cpplib.h"
      27                 :             : #include "rust-keyword-values.h"
      28                 :             : 
      29                 :             : namespace Rust {
      30                 :             : // TODO: move to separate compilation unit?
      31                 :             : // overload += for uint32_t to allow 32-bit encoded utf-8 to be added
      32                 :             : std::string &
      33                 :     1393358 : operator+= (std::string &str, Codepoint char32)
      34                 :             : {
      35                 :     1393358 :   if (char32.value < 0x80)
      36                 :             :     {
      37                 :     1392485 :       str += static_cast<char> (char32.value);
      38                 :             :     }
      39                 :         873 :   else if (char32.value < (0x1F + 1) << (1 * 6))
      40                 :             :     {
      41                 :         604 :       str += static_cast<char> (0xC0 | ((char32.value >> 6) & 0x1F));
      42                 :         604 :       str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
      43                 :             :     }
      44                 :         269 :   else if (char32.value < (0x0F + 1) << (2 * 6))
      45                 :             :     {
      46                 :         259 :       str += static_cast<char> (0xE0 | ((char32.value >> 12) & 0x0F));
      47                 :         259 :       str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
      48                 :         259 :       str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
      49                 :             :     }
      50                 :          10 :   else if (char32.value < (0x07 + 1) << (3 * 6))
      51                 :             :     {
      52                 :           6 :       str += static_cast<char> (0xF0 | ((char32.value >> 18) & 0x07));
      53                 :           6 :       str += static_cast<char> (0x80 | ((char32.value >> 12) & 0x3F));
      54                 :           6 :       str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
      55                 :           6 :       str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
      56                 :             :     }
      57                 :             :   else
      58                 :             :     {
      59                 :           4 :       rust_debug ("Invalid unicode codepoint found: '%u' ", char32.value);
      60                 :             :     }
      61                 :     1393358 :   return str;
      62                 :             : }
      63                 :             : 
      64                 :             : std::string
      65                 :     1332903 : Codepoint::as_string ()
      66                 :             : {
      67                 :     1332903 :   std::string str;
      68                 :             : 
      69                 :             :   // str += Codepoint (value);
      70                 :     1332903 :   str += *this;
      71                 :             : 
      72                 :     1332903 :   return str;
      73                 :             : }
      74                 :             : 
      75                 :             : /* Includes all allowable float digits EXCEPT _ and . as that needs lookahead
      76                 :             :  * for handling. */
      77                 :             : bool
      78                 :           0 : is_float_digit (uint32_t number)
      79                 :             : {
      80                 :           0 :   return ISDIGIT (number) || number == 'E' || number == 'e';
      81                 :             : }
      82                 :             : 
      83                 :             : /* Basically ISXDIGIT from safe-ctype but may change if Rust's encoding or
      84                 :             :  * whatever is different */
      85                 :             : bool
      86                 :         914 : is_x_digit (uint32_t number)
      87                 :             : {
      88                 :         914 :   return ISXDIGIT (number);
      89                 :             : }
      90                 :             : 
      91                 :             : bool
      92                 :          53 : is_octal_digit (uint32_t number)
      93                 :             : {
      94                 :          53 :   return number >= '0' && number <= '7';
      95                 :             : }
      96                 :             : 
      97                 :             : bool
      98                 :         193 : is_bin_digit (uint32_t number)
      99                 :             : {
     100                 :         193 :   return number == '0' || number == '1';
     101                 :             : }
     102                 :             : 
     103                 :             : bool
     104                 :          85 : check_valid_float_dot_end (uint32_t character)
     105                 :             : {
     106                 :          85 :   return character != '.' && character != '_' && !ISALPHA (character);
     107                 :             : }
     108                 :             : 
     109                 :             : bool
     110                 :         728 : is_whitespace (uint32_t character)
     111                 :             : {
     112                 :             :   // https://doc.rust-lang.org/reference/whitespace.html
     113                 :         728 :   return character == '\t' || character == '\n' || character == '\v'
     114                 :         728 :          || character == '\f' || character == '\r' || character == ' '
     115                 :         301 :          || character == 0x0085  // next line
     116                 :         301 :          || character == 0x200e  // left-to-right mark
     117                 :         301 :          || character == 0x200f  // right-to-left mark
     118                 :         301 :          || character == 0x2028  // line separator
     119                 :        1029 :          || character == 0x2029; // pragraph separator
     120                 :             : }
     121                 :             : 
     122                 :             : bool
     123                 :        2343 : is_non_decimal_int_literal_separator (uint32_t character)
     124                 :             : {
     125                 :        2343 :   return character == 'x' || character == 'o' || character == 'b';
     126                 :             : }
     127                 :             : 
     128                 :             : bool
     129                 :      173698 : is_identifier_start (uint32_t codepoint)
     130                 :             : {
     131                 :      173698 :   return (cpp_check_xid_property (codepoint) & CPP_XID_START) || codepoint == '_';
     132                 :             : }
     133                 :             : 
     134                 :             : bool
     135                 :      549868 : is_identifier_continue (uint32_t codepoint)
     136                 :             : {
     137                 :      549868 :   return cpp_check_xid_property (codepoint) & CPP_XID_CONTINUE;
     138                 :             : }
     139                 :             : 
     140                 :          92 : Lexer::Lexer (const std::string &input, Linemap *linemap)
     141                 :          92 :   : input (RAIIFile::create_error ()), current_line (1), current_column (1),
     142                 :          92 :     line_map (linemap), dump_lex_out ({}),
     143                 :          92 :     raw_input_source (new BufferInputSource (input, 0)),
     144                 :          92 :     input_queue{*raw_input_source}, token_queue (TokenSource (this))
     145                 :          92 : {}
     146                 :             : 
     147                 :        3746 : Lexer::Lexer (const char *filename, RAIIFile file_input, Linemap *linemap,
     148                 :        3746 :               tl::optional<std::ofstream &> dump_lex_opt)
     149                 :        3746 :   : input (std::move (file_input)), current_line (1), current_column (1),
     150                 :        3746 :     line_map (linemap), dump_lex_out (dump_lex_opt),
     151                 :        7492 :     raw_input_source (new FileInputSource (input.get_raw ())),
     152                 :        7492 :     input_queue{*raw_input_source}, token_queue (TokenSource (this))
     153                 :             : {
     154                 :             :   // inform line_table that file is being entered and is in line 1
     155                 :        3746 :   if (linemap)
     156                 :        3746 :     line_map->start_file (filename, current_line);
     157                 :        3746 : }
     158                 :             : 
     159                 :        3834 : Lexer::~Lexer ()
     160                 :             : {
     161                 :             :   /* ok apparently stop (which is equivalent of original code in destructor) is
     162                 :             :    * meant to be called after all files have finished parsing, for cleanup. On
     163                 :             :    * the other hand, actual code that it calls to leave a certain line map is
     164                 :             :    * mentioned in GCC docs as being useful for "just leaving an included header"
     165                 :             :    * and stuff like that, so this line mapping functionality may need fixing.
     166                 :             :    * FIXME: find out whether this occurs. */
     167                 :             : 
     168                 :             :   // line_map->stop();
     169                 :        3834 : }
     170                 :             : 
     171                 :             : bool
     172                 :        3696 : Lexer::input_source_is_valid_utf8 ()
     173                 :             : {
     174                 :        3696 :   return raw_input_source->is_valid ();
     175                 :             : }
     176                 :             : 
     177                 :             : location_t
     178                 :      902705 : Lexer::get_current_location ()
     179                 :             : {
     180                 :      902705 :   if (line_map)
     181                 :      902542 :     return linemap_position_for_column (line_table, current_column);
     182                 :             :   else
     183                 :             :     // If we have no linemap, we're lexing something without proper locations
     184                 :             :     return UNDEF_LOCATION;
     185                 :             : }
     186                 :             : 
     187                 :             : Codepoint
     188                 :     1980098 : Lexer::peek_input (int n)
     189                 :             : {
     190                 :     1980098 :   return input_queue.peek (n);
     191                 :             : }
     192                 :             : 
     193                 :             : Codepoint
     194                 :     1950471 : Lexer::peek_input ()
     195                 :             : {
     196                 :     1950471 :   return peek_input (0);
     197                 :             : }
     198                 :             : 
     199                 :             : void
     200                 :     1648430 : Lexer::skip_input (int n)
     201                 :             : {
     202                 :     1648430 :   input_queue.skip (n);
     203                 :     1648430 : }
     204                 :             : 
     205                 :             : void
     206                 :     1646409 : Lexer::skip_input ()
     207                 :             : {
     208                 :     1646409 :   skip_input (0);
     209                 :     1646409 : }
     210                 :             : 
     211                 :             : void
     212                 :      373766 : Lexer::skip_token (int n)
     213                 :             : {
     214                 :             :   // dump tokens if dump-lex option is enabled
     215                 :      373766 :   if (dump_lex_out.has_value ())
     216                 :           0 :     dump_and_skip (n);
     217                 :             :   else
     218                 :      373766 :     token_queue.skip (n);
     219                 :      373766 : }
     220                 :             : 
     221                 :             : void
     222                 :           0 : Lexer::dump_and_skip (int n)
     223                 :             : {
     224                 :           0 :   std::ofstream &out = dump_lex_out.value ();
     225                 :           0 :   bool found_eof = false;
     226                 :           0 :   const_TokenPtr tok;
     227                 :           0 :   for (int i = 0; i < n + 1; i++)
     228                 :             :     {
     229                 :           0 :       if (!found_eof)
     230                 :             :         {
     231                 :           0 :           tok = peek_token ();
     232                 :           0 :           found_eof |= tok->get_id () == Rust::END_OF_FILE;
     233                 :             : 
     234                 :           0 :           location_t loc = tok->get_locus ();
     235                 :             : 
     236                 :           0 :           out << "<id=";
     237                 :           0 :           out << tok->token_id_to_str ();
     238                 :           0 :           out << (tok->has_str () ? (std::string (", text=") + tok->get_str ()
     239                 :           0 :                                      + std::string (", typehint=")
     240                 :           0 :                                      + std::string (tok->get_type_hint_str ()))
     241                 :             :                                   : "")
     242                 :           0 :               << " ";
     243                 :           0 :           out << Linemap::location_to_string (loc) << '\n';
     244                 :             :         }
     245                 :             : 
     246                 :           0 :       token_queue.skip (0);
     247                 :             :     }
     248                 :           0 : }
     249                 :             : 
     250                 :             : void
     251                 :           0 : Lexer::replace_current_token (TokenPtr replacement)
     252                 :             : {
     253                 :           0 :   token_queue.replace_current_value (replacement);
     254                 :             : 
     255                 :           0 :   rust_debug ("called 'replace_current_token' - this is deprecated");
     256                 :           0 : }
     257                 :             : 
     258                 :             : /* Determines whether the string passed in is a keyword or not. If it is, it
     259                 :             :  * returns the keyword name.  */
     260                 :             : TokenId
     261                 :      155449 : Lexer::classify_keyword (const std::string &str)
     262                 :             : {
     263                 :      155449 :   auto &keywords = Rust::Values::Keywords::keywords_tokens;
     264                 :      155449 :   auto keyword = keywords.find (str);
     265                 :             : 
     266                 :      155449 :   if (keyword == keywords.end ())
     267                 :             :     return IDENTIFIER;
     268                 :             : 
     269                 :       56025 :   auto id = keyword->second;
     270                 :             : 
     271                 :             :   // We now have the expected token ID of the reserved keyword. However, some
     272                 :             :   // keywords are reserved starting in certain editions. For example, `try` is
     273                 :             :   // only a reserved keyword in editions >=2018. The language might gain new
     274                 :             :   // reserved keywords in the future.
     275                 :             :   //
     276                 :             :   // https://doc.rust-lang.org/reference/keywords.html#reserved-keywords
     277                 :             : 
     278                 :             :   // `try` is not a reserved keyword before 2018
     279                 :       56025 :   if (Session::get_instance ().options.get_edition ()
     280                 :             :         == CompileOptions::Edition::E2015
     281                 :       56025 :       && id == TRY)
     282                 :             :     return IDENTIFIER;
     283                 :             : 
     284                 :             :   return id;
     285                 :             : }
     286                 :             : 
     287                 :             : TokenPtr
     288                 :      379057 : Lexer::build_token ()
     289                 :             : {
     290                 :             :   // loop to go through multiple characters to build a single token
     291                 :      897723 :   while (true)
     292                 :             :     {
     293                 :      897723 :       location_t loc = get_current_location ();
     294                 :             : 
     295                 :      897723 :       current_char = peek_input ();
     296                 :      897723 :       skip_input ();
     297                 :             : 
     298                 :             :       // detect shebang
     299                 :             :       // Must be the first thing on the first line, starting with #!
     300                 :             :       // But since an attribute can also start with an #! we don't count it as a
     301                 :             :       // shebang line when after any whitespace or comments there is a [. If it
     302                 :             :       // is a shebang line we simple drop the line. Otherwise we don't consume
     303                 :             :       // any characters and fall through to the real tokenizer.
     304                 :        3837 :       if (current_line == 1 && current_column == 1 && current_char == '#'
     305                 :      901560 :           && peek_input () == '!')
     306                 :             :         {
     307                 :             :           int n = 1;
     308                 :         356 :           while (true)
     309                 :             :             {
     310                 :         356 :               Codepoint next_char = peek_input (n);
     311                 :         356 :               if (is_whitespace (next_char.value))
     312                 :          91 :                 n++;
     313                 :         265 :               else if ((next_char == '/' && peek_input (n + 1) == '/'
     314                 :          21 :                         && peek_input (n + 2) != '!'
     315                 :          21 :                         && peek_input (n + 2) != '/')
     316                 :         300 :                        || (next_char == '/' && peek_input (n + 1) == '/'
     317                 :           0 :                            && peek_input (n + 2) == '/'
     318                 :           0 :                            && peek_input (n + 3) == '/'))
     319                 :             :                 {
     320                 :             :                   // two // or four ////
     321                 :             :                   // A single line comment
     322                 :             :                   // (but not an inner or outer doc comment)
     323                 :          21 :                   n += 2;
     324                 :          21 :                   next_char = peek_input (n);
     325                 :         371 :                   while (next_char != '\n' && !next_char.is_eof ())
     326                 :             :                     {
     327                 :         350 :                       n++;
     328                 :         350 :                       next_char = peek_input (n);
     329                 :             :                     }
     330                 :          21 :                   if (next_char == '\n')
     331                 :          21 :                     n++;
     332                 :             :                 }
     333                 :         244 :               else if (next_char == '/' && peek_input (n + 1) == '*'
     334                 :          14 :                        && peek_input (n + 2) == '*'
     335                 :         244 :                        && peek_input (n + 3) == '/')
     336                 :             :                 {
     337                 :             :                   /**/
     338                 :           0 :                   n += 4;
     339                 :             :                 }
     340                 :         244 :               else if (next_char == '/' && peek_input (n + 1) == '*'
     341                 :          14 :                        && peek_input (n + 2) == '*' && peek_input (n + 3) == '*'
     342                 :         244 :                        && peek_input (n + 4) == '/')
     343                 :             :                 {
     344                 :             :                   /***/
     345                 :           0 :                   n += 5;
     346                 :             :                 }
     347                 :         244 :               else if ((next_char == '/' && peek_input (n + 1) == '*'
     348                 :          14 :                         && peek_input (n + 2) != '*'
     349                 :          14 :                         && peek_input (n + 2) != '!')
     350                 :         265 :                        || (next_char == '/' && peek_input (n + 1) == '*'
     351                 :           0 :                            && peek_input (n + 2) == '*'
     352                 :           0 :                            && peek_input (n + 3) == '*'))
     353                 :             :                 {
     354                 :             :                   // one /* or three /***
     355                 :             :                   // Start of a block comment
     356                 :             :                   // (but not an inner or outer doc comment)
     357                 :          14 :                   n += 2;
     358                 :          14 :                   int level = 1;
     359                 :        1029 :                   while (level > 0)
     360                 :             :                     {
     361                 :        1015 :                       if (peek_input (n).is_eof ())
     362                 :             :                         break;
     363                 :        1015 :                       else if (peek_input (n) == '/'
     364                 :        1015 :                                && peek_input (n + 1) == '*')
     365                 :             :                         {
     366                 :           7 :                           n += 2;
     367                 :           7 :                           level += 1;
     368                 :             :                         }
     369                 :        1008 :                       else if (peek_input (n) == '*'
     370                 :        1008 :                                && peek_input (n + 1) == '/')
     371                 :             :                         {
     372                 :          21 :                           n += 2;
     373                 :          21 :                           level -= 1;
     374                 :             :                         }
     375                 :             :                       else
     376                 :         987 :                         n++;
     377                 :             :                     }
     378                 :             :                 }
     379                 :         230 :               else if (next_char != '[')
     380                 :             :                 {
     381                 :             :                   // definitely shebang, ignore the first line
     382                 :         532 :                   while (current_char != '\n' && !current_char.is_eof ())
     383                 :             :                     {
     384                 :         504 :                       current_char = peek_input ();
     385                 :         504 :                       skip_input ();
     386                 :             :                     }
     387                 :             : 
     388                 :             :                   // newline
     389                 :          28 :                   current_line++;
     390                 :          28 :                   current_column = 1;
     391                 :             :                   // tell line_table that new line starts
     392                 :          28 :                   start_line (current_line, max_column_hint);
     393                 :          28 :                   break;
     394                 :             :                 }
     395                 :             :               else
     396                 :             :                 break; /* Definitely not a shebang line. */
     397                 :             :             }
     398                 :             :         }
     399                 :             : 
     400                 :             :       // return end of file token if end of file
     401                 :      897723 :       if (current_char.is_eof ())
     402                 :        4060 :         return Token::make (END_OF_FILE, loc);
     403                 :             : 
     404                 :             :       // if not end of file, start tokenising
     405                 :      893663 :       switch (current_char.value)
     406                 :             :         {
     407                 :             :         /* ignore whitespace characters for tokens but continue updating
     408                 :             :          * location */
     409                 :       87516 :         case '\n':   // newline
     410                 :       87516 :         case 0x0085: // next line
     411                 :       87516 :         case 0x2028: // line separator
     412                 :       87516 :         case 0x2029: // paragraph separator
     413                 :       87516 :           current_line++;
     414                 :       87516 :           current_column = 1;
     415                 :             :           // tell line_table that new line starts
     416                 :       87516 :           start_line (current_line, max_column_hint);
     417                 :       87516 :           continue;
     418                 :         252 :         case '\r': // cr
     419                 :             :           // Ignore, we expect a newline (lf) soon.
     420                 :         252 :           continue;
     421                 :      425123 :         case ' ': // space
     422                 :      425123 :           current_column++;
     423                 :      425123 :           continue;
     424                 :         103 :         case '\t': // horizontal tab
     425                 :             :           // width of a tab is not well-defined, assume 8 spaces
     426                 :         103 :           current_column += 8;
     427                 :         103 :           continue;
     428                 :          28 :         case '\v':   // vertical tab
     429                 :          28 :         case 0x000c: // form feed
     430                 :          28 :         case 0x200e: // left-to-right mark
     431                 :          28 :         case 0x200f: // right-to-left mark
     432                 :             :           // Ignored.
     433                 :          28 :           continue;
     434                 :             : 
     435                 :             :         // punctuation - actual tokens
     436                 :       16197 :         case '=':
     437                 :       16197 :           if (peek_input () == '>')
     438                 :             :             {
     439                 :             :               // match arm arrow
     440                 :        1304 :               skip_input ();
     441                 :        1304 :               current_column += 2;
     442                 :        1304 :               loc += 1;
     443                 :             : 
     444                 :        1304 :               return Token::make (MATCH_ARROW, loc);
     445                 :             :             }
     446                 :       14893 :           else if (peek_input () == '=')
     447                 :             :             {
     448                 :             :               // equality operator
     449                 :         434 :               skip_input ();
     450                 :         434 :               current_column += 2;
     451                 :         434 :               loc += 1;
     452                 :             : 
     453                 :         434 :               return Token::make (EQUAL_EQUAL, loc);
     454                 :             :             }
     455                 :             :           else
     456                 :             :             {
     457                 :             :               // assignment operator
     458                 :       14459 :               current_column++;
     459                 :       14459 :               return Token::make (EQUAL, loc);
     460                 :             :             }
     461                 :       22804 :         case '(':
     462                 :       22804 :           current_column++;
     463                 :       22804 :           return Token::make (LEFT_PAREN, loc);
     464                 :        6720 :         case '-':
     465                 :        6720 :           if (peek_input () == '>')
     466                 :             :             {
     467                 :             :               // return type specifier
     468                 :        5873 :               skip_input ();
     469                 :        5873 :               current_column += 2;
     470                 :        5873 :               loc += 1;
     471                 :             : 
     472                 :        5873 :               return Token::make (RETURN_TYPE, loc);
     473                 :             :             }
     474                 :         847 :           else if (peek_input () == '=')
     475                 :             :             {
     476                 :             :               // minus-assign
     477                 :          28 :               skip_input ();
     478                 :          28 :               current_column += 2;
     479                 :          28 :               loc += 1;
     480                 :             : 
     481                 :          28 :               return Token::make (MINUS_EQ, loc);
     482                 :             :             }
     483                 :             :           else
     484                 :             :             {
     485                 :             :               // minus
     486                 :         819 :               current_column++;
     487                 :         819 :               return Token::make (MINUS, loc);
     488                 :             :             }
     489                 :        1366 :         case '+':
     490                 :        1366 :           if (peek_input () == '=')
     491                 :             :             {
     492                 :             :               // add-assign
     493                 :          62 :               skip_input ();
     494                 :          62 :               current_column += 2;
     495                 :          62 :               loc += 1;
     496                 :             : 
     497                 :          62 :               return Token::make (PLUS_EQ, loc);
     498                 :             :             }
     499                 :             :           else
     500                 :             :             {
     501                 :             :               // add
     502                 :        1304 :               current_column++;
     503                 :        1304 :               return Token::make (PLUS, loc);
     504                 :             :             }
     505                 :       22785 :         case ')':
     506                 :       22785 :           current_column++;
     507                 :       22785 :           return Token::make (RIGHT_PAREN, loc);
     508                 :       21992 :         case ';':
     509                 :       21992 :           current_column++;
     510                 :       21992 :           return Token::make (SEMICOLON, loc);
     511                 :        6108 :         case '*':
     512                 :        6108 :           if (peek_input () == '=')
     513                 :             :             {
     514                 :             :               // multiplication-assign
     515                 :           7 :               skip_input ();
     516                 :           7 :               current_column += 2;
     517                 :           7 :               loc += 1;
     518                 :             : 
     519                 :           7 :               return Token::make (ASTERISK_EQ, loc);
     520                 :             :             }
     521                 :             :           else
     522                 :             :             {
     523                 :             :               // multiplication
     524                 :        6101 :               current_column++;
     525                 :        6101 :               return Token::make (ASTERISK, loc);
     526                 :             :             }
     527                 :       11566 :         case ',':
     528                 :       11566 :           current_column++;
     529                 :       11566 :           return Token::make (COMMA, loc);
     530                 :        6044 :         case '/':
     531                 :        6044 :           if (peek_input () == '=')
     532                 :             :             {
     533                 :             :               // division-assign
     534                 :           7 :               skip_input ();
     535                 :           7 :               current_column += 2;
     536                 :           7 :               loc += 1;
     537                 :             : 
     538                 :           7 :               return Token::make (DIV_EQ, loc);
     539                 :             :             }
     540                 :        6037 :           else if ((peek_input () == '/' && peek_input (1) != '!'
     541                 :        5352 :                     && peek_input (1) != '/')
     542                 :        6298 :                    || (peek_input () == '/' && peek_input (1) == '/'
     543                 :         199 :                        && peek_input (2) == '/'))
     544                 :             :             {
     545                 :             :               // two // or four ////
     546                 :             :               // single line comment
     547                 :             :               // (but not an inner or outer doc comment)
     548                 :        5168 :               skip_input ();
     549                 :        5168 :               current_column += 2;
     550                 :        5168 :               current_char = peek_input ();
     551                 :             : 
     552                 :             :               // basically ignore until line finishes
     553                 :      271264 :               while (current_char != '\n' && !current_char.is_eof ())
     554                 :             :                 {
     555                 :      260928 :                   skip_input ();
     556                 :      260928 :                   current_column++; // not used
     557                 :      260928 :                   current_char = peek_input ();
     558                 :             :                 }
     559                 :        5168 :               continue;
     560                 :             :             }
     561                 :         869 :           else if (peek_input () == '/'
     562                 :         869 :                    && (peek_input (1) == '!' || peek_input (1) == '/'))
     563                 :             :             {
     564                 :             :               /* single line doc comment, inner or outer.  */
     565                 :         246 :               bool is_inner = peek_input (1) == '!';
     566                 :         246 :               skip_input (1);
     567                 :         246 :               current_column += 3;
     568                 :             : 
     569                 :         246 :               std::string str;
     570                 :         246 :               str.reserve (32);
     571                 :         246 :               current_char = peek_input ();
     572                 :        7421 :               while (current_char != '\n')
     573                 :             :                 {
     574                 :        6978 :                   skip_input ();
     575                 :        6978 :                   if (current_char == '\r')
     576                 :             :                     {
     577                 :          51 :                       Codepoint next_char = peek_input ();
     578                 :          51 :                       if (next_char == '\n')
     579                 :             :                         {
     580                 :          49 :                           current_char = '\n';
     581                 :          49 :                           break;
     582                 :             :                         }
     583                 :           2 :                       rust_error_at (
     584                 :             :                         loc, "Isolated CR %<\\r%> not allowed in doc comment");
     585                 :           2 :                       current_char = next_char;
     586                 :           2 :                       continue;
     587                 :           2 :                     }
     588                 :        6927 :                   if (current_char.is_eof ())
     589                 :             :                     {
     590                 :           0 :                       rust_error_at (
     591                 :             :                         loc, "unexpected EOF while looking for end of comment");
     592                 :           0 :                       break;
     593                 :             :                     }
     594                 :        6927 :                   str += current_char;
     595                 :        6927 :                   current_char = peek_input ();
     596                 :             :                 }
     597                 :         246 :               skip_input ();
     598                 :         246 :               current_line++;
     599                 :         246 :               current_column = 1;
     600                 :             :               // tell line_table that new line starts
     601                 :         246 :               start_line (current_line, max_column_hint);
     602                 :             : 
     603                 :         246 :               str.shrink_to_fit ();
     604                 :             : 
     605                 :         246 :               loc += str.size () - 1;
     606                 :         246 :               if (is_inner)
     607                 :          62 :                 return Token::make_inner_doc_comment (loc, std::move (str));
     608                 :             :               else
     609                 :         184 :                 return Token::make_outer_doc_comment (loc, std::move (str));
     610                 :         246 :             }
     611                 :         623 :           else if (peek_input () == '*' && peek_input (1) == '*'
     612                 :         708 :                    && peek_input (2) == '/')
     613                 :             :             {
     614                 :             :               /**/
     615                 :          14 :               skip_input (2);
     616                 :          14 :               current_column += 4;
     617                 :          14 :               continue;
     618                 :             :             }
     619                 :         609 :           else if (peek_input () == '*' && peek_input (1) == '*'
     620                 :         680 :                    && peek_input (2) == '*' && peek_input (3) == '/')
     621                 :             :             {
     622                 :             :               /***/
     623                 :          14 :               skip_input (3);
     624                 :          14 :               current_column += 5;
     625                 :          14 :               continue;
     626                 :             :             }
     627                 :         595 :           else if ((peek_input () == '*' && peek_input (1) != '!'
     628                 :         490 :                     && peek_input (1) != '*')
     629                 :         725 :                    || (peek_input () == '*' && peek_input (1) == '*'
     630                 :          57 :                        && peek_input (2) == '*'))
     631                 :             :             {
     632                 :             :               // one /* or three /***
     633                 :             :               // block comment
     634                 :             :               // (but not an inner or outer doc comment)
     635                 :         447 :               skip_input ();
     636                 :         447 :               current_column += 2;
     637                 :             : 
     638                 :         447 :               int level = 1;
     639                 :       16710 :               while (level > 0)
     640                 :             :                 {
     641                 :       16264 :                   current_char = peek_input ();
     642                 :             : 
     643                 :       16264 :                   if (current_char.is_eof ())
     644                 :             :                     {
     645                 :           1 :                       rust_error_at (
     646                 :             :                         loc, "unexpected EOF while looking for end of comment");
     647                 :           1 :                       break;
     648                 :             :                     }
     649                 :             : 
     650                 :             :                   // if /* found
     651                 :       16263 :                   if (current_char == '/' && peek_input (1) == '*')
     652                 :             :                     {
     653                 :             :                       // skip /* characters
     654                 :          49 :                       skip_input (1);
     655                 :             : 
     656                 :          49 :                       current_column += 2;
     657                 :             : 
     658                 :          49 :                       level += 1;
     659                 :          49 :                       continue;
     660                 :             :                     }
     661                 :             : 
     662                 :             :                   // ignore until */ is found
     663                 :       16214 :                   if (current_char == '*' && peek_input (1) == '/')
     664                 :             :                     {
     665                 :             :                       // skip */ characters
     666                 :         495 :                       skip_input (1);
     667                 :             : 
     668                 :         495 :                       current_column += 2;
     669                 :             : 
     670                 :         495 :                       level -= 1;
     671                 :         495 :                       continue;
     672                 :             :                     }
     673                 :             : 
     674                 :       15719 :                   if (current_char == '\n')
     675                 :             :                     {
     676                 :         151 :                       skip_input ();
     677                 :         151 :                       current_line++;
     678                 :         151 :                       current_column = 1;
     679                 :             :                       // tell line_table that new line starts
     680                 :         151 :                       start_line (current_line, max_column_hint);
     681                 :         151 :                       continue;
     682                 :             :                     }
     683                 :             : 
     684                 :       15568 :                   skip_input ();
     685                 :       15568 :                   current_column++;
     686                 :             :                 }
     687                 :             : 
     688                 :             :               // refresh new token
     689                 :         447 :               continue;
     690                 :         447 :             }
     691                 :         148 :           else if (peek_input () == '*'
     692                 :         148 :                    && (peek_input (1) == '!' || peek_input (1) == '*'))
     693                 :             :             {
     694                 :             :               // block doc comment, inner /*! or outer /**
     695                 :         116 :               bool is_inner = peek_input (1) == '!';
     696                 :         116 :               skip_input (1);
     697                 :         116 :               current_column += 3;
     698                 :             : 
     699                 :         116 :               std::string str;
     700                 :         116 :               str.reserve (96);
     701                 :             : 
     702                 :         116 :               int level = 1;
     703                 :         116 :               while (level > 0)
     704                 :             :                 {
     705                 :        2685 :                   current_char = peek_input ();
     706                 :             : 
     707                 :        2685 :                   if (current_char.is_eof ())
     708                 :             :                     {
     709                 :           0 :                       rust_error_at (
     710                 :             :                         loc, "unexpected EOF while looking for end of comment");
     711                 :           0 :                       break;
     712                 :             :                     }
     713                 :             : 
     714                 :             :                   // if /* found
     715                 :        2685 :                   if (current_char == '/' && peek_input (1) == '*')
     716                 :             :                     {
     717                 :             :                       // skip /* characters
     718                 :          84 :                       skip_input (1);
     719                 :          84 :                       current_column += 2;
     720                 :             : 
     721                 :          84 :                       level += 1;
     722                 :          84 :                       str += "/*";
     723                 :          84 :                       continue;
     724                 :             :                     }
     725                 :             : 
     726                 :             :                   // ignore until */ is found
     727                 :        2601 :                   if (current_char == '*' && peek_input (1) == '/')
     728                 :             :                     {
     729                 :             :                       // skip */ characters
     730                 :         200 :                       skip_input (1);
     731                 :         200 :                       current_column += 2;
     732                 :             : 
     733                 :         200 :                       level -= 1;
     734                 :         200 :                       if (level > 0)
     735                 :          84 :                         str += "*/";
     736                 :         200 :                       continue;
     737                 :             :                     }
     738                 :             : 
     739                 :        2401 :                   if (current_char == '\r' && peek_input (1) != '\n')
     740                 :           2 :                     rust_error_at (
     741                 :             :                       loc, "Isolated CR %<\\r%> not allowed in doc comment");
     742                 :             : 
     743                 :        2401 :                   if (current_char == '\n')
     744                 :             :                     {
     745                 :           0 :                       skip_input ();
     746                 :           0 :                       current_line++;
     747                 :           0 :                       current_column = 1;
     748                 :             :                       // tell line_table that new line starts
     749                 :           0 :                       start_line (current_line, max_column_hint);
     750                 :           0 :                       str += '\n';
     751                 :           0 :                       continue;
     752                 :             :                     }
     753                 :             : 
     754                 :        2401 :                   str += current_char;
     755                 :        2401 :                   skip_input ();
     756                 :        2401 :                   current_column++;
     757                 :             :                 }
     758                 :             : 
     759                 :         116 :               str.shrink_to_fit ();
     760                 :             : 
     761                 :         116 :               loc += str.size () - 1;
     762                 :         116 :               if (is_inner)
     763                 :          73 :                 return Token::make_inner_doc_comment (loc, std::move (str));
     764                 :             :               else
     765                 :          43 :                 return Token::make_outer_doc_comment (loc, std::move (str));
     766                 :         116 :             }
     767                 :             :           else
     768                 :             :             {
     769                 :             :               // division
     770                 :          32 :               current_column++;
     771                 :          32 :               return Token::make (DIV, loc);
     772                 :             :             }
     773                 :          43 :         case '%':
     774                 :          43 :           if (peek_input () == '=')
     775                 :             :             {
     776                 :             :               // modulo-assign
     777                 :           7 :               skip_input ();
     778                 :           7 :               current_column += 2;
     779                 :           7 :               loc += 1;
     780                 :             : 
     781                 :           7 :               return Token::make (PERCENT_EQ, loc);
     782                 :             :             }
     783                 :             :           else
     784                 :             :             {
     785                 :             :               // modulo
     786                 :          36 :               current_column++;
     787                 :          36 :               return Token::make (PERCENT, loc);
     788                 :             :             }
     789                 :          21 :         case '^':
     790                 :          21 :           if (peek_input () == '=')
     791                 :             :             {
     792                 :             :               // xor-assign?
     793                 :           7 :               skip_input ();
     794                 :           7 :               current_column += 2;
     795                 :           7 :               loc += 1;
     796                 :             : 
     797                 :           7 :               return Token::make (CARET_EQ, loc);
     798                 :             :             }
     799                 :             :           else
     800                 :             :             {
     801                 :             :               // xor?
     802                 :          14 :               current_column++;
     803                 :          14 :               return Token::make (CARET, loc);
     804                 :             :             }
     805                 :        4793 :         case '<':
     806                 :        4793 :           if (peek_input () == '<')
     807                 :             :             {
     808                 :          30 :               if (peek_input (1) == '=')
     809                 :             :                 {
     810                 :             :                   // left-shift assign
     811                 :           7 :                   skip_input (1);
     812                 :           7 :                   current_column += 3;
     813                 :           7 :                   loc += 2;
     814                 :             : 
     815                 :           7 :                   return Token::make (LEFT_SHIFT_EQ, loc);
     816                 :             :                 }
     817                 :             :               else
     818                 :             :                 {
     819                 :             :                   // left-shift
     820                 :          23 :                   skip_input ();
     821                 :          23 :                   current_column += 2;
     822                 :          23 :                   loc += 1;
     823                 :             : 
     824                 :          23 :                   return Token::make (LEFT_SHIFT, loc);
     825                 :             :                 }
     826                 :             :             }
     827                 :        4763 :           else if (peek_input () == '=')
     828                 :             :             {
     829                 :             :               // smaller than or equal to
     830                 :          59 :               skip_input ();
     831                 :          59 :               current_column += 2;
     832                 :          59 :               loc += 1;
     833                 :             : 
     834                 :          59 :               return Token::make (LESS_OR_EQUAL, loc);
     835                 :             :             }
     836                 :             :           else
     837                 :             :             {
     838                 :             :               // smaller than
     839                 :        4704 :               current_column++;
     840                 :        4704 :               return Token::make (LEFT_ANGLE, loc);
     841                 :             :             }
     842                 :        4802 :           break;
     843                 :        4802 :         case '>':
     844                 :        4802 :           if (peek_input () == '>')
     845                 :             :             {
     846                 :         114 :               if (peek_input (1) == '=')
     847                 :             :                 {
     848                 :             :                   // right-shift-assign
     849                 :           7 :                   skip_input (1);
     850                 :           7 :                   current_column += 3;
     851                 :           7 :                   loc += 2;
     852                 :             : 
     853                 :           7 :                   return Token::make (RIGHT_SHIFT_EQ, loc);
     854                 :             :                 }
     855                 :             :               else
     856                 :             :                 {
     857                 :             :                   // right-shift
     858                 :         107 :                   skip_input ();
     859                 :         107 :                   current_column += 2;
     860                 :         107 :                   loc += 1;
     861                 :             : 
     862                 :         107 :                   return Token::make (RIGHT_SHIFT, loc);
     863                 :             :                 }
     864                 :             :             }
     865                 :        4688 :           else if (peek_input () == '=')
     866                 :             :             {
     867                 :             :               // larger than or equal to
     868                 :          22 :               skip_input ();
     869                 :          22 :               current_column += 2;
     870                 :          22 :               loc += 1;
     871                 :             : 
     872                 :          22 :               return Token::make (GREATER_OR_EQUAL, loc);
     873                 :             :             }
     874                 :             :           else
     875                 :             :             {
     876                 :             :               // larger than
     877                 :        4666 :               current_column++;
     878                 :        4666 :               return Token::make (RIGHT_ANGLE, loc);
     879                 :             :             }
     880                 :       15169 :         case ':':
     881                 :       15169 :           if (peek_input () == ':')
     882                 :             :             {
     883                 :             :               // scope resolution ::
     884                 :        3669 :               skip_input ();
     885                 :        3669 :               current_column += 2;
     886                 :        3669 :               loc += 1;
     887                 :             : 
     888                 :        3669 :               return Token::make (SCOPE_RESOLUTION, loc);
     889                 :             :             }
     890                 :             :           else
     891                 :             :             {
     892                 :             :               // single colon :
     893                 :       11500 :               current_column++;
     894                 :       11500 :               return Token::make (COLON, loc);
     895                 :             :             }
     896                 :        2401 :         case '!':
     897                 :             :           // no special handling for macros in lexer?
     898                 :        2401 :           if (peek_input () == '=')
     899                 :             :             {
     900                 :             :               // not equal boolean operator
     901                 :         108 :               skip_input ();
     902                 :         108 :               current_column += 2;
     903                 :         108 :               loc += 1;
     904                 :             : 
     905                 :         108 :               return Token::make (NOT_EQUAL, loc);
     906                 :             :             }
     907                 :             :           else
     908                 :             :             {
     909                 :             :               // not equal unary operator
     910                 :        2293 :               current_column++;
     911                 :             : 
     912                 :        2293 :               return Token::make (EXCLAM, loc);
     913                 :             :             }
     914                 :          44 :         case '?':
     915                 :          44 :           current_column++;
     916                 :          44 :           return Token::make (QUESTION_MARK, loc);
     917                 :        2999 :         case '#':
     918                 :        2999 :           current_column++;
     919                 :        2999 :           return Token::make (HASH, loc);
     920                 :        4764 :         case '[':
     921                 :        4764 :           current_column++;
     922                 :        4764 :           return Token::make (LEFT_SQUARE, loc);
     923                 :        4757 :         case ']':
     924                 :        4757 :           current_column++;
     925                 :        4757 :           return Token::make (RIGHT_SQUARE, loc);
     926                 :       20551 :         case '{':
     927                 :       20551 :           current_column++;
     928                 :       20551 :           return Token::make (LEFT_CURLY, loc);
     929                 :       20509 :         case '}':
     930                 :       20509 :           current_column++;
     931                 :       20509 :           return Token::make (RIGHT_CURLY, loc);
     932                 :           0 :         case '@':
     933                 :           0 :           current_column++;
     934                 :           0 :           return Token::make (PATTERN_BIND, loc);
     935                 :        2125 :         case '$':
     936                 :        2125 :           current_column++;
     937                 :        2125 :           return Token::make (DOLLAR_SIGN, loc);
     938                 :           0 :         case '~':
     939                 :           0 :           current_column++;
     940                 :           0 :           return Token::make (TILDE, loc);
     941                 :           0 :         case '\\':
     942                 :           0 :           current_column++;
     943                 :           0 :           return Token::make (BACKSLASH, loc);
     944                 :           0 :         case '`':
     945                 :           0 :           current_column++;
     946                 :           0 :           return Token::make (BACKTICK, loc);
     947                 :         265 :         case '|':
     948                 :         265 :           if (peek_input () == '=')
     949                 :             :             {
     950                 :             :               // bitwise or-assign?
     951                 :           7 :               skip_input ();
     952                 :           7 :               current_column += 2;
     953                 :           7 :               loc += 1;
     954                 :             : 
     955                 :           7 :               return Token::make (PIPE_EQ, loc);
     956                 :             :             }
     957                 :         258 :           else if (peek_input () == '|')
     958                 :             :             {
     959                 :             :               // logical or
     960                 :          65 :               skip_input ();
     961                 :          65 :               current_column += 2;
     962                 :          65 :               loc += 1;
     963                 :             : 
     964                 :          65 :               return Token::make (OR, loc);
     965                 :             :             }
     966                 :             :           else
     967                 :             :             {
     968                 :             :               // bitwise or
     969                 :         193 :               current_column++;
     970                 :             : 
     971                 :         193 :               return Token::make (PIPE, loc);
     972                 :             :             }
     973                 :        4253 :         case '&':
     974                 :        4253 :           if (peek_input () == '=')
     975                 :             :             {
     976                 :             :               // bitwise and-assign?
     977                 :          21 :               skip_input ();
     978                 :          21 :               current_column += 2;
     979                 :          21 :               loc += 1;
     980                 :             : 
     981                 :          21 :               return Token::make (AMP_EQ, loc);
     982                 :             :             }
     983                 :        4232 :           else if (peek_input () == '&')
     984                 :             :             {
     985                 :             :               // logical and
     986                 :         368 :               skip_input ();
     987                 :         368 :               current_column += 2;
     988                 :         368 :               loc += 1;
     989                 :             : 
     990                 :         368 :               return Token::make (LOGICAL_AND, loc);
     991                 :             :             }
     992                 :             :           else
     993                 :             :             {
     994                 :             :               // bitwise and/reference
     995                 :        3864 :               current_column++;
     996                 :             : 
     997                 :        3864 :               return Token::make (AMP, loc);
     998                 :             :             }
     999                 :        4051 :         case '.':
    1000                 :        4051 :           if (peek_input () == '.')
    1001                 :             :             {
    1002                 :         873 :               if (peek_input (1) == '.')
    1003                 :             :                 {
    1004                 :             :                   // ellipsis
    1005                 :         709 :                   skip_input (1);
    1006                 :         709 :                   current_column += 3;
    1007                 :         709 :                   loc += 2;
    1008                 :             : 
    1009                 :         709 :                   return Token::make (ELLIPSIS, loc);
    1010                 :             :                 }
    1011                 :         164 :               else if (peek_input (1) == '=')
    1012                 :             :                 {
    1013                 :             :                   // ..=
    1014                 :          28 :                   skip_input (1);
    1015                 :          28 :                   current_column += 3;
    1016                 :          28 :                   loc += 2;
    1017                 :             : 
    1018                 :          28 :                   return Token::make (DOT_DOT_EQ, loc);
    1019                 :             :                 }
    1020                 :             :               else
    1021                 :             :                 {
    1022                 :             :                   // ..
    1023                 :         136 :                   skip_input ();
    1024                 :         136 :                   current_column += 2;
    1025                 :         136 :                   loc += 1;
    1026                 :             : 
    1027                 :         136 :                   return Token::make (DOT_DOT, loc);
    1028                 :             :                 }
    1029                 :             :             }
    1030                 :             :           else /*if (!ISDIGIT (peek_input ()))*/
    1031                 :             :             {
    1032                 :             :               // single dot .
    1033                 :             :               // Only if followed by a non-number - otherwise is float
    1034                 :             :               // nope, float cannot start with '.'.
    1035                 :        3178 :               current_column++;
    1036                 :        3178 :               return Token::make (DOT, loc);
    1037                 :             :             }
    1038                 :      513022 :         }
    1039                 :             :       // TODO: special handling of _ in the lexer? instead of being identifier
    1040                 :             : 
    1041                 :             :       // byte character, byte string and raw byte string literals
    1042                 :      173512 :       if (current_char == 'b')
    1043                 :             :         {
    1044                 :        7526 :           if (peek_input () == '\'')
    1045                 :          78 :             return parse_byte_char (loc);
    1046                 :        7448 :           else if (peek_input () == '"')
    1047                 :          56 :             return parse_byte_string (loc);
    1048                 :        7392 :           else if (peek_input () == 'r'
    1049                 :        7392 :                    && (peek_input (1) == '#' || peek_input (1) == '"'))
    1050                 :          24 :             return parse_raw_byte_string (loc);
    1051                 :             :         }
    1052                 :             : 
    1053                 :             :       // raw identifiers and raw strings
    1054                 :      173354 :       if (current_char == 'r')
    1055                 :             :         {
    1056                 :        2535 :           Codepoint peek = peek_input ();
    1057                 :        2535 :           Codepoint peek1 = peek_input (1);
    1058                 :             : 
    1059                 :             :           // TODO (tamaron) parse Unicode ident
    1060                 :        2535 :           if (peek == '#' && is_identifier_start (peek1.value))
    1061                 :             :             {
    1062                 :          79 :               TokenPtr raw_ident_ptr = parse_raw_identifier (loc);
    1063                 :          79 :               if (raw_ident_ptr != nullptr)
    1064                 :          78 :                 return raw_ident_ptr;
    1065                 :             :               else
    1066                 :           1 :                 continue; /* input got parsed, it just wasn't valid. An error
    1067                 :             :                              was produced. */
    1068                 :           1 :             }
    1069                 :             :           else
    1070                 :             :             {
    1071                 :        2456 :               TokenPtr maybe_raw_string_ptr = maybe_parse_raw_string (loc);
    1072                 :        2456 :               if (maybe_raw_string_ptr != nullptr)
    1073                 :          23 :                 return maybe_raw_string_ptr;
    1074                 :        2456 :             }
    1075                 :             :         }
    1076                 :             : 
    1077                 :             :       // find identifiers and keywords.
    1078                 :      173252 :       if (is_identifier_start (current_char.value))
    1079                 :      155885 :         return parse_identifier_or_keyword (loc);
    1080                 :             : 
    1081                 :             :       // int and float literals
    1082                 :       17367 :       if (ISDIGIT (current_char.value))
    1083                 :             :         { //  _ not allowed as first char
    1084                 :       11911 :           if (current_char == '0'
    1085                 :       11911 :               && is_non_decimal_int_literal_separator (peek_input ().value))
    1086                 :             :             {
    1087                 :             :               // handle binary, octal, hex literals
    1088                 :         108 :               TokenPtr non_dec_int_lit_ptr
    1089                 :         108 :                 = parse_non_decimal_int_literals (loc);
    1090                 :         108 :               if (non_dec_int_lit_ptr != nullptr)
    1091                 :         108 :                 return non_dec_int_lit_ptr;
    1092                 :         108 :             }
    1093                 :             :           else
    1094                 :             :             {
    1095                 :             :               // handle decimals (integer or float)
    1096                 :       11803 :               TokenPtr decimal_or_float_ptr = parse_decimal_int_or_float (loc);
    1097                 :       11803 :               if (decimal_or_float_ptr != nullptr)
    1098                 :       11803 :                 return decimal_or_float_ptr;
    1099                 :       11803 :             }
    1100                 :             :         }
    1101                 :             : 
    1102                 :             :       // string literals
    1103                 :        5456 :       if (current_char == '"')
    1104                 :        4868 :         return parse_string (loc);
    1105                 :             : 
    1106                 :             :       // char literals and lifetime names
    1107                 :         588 :       if (current_char == '\'')
    1108                 :             :         {
    1109                 :         588 :           TokenPtr char_or_lifetime_ptr = parse_char_or_lifetime (loc);
    1110                 :         588 :           if (char_or_lifetime_ptr != nullptr)
    1111                 :         588 :             return char_or_lifetime_ptr;
    1112                 :         588 :         }
    1113                 :             : 
    1114                 :             :       // DEBUG: check for specific character problems:
    1115                 :           0 :       if (current_char == '0')
    1116                 :           0 :         rust_debug ("'0' uncaught before unexpected character");
    1117                 :           0 :       else if (current_char == ']')
    1118                 :           0 :         rust_debug ("']' uncaught before unexpected character");
    1119                 :             :       else if (current_char == 0x5d)
    1120                 :             :         rust_debug ("whatever 0x5d is (not '0' or ']') uncaught before "
    1121                 :             :                     "unexpected character");
    1122                 :             : 
    1123                 :             :       // didn't match anything so error
    1124                 :           0 :       rust_error_at (loc, "unexpected character %<%x%>", current_char.value);
    1125                 :           0 :       current_column++;
    1126                 :             :     }
    1127                 :             : }
    1128                 :             : 
    1129                 :             : // Parses in a type suffix.
    1130                 :             : std::pair<PrimitiveCoreType, int>
    1131                 :       11903 : Lexer::parse_in_type_suffix ()
    1132                 :             : {
    1133                 :       11903 :   std::string suffix;
    1134                 :       11903 :   suffix.reserve (5);
    1135                 :             : 
    1136                 :       11903 :   int additional_length_offset = 0;
    1137                 :             : 
    1138                 :             :   // get suffix
    1139                 :       11903 :   while (ISALPHA (current_char.value) || ISDIGIT (current_char.value)
    1140                 :       14952 :          || current_char == '_')
    1141                 :             :     {
    1142                 :        3049 :       if (current_char == '_')
    1143                 :             :         {
    1144                 :             :           // don't add _ to suffix
    1145                 :           0 :           skip_input ();
    1146                 :           0 :           current_char = peek_input ();
    1147                 :             : 
    1148                 :           0 :           additional_length_offset++;
    1149                 :             : 
    1150                 :           0 :           continue;
    1151                 :             :         }
    1152                 :             : 
    1153                 :        3049 :       additional_length_offset++;
    1154                 :             : 
    1155                 :        3049 :       suffix += current_char;
    1156                 :        3049 :       skip_input ();
    1157                 :        3049 :       current_char = peek_input ();
    1158                 :             :     }
    1159                 :             : 
    1160                 :       11903 :   if (suffix.empty ())
    1161                 :             :     {
    1162                 :             :       // no type suffix: do nothing but also no error
    1163                 :       10885 :       return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
    1164                 :             :     }
    1165                 :        1018 :   else if (suffix == "f32")
    1166                 :             :     {
    1167                 :         490 :       return std::make_pair (CORETYPE_F32, additional_length_offset);
    1168                 :             :     }
    1169                 :         528 :   else if (suffix == "f64")
    1170                 :             :     {
    1171                 :         219 :       return std::make_pair (CORETYPE_F64, additional_length_offset);
    1172                 :             :     }
    1173                 :         309 :   else if (suffix == "i8")
    1174                 :             :     {
    1175                 :          23 :       return std::make_pair (CORETYPE_I8, additional_length_offset);
    1176                 :             :     }
    1177                 :         286 :   else if (suffix == "i16")
    1178                 :             :     {
    1179                 :          15 :       return std::make_pair (CORETYPE_I16, additional_length_offset);
    1180                 :             :     }
    1181                 :         271 :   else if (suffix == "i32")
    1182                 :             :     {
    1183                 :          82 :       return std::make_pair (CORETYPE_I32, additional_length_offset);
    1184                 :             :     }
    1185                 :         189 :   else if (suffix == "i64")
    1186                 :             :     {
    1187                 :          15 :       return std::make_pair (CORETYPE_I64, additional_length_offset);
    1188                 :             :     }
    1189                 :         174 :   else if (suffix == "i128")
    1190                 :             :     {
    1191                 :          15 :       return std::make_pair (CORETYPE_I128, additional_length_offset);
    1192                 :             :     }
    1193                 :         159 :   else if (suffix == "isize")
    1194                 :             :     {
    1195                 :           3 :       return std::make_pair (CORETYPE_ISIZE, additional_length_offset);
    1196                 :             :     }
    1197                 :         156 :   else if (suffix == "u8")
    1198                 :             :     {
    1199                 :          28 :       return std::make_pair (CORETYPE_U8, additional_length_offset);
    1200                 :             :     }
    1201                 :         128 :   else if (suffix == "u16")
    1202                 :             :     {
    1203                 :          24 :       return std::make_pair (CORETYPE_U16, additional_length_offset);
    1204                 :             :     }
    1205                 :         104 :   else if (suffix == "u32")
    1206                 :             :     {
    1207                 :          60 :       return std::make_pair (CORETYPE_U32, additional_length_offset);
    1208                 :             :     }
    1209                 :          44 :   else if (suffix == "u64")
    1210                 :             :     {
    1211                 :          24 :       return std::make_pair (CORETYPE_U64, additional_length_offset);
    1212                 :             :     }
    1213                 :          20 :   else if (suffix == "u128")
    1214                 :             :     {
    1215                 :          15 :       return std::make_pair (CORETYPE_U128, additional_length_offset);
    1216                 :             :     }
    1217                 :           5 :   else if (suffix == "usize")
    1218                 :             :     {
    1219                 :           5 :       return std::make_pair (CORETYPE_USIZE, additional_length_offset);
    1220                 :             :     }
    1221                 :             :   else
    1222                 :             :     {
    1223                 :           0 :       rust_error_at (get_current_location (), "unknown number suffix %qs",
    1224                 :             :                      suffix.c_str ());
    1225                 :             : 
    1226                 :           0 :       return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
    1227                 :             :     }
    1228                 :       11903 : }
    1229                 :             : 
    1230                 :             : // Parses in the exponent part (if any) of a float literal.
    1231                 :             : std::pair<std::string, int>
    1232                 :         326 : Lexer::parse_in_exponent_part ()
    1233                 :             : {
    1234                 :         326 :   int additional_length_offset = 0;
    1235                 :         326 :   std::string str;
    1236                 :         326 :   if (current_char == 'E' || current_char == 'e')
    1237                 :             :     {
    1238                 :             :       // add exponent to string as strtod works with it
    1239                 :           0 :       str += current_char;
    1240                 :           0 :       skip_input ();
    1241                 :           0 :       current_char = peek_input ();
    1242                 :             : 
    1243                 :           0 :       additional_length_offset++;
    1244                 :             : 
    1245                 :             :       // special - and + handling
    1246                 :           0 :       if (current_char == '-')
    1247                 :             :         {
    1248                 :           0 :           str += '-';
    1249                 :             : 
    1250                 :           0 :           skip_input ();
    1251                 :           0 :           current_char = peek_input ();
    1252                 :             : 
    1253                 :           0 :           additional_length_offset++;
    1254                 :             :         }
    1255                 :           0 :       else if (current_char == '+')
    1256                 :             :         {
    1257                 :             :           // don't add + but still skip input
    1258                 :           0 :           skip_input ();
    1259                 :           0 :           current_char = peek_input ();
    1260                 :             : 
    1261                 :           0 :           additional_length_offset++;
    1262                 :             :         }
    1263                 :             : 
    1264                 :             :       // parse another decimal number for exponent
    1265                 :           0 :       auto str_length = parse_in_decimal ();
    1266                 :           0 :       str += std::get<0> (str_length);
    1267                 :           0 :       additional_length_offset += std::get<1> (str_length);
    1268                 :           0 :     }
    1269                 :         326 :   return std::make_pair (str, additional_length_offset);
    1270                 :         326 : }
    1271                 :             : 
    1272                 :             : // Parses a decimal integer.
    1273                 :             : std::tuple<std::string, int, bool>
    1274                 :       12129 : Lexer::parse_in_decimal ()
    1275                 :             : {
    1276                 :             :   /* A pure decimal contains only digits.  */
    1277                 :       12129 :   bool pure_decimal = true;
    1278                 :       12129 :   int additional_length_offset = 0;
    1279                 :       12129 :   std::string str;
    1280                 :       18011 :   while (ISDIGIT (current_char.value) || current_char.value == '_')
    1281                 :             :     {
    1282                 :        5882 :       if (current_char == '_')
    1283                 :             :         {
    1284                 :           9 :           pure_decimal = false;
    1285                 :             :           // don't add _ to number
    1286                 :           9 :           skip_input ();
    1287                 :           9 :           current_char = peek_input ();
    1288                 :             : 
    1289                 :           9 :           additional_length_offset++;
    1290                 :             : 
    1291                 :           9 :           continue;
    1292                 :             :         }
    1293                 :             : 
    1294                 :        5873 :       additional_length_offset++;
    1295                 :             : 
    1296                 :        5873 :       str += current_char;
    1297                 :        5873 :       skip_input ();
    1298                 :        5873 :       current_char = peek_input ();
    1299                 :             :     }
    1300                 :       12129 :   return std::make_tuple (str, additional_length_offset, pure_decimal);
    1301                 :       12129 : }
    1302                 :             : 
    1303                 :             : /* Parses escapes (and string continues) in "byte" strings and characters. Does
    1304                 :             :  * not support unicode. */
    1305                 :             : std::tuple<char, int, bool>
    1306                 :          60 : Lexer::parse_escape (char opening_char)
    1307                 :             : {
    1308                 :          60 :   int additional_length_offset = 0;
    1309                 :          60 :   char output_char = 0;
    1310                 :             : 
    1311                 :             :   // skip to actual letter
    1312                 :          60 :   skip_input ();
    1313                 :          60 :   current_char = peek_input ();
    1314                 :          60 :   additional_length_offset++;
    1315                 :             : 
    1316                 :          60 :   switch (current_char.value)
    1317                 :             :     {
    1318                 :          17 :       case 'x': {
    1319                 :          17 :         auto hex_escape_pair = parse_partial_hex_escape ();
    1320                 :          17 :         long hexLong = hex_escape_pair.first;
    1321                 :          17 :         additional_length_offset += hex_escape_pair.second;
    1322                 :             : 
    1323                 :          17 :         if (hexLong > 255 || hexLong < 0)
    1324                 :           0 :           rust_error_at (
    1325                 :             :             get_current_location (),
    1326                 :             :             "byte \\x escape %<\\x%x%> out of range - allows up to %<\\xFF%>",
    1327                 :             :             static_cast<unsigned int> (hexLong));
    1328                 :             :         /* TODO: restore capital for escape output - gcc pretty-printer doesn't
    1329                 :             :          * support %X directly */
    1330                 :          17 :         char hexChar = static_cast<char> (hexLong);
    1331                 :             : 
    1332                 :          17 :         output_char = hexChar;
    1333                 :             :       }
    1334                 :          17 :       break;
    1335                 :             :     case 'n':
    1336                 :             :       output_char = '\n';
    1337                 :             :       break;
    1338                 :           0 :     case 'r':
    1339                 :           0 :       output_char = '\r';
    1340                 :           0 :       break;
    1341                 :           1 :     case 't':
    1342                 :           1 :       output_char = '\t';
    1343                 :           1 :       break;
    1344                 :           8 :     case '\\':
    1345                 :           8 :       output_char = '\\';
    1346                 :           8 :       break;
    1347                 :           8 :     case '0':
    1348                 :           8 :       output_char = '\0';
    1349                 :           8 :       break;
    1350                 :          15 :     case '\'':
    1351                 :          15 :       output_char = '\'';
    1352                 :          15 :       break;
    1353                 :           1 :     case '"':
    1354                 :           1 :       output_char = '"';
    1355                 :           1 :       break;
    1356                 :           2 :     case 'u':
    1357                 :           3 :       rust_error_at (get_current_location (),
    1358                 :             :                      "cannot have a unicode escape \\u in a byte %s",
    1359                 :             :                      opening_char == '\'' ? "character" : "string");
    1360                 :             :       // Try to parse it anyway, just to skip it
    1361                 :           2 :       parse_partial_unicode_escape ();
    1362                 :           2 :       return std::make_tuple (output_char, additional_length_offset, false);
    1363                 :           0 :     case '\r':
    1364                 :           0 :     case '\n':
    1365                 :             :       // string continue
    1366                 :           0 :       return std::make_tuple (0, parse_partial_string_continue (), true);
    1367                 :           1 :     default:
    1368                 :           1 :       rust_error_at (get_current_location (),
    1369                 :             :                      "unknown escape sequence %<\\%s%>",
    1370                 :           1 :                      current_char.as_string ().c_str ());
    1371                 :             :       // returns false if no parsing could be done
    1372                 :             :       // return false;
    1373                 :           1 :       return std::make_tuple (output_char, additional_length_offset, false);
    1374                 :          57 :       break;
    1375                 :             :     }
    1376                 :             :   // all non-special cases (string continue) should skip their used char
    1377                 :          57 :   skip_input ();
    1378                 :          57 :   current_char = peek_input ();
    1379                 :          57 :   additional_length_offset++;
    1380                 :             : 
    1381                 :             :   // returns true if parsing was successful
    1382                 :             :   // return true;
    1383                 :          57 :   return std::make_tuple (output_char, additional_length_offset, false);
    1384                 :             : }
    1385                 :             : 
    1386                 :             : /* Parses an escape (or string continue) in a string or character. Supports
    1387                 :             :  * unicode escapes. */
    1388                 :             : std::tuple<Codepoint, int, bool>
    1389                 :        2297 : Lexer::parse_utf8_escape ()
    1390                 :             : {
    1391                 :        2297 :   Codepoint output_char;
    1392                 :        2297 :   int additional_length_offset = 0;
    1393                 :             : 
    1394                 :             :   // skip to actual letter
    1395                 :        2297 :   skip_input ();
    1396                 :        2297 :   current_char = peek_input ();
    1397                 :        2297 :   additional_length_offset++;
    1398                 :             : 
    1399                 :        2297 :   switch (current_char.value)
    1400                 :             :     {
    1401                 :          17 :       case 'x': {
    1402                 :          17 :         auto hex_escape_pair = parse_partial_hex_escape ();
    1403                 :          17 :         long hexLong = hex_escape_pair.first;
    1404                 :          17 :         additional_length_offset += hex_escape_pair.second;
    1405                 :             : 
    1406                 :          17 :         if (hexLong > 127 || hexLong < 0)
    1407                 :           4 :           rust_error_at (
    1408                 :             :             get_current_location (),
    1409                 :             :             "ascii \\x escape %<\\x%x%> out of range - allows up to %<\\x7F%>",
    1410                 :             :             static_cast<unsigned int> (hexLong));
    1411                 :             :         /* TODO: restore capital for escape output - gcc pretty-printer doesn't
    1412                 :             :          * support %X directly */
    1413                 :          17 :         char hexChar = static_cast<char> (hexLong);
    1414                 :             : 
    1415                 :          17 :         output_char = hexChar;
    1416                 :             :       }
    1417                 :          17 :       break;
    1418                 :             :     case 'n':
    1419                 :             :       output_char = '\n';
    1420                 :             :       break;
    1421                 :           0 :     case 'r':
    1422                 :           0 :       output_char = '\r';
    1423                 :           0 :       break;
    1424                 :           1 :     case 't':
    1425                 :           1 :       output_char = '\t';
    1426                 :           1 :       break;
    1427                 :           1 :     case '\\':
    1428                 :           1 :       output_char = '\\';
    1429                 :           1 :       break;
    1430                 :        1083 :     case '0':
    1431                 :        1083 :       output_char = '\0';
    1432                 :        1083 :       break;
    1433                 :           1 :     case '\'':
    1434                 :           1 :       output_char = '\'';
    1435                 :           1 :       break;
    1436                 :           1 :     case '"':
    1437                 :           1 :       output_char = '"';
    1438                 :           1 :       break;
    1439                 :          46 :       case 'u': {
    1440                 :          46 :         auto unicode_escape_pair = parse_partial_unicode_escape ();
    1441                 :          46 :         output_char = unicode_escape_pair.first;
    1442                 :          46 :         additional_length_offset += unicode_escape_pair.second;
    1443                 :             : 
    1444                 :          46 :         return std::make_tuple (output_char, additional_length_offset, false);
    1445                 :             :       }
    1446                 :          28 :       break;
    1447                 :          28 :     case '\r':
    1448                 :          28 :     case '\n':
    1449                 :             :       // string continue
    1450                 :          28 :       return std::make_tuple (0, parse_partial_string_continue (), true);
    1451                 :           1 :     default:
    1452                 :           1 :       rust_error_at (get_current_location (),
    1453                 :             :                      "unknown escape sequence %<\\%s%>",
    1454                 :           1 :                      current_char.as_string ().c_str ());
    1455                 :             :       // returns false if no parsing could be done
    1456                 :             :       // return false;
    1457                 :           1 :       return std::make_tuple (output_char, additional_length_offset, false);
    1458                 :        2222 :       break;
    1459                 :             :     }
    1460                 :             :   /* all non-special cases (unicode, string continue) should skip their used
    1461                 :             :    * char */
    1462                 :        2222 :   skip_input ();
    1463                 :        2222 :   current_char = peek_input ();
    1464                 :        2222 :   additional_length_offset++;
    1465                 :             : 
    1466                 :             :   // returns true if parsing was successful
    1467                 :             :   // return true;
    1468                 :        2222 :   return std::make_tuple (output_char, additional_length_offset, false);
    1469                 :             : }
    1470                 :             : 
    1471                 :             : // Parses the body of a string continue that has been found in an escape.
    1472                 :             : int
    1473                 :          28 : Lexer::parse_partial_string_continue ()
    1474                 :             : {
    1475                 :          28 :   int additional_length_offset = 1;
    1476                 :             : 
    1477                 :             :   // string continue
    1478                 :             :   // TODO use utf-8 codepoint to skip whitespaces
    1479                 :         364 :   while (is_whitespace (current_char.value))
    1480                 :             :     {
    1481                 :         336 :       if (current_char == '\n')
    1482                 :             :         {
    1483                 :          28 :           current_line++;
    1484                 :          28 :           current_column = 1;
    1485                 :             :           // tell line_table that new line starts
    1486                 :          28 :           start_line (current_line, max_column_hint);
    1487                 :             : 
    1488                 :             :           // reset "length"
    1489                 :          28 :           additional_length_offset = 1;
    1490                 :             : 
    1491                 :             :           // get next char
    1492                 :          28 :           skip_input ();
    1493                 :          28 :           current_char = peek_input ();
    1494                 :             : 
    1495                 :          28 :           continue;
    1496                 :             :         }
    1497                 :             : 
    1498                 :         308 :       skip_input ();
    1499                 :         308 :       current_char = peek_input ();
    1500                 :         308 :       additional_length_offset++;
    1501                 :             :     }
    1502                 :             : 
    1503                 :          28 :   return additional_length_offset;
    1504                 :             : }
    1505                 :             : 
    1506                 :             : /* Parses the body of a '\x' escape. Note that it does not check that the number
    1507                 :             :  * is valid and smaller than 255. */
    1508                 :             : std::pair<long, int>
    1509                 :          34 : Lexer::parse_partial_hex_escape ()
    1510                 :             : {
    1511                 :             :   // hex char string (null-terminated)
    1512                 :          34 :   char hexNum[3] = {0, 0, 0};
    1513                 :             : 
    1514                 :             :   // first hex char
    1515                 :          34 :   current_char = peek_input (1);
    1516                 :          34 :   int additional_length_offset = 1;
    1517                 :             : 
    1518                 :          34 :   if (!is_x_digit (current_char.value))
    1519                 :             :     {
    1520                 :           4 :       rust_error_at (get_current_location (),
    1521                 :             :                      "invalid character %<\\x%s%> in \\x sequence",
    1522                 :           4 :                      current_char.as_string ().c_str ());
    1523                 :           4 :       return std::make_pair (0, 0);
    1524                 :             :     }
    1525                 :          30 :   hexNum[0] = current_char.value;
    1526                 :             : 
    1527                 :             :   // second hex char
    1528                 :          30 :   skip_input ();
    1529                 :          30 :   current_char = peek_input (1);
    1530                 :          30 :   additional_length_offset++;
    1531                 :             : 
    1532                 :          30 :   if (!is_x_digit (current_char.value))
    1533                 :             :     {
    1534                 :           2 :       rust_error_at (get_current_location (),
    1535                 :           2 :                      "invalid character %<\\x%c%s%> in \\x sequence", hexNum[0],
    1536                 :           2 :                      current_char.as_string ().c_str ());
    1537                 :           2 :       return std::make_pair (0, 1);
    1538                 :             :     }
    1539                 :          28 :   skip_input ();
    1540                 :          28 :   hexNum[1] = current_char.value;
    1541                 :             : 
    1542                 :          28 :   long hexLong = std::strtol (hexNum, nullptr, 16);
    1543                 :             : 
    1544                 :          28 :   return std::make_pair (hexLong, additional_length_offset);
    1545                 :             : }
    1546                 :             : 
    1547                 :             : // Parses the body of a unicode escape.
    1548                 :             : std::pair<Codepoint, int>
    1549                 :          48 : Lexer::parse_partial_unicode_escape ()
    1550                 :             : {
    1551                 :          48 :   skip_input ();
    1552                 :          48 :   current_char = peek_input ();
    1553                 :          48 :   int additional_length_offset = 0;
    1554                 :             : 
    1555                 :          48 :   if (current_char != '{')
    1556                 :             :     {
    1557                 :           2 :       rust_error_at (get_current_location (),
    1558                 :             :                      "unicode escape should start with %<{%>");
    1559                 :             :       /* Skip what should probaby have been between brackets.  */
    1560                 :          10 :       while (is_x_digit (current_char.value) || current_char == '_')
    1561                 :             :         {
    1562                 :           6 :           skip_input ();
    1563                 :           6 :           current_char = peek_input ();
    1564                 :           6 :           additional_length_offset++;
    1565                 :             :         }
    1566                 :           2 :       return std::make_pair (Codepoint (0), additional_length_offset);
    1567                 :             :     }
    1568                 :             : 
    1569                 :          46 :   skip_input ();
    1570                 :          46 :   current_char = peek_input ();
    1571                 :          46 :   additional_length_offset++;
    1572                 :             : 
    1573                 :          46 :   if (current_char == '_')
    1574                 :             :     {
    1575                 :           2 :       rust_error_at (get_current_location (),
    1576                 :             :                      "unicode escape cannot start with %<_%>");
    1577                 :           2 :       skip_input ();
    1578                 :           2 :       current_char = peek_input ();
    1579                 :           2 :       additional_length_offset++;
    1580                 :             :       // fallthrough and try to parse the rest anyway
    1581                 :             :     }
    1582                 :             : 
    1583                 :             :   // parse unicode escape - 1-6 hex digits
    1584                 :          46 :   std::string num_str;
    1585                 :          46 :   num_str.reserve (6);
    1586                 :             : 
    1587                 :             :   // loop through to add entire hex number to string
    1588                 :         304 :   while (is_x_digit (current_char.value) || current_char.value == '_')
    1589                 :             :     {
    1590                 :         212 :       if (current_char == '_')
    1591                 :             :         {
    1592                 :             :           // don't add _ to number
    1593                 :          24 :           skip_input ();
    1594                 :          24 :           current_char = peek_input ();
    1595                 :             : 
    1596                 :          24 :           additional_length_offset++;
    1597                 :             : 
    1598                 :          24 :           continue;
    1599                 :             :         }
    1600                 :             : 
    1601                 :         188 :       additional_length_offset++;
    1602                 :             : 
    1603                 :             :       // add raw hex numbers
    1604                 :         188 :       num_str += current_char;
    1605                 :             : 
    1606                 :         188 :       skip_input ();
    1607                 :         188 :       current_char = peek_input ();
    1608                 :             :     }
    1609                 :             : 
    1610                 :          46 :   if (current_char == '}')
    1611                 :             :     {
    1612                 :          44 :       skip_input ();
    1613                 :          44 :       current_char = peek_input ();
    1614                 :          44 :       additional_length_offset++;
    1615                 :             :     }
    1616                 :             :   else
    1617                 :             :     {
    1618                 :             :       // actually an error, but allow propagation anyway Assume that
    1619                 :             :       // wrong bracketm whitespace or single/double quotes are wrong
    1620                 :             :       // termination, otherwise it is a wrong character, then skip to the actual
    1621                 :             :       // terminator.
    1622                 :             :       // TODO use utf-8 codepoint to skip whitespaces
    1623                 :           2 :       if (current_char == '{' || is_whitespace (current_char.value)
    1624                 :           4 :           || current_char == '\'' || current_char == '"')
    1625                 :             :         {
    1626                 :           0 :           rust_error_at (get_current_location (),
    1627                 :             :                          "expected terminating %<}%> in unicode escape");
    1628                 :           0 :           return std::make_pair (Codepoint (0), additional_length_offset);
    1629                 :             :         }
    1630                 :             :       else
    1631                 :             :         {
    1632                 :           2 :           rust_error_at (get_current_location (),
    1633                 :             :                          "invalid character %<%s%> in unicode escape",
    1634                 :           2 :                          current_char.as_string ().c_str ());
    1635                 :             :           // TODO use utf-8 codepoint to skip whitespaces
    1636                 :           8 :           while (current_char != '}' && current_char != '{'
    1637                 :           6 :                  && !is_whitespace (current_char.value) && current_char != '\''
    1638                 :          14 :                  && current_char != '"')
    1639                 :             :             {
    1640                 :           6 :               skip_input ();
    1641                 :           6 :               current_char = peek_input ();
    1642                 :           6 :               additional_length_offset++;
    1643                 :             :             }
    1644                 :             :           // Consume the actual closing bracket if found
    1645                 :           2 :           if (current_char == '}')
    1646                 :             :             {
    1647                 :           2 :               skip_input ();
    1648                 :           2 :               current_char = peek_input ();
    1649                 :           2 :               additional_length_offset++;
    1650                 :             :             }
    1651                 :           2 :           return std::make_pair (Codepoint (0), additional_length_offset);
    1652                 :             :         }
    1653                 :             :     }
    1654                 :             : 
    1655                 :             :   // ensure 1-6 hex characters
    1656                 :          44 :   if (num_str.length () > 6 || num_str.length () < 1)
    1657                 :             :     {
    1658                 :           4 :       rust_error_at (get_current_location (),
    1659                 :             :                      "unicode escape should be between 1 and 6 hex "
    1660                 :             :                      "characters; it is %lu",
    1661                 :           4 :                      (unsigned long) num_str.length ());
    1662                 :             :       // return false;
    1663                 :           4 :       return std::make_pair (Codepoint (0), additional_length_offset);
    1664                 :             :     }
    1665                 :             : 
    1666                 :          40 :   unsigned long hex_num = std::strtoul (num_str.c_str (), nullptr, 16);
    1667                 :             : 
    1668                 :          40 :   if (hex_num > 0xd7ff && hex_num < 0xe000)
    1669                 :             :     {
    1670                 :           4 :       rust_error_at (
    1671                 :             :         get_current_location (),
    1672                 :             :         "unicode escape cannot be a surrogate value (D800 to DFFF)");
    1673                 :           4 :       return std::make_pair (Codepoint (0), additional_length_offset);
    1674                 :             :     }
    1675                 :             : 
    1676                 :          36 :   if (hex_num > 0x10ffff)
    1677                 :             :     {
    1678                 :           4 :       rust_error_at (get_current_location (),
    1679                 :             :                      "unicode escape cannot be larger than 10FFFF");
    1680                 :           4 :       return std::make_pair (Codepoint (0), additional_length_offset);
    1681                 :             :     }
    1682                 :             : 
    1683                 :             :   // return true;
    1684                 :          32 :   return std::make_pair (Codepoint (static_cast<uint32_t> (hex_num)),
    1685                 :             :                          additional_length_offset);
    1686                 :          46 : }
    1687                 :             : 
    1688                 :             : // Parses a byte character.
    1689                 :             : TokenPtr
    1690                 :          78 : Lexer::parse_byte_char (location_t loc)
    1691                 :             : {
    1692                 :          78 :   skip_input ();
    1693                 :          78 :   current_column++;
    1694                 :             :   // make current char the next character
    1695                 :          78 :   current_char = peek_input ();
    1696                 :             : 
    1697                 :          78 :   int length = 1;
    1698                 :             : 
    1699                 :             :   // char to save
    1700                 :          78 :   Codepoint byte_char = 0;
    1701                 :             : 
    1702                 :             :   // detect escapes
    1703                 :          78 :   if (current_char == '\\')
    1704                 :             :     {
    1705                 :          30 :       auto escape_length_pair = parse_escape ('\'');
    1706                 :          30 :       byte_char = std::get<0> (escape_length_pair);
    1707                 :          30 :       length += std::get<1> (escape_length_pair);
    1708                 :             : 
    1709                 :          30 :       current_char = peek_input ();
    1710                 :             : 
    1711                 :          30 :       if (current_char != '\'')
    1712                 :             :         {
    1713                 :           0 :           rust_error_at (get_current_location (), "unclosed %<byte char%>");
    1714                 :             :         }
    1715                 :             : 
    1716                 :          30 :       skip_input ();
    1717                 :          30 :       current_char = peek_input ();
    1718                 :          30 :       length++; // go to next char
    1719                 :             :     }
    1720                 :          48 :   else if (current_char != '\'')
    1721                 :             :     {
    1722                 :             :       // otherwise, get character from direct input character
    1723                 :          48 :       byte_char = current_char;
    1724                 :             : 
    1725                 :          48 :       if (!byte_char.is_ascii ())
    1726                 :             :         {
    1727                 :           2 :           rust_error_at (get_current_location (),
    1728                 :             :                          "non-ASCII character in %<byte char%>");
    1729                 :             :         }
    1730                 :             : 
    1731                 :          48 :       skip_input ();
    1732                 :          48 :       current_char = peek_input ();
    1733                 :          48 :       length++;
    1734                 :             : 
    1735                 :          48 :       if (current_char != '\'')
    1736                 :             :         {
    1737                 :           0 :           rust_error_at (get_current_location (), "unclosed %<byte char%>");
    1738                 :             :         }
    1739                 :             : 
    1740                 :          48 :       skip_input ();
    1741                 :          48 :       current_char = peek_input ();
    1742                 :          48 :       length++; // go to next char
    1743                 :             :     }
    1744                 :             :   else
    1745                 :             :     {
    1746                 :           0 :       rust_error_at (get_current_location (),
    1747                 :             :                      "no character inside %<%> for %<byte char%>");
    1748                 :             :     }
    1749                 :             : 
    1750                 :          78 :   current_column += length;
    1751                 :             : 
    1752                 :          78 :   loc += length - 1;
    1753                 :          78 :   return Token::make_byte_char (loc, byte_char.value);
    1754                 :             : }
    1755                 :             : 
    1756                 :             : // Parses a byte string.
    1757                 :             : TokenPtr
    1758                 :          56 : Lexer::parse_byte_string (location_t loc)
    1759                 :             : {
    1760                 :             :   // byte string
    1761                 :             : 
    1762                 :             :   // skip quote character
    1763                 :          56 :   skip_input ();
    1764                 :          56 :   current_column++;
    1765                 :             : 
    1766                 :          56 :   std::string str;
    1767                 :          56 :   str.reserve (16); // some sensible default
    1768                 :             : 
    1769                 :          56 :   current_char = peek_input ();
    1770                 :             : 
    1771                 :          56 :   const location_t string_begin_locus = get_current_location ();
    1772                 :             : 
    1773                 :         386 :   while (current_char != '"' && !current_char.is_eof ())
    1774                 :             :     {
    1775                 :         274 :       if (current_char == '\\')
    1776                 :             :         {
    1777                 :          30 :           int length = 1;
    1778                 :          30 :           auto escape_length_pair = parse_escape ('"');
    1779                 :          30 :           char output_char = std::get<0> (escape_length_pair);
    1780                 :             : 
    1781                 :          30 :           if (output_char == 0 && std::get<2> (escape_length_pair))
    1782                 :           0 :             length = std::get<1> (escape_length_pair) - 1;
    1783                 :             :           else
    1784                 :          30 :             length += std::get<1> (escape_length_pair);
    1785                 :             : 
    1786                 :          30 :           if (output_char != 0 || !std::get<2> (escape_length_pair))
    1787                 :          30 :             str += output_char;
    1788                 :             : 
    1789                 :          30 :           current_column += length;
    1790                 :             : 
    1791                 :          30 :           continue;
    1792                 :          30 :         }
    1793                 :             : 
    1794                 :         244 :       current_column++;
    1795                 :         244 :       if (current_char.value == '\n')
    1796                 :             :         {
    1797                 :          23 :           current_line++;
    1798                 :          23 :           current_column = 1;
    1799                 :             :           // tell line_table that new line starts
    1800                 :          23 :           start_line (current_line, max_column_hint);
    1801                 :             :         }
    1802                 :             : 
    1803                 :         244 :       str += current_char;
    1804                 :         244 :       skip_input ();
    1805                 :         244 :       current_char = peek_input ();
    1806                 :             :     }
    1807                 :             : 
    1808                 :          56 :   if (current_char == '"')
    1809                 :             :     {
    1810                 :          49 :       current_column++;
    1811                 :             : 
    1812                 :          49 :       skip_input ();
    1813                 :          49 :       current_char = peek_input ();
    1814                 :             :     }
    1815                 :           7 :   else if (current_char.is_eof ())
    1816                 :             :     {
    1817                 :           7 :       rust_error_at (string_begin_locus, "unended byte string literal");
    1818                 :           7 :       return Token::make (END_OF_FILE, get_current_location ());
    1819                 :             :     }
    1820                 :             :   else
    1821                 :             :     {
    1822                 :             :       rust_unreachable ();
    1823                 :             :     }
    1824                 :             : 
    1825                 :          49 :   str.shrink_to_fit ();
    1826                 :          49 :   loc += str.size () - 1;
    1827                 :             : 
    1828                 :          49 :   return Token::make_byte_string (loc, std::move (str));
    1829                 :          56 : }
    1830                 :             : 
    1831                 :             : // Parses a raw byte string.
    1832                 :             : TokenPtr
    1833                 :          24 : Lexer::parse_raw_byte_string (location_t loc)
    1834                 :             : {
    1835                 :             :   // raw byte string literals
    1836                 :          24 :   std::string str;
    1837                 :          24 :   str.reserve (16); // some sensible default
    1838                 :             : 
    1839                 :          24 :   int length = 1;
    1840                 :          24 :   int hash_count = 0;
    1841                 :             : 
    1842                 :             :   // get hash count at beginnning
    1843                 :          24 :   skip_input ();
    1844                 :          24 :   current_char = peek_input ();
    1845                 :          24 :   length++;
    1846                 :          55 :   while (current_char == '#')
    1847                 :             :     {
    1848                 :           7 :       hash_count++;
    1849                 :           7 :       length++;
    1850                 :             : 
    1851                 :           7 :       skip_input ();
    1852                 :           7 :       current_char = peek_input ();
    1853                 :             :     }
    1854                 :             : 
    1855                 :          24 :   if (current_char != '"')
    1856                 :             :     {
    1857                 :           0 :       rust_error_at (get_current_location (),
    1858                 :             :                      "raw byte string has no opening %<\"%>");
    1859                 :             :     }
    1860                 :             : 
    1861                 :          24 :   skip_input ();
    1862                 :          24 :   current_char = peek_input ();
    1863                 :          24 :   length++;
    1864                 :             : 
    1865                 :         186 :   while (true)
    1866                 :             :     {
    1867                 :         105 :       if (current_char == '"')
    1868                 :             :         {
    1869                 :          35 :           bool enough_hashes = true;
    1870                 :             : 
    1871                 :          35 :           for (int i = 0; i < hash_count; i++)
    1872                 :             :             {
    1873                 :          11 :               if (peek_input (i + 1) != '#')
    1874                 :             :                 {
    1875                 :             :                   enough_hashes = false;
    1876                 :             :                   break;
    1877                 :             :                 }
    1878                 :             :             }
    1879                 :             : 
    1880                 :          27 :           if (enough_hashes)
    1881                 :             :             {
    1882                 :             :               // skip enough input and peek enough input
    1883                 :          24 :               skip_input (hash_count);
    1884                 :          24 :               current_char = peek_input ();
    1885                 :          24 :               length += hash_count + 1;
    1886                 :          24 :               break;
    1887                 :             :             }
    1888                 :             :         }
    1889                 :             : 
    1890                 :          81 :       if (current_char.value > 127)
    1891                 :             :         {
    1892                 :           1 :           rust_error_at (get_current_location (),
    1893                 :             :                          "character %<%s%> in raw byte string out of range",
    1894                 :           1 :                          current_char.as_string ().c_str ());
    1895                 :           1 :           current_char = 0;
    1896                 :             :         }
    1897                 :             : 
    1898                 :          81 :       length++;
    1899                 :             : 
    1900                 :          81 :       str += current_char;
    1901                 :          81 :       skip_input ();
    1902                 :          81 :       current_char = peek_input ();
    1903                 :          81 :     }
    1904                 :             : 
    1905                 :          24 :   current_column += length;
    1906                 :             : 
    1907                 :          24 :   loc += length - 1;
    1908                 :             : 
    1909                 :          24 :   str.shrink_to_fit ();
    1910                 :             : 
    1911                 :          24 :   return Token::make_byte_string (loc, std::move (str));
    1912                 :          24 : }
    1913                 :             : 
    1914                 :             : // Parses a raw identifier.
    1915                 :             : TokenPtr
    1916                 :          79 : Lexer::parse_raw_identifier (location_t loc)
    1917                 :             : {
    1918                 :             :   // raw identifier
    1919                 :          79 :   std::string str;
    1920                 :          79 :   str.reserve (16); // default
    1921                 :             : 
    1922                 :          79 :   skip_input ();
    1923                 :          79 :   current_char = peek_input ();
    1924                 :             : 
    1925                 :          79 :   current_column += 2;
    1926                 :             : 
    1927                 :          79 :   bool first_is_underscore = current_char == '_';
    1928                 :             : 
    1929                 :          79 :   int length = 0;
    1930                 :          79 :   current_char = peek_input ();
    1931                 :             :   // loop through entire name
    1932                 :         465 :   while (is_identifier_continue (current_char.value))
    1933                 :             :     {
    1934                 :         307 :       length++;
    1935                 :             : 
    1936                 :         307 :       str += current_char;
    1937                 :         307 :       skip_input ();
    1938                 :         307 :       current_char = peek_input ();
    1939                 :             :     }
    1940                 :             : 
    1941                 :          79 :   current_column += length;
    1942                 :             : 
    1943                 :          79 :   rust_debug ("raw ident: %s", str.c_str ());
    1944                 :             : 
    1945                 :             :   // if just a single underscore, not an identifier
    1946                 :          79 :   if (first_is_underscore && length == 1)
    1947                 :           1 :     rust_error_at (get_current_location (),
    1948                 :             :                    "%<_%> is not a valid raw identifier");
    1949                 :             : 
    1950                 :          79 :   using namespace Rust::Values;
    1951                 :          79 :   std::set<std::string> invalid{
    1952                 :             :     Keywords::CRATE, Keywords::EXTERN_KW,  Keywords::SELF,
    1953                 :             :     Keywords::SUPER, Keywords::SELF_ALIAS,
    1954                 :         474 :   };
    1955                 :             : 
    1956                 :          79 :   if (invalid.find (str) != invalid.end ())
    1957                 :             :     {
    1958                 :           1 :       rust_error_at (get_current_location (),
    1959                 :             :                      "%qs is a forbidden raw identifier", str.c_str ());
    1960                 :             : 
    1961                 :           1 :       return nullptr;
    1962                 :             :     }
    1963                 :             :   else
    1964                 :             :     {
    1965                 :          78 :       str.shrink_to_fit ();
    1966                 :          78 :       loc += length - 1;
    1967                 :             : 
    1968                 :          78 :       return Token::make_identifier (loc, std::move (str));
    1969                 :             :     }
    1970                 :          79 : }
    1971                 :             : 
    1972                 :             : // skip broken string input (unterminated strings)
    1973                 :             : void
    1974                 :           0 : Lexer::skip_broken_string_input (Codepoint current_char)
    1975                 :             : {
    1976                 :           0 :   while (current_char != '"' && !current_char.is_eof ())
    1977                 :             :     {
    1978                 :           0 :       if (current_char == '\n')
    1979                 :             :         {
    1980                 :           0 :           current_line++;
    1981                 :           0 :           current_column = 1;
    1982                 :             :         }
    1983                 :             :       else
    1984                 :             :         {
    1985                 :           0 :           current_column++;
    1986                 :             :         }
    1987                 :           0 :       skip_input ();
    1988                 :           0 :       current_char = peek_input ();
    1989                 :             :     }
    1990                 :           0 :   if (current_char == '"')
    1991                 :             :     {
    1992                 :           0 :       current_column++;
    1993                 :             : 
    1994                 :           0 :       skip_input ();
    1995                 :           0 :       current_char = peek_input ();
    1996                 :             :     }
    1997                 :           0 :   rust_debug ("skipped to %d:%d due to bad quotes", current_line,
    1998                 :             :               current_column);
    1999                 :           0 : }
    2000                 :             : 
    2001                 :             : // Parses a string.
    2002                 :             : TokenPtr
    2003                 :        4868 : Lexer::parse_string (location_t loc)
    2004                 :             : {
    2005                 :        4868 :   std::string str;
    2006                 :        4868 :   str.reserve (16); // some sensible default
    2007                 :             : 
    2008                 :        4868 :   current_char = peek_input ();
    2009                 :             : 
    2010                 :        4868 :   const location_t string_begin_locus = get_current_location ();
    2011                 :             : 
    2012                 :             :   // FIXME: This fails if the input ends. How do we check for EOF?
    2013                 :       40449 :   while (current_char.value != '"' && !current_char.is_eof ())
    2014                 :             :     {
    2015                 :       30713 :       if (current_char.value == '\\')
    2016                 :             :         {
    2017                 :        2274 :           int length = 1;
    2018                 :             : 
    2019                 :             :           // parse escape
    2020                 :        2274 :           auto utf8_escape_pair = parse_utf8_escape ();
    2021                 :        2274 :           current_char = std::get<0> (utf8_escape_pair);
    2022                 :             : 
    2023                 :        2274 :           if (current_char == Codepoint (0) && std::get<2> (utf8_escape_pair))
    2024                 :          28 :             length = std::get<1> (utf8_escape_pair) - 1;
    2025                 :             :           else
    2026                 :        2246 :             length += std::get<1> (utf8_escape_pair);
    2027                 :             : 
    2028                 :        2274 :           if (current_char != Codepoint (0) || !std::get<2> (utf8_escape_pair))
    2029                 :        2246 :             str += current_char.as_string ();
    2030                 :             : 
    2031                 :        2274 :           current_column += length;
    2032                 :             : 
    2033                 :             :           // FIXME: should remove this but can't.
    2034                 :             :           // `parse_utf8_escape` does not update `current_char` correctly.
    2035                 :        2274 :           current_char = peek_input ();
    2036                 :        2274 :           continue;
    2037                 :        2274 :         }
    2038                 :             : 
    2039                 :       28439 :       current_column++;
    2040                 :       28439 :       if (current_char.value == '\n')
    2041                 :             :         {
    2042                 :          44 :           current_line++;
    2043                 :          44 :           current_column = 1;
    2044                 :             :           // tell line_table that new line starts
    2045                 :          44 :           start_line (current_line, max_column_hint);
    2046                 :             :         }
    2047                 :             : 
    2048                 :       28439 :       str += current_char;
    2049                 :       28439 :       skip_input ();
    2050                 :       28439 :       current_char = peek_input ();
    2051                 :             :     }
    2052                 :             : 
    2053                 :        4868 :   if (current_char.value == '"')
    2054                 :             :     {
    2055                 :        4854 :       current_column++;
    2056                 :             : 
    2057                 :        4854 :       skip_input ();
    2058                 :        4854 :       current_char = peek_input ();
    2059                 :             :     }
    2060                 :          14 :   else if (current_char.is_eof ())
    2061                 :             :     {
    2062                 :          14 :       rust_error_at (string_begin_locus, "unended string literal");
    2063                 :          14 :       return Token::make (END_OF_FILE, get_current_location ());
    2064                 :             :     }
    2065                 :             :   else
    2066                 :             :     {
    2067                 :             :       rust_unreachable ();
    2068                 :             :     }
    2069                 :             : 
    2070                 :        4854 :   str.shrink_to_fit ();
    2071                 :             : 
    2072                 :        4854 :   return Token::make_string (loc, std::move (str));
    2073                 :        4868 : }
    2074                 :             : 
    2075                 :             : // Parses an identifier or keyword.
    2076                 :             : TokenPtr
    2077                 :      155885 : Lexer::parse_identifier_or_keyword (location_t loc)
    2078                 :             : {
    2079                 :      155885 :   std::string str;
    2080                 :      155885 :   str.reserve (16); // default
    2081                 :      155885 :   str += current_char.as_string ();
    2082                 :             : 
    2083                 :      155885 :   bool first_is_underscore = current_char == '_';
    2084                 :             : 
    2085                 :      155885 :   int length = 1;
    2086                 :      155885 :   current_char = peek_input ();
    2087                 :             : 
    2088                 :             :   // loop through entire name
    2089                 :      704743 :   while (is_identifier_continue (current_char.value))
    2090                 :             :     {
    2091                 :      392973 :       auto s = current_char.as_string ();
    2092                 :      392973 :       length++;
    2093                 :             : 
    2094                 :      392973 :       str += current_char.as_string ();
    2095                 :      392973 :       skip_input ();
    2096                 :      392973 :       current_char = peek_input ();
    2097                 :      392973 :     }
    2098                 :             : 
    2099                 :      155885 :   current_column += length;
    2100                 :             : 
    2101                 :             :   // if just a single underscore, not an identifier
    2102                 :      155885 :   if (first_is_underscore && length == 1)
    2103                 :         436 :     return Token::make (UNDERSCORE, loc);
    2104                 :             : 
    2105                 :      155449 :   str.shrink_to_fit ();
    2106                 :             : 
    2107                 :      155449 :   loc += length - 1;
    2108                 :             : 
    2109                 :      155449 :   TokenId keyword = classify_keyword (str);
    2110                 :      155449 :   if (keyword == IDENTIFIER)
    2111                 :       99425 :     return Token::make_identifier (loc, std::move (str));
    2112                 :             :   else
    2113                 :       56024 :     return Token::make (keyword, loc);
    2114                 :      155885 : }
    2115                 :             : 
    2116                 :             : // Possibly returns a raw string token if it exists - otherwise returns null.
    2117                 :             : TokenPtr
    2118                 :        2456 : Lexer::maybe_parse_raw_string (location_t loc)
    2119                 :             : {
    2120                 :        2456 :   int peek_index = 0;
    2121                 :        2463 :   while (peek_input (peek_index) == '#')
    2122                 :           7 :     peek_index++;
    2123                 :             : 
    2124                 :        2456 :   if (peek_input (peek_index) == '"')
    2125                 :          23 :     return parse_raw_string (loc, peek_index);
    2126                 :             :   else
    2127                 :        2433 :     return nullptr;
    2128                 :             : }
    2129                 :             : 
    2130                 :             : // Returns a raw string token.
    2131                 :             : TokenPtr
    2132                 :          23 : Lexer::parse_raw_string (location_t loc, int initial_hash_count)
    2133                 :             : {
    2134                 :             :   // raw string literals
    2135                 :          23 :   std::string str;
    2136                 :          23 :   str.reserve (16); // some sensible default
    2137                 :             : 
    2138                 :          23 :   int length = 1 + initial_hash_count;
    2139                 :             : 
    2140                 :          23 :   if (initial_hash_count > 0)
    2141                 :           5 :     skip_input (initial_hash_count - 1);
    2142                 :             : 
    2143                 :          23 :   current_char = peek_input ();
    2144                 :             : 
    2145                 :          23 :   if (current_char != '"')
    2146                 :           0 :     rust_error_at (get_current_location (), "raw string has no opening %<\"%>");
    2147                 :             : 
    2148                 :          23 :   length++;
    2149                 :          23 :   skip_input ();
    2150                 :          23 :   current_char = peek_input ();
    2151                 :             : 
    2152                 :         119 :   while (!current_char.is_eof ())
    2153                 :             :     {
    2154                 :          96 :       if (current_char.value == '"')
    2155                 :             :         {
    2156                 :          34 :           bool enough_hashes = true;
    2157                 :             : 
    2158                 :          34 :           for (int i = 0; i < initial_hash_count; i++)
    2159                 :             :             {
    2160                 :          11 :               if (peek_input (i + 1) != '#')
    2161                 :             :                 {
    2162                 :             :                   enough_hashes = false;
    2163                 :             :                   break;
    2164                 :             :                 }
    2165                 :             :             }
    2166                 :             : 
    2167                 :          26 :           if (enough_hashes)
    2168                 :             :             {
    2169                 :             :               // skip enough input and peek enough input
    2170                 :          23 :               skip_input (initial_hash_count);
    2171                 :          23 :               current_char = peek_input ();
    2172                 :          23 :               length += initial_hash_count + 1;
    2173                 :          23 :               break;
    2174                 :             :             }
    2175                 :             :         }
    2176                 :             : 
    2177                 :          73 :       length++;
    2178                 :             : 
    2179                 :          73 :       str += current_char.as_string ();
    2180                 :          73 :       skip_input ();
    2181                 :          73 :       current_char = peek_input ();
    2182                 :             :     }
    2183                 :             : 
    2184                 :          23 :   current_column += length;
    2185                 :             : 
    2186                 :          23 :   loc += length - 1;
    2187                 :             : 
    2188                 :          23 :   str.shrink_to_fit ();
    2189                 :             : 
    2190                 :          23 :   return Token::make_string (loc, std::move (str));
    2191                 :          23 : }
    2192                 :             : 
    2193                 :             : template <typename IsDigitFunc>
    2194                 :             : TokenPtr
    2195                 :         108 : Lexer::parse_non_decimal_int_literal (location_t loc, IsDigitFunc is_digit_func,
    2196                 :             :                                       std::string existent_str, int base)
    2197                 :             : {
    2198                 :         108 :   int length = 1;
    2199                 :             : 
    2200                 :         108 :   skip_input ();
    2201                 :         108 :   current_char = peek_input ();
    2202                 :             : 
    2203                 :         108 :   length++;
    2204                 :             : 
    2205                 :             :   // loop through to add entire number to string
    2206                 :         830 :   while (is_digit_func (current_char.value) || current_char == '_')
    2207                 :             :     {
    2208                 :         722 :       if (current_char == '_')
    2209                 :             :         {
    2210                 :             :           // don't add _ to number
    2211                 :          21 :           skip_input ();
    2212                 :          21 :           current_char = peek_input ();
    2213                 :             : 
    2214                 :          21 :           length++;
    2215                 :             : 
    2216                 :          21 :           continue;
    2217                 :             :         }
    2218                 :             : 
    2219                 :         701 :       length++;
    2220                 :             : 
    2221                 :             :       // add raw numbers
    2222                 :         701 :       existent_str += current_char;
    2223                 :         701 :       skip_input ();
    2224                 :         701 :       current_char = peek_input ();
    2225                 :             :     }
    2226                 :             : 
    2227                 :             :   // convert value to decimal representation
    2228                 :         108 :   long dec_num = std::strtol (existent_str.c_str (), nullptr, base);
    2229                 :             : 
    2230                 :         108 :   existent_str = std::to_string (dec_num);
    2231                 :             : 
    2232                 :             :   // parse in type suffix if it exists
    2233                 :         108 :   auto type_suffix_pair = parse_in_type_suffix ();
    2234                 :         108 :   PrimitiveCoreType type_hint = type_suffix_pair.first;
    2235                 :         108 :   length += type_suffix_pair.second;
    2236                 :             : 
    2237                 :         108 :   current_column += length;
    2238                 :             : 
    2239                 :         108 :   if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
    2240                 :             :     {
    2241                 :           0 :       rust_error_at (get_current_location (),
    2242                 :             :                      "invalid type suffix %qs for integer (%s) literal",
    2243                 :             :                      get_type_hint_string (type_hint),
    2244                 :             :                      base == 16
    2245                 :             :                        ? "hex"
    2246                 :             :                        : (base == 8 ? "octal"
    2247                 :             :                                     : (base == 2 ? "binary"
    2248                 :             :                                                  : "<insert unknown base>")));
    2249                 :           0 :       return nullptr;
    2250                 :             :     }
    2251                 :             : 
    2252                 :         108 :   loc += length - 1;
    2253                 :             : 
    2254                 :         108 :   return Token::make_int (loc, std::move (existent_str), type_hint);
    2255                 :             : }
    2256                 :             : 
    2257                 :             : // Parses a hex, binary or octal int literal.
    2258                 :             : TokenPtr
    2259                 :         108 : Lexer::parse_non_decimal_int_literals (location_t loc)
    2260                 :             : {
    2261                 :         108 :   std::string str;
    2262                 :         108 :   str.reserve (16); // some sensible default
    2263                 :         108 :   str += current_char;
    2264                 :             : 
    2265                 :         108 :   current_char = peek_input ();
    2266                 :             : 
    2267                 :         108 :   if (current_char == 'x')
    2268                 :             :     {
    2269                 :             :       // hex (integer only)
    2270                 :          76 :       return parse_non_decimal_int_literal (loc, is_x_digit, str + "x", 16);
    2271                 :             :     }
    2272                 :          32 :   else if (current_char == 'o')
    2273                 :             :     {
    2274                 :             :       // octal (integer only)
    2275                 :          16 :       return parse_non_decimal_int_literal (loc, is_octal_digit,
    2276                 :          16 :                                             std::move (str), 8);
    2277                 :             :     }
    2278                 :          16 :   else if (current_char == 'b')
    2279                 :             :     {
    2280                 :             :       // binary (integer only)
    2281                 :          16 :       return parse_non_decimal_int_literal (loc, is_bin_digit, std::move (str),
    2282                 :          16 :                                             2);
    2283                 :             :     }
    2284                 :             :   else
    2285                 :             :     {
    2286                 :           0 :       return nullptr;
    2287                 :             :     }
    2288                 :         108 : }
    2289                 :             : 
    2290                 :             : // Parses a decimal-based int literal or float literal.
    2291                 :             : TokenPtr
    2292                 :       11803 : Lexer::parse_decimal_int_or_float (location_t loc)
    2293                 :             : {
    2294                 :       11803 :   std::string str;
    2295                 :       11803 :   str.reserve (16); // some sensible default
    2296                 :       11803 :   str += current_char;
    2297                 :             : 
    2298                 :       11803 :   int length = 1;
    2299                 :       11803 :   bool first_zero = current_char == '0';
    2300                 :             : 
    2301                 :       11803 :   current_char = peek_input ();
    2302                 :             : 
    2303                 :             :   // parse initial decimal integer (or first integer part of float) literal
    2304                 :       11803 :   auto initial_decimal = parse_in_decimal ();
    2305                 :       11803 :   str += std::get<0> (initial_decimal);
    2306                 :       11803 :   length += std::get<1> (initial_decimal);
    2307                 :             : 
    2308                 :             :   // detect float literal
    2309                 :             :   //
    2310                 :             :   // Note:
    2311                 :             :   //
    2312                 :             :   // We should not use is_float_digit () for this verification but instead
    2313                 :             :   // directly ISDIGIT because rust does not support non digit values right after
    2314                 :             :   // a dot.
    2315                 :             :   // The following value is not legal in rust:
    2316                 :             :   // let a = 3.e1;
    2317                 :             :   // A `0` should be put between the dot and the exponent to be valid
    2318                 :             :   // (eg. 3.0e1).
    2319                 :       11803 :   if (current_char == '.' && ISDIGIT (peek_input (1).value))
    2320                 :             :     {
    2321                 :             :       // float with a '.', parse another decimal into it
    2322                 :             : 
    2323                 :             :       // add . to str
    2324                 :         326 :       str += current_char;
    2325                 :         326 :       skip_input ();
    2326                 :         326 :       current_char = peek_input ();
    2327                 :         326 :       length++;
    2328                 :             : 
    2329                 :             :       // parse another decimal number for float
    2330                 :         326 :       auto second_decimal = parse_in_decimal ();
    2331                 :         326 :       str += std::get<0> (second_decimal);
    2332                 :         326 :       length += std::get<1> (second_decimal);
    2333                 :             : 
    2334                 :             :       // parse in exponent part if it exists
    2335                 :         326 :       auto exponent_pair = parse_in_exponent_part ();
    2336                 :         326 :       str += exponent_pair.first;
    2337                 :         326 :       length += exponent_pair.second;
    2338                 :             : 
    2339                 :             :       // parse in type suffix if it exists
    2340                 :         326 :       auto type_suffix_pair = parse_in_type_suffix ();
    2341                 :         326 :       PrimitiveCoreType type_hint = type_suffix_pair.first;
    2342                 :         326 :       length += type_suffix_pair.second;
    2343                 :             : 
    2344                 :         326 :       if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
    2345                 :         326 :           && type_hint != CORETYPE_UNKNOWN)
    2346                 :             :         {
    2347                 :           0 :           rust_error_at (get_current_location (),
    2348                 :             :                          "invalid type suffix %qs for floating-point literal",
    2349                 :             :                          get_type_hint_string (type_hint));
    2350                 :             :           // ignore invalid type suffix as everything else seems fine
    2351                 :           0 :           type_hint = CORETYPE_UNKNOWN;
    2352                 :             :         }
    2353                 :             : 
    2354                 :         326 :       current_column += length;
    2355                 :             : 
    2356                 :         326 :       loc += length - 1;
    2357                 :             : 
    2358                 :         326 :       str.shrink_to_fit ();
    2359                 :         326 :       return Token::make_float (loc, std::move (str), type_hint);
    2360                 :         326 :     }
    2361                 :       11477 :   else if (current_char == '.'
    2362                 :       11477 :            && check_valid_float_dot_end (peek_input (1).value))
    2363                 :             :     {
    2364                 :             :       // float that is just an integer with a terminating '.' character
    2365                 :             : 
    2366                 :             :       // add . to str
    2367                 :           8 :       str += current_char;
    2368                 :           8 :       skip_input ();
    2369                 :           8 :       current_char = peek_input ();
    2370                 :           8 :       length++;
    2371                 :             : 
    2372                 :             :       // type hint not allowed
    2373                 :             : 
    2374                 :           8 :       current_column += length;
    2375                 :             : 
    2376                 :           8 :       loc += length - 1;
    2377                 :             : 
    2378                 :           8 :       str.shrink_to_fit ();
    2379                 :           8 :       return Token::make_float (loc, std::move (str), CORETYPE_UNKNOWN);
    2380                 :             :     }
    2381                 :       11469 :   else if (current_char == 'E' || current_char == 'e')
    2382                 :             :     {
    2383                 :             :       // exponent float with no '.' character
    2384                 :             : 
    2385                 :             :       // parse exponent part
    2386                 :           0 :       auto exponent_pair = parse_in_exponent_part ();
    2387                 :           0 :       str += exponent_pair.first;
    2388                 :           0 :       length += exponent_pair.second;
    2389                 :             : 
    2390                 :             :       // parse in type suffix if it exists
    2391                 :           0 :       auto type_suffix_pair = parse_in_type_suffix ();
    2392                 :           0 :       PrimitiveCoreType type_hint = type_suffix_pair.first;
    2393                 :           0 :       length += type_suffix_pair.second;
    2394                 :             : 
    2395                 :           0 :       if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
    2396                 :           0 :           && type_hint != CORETYPE_UNKNOWN)
    2397                 :             :         {
    2398                 :           0 :           rust_error_at (get_current_location (),
    2399                 :             :                          "invalid type suffix %qs for floating-point literal",
    2400                 :             :                          get_type_hint_string (type_hint));
    2401                 :             :           // ignore invalid type suffix as everything else seems fine
    2402                 :           0 :           type_hint = CORETYPE_UNKNOWN;
    2403                 :             :         }
    2404                 :             : 
    2405                 :           0 :       current_column += length;
    2406                 :             : 
    2407                 :           0 :       loc += length - 1;
    2408                 :             : 
    2409                 :           0 :       str.shrink_to_fit ();
    2410                 :           0 :       return Token::make_float (loc, std::move (str), type_hint);
    2411                 :           0 :     }
    2412                 :             :   else
    2413                 :             :     {
    2414                 :             :       // is an integer
    2415                 :             : 
    2416                 :             :       // parse in type suffix if it exists
    2417                 :       11469 :       auto type_suffix_pair = parse_in_type_suffix ();
    2418                 :       11469 :       PrimitiveCoreType type_hint = type_suffix_pair.first;
    2419                 :             :       /* A "real" pure decimal doesn't have a suffix and no zero prefix.  */
    2420                 :       11469 :       if (type_hint == CORETYPE_UNKNOWN)
    2421                 :             :         {
    2422                 :       10508 :           bool pure_decimal = std::get<2> (initial_decimal);
    2423                 :       10508 :           if (pure_decimal && (!first_zero || str.size () == 1))
    2424                 :             :             type_hint = CORETYPE_PURE_DECIMAL;
    2425                 :             :         }
    2426                 :       11469 :       length += type_suffix_pair.second;
    2427                 :             : 
    2428                 :       11469 :       current_column += length;
    2429                 :             : 
    2430                 :       11469 :       loc += length - 1;
    2431                 :             : 
    2432                 :       11469 :       str.shrink_to_fit ();
    2433                 :       11469 :       return Token::make_int (loc, std::move (str), type_hint);
    2434                 :             :     }
    2435                 :       11803 : }
    2436                 :             : 
    2437                 :             : TokenPtr
    2438                 :         588 : Lexer::parse_char_or_lifetime (location_t loc)
    2439                 :             : {
    2440                 :         588 :   int length = 1;
    2441                 :             : 
    2442                 :         588 :   current_char = peek_input ();
    2443                 :         588 :   if (current_char.is_eof ())
    2444                 :           0 :     return nullptr;
    2445                 :             : 
    2446                 :             :   // parse escaped char literal
    2447                 :         588 :   if (current_char.value == '\\')
    2448                 :             :     {
    2449                 :             :       // parse escape
    2450                 :          23 :       auto utf8_escape_pair = parse_utf8_escape ();
    2451                 :          23 :       Codepoint escaped_char = std::get<0> (utf8_escape_pair);
    2452                 :          23 :       length += std::get<1> (utf8_escape_pair);
    2453                 :             : 
    2454                 :          23 :       if (peek_input ().value != '\'')
    2455                 :             :         {
    2456                 :           0 :           rust_error_at (get_current_location (), "unended character literal");
    2457                 :             :         }
    2458                 :             :       else
    2459                 :             :         {
    2460                 :          23 :           skip_input ();
    2461                 :          23 :           current_char = peek_input ();
    2462                 :          23 :           length++;
    2463                 :             :         }
    2464                 :             : 
    2465                 :          23 :       current_column += length;
    2466                 :             : 
    2467                 :          23 :       loc += length - 1;
    2468                 :             : 
    2469                 :          23 :       return Token::make_char (loc, escaped_char);
    2470                 :             :     }
    2471                 :             :   else
    2472                 :             :     {
    2473                 :         565 :       skip_input ();
    2474                 :             : 
    2475                 :         565 :       if (peek_input ().value == '\'')
    2476                 :             :         {
    2477                 :             :           // parse non-escaped char literal
    2478                 :         203 :           Codepoint non_escaped_char = current_char;
    2479                 :             : 
    2480                 :             :           // skip the ' character
    2481                 :         203 :           skip_input ();
    2482                 :         203 :           current_char = peek_input ();
    2483                 :             : 
    2484                 :             :           // TODO fix due to different widths of utf-8 chars?
    2485                 :         203 :           current_column += 3;
    2486                 :             : 
    2487                 :         203 :           loc += 2;
    2488                 :             : 
    2489                 :         203 :           return Token::make_char (loc, non_escaped_char);
    2490                 :             :         }
    2491                 :         362 :       else if (is_identifier_start (current_char.value))
    2492                 :             :         {
    2493                 :             :           // parse lifetime name
    2494                 :         362 :           std::string str;
    2495                 :         362 :           str += current_char.as_string ();
    2496                 :         362 :           length++;
    2497                 :             : 
    2498                 :         362 :           current_char = peek_input ();
    2499                 :         986 :           while (is_identifier_continue (current_char.value))
    2500                 :             :             {
    2501                 :         262 :               str += current_char.as_string ();
    2502                 :         262 :               skip_input ();
    2503                 :         262 :               current_char = peek_input ();
    2504                 :         262 :               length++;
    2505                 :             :             }
    2506                 :             : 
    2507                 :         362 :           current_column += length;
    2508                 :             : 
    2509                 :         362 :           loc += length - 1;
    2510                 :             : 
    2511                 :             :           // TODO some keywords cannot be used for a lifetime label #2306
    2512                 :             :           // https://doc.rust-lang.org/reference/tokens.html
    2513                 :             : 
    2514                 :         362 :           str.shrink_to_fit ();
    2515                 :         362 :           return Token::make_lifetime (loc, std::move (str));
    2516                 :         362 :         }
    2517                 :             :       else
    2518                 :             :         {
    2519                 :           0 :           rust_error_at (
    2520                 :             :             get_current_location (),
    2521                 :             :             "expected %' after character constant in character literal");
    2522                 :           0 :           return nullptr;
    2523                 :             :         }
    2524                 :             :     }
    2525                 :             : }
    2526                 :             : 
    2527                 :             : void
    2528                 :          89 : Lexer::split_current_token (TokenId new_left, TokenId new_right)
    2529                 :             : {
    2530                 :             :   /* TODO: assert that this TokenId is a "simple token" like punctuation and not
    2531                 :             :    * like "IDENTIFIER"? */
    2532                 :          89 :   location_t current_loc = peek_token ()->get_locus ();
    2533                 :          89 :   TokenPtr new_left_tok = Token::make (new_left, current_loc);
    2534                 :          89 :   TokenPtr new_right_tok = Token::make (new_right, current_loc + 1);
    2535                 :             : 
    2536                 :          89 :   token_queue.replace_current_value (std::move (new_left_tok));
    2537                 :          89 :   token_queue.insert (1, std::move (new_right_tok));
    2538                 :          89 : }
    2539                 :             : 
    2540                 :             : void
    2541                 :           2 : Lexer::split_current_token (std::vector<TokenPtr> new_tokens)
    2542                 :             : {
    2543                 :           2 :   rust_assert (new_tokens.size () > 0);
    2544                 :           4 :   token_queue.replace_current_value (new_tokens[0]);
    2545                 :             : 
    2546                 :           5 :   for (size_t i = 1; i < new_tokens.size (); i++)
    2547                 :             :     {
    2548                 :           6 :       token_queue.insert (i, new_tokens[i]);
    2549                 :             :     }
    2550                 :           2 : }
    2551                 :             : 
    2552                 :             : void
    2553                 :       88036 : Lexer::start_line (int current_line, int current_column)
    2554                 :             : {
    2555                 :       88036 :   if (line_map)
    2556                 :       88036 :     linemap_line_start (line_table, current_line, current_column);
    2557                 :       88036 : }
    2558                 :             : 
    2559                 :             : } // namespace Rust
    2560                 :             : 
    2561                 :             : #if CHECKING_P
    2562                 :             : 
    2563                 :             : namespace selftest {
    2564                 :             : 
    2565                 :             : // Checks if `src` has the same contents as the given characters
    2566                 :             : static void
    2567                 :           6 : assert_source_content (Rust::InputSource &src,
    2568                 :             :                        const std::vector<uint32_t> &expected)
    2569                 :             : {
    2570                 :           6 :   Rust::Codepoint src_char = src.next ();
    2571                 :          41 :   for (auto expected_char : expected)
    2572                 :             :     {
    2573                 :             :       // Make sure that `src` is not shorter than `expected`
    2574                 :          35 :       ASSERT_FALSE (src_char.is_eof ());
    2575                 :             :       // Checks skipped character is expeceted one.
    2576                 :          35 :       ASSERT_EQ (src_char.value, expected_char);
    2577                 :          35 :       src_char = src.next ();
    2578                 :             :     }
    2579                 :             :   // Checks if `src` and `chars` has the same length.
    2580                 :           6 :   ASSERT_TRUE (src_char.is_eof ());
    2581                 :           6 : }
    2582                 :             : 
    2583                 :             : static void
    2584                 :           4 : test_buffer_input_source (std::string str,
    2585                 :             :                           const std::vector<uint32_t> &expected)
    2586                 :             : {
    2587                 :           4 :   Rust::BufferInputSource source (str, 0);
    2588                 :           4 :   assert_source_content (source, expected);
    2589                 :           4 : }
    2590                 :             : 
    2591                 :             : static void
    2592                 :           2 : test_file_input_source (std::string str, const std::vector<uint32_t> &expected)
    2593                 :             : {
    2594                 :           2 :   FILE *tmpf = tmpfile ();
    2595                 :             :   // Moves to the first character
    2596                 :           2 :   fputs (str.c_str (), tmpf);
    2597                 :           2 :   std::rewind (tmpf);
    2598                 :           2 :   Rust::FileInputSource source (tmpf);
    2599                 :           2 :   assert_source_content (source, expected);
    2600                 :           2 : }
    2601                 :             : 
    2602                 :             : void
    2603                 :           1 : rust_input_source_test ()
    2604                 :             : {
    2605                 :             :   // ASCII
    2606                 :           1 :   std::string src = u8"_abcde\tXYZ\v\f";
    2607                 :           1 :   std::vector<uint32_t> expected
    2608                 :           1 :     = {'_', 'a', 'b', 'c', 'd', 'e', '\t', 'X', 'Y', 'Z', '\v', '\f'};
    2609                 :           1 :   test_buffer_input_source (src, expected);
    2610                 :             : 
    2611                 :             :   // BOM
    2612                 :           1 :   src = u8"\xef\xbb\xbfOK";
    2613                 :           1 :   expected = {'O', 'K'};
    2614                 :           1 :   test_buffer_input_source (src, expected);
    2615                 :             : 
    2616                 :             :   // Russian
    2617                 :           1 :   src = u8"приве́т";
    2618                 :           2 :   expected = {L'п',
    2619                 :             :               L'р',
    2620                 :             :               L'и',
    2621                 :             :               L'в',
    2622                 :             :               0x0435 /* CYRILLIC SMALL LETTER IE е */,
    2623                 :             :               0x301 /* COMBINING ACUTE ACCENT ́ */,
    2624                 :           1 :               L'т'};
    2625                 :           1 :   test_buffer_input_source (src, expected);
    2626                 :             : 
    2627                 :           1 :   src = u8"❤️🦀";
    2628                 :           2 :   expected = {0x2764 /* HEAVY BLACK HEART */,
    2629                 :           1 :               0xfe0f /* VARIATION SELECTOR-16 */, L'🦀'};
    2630                 :           1 :   test_buffer_input_source (src, expected);
    2631                 :             : 
    2632                 :           1 :   src = u8"こんにちは";
    2633                 :           1 :   expected = {L'こ', L'ん', L'に', L'ち', L'は'};
    2634                 :           1 :   test_file_input_source (src, expected);
    2635                 :             : 
    2636                 :           1 :   src = u8"👮‍♂👩‍⚕";
    2637                 :           1 :   expected
    2638                 :           2 :     = {0x1f46e /* POLICE OFFICER */,   0x200d /* ZERO WIDTH JOINER */,
    2639                 :             :        0x2642 /* MALE SIGN */,         0x1f469 /* WOMAN */,
    2640                 :           1 :        0x200d /* ZERO WIDTH JOINER */, 0x2695 /* STAFF OF AESCULAPIUS */};
    2641                 :           1 :   test_file_input_source (src, expected);
    2642                 :           1 : }
    2643                 :             : 
    2644                 :             : } // namespace selftest
    2645                 :             : 
    2646                 :             : #endif // CHECKING_P
        

Generated by: LCOV version 2.1-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.