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