Remove functor from autofix title (#4245)

This commit is contained in:
Micha Reiser 2023-05-10 09:21:15 +02:00 committed by GitHub
parent 8969ad5879
commit a2b8487ae3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 234 additions and 305 deletions

View file

@ -65,18 +65,12 @@ impl Violation for UnusedLoopControlVariable {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
let UnusedLoopControlVariable { let UnusedLoopControlVariable { rename, name, .. } = self;
certainty, rename, ..
} = self; rename
if certainty.to_bool() && rename.is_some() { .as_ref()
Some(|UnusedLoopControlVariable { name, rename, .. }| { .map(|rename| format!("Rename unused `{name}` to `{rename}`"))
let rename = rename.as_ref().unwrap();
format!("Rename unused `{name}` to `{rename}`")
})
} else {
None
}
} }
} }

View file

@ -65,6 +65,7 @@ B007.py:34:10: B007 Loop control variable `bar` may not be used within loop body
36 | if foo: 36 | if foo:
37 | print(FMT.format(**locals())) 37 | print(FMT.format(**locals()))
| |
= help: Rename unused `bar` to `_bar`
B007.py:38:10: B007 Loop control variable `bar` may not be used within loop body B007.py:38:10: B007 Loop control variable `bar` may not be used within loop body
| |
@ -75,6 +76,7 @@ B007.py:38:10: B007 Loop control variable `bar` may not be used within loop body
41 | if foo: 41 | if foo:
42 | print(FMT.format(**globals())) 42 | print(FMT.format(**globals()))
| |
= help: Rename unused `bar` to `_bar`
B007.py:42:10: B007 Loop control variable `bar` may not be used within loop body B007.py:42:10: B007 Loop control variable `bar` may not be used within loop body
| |
@ -85,6 +87,7 @@ B007.py:42:10: B007 Loop control variable `bar` may not be used within loop body
45 | if foo: 45 | if foo:
46 | print(FMT.format(**vars())) 46 | print(FMT.format(**vars()))
| |
= help: Rename unused `bar` to `_bar`
B007.py:46:10: B007 Loop control variable `bar` may not be used within loop body B007.py:46:10: B007 Loop control variable `bar` may not be used within loop body
| |
@ -94,6 +97,7 @@ B007.py:46:10: B007 Loop control variable `bar` may not be used within loop body
| ^^^ B007 | ^^^ B007
49 | print(FMT.format(foo=foo, bar=eval("bar"))) 49 | print(FMT.format(foo=foo, bar=eval("bar")))
| |
= help: Rename unused `bar` to `_bar`
B007.py:52:14: B007 [*] Loop control variable `bar` not used within loop body B007.py:52:14: B007 [*] Loop control variable `bar` not used within loop body
| |

View file

@ -50,8 +50,8 @@ impl Violation for UnnecessaryComprehensionAnyAll {
format!("Unnecessary list comprehension.") format!("Unnecessary list comprehension.")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|_| "Remove unnecessary list comprehension".to_string()) Some("Remove unnecessary list comprehension".to_string())
} }
} }

View file

@ -56,13 +56,12 @@ impl Violation for UnnecessaryMap {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|UnnecessaryMap { obj_type }| { let UnnecessaryMap { obj_type } = self;
if obj_type == "generator" { Some(if obj_type == "generator" {
format!("Replace `map` using a generator expression") format!("Replace `map` using a generator expression")
} else { } else {
format!("Replace `map` using a `{obj_type}` comprehension") format!("Replace `map` using a `{obj_type}` comprehension")
}
}) })
} }
} }

View file

