[refurb] Also report non-name expressions (FURB169) (#15905)

## Summary

Follow-up to #15779.

Prior to this change, non-name expressions are not reported at all:

```python
type(a.b) is type(None)  # no error
```

This change enhances the rule so that such cases are also reported in
preview. Additionally:

* The fix will now be marked as unsafe if there are any comments within
its range.
* Error messages are slightly modified.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
InSync 2025-02-05 14:46:37 +07:00 committed by GitHub
parent 700e969c56
commit 5852217198
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 497 additions and 147 deletions

View file

@ -26,6 +26,23 @@ type(None) != type(foo)
type(None) != type(None)
type(a.b) is type(None)
type(
a(
# Comment
)
) != type(None)
type(
a := 1
) == type(None)
type(
a for a in range(0)
) is not type(None)
# Ok.
foo is None

View file

@ -1,9 +1,11 @@
use ruff_diagnostics::{Applicability, Edit, Fix};
use ruff_python_ast::name::Name;
use ruff_python_ast::{self as ast, Expr};
use ruff_python_codegen::Generator;
use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion;
/// Format a code snippet to call `name.method()`.
@ -39,31 +41,45 @@ pub(super) fn generate_method_call(name: Name, method: &str, generator: Generato
generator.stmt(&stmt.into())
}
/// Format a code snippet comparing `name` to `None` (e.g., `name is None`).
pub(super) fn generate_none_identity_comparison(
name: Name,
/// Returns a fix that replace `range` with
/// a generated `a is None`/`a is not None` check.
pub(super) fn replace_with_identity_check(
left: &Expr,
range: TextRange,
negate: bool,
generator: Generator,
) -> String {
// Construct `name`.
let var = ast::ExprName {
id: name,
ctx: ast::ExprContext::Load,
range: TextRange::default(),
};
// Construct `name is None` or `name is not None`.
checker: &Checker,
) -> Fix {
let (semantic, generator) = (checker.semantic(), checker.generator());
let op = if negate {
ast::CmpOp::IsNot
} else {
ast::CmpOp::Is
};
let compare = ast::ExprCompare {
left: Box::new(var.into()),
ops: Box::from([op]),
comparators: Box::from([ast::Expr::NoneLiteral(ast::ExprNoneLiteral::default())]),
let new_expr = Expr::Compare(ast::ExprCompare {
left: left.clone().into(),
ops: [op].into(),
comparators: [ast::ExprNoneLiteral::default().into()].into(),
range: TextRange::default(),
});
let new_content = generator.expr(&new_expr);
let new_content = if semantic.current_expression_parent().is_some() {
format!("({new_content})")
} else {
new_content
};
generator.expr(&compare.into())
let applicability = if checker.comment_ranges().intersects(range) {
Applicability::Unsafe
} else {
Applicability::Safe
};
let edit = Edit::range_replacement(new_content, range);
Fix::applicable_edit(edit, applicability)
}
// Helpers for read-whole-file and write-whole-file

View file

@ -11,7 +11,7 @@ mod tests {
use test_case::test_case;
use crate::registry::Rule;
use crate::settings::types::PythonVersion;
use crate::settings::types::{PreviewMode, PythonVersion};
use crate::test::test_path;
use crate::{assert_messages, settings};
@ -60,6 +60,24 @@ mod tests {
Ok(())
}
#[test_case(Rule::TypeNoneComparison, Path::new("FURB169.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"preview__{}_{}",
rule_code.noqa_code(),
path.to_string_lossy()
);
let diagnostics = test_path(
Path::new("refurb").join(path).as_path(),
&settings::LinterSettings {
preview: PreviewMode::Enabled,
..settings::LinterSettings::for_rule(rule_code)
},
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
#[test]
fn write_whole_file_python_39() -> Result<()> {
let diagnostics = test_path(

View file

@ -1,11 +1,10 @@
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_python_ast::{self as ast, CmpOp, Expr, Operator};
use ruff_diagnostics::{Diagnostic, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_python_semantic::SemanticModel;
use ruff_text_size::TextRange;
use crate::checkers::ast::Checker;
use crate::rules::refurb::helpers::replace_with_identity_check;
/// ## What it does
/// Checks for uses of `isinstance` that check if an object is of type `None`.
@ -69,7 +68,7 @@ pub(crate) fn isinstance_type_none(checker: &mut Checker, call: &ast::ExprCall)
return;
}
let fix = replace_with_identity_check(expr, call.range, checker);
let fix = replace_with_identity_check(expr, call.range, false, checker);
let diagnostic = Diagnostic::new(IsinstanceTypeNone, call.range);
checker.diagnostics.push(diagnostic.with_fix(fix));
@ -138,31 +137,3 @@ fn is_none(expr: &Expr, semantic: &SemanticModel) -> bool {
}
inner(expr, false, semantic)
}
fn replace_with_identity_check(expr: &Expr, range: TextRange, checker: &Checker) -> Fix {
let (semantic, generator) = (checker.semantic(), checker.generator());
let new_expr = Expr::Compare(ast::ExprCompare {
left: expr.clone().into(),
ops: [CmpOp::Is].into(),
comparators: [ast::ExprNoneLiteral::default().into()].into(),
range: TextRange::default(),
});
let new_content = generator.expr(&new_expr);
let new_content = if semantic.current_expression_parent().is_some() {
format!("({new_content})")
} else {
new_content
};
let applicability = if checker.comment_ranges().intersects(range) {
Applicability::Unsafe
} else {
Applicability::Safe
};
let edit = Edit::range_replacement(new_content, range);
Fix::applicable_edit(edit, applicability)
}

View file

@ -1,22 +1,21 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::name::Name;
use ruff_python_ast::{self as ast, CmpOp, Expr};
use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::edits::pad;
use crate::rules::refurb::helpers::generate_none_identity_comparison;
use crate::rules::refurb::helpers::replace_with_identity_check;
/// ## What it does
/// Checks for uses of `type` that compare the type of an object to the type of
/// `None`.
/// Checks for uses of `type` that compare the type of an object to the type of `None`.
///
/// ## Why is this bad?
/// There is only ever one instance of `None`, so it is more efficient and
/// readable to use the `is` operator to check if an object is `None`.
///
/// Only name expressions (e.g., `type(foo) == type(None)`) are reported.
/// In [preview], the rule will also report other kinds of expressions.
///
/// ## Example
/// ```python
/// type(obj) is type(None)
@ -27,34 +26,32 @@ use crate::rules::refurb::helpers::generate_none_identity_comparison;
/// obj is None
/// ```
///
/// ## Fix safety
/// If the fix might remove comments, it will be marked as unsafe.
///
/// ## References
/// - [Python documentation: `isinstance`](https://docs.python.org/3/library/functions.html#isinstance)
/// - [Python documentation: `None`](https://docs.python.org/3/library/constants.html#None)
/// - [Python documentation: `type`](https://docs.python.org/3/library/functions.html#type)
/// - [Python documentation: Identity comparisons](https://docs.python.org/3/reference/expressions.html#is-not)
///
/// [preview]: https://docs.astral.sh/ruff/preview/
#[derive(ViolationMetadata)]
pub(crate) struct TypeNoneComparison {
object: Name,
comparison: Comparison,
replacement: IdentityCheck,
}
impl Violation for TypeNoneComparison {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
impl AlwaysFixableViolation for TypeNoneComparison {
#[derive_message_formats]
fn message(&self) -> String {
let TypeNoneComparison { object, .. } = self;
format!("Compare the identities of `{object}` and `None` instead of their respective types")
format!(
"When checking against `None`, use `{}` instead of comparison with `type(None)`",
self.replacement.op()
)
}
fn fix_title(&self) -> Option<String> {
let TypeNoneComparison { object, comparison } = self;
match comparison {
Comparison::Is | Comparison::Eq => Some(format!("Replace with `{object} is None`")),
Comparison::IsNot | Comparison::NotEq => {
Some(format!("Replace with `{object} is not None`"))
}
}
fn fix_title(&self) -> String {
format!("Replace with `{} None`", self.replacement.op())
}
}
@ -64,16 +61,12 @@ pub(crate) fn type_none_comparison(checker: &mut Checker, compare: &ast::ExprCom
return;
};
// Ensure that the comparison is an identity or equality test.
let comparison = match op {
CmpOp::Is => Comparison::Is,
CmpOp::IsNot => Comparison::IsNot,
CmpOp::Eq => Comparison::Eq,
CmpOp::NotEq => Comparison::NotEq,
let replacement = match op {
CmpOp::Is | CmpOp::Eq => IdentityCheck::Is,
CmpOp::IsNot | CmpOp::NotEq => IdentityCheck::IsNot,
_ => return,
};
// Get the objects whose types are being compared.
let Some(left_arg) = type_call_arg(&compare.left, checker.semantic()) else {
return;
};
@ -81,48 +74,24 @@ pub(crate) fn type_none_comparison(checker: &mut Checker, compare: &ast::ExprCom
return;
};
// If one of the objects is `None`, get the other object; else, return.
let other_arg = match (
left_arg.is_none_literal_expr(),
right_arg.is_none_literal_expr(),
) {
(true, false) => right_arg,
(false, true) => left_arg,
// If both are `None`, just pick one.
(true, true) => left_arg,
let other_arg = match (left_arg, right_arg) {
(Expr::NoneLiteral(_), _) => right_arg,
(_, Expr::NoneLiteral(_)) => left_arg,
_ => return,
};
// Get the name of the other object (or `None` if both were `None`).
let other_arg_name = match other_arg {
Expr::Name(ast::ExprName { id, .. }) => id.clone(),
Expr::NoneLiteral { .. } => Name::new_static("None"),
_ => return,
};
if checker.settings.preview.is_disabled()
&& !matches!(other_arg, Expr::Name(_) | Expr::NoneLiteral(_))
{
return;
}
let mut diagnostic = Diagnostic::new(
TypeNoneComparison {
object: other_arg_name.clone(),
comparison,
},
compare.range(),
);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
pad(
match comparison {
Comparison::Is | Comparison::Eq => {
generate_none_identity_comparison(other_arg_name, false, checker.generator())
}
Comparison::IsNot | Comparison::NotEq => {
generate_none_identity_comparison(other_arg_name, true, checker.generator())
}
},
compare.range(),
checker.locator(),
),
compare.range(),
)));
checker.diagnostics.push(diagnostic);
let diagnostic = Diagnostic::new(TypeNoneComparison { replacement }, compare.range);
let negate = replacement == IdentityCheck::IsNot;
let fix = replace_with_identity_check(other_arg, compare.range, negate, checker);
checker.diagnostics.push(diagnostic.with_fix(fix));
}
/// Returns the object passed to the function, if the expression is a call to
@ -143,9 +112,16 @@ fn type_call_arg<'a>(expr: &'a Expr, semantic: &'a SemanticModel) -> Option<&'a
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Comparison {
enum IdentityCheck {
Is,
IsNot,
Eq,
NotEq,
}
impl IdentityCheck {
fn op(self) -> CmpOp {
match self {
Self::Is => CmpOp::Is,
Self::IsNot => CmpOp::IsNot,
}
}
}

View file

@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/refurb/mod.rs
---
FURB169.py:5:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:5:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
3 | # Error.
4 |
@ -10,7 +10,7 @@ FURB169.py:5:1: FURB169 [*] Compare the identities of `foo` and `None` instead o
6 |
7 | type(None) is type(foo)
|
= help: Replace with `foo is None`
= help: Replace with `is None`
Safe fix
2 2 |
@ -22,7 +22,7 @@ FURB169.py:5:1: FURB169 [*] Compare the identities of `foo` and `None` instead o
7 7 | type(None) is type(foo)
8 8 |
FURB169.py:7:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:7:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
5 | type(foo) is type(None)
6 |
@ -31,7 +31,7 @@ FURB169.py:7:1: FURB169 [*] Compare the identities of `foo` and `None` instead o
8 |
9 | type(None) is type(None)
|
= help: Replace with `foo is None`
= help: Replace with `is None`
Safe fix
4 4 |
@ -43,7 +43,7 @@ FURB169.py:7:1: FURB169 [*] Compare the identities of `foo` and `None` instead o
9 9 | type(None) is type(None)
10 10 |
FURB169.py:9:1: FURB169 [*] Compare the identities of `None` and `None` instead of their respective types
FURB169.py:9:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
7 | type(None) is type(foo)
8 |
@ -52,7 +52,7 @@ FURB169.py:9:1: FURB169 [*] Compare the identities of `None` and `None` instead
10 |
11 | type(foo) is not type(None)
|
= help: Replace with `None is None`
= help: Replace with `is None`
Safe fix
6 6 |
@ -64,7 +64,7 @@ FURB169.py:9:1: FURB169 [*] Compare the identities of `None` and `None` instead
11 11 | type(foo) is not type(None)
12 12 |
FURB169.py:11:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:11:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
9 | type(None) is type(None)
10 |
@ -73,7 +73,7 @@ FURB169.py:11:1: FURB169 [*] Compare the identities of `foo` and `None` instead
12 |
13 | type(None) is not type(foo)
|
= help: Replace with `foo is not None`
= help: Replace with `is not None`
Safe fix
8 8 |
@ -85,7 +85,7 @@ FURB169.py:11:1: FURB169 [*] Compare the identities of `foo` and `None` instead
13 13 | type(None) is not type(foo)
14 14 |
FURB169.py:13:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:13:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
11 | type(foo) is not type(None)
12 |
@ -94,7 +94,7 @@ FURB169.py:13:1: FURB169 [*] Compare the identities of `foo` and `None` instead
14 |
15 | type(None) is not type(None)
|
= help: Replace with `foo is not None`
= help: Replace with `is not None`
Safe fix
10 10 |
@ -106,7 +106,7 @@ FURB169.py:13:1: FURB169 [*] Compare the identities of `foo` and `None` instead
15 15 | type(None) is not type(None)
16 16 |
FURB169.py:15:1: FURB169 [*] Compare the identities of `None` and `None` instead of their respective types
FURB169.py:15:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
13 | type(None) is not type(foo)
14 |
@ -115,7 +115,7 @@ FURB169.py:15:1: FURB169 [*] Compare the identities of `None` and `None` instead
16 |
17 | type(foo) == type(None)
|
= help: Replace with `None is not None`
= help: Replace with `is not None`
Safe fix
12 12 |
@ -127,7 +127,7 @@ FURB169.py:15:1: FURB169 [*] Compare the identities of `None` and `None` instead
17 17 | type(foo) == type(None)
18 18 |
FURB169.py:17:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:17:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
15 | type(None) is not type(None)
16 |
@ -136,7 +136,7 @@ FURB169.py:17:1: FURB169 [*] Compare the identities of `foo` and `None` instead
18 |
19 | type(None) == type(foo)
|
= help: Replace with `foo is None`
= help: Replace with `is None`
Safe fix
14 14 |
@ -148,7 +148,7 @@ FURB169.py:17:1: FURB169 [*] Compare the identities of `foo` and `None` instead
19 19 | type(None) == type(foo)
20 20 |
FURB169.py:19:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:19:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
17 | type(foo) == type(None)
18 |
@ -157,7 +157,7 @@ FURB169.py:19:1: FURB169 [*] Compare the identities of `foo` and `None` instead
20 |
21 | type(None) == type(None)
|
= help: Replace with `foo is None`
= help: Replace with `is None`
Safe fix
16 16 |
@ -169,7 +169,7 @@ FURB169.py:19:1: FURB169 [*] Compare the identities of `foo` and `None` instead
21 21 | type(None) == type(None)
22 22 |
FURB169.py:21:1: FURB169 [*] Compare the identities of `None` and `None` instead of their respective types
FURB169.py:21:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
19 | type(None) == type(foo)
20 |
@ -178,7 +178,7 @@ FURB169.py:21:1: FURB169 [*] Compare the identities of `None` and `None` instead
22 |
23 | type(foo) != type(None)
|
= help: Replace with `None is None`
= help: Replace with `is None`
Safe fix
18 18 |
@ -190,7 +190,7 @@ FURB169.py:21:1: FURB169 [*] Compare the identities of `None` and `None` instead
23 23 | type(foo) != type(None)
24 24 |
FURB169.py:23:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:23:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
21 | type(None) == type(None)
22 |
@ -199,7 +199,7 @@ FURB169.py:23:1: FURB169 [*] Compare the identities of `foo` and `None` instead
24 |
25 | type(None) != type(foo)
|
= help: Replace with `foo is not None`
= help: Replace with `is not None`
Safe fix
20 20 |
@ -211,7 +211,7 @@ FURB169.py:23:1: FURB169 [*] Compare the identities of `foo` and `None` instead
25 25 | type(None) != type(foo)
26 26 |
FURB169.py:25:1: FURB169 [*] Compare the identities of `foo` and `None` instead of their respective types
FURB169.py:25:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
23 | type(foo) != type(None)
24 |
@ -220,7 +220,7 @@ FURB169.py:25:1: FURB169 [*] Compare the identities of `foo` and `None` instead
26 |
27 | type(None) != type(None)
|
= help: Replace with `foo is not None`
= help: Replace with `is not None`
Safe fix
22 22 |
@ -232,16 +232,16 @@ FURB169.py:25:1: FURB169 [*] Compare the identities of `foo` and `None` instead
27 27 | type(None) != type(None)
28 28 |
FURB169.py:27:1: FURB169 [*] Compare the identities of `None` and `None` instead of their respective types
FURB169.py:27:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
25 | type(None) != type(foo)
26 |
27 | type(None) != type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
28 |
29 | # Ok.
29 | type(a.b) is type(None)
|
= help: Replace with `None is not None`
= help: Replace with `is not None`
Safe fix
24 24 |
@ -250,5 +250,5 @@ FURB169.py:27:1: FURB169 [*] Compare the identities of `None` and `None` instead
27 |-type(None) != type(None)
27 |+None is not None
28 28 |
29 29 | # Ok.
29 29 | type(a.b) is type(None)
30 30 |

View file

@ -0,0 +1,352 @@
---
source: crates/ruff_linter/src/rules/refurb/mod.rs
---
FURB169.py:5:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
3 | # Error.
4 |
5 | type(foo) is type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
6 |
7 | type(None) is type(foo)
|
= help: Replace with `is None`
Safe fix
2 2 |
3 3 | # Error.
4 4 |
5 |-type(foo) is type(None)
5 |+foo is None
6 6 |
7 7 | type(None) is type(foo)
8 8 |
FURB169.py:7:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
5 | type(foo) is type(None)
6 |
7 | type(None) is type(foo)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
8 |
9 | type(None) is type(None)
|
= help: Replace with `is None`
Safe fix
4 4 |
5 5 | type(foo) is type(None)
6 6 |
7 |-type(None) is type(foo)
7 |+foo is None
8 8 |
9 9 | type(None) is type(None)
10 10 |
FURB169.py:9:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
7 | type(None) is type(foo)
8 |
9 | type(None) is type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
10 |
11 | type(foo) is not type(None)
|
= help: Replace with `is None`
Safe fix
6 6 |
7 7 | type(None) is type(foo)
8 8 |
9 |-type(None) is type(None)
9 |+None is None
10 10 |
11 11 | type(foo) is not type(None)
12 12 |
FURB169.py:11:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
9 | type(None) is type(None)
10 |
11 | type(foo) is not type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
12 |
13 | type(None) is not type(foo)
|
= help: Replace with `is not None`
Safe fix
8 8 |
9 9 | type(None) is type(None)
10 10 |
11 |-type(foo) is not type(None)
11 |+foo is not None
12 12 |
13 13 | type(None) is not type(foo)
14 14 |
FURB169.py:13:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
11 | type(foo) is not type(None)
12 |
13 | type(None) is not type(foo)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
14 |
15 | type(None) is not type(None)
|
= help: Replace with `is not None`
Safe fix
10 10 |
11 11 | type(foo) is not type(None)
12 12 |
13 |-type(None) is not type(foo)
13 |+foo is not None
14 14 |
15 15 | type(None) is not type(None)
16 16 |
FURB169.py:15:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
13 | type(None) is not type(foo)
14 |
15 | type(None) is not type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
16 |
17 | type(foo) == type(None)
|
= help: Replace with `is not None`
Safe fix
12 12 |
13 13 | type(None) is not type(foo)
14 14 |
15 |-type(None) is not type(None)
15 |+None is not None
16 16 |
17 17 | type(foo) == type(None)
18 18 |
FURB169.py:17:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
15 | type(None) is not type(None)
16 |
17 | type(foo) == type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
18 |
19 | type(None) == type(foo)
|
= help: Replace with `is None`
Safe fix
14 14 |
15 15 | type(None) is not type(None)
16 16 |
17 |-type(foo) == type(None)
17 |+foo is None
18 18 |
19 19 | type(None) == type(foo)
20 20 |
FURB169.py:19:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
17 | type(foo) == type(None)
18 |
19 | type(None) == type(foo)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
20 |
21 | type(None) == type(None)
|
= help: Replace with `is None`
Safe fix
16 16 |
17 17 | type(foo) == type(None)
18 18 |
19 |-type(None) == type(foo)
19 |+foo is None
20 20 |
21 21 | type(None) == type(None)
22 22 |
FURB169.py:21:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
19 | type(None) == type(foo)
20 |
21 | type(None) == type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
22 |
23 | type(foo) != type(None)
|
= help: Replace with `is None`
Safe fix
18 18 |
19 19 | type(None) == type(foo)
20 20 |
21 |-type(None) == type(None)
21 |+None is None
22 22 |
23 23 | type(foo) != type(None)
24 24 |
FURB169.py:23:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
21 | type(None) == type(None)
22 |
23 | type(foo) != type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
24 |
25 | type(None) != type(foo)
|
= help: Replace with `is not None`
Safe fix
20 20 |
21 21 | type(None) == type(None)
22 22 |
23 |-type(foo) != type(None)
23 |+foo is not None
24 24 |
25 25 | type(None) != type(foo)
26 26 |
FURB169.py:25:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
23 | type(foo) != type(None)
24 |
25 | type(None) != type(foo)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
26 |
27 | type(None) != type(None)
|
= help: Replace with `is not None`
Safe fix
22 22 |
23 23 | type(foo) != type(None)
24 24 |
25 |-type(None) != type(foo)
25 |+foo is not None
26 26 |
27 27 | type(None) != type(None)
28 28 |
FURB169.py:27:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
25 | type(None) != type(foo)
26 |
27 | type(None) != type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^^ FURB169
28 |
29 | type(a.b) is type(None)
|
= help: Replace with `is not None`
Safe fix
24 24 |
25 25 | type(None) != type(foo)
26 26 |
27 |-type(None) != type(None)
27 |+None is not None
28 28 |
29 29 | type(a.b) is type(None)
30 30 |
FURB169.py:29:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
27 | type(None) != type(None)
28 |
29 | type(a.b) is type(None)
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB169
30 |
31 | type(
|
= help: Replace with `is None`
Safe fix
26 26 |
27 27 | type(None) != type(None)
28 28 |
29 |-type(a.b) is type(None)
29 |+a.b is None
30 30 |
31 31 | type(
32 32 | a(
FURB169.py:31:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
29 | type(a.b) is type(None)
30 |
31 | / type(
32 | | a(
33 | | # Comment
34 | | )
35 | | ) != type(None)
| |_______________^ FURB169
36 |
37 | type(
|
= help: Replace with `is not None`
Unsafe fix
28 28 |
29 29 | type(a.b) is type(None)
30 30 |
31 |-type(
32 |- a(
33 |- # Comment
34 |- )
35 |-) != type(None)
31 |+a() is not None
36 32 |
37 33 | type(
38 34 | a := 1
FURB169.py:37:1: FURB169 [*] When checking against `None`, use `is` instead of comparison with `type(None)`
|
35 | ) != type(None)
36 |
37 | / type(
38 | | a := 1
39 | | ) == type(None)
| |_______________^ FURB169
40 |
41 | type(
|
= help: Replace with `is None`
Safe fix
34 34 | )
35 35 | ) != type(None)
36 36 |
37 |-type(
38 |- a := 1
39 |-) == type(None)
37 |+(a := 1) is None
40 38 |
41 39 | type(
42 40 | a for a in range(0)
FURB169.py:41:1: FURB169 [*] When checking against `None`, use `is not` instead of comparison with `type(None)`
|
39 | ) == type(None)
40 |
41 | / type(
42 | | a for a in range(0)
43 | | ) is not type(None)
| |___________________^ FURB169
|
= help: Replace with `is not None`
Safe fix
38 38 | a := 1
39 39 | ) == type(None)
40 40 |
41 |-type(
42 |- a for a in range(0)
43 |-) is not type(None)
41 |+(a for a in range(0)) is not None
44 42 |
45 43 |
46 44 | # Ok.