[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.

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
This is a test.
This commit is contained in:
Max Mynter
2025-05-09 20:54:05 +02:00
committed by GitHub
parent 7a48477c67
commit b4a1ebdfe3
12 changed files with 270 additions and 2 deletions

View File

@@ -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,

View File

@@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:22: SyntaxError: named expression cannot be used within a generic definition
|
2 | def f[T](x: int) -> (y := 3): return x
| ^^^^^^
|

View File

@@ -0,0 +1,9 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:13: SyntaxError: yield expression cannot be used within a generic definition
|
2 | class C[T]((yield from [object])):
| ^^^^^^^^^^^^^^^^^^^
3 | pass
|

View File

@@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:11: SyntaxError: yield expression cannot be used within a type alias
|
2 | type Y = (yield 1)
| ^^^^^^^
|

View File

@@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:12: SyntaxError: yield expression cannot be used within a TypeVar bound
|
2 | type X[T: (yield 1)] = int
| ^^^^^^^
|

View File

@@ -0,0 +1,11 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:3:10: SyntaxError: name capture `irrefutable` makes remaining patterns unreachable
|
2 | match value:
3 | case irrefutable:
| ^^^^^^^^^^^
4 | pass
5 | case 1:
|

View File

@@ -0,0 +1,11 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:3:10: SyntaxError: wildcard makes remaining patterns unreachable
|
2 | match value:
3 | case _:
| ^
4 | pass
5 | case 1:
|

View File

@@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:1:1: SyntaxError: starred assignment target must be in a list or tuple
|
1 | *a = [1, 2, 3, 4]
| ^^
|

View File

@@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:1: SyntaxError: cannot assign to `__debug__`
|
2 | __debug__ = False
| ^^^^^^^^^
|

View File

@@ -0,0 +1,9 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:15: SyntaxError: cannot assign to `__debug__`
|
2 | class Generic[__debug__]:
| ^^^^^^^^^
3 | pass
|

View File

@@ -0,0 +1,9 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:2:13: SyntaxError: cannot assign to `__debug__`
|
2 | def process(__debug__):
| ^^^^^^^^^
3 | pass
|

View File

@@ -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)]
```