@ -47,9 +47,7 @@ use crate::registry::{AsRule, Rule};
/// RuntimeError: 'Some value' is incorrect /// RuntimeError: 'Some value' is incorrect
/// ``` /// ```
#[violation] #[violation]
pub struct RawStringInException { pub struct RawStringInException;
fixable: bool,
}
impl Violation for RawStringInException { impl Violation for RawStringInException {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -59,9 +57,8 @@ impl Violation for RawStringInException {
format!("Exception must not use a string literal, assign to variable first") format!("Exception must not use a string literal, assign to variable first")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Assign to variable; remove string literal".to_string())
.then_some(|_| format!("Assign to variable; remove string literal"))
} }
} }
@ -104,9 +101,7 @@ impl Violation for RawStringInException {
/// RuntimeError: 'Some value' is incorrect /// RuntimeError: 'Some value' is incorrect
/// ``` /// ```
#[violation] #[violation]
pub struct FStringInException { pub struct FStringInException;
fixable: bool,
}
impl Violation for FStringInException { impl Violation for FStringInException {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -116,9 +111,8 @@ impl Violation for FStringInException {
format!("Exception must not use an f-string literal, assign to variable first") format!("Exception must not use an f-string literal, assign to variable first")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Assign to variable; remove f-string literal".to_string())
.then_some(|_| format!("Assign to variable; remove f-string literal"))
} }
} }
@ -163,9 +157,7 @@ impl Violation for FStringInException {
/// RuntimeError: 'Some value' is incorrect /// RuntimeError: 'Some value' is incorrect
/// ``` /// ```
#[violation] #[violation]
pub struct DotFormatInException { pub struct DotFormatInException;
fixable: bool,
}
impl Violation for DotFormatInException { impl Violation for DotFormatInException {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -175,9 +167,8 @@ impl Violation for DotFormatInException {
format!("Exception must not use a `.format()` string directly, assign to variable first") format!("Exception must not use a `.format()` string directly, assign to variable first")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Assign to variable; remove `.format()` string".to_string())
.then_some(|_| format!("Assign to variable; remove `.format()` string"))
} }
} }
@ -241,12 +232,8 @@ pub fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr) {
None None
} }
}); });
let mut diagnostic = Diagnostic::new( let mut diagnostic =
RawStringInException { Diagnostic::new(RawStringInException, first.range());
fixable: indentation.is_some(),
},
first.range(),
);
if let Some(indentation) = indentation { if let Some(indentation) = indentation {
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(generate_fix( diagnostic.set_fix(generate_fix(
@ -273,12 +260,7 @@ pub fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr) {
} }
}, },
); );
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(FStringInException, first.range());
FStringInException {
fixable: indentation.is_some(),
},
first.range(),
);
if let Some(indentation) = indentation { if let Some(indentation) = indentation {
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(generate_fix( diagnostic.set_fix(generate_fix(
@ -305,12 +287,8 @@ pub fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr) {
None None
} }
}); });
let mut diagnostic = Diagnostic::new( let mut diagnostic =
DotFormatInException { Diagnostic::new(DotFormatInException, first.range());
fixable: indentation.is_some(),
},
first.range(),
);
if let Some(indentation) = indentation { if let Some(indentation) = indentation {
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(generate_fix( diagnostic.set_fix(generate_fix(

View file

@ -66,6 +66,7 @@ EM.py:28:24: EM101 Exception must not use a string literal, assign to variable f
30 | raise RuntimeError("This is an example exception") 30 | raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
| |
= help: Assign to variable; remove string literal
EM.py:35:24: EM101 [*] Exception must not use a string literal, assign to variable first EM.py:35:24: EM101 [*] Exception must not use a string literal, assign to variable first
| |
@ -93,6 +94,7 @@ EM.py:42:28: EM101 Exception must not use a string literal, assign to variable f
43 | raise RuntimeError("This is an example exception") 43 | raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
| |
= help: Assign to variable; remove string literal
EM.py:47:28: EM101 [*] Exception must not use a string literal, assign to variable first EM.py:47:28: EM101 [*] Exception must not use a string literal, assign to variable first
| |
@ -164,6 +166,7 @@ EM.py:55:28: EM101 Exception must not use a string literal, assign to variable f
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
57 | if foo: x = 1; raise RuntimeError("This is an example exception") 57 | if foo: x = 1; raise RuntimeError("This is an example exception")
| |
= help: Assign to variable; remove string literal
EM.py:56:35: EM101 Exception must not use a string literal, assign to variable first EM.py:56:35: EM101 Exception must not use a string literal, assign to variable first
| |
@ -172,5 +175,6 @@ EM.py:56:35: EM101 Exception must not use a string literal, assign to variable f
58 | if foo: x = 1; raise RuntimeError("This is an example exception") 58 | if foo: x = 1; raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
| |
= help: Assign to variable; remove string literal

View file

@ -85,6 +85,7 @@ EM.py:28:24: EM101 Exception must not use a string literal, assign to variable f
30 | raise RuntimeError("This is an example exception") 30 | raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
| |
= help: Assign to variable; remove string literal
EM.py:35:24: EM101 [*] Exception must not use a string literal, assign to variable first EM.py:35:24: EM101 [*] Exception must not use a string literal, assign to variable first
| |
@ -112,6 +113,7 @@ EM.py:42:28: EM101 Exception must not use a string literal, assign to variable f
43 | raise RuntimeError("This is an example exception") 43 | raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
| |
= help: Assign to variable; remove string literal
EM.py:47:28: EM101 [*] Exception must not use a string literal, assign to variable first EM.py:47:28: EM101 [*] Exception must not use a string literal, assign to variable first
| |
@ -183,6 +185,7 @@ EM.py:55:28: EM101 Exception must not use a string literal, assign to variable f
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
57 | if foo: x = 1; raise RuntimeError("This is an example exception") 57 | if foo: x = 1; raise RuntimeError("This is an example exception")
| |
= help: Assign to variable; remove string literal
EM.py:56:35: EM101 Exception must not use a string literal, assign to variable first EM.py:56:35: EM101 Exception must not use a string literal, assign to variable first
| |
@ -191,5 +194,6 @@ EM.py:56:35: EM101 Exception must not use a string literal, assign to variable f
58 | if foo: x = 1; raise RuntimeError("This is an example exception") 58 | if foo: x = 1; raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
| |
= help: Assign to variable; remove string literal

View file

@ -51,9 +51,7 @@ use super::unittest_assert::UnittestAssert;
/// assert not something_else /// assert not something_else
/// ``` /// ```
#[violation] #[violation]
pub struct PytestCompositeAssertion { pub struct PytestCompositeAssertion;
fixable: bool,
}
impl Violation for PytestCompositeAssertion { impl Violation for PytestCompositeAssertion {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -63,9 +61,8 @@ impl Violation for PytestCompositeAssertion {
format!("Assertion should be broken down into multiple parts") format!("Assertion should be broken down into multiple parts")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Break down assertion into multiple parts".to_string())
.then_some(|_| format!("Break down assertion into multiple parts"))
} }
} }
@ -97,7 +94,6 @@ impl Violation for PytestAssertAlwaysFalse {
#[violation] #[violation]
pub struct PytestUnittestAssertion { pub struct PytestUnittestAssertion {
assertion: String, assertion: String,
fixable: bool,
} }
impl Violation for PytestUnittestAssertion { impl Violation for PytestUnittestAssertion {
@ -105,15 +101,13 @@ impl Violation for PytestUnittestAssertion {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let PytestUnittestAssertion { assertion, .. } = self; let PytestUnittestAssertion { assertion } = self;
format!("Use a regular `assert` instead of unittest-style `{assertion}`") format!("Use a regular `assert` instead of unittest-style `{assertion}`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let PytestUnittestAssertion { assertion } = self;
.then_some(|PytestUnittestAssertion { assertion, .. }| { Some(format!("Replace `{assertion}(...)` with `assert ...`"))
format!("Replace `{assertion}(...)` with `assert ...`")
})
} }
} }
@ -198,7 +192,6 @@ pub fn unittest_assertion(
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
PytestUnittestAssertion { PytestUnittestAssertion {
assertion: unittest_assert.to_string(), assertion: unittest_assert.to_string(),
fixable,
}, },
func.range(), func.range(),
); );
@ -426,7 +419,7 @@ pub fn composite_condition(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg:
let fixable = matches!(composite, CompositionKind::Simple) let fixable = matches!(composite, CompositionKind::Simple)
&& msg.is_none() && msg.is_none()
&& !has_comments_in(stmt.range(), checker.locator); && !has_comments_in(stmt.range(), checker.locator);
let mut diagnostic = Diagnostic::new(PytestCompositeAssertion { fixable }, stmt.range()); let mut diagnostic = Diagnostic::new(PytestCompositeAssertion, stmt.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]
diagnostic.try_set_fix_from_edit(|| { diagnostic.try_set_fix_from_edit(|| {

View file

@ -26,11 +26,9 @@ impl Violation for PytestParametrizeNamesWrongType {
format!("Wrong name(s) type in `@pytest.mark.parametrize`, expected `{expected}`") format!("Wrong name(s) type in `@pytest.mark.parametrize`, expected `{expected}`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|violation| { let PytestParametrizeNamesWrongType { expected } = self;
let PytestParametrizeNamesWrongType { expected } = violation; Some(format!("Use a `{expected}` for parameter names"))
format!("Use a `{expected}` for parameter names")
})
} }
} }

View file

@ -159,6 +159,7 @@ PT009.py:21:13: PT009 Use a regular `assert` instead of unittest-style `assertIs
24 | if expect_condition 24 | if expect_condition
25 | else self.assertIsNone(value) # Error, unfixable 25 | else self.assertIsNone(value) # Error, unfixable
| |
= help: Replace `assertIsNotNone(...)` with `assert ...`
PT009.py:23:18: PT009 Use a regular `assert` instead of unittest-style `assertIsNone` PT009.py:23:18: PT009 Use a regular `assert` instead of unittest-style `assertIsNone`
| |
@ -169,6 +170,7 @@ PT009.py:23:18: PT009 Use a regular `assert` instead of unittest-style `assertIs
26 | ) 26 | )
27 | return self.assertEqual(True, False) # Error, unfixable 27 | return self.assertEqual(True, False) # Error, unfixable
| |
= help: Replace `assertIsNone(...)` with `assert ...`
PT009.py:25:16: PT009 Use a regular `assert` instead of unittest-style `assertEqual` PT009.py:25:16: PT009 Use a regular `assert` instead of unittest-style `assertEqual`
| |
@ -179,6 +181,7 @@ PT009.py:25:16: PT009 Use a regular `assert` instead of unittest-style `assertEq
28 | 28 |
29 | def test_assert_false(self): 29 | def test_assert_false(self):
| |
= help: Replace `assertEqual(...)` with `assert ...`
PT009.py:28:9: PT009 [*] Use a regular `assert` instead of unittest-style `assertFalse` PT009.py:28:9: PT009 [*] Use a regular `assert` instead of unittest-style `assertFalse`
| |

View file

@ -229,6 +229,7 @@ PT018.py:30:5: PT018 Assertion should be broken down into multiple parts
32 | assert not (something or something_else and something_third), "with message" 32 | assert not (something or something_else and something_third), "with message"
33 | # detected, but no autofix for mixed conditions (e.g. `a or b and c`) 33 | # detected, but no autofix for mixed conditions (e.g. `a or b and c`)
| |
= help: Break down assertion into multiple parts
PT018.py:31:5: PT018 Assertion should be broken down into multiple parts PT018.py:31:5: PT018 Assertion should be broken down into multiple parts
| |
@ -239,6 +240,7 @@ PT018.py:31:5: PT018 Assertion should be broken down into multiple parts
34 | # detected, but no autofix for mixed conditions (e.g. `a or b and c`) 34 | # detected, but no autofix for mixed conditions (e.g. `a or b and c`)
35 | assert not (something or something_else and something_third) 35 | assert not (something or something_else and something_third)
| |
= help: Break down assertion into multiple parts
PT018.py:33:5: PT018 Assertion should be broken down into multiple parts PT018.py:33:5: PT018 Assertion should be broken down into multiple parts
| |
@ -249,6 +251,7 @@ PT018.py:33:5: PT018 Assertion should be broken down into multiple parts
36 | # detected, but no autofix for parenthesized conditions 36 | # detected, but no autofix for parenthesized conditions
37 | assert ( 37 | assert (
| |
= help: Break down assertion into multiple parts
PT018.py:35:5: PT018 Assertion should be broken down into multiple parts PT018.py:35:5: PT018 Assertion should be broken down into multiple parts
| |

View file

@ -47,8 +47,7 @@ use crate::registry::AsRule;
/// - [Python: "isinstance"](https://docs.python.org/3/library/functions.html#isinstance) /// - [Python: "isinstance"](https://docs.python.org/3/library/functions.html#isinstance)
#[violation] #[violation]
pub struct DuplicateIsinstanceCall { pub struct DuplicateIsinstanceCall {
pub name: Option<String>, name: Option<String>,
fixable: bool,
} }
impl Violation for DuplicateIsinstanceCall { impl Violation for DuplicateIsinstanceCall {
@ -56,7 +55,7 @@ impl Violation for DuplicateIsinstanceCall {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let DuplicateIsinstanceCall { name, .. } = self; let DuplicateIsinstanceCall { name } = self;
if let Some(name) = name { if let Some(name) = name {
format!("Multiple `isinstance` calls for `{name}`, merge into a single call") format!("Multiple `isinstance` calls for `{name}`, merge into a single call")
} else { } else {
@ -64,15 +63,14 @@ impl Violation for DuplicateIsinstanceCall {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let DuplicateIsinstanceCall { name } = self;
.then_some(|DuplicateIsinstanceCall { name, .. }| {
if let Some(name) = name { Some(if let Some(name) = name {
format!("Merge `isinstance` calls for `{name}`") format!("Merge `isinstance` calls for `{name}`")
} else { } else {
format!("Merge `isinstance` calls") "Merge `isinstance` calls".to_string()
} })
})
} }
} }
@ -305,7 +303,6 @@ pub fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
} else { } else {
None None
}, },
fixable,
}, },
expr.range(), expr.range(),
); );

View file

@ -22,11 +22,9 @@ impl Violation for UncapitalizedEnvironmentVariables {
format!("Use capitalized environment variable `{expected}` instead of `{original}`") format!("Use capitalized environment variable `{expected}` instead of `{original}`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|violation| { let UncapitalizedEnvironmentVariables { expected, original } = self;
let UncapitalizedEnvironmentVariables { expected, original } = violation; Some(format!("Replace `{original}` with `{expected}`"))
format!("Replace `{original}` with `{expected}`")
})
} }
} }

View file

@ -37,9 +37,7 @@ fn compare_body(body1: &[Stmt], body2: &[Stmt]) -> bool {
} }
#[violation] #[violation]
pub struct CollapsibleIf { pub struct CollapsibleIf;
fixable: bool,
}
impl Violation for CollapsibleIf { impl Violation for CollapsibleIf {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -49,16 +47,14 @@ impl Violation for CollapsibleIf {
format!("Use a single `if` statement instead of nested `if` statements") format!("Use a single `if` statement instead of nested `if` statements")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Combine `if` statements using `and`".to_string())
.then_some(|_| format!("Combine `if` statements using `and`"))
} }
} }
#[violation] #[violation]
pub struct NeedlessBool { pub struct NeedlessBool {
condition: String, condition: String,
fixable: bool,
} }
impl Violation for NeedlessBool { impl Violation for NeedlessBool {
@ -66,14 +62,13 @@ impl Violation for NeedlessBool {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let NeedlessBool { condition, .. } = self; let NeedlessBool { condition } = self;
format!("Return the condition `{condition}` directly") format!("Return the condition `{condition}` directly")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable.then_some(|NeedlessBool { condition, .. }| { let NeedlessBool { condition } = self;
format!("Replace with `return {condition}`") Some(format!("Replace with `return {condition}`"))
})
} }
} }
@ -110,7 +105,6 @@ impl Violation for IfElseBlockInsteadOfDictLookup {
#[violation] #[violation]
pub struct IfElseBlockInsteadOfIfExp { pub struct IfElseBlockInsteadOfIfExp {
contents: String, contents: String,
fixable: bool,
} }
impl Violation for IfElseBlockInsteadOfIfExp { impl Violation for IfElseBlockInsteadOfIfExp {
@ -118,15 +112,13 @@ impl Violation for IfElseBlockInsteadOfIfExp {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let IfElseBlockInsteadOfIfExp { contents, .. } = self; let IfElseBlockInsteadOfIfExp { contents } = self;
format!("Use ternary operator `{contents}` instead of `if`-`else`-block") format!("Use ternary operator `{contents}` instead of `if`-`else`-block")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let IfElseBlockInsteadOfIfExp { contents } = self;
.then_some(|IfElseBlockInsteadOfIfExp { contents, .. }| { Some(format!("Replace `if`-`else`-block with `{contents}`"))
format!("Replace `if`-`else`-block with `{contents}`")
})
} }
} }
@ -163,7 +155,6 @@ impl Violation for IfWithSameArms {
#[violation] #[violation]
pub struct IfElseBlockInsteadOfDictGet { pub struct IfElseBlockInsteadOfDictGet {
contents: String, contents: String,
fixable: bool,
} }
impl Violation for IfElseBlockInsteadOfDictGet { impl Violation for IfElseBlockInsteadOfDictGet {
@ -171,15 +162,13 @@ impl Violation for IfElseBlockInsteadOfDictGet {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let IfElseBlockInsteadOfDictGet { contents, .. } = self; let IfElseBlockInsteadOfDictGet { contents } = self;
format!("Use `{contents}` instead of an `if` block") format!("Use `{contents}` instead of an `if` block")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let IfElseBlockInsteadOfDictGet { contents } = self;
.then_some(|IfElseBlockInsteadOfDictGet { contents, .. }| { Some(format!("Replace with `{contents}`"))
format!("Replace with `{contents}`")
})
} }
} }
@ -289,7 +278,7 @@ pub fn nested_if_statements(
); );
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
CollapsibleIf { fixable }, CollapsibleIf,
colon.map_or_else( colon.map_or_else(
|| stmt.range(), || stmt.range(),
|colon| TextRange::new(stmt.start(), colon.end()), |colon| TextRange::new(stmt.start(), colon.end()),
@ -367,7 +356,7 @@ pub fn needless_bool(checker: &mut Checker, stmt: &Stmt) {
&& !has_comments(stmt, checker.locator) && !has_comments(stmt, checker.locator)
&& (matches!(test.node, ExprKind::Compare { .. }) || checker.ctx.is_builtin("bool")); && (matches!(test.node, ExprKind::Compare { .. }) || checker.ctx.is_builtin("bool"));
let mut diagnostic = Diagnostic::new(NeedlessBool { condition, fixable }, stmt.range()); let mut diagnostic = Diagnostic::new(NeedlessBool { condition }, stmt.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
if matches!(test.node, ExprKind::Compare { .. }) { if matches!(test.node, ExprKind::Compare { .. }) {
// If the condition is a comparison, we can replace it with the condition. // If the condition is a comparison, we can replace it with the condition.
@ -526,7 +515,6 @@ pub fn use_ternary_operator(checker: &mut Checker, stmt: &Stmt, parent: Option<&
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
IfElseBlockInsteadOfIfExp { IfElseBlockInsteadOfIfExp {
contents: contents.clone(), contents: contents.clone(),
fixable,
}, },
stmt.range(), stmt.range(),
); );
@ -877,7 +865,6 @@ pub fn use_dict_get_with_default(
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
IfElseBlockInsteadOfDictGet { IfElseBlockInsteadOfDictGet {
contents: contents.clone(), contents: contents.clone(),
fixable,
}, },
stmt.range(), stmt.range(),
); );

View file

@ -21,11 +21,9 @@ impl Violation for IfExprWithTrueFalse {
format!("Use `bool({expr})` instead of `True if {expr} else False`") format!("Use `bool({expr})` instead of `True if {expr} else False`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|violation| { let IfExprWithTrueFalse { expr } = self;
let IfExprWithTrueFalse { expr } = violation; Some(format!("Replace with `not {expr}"))
format!("Replace with `not {expr}")
})
} }
} }

