Redirect PLR1701 to SIM101 (#12021)

## Summary

This rule removes `PLR1701` and redirects it to `SIM101`.

In addition to that, the `SIM101` autofix has been fixed to add padding
if required.

### `PLR1701` has bugs

It also seems that the implementation of `PLR1701` is incorrect in
multiple scenarios. For example, the following code snippet:
```py
# There are two _different_ variables `a` and `b`
if isinstance(a, int) or isinstance(b, bool) or isinstance(a, float):
    pass
# There's another condition `or 1`
if isinstance(self.k, int) or isinstance(self.k, float) or 1:
    pass
```
is fixed to:
```py
# Fixed to only considering variable `a`
if isinstance(a, (float, int)):
    pass
# The additional condition is not present in the fix
if isinstance(self.k, (float, int)):
    pass
```

Playground: https://play.ruff.rs/6cfbdfb7-f183-43b0-b59e-31e728b34190

## Documentation Preview

### `PLR1701`

<img width="1397" alt="Screenshot 2024-06-25 at 11 14 40"
src="779ee84d-7c4d-4bb8-a3a4-c2b23a313eba">

## Test Plan

Remove the test cases for `PLR1701`, port the padding test case to
`SIM101` and update the snapshot.
This commit is contained in:
Dhruv Manilawala 2024-06-25 19:21:34 +05:30 committed by Micha Reiser
parent 0a24d70bfd
commit 1968332d93
12 changed files with 54 additions and 467 deletions

View file

@ -48,3 +48,7 @@ def f():
if isinstance(a, int) or isinstance(a, float): if isinstance(a, int) or isinstance(a, float):
pass pass
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
if(isinstance(a, int)) or (isinstance(a, float)):
pass

View file

@ -1,43 +0,0 @@
"""Checks use of consider-merging-isinstance"""
# pylint:disable=line-too-long, simplifiable-condition
def isinstances():
"Examples of isinstances"
var = range(10)
# merged
if isinstance(var[1], (int, float)):
pass
result = isinstance(var[2], (int, float))
# not merged
if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
pass
result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
inferred_isinstance = isinstance
result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
result = isinstance(var[20])
result = isinstance()
# Combination merged and not merged
result = isinstance(var[12], (int, float)) or isinstance(var[12], list) # [consider-merging-isinstance]
# not merged but valid
result = isinstance(var[5], int) and var[5] * 14 or isinstance(var[5], float) and var[5] * 14.4
result = isinstance(var[7], int) or not isinstance(var[7], float)
result = isinstance(var[6], int) or isinstance(var[7], float)
result = isinstance(var[6], int) or isinstance(var[7], int)
result = isinstance(var[6], (float, int)) or False
return result
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
if(isinstance(self.k, int)) or (isinstance(self.k, float)):
...

View file

@ -1515,16 +1515,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
refurb::rules::reimplemented_starmap(checker, &generator.into()); refurb::rules::reimplemented_starmap(checker, &generator.into());
} }
} }
Expr::BoolOp( Expr::BoolOp(bool_op) => {
bool_op @ ast::ExprBoolOp {
op,
values,
range: _,
},
) => {
if checker.enabled(Rule::RepeatedIsinstanceCalls) {
pylint::rules::repeated_isinstance_calls(checker, expr, *op, values);
}
if checker.enabled(Rule::MultipleStartsEndsWith) { if checker.enabled(Rule::MultipleStartsEndsWith) {
flake8_pie::rules::multiple_starts_ends_with(checker, expr); flake8_pie::rules::multiple_starts_ends_with(checker, expr);
} }

View file

@ -248,7 +248,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "R0915") => (RuleGroup::Stable, rules::pylint::rules::TooManyStatements), (Pylint, "R0915") => (RuleGroup::Stable, rules::pylint::rules::TooManyStatements),
(Pylint, "R0916") => (RuleGroup::Preview, rules::pylint::rules::TooManyBooleanExpressions), (Pylint, "R0916") => (RuleGroup::Preview, rules::pylint::rules::TooManyBooleanExpressions),
(Pylint, "R0917") => (RuleGroup::Preview, rules::pylint::rules::TooManyPositional), (Pylint, "R0917") => (RuleGroup::Preview, rules::pylint::rules::TooManyPositional),
(Pylint, "R1701") => (RuleGroup::Stable, rules::pylint::rules::RepeatedIsinstanceCalls), (Pylint, "R1701") => (RuleGroup::Removed, rules::pylint::rules::RepeatedIsinstanceCalls),
(Pylint, "R1702") => (RuleGroup::Preview, rules::pylint::rules::TooManyNestedBlocks), (Pylint, "R1702") => (RuleGroup::Preview, rules::pylint::rules::TooManyNestedBlocks),
(Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal), (Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal),
(Pylint, "R1706") => (RuleGroup::Removed, rules::pylint::rules::AndOrTernary), (Pylint, "R1706") => (RuleGroup::Removed, rules::pylint::rules::AndOrTernary),

View file

@ -103,6 +103,8 @@ static REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
("TRY200", "B904"), ("TRY200", "B904"),
("PGH001", "S307"), ("PGH001", "S307"),
("PGH002", "G010"), ("PGH002", "G010"),
// Removed in v0.5
("PLR1701", "SIM101"),
// Test redirect by exact code // Test redirect by exact code
#[cfg(any(feature = "test-rules", test))] #[cfg(any(feature = "test-rules", test))]
("RUF940", "RUF950"), ("RUF940", "RUF950"),

View file

@ -15,6 +15,7 @@ use ruff_python_codegen::Generator;
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::edits::pad;
/// ## What it does /// ## What it does
/// Checks for multiple `isinstance` calls on the same target. /// Checks for multiple `isinstance` calls on the same target.
@ -404,7 +405,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
.collect(); .collect();
// Generate a single `isinstance` call. // Generate a single `isinstance` call.
let node = ast::ExprTuple { let tuple = ast::ExprTuple {
// Flatten all the types used across the `isinstance` calls. // Flatten all the types used across the `isinstance` calls.
elts: types elts: types
.iter() .iter()
@ -421,21 +422,23 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
range: TextRange::default(), range: TextRange::default(),
parenthesized: true, parenthesized: true,
}; };
let node1 = ast::ExprName { let isinstance_call = ast::ExprCall {
id: "isinstance".into(), func: Box::new(
ctx: ExprContext::Load, ast::ExprName {
range: TextRange::default(), id: "isinstance".into(),
}; ctx: ExprContext::Load,
let node2 = ast::ExprCall { range: TextRange::default(),
func: Box::new(node1.into()), }
.into(),
),
arguments: Arguments { arguments: Arguments {
args: Box::from([target.clone(), node.into()]), args: Box::from([target.clone(), tuple.into()]),
keywords: Box::from([]), keywords: Box::from([]),
range: TextRange::default(), range: TextRange::default(),
}, },
range: TextRange::default(), range: TextRange::default(),
}; }
let call = node2.into(); .into();
// Generate the combined `BoolOp`. // Generate the combined `BoolOp`.
let [first, .., last] = indices.as_slice() else { let [first, .., last] = indices.as_slice() else {
@ -443,17 +446,21 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
}; };
let before = values.iter().take(*first).cloned(); let before = values.iter().take(*first).cloned();
let after = values.iter().skip(last + 1).cloned(); let after = values.iter().skip(last + 1).cloned();
let node = ast::ExprBoolOp { let bool_op = ast::ExprBoolOp {
op: BoolOp::Or, op: BoolOp::Or,
values: before.chain(iter::once(call)).chain(after).collect(), values: before
.chain(iter::once(isinstance_call))
.chain(after)
.collect(),
range: TextRange::default(), range: TextRange::default(),
}; }
let bool_op = node.into(); .into();
let fixed_source = checker.generator().expr(&bool_op);
// Populate the `Fix`. Replace the _entire_ `BoolOp`. Note that if we have // Populate the `Fix`. Replace the _entire_ `BoolOp`. Note that if we have
// multiple duplicates, the fixes will conflict. // multiple duplicates, the fixes will conflict.
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&bool_op), pad(fixed_source, expr.range(), checker.locator()),
expr.range(), expr.range(),
))); )));
} }

