From b27c961af55e3b9c60161933e49ff8ad43f03cb8 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Mon, 3 Mar 2025 13:55:08 -0500 Subject: [PATCH 1/6] [syntax-errors] `type` statements before Python 3.12 Summary -- Another simple one, just detect standalone `type` statements. I limited the diagnostic to `type` itself like [pyright]. That probably makes the most sense for more complicated examples. Test Plan -- Inline tests. [pyright]: https://pyright-play.net/?pythonVersion=3.8&strict=true&code=C4TwDgpgBAHlC8UCWA7YQ --- .../resources/inline/err/type_stmt_py311.py | 2 + .../resources/inline/ok/type_stmt_py312.py | 2 + crates/ruff_python_parser/src/error.rs | 3 ++ .../src/parser/statement.rs | 12 ++++++ .../invalid_syntax@type_stmt_py311.py.snap | 42 +++++++++++++++++++ .../valid_syntax@type_stmt_py312.py.snap | 35 ++++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 crates/ruff_python_parser/resources/inline/err/type_stmt_py311.py create mode 100644 crates/ruff_python_parser/resources/inline/ok/type_stmt_py312.py create mode 100644 crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@type_stmt_py312.py.snap diff --git a/crates/ruff_python_parser/resources/inline/err/type_stmt_py311.py b/crates/ruff_python_parser/resources/inline/err/type_stmt_py311.py new file mode 100644 index 0000000000000..37a11f089913b --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/err/type_stmt_py311.py @@ -0,0 +1,2 @@ +# parse_options: {"target-version": "3.11"} +type x = int diff --git a/crates/ruff_python_parser/resources/inline/ok/type_stmt_py312.py b/crates/ruff_python_parser/resources/inline/ok/type_stmt_py312.py new file mode 100644 index 0000000000000..f2e3aee5d7202 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/type_stmt_py312.py @@ -0,0 +1,2 @@ +# parse_options: {"target-version": "3.12"} +type x = int diff --git a/crates/ruff_python_parser/src/error.rs b/crates/ruff_python_parser/src/error.rs index 191068c2a1128..018ebe7e6268b 100644 --- a/crates/ruff_python_parser/src/error.rs +++ b/crates/ruff_python_parser/src/error.rs @@ -449,6 +449,7 @@ pub enum UnsupportedSyntaxErrorKind { Match, Walrus, ExceptStar, + TypeStmt, } impl Display for UnsupportedSyntaxError { @@ -457,6 +458,7 @@ impl Display for UnsupportedSyntaxError { UnsupportedSyntaxErrorKind::Match => "`match` statement", UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)", UnsupportedSyntaxErrorKind::ExceptStar => "`except*`", + UnsupportedSyntaxErrorKind::TypeStmt => "`type` statement", }; write!( f, @@ -474,6 +476,7 @@ impl UnsupportedSyntaxErrorKind { UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310, UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38, UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311, + UnsupportedSyntaxErrorKind::TypeStmt => PythonVersion::PY312, } } } diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index 5f7f870a00c2f..65bf82fcd21b0 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -890,6 +890,8 @@ impl<'src> Parser<'src> { let start = self.node_start(); self.bump(TokenKind::Type); + let type_range = self.node_range(start); + let mut name = Expr::Name(self.parse_name()); helpers::set_expr_ctx(&mut name, ExprContext::Store); @@ -909,6 +911,16 @@ impl<'src> Parser<'src> { // type x = x := 1 let value = self.parse_conditional_expression_or_higher(); + // test_ok type_stmt_py312 + // # parse_options: {"target-version": "3.12"} + // type x = int + + // test_err type_stmt_py311 + // # parse_options: {"target-version": "3.11"} + // type x = int + + self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::TypeStmt, type_range); + ast::StmtTypeAlias { name: Box::new(name), type_params, diff --git a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap new file mode 100644 index 0000000000000..00e165e1aec12 --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap @@ -0,0 +1,42 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/err/type_stmt_py311.py +--- +## AST + +``` +Module( + ModModule { + range: 0..57, + body: [ + TypeAlias( + StmtTypeAlias { + range: 44..56, + name: Name( + ExprName { + range: 49..50, + id: Name("x"), + ctx: Store, + }, + ), + type_params: None, + value: Name( + ExprName { + range: 53..56, + id: Name("int"), + ctx: Load, + }, + ), + }, + ), + ], + }, +) +``` +## Unsupported Syntax Errors + + | +1 | # parse_options: {"target-version": "3.11"} +2 | type x = int + | ^^^^ Syntax Error: Cannot use `type` statement on Python 3.11 (syntax was added in Python 3.12) + | diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@type_stmt_py312.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@type_stmt_py312.py.snap new file mode 100644 index 0000000000000..62eeead56c1fc --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@type_stmt_py312.py.snap @@ -0,0 +1,35 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/type_stmt_py312.py +--- +## AST + +``` +Module( + ModModule { + range: 0..57, + body: [ + TypeAlias( + StmtTypeAlias { + range: 44..56, + name: Name( + ExprName { + range: 49..50, + id: Name("x"), + ctx: Store, + }, + ), + type_params: None, + value: Name( + ExprName { + range: 53..56, + id: Name("int"), + ctx: Load, + }, + ), + }, + ), + ], + }, +) +``` From c991bf262ff1629fb758a06733597a6a066dd068 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Tue, 4 Mar 2025 12:05:31 -0500 Subject: [PATCH 2/6] use current_token_range --- crates/ruff_python_parser/src/parser/statement.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index a1bfa53116be5..6469e20c6599c 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -888,10 +888,9 @@ impl<'src> Parser<'src> { /// See: fn parse_type_alias_statement(&mut self) -> ast::StmtTypeAlias { let start = self.node_start(); + let type_range = self.current_token_range(); self.bump(TokenKind::Type); - let type_range = self.node_range(start); - let mut name = Expr::Name(self.parse_name()); helpers::set_expr_ctx(&mut name, ExprContext::Store); From 99931646b58595d0653ec3f451d8eabe1b2692ce Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Tue, 4 Mar 2025 12:07:38 -0500 Subject: [PATCH 3/6] move tests to right after parsing `type` --- .../ruff_python_parser/src/parser/statement.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index 6469e20c6599c..d4baf2a3b180b 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -891,6 +891,14 @@ impl<'src> Parser<'src> { let type_range = self.current_token_range(); self.bump(TokenKind::Type); + // test_ok type_stmt_py312 + // # parse_options: {"target-version": "3.12"} + // type x = int + + // test_err type_stmt_py311 + // # parse_options: {"target-version": "3.11"} + // type x = int + let mut name = Expr::Name(self.parse_name()); helpers::set_expr_ctx(&mut name, ExprContext::Store); @@ -910,14 +918,6 @@ impl<'src> Parser<'src> { // type x = x := 1 let value = self.parse_conditional_expression_or_higher(); - // test_ok type_stmt_py312 - // # parse_options: {"target-version": "3.12"} - // type x = int - - // test_err type_stmt_py311 - // # parse_options: {"target-version": "3.11"} - // type x = int - self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::TypeStmt, type_range); ast::StmtTypeAlias { From 423ba8d5bf7bc16e845df9fa68ac1040e700b0c2 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Tue, 4 Mar 2025 12:08:14 -0500 Subject: [PATCH 4/6] TypeStmt -> TypeAliasStatement --- crates/ruff_python_parser/src/error.rs | 6 +++--- crates/ruff_python_parser/src/parser/statement.rs | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/ruff_python_parser/src/error.rs b/crates/ruff_python_parser/src/error.rs index 8ab460c7a4bca..21ba6ca60c1b6 100644 --- a/crates/ruff_python_parser/src/error.rs +++ b/crates/ruff_python_parser/src/error.rs @@ -449,7 +449,7 @@ pub enum UnsupportedSyntaxErrorKind { Match, Walrus, ExceptStar, - TypeStmt, + TypeAliasStatement, TypeParamDefault, } @@ -459,7 +459,7 @@ impl Display for UnsupportedSyntaxError { UnsupportedSyntaxErrorKind::Match => "`match` statement", UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)", UnsupportedSyntaxErrorKind::ExceptStar => "`except*`", - UnsupportedSyntaxErrorKind::TypeStmt => "`type` statement", + UnsupportedSyntaxErrorKind::TypeAliasStatement => "`type` statement", UnsupportedSyntaxErrorKind::TypeParamDefault => { "Cannot set default type for a type parameter" } @@ -480,7 +480,7 @@ impl UnsupportedSyntaxErrorKind { UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310, UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38, UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311, - UnsupportedSyntaxErrorKind::TypeStmt => PythonVersion::PY312, + UnsupportedSyntaxErrorKind::TypeAliasStatement => PythonVersion::PY312, UnsupportedSyntaxErrorKind::TypeParamDefault => PythonVersion::PY313, } } diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index d4baf2a3b180b..d346418dc352a 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -918,7 +918,10 @@ impl<'src> Parser<'src> { // type x = x := 1 let value = self.parse_conditional_expression_or_higher(); - self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::TypeStmt, type_range); + self.add_unsupported_syntax_error( + UnsupportedSyntaxErrorKind::TypeAliasStatement, + type_range, + ); ast::StmtTypeAlias { name: Box::new(name), From 0482e514d2e117e3f7df84cdbf2c7d833c2622d6 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Tue, 4 Mar 2025 12:09:58 -0500 Subject: [PATCH 5/6] add "alias" to message and fix missing "Cannot use" prefixes --- crates/ruff_python_parser/src/error.rs | 8 ++++---- .../snapshots/invalid_syntax@type_stmt_py311.py.snap | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/ruff_python_parser/src/error.rs b/crates/ruff_python_parser/src/error.rs index 21ba6ca60c1b6..97af7a20a8d33 100644 --- a/crates/ruff_python_parser/src/error.rs +++ b/crates/ruff_python_parser/src/error.rs @@ -456,10 +456,10 @@ pub enum UnsupportedSyntaxErrorKind { impl Display for UnsupportedSyntaxError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let kind = match self.kind { - UnsupportedSyntaxErrorKind::Match => "`match` statement", - UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)", - UnsupportedSyntaxErrorKind::ExceptStar => "`except*`", - UnsupportedSyntaxErrorKind::TypeAliasStatement => "`type` statement", + UnsupportedSyntaxErrorKind::Match => "Cannot use `match` statement", + UnsupportedSyntaxErrorKind::Walrus => "Cannot use named assignment expression (`:=`)", + UnsupportedSyntaxErrorKind::ExceptStar => "Cannot use `except*`", + UnsupportedSyntaxErrorKind::TypeAliasStatement => "Cannot use `type` alias statement", UnsupportedSyntaxErrorKind::TypeParamDefault => { "Cannot set default type for a type parameter" } diff --git a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap index 00e165e1aec12..dfb7a15121bce 100644 --- a/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap +++ b/crates/ruff_python_parser/tests/snapshots/invalid_syntax@type_stmt_py311.py.snap @@ -38,5 +38,5 @@ Module( | 1 | # parse_options: {"target-version": "3.11"} 2 | type x = int - | ^^^^ Syntax Error: Cannot use `type` statement on Python 3.11 (syntax was added in Python 3.12) + | ^^^^ Syntax Error: Cannot use `type` alias statement on Python 3.11 (syntax was added in Python 3.12) | From 9f3f6bd17874da52881d5ec6872fd77f3c1953f8 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Tue, 4 Mar 2025 12:14:44 -0500 Subject: [PATCH 6/6] move error too --- crates/ruff_python_parser/src/parser/statement.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index d346418dc352a..deabb8fe1ec79 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -899,6 +899,11 @@ impl<'src> Parser<'src> { // # parse_options: {"target-version": "3.11"} // type x = int + self.add_unsupported_syntax_error( + UnsupportedSyntaxErrorKind::TypeAliasStatement, + type_range, + ); + let mut name = Expr::Name(self.parse_name()); helpers::set_expr_ctx(&mut name, ExprContext::Store); @@ -918,11 +923,6 @@ impl<'src> Parser<'src> { // type x = x := 1 let value = self.parse_conditional_expression_or_higher(); - self.add_unsupported_syntax_error( - UnsupportedSyntaxErrorKind::TypeAliasStatement, - type_range, - ); - ast::StmtTypeAlias { name: Box::new(name), type_params,