View file

@ -42,9 +42,7 @@ use super::fix_with;
/// ## References /// ## References
/// - [Python: "The with statement"](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) /// - [Python: "The with statement"](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)
#[violation] #[violation]
pub struct MultipleWithStatements { pub struct MultipleWithStatements;
fixable: bool,
}
impl Violation for MultipleWithStatements { impl Violation for MultipleWithStatements {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -57,9 +55,8 @@ impl Violation for MultipleWithStatements {
) )
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Combine `with` statements".to_string())
.then_some(|_| format!("Combine `with` statements"))
} }
} }
@ -99,7 +96,7 @@ pub fn multiple_with_statements(
checker.locator, checker.locator,
); );
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
MultipleWithStatements { fixable }, MultipleWithStatements,
colon.map_or_else( colon.map_or_else(
|| with_stmt.range(), || with_stmt.range(),
|colon| TextRange::new(with_stmt.start(), colon.end()), |colon| TextRange::new(with_stmt.start(), colon.end()),

View file

@ -26,11 +26,9 @@ impl Violation for ReimplementedBuiltin {
format!("Use `{repl}` instead of `for` loop") format!("Use `{repl}` instead of `for` loop")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|violation| { let ReimplementedBuiltin { repl } = self;
let ReimplementedBuiltin { repl } = violation; Some(format!("Replace with `{repl}`"))
format!("Replace with `{repl}`")
})
} }
} }

