Skip to content

Commit 671494a

Browse files
[pylint] Also reports case np.nan/case math.nan (PLW0177) (#16378)
## Summary Resolves #16374. `PLW0177` now also reports the pattern of a case branch if it is an attribute access whose qualified name is that of either `np.nan` or `math.nan`. As the rule is in preview, the changes are not preview-gated. ## Test Plan `cargo nextest run` and `cargo insta test`.
1 parent b89d61b commit 671494a

File tree

4 files changed

+55
-2
lines changed

4 files changed

+55
-2
lines changed

crates/ruff_linter/resources/test/fixtures/pylint/nan_comparison.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@
5353
if x == builtins.float("nan"):
5454
pass
5555

56+
# https://github.com/astral-sh/ruff/issues/16374
57+
match number:
58+
# Errors
59+
case np.nan: ...
60+
case math.nan: ...
61+
62+
# No errors
63+
case np.nan(): ...
64+
case math.nan(): ...
65+
case float('nan'): ...
66+
case npy_nan: ...
67+
5668
# OK
5769
if math.isnan(x):
5870
pass

crates/ruff_linter/src/checkers/ast/analyze/statement.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,15 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
17421742
pylint::rules::useless_exception_statement(checker, expr);
17431743
}
17441744
}
1745+
Stmt::Match(ast::StmtMatch {
1746+
subject: _,
1747+
cases,
1748+
range: _,
1749+
}) => {
1750+
if checker.enabled(Rule::NanComparison) {
1751+
pylint::rules::nan_comparison_match(checker, cases);
1752+
}
1753+
}
17451754
_ => {}
17461755
}
17471756
}

crates/ruff_linter/src/rules/pylint/rules/nan_comparison.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,21 @@ impl Violation for NanComparison {
4848

4949
/// PLW0177
5050
pub(crate) fn nan_comparison(checker: &Checker, left: &Expr, comparators: &[Expr]) {
51-
for expr in std::iter::once(left).chain(comparators) {
51+
nan_comparison_impl(checker, std::iter::once(left).chain(comparators));
52+
}
53+
54+
/// PLW0177
55+
pub(crate) fn nan_comparison_match(checker: &Checker, cases: &[ast::MatchCase]) {
56+
nan_comparison_impl(
57+
checker,
58+
cases
59+
.iter()
60+
.filter_map(|case| case.pattern.as_match_value().map(|pattern| &*pattern.value)),
61+
);
62+
}
63+
64+
fn nan_comparison_impl<'a>(checker: &Checker, comparators: impl Iterator<Item = &'a Expr>) {
65+
for expr in comparators {
5266
if let Some(qualified_name) = checker.semantic().resolve_qualified_name(expr) {
5367
match qualified_name.segments() {
5468
["numpy", "nan" | "NAN" | "NaN"] => {

crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0177_nan_comparison.py.snap

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: crates/ruff_linter/src/rules/pylint/mod.rs
3-
snapshot_kind: text
43
---
54
nan_comparison.py:11:9: PLW0177 Comparing against a NaN value; use `math.isnan` instead
65
|
@@ -89,3 +88,22 @@ nan_comparison.py:53:9: PLW0177 Comparing against a NaN value; use `math.isnan`
8988
| ^^^^^^^^^^^^^^^^^^^^^ PLW0177
9089
54 | pass
9190
|
91+
92+
nan_comparison.py:59:10: PLW0177 Comparing against a NaN value; use `np.isnan` instead
93+
|
94+
57 | match number:
95+
58 | # Errors
96+
59 | case np.nan: ...
97+
| ^^^^^^ PLW0177
98+
60 | case math.nan: ...
99+
|
100+
101+
nan_comparison.py:60:10: PLW0177 Comparing against a NaN value; use `math.isnan` instead
102+
|
103+
58 | # Errors
104+
59 | case np.nan: ...
105+
60 | case math.nan: ...
106+
| ^^^^^^^^ PLW0177
107+
61 |
108+
62 | # No errors
109+
|

0 commit comments

Comments
 (0)