Skip to content

Commit

Permalink
Fix false negative on lambdas inside decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Nov 27, 2024
1 parent 82af9b9 commit 7e13565
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
18 changes: 18 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/refurb/FURB118.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,21 @@ def x(self, other):

class Bar:
y = lambda self, other: self == other

from typing import Callable
class Baz:
z: Callable = lambda self, other: self == other


# lambdas used in decorators do not constitute method definitions,
# so these *should* be flagged:
class TheLambdasHereAreNotMethods:
@pytest.mark.parametrize(
"slicer, expected",
[
(lambda x: x[-2:], "foo"),
(lambda x: x[-5:-3], "bar"),
],
)
def test_inlet_asset_alias_extra_slice(self, slicer, expected):
assert slice("whatever") == expected
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ 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.
if checker.semantic().current_scope().kind.is_class() {
if checker.semantic().current_scope().kind.is_class()
&& (target.is_function_def()
|| checker
.semantic()
.current_statements()
.any(|stmt| matches!(stmt, Stmt::AnnAssign(_) | Stmt::Assign(_))))
{
return;
}

Expand Down Expand Up @@ -152,6 +158,10 @@ impl FunctionLike<'_> {
}
}

const fn is_function_def(&self) -> bool {
matches!(self, Self::Function(_))
}

/// Return the body of the function-like node.
///
/// If the node is a function definition that consists of more than a single return statement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -949,3 +949,61 @@ FURB118.py:95:17: FURB118 [*] Use `operator.itemgetter((1, 2))` instead
96 97 |
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
|
115 | "slicer, expected",
116 | [
117 | (lambda x: x[-2:], "foo"),
| ^^^^^^^^^^^^^^^^ FURB118
118 | (lambda x: x[-5:-3], "bar"),
119 | ],
|
= 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
|
116 | [
117 | (lambda x: x[-2:], "foo"),
118 | (lambda x: x[-5:-3], "bar"),
| ^^^^^^^^^^^^^^^^^^ FURB118
119 | ],
120 | )
|
= 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):

0 comments on commit 7e13565

Please sign in to comment.