View file

@ -28,11 +28,9 @@ impl Violation for SuppressibleException {
format!("Use `contextlib.suppress({exception})` instead of `try`-`except`-`pass`") format!("Use `contextlib.suppress({exception})` instead of `try`-`except`-`pass`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let SuppressibleException { exception, .. } = self;
.then_some(|SuppressibleException { exception, .. }| { Some(format!("Replace with `contextlib.suppress({exception})`"))
format!("Replace with `contextlib.suppress({exception})`")
})
} }
} }

View file

@ -29,16 +29,11 @@ impl Violation for YodaConditions {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
let YodaConditions { suggestion } = self; let YodaConditions { suggestion } = self;
if suggestion.is_some() { suggestion
Some(|YodaConditions { suggestion }| { .as_ref()
let suggestion = suggestion.as_ref().unwrap(); .map(|suggestion| format!("Replace Yoda condition with `{suggestion}`"))
format!("Replace Yoda condition with `{suggestion}`")
})
} else {
None
}
} }
} }

View file

@ -144,5 +144,6 @@ SIM101.py:22:4: SIM101 Multiple `isinstance` calls for expression, merge into a
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM101 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM101
25 | pass 25 | pass
| |
= help: Merge `isinstance` calls

View file

@ -81,6 +81,7 @@ SIM102.py:20:1: SIM102 Use a single `if` statement instead of nested `if` statem
| |_________^ SIM102 | |_________^ SIM102
24 | c 24 | c
| |
= help: Combine `if` statements using `and`
SIM102.py:26:1: SIM102 [*] Use a single `if` statement instead of nested `if` statements SIM102.py:26:1: SIM102 [*] Use a single `if` statement instead of nested `if` statements
| |

View file

@ -115,6 +115,7 @@ SIM103.py:57:5: SIM103 Return the condition `a` directly
62 | | return True 62 | | return True
| |___________________^ SIM103 | |___________________^ SIM103
| |
= help: Replace with `return a`
SIM103.py:83:5: SIM103 Return the condition `a` directly SIM103.py:83:5: SIM103 Return the condition `a` directly
| |
@ -127,5 +128,6 @@ SIM103.py:83:5: SIM103 Return the condition `a` directly
88 | | return False 88 | | return False
| |____________________^ SIM103 | |____________________^ SIM103
| |
= help: Replace with `return a`

View file

@ -175,5 +175,6 @@ SIM105_0.py:93:5: SIM105 Use `contextlib.suppress(ValueError, OSError)` instead
97 | | pass # Trailing comment. 97 | | pass # Trailing comment.
| |____________^ SIM105 | |____________^ SIM105
| |
= help: Replace with `contextlib.suppress(ValueError, OSError)`

View file