View file

@ -166,4 +166,19 @@ SIM101.py:41:4: SIM101 [*] Multiple `isinstance` calls for `a`, merge into a sin
43 43 | 43 43 |
44 44 | def f(): 44 44 | def f():
SIM101.py:53:3: SIM101 [*] Multiple `isinstance` calls for `a`, merge into a single call
|
52 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
53 | if(isinstance(a, int)) or (isinstance(a, float)):
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM101
54 | pass
|
= help: Merge `isinstance` calls for `a`
Unsafe fix
50 50 | pass
51 51 |
52 52 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
53 |-if(isinstance(a, int)) or (isinstance(a, float)):
53 |+if isinstance(a, (int, float)):
54 54 | pass

View file

@ -39,10 +39,6 @@ mod tests {
#[test_case(Rule::CollapsibleElseIf, Path::new("collapsible_else_if.py"))] #[test_case(Rule::CollapsibleElseIf, Path::new("collapsible_else_if.py"))]
#[test_case(Rule::CompareToEmptyString, Path::new("compare_to_empty_string.py"))] #[test_case(Rule::CompareToEmptyString, Path::new("compare_to_empty_string.py"))]
#[test_case(Rule::ComparisonOfConstant, Path::new("comparison_of_constant.py"))] #[test_case(Rule::ComparisonOfConstant, Path::new("comparison_of_constant.py"))]
#[test_case(
Rule::RepeatedIsinstanceCalls,
Path::new("repeated_isinstance_calls.py")
)]
#[test_case(Rule::ComparisonWithItself, Path::new("comparison_with_itself.py"))] #[test_case(Rule::ComparisonWithItself, Path::new("comparison_with_itself.py"))]
#[test_case(Rule::EqWithoutHash, Path::new("eq_without_hash.py"))] #[test_case(Rule::EqWithoutHash, Path::new("eq_without_hash.py"))]
#[test_case(Rule::EmptyComment, Path::new("empty_comment.py"))] #[test_case(Rule::EmptyComment, Path::new("empty_comment.py"))]
@ -229,17 +225,6 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn repeated_isinstance_calls() -> Result<()> {
let diagnostics = test_path(
Path::new("pylint/repeated_isinstance_calls.py"),
&LinterSettings::for_rule(Rule::RepeatedIsinstanceCalls)
.with_target_version(PythonVersion::Py39),
)?;
assert_messages!(diagnostics);
Ok(())
}
#[test] #[test]
fn continue_in_finally() -> Result<()> { fn continue_in_finally() -> Result<()> {
let diagnostics = test_path( let diagnostics = test_path(

View file

@ -1,18 +1,11 @@
use itertools::Itertools; use ruff_diagnostics::AlwaysFixableViolation;
use ruff_python_ast::{self as ast, Arguments, BoolOp, Expr};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::fix::edits::pad;
use crate::fix::snippet::SourceCodeSnippet;
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::hashable::HashableExpr;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::fix::snippet::SourceCodeSnippet;
use crate::settings::types::PythonVersion;
/// ## Removed
/// This rule is identical to [SIM101] which should be used instead.
///
/// ## What it does /// ## What it does
/// Checks for repeated `isinstance` calls on the same object. /// Checks for repeated `isinstance` calls on the same object.
/// ///
@ -53,11 +46,14 @@ use crate::settings::types::PythonVersion;
/// ///
/// ## References /// ## References
/// - [Python documentation: `isinstance`](https://docs.python.org/3/library/functions.html#isinstance) /// - [Python documentation: `isinstance`](https://docs.python.org/3/library/functions.html#isinstance)
///
/// [SIM101]: https://docs.astral.sh/ruff/rules/duplicate-isinstance-call/
#[violation] #[violation]
pub struct RepeatedIsinstanceCalls { pub struct RepeatedIsinstanceCalls {
expression: SourceCodeSnippet, expression: SourceCodeSnippet,
} }
// PLR1701
impl AlwaysFixableViolation for RepeatedIsinstanceCalls { impl AlwaysFixableViolation for RepeatedIsinstanceCalls {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
@ -78,88 +74,3 @@ impl AlwaysFixableViolation for RepeatedIsinstanceCalls {
} }
} }
} }
/// PLR1701
pub(crate) fn repeated_isinstance_calls(
checker: &mut Checker,
expr: &Expr,
op: BoolOp,
values: &[Expr],
) {
if !op.is_or() {
return;
}
let mut obj_to_types: FxHashMap<HashableExpr, (usize, FxHashSet<HashableExpr>)> =
FxHashMap::default();
for value in values {
let Expr::Call(ast::ExprCall {
func,
arguments: Arguments { args, .. },
..
}) = value
else {
continue;
};
let [obj, types] = &args[..] else {
continue;
};
if !checker.semantic().match_builtin_expr(func, "isinstance") {
continue;
}
let (num_calls, matches) = obj_to_types
.entry(obj.into())
.or_insert_with(|| (0, FxHashSet::default()));
*num_calls += 1;
matches.extend(match types {
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
elts.iter().map(HashableExpr::from_expr).collect()
}
_ => {
vec![types.into()]
}
});
}
for (obj, (num_calls, types)) in obj_to_types {
if num_calls > 1 && types.len() > 1 {
let call = merged_isinstance_call(
&checker.generator().expr(obj.as_expr()),
types
.iter()
.map(HashableExpr::as_expr)
.map(|expr| checker.generator().expr(expr))
.sorted(),
checker.settings.target_version,
);
let mut diagnostic = Diagnostic::new(
RepeatedIsinstanceCalls {
expression: SourceCodeSnippet::new(call.clone()),
},
expr.range(),
);
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_replacement(pad(call, expr.range(), checker.locator()), expr.range()),
if checker.settings.target_version >= PythonVersion::Py310 {
Applicability::Unsafe
} else {
Applicability::Safe
},
));
checker.diagnostics.push(diagnostic);
}
}
}
fn merged_isinstance_call(
obj: &str,
types: impl IntoIterator<Item = String>,
target_version: PythonVersion,
) -> String {
if target_version >= PythonVersion::Py310 {
format!("isinstance({}, {})", obj, types.into_iter().join(" | "))
} else {
format!("isinstance({}, ({}))", obj, types.into_iter().join(", "))
}
}

