|
7 | 7 |
|
8 | 8 | #define MAKE_TOKEN(token_type) _PyLexer_token_setup(tok, token, token_type, p_start, p_end) |
9 | 9 |
|
| 10 | +static int |
| 11 | +string_error_token(struct tok_state *tok, struct token *token, |
| 12 | + const char *start, _PyTok_Loc location) |
| 13 | +{ |
| 14 | + tok->diagnostic = (_PyTokenizer_Diagnostic){ |
| 15 | + .location = {location.lineno, location.byte_col + 1}, |
| 16 | + .text_span = _PyLexer_BufferSpan(tok, start - location.byte_col, tok->inp), |
| 17 | + }; |
| 18 | + int type = _PyLexer_token_setup(tok, token, ERRORTOKEN, NULL, NULL); |
| 19 | + token->start_loc = location; |
| 20 | + token->end_loc = (_PyTok_Loc){location.lineno, -1}; |
| 21 | + return type; |
| 22 | +} |
| 23 | + |
10 | 24 | int |
11 | 25 | _PyLexer_set_ftstring_expr(struct tok_state* tok, struct token *token, char c) { |
12 | 26 | assert(token != NULL); |
@@ -347,55 +361,52 @@ _PyLexer_scan_string(struct tok_state *tok, struct token *token, int c) |
347 | 361 | break; |
348 | 362 | } |
349 | 363 | if (c == EOF || (quote_size == 1 && c == '\n')) { |
350 | | - assert(tok->multi_line_start != NULL); |
351 | | - // shift the tok_state's location into |
352 | | - // the start of string, and report the error |
353 | | - // from the initial quote character |
354 | | - tok->cur = (char *)tok->start; |
355 | | - tok->cur++; |
356 | | - tok->line_start = tok->multi_line_start; |
357 | | - int start = tok->lineno; |
358 | | - tok->lineno = tok->first_lineno; |
359 | | - |
360 | | - if (INSIDE_FSTRING(tok)) { |
361 | | - /* When we are in an f-string, before raising the |
362 | | - * unterminated string literal error, check whether |
363 | | - * does the initial quote matches with f-strings quotes |
364 | | - * and if it is, then this must be a missing '}' token |
365 | | - * so raise the proper error */ |
366 | | - tokenizer_mode *the_current_tok = TOK_GET_MODE(tok); |
367 | | - if (the_current_tok->quote == quote && |
368 | | - the_current_tok->quote_size == quote_size) { |
369 | | - return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, |
370 | | - "%c-string: expecting '}'", TOK_GET_STRING_PREFIX(tok))); |
| 364 | + int end_lineno = tok->lineno; |
| 365 | + _PyTok_Loc location = {tok->first_lineno, tok->starting_col_offset}; |
| 366 | + const char *line = tok->start - location.byte_col; |
| 367 | + Py_ssize_t cursor_offset = (Py_ssize_t)location.byte_col + 1; |
| 368 | + |
| 369 | + const tokenizer_mode *state = INSIDE_FSTRING(tok) ? TOK_GET_MODE(tok) : NULL; |
| 370 | + if (state != NULL) { |
| 371 | + /* A matching quote belongs to the surrounding formatted |
| 372 | + * string, so the expression is missing its closing brace. */ |
| 373 | + if (state->quote == quote && state->quote_size == quote_size) { |
| 374 | + _PyTokenizer_syntaxerror_at( |
| 375 | + tok, line, cursor_offset, location.lineno, -1, -1, |
| 376 | + "%c-string: expecting '}'", |
| 377 | + TOK_GET_STRING_PREFIX(tok)); |
| 378 | + return string_error_token(tok, token, tok->start, location); |
371 | 379 | } |
372 | 380 | } |
373 | 381 |
|
374 | 382 | if (quote_size == 3) { |
375 | | - _PyTokenizer_syntaxerror(tok, "unterminated triple-quoted string literal" |
376 | | - " (detected at line %d)", start); |
| 383 | + _PyTokenizer_syntaxerror_at( |
| 384 | + tok, line, cursor_offset, location.lineno, -1, -1, |
| 385 | + "unterminated triple-quoted string literal" |
| 386 | + " (detected at line %d)", end_lineno); |
377 | 387 | if (c != '\n') { |
378 | 388 | tok->done = E_EOFS; |
379 | 389 | } |
380 | | - return MAKE_TOKEN(ERRORTOKEN); |
| 390 | + return string_error_token(tok, token, tok->start, location); |
381 | 391 | } |
382 | 392 | else { |
383 | 393 | if (has_escaped_quote) { |
384 | | - _PyTokenizer_syntaxerror( |
385 | | - tok, |
| 394 | + _PyTokenizer_syntaxerror_at( |
| 395 | + tok, line, cursor_offset, location.lineno, -1, -1, |
386 | 396 | "unterminated string literal (detected at line %d); " |
387 | 397 | "perhaps you escaped the end quote?", |
388 | | - start |
| 398 | + end_lineno |
389 | 399 | ); |
390 | 400 | } else { |
391 | | - _PyTokenizer_syntaxerror( |
392 | | - tok, "unterminated string literal (detected at line %d)", start |
| 401 | + _PyTokenizer_syntaxerror_at( |
| 402 | + tok, line, cursor_offset, location.lineno, -1, -1, |
| 403 | + "unterminated string literal (detected at line %d)", end_lineno |
393 | 404 | ); |
394 | 405 | } |
395 | 406 | if (c != '\n') { |
396 | 407 | tok->done = E_EOLS; |
397 | 408 | } |
398 | | - return MAKE_TOKEN(ERRORTOKEN); |
| 409 | + return string_error_token(tok, token, tok->start, location); |
399 | 410 | } |
400 | 411 | } |
401 | 412 | if (c == quote) { |
@@ -516,32 +527,31 @@ _PyLexer_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, st |
516 | 527 | return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok)); |
517 | 528 | } |
518 | 529 |
|
519 | | - assert(tok->multi_line_start != NULL); |
520 | | - // shift the tok_state's location into |
521 | | - // the start of string, and report the error |
522 | | - // from the initial quote character |
523 | | - tok->cur = (char *)current_tok->start; |
524 | | - tok->cur++; |
525 | | - tok->line_start = current_tok->multi_line_start; |
526 | | - int start = tok->lineno; |
527 | | - |
528 | | - tokenizer_mode *the_current_tok = TOK_GET_MODE(tok); |
529 | | - tok->lineno = the_current_tok->first_line; |
| 530 | + int end_lineno = tok->lineno; |
| 531 | + _PyTok_Loc location = {current_tok->first_line, |
| 532 | + (int)(current_tok->start - current_tok->multi_line_start)}; |
| 533 | + const char *line = current_tok->multi_line_start; |
| 534 | + Py_ssize_t cursor_offset = (Py_ssize_t)location.byte_col + 1; |
530 | 535 |
|
531 | 536 | if (current_tok->quote_size == 3) { |
532 | | - _PyTokenizer_syntaxerror(tok, |
533 | | - "unterminated triple-quoted %c-string literal" |
534 | | - " (detected at line %d)", |
535 | | - TOK_GET_STRING_PREFIX(tok), start); |
| 537 | + _PyTokenizer_syntaxerror_at( |
| 538 | + tok, line, cursor_offset, location.lineno, -1, -1, |
| 539 | + "unterminated triple-quoted %c-string literal" |
| 540 | + " (detected at line %d)", |
| 541 | + TOK_GET_STRING_PREFIX(tok), end_lineno); |
536 | 542 | if (c != '\n') { |
537 | 543 | tok->done = E_EOFS; |
538 | 544 | } |
539 | | - return MAKE_TOKEN(ERRORTOKEN); |
| 545 | + return string_error_token(tok, token, |
| 546 | + current_tok->start, location); |
540 | 547 | } |
541 | 548 | else { |
542 | | - return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, |
543 | | - "unterminated %c-string literal (detected at" |
544 | | - " line %d)", TOK_GET_STRING_PREFIX(tok), start)); |
| 549 | + _PyTokenizer_syntaxerror_at( |
| 550 | + tok, line, cursor_offset, location.lineno, -1, -1, |
| 551 | + "unterminated %c-string literal (detected at line %d)", |
| 552 | + TOK_GET_STRING_PREFIX(tok), end_lineno); |
| 553 | + return string_error_token(tok, token, |
| 554 | + current_tok->start, location); |
545 | 555 | } |
546 | 556 | } |
547 | 557 |
|
|
0 commit comments