diff --git a/Lib/test/test_eof.py b/Lib/test/test_eof.py index 2c74e2d87..f5a0bc569 100644 --- a/Lib/test/test_eof.py +++ b/Lib/test/test_eof.py @@ -9,7 +9,6 @@ from test.support import warnings_helper import unittest class EOFTestCase(unittest.TestCase): - @unittest.expectedFailure # TODO: RUSTPYTHON def test_EOF_single_quote(self): expect = "unterminated string literal (detected at line 1) (, line 1)" for quote in ("'", "\""): @@ -19,7 +18,7 @@ class EOFTestCase(unittest.TestCase): self.assertEqual(str(cm.exception), expect) self.assertEqual(cm.exception.offset, 1) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON def test_EOFS(self): expect = ("unterminated triple-quoted string literal (detected at line 3) (, line 1)") with self.assertRaises(SyntaxError) as cm: @@ -46,7 +45,7 @@ class EOFTestCase(unittest.TestCase): self.assertEqual(cm.exception.text, "ä = '''thîs is ") self.assertEqual(cm.exception.offset, 5) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON @force_not_colorized def test_EOFS_with_file(self): expect = ("(, line 1)") @@ -87,7 +86,7 @@ class EOFTestCase(unittest.TestCase): ' ^', 'SyntaxError: unterminated triple-quoted string literal (detected at line 4)']) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON @warnings_helper.ignore_warnings(category=SyntaxWarning) def test_eof_with_line_continuation(self): expect = "unexpected EOF while parsing (, line 1)" @@ -95,7 +94,7 @@ class EOFTestCase(unittest.TestCase): compile('"\\Xhh" \\', '', 'exec') self.assertEqual(str(cm.exception), expect) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON def test_line_continuation_EOF(self): """A continuation at the end of input must be an error; bpo2180.""" expect = 'unexpected EOF while parsing (, line 1)' @@ -128,7 +127,7 @@ class EOFTestCase(unittest.TestCase): exec('\\') self.assertEqual(str(cm.exception), expect) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipIf(not sys.executable, "sys.executable required") @force_not_colorized def test_line_continuation_EOF_from_file_bpo2180(self): diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 825d711ed..1c6e536c7 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -156,15 +156,15 @@ SyntaxError: cannot assign to expression Traceback (most recent call last): SyntaxError: cannot assign to conditional expression ->>> a = 42 if True # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> a = 42 if True Traceback (most recent call last): SyntaxError: expected 'else' after 'if' expression ->>> a = (42 if True) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> a = (42 if True) Traceback (most recent call last): SyntaxError: expected 'else' after 'if' expression ->>> a = [1, 42 if True, 4] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> a = [1, 42 if True, 4] Traceback (most recent call last): SyntaxError: expected 'else' after 'if' expression @@ -275,7 +275,7 @@ SyntaxError: cannot assign to function call Traceback (most recent call last): SyntaxError: cannot assign to function call ->>> with a as b # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> with a as b Traceback (most recent call last): SyntaxError: expected ':' @@ -478,47 +478,47 @@ SyntaxError: var-keyword argument cannot have default value Traceback (most recent call last): SyntaxError: var-keyword argument cannot have default value ->>> def foo(a,*a, b, **c, d): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,*a, b, **c, d): ... pass Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> def foo(a,*a, b, **c, d=4): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,*a, b, **c, d=4): ... pass Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> def foo(a,*a, b, **c, *d): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,*a, b, **c, *d): ... pass Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> def foo(a,*a, b, **c, **d): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,*a, b, **c, **d): ... pass Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> def foo(a=1,/,**b,/,c): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a=1,/,**b,/,c): ... pass Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> def foo(*b,*d): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(*b,*d): ... pass Traceback (most recent call last): SyntaxError: * argument may appear only once ->>> def foo(a,*b,c,*d,*e,c): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,*b,c,*d,*e,c): ... pass Traceback (most recent call last): SyntaxError: * argument may appear only once ->>> def foo(a,b,/,c,*b,c,*d,*e,c): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,b,/,c,*b,c,*d,*e,c): ... pass Traceback (most recent call last): SyntaxError: * argument may appear only once ->>> def foo(a,b,/,c,*b,c,*d,**e): # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> def foo(a,b,/,c,*b,c,*d,**e): ... pass Traceback (most recent call last): SyntaxError: * argument may appear only once @@ -583,39 +583,39 @@ SyntaxError: var-positional argument cannot have default value Traceback (most recent call last): SyntaxError: var-keyword argument cannot have default value ->>> lambda a, *a, b, **c, d: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a, *a, b, **c, d: None Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> lambda a,*a, b, **c, d=4: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a,*a, b, **c, d=4: None Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> lambda a,*a, b, **c, *d: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a,*a, b, **c, *d: None Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> lambda a,*a, b, **c, **d: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a,*a, b, **c, **d: None Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> lambda a=1,/,**b,/,c: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a=1,/,**b,/,c: None Traceback (most recent call last): SyntaxError: arguments cannot follow var-keyword argument ->>> lambda *b,*d: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda *b,*d: None Traceback (most recent call last): SyntaxError: * argument may appear only once ->>> lambda a,*b,c,*d,*e,c: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a,*b,c,*d,*e,c: None Traceback (most recent call last): SyntaxError: * argument may appear only once ->>> lambda a,b,/,c,*b,c,*d,*e,c: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a,b,/,c,*b,c,*d,*e,c: None Traceback (most recent call last): SyntaxError: * argument may appear only once ->>> lambda a,b,/,c,*b,c,*d,**e: None # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE +>>> lambda a,b,/,c,*b,c,*d,**e: None Traceback (most recent call last): SyntaxError: * argument may appear only once @@ -1093,27 +1093,27 @@ leading to spurious errors. Missing ':' before suites: - >>> def f() # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> def f() ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> def f[T]() # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> def f[T]() ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> class A # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> class A ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> class A[T] # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> class A[T] ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> class A[T]() # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> class A[T]() ... pass Traceback (most recent call last): SyntaxError: expected ':' @@ -1123,7 +1123,7 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: invalid syntax - >>> if 1 # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> if 1 ... pass ... elif 1: ... pass @@ -1132,7 +1132,7 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: expected ':' - >>> if 1: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> if 1: ... pass ... elif 1 ... pass @@ -1141,7 +1141,7 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: expected ':' - >>> if 1: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> if 1: ... pass ... elif 1: ... pass @@ -1150,7 +1150,7 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: expected ':' - >>> for x in range(10) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> for x in range(10) ... pass Traceback (most recent call last): SyntaxError: expected ':' @@ -1160,47 +1160,47 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: invalid syntax - >>> while True # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> while True ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with blech as something # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with blech as something ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with blech # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with blech ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with blech, block as something # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with blech, block as something ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with blech, block as something, bluch # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with blech, block as something, bluch ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with (blech as something) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with (blech as something) ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with (blech) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with (blech) ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with (blech, block as something) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with (blech, block as something) ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> with (blech, block as something, bluch) # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> with (blech, block as something, bluch) ... pass Traceback (most recent call last): SyntaxError: expected ':' @@ -1210,19 +1210,19 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: invalid syntax. Did you mean 'and'? - >>> try # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> try ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> try: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> try: ... pass ... except ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> match x # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> match x ... case list(): ... pass Traceback (most recent call last): @@ -1234,13 +1234,13 @@ Missing ':' before suites: Traceback (most recent call last): SyntaxError: invalid syntax - >>> match x: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> match x: ... case list() ... pass Traceback (most recent call last): SyntaxError: expected ':' - >>> match x: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> match x: ... case [y] if y > 0 ... pass Traceback (most recent call last): @@ -1287,27 +1287,27 @@ Missing ':' before suites: Missing parens after function definition - >>> def f: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> def f: Traceback (most recent call last): SyntaxError: expected '(' - >>> async def f: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> async def f: Traceback (most recent call last): SyntaxError: expected '(' - >>> def f -> int: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> def f -> int: Traceback (most recent call last): SyntaxError: expected '(' - >>> async def f -> int: # type: int # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> async def f -> int: # type: int Traceback (most recent call last): SyntaxError: expected '(' - >>> async def f[T]: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> async def f[T]: Traceback (most recent call last): SyntaxError: expected '(' - >>> def f[T] -> str: # TODO: RUSTPYTHON; Wrong error message # doctest: +EXPECTED_FAILURE + >>> def f[T] -> str: Traceback (most recent call last): SyntaxError: expected '(' @@ -3092,7 +3092,6 @@ class A: with self.subTest(f"out of range: {n=}"): self._check_error(get_code(n), "too many statically nested blocks") - @unittest.expectedFailure # TODO: RUSTPYTHON def test_barry_as_flufl_with_syntax_errors(self): # The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if # is reading the wrong token in the presence of syntax errors later diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index 24e54a862..7976e37b4 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "parser")] +use ruff_python_ast::token::TokenKind; + use ruff_python_parser::{InterpolatedStringErrorType, LexicalErrorType, ParseErrorType}; use rustpython_common::wtf8::Wtf8Buf; @@ -59,9 +62,28 @@ impl SyntaxErrorInfo { self.narrow_caret = narrow_caret; } + #[cfg(feature = "parser")] + #[must_use] + const fn handle_expected_token(expected: &TokenKind, found: &TokenKind) -> &'static str { + match (*expected, *found) { + (TokenKind::Colon, TokenKind::Newline) => "expected ':'", + + (TokenKind::Lpar, _) => "expected '('", + + (TokenKind::Else, y) if !matches!(y, TokenKind::Colon) => { + "expected 'else' after 'if' expression" + } + + _ => "invalid syntax", + } + } + #[cfg(feature = "parser")] fn analyze_compile_error(&mut self, compile_error: &CompileError) { - let CompileError::Parse(ParseError { error, .. }) = compile_error else { + let CompileError::Parse(ParseError { + error, location, .. + }) = compile_error + else { return; }; @@ -86,11 +108,9 @@ impl SyntaxErrorInfo { ParseErrorType::UnexpectedExpressionToken => format!("invalid syntax: {}", self.msg), - ParseErrorType::Lexical(LexicalErrorType::UnrecognizedToken { .. }) - | ParseErrorType::SimpleStatementsOnSameLine - | ParseErrorType::SimpleAndCompoundStatementOnSameLine - | ParseErrorType::ExpectedToken { .. } - | ParseErrorType::ExpectedExpression => "invalid syntax".into(), + ParseErrorType::ExpectedToken { expected, found } => { + Self::handle_expected_token(expected, found).into() + } ParseErrorType::InvalidStarredExpressionUsage => { self.with_narrow_caret(true); @@ -104,7 +124,10 @@ impl SyntaxErrorInfo { } ParseErrorType::Lexical(LexicalErrorType::UnclosedStringError) => { - "unterminated string literal".into() + format!( + "unterminated string literal (detected at line {})", + location.line + ) } ParseErrorType::EmptyTypeParams => "Type parameter list cannot be empty".into(), @@ -146,6 +169,21 @@ impl SyntaxErrorInfo { "iterable argument unpacking follows keyword argument unpacking".into() } + ParseErrorType::ParamAfterVarKeywordParam => { + "arguments cannot follow var-keyword argument".into() + } + + ParseErrorType::Lexical(LexicalErrorType::UnrecognizedToken { .. }) + | ParseErrorType::SimpleStatementsOnSameLine + | ParseErrorType::SimpleAndCompoundStatementOnSameLine + | ParseErrorType::ExpectedExpression => "invalid syntax".into(), + + ParseErrorType::OtherError(s) + if s.starts_with("Expected an identifier, but found a keyword") => + { + "invalid syntax".into() + } + ParseErrorType::OtherError(s) if s.eq_ignore_ascii_case( "bytes literal cannot be mixed with non-bytes literals", @@ -154,12 +192,6 @@ impl SyntaxErrorInfo { "cannot mix bytes and nonbytes literals".into() } - ParseErrorType::OtherError(s) - if s.starts_with("Expected an identifier, but found a keyword") => - { - "invalid syntax".into() - } - ParseErrorType::OtherError(s) if s.eq_ignore_ascii_case("positional patterns cannot follow keyword patterns") => { @@ -207,11 +239,25 @@ impl SyntaxErrorInfo { } ParseErrorType::OtherError(s) - if s.eq_ignore_ascii_case("Expected `except` or `finally` after `try` block") => + if s.eq_ignore_ascii_case("expected `except` or `finally` after `try` block") => { "expected 'except' or 'finally' block".into() } + ParseErrorType::OtherError(s) + if s.eq_ignore_ascii_case("only one '*' parameter allowed") => + { + "* argument may appear only once".into() + } + + ParseErrorType::OtherError(s) + if s.eq_ignore_ascii_case( + r#"cannot have both 'except' and 'except*' on the same 'try'"#, + ) => + { + r#"cannot have both 'except' and 'except*' on the same 'try'"#.into() + } + _ => return, };