Implement autofix for unnecessary-lambda (PLW0108) (#8621)

Closes https://github.com/astral-sh/ruff/issues/8618.
This commit is contained in:
Charlie Marsh 2023-11-11 15:34:02 -08:00 committed by GitHub
parent d7144d6d8e
commit 9724dfd939
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 122 additions and 13 deletions

View file

@ -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_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{self as ast, visitor, Expr, ExprLambda, Parameter, ParameterWithDefault}; use ruff_python_ast::{self as ast, visitor, Expr, ExprLambda, Parameter, ParameterWithDefault};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -24,14 +25,29 @@ use crate::checkers::ast::Checker;
/// ```python /// ```python
/// df.apply(str) /// 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] #[violation]
pub struct UnnecessaryLambda; pub struct UnnecessaryLambda;
impl Violation for UnnecessaryLambda { impl AlwaysFixableViolation for UnnecessaryLambda {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!("Lambda may be unnecessary; consider inlining inner function") format!("Lambda may be unnecessary; consider inlining inner function")
} }
fn fix_title(&self) -> String {
"Inline function call".to_string()
}
} }
/// PLW0108 /// PLW0108
@ -184,9 +200,19 @@ pub(crate) fn unnecessary_lambda(checker: &mut Checker, lambda: &ExprLambda) {
} }
} }
checker let mut diagnostic = Diagnostic::new(UnnecessaryLambda, lambda.range());
.diagnostics diagnostic.set_fix(Fix::applicable_edit(
.push(Diagnostic::new(UnnecessaryLambda, lambda.range())); 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. /// Identify all `Expr::Name` nodes in an AST.

View file

@ -1,14 +1,22 @@
--- ---
source: crates/ruff_linter/src/rules/pylint/mod.rs 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] 1 | _ = lambda: print() # [unnecessary-lambda]
| ^^^^^^^^^^^^^^^ PLW0108 | ^^^^^^^^^^^^^^^ PLW0108
2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda] 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] 1 | _ = lambda: print() # [unnecessary-lambda]
2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda] 2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
@ -16,8 +24,17 @@ unnecessary_lambda.py:2:5: PLW0108 Lambda may be unnecessary; consider inlining
3 | 3 |
4 | _ = lambda *args: f(*args) # [unnecessary-lambda] 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
| |
= help: Inline function call
unnecessary_lambda.py:4:5: PLW0108 Lambda may be unnecessary; consider inlining inner function 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
| |
2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda] 2 | _ = lambda x, y: min(x, y) # [unnecessary-lambda]
3 | 3 |
@ -26,8 +43,19 @@ unnecessary_lambda.py:4:5: PLW0108 Lambda may be unnecessary; consider inlining
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda] 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 | _ = lambda *args, **kwargs: f(*args, **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] 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda] 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
@ -35,8 +63,19 @@ unnecessary_lambda.py:5:5: PLW0108 Lambda may be unnecessary; consider inlining
6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda] 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 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
| |
= help: Inline function call
unnecessary_lambda.py:6:5: PLW0108 Lambda may be unnecessary; consider inlining inner function 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
| |
4 | _ = lambda *args: f(*args) # [unnecessary-lambda] 4 | _ = lambda *args: f(*args) # [unnecessary-lambda]
5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda] 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
@ -44,8 +83,19 @@ unnecessary_lambda.py:6:5: PLW0108 Lambda may be unnecessary; consider inlining
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda] 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] 5 | _ = lambda **kwargs: f(**kwargs) # [unnecessary-lambda]
6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda] 6 | _ = lambda *args, **kwargs: f(*args, **kwargs) # [unnecessary-lambda]
@ -54,8 +104,19 @@ unnecessary_lambda.py:7:5: PLW0108 Lambda may be unnecessary; consider inlining
8 | 8 |
9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda] 9 | _ = lambda x: f(lambda x: x)(x) # [unnecessary-lambda]
| |
= help: Inline function call
unnecessary_lambda.py:9:5: PLW0108 Lambda may be unnecessary; consider inlining inner function 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
| |
7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda] 7 | _ = lambda x, y, z, *args, **kwargs: f(x, y, z, *args, **kwargs) # [unnecessary-lambda]
8 | 8 |
@ -63,8 +124,19 @@ unnecessary_lambda.py:9:5: PLW0108 Lambda may be unnecessary; consider inlining
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
10 | _ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda] 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] 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 | _ = lambda x, y: f(lambda x, y: x + y)(x, y) # [unnecessary-lambda]
@ -72,5 +144,16 @@ unnecessary_lambda.py:10:5: PLW0108 Lambda may be unnecessary; consider inlining
11 | 11 |
12 | # default value in lambda parameters 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)