From cdc4e861581b440427a81701bc49382c6cc62fdf Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 21 Feb 2023 13:42:20 -0500 Subject: [PATCH] Add support for TryStar (#3089) --- Cargo.lock | 8 +-- Cargo.toml | 4 +- .../test/fixtures/tryceratops/TRY301.py | 18 +++++++ crates/ruff/src/ast/branch_detection.rs | 6 +++ crates/ruff/src/ast/comparable.rs | 17 +++++++ crates/ruff/src/ast/helpers.rs | 6 +++ crates/ruff/src/ast/operations.rs | 5 +- crates/ruff/src/ast/visitor.rs | 13 +++++ crates/ruff/src/autofix/helpers.rs | 6 +++ crates/ruff/src/checkers/ast.rs | 13 +++++ .../rules/jump_statement_in_finally.rs | 1 + .../rules/raise_without_from_inside_except.rs | 3 +- .../rules/flake8_pytest_style/rules/raises.rs | 3 +- crates/ruff/src/rules/flake8_return/rules.rs | 5 +- .../ruff/src/rules/flake8_return/visitor.rs | 2 +- crates/ruff/src/rules/isort/track.rs | 6 +++ crates/ruff/src/rules/mccabe/rules.rs | 6 +++ .../rules/pylint/rules/too_many_branches.rs | 6 +++ .../rules/pylint/rules/too_many_statements.rs | 6 +++ .../pylint/rules/useless_else_on_loop.rs | 7 +++ .../tryceratops/rules/raise_within_try.rs | 2 +- .../rules/tryceratops/rules/verbose_raise.rs | 3 ++ ...ps__tests__raise-within-try_TRY301.py.snap | 32 +++++++++++- crates/ruff/src/source_code/generator.rs | 49 ++++++++++++++++++- .../ruff_python_formatter/src/core/visitor.rs | 13 +++++ crates/ruff_python_formatter/src/cst.rs | 23 +++++++++ .../ruff_python_formatter/src/format/stmt.rs | 28 +++++++++++ crates/ruff_python_formatter/src/newlines.rs | 6 +++ .../ruff_python_formatter/src/parentheses.rs | 1 + crates/ruff_python_formatter/src/trivia.rs | 6 +++ 30 files changed, 289 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42ae2ef186..b2d4c3ad19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2146,7 +2146,7 @@ dependencies = [ [[package]] name = "rustpython-ast" version = "0.2.0" -source = "git+https://github.com/RustPython/RustPython.git?rev=61b48f108982d865524f86624a9d5bc2ae3bccef#61b48f108982d865524f86624a9d5bc2ae3bccef" +source = "git+https://github.com/RustPython/RustPython.git?rev=ef873b4b606f0a58e3640b6186416631fdeead26#ef873b4b606f0a58e3640b6186416631fdeead26" dependencies = [ "num-bigint", "rustpython-compiler-core", @@ -2155,7 +2155,7 @@ dependencies = [ [[package]] name = "rustpython-common" version = "0.2.0" -source = "git+https://github.com/RustPython/RustPython.git?rev=61b48f108982d865524f86624a9d5bc2ae3bccef#61b48f108982d865524f86624a9d5bc2ae3bccef" +source = "git+https://github.com/RustPython/RustPython.git?rev=ef873b4b606f0a58e3640b6186416631fdeead26#ef873b4b606f0a58e3640b6186416631fdeead26" dependencies = [ "ascii", "bitflags", @@ -2180,7 +2180,7 @@ dependencies = [ [[package]] name = "rustpython-compiler-core" version = "0.2.0" -source = "git+https://github.com/RustPython/RustPython.git?rev=61b48f108982d865524f86624a9d5bc2ae3bccef#61b48f108982d865524f86624a9d5bc2ae3bccef" +source = "git+https://github.com/RustPython/RustPython.git?rev=ef873b4b606f0a58e3640b6186416631fdeead26#ef873b4b606f0a58e3640b6186416631fdeead26" dependencies = [ "bincode", "bitflags", @@ -2197,7 +2197,7 @@ dependencies = [ [[package]] name = "rustpython-parser" version = "0.2.0" -source = "git+https://github.com/RustPython/RustPython.git?rev=61b48f108982d865524f86624a9d5bc2ae3bccef#61b48f108982d865524f86624a9d5bc2ae3bccef" +source = "git+https://github.com/RustPython/RustPython.git?rev=ef873b4b606f0a58e3640b6186416631fdeead26#ef873b4b606f0a58e3640b6186416631fdeead26" dependencies = [ "ahash", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 596afb7e58..4fc089c8e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a87 once_cell = { version = "1.16.0" } regex = { version = "1.6.0" } rustc-hash = { version = "1.1.0" } -rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "61b48f108982d865524f86624a9d5bc2ae3bccef" } -rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "61b48f108982d865524f86624a9d5bc2ae3bccef" } +rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ef873b4b606f0a58e3640b6186416631fdeead26" } +rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ef873b4b606f0a58e3640b6186416631fdeead26" } schemars = { version = "0.8.11" } serde = { version = "1.0.147", features = ["derive"] } serde_json = { version = "1.0.87" } diff --git a/crates/ruff/resources/test/fixtures/tryceratops/TRY301.py b/crates/ruff/resources/test/fixtures/tryceratops/TRY301.py index e585c92f84..f9b98b8a0e 100644 --- a/crates/ruff/resources/test/fixtures/tryceratops/TRY301.py +++ b/crates/ruff/resources/test/fixtures/tryceratops/TRY301.py @@ -20,6 +20,24 @@ def bad(): logger.exception("something failed") +def bad(): + try: + a = process() + if not a: + raise MyException(a) + + raise MyException(a) + + try: + b = process() + if not b: + raise MyException(b) + except* Exception: + logger.exception("something failed") + except* Exception: + logger.exception("something failed") + + def good(): try: a = process() # This throws the exception now diff --git a/crates/ruff/src/ast/branch_detection.rs b/crates/ruff/src/ast/branch_detection.rs index fa22dccad5..c84b76c6ed 100644 --- a/crates/ruff/src/ast/branch_detection.rs +++ b/crates/ruff/src/ast/branch_detection.rs @@ -59,6 +59,12 @@ fn alternatives<'a>(stmt: &'a RefEquality<'a, Stmt>) -> Vec vec![body.iter().chain(orelse.iter()).map(RefEquality).collect()] .into_iter() .chain(handlers.iter().map(|handler| { diff --git a/crates/ruff/src/ast/comparable.rs b/crates/ruff/src/ast/comparable.rs index a6b15bc6ef..24867ee40f 100644 --- a/crates/ruff/src/ast/comparable.rs +++ b/crates/ruff/src/ast/comparable.rs @@ -654,6 +654,12 @@ pub enum ComparableStmt<'a> { orelse: Vec>, finalbody: Vec>, }, + TryStar { + body: Vec>, + handlers: Vec>, + orelse: Vec>, + finalbody: Vec>, + }, Assert { test: ComparableExpr<'a>, msg: Option>, @@ -827,6 +833,17 @@ impl<'a> From<&'a Stmt> for ComparableStmt<'a> { orelse: orelse.iter().map(Into::into).collect(), finalbody: finalbody.iter().map(Into::into).collect(), }, + StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => Self::TryStar { + body: body.iter().map(Into::into).collect(), + handlers: handlers.iter().map(Into::into).collect(), + orelse: orelse.iter().map(Into::into).collect(), + finalbody: finalbody.iter().map(Into::into).collect(), + }, StmtKind::Assert { test, msg } => Self::Assert { test: test.into(), msg: msg.as_ref().map(Into::into), diff --git a/crates/ruff/src/ast/helpers.rs b/crates/ruff/src/ast/helpers.rs index e55728b691..7dfeecc6cc 100644 --- a/crates/ruff/src/ast/helpers.rs +++ b/crates/ruff/src/ast/helpers.rs @@ -391,6 +391,12 @@ where handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { any_over_body(body, func) || handlers.iter().any(|handler| { diff --git a/crates/ruff/src/ast/operations.rs b/crates/ruff/src/ast/operations.rs index ec33973669..1faa0fd95e 100644 --- a/crates/ruff/src/ast/operations.rs +++ b/crates/ruff/src/ast/operations.rs @@ -198,7 +198,10 @@ pub fn in_nested_block<'a>(mut parents: impl Iterator) -> bool parents.any(|parent| { matches!( parent.node, - StmtKind::Try { .. } | StmtKind::If { .. } | StmtKind::With { .. } + StmtKind::Try { .. } + | StmtKind::TryStar { .. } + | StmtKind::If { .. } + | StmtKind::With { .. } ) }) } diff --git a/crates/ruff/src/ast/visitor.rs b/crates/ruff/src/ast/visitor.rs index e491f15306..3dbdf201eb 100644 --- a/crates/ruff/src/ast/visitor.rs +++ b/crates/ruff/src/ast/visitor.rs @@ -232,6 +232,19 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { visitor.visit_body(orelse); visitor.visit_body(finalbody); } + StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => { + visitor.visit_body(body); + for excepthandler in handlers { + visitor.visit_excepthandler(excepthandler); + } + visitor.visit_body(orelse); + visitor.visit_body(finalbody); + } StmtKind::Assert { test, msg } => { visitor.visit_expr(test); if let Some(expr) = msg { diff --git a/crates/ruff/src/autofix/helpers.rs b/crates/ruff/src/autofix/helpers.rs index b1cbf04ef7..0018d3de9a 100644 --- a/crates/ruff/src/autofix/helpers.rs +++ b/crates/ruff/src/autofix/helpers.rs @@ -53,6 +53,12 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { if body.iter().contains(child) { Ok(has_single_child(body, deleted)) diff --git a/crates/ruff/src/checkers/ast.rs b/crates/ruff/src/checkers/ast.rs index 4d7f8a74ff..64e38e914d 100644 --- a/crates/ruff/src/checkers/ast.rs +++ b/crates/ruff/src/checkers/ast.rs @@ -1705,6 +1705,13 @@ where orelse, finalbody, .. + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + .. } => { if self.settings.rules.enabled(&Rule::DefaultExceptNotLast) { if let Some(diagnostic) = @@ -1992,6 +1999,12 @@ where handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { // TODO(charlie): The use of `smallvec` here leads to a lifetime issue. let handler_names = extract_handler_names(handlers) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs b/crates/ruff/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs index fb2526fe9e..16001b2a11 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs @@ -46,6 +46,7 @@ fn walk_stmt(checker: &mut Checker, body: &[Stmt], f: fn(&Stmt) -> bool) { } StmtKind::If { body, .. } | StmtKind::Try { body, .. } + | StmtKind::TryStar { body, .. } | StmtKind::With { body, .. } | StmtKind::AsyncWith { body, .. } => { walk_stmt(checker, body, f); diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs b/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs index 67cd5cee16..b68c42a8fa 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs @@ -44,7 +44,8 @@ impl<'a> Visitor<'a> for RaiseVisitor { StmtKind::ClassDef { .. } | StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } - | StmtKind::Try { .. } => {} + | StmtKind::Try { .. } + | StmtKind::TryStar { .. } => {} StmtKind::If { body, orelse, .. } => { visitor::walk_body(self, body); visitor::walk_body(self, orelse); diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs index 01fd3c08c2..51bb671507 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs @@ -116,7 +116,8 @@ pub fn complex_raises(checker: &mut Checker, stmt: &Stmt, items: &[Withitem], bo | StmtKind::For { .. } | StmtKind::AsyncFor { .. } | StmtKind::While { .. } - | StmtKind::Try { .. } => { + | StmtKind::Try { .. } + | StmtKind::TryStar { .. } => { is_too_complex = true; } _ => {} diff --git a/crates/ruff/src/rules/flake8_return/rules.rs b/crates/ruff/src/rules/flake8_return/rules.rs index ba43b5397c..6a92a04489 100644 --- a/crates/ruff/src/rules/flake8_return/rules.rs +++ b/crates/ruff/src/rules/flake8_return/rules.rs @@ -260,7 +260,10 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) { implicit_return(checker, last_stmt); } } - StmtKind::Return { .. } | StmtKind::Raise { .. } | StmtKind::Try { .. } => {} + StmtKind::Return { .. } + | StmtKind::Raise { .. } + | StmtKind::Try { .. } + | StmtKind::TryStar { .. } => {} StmtKind::Expr { value, .. } if matches!( &value.node, diff --git a/crates/ruff/src/rules/flake8_return/visitor.rs b/crates/ruff/src/rules/flake8_return/visitor.rs index 74768eb91e..fb4570f116 100644 --- a/crates/ruff/src/rules/flake8_return/visitor.rs +++ b/crates/ruff/src/rules/flake8_return/visitor.rs @@ -136,7 +136,7 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> { visitor::walk_stmt(self, stmt); self.parents.pop(); } - StmtKind::Try { .. } => { + StmtKind::Try { .. } | StmtKind::TryStar { .. } => { self.stack .tries .push((stmt.location, stmt.end_location.unwrap())); diff --git a/crates/ruff/src/rules/isort/track.rs b/crates/ruff/src/rules/isort/track.rs index befdd3c92b..708bf1746a 100644 --- a/crates/ruff/src/rules/isort/track.rs +++ b/crates/ruff/src/rules/isort/track.rs @@ -226,6 +226,12 @@ where handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { for excepthandler in handlers { self.visit_excepthandler(excepthandler); diff --git a/crates/ruff/src/rules/mccabe/rules.rs b/crates/ruff/src/rules/mccabe/rules.rs index c5b0022c97..77bc3a8161 100644 --- a/crates/ruff/src/rules/mccabe/rules.rs +++ b/crates/ruff/src/rules/mccabe/rules.rs @@ -87,6 +87,12 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize { handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { complexity += 1; complexity += get_complexity_number(body); diff --git a/crates/ruff/src/rules/pylint/rules/too_many_branches.rs b/crates/ruff/src/rules/pylint/rules/too_many_branches.rs index 81bb7ddd78..185cec8b4d 100644 --- a/crates/ruff/src/rules/pylint/rules/too_many_branches.rs +++ b/crates/ruff/src/rules/pylint/rules/too_many_branches.rs @@ -56,6 +56,12 @@ fn num_branches(stmts: &[Stmt]) -> usize { handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { 1 + num_branches(body) + (if orelse.is_empty() { diff --git a/crates/ruff/src/rules/pylint/rules/too_many_statements.rs b/crates/ruff/src/rules/pylint/rules/too_many_statements.rs index 07549c973f..74ab824b3b 100644 --- a/crates/ruff/src/rules/pylint/rules/too_many_statements.rs +++ b/crates/ruff/src/rules/pylint/rules/too_many_statements.rs @@ -54,6 +54,12 @@ fn num_statements(stmts: &[Stmt]) -> usize { handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { count += 1; count += num_statements(body); diff --git a/crates/ruff/src/rules/pylint/rules/useless_else_on_loop.rs b/crates/ruff/src/rules/pylint/rules/useless_else_on_loop.rs index c650de473a..4df33a31d3 100644 --- a/crates/ruff/src/rules/pylint/rules/useless_else_on_loop.rs +++ b/crates/ruff/src/rules/pylint/rules/useless_else_on_loop.rs @@ -29,6 +29,13 @@ fn loop_exits_early(body: &[Stmt]) -> bool { orelse, finalbody, .. + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + .. } => { loop_exits_early(body) || loop_exits_early(orelse) diff --git a/crates/ruff/src/rules/tryceratops/rules/raise_within_try.rs b/crates/ruff/src/rules/tryceratops/rules/raise_within_try.rs index ae425bd841..76e555cacc 100644 --- a/crates/ruff/src/rules/tryceratops/rules/raise_within_try.rs +++ b/crates/ruff/src/rules/tryceratops/rules/raise_within_try.rs @@ -29,7 +29,7 @@ where fn visit_stmt(&mut self, stmt: &'b Stmt) { match stmt.node { StmtKind::Raise { .. } => self.raises.push(stmt), - StmtKind::Try { .. } => (), + StmtKind::Try { .. } | StmtKind::TryStar { .. } => (), _ => visitor::walk_stmt(self, stmt), } } diff --git a/crates/ruff/src/rules/tryceratops/rules/verbose_raise.rs b/crates/ruff/src/rules/tryceratops/rules/verbose_raise.rs index 4fa92d9618..ad57a076bc 100644 --- a/crates/ruff/src/rules/tryceratops/rules/verbose_raise.rs +++ b/crates/ruff/src/rules/tryceratops/rules/verbose_raise.rs @@ -32,6 +32,9 @@ where StmtKind::Raise { exc, cause } => self.raises.push((exc.as_deref(), cause.as_deref())), StmtKind::Try { body, finalbody, .. + } + | StmtKind::TryStar { + body, finalbody, .. } => { for stmt in body.iter().chain(finalbody.iter()) { visitor::walk_stmt(self, stmt); diff --git a/crates/ruff/src/rules/tryceratops/snapshots/ruff__rules__tryceratops__tests__raise-within-try_TRY301.py.snap b/crates/ruff/src/rules/tryceratops/snapshots/ruff__rules__tryceratops__tests__raise-within-try_TRY301.py.snap index fe297a291e..36ef87ee14 100644 --- a/crates/ruff/src/rules/tryceratops/snapshots/ruff__rules__tryceratops__tests__raise-within-try_TRY301.py.snap +++ b/crates/ruff/src/rules/tryceratops/snapshots/ruff__rules__tryceratops__tests__raise-within-try_TRY301.py.snap @@ -1,5 +1,5 @@ --- -source: src/rules/tryceratops/mod.rs +source: crates/ruff/src/rules/tryceratops/mod.rs expression: diagnostics --- - kind: @@ -32,4 +32,34 @@ expression: diagnostics column: 36 fix: ~ parent: ~ +- kind: + RaiseWithinTry: ~ + location: + row: 27 + column: 12 + end_location: + row: 27 + column: 32 + fix: ~ + parent: ~ +- kind: + RaiseWithinTry: ~ + location: + row: 29 + column: 8 + end_location: + row: 29 + column: 28 + fix: ~ + parent: ~ +- kind: + RaiseWithinTry: ~ + location: + row: 34 + column: 16 + end_location: + row: 34 + column: 36 + fix: ~ + parent: ~ diff --git a/crates/ruff/src/source_code/generator.rs b/crates/ruff/src/source_code/generator.rs index e25b28a44f..629a0c55eb 100644 --- a/crates/ruff/src/source_code/generator.rs +++ b/crates/ruff/src/source_code/generator.rs @@ -483,7 +483,37 @@ impl<'a> Generator<'a> { for handler in handlers { statement!({ - self.unparse_excepthandler(handler); + self.unparse_excepthandler(handler, false); + }); + } + + if !orelse.is_empty() { + statement!({ + self.p("else:"); + }); + self.body(orelse); + } + if !finalbody.is_empty() { + statement!({ + self.p("finally:"); + }); + self.body(finalbody); + } + } + StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => { + statement!({ + self.p("try:"); + }); + self.body(body); + + for handler in handlers { + statement!({ + self.unparse_excepthandler(handler, true); }); } @@ -584,10 +614,13 @@ impl<'a> Generator<'a> { } } - fn unparse_excepthandler(&mut self, ast: &Excepthandler) { + fn unparse_excepthandler(&mut self, ast: &Excepthandler, star: bool) { match &ast.node { ExcepthandlerKind::ExceptHandler { type_, name, body } => { self.p("except"); + if star { + self.p("*"); + } if let Some(type_) = type_ { self.p(" "); self.unparse_expr(type_, precedence::MAX); @@ -1265,6 +1298,18 @@ mod tests { r#"@functools.lru_cache(maxsize=None) def f(x: int, y: int) -> int: return x + y"# + ); + assert_round_trip!( + r#"try: + pass +except Exception as e: + pass"# + ); + assert_round_trip!( + r#"try: + pass +except* Exception as e: + pass"# ); assert_eq!(round_trip(r#"x = (1, 2, 3)"#), r#"x = 1, 2, 3"#); assert_eq!(round_trip(r#"-(1) + ~(2) + +(3)"#), r#"-1 + ~2 + +3"#); diff --git a/crates/ruff_python_formatter/src/core/visitor.rs b/crates/ruff_python_formatter/src/core/visitor.rs index e562609c52..cfb07a6210 100644 --- a/crates/ruff_python_formatter/src/core/visitor.rs +++ b/crates/ruff_python_formatter/src/core/visitor.rs @@ -234,6 +234,19 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a mut Stm visitor.visit_body(orelse); visitor.visit_body(finalbody); } + StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => { + visitor.visit_body(body); + for excepthandler in handlers { + visitor.visit_excepthandler(excepthandler); + } + visitor.visit_body(orelse); + visitor.visit_body(finalbody); + } StmtKind::Assert { test, msg } => { visitor.visit_expr(test); if let Some(expr) = msg { diff --git a/crates/ruff_python_formatter/src/cst.rs b/crates/ruff_python_formatter/src/cst.rs index 24471ac76d..5c9b466fec 100644 --- a/crates/ruff_python_formatter/src/cst.rs +++ b/crates/ruff_python_formatter/src/cst.rs @@ -249,6 +249,12 @@ pub enum StmtKind { orelse: Vec, finalbody: Vec, }, + TryStar { + body: Vec, + handlers: Vec, + orelse: Vec, + finalbody: Vec, + }, Assert { test: Box, msg: Option>, @@ -815,6 +821,23 @@ impl From for Stmt { trivia: vec![], parentheses: Parenthesize::Never, }, + rustpython_parser::ast::StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => Stmt { + location: stmt.location, + end_location: stmt.end_location, + node: StmtKind::TryStar { + body: body.into_iter().map(Into::into).collect(), + handlers: handlers.into_iter().map(Into::into).collect(), + orelse: orelse.into_iter().map(Into::into).collect(), + finalbody: finalbody.into_iter().map(Into::into).collect(), + }, + trivia: vec![], + parentheses: Parenthesize::Never, + }, rustpython_parser::ast::StmtKind::Import { names } => Stmt { location: stmt.location, end_location: stmt.end_location, diff --git a/crates/ruff_python_formatter/src/format/stmt.rs b/crates/ruff_python_formatter/src/format/stmt.rs index 6bba65f14b..15720d3fd7 100644 --- a/crates/ruff_python_formatter/src/format/stmt.rs +++ b/crates/ruff_python_formatter/src/format/stmt.rs @@ -476,6 +476,28 @@ fn format_try( Ok(()) } +fn format_try_star( + f: &mut Formatter>, + stmt: &Stmt, + body: &[Stmt], + handlers: &[Excepthandler], + orelse: &[Stmt], + finalbody: &[Stmt], +) -> FormatResult<()> { + write!(f, [text("try:"), block_indent(&block(body))])?; + for handler in handlers { + // TODO(charlie): Include `except*`. + write!(f, [handler.format()])?; + } + if !orelse.is_empty() { + write!(f, [text("else:"), block_indent(&block(orelse))])?; + } + if !finalbody.is_empty() { + write!(f, [text("finally:"), block_indent(&block(finalbody))])?; + } + Ok(()) +} + fn format_assert( f: &mut Formatter>, stmt: &Stmt, @@ -832,6 +854,12 @@ impl Format> for FormatStmt<'_> { orelse, finalbody, } => format_try(f, self.item, body, handlers, orelse, finalbody), + StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, + } => format_try_star(f, self.item, body, handlers, orelse, finalbody), StmtKind::Assert { test, msg } => { format_assert(f, self.item, test, msg.as_ref().map(|expr| &**expr)) } diff --git a/crates/ruff_python_formatter/src/newlines.rs b/crates/ruff_python_formatter/src/newlines.rs index 7f8d48425d..70cb86b844 100644 --- a/crates/ruff_python_formatter/src/newlines.rs +++ b/crates/ruff_python_formatter/src/newlines.rs @@ -226,6 +226,12 @@ impl<'a> Visitor<'a> for NewlineNormalizer { handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { self.depth = Depth::Nested; self.trailer = Trailer::CompoundStatement; diff --git a/crates/ruff_python_formatter/src/parentheses.rs b/crates/ruff_python_formatter/src/parentheses.rs index fde05d4c03..502eec7f73 100644 --- a/crates/ruff_python_formatter/src/parentheses.rs +++ b/crates/ruff_python_formatter/src/parentheses.rs @@ -81,6 +81,7 @@ impl<'a> Visitor<'a> for ParenthesesNormalizer { StmtKind::Match { .. } => {} StmtKind::Raise { .. } => {} StmtKind::Try { .. } => {} + StmtKind::TryStar { .. } => {} StmtKind::Assert { test, msg } => { use_inferred_parens(test); if let Some(msg) = msg { diff --git a/crates/ruff_python_formatter/src/trivia.rs b/crates/ruff_python_formatter/src/trivia.rs index 4eac465d73..fbfe3864f1 100644 --- a/crates/ruff_python_formatter/src/trivia.rs +++ b/crates/ruff_python_formatter/src/trivia.rs @@ -411,6 +411,12 @@ fn sorted_child_nodes_inner<'a>(node: &Node<'a>, result: &mut Vec>) { handlers, orelse, finalbody, + } + | StmtKind::TryStar { + body, + handlers, + orelse, + finalbody, } => { for stmt in body { result.push(Node::Stmt(stmt));