View file

@ -1,142 +0,0 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
repeated_isinstance_calls.py:15:8: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[3], float | int)`
|
14 | # not merged
15 | if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
16 | pass
17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
|
= help: Replace with `isinstance(var[3], float | int)`
Unsafe fix
12 12 | result = isinstance(var[2], (int, float))
13 13 |
14 14 | # not merged
15 |- if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
15 |+ if isinstance(var[3], float | int): # [consider-merging-isinstance]
16 16 | pass
17 17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
18 18 |
repeated_isinstance_calls.py:17:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[4], float | int)`
|
15 | if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
16 | pass
17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
18 |
19 | result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
|
= help: Replace with `isinstance(var[4], float | int)`
Unsafe fix
14 14 | # not merged
15 15 | if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
16 16 | pass
17 |- result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
17 |+ result = isinstance(var[4], float | int) # [consider-merging-isinstance]
18 18 |
19 19 | result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
20 20 |
repeated_isinstance_calls.py:19:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[5], float | int)`
|
17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
18 |
19 | result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
20 |
21 | inferred_isinstance = isinstance
|
= help: Replace with `isinstance(var[5], float | int)`
Unsafe fix
16 16 | pass
17 17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
18 18 |
19 |- result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
19 |+ result = isinstance(var[5], float | int) # [consider-merging-isinstance]
20 20 |
21 21 | inferred_isinstance = isinstance
22 22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
repeated_isinstance_calls.py:23:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[10], list | str)`
|
21 | inferred_isinstance = isinstance
22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 | result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
24 | result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
|
= help: Replace with `isinstance(var[10], list | str)`
Unsafe fix
20 20 |
21 21 | inferred_isinstance = isinstance
22 22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 |- result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
23 |+ result = isinstance(var[10], list | str) # [consider-merging-isinstance]
24 24 | result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
25 25 |
26 26 | result = isinstance(var[20])
repeated_isinstance_calls.py:24:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[11], float | int)`
|
22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 | result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
24 | result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
25 |
26 | result = isinstance(var[20])
|
= help: Replace with `isinstance(var[11], float | int)`
Unsafe fix
21 21 | inferred_isinstance = isinstance
22 22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 23 | result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
24 |- result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
24 |+ result = isinstance(var[11], float | int) # [consider-merging-isinstance]
25 25 |
26 26 | result = isinstance(var[20])
27 27 | result = isinstance()
repeated_isinstance_calls.py:30:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[12], float | int | list)`
|
29 | # Combination merged and not merged
30 | result = isinstance(var[12], (int, float)) or isinstance(var[12], list) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
31 |
32 | # not merged but valid
|
= help: Replace with `isinstance(var[12], float | int | list)`
Unsafe fix
27 27 | result = isinstance()
28 28 |
29 29 | # Combination merged and not merged
30 |- result = isinstance(var[12], (int, float)) or isinstance(var[12], list) # [consider-merging-isinstance]
30 |+ result = isinstance(var[12], float | int | list) # [consider-merging-isinstance]
31 31 |
32 32 | # not merged but valid
33 33 | result = isinstance(var[5], int) and var[5] * 14 or isinstance(var[5], float) and var[5] * 14.4
repeated_isinstance_calls.py:42:3: PLR1701 [*] Merge `isinstance` calls: `isinstance(self.k, float | int)`
|
41 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
42 | if(isinstance(self.k, int)) or (isinstance(self.k, float)):
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
43 | ...
|
= help: Replace with `isinstance(self.k, float | int)`
Unsafe fix
39 39 |
40 40 |
41 41 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
42 |-if(isinstance(self.k, int)) or (isinstance(self.k, float)):
42 |+if isinstance(self.k, float | int):
43 43 | ...

