From eb3d0728e9517f470139a0237530e4a7e6f658b1 Mon Sep 17 00:00:00 2001 From: boolean-light <61526956+boolean-light@users.noreply.github.com> Date: Sat, 23 Mar 2024 15:27:57 +0900 Subject: [PATCH 1/3] Implemented FURB157 simplify-decimal-ctor --- .../resources/test/fixtures/refurb/FURB157.py | 22 ++ .../src/checkers/ast/analyze/expression.rs | 3 + crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/refurb/mod.rs | 1 + .../ruff_linter/src/rules/refurb/rules/mod.rs | 2 + .../refurb/rules/simplify_decimal_ctor.rs | 159 +++++++++++++++ ...es__refurb__tests__FURB157_FURB157.py.snap | 189 ++++++++++++++++++ ruff.schema.json | 1 + 8 files changed, 378 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py create mode 100644 crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs create mode 100644 crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py b/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py new file mode 100644 index 0000000000000..56631e66c825c --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py @@ -0,0 +1,22 @@ +import decimal +from decimal import Decimal +from decimal import Decimal as dc + +# Positive cases + +Decimal("0") +Decimal("-42") +Decimal(float("Infinity")) +Decimal(float("-Infinity")) +Decimal(float("inf")) +Decimal(float("-inf")) +Decimal(float("nan")) +decimal.Decimal("0") +dc("0") + +# Negative cases + +Decimal(0) +Decimal("Infinity") +decimal.Decimal(0) +dc(0) \ No newline at end of file diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 785ffd34a6d98..ff71dfd177d81 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -926,6 +926,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::RedundantLogBase) { refurb::rules::redundant_log_base(checker, call); } + if checker.enabled(Rule::SimplifyDecimalCtor) { + refurb::rules::simplify_decimal_ctor(checker, call); + } if checker.enabled(Rule::QuadraticListSummation) { ruff::rules::quadratic_list_summation(checker, call); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index f4b0a97b29b0e..039edd3a932ad 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -1047,6 +1047,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy), (Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate), (Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant), + (Refurb, "157") => (RuleGroup::Preview, rules::refurb::rules::SimplifyDecimalCtor), (Refurb, "161") => (RuleGroup::Preview, rules::refurb::rules::BitCount), (Refurb, "163") => (RuleGroup::Preview, rules::refurb::rules::RedundantLogBase), (Refurb, "167") => (RuleGroup::Preview, rules::refurb::rules::RegexFlagAlias), diff --git a/crates/ruff_linter/src/rules/refurb/mod.rs b/crates/ruff_linter/src/rules/refurb/mod.rs index 91af7e47d2060..aeb1987c43f65 100644 --- a/crates/ruff_linter/src/rules/refurb/mod.rs +++ b/crates/ruff_linter/src/rules/refurb/mod.rs @@ -25,6 +25,7 @@ mod tests { #[test_case(Rule::SliceCopy, Path::new("FURB145.py"))] #[test_case(Rule::UnnecessaryEnumerate, Path::new("FURB148.py"))] #[test_case(Rule::MathConstant, Path::new("FURB152.py"))] + #[test_case(Rule::SimplifyDecimalCtor, Path::new("FURB157.py"))] #[test_case(Rule::PrintEmptyString, Path::new("FURB105.py"))] #[test_case(Rule::ImplicitCwd, Path::new("FURB177.py"))] #[test_case(Rule::SingleItemMembershipTest, Path::new("FURB171.py"))] diff --git a/crates/ruff_linter/src/rules/refurb/rules/mod.rs b/crates/ruff_linter/src/rules/refurb/rules/mod.rs index 97bc141fe0d46..8f31f0b029ae7 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/mod.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/mod.rs @@ -16,6 +16,7 @@ pub(crate) use regex_flag_alias::*; pub(crate) use reimplemented_operator::*; pub(crate) use reimplemented_starmap::*; pub(crate) use repeated_append::*; +pub(crate) use simplify_decimal_ctor::*; pub(crate) use single_item_membership_test::*; pub(crate) use slice_copy::*; pub(crate) use type_none_comparison::*; @@ -39,6 +40,7 @@ mod regex_flag_alias; mod reimplemented_operator; mod reimplemented_starmap; mod repeated_append; +mod simplify_decimal_ctor; mod single_item_membership_test; mod slice_copy; mod type_none_comparison; diff --git a/crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs b/crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs new file mode 100644 index 0000000000000..64d3f6efb8fa7 --- /dev/null +++ b/crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs @@ -0,0 +1,159 @@ +use regex::Regex; +use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, Expr, ExprCall}; +use ruff_python_trivia::PythonWhitespace; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for the use of `Decimal` constructor that can be made more succinct. +/// This includes unnecessary string literal or special literal of float. +/// +/// ## Why is this bad? +/// This will make code longer and harder to read. +/// +/// ## Example +/// ```python +/// Decimal("0") +/// Decimal(float("Infinity")) +/// ``` +/// +/// Use instead: +/// ```python +/// Decimal(0) +/// Decimal("Infinity") +/// ``` +/// +/// ## Fix safety +/// This rule's fix is marked as unsafe, as the `Decimal` could be user-defined +/// function or constructor, which is not intended for the fixtures like above. +/// +/// ## References +/// - [Python documentation: `decimal`](https://docs.python.org/3/library/decimal.html) +#[violation] +pub struct SimplifyDecimalCtor { + replace_old: String, + replace_new: String, +} + +impl Violation for SimplifyDecimalCtor { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Always; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Verbose expression in `Decimal` constructor") + } + + fn fix_title(&self) -> Option { + Some(format!( + "Replace {} with {}", + self.replace_old, self.replace_new + )) + } +} + +/// FURB157 +pub(crate) fn simplify_decimal_ctor(checker: &mut Checker, call: &ExprCall) { + if !checker + .semantic() + .resolve_qualified_name(&call.func) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["decimal", "Decimal"])) + { + return; + } + let ast::Arguments { args, keywords, .. } = &call.arguments; + // Decimal accepts arguments of the form Decimal(value='0', context=None). + let Some(value) = args.first().or_else(|| { + keywords + .iter() + .find(|&keyword| keyword.arg.as_ref().map(ast::Identifier::as_str) == Some("value")) + .map(|keyword| &keyword.value) + }) else { + return; + }; + + let decimal_constructor = checker.locator().slice(call.func.range()); + + let diagnostic = match value { + Expr::StringLiteral(ast::ExprStringLiteral { + value: str_literal, .. + }) => { + let trimmed = str_literal + .to_str() + .trim_whitespace() + .trim_start_matches('+'); + let integer_string = Regex::new(r"^([\+\-]?)0*(\d+)$").unwrap(); + if !integer_string.is_match(trimmed) { + return; + }; + + let intg = integer_string.replace(trimmed, "$1$2").into_owned(); + + let mut diagnostic = Diagnostic::new( + SimplifyDecimalCtor { + replace_old: format!("{}(\"{}\")", decimal_constructor, str_literal.to_str()), + replace_new: format!("{decimal_constructor}({intg})"), + }, + call.range(), + ); + + diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( + format!("{decimal_constructor}({intg})"), + call.range(), + ))); + + diagnostic + } + Expr::Call( + floatcall @ ast::ExprCall { + func, arguments, .. + }, + ) => { + let Some(func_name) = func.as_name_expr() else { + return; + }; + if func_name.id != "float" { + return; + }; + if !checker.semantic().is_builtin(&func_name.id) { + return; + }; + + if arguments.args.len() != 1 || arguments.keywords.len() > 0 { + return; + }; + let Some(value_float) = arguments.args[0].as_string_literal_expr() else { + return; + }; + let value_float_str = value_float.value.to_str(); + if !matches!( + value_float_str.to_lowercase().as_str(), + "inf" | "-inf" | "infinity" | "-infinity" | "nan" + ) { + return; + } + + let mut diagnostic = Diagnostic::new( + SimplifyDecimalCtor { + replace_old: format!("float(\"{value_float_str}\")"), + replace_new: format!("\"{value_float_str}\""), + }, + call.range(), + ); + + diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( + format!("\"{value_float_str}\""), + floatcall.range(), + ))); + + diagnostic + } + _ => { + return; + } + }; + + checker.diagnostics.push(diagnostic); +} diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap new file mode 100644 index 0000000000000..ab5c1cbd69d57 --- /dev/null +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap @@ -0,0 +1,189 @@ +--- +source: crates/ruff_linter/src/rules/refurb/mod.rs +--- +FURB157.py:7:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +5 | # Positive cases +6 | +7 | Decimal("0") + | ^^^^^^^^^^^^ FURB157 +8 | Decimal("-42") +9 | Decimal(float("Infinity")) + | + = help: Replace Decimal("0") with Decimal(0) + +ℹ Unsafe fix +4 4 | +5 5 | # Positive cases +6 6 | +7 |-Decimal("0") + 7 |+Decimal(0) +8 8 | Decimal("-42") +9 9 | Decimal(float("Infinity")) +10 10 | Decimal(float("-Infinity")) + +FURB157.py:8:1: FURB157 [*] Verbose expression in `Decimal` constructor + | + 7 | Decimal("0") + 8 | Decimal("-42") + | ^^^^^^^^^^^^^^ FURB157 + 9 | Decimal(float("Infinity")) +10 | Decimal(float("-Infinity")) + | + = help: Replace Decimal("-42") with Decimal(-42) + +ℹ Unsafe fix +5 5 | # Positive cases +6 6 | +7 7 | Decimal("0") +8 |-Decimal("-42") + 8 |+Decimal(-42) +9 9 | Decimal(float("Infinity")) +10 10 | Decimal(float("-Infinity")) +11 11 | Decimal(float("inf")) + +FURB157.py:9:1: FURB157 [*] Verbose expression in `Decimal` constructor + | + 7 | Decimal("0") + 8 | Decimal("-42") + 9 | Decimal(float("Infinity")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 +10 | Decimal(float("-Infinity")) +11 | Decimal(float("inf")) + | + = help: Replace float("Infinity") with "Infinity" + +ℹ Unsafe fix +6 6 | +7 7 | Decimal("0") +8 8 | Decimal("-42") +9 |-Decimal(float("Infinity")) + 9 |+Decimal("Infinity") +10 10 | Decimal(float("-Infinity")) +11 11 | Decimal(float("inf")) +12 12 | Decimal(float("-inf")) + +FURB157.py:10:1: FURB157 [*] Verbose expression in `Decimal` constructor + | + 8 | Decimal("-42") + 9 | Decimal(float("Infinity")) +10 | Decimal(float("-Infinity")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 +11 | Decimal(float("inf")) +12 | Decimal(float("-inf")) + | + = help: Replace float("-Infinity") with "-Infinity" + +ℹ Unsafe fix +7 7 | Decimal("0") +8 8 | Decimal("-42") +9 9 | Decimal(float("Infinity")) +10 |-Decimal(float("-Infinity")) + 10 |+Decimal("-Infinity") +11 11 | Decimal(float("inf")) +12 12 | Decimal(float("-inf")) +13 13 | Decimal(float("nan")) + +FURB157.py:11:1: FURB157 [*] Verbose expression in `Decimal` constructor + | + 9 | Decimal(float("Infinity")) +10 | Decimal(float("-Infinity")) +11 | Decimal(float("inf")) + | ^^^^^^^^^^^^^^^^^^^^^ FURB157 +12 | Decimal(float("-inf")) +13 | Decimal(float("nan")) + | + = help: Replace float("inf") with "inf" + +ℹ Unsafe fix +8 8 | Decimal("-42") +9 9 | Decimal(float("Infinity")) +10 10 | Decimal(float("-Infinity")) +11 |-Decimal(float("inf")) + 11 |+Decimal("inf") +12 12 | Decimal(float("-inf")) +13 13 | Decimal(float("nan")) +14 14 | decimal.Decimal("0") + +FURB157.py:12:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +10 | Decimal(float("-Infinity")) +11 | Decimal(float("inf")) +12 | Decimal(float("-inf")) + | ^^^^^^^^^^^^^^^^^^^^^^ FURB157 +13 | Decimal(float("nan")) +14 | decimal.Decimal("0") + | + = help: Replace float("-inf") with "-inf" + +ℹ Unsafe fix +9 9 | Decimal(float("Infinity")) +10 10 | Decimal(float("-Infinity")) +11 11 | Decimal(float("inf")) +12 |-Decimal(float("-inf")) + 12 |+Decimal("-inf") +13 13 | Decimal(float("nan")) +14 14 | decimal.Decimal("0") +15 15 | dc("0") + +FURB157.py:13:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +11 | Decimal(float("inf")) +12 | Decimal(float("-inf")) +13 | Decimal(float("nan")) + | ^^^^^^^^^^^^^^^^^^^^^ FURB157 +14 | decimal.Decimal("0") +15 | dc("0") + | + = help: Replace float("nan") with "nan" + +ℹ Unsafe fix +10 10 | Decimal(float("-Infinity")) +11 11 | Decimal(float("inf")) +12 12 | Decimal(float("-inf")) +13 |-Decimal(float("nan")) + 13 |+Decimal("nan") +14 14 | decimal.Decimal("0") +15 15 | dc("0") +16 16 | + +FURB157.py:14:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +12 | Decimal(float("-inf")) +13 | Decimal(float("nan")) +14 | decimal.Decimal("0") + | ^^^^^^^^^^^^^^^^^^^^ FURB157 +15 | dc("0") + | + = help: Replace decimal.Decimal("0") with decimal.Decimal(0) + +ℹ Unsafe fix +11 11 | Decimal(float("inf")) +12 12 | Decimal(float("-inf")) +13 13 | Decimal(float("nan")) +14 |-decimal.Decimal("0") + 14 |+decimal.Decimal(0) +15 15 | dc("0") +16 16 | +17 17 | # Negative cases + +FURB157.py:15:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +13 | Decimal(float("nan")) +14 | decimal.Decimal("0") +15 | dc("0") + | ^^^^^^^ FURB157 +16 | +17 | # Negative cases + | + = help: Replace dc("0") with dc(0) + +ℹ Unsafe fix +12 12 | Decimal(float("-inf")) +13 13 | Decimal(float("nan")) +14 14 | decimal.Decimal("0") +15 |-dc("0") + 15 |+dc(0) +16 16 | +17 17 | # Negative cases +18 18 | diff --git a/ruff.schema.json b/ruff.schema.json index b3e88111c7f73..40bf793829239 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3018,6 +3018,7 @@ "FURB148", "FURB15", "FURB152", + "FURB157", "FURB16", "FURB161", "FURB163", From 9517e9a4aaa7efbc602759735c72845136fde107 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 24 Mar 2024 21:48:26 -0400 Subject: [PATCH 2/3] Change rule name --- .../resources/test/fixtures/refurb/FURB157.py | 9 +- .../src/checkers/ast/analyze/expression.rs | 4 +- crates/ruff_linter/src/codes.rs | 2 +- crates/ruff_linter/src/rules/refurb/mod.rs | 2 +- .../ruff_linter/src/rules/refurb/rules/mod.rs | 4 +- ...ctor.rs => verbose_decimal_constructor.rs} | 12 +- ...es__refurb__tests__FURB157_FURB157.py.snap | 259 ++++++++---------- 7 files changed, 133 insertions(+), 159 deletions(-) rename crates/ruff_linter/src/rules/refurb/rules/{simplify_decimal_ctor.rs => verbose_decimal_constructor.rs} (93%) diff --git a/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py b/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py index 56631e66c825c..2d4b3aa089c02 100644 --- a/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py +++ b/crates/ruff_linter/resources/test/fixtures/refurb/FURB157.py @@ -1,9 +1,7 @@ import decimal from decimal import Decimal -from decimal import Decimal as dc - -# Positive cases +# Errors Decimal("0") Decimal("-42") Decimal(float("Infinity")) @@ -12,11 +10,8 @@ Decimal(float("-inf")) Decimal(float("nan")) decimal.Decimal("0") -dc("0") - -# Negative cases +# OK Decimal(0) Decimal("Infinity") decimal.Decimal(0) -dc(0) \ No newline at end of file diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index ff71dfd177d81..e2d9f94a63a6b 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -926,8 +926,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::RedundantLogBase) { refurb::rules::redundant_log_base(checker, call); } - if checker.enabled(Rule::SimplifyDecimalCtor) { - refurb::rules::simplify_decimal_ctor(checker, call); + if checker.enabled(Rule::VerboseDecimalConstructor) { + refurb::rules::verbose_decimal_constructor(checker, call); } if checker.enabled(Rule::QuadraticListSummation) { ruff::rules::quadratic_list_summation(checker, call); diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 039edd3a932ad..efbf51abf39d0 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -1047,7 +1047,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy), (Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate), (Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant), - (Refurb, "157") => (RuleGroup::Preview, rules::refurb::rules::SimplifyDecimalCtor), + (Refurb, "157") => (RuleGroup::Preview, rules::refurb::rules::VerboseDecimalConstructor), (Refurb, "161") => (RuleGroup::Preview, rules::refurb::rules::BitCount), (Refurb, "163") => (RuleGroup::Preview, rules::refurb::rules::RedundantLogBase), (Refurb, "167") => (RuleGroup::Preview, rules::refurb::rules::RegexFlagAlias), diff --git a/crates/ruff_linter/src/rules/refurb/mod.rs b/crates/ruff_linter/src/rules/refurb/mod.rs index aeb1987c43f65..f67ddee937a01 100644 --- a/crates/ruff_linter/src/rules/refurb/mod.rs +++ b/crates/ruff_linter/src/rules/refurb/mod.rs @@ -25,7 +25,7 @@ mod tests { #[test_case(Rule::SliceCopy, Path::new("FURB145.py"))] #[test_case(Rule::UnnecessaryEnumerate, Path::new("FURB148.py"))] #[test_case(Rule::MathConstant, Path::new("FURB152.py"))] - #[test_case(Rule::SimplifyDecimalCtor, Path::new("FURB157.py"))] + #[test_case(Rule::VerboseDecimalConstructor, Path::new("FURB157.py"))] #[test_case(Rule::PrintEmptyString, Path::new("FURB105.py"))] #[test_case(Rule::ImplicitCwd, Path::new("FURB177.py"))] #[test_case(Rule::SingleItemMembershipTest, Path::new("FURB171.py"))] diff --git a/crates/ruff_linter/src/rules/refurb/rules/mod.rs b/crates/ruff_linter/src/rules/refurb/rules/mod.rs index 8f31f0b029ae7..a395e6065a71a 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/mod.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/mod.rs @@ -16,11 +16,11 @@ pub(crate) use regex_flag_alias::*; pub(crate) use reimplemented_operator::*; pub(crate) use reimplemented_starmap::*; pub(crate) use repeated_append::*; -pub(crate) use simplify_decimal_ctor::*; pub(crate) use single_item_membership_test::*; pub(crate) use slice_copy::*; pub(crate) use type_none_comparison::*; pub(crate) use unnecessary_enumerate::*; +pub(crate) use verbose_decimal_constructor::*; mod bit_count; mod check_and_remove_from_set; @@ -40,8 +40,8 @@ mod regex_flag_alias; mod reimplemented_operator; mod reimplemented_starmap; mod repeated_append; -mod simplify_decimal_ctor; mod single_item_membership_test; mod slice_copy; mod type_none_comparison; mod unnecessary_enumerate; +mod verbose_decimal_constructor; diff --git a/crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs b/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs similarity index 93% rename from crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs rename to crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs index 64d3f6efb8fa7..5a0600e4f394b 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/simplify_decimal_ctor.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs @@ -33,12 +33,12 @@ use crate::checkers::ast::Checker; /// ## References /// - [Python documentation: `decimal`](https://docs.python.org/3/library/decimal.html) #[violation] -pub struct SimplifyDecimalCtor { +pub struct VerboseDecimalConstructor { replace_old: String, replace_new: String, } -impl Violation for SimplifyDecimalCtor { +impl Violation for VerboseDecimalConstructor { const FIX_AVAILABILITY: FixAvailability = FixAvailability::Always; #[derive_message_formats] @@ -55,7 +55,7 @@ impl Violation for SimplifyDecimalCtor { } /// FURB157 -pub(crate) fn simplify_decimal_ctor(checker: &mut Checker, call: &ExprCall) { +pub(crate) fn verbose_decimal_constructor(checker: &mut Checker, call: &ExprCall) { if !checker .semantic() .resolve_qualified_name(&call.func) @@ -84,7 +84,7 @@ pub(crate) fn simplify_decimal_ctor(checker: &mut Checker, call: &ExprCall) { .to_str() .trim_whitespace() .trim_start_matches('+'); - let integer_string = Regex::new(r"^([\+\-]?)0*(\d+)$").unwrap(); + let integer_string = Regex::new(r"^([+\-]?)0*(\d+)$").unwrap(); if !integer_string.is_match(trimmed) { return; }; @@ -92,7 +92,7 @@ pub(crate) fn simplify_decimal_ctor(checker: &mut Checker, call: &ExprCall) { let intg = integer_string.replace(trimmed, "$1$2").into_owned(); let mut diagnostic = Diagnostic::new( - SimplifyDecimalCtor { + VerboseDecimalConstructor { replace_old: format!("{}(\"{}\")", decimal_constructor, str_literal.to_str()), replace_new: format!("{decimal_constructor}({intg})"), }, @@ -136,7 +136,7 @@ pub(crate) fn simplify_decimal_ctor(checker: &mut Checker, call: &ExprCall) { } let mut diagnostic = Diagnostic::new( - SimplifyDecimalCtor { + VerboseDecimalConstructor { replace_old: format!("float(\"{value_float_str}\")"), replace_new: format!("\"{value_float_str}\""), }, diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap index ab5c1cbd69d57..35ba0f464b893 100644 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap @@ -1,189 +1,168 @@ --- source: crates/ruff_linter/src/rules/refurb/mod.rs --- -FURB157.py:7:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:5:1: FURB157 [*] Verbose expression in `Decimal` constructor | -5 | # Positive cases -6 | -7 | Decimal("0") +4 | # Errors +5 | Decimal("0") | ^^^^^^^^^^^^ FURB157 -8 | Decimal("-42") -9 | Decimal(float("Infinity")) +6 | Decimal("-42") +7 | Decimal(float("Infinity")) | = help: Replace Decimal("0") with Decimal(0) ℹ Unsafe fix -4 4 | -5 5 | # Positive cases -6 6 | -7 |-Decimal("0") - 7 |+Decimal(0) -8 8 | Decimal("-42") -9 9 | Decimal(float("Infinity")) -10 10 | Decimal(float("-Infinity")) - -FURB157.py:8:1: FURB157 [*] Verbose expression in `Decimal` constructor - | - 7 | Decimal("0") - 8 | Decimal("-42") - | ^^^^^^^^^^^^^^ FURB157 - 9 | Decimal(float("Infinity")) -10 | Decimal(float("-Infinity")) - | - = help: Replace Decimal("-42") with Decimal(-42) +2 2 | from decimal import Decimal +3 3 | +4 4 | # Errors +5 |-Decimal("0") + 5 |+Decimal(0) +6 6 | Decimal("-42") +7 7 | Decimal(float("Infinity")) +8 8 | Decimal(float("-Infinity")) + +FURB157.py:6:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +4 | # Errors +5 | Decimal("0") +6 | Decimal("-42") + | ^^^^^^^^^^^^^^ FURB157 +7 | Decimal(float("Infinity")) +8 | Decimal(float("-Infinity")) + | + = help: Replace Decimal("-42") with Decimal(-42) ℹ Unsafe fix -5 5 | # Positive cases -6 6 | -7 7 | Decimal("0") -8 |-Decimal("-42") - 8 |+Decimal(-42) -9 9 | Decimal(float("Infinity")) -10 10 | Decimal(float("-Infinity")) -11 11 | Decimal(float("inf")) +3 3 | +4 4 | # Errors +5 5 | Decimal("0") +6 |-Decimal("-42") + 6 |+Decimal(-42) +7 7 | Decimal(float("Infinity")) +8 8 | Decimal(float("-Infinity")) +9 9 | Decimal(float("inf")) -FURB157.py:9:1: FURB157 [*] Verbose expression in `Decimal` constructor - | - 7 | Decimal("0") - 8 | Decimal("-42") - 9 | Decimal(float("Infinity")) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 -10 | Decimal(float("-Infinity")) -11 | Decimal(float("inf")) - | - = help: Replace float("Infinity") with "Infinity" +FURB157.py:7:1: FURB157 [*] Verbose expression in `Decimal` constructor + | +5 | Decimal("0") +6 | Decimal("-42") +7 | Decimal(float("Infinity")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 +8 | Decimal(float("-Infinity")) +9 | Decimal(float("inf")) + | + = help: Replace float("Infinity") with "Infinity" ℹ Unsafe fix -6 6 | -7 7 | Decimal("0") -8 8 | Decimal("-42") -9 |-Decimal(float("Infinity")) - 9 |+Decimal("Infinity") -10 10 | Decimal(float("-Infinity")) -11 11 | Decimal(float("inf")) -12 12 | Decimal(float("-inf")) +4 4 | # Errors +5 5 | Decimal("0") +6 6 | Decimal("-42") +7 |-Decimal(float("Infinity")) + 7 |+Decimal("Infinity") +8 8 | Decimal(float("-Infinity")) +9 9 | Decimal(float("inf")) +10 10 | Decimal(float("-inf")) -FURB157.py:10:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:8:1: FURB157 [*] Verbose expression in `Decimal` constructor | - 8 | Decimal("-42") - 9 | Decimal(float("Infinity")) -10 | Decimal(float("-Infinity")) + 6 | Decimal("-42") + 7 | Decimal(float("Infinity")) + 8 | Decimal(float("-Infinity")) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 -11 | Decimal(float("inf")) -12 | Decimal(float("-inf")) + 9 | Decimal(float("inf")) +10 | Decimal(float("-inf")) | = help: Replace float("-Infinity") with "-Infinity" ℹ Unsafe fix -7 7 | Decimal("0") -8 8 | Decimal("-42") -9 9 | Decimal(float("Infinity")) -10 |-Decimal(float("-Infinity")) - 10 |+Decimal("-Infinity") -11 11 | Decimal(float("inf")) -12 12 | Decimal(float("-inf")) -13 13 | Decimal(float("nan")) +5 5 | Decimal("0") +6 6 | Decimal("-42") +7 7 | Decimal(float("Infinity")) +8 |-Decimal(float("-Infinity")) + 8 |+Decimal("-Infinity") +9 9 | Decimal(float("inf")) +10 10 | Decimal(float("-inf")) +11 11 | Decimal(float("nan")) -FURB157.py:11:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:9:1: FURB157 [*] Verbose expression in `Decimal` constructor | - 9 | Decimal(float("Infinity")) -10 | Decimal(float("-Infinity")) -11 | Decimal(float("inf")) + 7 | Decimal(float("Infinity")) + 8 | Decimal(float("-Infinity")) + 9 | Decimal(float("inf")) | ^^^^^^^^^^^^^^^^^^^^^ FURB157 -12 | Decimal(float("-inf")) -13 | Decimal(float("nan")) +10 | Decimal(float("-inf")) +11 | Decimal(float("nan")) | = help: Replace float("inf") with "inf" ℹ Unsafe fix -8 8 | Decimal("-42") -9 9 | Decimal(float("Infinity")) -10 10 | Decimal(float("-Infinity")) -11 |-Decimal(float("inf")) - 11 |+Decimal("inf") -12 12 | Decimal(float("-inf")) -13 13 | Decimal(float("nan")) -14 14 | decimal.Decimal("0") +6 6 | Decimal("-42") +7 7 | Decimal(float("Infinity")) +8 8 | Decimal(float("-Infinity")) +9 |-Decimal(float("inf")) + 9 |+Decimal("inf") +10 10 | Decimal(float("-inf")) +11 11 | Decimal(float("nan")) +12 12 | decimal.Decimal("0") -FURB157.py:12:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:10:1: FURB157 [*] Verbose expression in `Decimal` constructor | -10 | Decimal(float("-Infinity")) -11 | Decimal(float("inf")) -12 | Decimal(float("-inf")) + 8 | Decimal(float("-Infinity")) + 9 | Decimal(float("inf")) +10 | Decimal(float("-inf")) | ^^^^^^^^^^^^^^^^^^^^^^ FURB157 -13 | Decimal(float("nan")) -14 | decimal.Decimal("0") +11 | Decimal(float("nan")) +12 | decimal.Decimal("0") | = help: Replace float("-inf") with "-inf" ℹ Unsafe fix -9 9 | Decimal(float("Infinity")) -10 10 | Decimal(float("-Infinity")) -11 11 | Decimal(float("inf")) -12 |-Decimal(float("-inf")) - 12 |+Decimal("-inf") -13 13 | Decimal(float("nan")) -14 14 | decimal.Decimal("0") -15 15 | dc("0") +7 7 | Decimal(float("Infinity")) +8 8 | Decimal(float("-Infinity")) +9 9 | Decimal(float("inf")) +10 |-Decimal(float("-inf")) + 10 |+Decimal("-inf") +11 11 | Decimal(float("nan")) +12 12 | decimal.Decimal("0") +13 13 | -FURB157.py:13:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:11:1: FURB157 [*] Verbose expression in `Decimal` constructor | -11 | Decimal(float("inf")) -12 | Decimal(float("-inf")) -13 | Decimal(float("nan")) + 9 | Decimal(float("inf")) +10 | Decimal(float("-inf")) +11 | Decimal(float("nan")) | ^^^^^^^^^^^^^^^^^^^^^ FURB157 -14 | decimal.Decimal("0") -15 | dc("0") +12 | decimal.Decimal("0") | = help: Replace float("nan") with "nan" ℹ Unsafe fix -10 10 | Decimal(float("-Infinity")) -11 11 | Decimal(float("inf")) -12 12 | Decimal(float("-inf")) -13 |-Decimal(float("nan")) - 13 |+Decimal("nan") -14 14 | decimal.Decimal("0") -15 15 | dc("0") -16 16 | +8 8 | Decimal(float("-Infinity")) +9 9 | Decimal(float("inf")) +10 10 | Decimal(float("-inf")) +11 |-Decimal(float("nan")) + 11 |+Decimal("nan") +12 12 | decimal.Decimal("0") +13 13 | +14 14 | # OK -FURB157.py:14:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:12:1: FURB157 [*] Verbose expression in `Decimal` constructor | -12 | Decimal(float("-inf")) -13 | Decimal(float("nan")) -14 | decimal.Decimal("0") +10 | Decimal(float("-inf")) +11 | Decimal(float("nan")) +12 | decimal.Decimal("0") | ^^^^^^^^^^^^^^^^^^^^ FURB157 -15 | dc("0") +13 | +14 | # OK | = help: Replace decimal.Decimal("0") with decimal.Decimal(0) ℹ Unsafe fix -11 11 | Decimal(float("inf")) -12 12 | Decimal(float("-inf")) -13 13 | Decimal(float("nan")) -14 |-decimal.Decimal("0") - 14 |+decimal.Decimal(0) -15 15 | dc("0") -16 16 | -17 17 | # Negative cases - -FURB157.py:15:1: FURB157 [*] Verbose expression in `Decimal` constructor - | -13 | Decimal(float("nan")) -14 | decimal.Decimal("0") -15 | dc("0") - | ^^^^^^^ FURB157 -16 | -17 | # Negative cases - | - = help: Replace dc("0") with dc(0) - -ℹ Unsafe fix -12 12 | Decimal(float("-inf")) -13 13 | Decimal(float("nan")) -14 14 | decimal.Decimal("0") -15 |-dc("0") - 15 |+dc(0) -16 16 | -17 17 | # Negative cases -18 18 | +9 9 | Decimal(float("inf")) +10 10 | Decimal(float("-inf")) +11 11 | Decimal(float("nan")) +12 |-decimal.Decimal("0") + 12 |+decimal.Decimal(0) +13 13 | +14 14 | # OK +15 15 | Decimal(0) From 655821b048349da3536063f21b019a07ee5779fc Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 24 Mar 2024 22:07:21 -0400 Subject: [PATCH 3/3] Make safe; remove regex --- .../rules/verbose_decimal_constructor.rs | 117 ++++++++++-------- ...es__refurb__tests__FURB157_FURB157.py.snap | 64 +++++----- 2 files changed, 95 insertions(+), 86 deletions(-) diff --git a/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs b/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs index 5a0600e4f394b..154c0405c41fb 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/verbose_decimal_constructor.rs @@ -1,4 +1,3 @@ -use regex::Regex; use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::{self as ast, Expr, ExprCall}; @@ -8,11 +7,20 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; /// ## What it does -/// Checks for the use of `Decimal` constructor that can be made more succinct. -/// This includes unnecessary string literal or special literal of float. +/// Checks for unnecessary string literal or float casts in `Decimal` +/// constructors. /// /// ## Why is this bad? -/// This will make code longer and harder to read. +/// The `Decimal` constructor accepts a variety of arguments, including +/// integers, floats, and strings. However, it's not necessary to cast +/// integer literals to strings when passing them to the `Decimal`. +/// +/// Similarly, `Decimal` accepts `inf`, `-inf`, and `nan` as string literals, +/// so there's no need to wrap those values in a `float` call when passing +/// them to the `Decimal` constructor. +/// +/// Prefer the more concise form of argument passing for `Decimal` +/// constructors, as it's more readable and idiomatic. /// /// ## Example /// ```python @@ -26,16 +34,11 @@ use crate::checkers::ast::Checker; /// Decimal("Infinity") /// ``` /// -/// ## Fix safety -/// This rule's fix is marked as unsafe, as the `Decimal` could be user-defined -/// function or constructor, which is not intended for the fixtures like above. -/// /// ## References /// - [Python documentation: `decimal`](https://docs.python.org/3/library/decimal.html) #[violation] pub struct VerboseDecimalConstructor { - replace_old: String, - replace_new: String, + replacement: String, } impl Violation for VerboseDecimalConstructor { @@ -47,10 +50,8 @@ impl Violation for VerboseDecimalConstructor { } fn fix_title(&self) -> Option { - Some(format!( - "Replace {} with {}", - self.replace_old, self.replace_new - )) + let VerboseDecimalConstructor { replacement } = self; + Some(format!("Replace with `{replacement}`")) } } @@ -63,89 +64,97 @@ pub(crate) fn verbose_decimal_constructor(checker: &mut Checker, call: &ExprCall { return; } - let ast::Arguments { args, keywords, .. } = &call.arguments; - // Decimal accepts arguments of the form Decimal(value='0', context=None). - let Some(value) = args.first().or_else(|| { - keywords - .iter() - .find(|&keyword| keyword.arg.as_ref().map(ast::Identifier::as_str) == Some("value")) - .map(|keyword| &keyword.value) - }) else { + + // Decimal accepts arguments of the form: `Decimal(value='0', context=None)` + let Some(value) = call.arguments.find_argument("value", 0) else { return; }; - let decimal_constructor = checker.locator().slice(call.func.range()); - let diagnostic = match value { Expr::StringLiteral(ast::ExprStringLiteral { value: str_literal, .. }) => { - let trimmed = str_literal - .to_str() - .trim_whitespace() - .trim_start_matches('+'); - let integer_string = Regex::new(r"^([+\-]?)0*(\d+)$").unwrap(); - if !integer_string.is_match(trimmed) { + // Parse the inner string as an integer. + let trimmed = str_literal.to_str().trim_whitespace(); + + // Extract the unary sign, if any. + let (unary, rest) = if let Some(trimmed) = trimmed.strip_prefix('+') { + ("+", trimmed) + } else if let Some(trimmed) = trimmed.strip_prefix('-') { + ("-", trimmed) + } else { + ("", trimmed) + }; + + // Skip leading zeros. + let rest = rest.trim_start_matches('0'); + + // Verify that the rest of the string is a valid integer. + if !rest.chars().all(|c| c.is_ascii_digit()) { return; }; - let intg = integer_string.replace(trimmed, "$1$2").into_owned(); + // If all the characters are zeros, then the value is zero. + let rest = if rest.is_empty() { "0" } else { rest }; + let replacement = format!("{unary}{rest}"); let mut diagnostic = Diagnostic::new( VerboseDecimalConstructor { - replace_old: format!("{}(\"{}\")", decimal_constructor, str_literal.to_str()), - replace_new: format!("{decimal_constructor}({intg})"), + replacement: replacement.clone(), }, - call.range(), + value.range(), ); - diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( - format!("{decimal_constructor}({intg})"), - call.range(), + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + replacement, + value.range(), ))); diagnostic } - Expr::Call( - floatcall @ ast::ExprCall { - func, arguments, .. - }, - ) => { + Expr::Call(ast::ExprCall { + func, arguments, .. + }) => { + // Must be a call to the `float` builtin. let Some(func_name) = func.as_name_expr() else { return; }; if func_name.id != "float" { return; }; - if !checker.semantic().is_builtin(&func_name.id) { + + // Must have exactly one argument, which is a string literal. + if arguments.keywords.len() != 0 { return; }; - - if arguments.args.len() != 1 || arguments.keywords.len() > 0 { + let [float] = arguments.args.as_ref() else { return; }; - let Some(value_float) = arguments.args[0].as_string_literal_expr() else { + let Some(float) = float.as_string_literal_expr() else { return; }; - let value_float_str = value_float.value.to_str(); if !matches!( - value_float_str.to_lowercase().as_str(), + float.value.to_str().to_lowercase().as_str(), "inf" | "-inf" | "infinity" | "-infinity" | "nan" ) { return; } + if !checker.semantic().is_builtin("float") { + return; + }; + + let replacement = checker.locator().slice(float).to_string(); let mut diagnostic = Diagnostic::new( VerboseDecimalConstructor { - replace_old: format!("float(\"{value_float_str}\")"), - replace_new: format!("\"{value_float_str}\""), + replacement: replacement.clone(), }, - call.range(), + value.range(), ); - diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( - format!("\"{value_float_str}\""), - floatcall.range(), + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + replacement, + value.range(), ))); diagnostic diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap index 35ba0f464b893..ef8680e3666d0 100644 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB157_FURB157.py.snap @@ -1,17 +1,17 @@ --- source: crates/ruff_linter/src/rules/refurb/mod.rs --- -FURB157.py:5:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:5:9: FURB157 [*] Verbose expression in `Decimal` constructor | 4 | # Errors 5 | Decimal("0") - | ^^^^^^^^^^^^ FURB157 + | ^^^ FURB157 6 | Decimal("-42") 7 | Decimal(float("Infinity")) | - = help: Replace Decimal("0") with Decimal(0) + = help: Replace with `0` -ℹ Unsafe fix +ℹ Safe fix 2 2 | from decimal import Decimal 3 3 | 4 4 | # Errors @@ -21,18 +21,18 @@ FURB157.py:5:1: FURB157 [*] Verbose expression in `Decimal` constructor 7 7 | Decimal(float("Infinity")) 8 8 | Decimal(float("-Infinity")) -FURB157.py:6:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:6:9: FURB157 [*] Verbose expression in `Decimal` constructor | 4 | # Errors 5 | Decimal("0") 6 | Decimal("-42") - | ^^^^^^^^^^^^^^ FURB157 + | ^^^^^ FURB157 7 | Decimal(float("Infinity")) 8 | Decimal(float("-Infinity")) | - = help: Replace Decimal("-42") with Decimal(-42) + = help: Replace with `-42` -ℹ Unsafe fix +ℹ Safe fix 3 3 | 4 4 | # Errors 5 5 | Decimal("0") @@ -42,18 +42,18 @@ FURB157.py:6:1: FURB157 [*] Verbose expression in `Decimal` constructor 8 8 | Decimal(float("-Infinity")) 9 9 | Decimal(float("inf")) -FURB157.py:7:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:7:9: FURB157 [*] Verbose expression in `Decimal` constructor | 5 | Decimal("0") 6 | Decimal("-42") 7 | Decimal(float("Infinity")) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 + | ^^^^^^^^^^^^^^^^^ FURB157 8 | Decimal(float("-Infinity")) 9 | Decimal(float("inf")) | - = help: Replace float("Infinity") with "Infinity" + = help: Replace with `"Infinity"` -ℹ Unsafe fix +ℹ Safe fix 4 4 | # Errors 5 5 | Decimal("0") 6 6 | Decimal("-42") @@ -63,18 +63,18 @@ FURB157.py:7:1: FURB157 [*] Verbose expression in `Decimal` constructor 9 9 | Decimal(float("inf")) 10 10 | Decimal(float("-inf")) -FURB157.py:8:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:8:9: FURB157 [*] Verbose expression in `Decimal` constructor | 6 | Decimal("-42") 7 | Decimal(float("Infinity")) 8 | Decimal(float("-Infinity")) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB157 + | ^^^^^^^^^^^^^^^^^^ FURB157 9 | Decimal(float("inf")) 10 | Decimal(float("-inf")) | - = help: Replace float("-Infinity") with "-Infinity" + = help: Replace with `"-Infinity"` -ℹ Unsafe fix +ℹ Safe fix 5 5 | Decimal("0") 6 6 | Decimal("-42") 7 7 | Decimal(float("Infinity")) @@ -84,18 +84,18 @@ FURB157.py:8:1: FURB157 [*] Verbose expression in `Decimal` constructor 10 10 | Decimal(float("-inf")) 11 11 | Decimal(float("nan")) -FURB157.py:9:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:9:9: FURB157 [*] Verbose expression in `Decimal` constructor | 7 | Decimal(float("Infinity")) 8 | Decimal(float("-Infinity")) 9 | Decimal(float("inf")) - | ^^^^^^^^^^^^^^^^^^^^^ FURB157 + | ^^^^^^^^^^^^ FURB157 10 | Decimal(float("-inf")) 11 | Decimal(float("nan")) | - = help: Replace float("inf") with "inf" + = help: Replace with `"inf"` -ℹ Unsafe fix +ℹ Safe fix 6 6 | Decimal("-42") 7 7 | Decimal(float("Infinity")) 8 8 | Decimal(float("-Infinity")) @@ -105,18 +105,18 @@ FURB157.py:9:1: FURB157 [*] Verbose expression in `Decimal` constructor 11 11 | Decimal(float("nan")) 12 12 | decimal.Decimal("0") -FURB157.py:10:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:10:9: FURB157 [*] Verbose expression in `Decimal` constructor | 8 | Decimal(float("-Infinity")) 9 | Decimal(float("inf")) 10 | Decimal(float("-inf")) - | ^^^^^^^^^^^^^^^^^^^^^^ FURB157 + | ^^^^^^^^^^^^^ FURB157 11 | Decimal(float("nan")) 12 | decimal.Decimal("0") | - = help: Replace float("-inf") with "-inf" + = help: Replace with `"-inf"` -ℹ Unsafe fix +ℹ Safe fix 7 7 | Decimal(float("Infinity")) 8 8 | Decimal(float("-Infinity")) 9 9 | Decimal(float("inf")) @@ -126,17 +126,17 @@ FURB157.py:10:1: FURB157 [*] Verbose expression in `Decimal` constructor 12 12 | decimal.Decimal("0") 13 13 | -FURB157.py:11:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:11:9: FURB157 [*] Verbose expression in `Decimal` constructor | 9 | Decimal(float("inf")) 10 | Decimal(float("-inf")) 11 | Decimal(float("nan")) - | ^^^^^^^^^^^^^^^^^^^^^ FURB157 + | ^^^^^^^^^^^^ FURB157 12 | decimal.Decimal("0") | - = help: Replace float("nan") with "nan" + = help: Replace with `"nan"` -ℹ Unsafe fix +ℹ Safe fix 8 8 | Decimal(float("-Infinity")) 9 9 | Decimal(float("inf")) 10 10 | Decimal(float("-inf")) @@ -146,18 +146,18 @@ FURB157.py:11:1: FURB157 [*] Verbose expression in `Decimal` constructor 13 13 | 14 14 | # OK -FURB157.py:12:1: FURB157 [*] Verbose expression in `Decimal` constructor +FURB157.py:12:17: FURB157 [*] Verbose expression in `Decimal` constructor | 10 | Decimal(float("-inf")) 11 | Decimal(float("nan")) 12 | decimal.Decimal("0") - | ^^^^^^^^^^^^^^^^^^^^ FURB157 + | ^^^ FURB157 13 | 14 | # OK | - = help: Replace decimal.Decimal("0") with decimal.Decimal(0) + = help: Replace with `0` -ℹ Unsafe fix +ℹ Safe fix 9 9 | Decimal(float("inf")) 10 10 | Decimal(float("-inf")) 11 11 | Decimal(float("nan"))