Expand Semantic Syntax Coverage (#17725)

Re: #17526 

## Summary
Adds tests to red knot and `linter.rs` for the semantic syntax. 

Specifically add tests for `ReboundComprehensionVariable`,
`DuplicateTypeParameter`, and `MultipleCaseAssignment`.

Refactor the `test_async_comprehension_in_sync_comprehension` →
`test_semantic_error` to be more general for all semantic syntax test
cases.

## Test Plan
This is a test.

## Question
I'm happy to contribute more tests the coming days. 

Should that happen here or should we merge this PR such that the
refactor `test_async_comprehension_in_sync_comprehension` →
`test_semantic_error` is available on main and others can chime in, too?
This commit is contained in:
Max Mynter 2025-04-30 16:14:08 +02:00 committed by GitHub
parent ad1a8da4d1
commit f584b66824
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 128 additions and 10 deletions

View file

@ -130,6 +130,62 @@ async def g():
(x async for x in g())
```
## Rebound comprehension variable
Walrus operators cannot rebind variables already in use as iterators:
```py
# error: [invalid-syntax] "assignment expression cannot rebind comprehension variable"
[x := 2 for x in range(10)]
# error: [invalid-syntax] "assignment expression cannot rebind comprehension variable"
{y := 5 for y in range(10)}
```
## Multiple case assignments
Variable names in pattern matching must be unique within a single pattern:
```toml
[environment]
python-version = "3.10"
```
```py
x = [1, 2]
match x:
# error: [invalid-syntax] "multiple assignments to name `a` in pattern"
case [a, a]:
pass
case _:
pass
d = {"key": "value"}
match d:
# error: [invalid-syntax] "multiple assignments to name `b` in pattern"
case {"key": b, "other": b}:
pass
```
## Duplicate type parameter
Type parameter names must be unique in a generic class or function definition:
```toml
[environment]
python-version = "3.12"
```
```py
# error: [invalid-syntax] "duplicate type parameter"
class C[T, T]:
pass
# error: [invalid-syntax] "duplicate type parameter"
def f[X, Y, X]():
pass
```
## `await` outside async function
This error includes `await`, `async for`, `async with`, and `async` comprehensions.

View file

@ -1006,19 +1006,22 @@ mod tests {
}
#[test_case(
"error_on_310",
"async_in_sync_error_on_310",
"async def f(): return [[x async for x in foo(n)] for n in range(3)]",
PythonVersion::PY310
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"okay_on_311",
"async_in_sync_okay_on_311",
"async def f(): return [[x async for x in foo(n)] for n in range(3)]",
PythonVersion::PY311
PythonVersion::PY311,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"okay_on_310",
"async_in_sync_okay_on_310",
"async def test(): return [[x async for x in elements(n)] async for n in range(3)]",
PythonVersion::PY310
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"deferred_function_body",
@ -1028,15 +1031,46 @@ mod tests {
def g(): ...
[x async for x in foo()]
",
PythonVersion::PY310
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case("false_positive", "[x async for x in y]", PythonVersion::PY310)]
fn test_async_comprehension_in_sync_comprehension(
#[test_case(
"async_in_sync_false_positive",
"[x async for x in y]",
PythonVersion::PY310,
"AsyncComprehensionOutsideAsyncFunction"
)]
#[test_case(
"rebound_comprehension",
"[x:= 2 for x in range(2)]",
PythonVersion::PY310,
"ReboundComprehensionVariable"
)]
#[test_case(
"duplicate_type_param",
"class C[T, T]: pass",
PythonVersion::PY312,
"DuplicateTypeParameter"
)]
#[test_case(
"multiple_case_assignment",
"
match x:
case [a, a]:
pass
case _:
pass
",
PythonVersion::PY310,
"MultipleCaseAssignment"
)]
fn test_semantic_errors(
name: &str,
contents: &str,
python_version: PythonVersion,
error_type: &str,
) {
let snapshot = format!("async_comprehension_in_sync_comprehension_{name}_{python_version}");
let snapshot = format!("semantic_syntax_error_{error_type}_{name}_{python_version}");
let messages = test_snippet_syntax_errors(
contents,
&LinterSettings {

View file

@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:1:12: SyntaxError: duplicate type parameter
|
1 | class C[T, T]: pass
| ^
|

View file

@ -0,0 +1,11 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:3:14: SyntaxError: multiple assignments to name `a` in pattern
|
2 | match x:
3 | case [a, a]:
| ^
4 | pass
5 | case _:
|

View file

@ -0,0 +1,8 @@
---
source: crates/ruff_linter/src/linter.rs
---
<filename>:1:2: SyntaxError: assignment expression cannot rebind comprehension variable
|
1 | [x:= 2 for x in range(2)]
| ^
|