Skip to content

Commit

Permalink
Consider irrefutable pattern similar to if .. else for C901 (#11565)
Browse files Browse the repository at this point in the history
## Summary

Follow up to #11521

Removes the extra added complexity for catch all match cases. This
matches the implementation of plain `else` statements.

## Test Plan
Added new test cases.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
  • Loading branch information
blueraft and dhruvmanila authored May 27, 2024
1 parent 34a5063 commit b36c713
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize {
complexity += 1;
complexity += get_complexity_number(&case.body);
}
if let Some(last_case) = cases.last() {
// The complexity of an irrefutable pattern is similar to an `else` block of an `if` statement.
//
// For example:
// ```python
// match subject:
// case 1: ...
// case _: ...
//
// match subject:
// case 1: ...
// case foo: ...
// ```
if last_case.guard.is_none() && last_case.pattern.is_irrefutable() {
complexity -= 1;
}
}
}
Stmt::Try(ast::StmtTry {
body,
Expand Down Expand Up @@ -431,17 +448,64 @@ def with_lock():
}

#[test]
fn match_case() -> Result<()> {
fn simple_match_case() -> Result<()> {
let source = r"
def f():
match a:
case 30:
match subject:
case 2:
print('foo')
case _:
print('bar')
";
let stmts = parse_suite(source)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}

#[test]
fn multiple_match_case() -> Result<()> {
let source = r"
def f():
match subject:
case 2:
print('foo')
case 2:
print('bar')
case _:
print('baz')
";
let stmts = parse_suite(source)?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}

#[test]
fn named_catch_all_match_case() -> Result<()> {
let source = r"
def f():
match subject:
case 2:
print('hello')
case x:
print(x)
";
let stmts = parse_suite(source)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}

#[test]
fn match_case_catch_all_with_seuqnece() -> Result<()> {
let source = r"
def f():
match subject:
case 2:
print('hello')
case 5 | _:
print(x)
";
let stmts = parse_suite(source)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
}
15 changes: 15 additions & 0 deletions crates/ruff_python_ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3020,6 +3020,21 @@ pub enum Pattern {
MatchOr(PatternMatchOr),
}

impl Pattern {
/// Checks if the [`Pattern`] is an [irrefutable pattern].
///
/// [irrefutable pattern]: https://peps.python.org/pep-0634/#irrefutable-case-blocks
pub fn is_irrefutable(&self) -> bool {
match self {
Pattern::MatchAs(PatternMatchAs { pattern: None, .. }) => true,
Pattern::MatchOr(PatternMatchOr { patterns, .. }) => {
patterns.iter().any(Pattern::is_irrefutable)
}
_ => false,
}
}
}

/// See also [MatchValue](https://docs.python.org/3/library/ast.html#ast.MatchValue)
#[derive(Clone, Debug, PartialEq)]
pub struct PatternMatchValue {
Expand Down

0 comments on commit b36c713

Please sign in to comment.