mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:55:08 +00:00
[refurb
] Implement no-slice-copy
(FURB145
) (#7007)
## Summary Implement [`no-slice-copy`](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/no_slice_copy.py) as `slice-copy` (`FURB145`). Related to #1348. ## Test Plan `cargo test`
This commit is contained in:
parent
b0cbcd3dfa
commit
ebe9c03545
13 changed files with 325 additions and 40 deletions
21
crates/ruff/resources/test/fixtures/refurb/FURB145.py
vendored
Normal file
21
crates/ruff/resources/test/fixtures/refurb/FURB145.py
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
l = [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
# Errors.
|
||||||
|
a = l[:]
|
||||||
|
b, c = 1, l[:]
|
||||||
|
d, e = l[:], 1
|
||||||
|
m = l[::]
|
||||||
|
l[:]
|
||||||
|
print(l[:])
|
||||||
|
|
||||||
|
# False negatives.
|
||||||
|
aa = a[:] # Type inference.
|
||||||
|
|
||||||
|
# OK.
|
||||||
|
t = (1, 2, 3, 4, 5)
|
||||||
|
f = t[:] # t.copy() is not supported.
|
||||||
|
g = l[1:3]
|
||||||
|
h = l[1:]
|
||||||
|
i = l[:3]
|
||||||
|
j = l[1:3:2]
|
||||||
|
k = l[::2]
|
|
@ -16,7 +16,7 @@ use crate::rules::{
|
||||||
flake8_future_annotations, flake8_gettext, flake8_implicit_str_concat, flake8_logging_format,
|
flake8_future_annotations, flake8_gettext, flake8_implicit_str_concat, flake8_logging_format,
|
||||||
flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_self, flake8_simplify,
|
flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_self, flake8_simplify,
|
||||||
flake8_tidy_imports, flake8_use_pathlib, flynt, numpy, pandas_vet, pep8_naming, pycodestyle,
|
flake8_tidy_imports, flake8_use_pathlib, flynt, numpy, pandas_vet, pep8_naming, pycodestyle,
|
||||||
pyflakes, pygrep_hooks, pylint, pyupgrade, ruff,
|
pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff,
|
||||||
};
|
};
|
||||||
use crate::settings::types::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
|
|
||||||
|
@ -113,10 +113,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::UnnecessaryIterableAllocationForFirstElement) {
|
if checker.enabled(Rule::UnnecessaryIterableAllocationForFirstElement) {
|
||||||
ruff::rules::unnecessary_iterable_allocation_for_first_element(checker, subscript);
|
ruff::rules::unnecessary_iterable_allocation_for_first_element(checker, subscript);
|
||||||
}
|
}
|
||||||
|
|
||||||
if checker.enabled(Rule::InvalidIndexType) {
|
if checker.enabled(Rule::InvalidIndexType) {
|
||||||
ruff::rules::invalid_index_type(checker, subscript);
|
ruff::rules::invalid_index_type(checker, subscript);
|
||||||
}
|
}
|
||||||
|
if checker.settings.rules.enabled(Rule::SliceCopy) {
|
||||||
|
refurb::rules::slice_copy(checker, subscript);
|
||||||
|
}
|
||||||
|
|
||||||
pandas_vet::rules::subscript(checker, value, expr);
|
pandas_vet::rules::subscript(checker, value, expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -916,6 +916,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Refurb, "131") => (RuleGroup::Nursery, rules::refurb::rules::DeleteFullSlice),
|
(Refurb, "131") => (RuleGroup::Nursery, rules::refurb::rules::DeleteFullSlice),
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
(Refurb, "132") => (RuleGroup::Nursery, rules::refurb::rules::CheckAndRemoveFromSet),
|
(Refurb, "132") => (RuleGroup::Nursery, rules::refurb::rules::CheckAndRemoveFromSet),
|
||||||
|
#[allow(deprecated)]
|
||||||
|
(Refurb, "145") => (RuleGroup::Nursery, rules::refurb::rules::SliceCopy),
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
|
|
36
crates/ruff/src/rules/refurb/helpers.rs
Normal file
36
crates/ruff/src/rules/refurb/helpers.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use ruff_python_ast as ast;
|
||||||
|
use ruff_python_codegen::Generator;
|
||||||
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
|
/// Format a code snippet to call `name.method()`.
|
||||||
|
pub(super) fn generate_method_call(name: &str, method: &str, generator: Generator) -> String {
|
||||||
|
// Construct `name`.
|
||||||
|
let var = ast::ExprName {
|
||||||
|
id: name.to_string(),
|
||||||
|
ctx: ast::ExprContext::Load,
|
||||||
|
range: TextRange::default(),
|
||||||
|
};
|
||||||
|
// Construct `name.method`.
|
||||||
|
let attr = ast::ExprAttribute {
|
||||||
|
value: Box::new(var.into()),
|
||||||
|
attr: ast::Identifier::new(method.to_string(), TextRange::default()),
|
||||||
|
ctx: ast::ExprContext::Load,
|
||||||
|
range: TextRange::default(),
|
||||||
|
};
|
||||||
|
// Make it into a call `name.method()`
|
||||||
|
let call = ast::ExprCall {
|
||||||
|
func: Box::new(attr.into()),
|
||||||
|
arguments: ast::Arguments {
|
||||||
|
args: vec![],
|
||||||
|
keywords: vec![],
|
||||||
|
range: TextRange::default(),
|
||||||
|
},
|
||||||
|
range: TextRange::default(),
|
||||||
|
};
|
||||||
|
// And finally, turn it into a statement.
|
||||||
|
let stmt = ast::StmtExpr {
|
||||||
|
value: Box::new(call.into()),
|
||||||
|
range: TextRange::default(),
|
||||||
|
};
|
||||||
|
generator.stmt(&stmt.into())
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
//! Rules from [refurb](https://pypi.org/project/refurb/)/
|
//! Rules from [refurb](https://pypi.org/project/refurb/)/
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
pub(crate) mod rules;
|
pub(crate) mod rules;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -16,6 +17,7 @@ mod tests {
|
||||||
#[test_case(Rule::RepeatedAppend, Path::new("FURB113.py"))]
|
#[test_case(Rule::RepeatedAppend, Path::new("FURB113.py"))]
|
||||||
#[test_case(Rule::DeleteFullSlice, Path::new("FURB131.py"))]
|
#[test_case(Rule::DeleteFullSlice, Path::new("FURB131.py"))]
|
||||||
#[test_case(Rule::CheckAndRemoveFromSet, Path::new("FURB132.py"))]
|
#[test_case(Rule::CheckAndRemoveFromSet, Path::new("FURB132.py"))]
|
||||||
|
#[test_case(Rule::SliceCopy, Path::new("FURB145.py"))]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
@ -18,6 +18,11 @@ use crate::registry::AsRule;
|
||||||
/// If an element should be removed from a set if it is present, it is more
|
/// If an element should be removed from a set if it is present, it is more
|
||||||
/// succinct and idiomatic to use `discard`.
|
/// succinct and idiomatic to use `discard`.
|
||||||
///
|
///
|
||||||
|
/// ## Known problems
|
||||||
|
/// This rule is prone to false negatives due to type inference limitations,
|
||||||
|
/// as it will only detect sets that are instantiated as literals or annotated
|
||||||
|
/// with a type annotation.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// nums = {123, 456}
|
/// nums = {123, 456}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_codegen::Generator;
|
|
||||||
use ruff_python_semantic::analyze::typing::{is_dict, is_list};
|
use ruff_python_semantic::analyze::typing::{is_dict, is_list};
|
||||||
use ruff_python_semantic::{Binding, SemanticModel};
|
use ruff_python_semantic::{Binding, SemanticModel};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
use crate::rules::refurb::helpers::generate_method_call;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for `del` statements that delete the entire slice of a list or
|
/// Checks for `del` statements that delete the entire slice of a list or
|
||||||
|
@ -17,6 +17,11 @@ use crate::registry::AsRule;
|
||||||
/// It's is faster and more succinct to remove all items via the `clear()`
|
/// It's is faster and more succinct to remove all items via the `clear()`
|
||||||
/// method.
|
/// method.
|
||||||
///
|
///
|
||||||
|
/// ## Known problems
|
||||||
|
/// This rule is prone to false negatives due to type inference limitations,
|
||||||
|
/// as it will only detect lists and dictionaries that are instantiated as
|
||||||
|
/// literals or annotated with a type annotation.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// names = {"key": "value"}
|
/// names = {"key": "value"}
|
||||||
|
@ -65,7 +70,7 @@ pub(crate) fn delete_full_slice(checker: &mut Checker, delete: &ast::StmtDelete)
|
||||||
|
|
||||||
// Fix is only supported for single-target deletions.
|
// Fix is only supported for single-target deletions.
|
||||||
if checker.patch(diagnostic.kind.rule()) && delete.targets.len() == 1 {
|
if checker.patch(diagnostic.kind.rule()) && delete.targets.len() == 1 {
|
||||||
let replacement = make_suggestion(name, checker.generator());
|
let replacement = generate_method_call(name, "clear", checker.generator());
|
||||||
diagnostic.set_fix(Fix::suggested(Edit::replacement(
|
diagnostic.set_fix(Fix::suggested(Edit::replacement(
|
||||||
replacement,
|
replacement,
|
||||||
delete.start(),
|
delete.start(),
|
||||||
|
@ -118,38 +123,3 @@ fn match_full_slice<'a>(expr: &'a Expr, semantic: &SemanticModel) -> Option<&'a
|
||||||
// Name is needed for the fix suggestion.
|
// Name is needed for the fix suggestion.
|
||||||
Some(name)
|
Some(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make fix suggestion for the given name, ie `name.clear()`.
|
|
||||||
fn make_suggestion(name: &str, generator: Generator) -> String {
|
|
||||||
// Here we construct `var.clear()`
|
|
||||||
//
|
|
||||||
// And start with construction of `var`
|
|
||||||
let var = ast::ExprName {
|
|
||||||
id: name.to_string(),
|
|
||||||
ctx: ast::ExprContext::Load,
|
|
||||||
range: TextRange::default(),
|
|
||||||
};
|
|
||||||
// Make `var.clear`.
|
|
||||||
let attr = ast::ExprAttribute {
|
|
||||||
value: Box::new(var.into()),
|
|
||||||
attr: ast::Identifier::new("clear".to_string(), TextRange::default()),
|
|
||||||
ctx: ast::ExprContext::Load,
|
|
||||||
range: TextRange::default(),
|
|
||||||
};
|
|
||||||
// Make it into a call `var.clear()`
|
|
||||||
let call = ast::ExprCall {
|
|
||||||
func: Box::new(attr.into()),
|
|
||||||
arguments: ast::Arguments {
|
|
||||||
args: vec![],
|
|
||||||
keywords: vec![],
|
|
||||||
range: TextRange::default(),
|
|
||||||
},
|
|
||||||
range: TextRange::default(),
|
|
||||||
};
|
|
||||||
// And finally, turn it into a statement.
|
|
||||||
let stmt = ast::StmtExpr {
|
|
||||||
value: Box::new(call.into()),
|
|
||||||
range: TextRange::default(),
|
|
||||||
};
|
|
||||||
generator.stmt(&stmt.into())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
pub(crate) use check_and_remove_from_set::*;
|
pub(crate) use check_and_remove_from_set::*;
|
||||||
pub(crate) use delete_full_slice::*;
|
pub(crate) use delete_full_slice::*;
|
||||||
pub(crate) use repeated_append::*;
|
pub(crate) use repeated_append::*;
|
||||||
|
pub(crate) use slice_copy::*;
|
||||||
|
|
||||||
mod check_and_remove_from_set;
|
mod check_and_remove_from_set;
|
||||||
mod delete_full_slice;
|
mod delete_full_slice;
|
||||||
mod repeated_append;
|
mod repeated_append;
|
||||||
|
mod slice_copy;
|
||||||
|
|
|
@ -21,6 +21,11 @@ use crate::registry::AsRule;
|
||||||
/// a single `extend`. Each `append` resizes the list individually, whereas an
|
/// a single `extend`. Each `append` resizes the list individually, whereas an
|
||||||
/// `extend` can resize the list once for all elements.
|
/// `extend` can resize the list once for all elements.
|
||||||
///
|
///
|
||||||
|
/// ## Known problems
|
||||||
|
/// This rule is prone to false negatives due to type inference limitations,
|
||||||
|
/// as it will only detect lists that are instantiated as literals or annotated
|
||||||
|
/// with a type annotation.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// nums = [1, 2, 3]
|
/// nums = [1, 2, 3]
|
||||||
|
|
109
crates/ruff/src/rules/refurb/rules/slice_copy.rs
Normal file
109
crates/ruff/src/rules/refurb/rules/slice_copy.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
use ruff_python_semantic::analyze::typing::is_list;
|
||||||
|
use ruff_python_semantic::{Binding, SemanticModel};
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::registry::AsRule;
|
||||||
|
use crate::rules::refurb::helpers::generate_method_call;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for unbounded slice expressions to copy a list.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// The `list#copy` method is more readable and consistent with copying other
|
||||||
|
/// types.
|
||||||
|
///
|
||||||
|
/// ## Known problems
|
||||||
|
/// This rule is prone to false negatives due to type inference limitations,
|
||||||
|
/// as it will only detect lists that are instantiated as literals or annotated
|
||||||
|
/// with a type annotation.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// a = [1, 2, 3]
|
||||||
|
/// b = a[:]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// a = [1, 2, 3]
|
||||||
|
/// b = a.copy()
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## References
|
||||||
|
/// - [Python documentation: Mutable Sequence Types](https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types)
|
||||||
|
#[violation]
|
||||||
|
pub struct SliceCopy;
|
||||||
|
|
||||||
|
impl Violation for SliceCopy {
|
||||||
|
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
|
||||||
|
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Prefer `copy` method over slicing")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn autofix_title(&self) -> Option<String> {
|
||||||
|
Some("Replace with `copy()`".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FURB145
|
||||||
|
pub(crate) fn slice_copy(checker: &mut Checker, subscript: &ast::ExprSubscript) {
|
||||||
|
if subscript.ctx.is_store() || subscript.ctx.is_del() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(name) = match_list_full_slice(subscript, checker.semantic()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut diagnostic = Diagnostic::new(SliceCopy, subscript.range());
|
||||||
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
|
let replacement = generate_method_call(name, "copy", checker.generator());
|
||||||
|
diagnostic.set_fix(Fix::suggested(Edit::replacement(
|
||||||
|
replacement,
|
||||||
|
subscript.start(),
|
||||||
|
subscript.end(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
checker.diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches `obj[:]` where `obj` is a list.
|
||||||
|
fn match_list_full_slice<'a>(
|
||||||
|
subscript: &'a ast::ExprSubscript,
|
||||||
|
semantic: &SemanticModel,
|
||||||
|
) -> Option<&'a str> {
|
||||||
|
// Check that it is `obj[:]`.
|
||||||
|
if !matches!(
|
||||||
|
subscript.slice.as_ref(),
|
||||||
|
Expr::Slice(ast::ExprSlice {
|
||||||
|
lower: None,
|
||||||
|
upper: None,
|
||||||
|
step: None,
|
||||||
|
range: _,
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ast::ExprName { id, .. } = subscript.value.as_name_expr()?;
|
||||||
|
|
||||||
|
// Check that `obj` is a list.
|
||||||
|
let scope = semantic.current_scope();
|
||||||
|
let bindings: Vec<&Binding> = scope
|
||||||
|
.get_all(id)
|
||||||
|
.map(|binding_id| semantic.binding(binding_id))
|
||||||
|
.collect();
|
||||||
|
let [binding] = bindings.as_slice() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
if !is_list(binding, semantic) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(id)
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/refurb/mod.rs
|
||||||
|
---
|
||||||
|
FURB145.py:4:5: FURB145 [*] Prefer `copy` method over slicing
|
||||||
|
|
|
||||||
|
3 | # Errors.
|
||||||
|
4 | a = l[:]
|
||||||
|
| ^^^^ FURB145
|
||||||
|
5 | b, c = 1, l[:]
|
||||||
|
6 | d, e = l[:], 1
|
||||||
|
|
|
||||||
|
= help: Replace with `copy()`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
1 1 | l = [1, 2, 3, 4, 5]
|
||||||
|
2 2 |
|
||||||
|
3 3 | # Errors.
|
||||||
|
4 |-a = l[:]
|
||||||
|
4 |+a = l.copy()
|
||||||
|
5 5 | b, c = 1, l[:]
|
||||||
|
6 6 | d, e = l[:], 1
|
||||||
|
7 7 | m = l[::]
|
||||||
|
|
||||||
|
FURB145.py:5:11: FURB145 [*] Prefer `copy` method over slicing
|
||||||
|
|
|
||||||
|
3 | # Errors.
|
||||||
|
4 | a = l[:]
|
||||||
|
5 | b, c = 1, l[:]
|
||||||
|
| ^^^^ FURB145
|
||||||
|
6 | d, e = l[:], 1
|
||||||
|
7 | m = l[::]
|
||||||
|
|
|
||||||
|
= help: Replace with `copy()`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
2 2 |
|
||||||
|
3 3 | # Errors.
|
||||||
|
4 4 | a = l[:]
|
||||||
|
5 |-b, c = 1, l[:]
|
||||||
|
5 |+b, c = 1, l.copy()
|
||||||
|
6 6 | d, e = l[:], 1
|
||||||
|
7 7 | m = l[::]
|
||||||
|
8 8 | l[:]
|
||||||
|
|
||||||
|
FURB145.py:6:8: FURB145 [*] Prefer `copy` method over slicing
|
||||||
|
|
|
||||||
|
4 | a = l[:]
|
||||||
|
5 | b, c = 1, l[:]
|
||||||
|
6 | d, e = l[:], 1
|
||||||
|
| ^^^^ FURB145
|
||||||
|
7 | m = l[::]
|
||||||
|
8 | l[:]
|
||||||
|
|
|
||||||
|
= help: Replace with `copy()`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
3 3 | # Errors.
|
||||||
|
4 4 | a = l[:]
|
||||||
|
5 5 | b, c = 1, l[:]
|
||||||
|
6 |-d, e = l[:], 1
|
||||||
|
6 |+d, e = l.copy(), 1
|
||||||
|
7 7 | m = l[::]
|
||||||
|
8 8 | l[:]
|
||||||
|
9 9 | print(l[:])
|
||||||
|
|
||||||
|
FURB145.py:7:5: FURB145 [*] Prefer `copy` method over slicing
|
||||||
|
|
|
||||||
|
5 | b, c = 1, l[:]
|
||||||
|
6 | d, e = l[:], 1
|
||||||
|
7 | m = l[::]
|
||||||
|
| ^^^^^ FURB145
|
||||||
|
8 | l[:]
|
||||||
|
9 | print(l[:])
|
||||||
|
|
|
||||||
|
= help: Replace with `copy()`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
4 4 | a = l[:]
|
||||||
|
5 5 | b, c = 1, l[:]
|
||||||
|
6 6 | d, e = l[:], 1
|
||||||
|
7 |-m = l[::]
|
||||||
|
7 |+m = l.copy()
|
||||||
|
8 8 | l[:]
|
||||||
|
9 9 | print(l[:])
|
||||||
|
10 10 |
|
||||||
|
|
||||||
|
FURB145.py:8:1: FURB145 [*] Prefer `copy` method over slicing
|
||||||
|
|
|
||||||
|
6 | d, e = l[:], 1
|
||||||
|
7 | m = l[::]
|
||||||
|
8 | l[:]
|
||||||
|
| ^^^^ FURB145
|
||||||
|
9 | print(l[:])
|
||||||
|
|
|
||||||
|
= help: Replace with `copy()`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
5 5 | b, c = 1, l[:]
|
||||||
|
6 6 | d, e = l[:], 1
|
||||||
|
7 7 | m = l[::]
|
||||||
|
8 |-l[:]
|
||||||
|
8 |+l.copy()
|
||||||
|
9 9 | print(l[:])
|
||||||
|
10 10 |
|
||||||
|
11 11 | # False negatives.
|
||||||
|
|
||||||
|
FURB145.py:9:7: FURB145 [*] Prefer `copy` method over slicing
|
||||||
|
|
|
||||||
|
7 | m = l[::]
|
||||||
|
8 | l[:]
|
||||||
|
9 | print(l[:])
|
||||||
|
| ^^^^ FURB145
|
||||||
|
10 |
|
||||||
|
11 | # False negatives.
|
||||||
|
|
|
||||||
|
= help: Replace with `copy()`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
6 6 | d, e = l[:], 1
|
||||||
|
7 7 | m = l[::]
|
||||||
|
8 8 | l[:]
|
||||||
|
9 |-print(l[:])
|
||||||
|
9 |+print(l.copy())
|
||||||
|
10 10 |
|
||||||
|
11 11 | # False negatives.
|
||||||
|
12 12 | aa = a[:] # Type inference.
|
||||||
|
|
||||||
|
|
|
@ -811,6 +811,7 @@ mod tests {
|
||||||
Rule::RepeatedAppend,
|
Rule::RepeatedAppend,
|
||||||
Rule::DeleteFullSlice,
|
Rule::DeleteFullSlice,
|
||||||
Rule::CheckAndRemoveFromSet,
|
Rule::CheckAndRemoveFromSet,
|
||||||
|
Rule::SliceCopy,
|
||||||
Rule::QuadraticListSummation,
|
Rule::QuadraticListSummation,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
2
ruff.schema.json
generated
2
ruff.schema.json
generated
|
@ -2079,6 +2079,8 @@
|
||||||
"FURB13",
|
"FURB13",
|
||||||
"FURB131",
|
"FURB131",
|
||||||
"FURB132",
|
"FURB132",
|
||||||
|
"FURB14",
|
||||||
|
"FURB145",
|
||||||
"G",
|
"G",
|
||||||
"G0",
|
"G0",
|
||||||
"G00",
|
"G00",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue