diff --git a/crates/ruff_linter/resources/test/fixtures/refurb/FURB118.py b/crates/ruff_linter/resources/test/fixtures/refurb/FURB118.py index f49b068cf5511d..72b1af66185b85 100644 --- a/crates/ruff_linter/resources/test/fixtures/refurb/FURB118.py +++ b/crates/ruff_linter/resources/test/fixtures/refurb/FURB118.py @@ -108,6 +108,18 @@ class Baz: z: Callable = lambda self, other: self == other +# Lambdas wrapped in function calls could also still be method definitions! +# To avoid false positives, we shouldn't flag any of these either: +from typing import final, override, no_type_check + + +class Foo: + a = final(lambda self, other: self == other) + b = override(lambda self, other: self == other) + c = no_type_check(lambda self, other: self == other) + d = final(override(no_type_check(lambda self, other: self == other))) + + # lambdas used in decorators do not constitute method definitions, # so these *should* be flagged: class TheLambdasHereAreNotMethods: diff --git a/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs b/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs index ad68ed8509fa36..b94610c4dd5ae6 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs @@ -91,7 +91,9 @@ impl Violation for ReimplementedOperator { /// FURB118 pub(crate) fn reimplemented_operator(checker: &mut Checker, target: &FunctionLike) { - // Ignore methods, whether defined using the `def` keyword or via a `lambda` assignment. + // Ignore methods. + // Methods can be defined via a `def` statement in a class scope, + // or via a lambda appearing on the right-hand side of an assignment in a class scope. if checker.semantic().current_scope().kind.is_class() && (target.is_function_def() || checker diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB118_FURB118.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB118_FURB118.py.snap index d245b7c98bc57b..3d21ba0d73107f 100644 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB118_FURB118.py.snap +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB118_FURB118.py.snap @@ -950,60 +950,60 @@ FURB118.py:95:17: FURB118 [*] Use `operator.itemgetter((1, 2))` instead 97 98 | 98 99 | # All methods in classes are ignored, even those defined using lambdas: -FURB118.py:117:14: FURB118 [*] Use `operator.itemgetter(slice(-2, None))` instead of defining a lambda +FURB118.py:129:14: FURB118 [*] Use `operator.itemgetter(slice(-2, None))` instead of defining a lambda | -115 | "slicer, expected", -116 | [ -117 | (lambda x: x[-2:], "foo"), +127 | "slicer, expected", +128 | [ +129 | (lambda x: x[-2:], "foo"), | ^^^^^^^^^^^^^^^^ FURB118 -118 | (lambda x: x[-5:-3], "bar"), -119 | ], +130 | (lambda x: x[-5:-3], "bar"), +131 | ], | = help: Replace with `operator.itemgetter(slice(-2, None))` ℹ Unsafe fix -104 104 | y = lambda self, other: self == other -105 105 | -106 106 | from typing import Callable - 107 |+import operator -107 108 | class Baz: -108 109 | z: Callable = lambda self, other: self == other -109 110 | --------------------------------------------------------------------------------- -114 115 | @pytest.mark.parametrize( -115 116 | "slicer, expected", -116 117 | [ -117 |- (lambda x: x[-2:], "foo"), - 118 |+ (operator.itemgetter(slice(-2, None)), "foo"), -118 119 | (lambda x: x[-5:-3], "bar"), -119 120 | ], -120 121 | ) - -FURB118.py:118:14: FURB118 [*] Use `operator.itemgetter(slice(-5, -3))` instead of defining a lambda +111 111 | # Lambdas wrapped in function calls could also still be method definitions! +112 112 | # To avoid false positives, we shouldn't flag any of these either: +113 113 | from typing import final, override, no_type_check + 114 |+import operator +114 115 | +115 116 | +116 117 | class Foo: +-------------------------------------------------------------------------------- +126 127 | @pytest.mark.parametrize( +127 128 | "slicer, expected", +128 129 | [ +129 |- (lambda x: x[-2:], "foo"), + 130 |+ (operator.itemgetter(slice(-2, None)), "foo"), +130 131 | (lambda x: x[-5:-3], "bar"), +131 132 | ], +132 133 | ) + +FURB118.py:130:14: FURB118 [*] Use `operator.itemgetter(slice(-5, -3))` instead of defining a lambda | -116 | [ -117 | (lambda x: x[-2:], "foo"), -118 | (lambda x: x[-5:-3], "bar"), +128 | [ +129 | (lambda x: x[-2:], "foo"), +130 | (lambda x: x[-5:-3], "bar"), | ^^^^^^^^^^^^^^^^^^ FURB118 -119 | ], -120 | ) +131 | ], +132 | ) | = help: Replace with `operator.itemgetter(slice(-5, -3))` ℹ Unsafe fix -104 104 | y = lambda self, other: self == other -105 105 | -106 106 | from typing import Callable - 107 |+import operator -107 108 | class Baz: -108 109 | z: Callable = lambda self, other: self == other -109 110 | --------------------------------------------------------------------------------- -115 116 | "slicer, expected", -116 117 | [ -117 118 | (lambda x: x[-2:], "foo"), -118 |- (lambda x: x[-5:-3], "bar"), - 119 |+ (operator.itemgetter(slice(-5, -3)), "bar"), -119 120 | ], -120 121 | ) -121 122 | def test_inlet_asset_alias_extra_slice(self, slicer, expected): +111 111 | # Lambdas wrapped in function calls could also still be method definitions! +112 112 | # To avoid false positives, we shouldn't flag any of these either: +113 113 | from typing import final, override, no_type_check + 114 |+import operator +114 115 | +115 116 | +116 117 | class Foo: +-------------------------------------------------------------------------------- +127 128 | "slicer, expected", +128 129 | [ +129 130 | (lambda x: x[-2:], "foo"), +130 |- (lambda x: x[-5:-3], "bar"), + 131 |+ (operator.itemgetter(slice(-5, -3)), "bar"), +131 132 | ], +132 133 | ) +133 134 | def test_inlet_asset_alias_extra_slice(self, slicer, expected):