@ -36,6 +36,7 @@ SIM108.py:58:1: SIM108 Use ternary operator `abc = x if x > 0 else -x` instead o
64 | | abc = -x 64 | | abc = -x
| |____________^ SIM108 | |____________^ SIM108
| |
= help: Replace `if`-`else`-block with `abc = x if x > 0 else -x`
SIM108.py:82:1: SIM108 [*] Use ternary operator `b = cccccccccccccccccccccccccccccccccccc if a else ddddddddddddddddddddddddddddddddddddd` instead of `if`-`else`-block SIM108.py:82:1: SIM108 [*] Use ternary operator `b = cccccccccccccccccccccccccccccccccccc if a else ddddddddddddddddddddddddddddddddddddd` instead of `if`-`else`-block
| |
@ -70,6 +71,7 @@ SIM108.py:97:1: SIM108 Use ternary operator `exitcode = 0 if True else 1` instea
101 | | exitcode = 1 # Trailing comment 101 | | exitcode = 1 # Trailing comment
| |________________^ SIM108 | |________________^ SIM108
| |
= help: Replace `if`-`else`-block with `exitcode = 0 if True else 1`
SIM108.py:104:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block SIM108.py:104:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block
| |
@ -78,6 +80,7 @@ SIM108.py:104:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `
106 | | else: x = 5 106 | | else: x = 5
| |___________^ SIM108 | |___________^ SIM108
| |
= help: Replace `if`-`else`-block with `x = 3 if True else 5`
SIM108.py:109:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block SIM108.py:109:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `if`-`else`-block
| |
@ -88,5 +91,6 @@ SIM108.py:109:1: SIM108 Use ternary operator `x = 3 if True else 5` instead of `
113 | | x = 5 113 | | x = 5
| |_________^ SIM108 | |_________^ SIM108
| |
= help: Replace `if`-`else`-block with `x = 3 if True else 5`

View file

@ -57,6 +57,7 @@ SIM117.py:13:1: SIM117 Use a single `with` statement with multiple contexts inst
| |__________________^ SIM117 | |__________________^ SIM117
17 | print("hello") 17 | print("hello")
| |
= help: Combine `with` statements
SIM117.py:19:1: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements SIM117.py:19:1: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
| |

View file

@ -73,12 +73,13 @@ impl Violation for RelativeImports {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|RelativeImports { strictness }| match strictness { let RelativeImports { strictness } = self;
Some(match strictness {
Strictness::Parents => { Strictness::Parents => {
format!("Replace relative imports from parent modules with absolute imports") "Replace relative imports from parent modules with absolute imports".to_string()
} }
Strictness::All => format!("Replace relative imports with absolute imports"), Strictness::All => "Replace relative imports with absolute imports".to_string(),
}) })
} }
} }

View file

@ -48,8 +48,8 @@ impl Violation for UnsortedImports {
format!("Import block is un-sorted or un-formatted") format!("Import block is un-sorted or un-formatted")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|_| "Organize imports".to_string()) Some("Organize imports".to_string())
} }
} }

View file

@ -32,9 +32,7 @@ use crate::rules::pandas_vet::fixes::convert_inplace_argument_to_assignment;
/// ## References /// ## References
/// - [_Why You Should Probably Never Use pandas inplace=True_](https://towardsdatascience.com/why-you-should-probably-never-use-pandas-inplace-true-9f9f211849e4) /// - [_Why You Should Probably Never Use pandas inplace=True_](https://towardsdatascience.com/why-you-should-probably-never-use-pandas-inplace-true-9f9f211849e4)
#[violation] #[violation]
pub struct PandasUseOfInplaceArgument { pub struct PandasUseOfInplaceArgument;
fixable: bool,
}
impl Violation for PandasUseOfInplaceArgument { impl Violation for PandasUseOfInplaceArgument {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -44,9 +42,8 @@ impl Violation for PandasUseOfInplaceArgument {
format!("`inplace=True` should be avoided; it has inconsistent behavior") format!("`inplace=True` should be avoided; it has inconsistent behavior")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Assign to variable; remove `inplace` arg".to_string())
.then_some(|_| format!("Assign to variable; remove `inplace` arg"))
} }
} }
@ -85,8 +82,7 @@ pub fn inplace_argument(
&& matches!(checker.ctx.stmt().node, StmtKind::Expr { .. }) && matches!(checker.ctx.stmt().node, StmtKind::Expr { .. })
&& checker.ctx.expr_parent().is_none() && checker.ctx.expr_parent().is_none()
&& !checker.ctx.scope().kind.is_lambda(); && !checker.ctx.scope().kind.is_lambda();
let mut diagnostic = let mut diagnostic = Diagnostic::new(PandasUseOfInplaceArgument, keyword.range());
Diagnostic::new(PandasUseOfInplaceArgument { fixable }, keyword.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
if let Some(fix) = convert_inplace_argument_to_assignment( if let Some(fix) = convert_inplace_argument_to_assignment(
checker.locator, checker.locator,

View file

@ -114,6 +114,7 @@ PD002.py:23:23: PD002 `inplace=True` should be avoided; it has inconsistent beha
| ^^^^^^^^^^^^ PD002 | ^^^^^^^^^^^^ PD002
25 | f(x.drop(["a"], axis=1, inplace=True)) 25 | f(x.drop(["a"], axis=1, inplace=True))
| |
= help: Assign to variable; remove `inplace` arg
PD002.py:24:25: PD002 `inplace=True` should be avoided; it has inconsistent behavior PD002.py:24:25: PD002 `inplace=True` should be avoided; it has inconsistent behavior
| |
@ -124,6 +125,7 @@ PD002.py:24:25: PD002 `inplace=True` should be avoided; it has inconsistent beha
27 | 27 |
28 | x.apply(lambda x: x.sort_values('a', inplace=True)) 28 | x.apply(lambda x: x.sort_values('a', inplace=True))
| |
= help: Assign to variable; remove `inplace` arg
PD002.py:26:38: PD002 `inplace=True` should be avoided; it has inconsistent behavior PD002.py:26:38: PD002 `inplace=True` should be avoided; it has inconsistent behavior
| |
@ -132,5 +134,6 @@ PD002.py:26:38: PD002 `inplace=True` should be avoided; it has inconsistent beha
28 | x.apply(lambda x: x.sort_values('a', inplace=True)) 28 | x.apply(lambda x: x.sort_values('a', inplace=True))
| ^^^^^^^^^^^^ PD002 | ^^^^^^^^^^^^ PD002
| |
= help: Assign to variable; remove `inplace` arg

View file

@ -39,7 +39,6 @@ use crate::registry::AsRule;
#[violation] #[violation]
pub struct LambdaAssignment { pub struct LambdaAssignment {
name: String, name: String,
fixable: bool,
} }
impl Violation for LambdaAssignment { impl Violation for LambdaAssignment {
@ -50,9 +49,9 @@ impl Violation for LambdaAssignment {
format!("Do not assign a `lambda` expression, use a `def`") format!("Do not assign a `lambda` expression, use a `def`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let LambdaAssignment { name } = self;
.then_some(|LambdaAssignment { name, .. }| format!("Rewrite `{name}` as a `def`")) Some(format!("Rewrite `{name}` as a `def`"))
} }
} }
@ -77,7 +76,6 @@ pub fn lambda_assignment(
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
LambdaAssignment { LambdaAssignment {
name: id.to_string(), name: id.to_string(),
fixable,
}, },
stmt.range(), stmt.range(),
); );

View file

@ -115,6 +115,7 @@ E731.py:14:5: E731 Do not assign a `lambda` expression, use a `def`
16 | f = lambda x: 2 * x 16 | f = lambda x: 2 * x
| ^^^^^^^^^^^^^^^^^^^ E731 | ^^^^^^^^^^^^^^^^^^^ E731
| |
= help: Rewrite `f` as a `def`
E731.py:32:1: E731 [*] Do not assign a `lambda` expression, use a `def` E731.py:32:1: E731 [*] Do not assign a `lambda` expression, use a `def`
| |

View file

@ -11,9 +11,6 @@ pub struct BlankLineAfterSummary {
num_lines: usize, num_lines: usize,
} }
fn fmt_blank_line_after_summary_autofix_msg(_: &BlankLineAfterSummary) -> String {
"Insert single blank line".to_string()
}
impl Violation for BlankLineAfterSummary { impl Violation for BlankLineAfterSummary {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -29,12 +26,8 @@ impl Violation for BlankLineAfterSummary {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
let BlankLineAfterSummary { num_lines } = self; Some("Insert single blank line".to_string())
if *num_lines > 0 {
return Some(fmt_blank_line_after_summary_autofix_msg);
}
None
} }
} }

View file

@ -19,8 +19,8 @@ impl Violation for SurroundingWhitespace {
format!("No whitespaces allowed surrounding docstring text") format!("No whitespaces allowed surrounding docstring text")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|_| "Trim surrounding whitespace".to_string()) Some("Trim surrounding whitespace".to_string())
} }
} }

