From b4a1ebdfe390d3cd170645c3516fa3fdc7445994 Mon Sep 17 00:00:00 2001 From: Max Mynter <32773644+maxmynter@users.noreply.github.com> Date: Fri, 9 May 2025 20:54:05 +0200 Subject: [PATCH] [semantic-syntax-tests] `IrrefutableCasePattern`, `SingleStarredAssignment`, `WriteToDebug`, `InvalidExpression` (#17748) Re: #17526 ## Summary Add integration test for semantic syntax for `IrrefutableCasePattern`, `SingleStarredAssignment`, `WriteToDebug`, and `InvalidExpression`. ## Notes - Following @ntBre's suggestion, I will keep the test coming in batches like this over the next few days in separate PRs to keep the review load per PR manageable while also not spamming too many. - I did not add a test for `del __debug__` which is one of the examples in `crates/ruff_python_parser/src/semantic_errors.rs:1051`. For python version `<= 3.8` there is no error and for `>=3.9` the error is not `WriteToDebug` but `SyntaxError: cannot delete __debug__ on Python 3.9 (syntax was removed in 3.9)`. - The `blacken-docs` bypass is necessary because otherwise the test does not pass pre-commit checks; but we want to check for this faulty syntax. ## Test Plan This is a test. --- crates/ruff_linter/src/linter.rs | 89 ++++++++++++++++++ ...sion_walrus_in_return_annotation_3.12.snap | 8 ++ ...ression_yield_from_in_base_class_3.12.snap | 9 ++ ...d_expression_yield_in_type_alias_3.12.snap | 8 ++ ...d_expression_yield_in_type_param_3.12.snap | 8 ++ ...irrefutable_case_pattern_capture_3.10.snap | 11 +++ ...rrefutable_case_pattern_wildcard_3.10.snap | 11 +++ ...gnment_single_starred_assignment_3.10.snap | 8 ++ ...rror_WriteToDebug_write_to_debug_3.10.snap | 8 ++ ..._write_to_debug_class_type_param_3.12.snap | 9 ++ ...write_to_debug_in_function_param_3.10.snap | 9 ++ .../diagnostics/semantic_syntax_errors.md | 94 ++++++++++++++++++- 12 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_walrus_in_return_annotation_3.12.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_from_in_base_class_3.12.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_alias_3.12.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_param_3.12.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_capture_3.10.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_wildcard_3.10.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_SingleStarredAssignment_single_starred_assignment_3.10.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_3.10.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_class_type_param_3.12.snap create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_in_function_param_3.10.snap diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index dc8fccdf9b..7427b75d9c 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -1116,6 +1116,95 @@ mod tests { PythonVersion::PY310, "InvalidStarExpression" )] + #[test_case( + "irrefutable_case_pattern_wildcard", + " + match value: + case _: + pass + case 1: + pass + ", + PythonVersion::PY310, + "IrrefutableCasePattern" + )] + #[test_case( + "irrefutable_case_pattern_capture", + " + match value: + case irrefutable: + pass + case 1: + pass + ", + PythonVersion::PY310, + "IrrefutableCasePattern" + )] + #[test_case( + "single_starred_assignment", + "*a = [1, 2, 3, 4]", + PythonVersion::PY310, + "SingleStarredAssignment" + )] + #[test_case( + "write_to_debug", + " + __debug__ = False + ", + PythonVersion::PY310, + "WriteToDebug" + )] + #[test_case( + "write_to_debug_in_function_param", + " + def process(__debug__): + pass + ", + PythonVersion::PY310, + "WriteToDebug" + )] + #[test_case( + "write_to_debug_class_type_param", + " + class Generic[__debug__]: + pass + ", + PythonVersion::PY312, + "WriteToDebug" + )] + #[test_case( + "invalid_expression_yield_in_type_param", + " + type X[T: (yield 1)] = int + ", + PythonVersion::PY312, + "InvalidExpression" + )] + #[test_case( + "invalid_expression_yield_in_type_alias", + " + type Y = (yield 1) + ", + PythonVersion::PY312, + "InvalidExpression" + )] + #[test_case( + "invalid_expression_walrus_in_return_annotation", + " + def f[T](x: int) -> (y := 3): return x + ", + PythonVersion::PY312, + "InvalidExpression" + )] + #[test_case( + "invalid_expression_yield_from_in_base_class", + " + class C[T]((yield from [object])): + pass + ", + PythonVersion::PY312, + "InvalidExpression" + )] fn test_semantic_errors( name: &str, contents: &str, diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_walrus_in_return_annotation_3.12.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_walrus_in_return_annotation_3.12.snap new file mode 100644 index 0000000000..a09dda153f --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_walrus_in_return_annotation_3.12.snap @@ -0,0 +1,8 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:22: SyntaxError: named expression cannot be used within a generic definition + | +2 | def f[T](x: int) -> (y := 3): return x + | ^^^^^^ + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_from_in_base_class_3.12.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_from_in_base_class_3.12.snap new file mode 100644 index 0000000000..6f71b926a1 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_from_in_base_class_3.12.snap @@ -0,0 +1,9 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:13: SyntaxError: yield expression cannot be used within a generic definition + | +2 | class C[T]((yield from [object])): + | ^^^^^^^^^^^^^^^^^^^ +3 | pass + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_alias_3.12.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_alias_3.12.snap new file mode 100644 index 0000000000..39c220e6a1 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_alias_3.12.snap @@ -0,0 +1,8 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:11: SyntaxError: yield expression cannot be used within a type alias + | +2 | type Y = (yield 1) + | ^^^^^^^ + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_param_3.12.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_param_3.12.snap new file mode 100644 index 0000000000..9f0c12aaac --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_InvalidExpression_invalid_expression_yield_in_type_param_3.12.snap @@ -0,0 +1,8 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:12: SyntaxError: yield expression cannot be used within a TypeVar bound + | +2 | type X[T: (yield 1)] = int + | ^^^^^^^ + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_capture_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_capture_3.10.snap new file mode 100644 index 0000000000..502f151558 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_capture_3.10.snap @@ -0,0 +1,11 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:3:10: SyntaxError: name capture `irrefutable` makes remaining patterns unreachable + | +2 | match value: +3 | case irrefutable: + | ^^^^^^^^^^^ +4 | pass +5 | case 1: + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_wildcard_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_wildcard_3.10.snap new file mode 100644 index 0000000000..cc54b4776b --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_IrrefutableCasePattern_irrefutable_case_pattern_wildcard_3.10.snap @@ -0,0 +1,11 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:3:10: SyntaxError: wildcard makes remaining patterns unreachable + | +2 | match value: +3 | case _: + | ^ +4 | pass +5 | case 1: + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_SingleStarredAssignment_single_starred_assignment_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_SingleStarredAssignment_single_starred_assignment_3.10.snap new file mode 100644 index 0000000000..ecc8f955d8 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_SingleStarredAssignment_single_starred_assignment_3.10.snap @@ -0,0 +1,8 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:1:1: SyntaxError: starred assignment target must be in a list or tuple + | +1 | *a = [1, 2, 3, 4] + | ^^ + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_3.10.snap new file mode 100644 index 0000000000..baebbdf41f --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_3.10.snap @@ -0,0 +1,8 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:1: SyntaxError: cannot assign to `__debug__` + | +2 | __debug__ = False + | ^^^^^^^^^ + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_class_type_param_3.12.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_class_type_param_3.12.snap new file mode 100644 index 0000000000..44139dc6cf --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_class_type_param_3.12.snap @@ -0,0 +1,9 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:15: SyntaxError: cannot assign to `__debug__` + | +2 | class Generic[__debug__]: + | ^^^^^^^^^ +3 | pass + | diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_in_function_param_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_in_function_param_3.10.snap new file mode 100644 index 0000000000..935ad56054 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_WriteToDebug_write_to_debug_in_function_param_3.10.snap @@ -0,0 +1,9 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +:2:13: SyntaxError: cannot assign to `__debug__` + | +2 | def process(__debug__): + | ^^^^^^^^^ +3 | pass + | diff --git a/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md b/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md index 25fadb9905..08813572f8 100644 --- a/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md +++ b/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md @@ -228,11 +228,102 @@ for x in *range(10): pass ``` +## Irrefutable case pattern + +Irrefutable patterns, i.e. wildcard or capture patterns, must be the last case in a match statement. +Following case statements are unreachable. + +```toml +[environment] +python-version = "3.12" +``` + +```py +value = 5 + +match value: + # error: [invalid-syntax] "wildcard makes remaining patterns unreachable" + case _: # Irrefutable wildcard pattern + pass + case 5: + pass + +match value: + # error: [invalid-syntax] "name capture `variable` makes remaining patterns unreachable" + case variable: # Irrefutable capture pattern + pass + case 10: + pass +``` + +## Single starred assignment + +Starred assignment targets cannot appear by themselves. They must be in the context of a list or +tuple. + +```py +# error: [invalid-syntax] "starred assignment target must be in a list or tuple" +*a = [1, 2, 3, 4] +``` + +## Write to debug + +The special Python builtin `__debug__` should not be modified. + +```toml +[environment] +python-version = "3.12" +``` + +```py +# error: [invalid-syntax] "cannot assign to `__debug__`" +__debug__ = False + +# error: [invalid-syntax] "cannot assign to `__debug__`" +def process(__debug__): + pass + +# error: [invalid-syntax] "cannot assign to `__debug__`" +class Generic[__debug__]: + pass +``` + +## Invalid expression + +Certain expressions like `yield` or inlined walrus assignments are not valid in specific contexts. + +```toml +[environment] +python-version = "3.12" +``` + +```py +# error: [invalid-type-form] "`yield` expressions are not allowed in type expressions" +# error: [invalid-syntax] "yield expression cannot be used within a TypeVar bound" +# error: [invalid-syntax] "`yield` statement outside of a function" +type X[T: (yield 1)] = int + +# error: [invalid-type-form] "`yield` expressions are not allowed in type expressions" +# error: [invalid-syntax] "yield expression cannot be used within a type alias" +# error: [invalid-syntax] "`yield` statement outside of a function" +type Y = (yield 1) + +# error: [invalid-type-form] "Named expressions are not allowed in type expressions" +# error: [invalid-syntax] "named expression cannot be used within a generic definition" +def f[T](x: int) -> (y := 3): + return x + +# error: [invalid-syntax] "`yield from` statement outside of a function" +# error: [invalid-syntax] "yield expression cannot be used within a generic definition" +class C[T]((yield from [object])): + pass +``` + ## `await` outside async function This error includes `await`, `async for`, `async with`, and `async` comprehensions. -```python +```py async def elements(n): yield n @@ -245,7 +336,6 @@ def _(): # error: [invalid-syntax] "`async with` outside of an asynchronous function" async with elements(1) as x: ... - # error: [invalid-syntax] "cannot use an asynchronous comprehension outside of an asynchronous function on Python 3.9 (syntax was added in 3.11)" # error: [invalid-syntax] "asynchronous comprehension outside of an asynchronous function" [x async for x in elements(1)] ```