Skip to content

Commit

Permalink
Implement autofix for unnecessary-lambda (PLW0108) (#8621)
Browse files Browse the repository at this point in the history
Closes #8618.
  • Loading branch information
charliermarsh committed Nov 11, 2023
1 parent d7144d6 commit 9724dfd
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 13 deletions.
36 changes: 31 additions & 5 deletions crates/ruff_linter/src/rules/pylint/rules/unnecessary_lambda.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{self as ast, visitor, Expr, ExprLambda, Parameter, ParameterWithDefault};
use ruff_text_size::Ranged;
Expand All @@ -24,14 +25,29 @@ use crate::checkers::ast::Checker;
/// ```python
/// df.apply(str)
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe in cases in which the lambda body itself
/// contains an effect.
///
/// For example, replacing `lambda x, y: (func()(x, y))` with `func()` would
/// lead to a change in behavior, as `func()` would be evaluated eagerly when
/// defining the lambda, rather than when the lambda is called.
///
/// When the lambda body contains no visible effects, the fix is considered
/// safe.
#[violation]
pub struct UnnecessaryLambda;

impl Violation for UnnecessaryLambda {
impl AlwaysFixableViolation for UnnecessaryLambda {
#[derive_message_formats]
fn message(&self) -> String {
format!("Lambda may be unnecessary; consider inlining inner function")
}

fn fix_title(&self) -> String {
"Inline function call".to_string()
}
}

/// PLW0108
Expand Down Expand Up @@ -184,9 +200,19 @@ pub(crate) fn unnecessary_lambda(checker: &mut Checker, lambda: &ExprLambda) {
}
}

checker
.diagnostics
.push(Diagnostic::new(UnnecessaryLambda, lambda.range()));
let mut diagnostic = Diagnostic::new(UnnecessaryLambda, lambda.range());
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_replacement(
checker.locator().slice(func.as_ref()).to_string(),
lambda.range(),
),
if contains_effect(func.as_ref(), |id| checker.semantic().is_builtin(id)) {
Applicability::Unsafe
} else {
Applicability::Safe
},
));
checker.diagnostics.push(diagnostic);
}

/// Identify all `Expr::Name` nodes in an AST.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
unnecessary_lambda.py:1:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
unnecessary_lambda.py:1:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
1 | _ = lambda: print() # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^ PLW0108
2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
|
= help: Inline function call

unnecessary_lambda.py:2:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
ℹ Safe fix
1 |-_ = lambda: print() # [unnecessary-lambda]
1 |+_ = print # [unnecessary-lambda]
2 2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
3 3 |
4 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]

unnecessary_lambda.py:2:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
1 | _ = lambda: print() # [unnecessary-lambda]
2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^^^^^^^^ PLW0108
3 |
4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
|
= help: Inline function call

ℹ Safe fix
1 1 | _ = lambda: print() # [unnecessary-lambda]
2 |-_ = lambda x, y: min(x, y) # [unnecessary-lambda]
2 |+_ = min # [unnecessary-lambda]
3 3 |
4 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]

unnecessary_lambda.py:4:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
unnecessary_lambda.py:4:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
3 |
Expand All @@ -26,26 +43,59 @@ unnecessary_lambda.py:4:5: PLW0108 Lambda may be unnecessary; consider inlining
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
|
= help: Inline function call

unnecessary_lambda.py:5:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
ℹ Safe fix
1 1 | _ = lambda: print() # [unnecessary-lambda]
2 2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
3 3 |
4 |-_ = lambda *args: f(*args) # [unnecessary-lambda]
4 |+_ = f # [unnecessary-lambda]
5 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
7 7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]

unnecessary_lambda.py:5:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
|
= help: Inline function call

ℹ Safe fix
2 2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
3 3 |
4 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 |-_ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
5 |+_ = f # [unnecessary-lambda]
6 6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
7 7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
8 8 |

unnecessary_lambda.py:6:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
unnecessary_lambda.py:6:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
|
= help: Inline function call

unnecessary_lambda.py:7:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
ℹ Safe fix
3 3 |
4 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 |-_ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
6 |+_ = f # [unnecessary-lambda]
7 7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
8 8 |
9 9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]

unnecessary_lambda.py:7:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
Expand All @@ -54,23 +104,56 @@ unnecessary_lambda.py:7:5: PLW0108 Lambda may be unnecessary; consider inlining
8 |
9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
|
= help: Inline function call

ℹ Safe fix
4 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
7 |-_ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
7 |+_ = f # [unnecessary-lambda]
8 8 |
9 9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
10 10 | _ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda]

unnecessary_lambda.py:9:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
unnecessary_lambda.py:9:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
8 |
9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
10 | _ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda]
|
= help: Inline function call

unnecessary_lambda.py:10:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
ℹ Unsafe fix
6 6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
7 7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
8 8 |
9 |-_ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
9 |+_ = f(lambda x: x) # [unnecessary-lambda]
10 10 | _ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda]
11 11 |
12 12 | # default value in lambda parameters

unnecessary_lambda.py:10:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
10 | _ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
11 |
12 | # default value in lambda parameters
|
= help: Inline function call

ℹ Unsafe fix
7 7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
8 8 |
9 9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
10 |-_ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda]
10 |+_ = f(lambda x, y: x + y) # [unnecessary-lambda]
11 11 |
12 12 | # default value in lambda parameters
13 13 | _ = lambda x=42: print(x)


0 comments on commit 9724dfd

Please sign in to comment.