View file

@ -18,8 +18,8 @@ impl Violation for FitsOnOneLine {
format!("One-line docstring should fit on one line") format!("One-line docstring should fit on one line")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|_| "Reformat to one line".to_string()) Some("Reformat to one line".to_string())
} }
} }

View file

@ -12,6 +12,7 @@ D.py:200:5: D205 1 blank line required between summary line and description
205 | | """ 205 | | """
| |_______^ D205 | |_______^ D205
| |
= help: Insert single blank line
D.py:210:5: D205 [*] 1 blank line required between summary line and description (found 2) D.py:210:5: D205 [*] 1 blank line required between summary line and description (found 2)
| |

View file

@ -44,17 +44,14 @@ impl Violation for UnusedImport {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
let UnusedImport { context, .. } = self; let UnusedImport { name, multiple, .. } = self;
context
.is_none() Some(if *multiple {
.then_some(|UnusedImport { name, multiple, .. }| { "Remove unused import".to_string()
if *multiple { } else {
"Remove unused import".to_string() format!("Remove unused import: `{name}`")
} else { })
format!("Remove unused import: `{name}`")
}
})
} }
} }
#[violation] #[violation]

View file

@ -26,12 +26,13 @@ impl Violation for MultiValueRepeatedKeyLiteral {
format!("Dictionary key literal `{name}` repeated") format!("Dictionary key literal `{name}` repeated")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
let MultiValueRepeatedKeyLiteral { repeated_value, .. } = self; let MultiValueRepeatedKeyLiteral {
repeated_value,
name,
} = self;
if *repeated_value { if *repeated_value {
Some(|MultiValueRepeatedKeyLiteral { name, .. }| { Some(format!("Remove repeated key literal `{name}`"))
format!("Remove repeated key literal `{name}`")
})
} else { } else {
None None
} }
@ -52,12 +53,13 @@ impl Violation for MultiValueRepeatedKeyVariable {
format!("Dictionary key `{name}` repeated") format!("Dictionary key `{name}` repeated")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
let MultiValueRepeatedKeyVariable { repeated_value, .. } = self; let MultiValueRepeatedKeyVariable {
repeated_value,
name,
} = self;
if *repeated_value { if *repeated_value {
Some(|MultiValueRepeatedKeyVariable { name, .. }| { Some(format!("Remove repeated key `{name}`"))
format!("Remove repeated key `{name}`")
})
} else { } else {
None None
} }

View file

@ -57,11 +57,9 @@ impl Violation for UnusedVariable {
format!("Local variable `{name}` is assigned to but never used") format!("Local variable `{name}` is assigned to but never used")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|violation| { let UnusedVariable { name } = self;
let UnusedVariable { name } = violation; Some(format!("Remove assignment to unused variable `{name}`"))
format!("Remove assignment to unused variable `{name}`")
})
} }
} }

View file

