diff --git a/tests/test_visitors/test_ast/test_conditions/test_simplified_match_conditions.py b/tests/test_visitors/test_ast/test_conditions/test_simplified_match_conditions.py index f7efa69de..88ad54a1f 100644 --- a/tests/test_visitors/test_ast/test_conditions/test_simplified_match_conditions.py +++ b/tests/test_visitors/test_ast/test_conditions/test_simplified_match_conditions.py @@ -1,6 +1,7 @@ import pytest from wemake_python_styleguide.violations.consistency import ( + SimplifiableIfMatchViolation, SimplifiableMatchViolation, ) from wemake_python_styleguide.visitors.ast.conditions import ( @@ -187,3 +188,111 @@ def test_not_simplifiable_match_templates( visitor = SimplifiableMatchVisitor(default_options, tree=tree) visitor.run() assert_errors(visitor, []) + + +# New tests for single-case matches: + +# Wrong (single case): +single_case_match = """ +match x: + case {0}: + do_something() +""" + +single_case_with_as_binding_match = """ +match x: + case {0} as y: + do_something() +""" + +# Correct (not single case or has guards): +multi_case_match = """ +match x: + case 1: + do_something() + case 2: + do_something_else() +""" + +single_case_with_guard = """ +match x: + case y if y > 0: + do_something() +""" + +wildcard_case = """ +match x: + case _: + do_something() +""" + +pattern_with_complex_structure = """ +match x: + case [a, b]: + do_something() +""" + +pattern_with_class_args = """ +match x: + case SomeClass(a): + do_something() +""" + + +@pytest.mark.parametrize( + 'code', + [ + '1', + 'True', + 'None', + '"string"', + 'ns.CONST', + 'State.ACCEPTED', + ], +) +def test_single_case_match( + code, + assert_errors, + parse_ast_tree, + default_options, +): + """Test that single-case matches raise a violation.""" + tree = parse_ast_tree(single_case_match.format(code)) + visitor = SimplifiableMatchVisitor(default_options, tree=tree) + visitor.run() + assert_errors(visitor, [SimplifiableIfMatchViolation]) + + +def test_single_case_with_as_binding( + assert_errors, + parse_ast_tree, + default_options, +): + """Test that single-case matches with as-binding raise a violation.""" + tree = parse_ast_tree(single_case_with_as_binding_match.format('1')) + visitor = SimplifiableMatchVisitor(default_options, tree=tree) + visitor.run() + assert_errors(visitor, [SimplifiableIfMatchViolation]) + + +@pytest.mark.parametrize( + 'template', + [ + multi_case_match, + single_case_with_guard, + wildcard_case, + pattern_with_complex_structure, + pattern_with_class_args, + ], +) +def test_not_single_case_match( + template, + assert_errors, + parse_ast_tree, + default_options, +): + """Test that non-single-case matches do not raise the single-case violation.""" + tree = parse_ast_tree(template) + visitor = SimplifiableMatchVisitor(default_options, tree=tree) + visitor.run() + assert_errors(visitor, []) diff --git a/wemake_python_styleguide/violations/consistency.py b/wemake_python_styleguide/violations/consistency.py index dc0ac8a6f..f21a011a2 100644 --- a/wemake_python_styleguide/violations/consistency.py +++ b/wemake_python_styleguide/violations/consistency.py @@ -2460,3 +2460,43 @@ class SimplifiableMatchViolation(ASTViolation): 'Found simplifiable `match` statement that can be just `if`' ) code = 365 + + +@final +class SimplifiableIfMatchViolation(ASTViolation): + """ + Single-case ``match`` statements can be simplified to ``if`` statements. + + Reasoning: + Using ``match`` for a single case is unnecessarily complex compared to a + simple ``if`` condition. The intent is clear, and using ``if`` reduces + nesting and cognitive load. + + Solution: + Replace single-case ``match ... case`` statements with ``if``. + + When is this violation is raised? + - When there is exactly one ``case`` statement + - When the pattern is simple (literal, constant, enum, etc.) + - When no guard (``if`` ...) is used + - When no wildcard pattern is used + + Example:: + + # Correct: + if x == 1: + do_something() + + # Wrong: + match x: + case 1: + do_something() + + .. versionadded:: 1.10.0 + + """ + + error_template = ( + 'Found single-case `match` statement that can be simplified to `if`' + ) + code = 366 diff --git a/wemake_python_styleguide/visitors/ast/conditions.py b/wemake_python_styleguide/visitors/ast/conditions.py index 0487facd0..aff89c738 100644 --- a/wemake_python_styleguide/visitors/ast/conditions.py +++ b/wemake_python_styleguide/visitors/ast/conditions.py @@ -208,7 +208,20 @@ def visit_Match(self, node: ast.Match) -> None: def _check_simplifiable_match(self, node: ast.Match) -> None: cases = node.cases - if len(cases) == 2: + + # Check for single-case matches with no guard + if len(cases) == 1: + case = cases[0] + # Check if there's no guard condition + if case.guard is None: + # Check if the pattern is simple (literal, constant, enum, etc.) + if pattern_matching.is_simple_pattern(case.pattern): + self.add_violation( + consistency.SimplifiableIfMatchViolation(node) + ) + + # Check for two-case matches with wildcard (existing logic) + elif len(cases) == 2: first, second = cases if not pattern_matching.is_wildcard_pattern(second):