diff --git a/personal_python_ast_optimizer/parser/skipper.py b/personal_python_ast_optimizer/parser/skipper.py index b5955ca..4179fe3 100644 --- a/personal_python_ast_optimizer/parser/skipper.py +++ b/personal_python_ast_optimizer/parser/skipper.py @@ -501,12 +501,30 @@ def visit_If(self, node: ast.If) -> ast.AST | list[ast.stmt] | None: ) return if_body or None - if not parsed_node.orelse and self._body_is_only_pass(parsed_node.body): - call_finder = _DanglingExprCallFinder( - self.optimizations_config.functions_safe_to_exclude_in_test_expr - ) - call_finder.visit(parsed_node.test) - return [ast.Expr(expr) for expr in call_finder.calls] + if not parsed_node.orelse: + if self._body_is_only_pass(parsed_node.body): + call_finder = _DanglingExprCallFinder( + self.optimizations_config.functions_safe_to_exclude_in_test_expr + ) + call_finder.visit(parsed_node.test) + return [ast.Expr(expr) for expr in call_finder.calls] + + if ( + len(parsed_node.body) == 1 + and isinstance(parsed_node.body[0], ast.If) + and not parsed_node.body[0].orelse + ): + # These if conditions can be combine into one if + if isinstance(parsed_node.test, ast.BoolOp) and isinstance( + parsed_node.test.op, ast.And + ): + parsed_node.test.values.append(parsed_node.body[0].test) + else: + parsed_node.test = ast.BoolOp( + ast.And(), [parsed_node.test, parsed_node.body[0].test] + ) + + parsed_node.body = parsed_node.body[0].body return parsed_node diff --git a/tests/parser/test_if.py b/tests/parser/test_if.py index 6fb2af5..632de68 100644 --- a/tests/parser/test_if.py +++ b/tests/parser/test_if.py @@ -85,3 +85,63 @@ @pytest.mark.parametrize("before_and_after", _if_cases) def test_if(before_and_after: BeforeAndAfter): run_minifier_and_assert_correct(before_and_after) + + +_nested_if_cases = [ + BeforeAndAfter( + """ +if a < b and foo() or bar(): + if b < c: + print() +""", + "if(a