@ -10,6 +10,7 @@ F401_10.py:6:16: F401 `orjson` imported but unused; consider using `importlib.ut
9 | 9 |
10 | return True 10 | return True
| |
= help: Remove unused import: `orjson`
F401_10.py:15:16: F401 [*] `orjson` imported but unused F401_10.py:15:16: F401 [*] `orjson` imported but unused
| |

View file

@ -11,7 +11,6 @@ use crate::registry::AsRule;
pub struct ManualFromImport { pub struct ManualFromImport {
module: String, module: String,
name: String, name: String,
fixable: bool,
} }
impl Violation for ManualFromImport { impl Violation for ManualFromImport {
@ -19,15 +18,13 @@ impl Violation for ManualFromImport {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let ManualFromImport { module, name, .. } = self; let ManualFromImport { module, name } = self;
format!("Use `from {module} import {name}` in lieu of alias") format!("Use `from {module} import {name}` in lieu of alias")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let ManualFromImport { module, name } = self;
.then_some(|ManualFromImport { module, name, .. }| { Some(format!("Replace with `from {module} import {name}`"))
format!("Replace with `from {module} import {name}`")
})
} }
} }
@ -48,7 +45,6 @@ pub fn manual_from_import(checker: &mut Checker, stmt: &Stmt, alias: &Alias, nam
ManualFromImport { ManualFromImport {
module: module.to_string(), module: module.to_string(),
name: name.to_string(), name: name.to_string(),
fixable,
}, },
alias.range(), alias.range(),
); );

View file

@ -1,4 +1,4 @@
use ruff_text_size::TextSize; use ruff_text_size::TextRange;
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
@ -17,7 +17,6 @@ pub enum MinMax {
#[violation] #[violation]
pub struct NestedMinMax { pub struct NestedMinMax {
func: MinMax, func: MinMax,
fixable: bool,
} }
impl Violation for NestedMinMax { impl Violation for NestedMinMax {
@ -28,9 +27,9 @@ impl Violation for NestedMinMax {
format!("Nested `{}` calls can be flattened", self.func) format!("Nested `{}` calls can be flattened", self.func)
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let NestedMinMax { func } = self;
.then_some(|NestedMinMax { func, .. }| format!("Flatten nested `{func}` calls")) Some(format!("Flatten nested `{func}` calls"))
} }
} }
@ -106,22 +105,15 @@ pub fn nested_min_max(
MinMax::try_from_call(func, keywords, &checker.ctx) == Some(min_max) MinMax::try_from_call(func, keywords, &checker.ctx) == Some(min_max)
}) { }) {
let fixable = !has_comments(expr, checker.locator); let fixable = !has_comments(expr, checker.locator);
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(NestedMinMax { func: min_max }, expr.range());
NestedMinMax {
func: min_max,
fixable,
},
expr.range(),
);
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
let flattened_expr = Expr::new( let flattened_expr = Expr::with_range(
TextSize::default(),
TextSize::default(),
ExprKind::Call { ExprKind::Call {
func: Box::new(func.clone()), func: Box::new(func.clone()),
args: collect_nested_args(&checker.ctx, min_max, args), args: collect_nested_args(&checker.ctx, min_max, args),
keywords: keywords.to_owned(), keywords: keywords.to_owned(),
}, },
TextRange::default(),
); );
#[allow(deprecated)] #[allow(deprecated)]
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement( diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(

View file

@ -21,8 +21,9 @@ impl Violation for SysExitAlias {
format!("Use `sys.exit()` instead of `{name}`") format!("Use `sys.exit()` instead of `{name}`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|SysExitAlias { name }| format!("Replace `{name}` with `sys.exit()`")) let SysExitAlias { name } = self;
Some(format!("Replace `{name}` with `sys.exit()`"))
} }
} }

View file

@ -52,5 +52,6 @@ import_aliasing.py:12:8: PLR0402 Use `from foo.bar import foobar` in lieu of ali
15 | import os 15 | import os
16 | import os as OS 16 | import os as OS
| |
= help: Replace with `from foo.bar import foobar`

View file

@ -192,5 +192,6 @@ nested_min_max.py:18:1: PLW3301 Nested `min` calls can be flattened
22 | | ) 22 | | )
| |_^ PLW3301 | |_^ PLW3301
| |
= help: Flatten nested `min` calls

View file

@ -14,7 +14,6 @@ use crate::registry::AsRule;
#[violation] #[violation]
pub struct ConvertNamedTupleFunctionalToClass { pub struct ConvertNamedTupleFunctionalToClass {
name: String, name: String,
fixable: bool,
} }
impl Violation for ConvertNamedTupleFunctionalToClass { impl Violation for ConvertNamedTupleFunctionalToClass {
@ -22,15 +21,14 @@ impl Violation for ConvertNamedTupleFunctionalToClass {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let ConvertNamedTupleFunctionalToClass { name, .. } = self; let ConvertNamedTupleFunctionalToClass { name } = self;
format!("Convert `{name}` from `NamedTuple` functional to class syntax") format!("Convert `{name}` from `NamedTuple` functional to class syntax")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let ConvertNamedTupleFunctionalToClass { name } = self;
.then_some(|ConvertNamedTupleFunctionalToClass { name, .. }| {
format!("Convert `{name}` to class syntax") Some(format!("Convert `{name}` to class syntax"))
})
} }
} }
@ -197,7 +195,6 @@ pub fn convert_named_tuple_functional_to_class(
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
ConvertNamedTupleFunctionalToClass { ConvertNamedTupleFunctionalToClass {
name: typename.to_string(), name: typename.to_string(),
fixable,
}, },
stmt.range(), stmt.range(),
); );

View file

@ -26,11 +26,9 @@ impl Violation for ConvertTypedDictFunctionalToClass {
format!("Convert `{name}` from `TypedDict` functional to class syntax") format!("Convert `{name}` from `TypedDict` functional to class syntax")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let ConvertTypedDictFunctionalToClass { name, .. } = self;
.then_some(|ConvertTypedDictFunctionalToClass { name, .. }| { Some(format!("Convert `{name}` to class syntax"))
format!("Convert `{name}` to class syntax")
})
} }
} }

View file

@ -20,12 +20,8 @@ impl Violation for DatetimeTimezoneUTC {
format!("Use `datetime.UTC` alias") format!("Use `datetime.UTC` alias")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
if self.straight_import { Some("Convert to `datetime.UTC` alias".to_string())
Some(|_| "Convert to `datetime.UTC` alias".to_string())
} else {
None
}
} }
} }

View file

@ -62,14 +62,9 @@ impl Violation for DeprecatedImport {
} }
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
if let Deprecation::WithoutRename(WithoutRename { fixable, .. }) = self.deprecation { if let Deprecation::WithoutRename(WithoutRename { target, .. }) = &self.deprecation {
fixable.then_some(|DeprecatedImport { deprecation }| { Some(format!("Import from `{target}`"))
let Deprecation::WithoutRename(WithoutRename { target, .. }) = deprecation else {
unreachable!();
};
format!("Import from `{target}`")
})
} else { } else {
None None
} }

View file

@ -24,8 +24,8 @@ impl Violation for FormatLiterals {
format!("Use implicit references for positional format fields") format!("Use implicit references for positional format fields")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(|_| "Remove explicit positional indices".to_string()) Some("Remove explicit positional indices".to_string())
} }
} }

View file

@ -7,9 +7,7 @@ use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
#[violation] #[violation]
pub struct OpenAlias { pub struct OpenAlias;
fixable: bool,
}
impl Violation for OpenAlias { impl Violation for OpenAlias {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -19,9 +17,8 @@ impl Violation for OpenAlias {
format!("Use builtin `open`") format!("Use builtin `open`")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable Some("Replace with builtin `open`".to_string())
.then_some(|_| format!("Replace with builtin `open`"))
} }
} }
@ -36,7 +33,7 @@ pub fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) {
.ctx .ctx
.find_binding("open") .find_binding("open")
.map_or(true, |binding| binding.kind.is_builtin()); .map_or(true, |binding| binding.kind.is_builtin());
let mut diagnostic = Diagnostic::new(OpenAlias { fixable }, expr.range()); let mut diagnostic = Diagnostic::new(OpenAlias, expr.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement( diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(

View file

@ -10,7 +10,6 @@ use crate::registry::AsRule;
#[violation] #[violation]
pub struct NonPEP585Annotation { pub struct NonPEP585Annotation {
name: String, name: String,
fixable: bool,
} }
impl Violation for NonPEP585Annotation { impl Violation for NonPEP585Annotation {
@ -18,7 +17,7 @@ impl Violation for NonPEP585Annotation {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let NonPEP585Annotation { name, .. } = self; let NonPEP585Annotation { name } = self;
format!( format!(
"Use `{}` instead of `{}` for type annotations", "Use `{}` instead of `{}` for type annotations",
name.to_lowercase(), name.to_lowercase(),
@ -26,10 +25,9 @@ impl Violation for NonPEP585Annotation {
) )
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable.then_some(|NonPEP585Annotation { name, .. }| { let NonPEP585Annotation { name } = self;
format!("Replace `{name}` with `{}`", name.to_lowercase()) Some(format!("Replace `{name}` with `{}`", name.to_lowercase()))
})
} }
} }
@ -48,7 +46,6 @@ pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
NonPEP585Annotation { NonPEP585Annotation {
name: binding.to_string(), name: binding.to_string(),
fixable,
}, },
expr.range(), expr.range(),
); );

View file

@ -10,9 +10,7 @@ use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
#[violation] #[violation]
pub struct NonPEP604Annotation { pub struct NonPEP604Annotation;
fixable: bool,
}
impl Violation for NonPEP604Annotation { impl Violation for NonPEP604Annotation {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes; const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
@ -22,8 +20,8 @@ impl Violation for NonPEP604Annotation {
format!("Use `X | Y` for type annotations") format!("Use `X | Y` for type annotations")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable.then_some(|_| format!("Convert to `X | Y`")) Some("Convert to `X | Y`".to_string())
} }
} }
@ -110,7 +108,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
match typing_member { match typing_member {
TypingMember::Optional => { TypingMember::Optional => {
let mut diagnostic = Diagnostic::new(NonPEP604Annotation { fixable }, expr.range()); let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement( diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
@ -121,7 +119,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
} }
TypingMember::Union => { TypingMember::Union => {
let mut diagnostic = Diagnostic::new(NonPEP604Annotation { fixable }, expr.range()); let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
match &slice.node { match &slice.node {
ExprKind::Slice { .. } => { ExprKind::Slice { .. } => {

View file

@ -151,6 +151,7 @@ UP006.py:45:10: UP006 Use `list` instead of `List` for type annotations
| ^^^^^^^^^^^^^^ UP006 | ^^^^^^^^^^^^^^ UP006
46 | ... 46 | ...
| |
= help: Replace `List` with `list`
UP006.py:49:11: UP006 [*] Use `list` instead of `List` for type annotations UP006.py:49:11: UP006 [*] Use `list` instead of `List` for type annotations
| |
@ -212,6 +213,7 @@ UP006.py:53:16: UP006 Use `list` instead of `List` for type annotations
| ^^^^^^^^^^^^^^ UP006 | ^^^^^^^^^^^^^^ UP006
54 | ... 54 | ...
| |
= help: Replace `List` with `list`
UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations
| |
@ -219,6 +221,7 @@ UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations
| ^^^^^^^^^^^^^^^^^^^^^^ UP006 | ^^^^^^^^^^^^^^^^^^^^^^ UP006
58 | ... 58 | ...
| |
= help: Replace `List` with `list`
UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations
| |
@ -226,5 +229,6 @@ UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations
| ^^^^^^^^^^^^^^^^^^^^^^ UP006 | ^^^^^^^^^^^^^^^^^^^^^^ UP006
58 | ... 58 | ...
| |
= help: Replace `List` with `list`

View file

@ -209,6 +209,7 @@ UP007.py:48:9: UP007 Use `X | Y` for type annotations
51 | 51 |
52 | x = Union[str, int] 52 | x = Union[str, int]
| |
= help: Convert to `X | Y`
UP007.py:50:9: UP007 Use `X | Y` for type annotations UP007.py:50:9: UP007 Use `X | Y` for type annotations
| |
@ -219,6 +220,7 @@ UP007.py:50:9: UP007 Use `X | Y` for type annotations
53 | x = Union["str", "int"] 53 | x = Union["str", "int"]
54 | x: Union[str, int] 54 | x: Union[str, int]
| |
= help: Convert to `X | Y`
UP007.py:52:8: UP007 [*] Use `X | Y` for type annotations UP007.py:52:8: UP007 [*] Use `X | Y` for type annotations
| |

View file

@ -28,5 +28,6 @@ UP020.py:8:6: UP020 Use builtin `open`
| ^^^^^^^^^^^^^ UP020 | ^^^^^^^^^^^^^ UP020
11 | print(f.read()) 11 | print(f.read())
| |
= help: Replace with builtin `open`

View file

@ -418,5 +418,6 @@ UP035.py:46:10: UP035 Import from `collections.abc` instead: `Mapping`
50 | 50 |
51 | # OK 51 | # OK
| |
= help: Import from `collections.abc`

View file

@ -8,6 +8,7 @@ UP017.py:7:7: UP017 Use `datetime.UTC` alias
| ^^^^^^^^^^^^ UP017 | ^^^^^^^^^^^^ UP017
9 | print(tz.utc) 9 | print(tz.utc)
| |
= help: Convert to `datetime.UTC` alias
UP017.py:8:7: UP017 Use `datetime.UTC` alias UP017.py:8:7: UP017 Use `datetime.UTC` alias
| |
@ -18,6 +19,7 @@ UP017.py:8:7: UP017 Use `datetime.UTC` alias
11 | 11 |
12 | print(datetime.timezone.utc) 12 | print(datetime.timezone.utc)
| |
= help: Convert to `datetime.UTC` alias
UP017.py:10:7: UP017 [*] Use `datetime.UTC` alias UP017.py:10:7: UP017 [*] Use `datetime.UTC` alias
| |
@ -43,5 +45,6 @@ UP017.py:11:7: UP017 Use `datetime.UTC` alias
12 | print(dt.timezone.utc) 12 | print(dt.timezone.utc)
| ^^^^^^^^^^^^^^^ UP017 | ^^^^^^^^^^^^^^^ UP017
| |
= help: Convert to `datetime.UTC` alias

View file

@ -10,7 +10,6 @@ use crate::registry::AsRule;
#[violation] #[violation]
pub struct CollectionLiteralConcatenation { pub struct CollectionLiteralConcatenation {
expr: String, expr: String,
fixable: bool,
} }
impl Violation for CollectionLiteralConcatenation { impl Violation for CollectionLiteralConcatenation {
@ -18,15 +17,13 @@ impl Violation for CollectionLiteralConcatenation {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let CollectionLiteralConcatenation { expr, .. } = self; let CollectionLiteralConcatenation { expr } = self;
format!("Consider `{expr}` instead of concatenation") format!("Consider `{expr}` instead of concatenation")
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
self.fixable let CollectionLiteralConcatenation { expr } = self;
.then_some(|CollectionLiteralConcatenation { expr, .. }| { Some(format!("Replace with `{expr}`"))
format!("Replace with `{expr}`")
})
} }
} }
@ -102,7 +99,6 @@ pub fn collection_literal_concatenation(checker: &mut Checker, expr: &Expr) {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
CollectionLiteralConcatenation { CollectionLiteralConcatenation {
expr: contents.clone(), expr: contents.clone(),
fixable,
}, },
expr.range(), expr.range(),
); );

View file

@ -226,6 +226,7 @@ RUF005.py:32:10: RUF005 Consider `[*first, 4, 5, 6]` instead of concatenation
42 | 42 |
43 | [] + foo + [ 43 | [] + foo + [
| |
= help: Replace with `[*first, 4, 5, 6]`
RUF005.py:41:1: RUF005 [*] Consider `[*foo]` instead of concatenation RUF005.py:41:1: RUF005 [*] Consider `[*foo]` instead of concatenation
| |

View file

@ -30,10 +30,13 @@ pub trait Violation: Debug + PartialEq + Eq {
None None
} }
/// If autofix is (potentially) available for this violation returns another // TODO micha: Move `autofix_title` to `Fix`, add new `advice` method that is shown as an advice.
/// function that in turn can be used to obtain a string describing the // Change the `Diagnostic` renderer to show the advice, and render the fix message after the `Suggested fix: <here>`
/// autofix.
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { /// Returns the title for the autofix. The message is also shown as an advice as part of the diagnostics.
///
/// Required for rules that have autofixes.
fn autofix_title(&self) -> Option<String> {
None None
} }
@ -72,8 +75,8 @@ impl<VA: AlwaysAutofixableViolation> Violation for VA {
<Self as AlwaysAutofixableViolation>::explanation() <Self as AlwaysAutofixableViolation>::explanation()
} }
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> { fn autofix_title(&self) -> Option<String> {
Some(Self::autofix_title) Some(<Self as AlwaysAutofixableViolation>::autofix_title(self))
} }
fn message_formats() -> &'static [&'static str] { fn message_formats() -> &'static [&'static str] {

View file

@ -59,7 +59,7 @@ pub fn violation(violation: &ItemStruct) -> Result<TokenStream> {
Self { Self {
body: Violation::message(&value), body: Violation::message(&value),
suggestion: value.autofix_title_formatter().map(|f| f(&value)), suggestion: Violation::autofix_title(&value),
name: stringify!(#ident).to_string(), name: stringify!(#ident).to_string(),
} }
} }
@ -82,7 +82,7 @@ pub fn violation(violation: &ItemStruct) -> Result<TokenStream> {
Self { Self {
body: Violation::message(&value), body: Violation::message(&value),
suggestion: value.autofix_title_formatter().map(|f| f(&value)), suggestion: Violation::autofix_title(&value),
name: stringify!(#ident).to_string(), name: stringify!(#ident).to_string(),
} }
} }