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