View file

@ -1,142 +0,0 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
repeated_isinstance_calls.py:15:8: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[3], (float, int))`
|
14 | # not merged
15 | if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
16 | pass
17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
|
= help: Replace with `isinstance(var[3], (float, int))`
Safe fix
12 12 | result = isinstance(var[2], (int, float))
13 13 |
14 14 | # not merged
15 |- if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
15 |+ if isinstance(var[3], (float, int)): # [consider-merging-isinstance]
16 16 | pass
17 17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
18 18 |
repeated_isinstance_calls.py:17:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[4], (float, int))`
|
15 | if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
16 | pass
17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
18 |
19 | result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
|
= help: Replace with `isinstance(var[4], (float, int))`
Safe fix
14 14 | # not merged
15 15 | if isinstance(var[3], int) or isinstance(var[3], float) or isinstance(var[3], list) and True: # [consider-merging-isinstance]
16 16 | pass
17 |- result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
17 |+ result = isinstance(var[4], (float, int)) # [consider-merging-isinstance]
18 18 |
19 19 | result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
20 20 |
repeated_isinstance_calls.py:19:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[5], (float, int))`
|
17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
18 |
19 | result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
20 |
21 | inferred_isinstance = isinstance
|
= help: Replace with `isinstance(var[5], (float, int))`
Safe fix
16 16 | pass
17 17 | result = isinstance(var[4], int) or isinstance(var[4], float) or isinstance(var[5], list) and False # [consider-merging-isinstance]
18 18 |
19 |- result = isinstance(var[5], int) or True or isinstance(var[5], float) # [consider-merging-isinstance]
19 |+ result = isinstance(var[5], (float, int)) # [consider-merging-isinstance]
20 20 |
21 21 | inferred_isinstance = isinstance
22 22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
repeated_isinstance_calls.py:23:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[10], (list, str))`
|
21 | inferred_isinstance = isinstance
22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 | result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
24 | result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
|
= help: Replace with `isinstance(var[10], (list, str))`
Safe fix
20 20 |
21 21 | inferred_isinstance = isinstance
22 22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 |- result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
23 |+ result = isinstance(var[10], (list, str)) # [consider-merging-isinstance]
24 24 | result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
25 25 |
26 26 | result = isinstance(var[20])
repeated_isinstance_calls.py:24:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[11], (float, int))`
|
22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 | result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
24 | result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
25 |
26 | result = isinstance(var[20])
|
= help: Replace with `isinstance(var[11], (float, int))`
Safe fix
21 21 | inferred_isinstance = isinstance
22 22 | result = inferred_isinstance(var[6], int) or inferred_isinstance(var[6], float) or inferred_isinstance(var[6], list) and False # [consider-merging-isinstance]
23 23 | result = isinstance(var[10], str) or isinstance(var[10], int) and var[8] * 14 or isinstance(var[10], float) and var[5] * 14.4 or isinstance(var[10], list) # [consider-merging-isinstance]
24 |- result = isinstance(var[11], int) or isinstance(var[11], int) or isinstance(var[11], float) # [consider-merging-isinstance]
24 |+ result = isinstance(var[11], (float, int)) # [consider-merging-isinstance]
25 25 |
26 26 | result = isinstance(var[20])
27 27 | result = isinstance()
repeated_isinstance_calls.py:30:14: PLR1701 [*] Merge `isinstance` calls: `isinstance(var[12], (float, int, list))`
|
29 | # Combination merged and not merged
30 | result = isinstance(var[12], (int, float)) or isinstance(var[12], list) # [consider-merging-isinstance]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
31 |
32 | # not merged but valid
|
= help: Replace with `isinstance(var[12], (float, int, list))`
Safe fix
27 27 | result = isinstance()
28 28 |
29 29 | # Combination merged and not merged
30 |- result = isinstance(var[12], (int, float)) or isinstance(var[12], list) # [consider-merging-isinstance]
30 |+ result = isinstance(var[12], (float, int, list)) # [consider-merging-isinstance]
31 31 |
32 32 | # not merged but valid
33 33 | result = isinstance(var[5], int) and var[5] * 14 or isinstance(var[5], float) and var[5] * 14.4
repeated_isinstance_calls.py:42:3: PLR1701 [*] Merge `isinstance` calls: `isinstance(self.k, (float, int))`
|
41 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
42 | if(isinstance(self.k, int)) or (isinstance(self.k, float)):
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR1701
43 | ...
|
= help: Replace with `isinstance(self.k, (float, int))`
Safe fix
39 39 |
40 40 |
41 41 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722460483
42 |-if(isinstance(self.k, int)) or (isinstance(self.k, float)):
42 |+if isinstance(self.k, (float, int)):
43 43 | ...

1
ruff.schema.json generated
View file

@ -3372,7 +3372,6 @@
"PLR1", "PLR1",
"PLR17", "PLR17",
"PLR170", "PLR170",
"PLR1701",
"PLR1702", "PLR1702",
"PLR1704", "PLR1704",
"PLR171", "PLR171",