From 0891689d2f2c17874f6eca7c9a62c3ace31a567c Mon Sep 17 00:00:00 2001 From: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:56:25 -0400 Subject: [PATCH] [syntax-errors] Check annotations in annotated assignments (#17283) Summary -- This PR extends the checks in #17101 and #17282 to annotated assignments after Python 3.13. Currently stacked on #17282 to include `await`. Test Plan -- New inline tests. These are simpler than the other cases because there's no place to put generics. --- .../inline/err/invalid_annotation_py314.py | 7 + .../inline/ok/valid_annotation_py313.py | 7 + .../ruff_python_parser/src/semantic_errors.rs | 26 +++ ...id_syntax@invalid_annotation_py314.py.snap | 215 ++++++++++++++++++ ...alid_syntax@valid_annotation_py313.py.snap | 178 +++++++++++++++ 5 files changed, 433 insertions(+) create mode 100644 crates/ruff_python_parser/resources/inline/err/invalid_annotation_py314.py create mode 100644 crates/ruff_python_parser/resources/inline/ok/valid_annotation_py313.py create mode 100644 crates/ruff_python_parser/tests/snapshots/invalid_syntax@invalid_annotation_py314.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@valid_annotation_py313.py.snap diff --git a/crates/ruff_python_parser/resources/inline/err/invalid_annotation_py314.py b/crates/ruff_python_parser/resources/inline/err/invalid_annotation_py314.py new file mode 100644 index 0000000000..ae647575d7 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/err/invalid_annotation_py314.py @@ -0,0 +1,7 @@ +# parse_options: {"target-version": "3.14"} +a: (x := 1) +def outer(): + b: (yield 1) + c: (yield from 1) +async def outer(): + d: (await 1) diff --git a/crates/ruff_python_parser/resources/inline/ok/valid_annotation_py313.py b/crates/ruff_python_parser/resources/inline/ok/valid_annotation_py313.py new file mode 100644 index 0000000000..63b09790b4 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/valid_annotation_py313.py @@ -0,0 +1,7 @@ +# parse_options: {"target-version": "3.13"} +a: (x := 1) +def outer(): + b: (yield 1) + c: (yield from 1) +async def outer(): + d: (await 1) diff --git a/crates/ruff_python_parser/src/semantic_errors.rs b/crates/ruff_python_parser/src/semantic_errors.rs index 07a4e516c4..8218c1ef58 100644 --- a/crates/ruff_python_parser/src/semantic_errors.rs +++ b/crates/ruff_python_parser/src/semantic_errors.rs @@ -119,6 +119,32 @@ impl SemanticSyntaxChecker { fn check_annotation(stmt: &ast::Stmt, ctx: &Ctx) { match stmt { + Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => { + if ctx.python_version() > PythonVersion::PY313 { + // test_ok valid_annotation_py313 + // # parse_options: {"target-version": "3.13"} + // a: (x := 1) + // def outer(): + // b: (yield 1) + // c: (yield from 1) + // async def outer(): + // d: (await 1) + + // test_err invalid_annotation_py314 + // # parse_options: {"target-version": "3.14"} + // a: (x := 1) + // def outer(): + // b: (yield 1) + // c: (yield from 1) + // async def outer(): + // d: (await 1) + let mut visitor = InvalidExpressionVisitor { + position: InvalidExpressionPosition::TypeAnnotation, + ctx, + }; + visitor.visit_expr(annotation); + } + } Stmt::FunctionDef(ast::StmtFunctionDef { type_params, parameters, diff --git a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@invalid_annotation_py314.py.snap b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@invalid_annotation_py314.py.snap new file mode 100644 index 0000000000..113edf8f44 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@invalid_annotation_py314.py.snap @@ -0,0 +1,215 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/err/invalid_annotation_py314.py +--- +## AST + +``` +Module( + ModModule { + range: 0..144, + body: [ + AnnAssign( + StmtAnnAssign { + range: 44..55, + target: Name( + ExprName { + range: 44..45, + id: Name("a"), + ctx: Store, + }, + ), + annotation: Named( + ExprNamed { + range: 48..54, + target: Name( + ExprName { + range: 48..49, + id: Name("x"), + ctx: Store, + }, + ), + value: NumberLiteral( + ExprNumberLiteral { + range: 53..54, + value: Int( + 1, + ), + }, + ), + }, + ), + value: None, + simple: true, + }, + ), + FunctionDef( + StmtFunctionDef { + range: 56..107, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("outer"), + range: 60..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + AnnAssign( + StmtAnnAssign { + range: 73..85, + target: Name( + ExprName { + range: 73..74, + id: Name("b"), + ctx: Store, + }, + ), + annotation: Yield( + ExprYield { + range: 77..84, + value: Some( + NumberLiteral( + ExprNumberLiteral { + range: 83..84, + value: Int( + 1, + ), + }, + ), + ), + }, + ), + value: None, + simple: true, + }, + ), + AnnAssign( + StmtAnnAssign { + range: 90..107, + target: Name( + ExprName { + range: 90..91, + id: Name("c"), + ctx: Store, + }, + ), + annotation: YieldFrom( + ExprYieldFrom { + range: 94..106, + value: NumberLiteral( + ExprNumberLiteral { + range: 105..106, + value: Int( + 1, + ), + }, + ), + }, + ), + value: None, + simple: true, + }, + ), + ], + }, + ), + FunctionDef( + StmtFunctionDef { + range: 108..143, + is_async: true, + decorator_list: [], + name: Identifier { + id: Name("outer"), + range: 118..123, + }, + type_params: None, + parameters: Parameters { + range: 123..125, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + AnnAssign( + StmtAnnAssign { + range: 131..143, + target: Name( + ExprName { + range: 131..132, + id: Name("d"), + ctx: Store, + }, + ), + annotation: Await( + ExprAwait { + range: 135..142, + value: NumberLiteral( + ExprNumberLiteral { + range: 141..142, + value: Int( + 1, + ), + }, + ), + }, + ), + value: None, + simple: true, + }, + ), + ], + }, + ), + ], + }, +) +``` +## Semantic Syntax Errors + + | +1 | # parse_options: {"target-version": "3.14"} +2 | a: (x := 1) + | ^^^^^^ Syntax Error: named expression cannot be used within a type annotation +3 | def outer(): +4 | b: (yield 1) + | + + + | +2 | a: (x := 1) +3 | def outer(): +4 | b: (yield 1) + | ^^^^^^^ Syntax Error: yield expression cannot be used within a type annotation +5 | c: (yield from 1) +6 | async def outer(): + | + + + | +3 | def outer(): +4 | b: (yield 1) +5 | c: (yield from 1) + | ^^^^^^^^^^^^ Syntax Error: yield expression cannot be used within a type annotation +6 | async def outer(): +7 | d: (await 1) + | + + + | +5 | c: (yield from 1) +6 | async def outer(): +7 | d: (await 1) + | ^^^^^^^ Syntax Error: await expression cannot be used within a type annotation + | diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@valid_annotation_py313.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@valid_annotation_py313.py.snap new file mode 100644 index 0000000000..9119467d84 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@valid_annotation_py313.py.snap @@ -0,0 +1,178 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/valid_annotation_py313.py +--- +## AST + +``` +Module( + ModModule { + range: 0..144, + body: [ + AnnAssign( + StmtAnnAssign { + range: 44..55, + target: Name( + ExprName { + range: 44..45, + id: Name("a"), + ctx: Store, + }, + ), + annotation: Named( + ExprNamed { + range: 48..54, + target: Name( + ExprName { + range: 48..49, + id: Name("x"), + ctx: Store, + }, + ), + value: NumberLiteral( + ExprNumberLiteral { + range: 53..54, + value: Int( + 1, + ), + }, + ), + }, + ), + value: None, + simple: true, + }, + ), + FunctionDef( + StmtFunctionDef { + range: 56..107, + is_async: false, + decorator_list: [], + name: Identifier { + id: Name("outer"), + range: 60..65, + }, + type_params: None, + parameters: Parameters { + range: 65..67, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + AnnAssign( + StmtAnnAssign { + range: 73..85, + target: Name( + ExprName { + range: 73..74, + id: Name("b"), + ctx: Store, + }, + ), + annotation: Yield( + ExprYield { + range: 77..84, + value: Some( + NumberLiteral( + ExprNumberLiteral { + range: 83..84, + value: Int( + 1, + ), + }, + ), + ), + }, + ), + value: None, + simple: true, + }, + ), + AnnAssign( + StmtAnnAssign { + range: 90..107, + target: Name( + ExprName { + range: 90..91, + id: Name("c"), + ctx: Store, + }, + ), + annotation: YieldFrom( + ExprYieldFrom { + range: 94..106, + value: NumberLiteral( + ExprNumberLiteral { + range: 105..106, + value: Int( + 1, + ), + }, + ), + }, + ), + value: None, + simple: true, + }, + ), + ], + }, + ), + FunctionDef( + StmtFunctionDef { + range: 108..143, + is_async: true, + decorator_list: [], + name: Identifier { + id: Name("outer"), + range: 118..123, + }, + type_params: None, + parameters: Parameters { + range: 123..125, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + AnnAssign( + StmtAnnAssign { + range: 131..143, + target: Name( + ExprName { + range: 131..132, + id: Name("d"), + ctx: Store, + }, + ), + annotation: Await( + ExprAwait { + range: 135..142, + value: NumberLiteral( + ExprNumberLiteral { + range: 141..142, + value: Int( + 1, + ), + }, + ), + }, + ), + value: None, + simple: true, + }, + ), + ], + }, + ), + ], + }, +) +```