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