mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
[pylint
] Fix PLW0108
autofix introducing a syntax error when the lambda's body contains an assignment expression (#18678)
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? --> ## Summary This PR also supresses the fix if the assignment expression target shadows one of the lambda's parameters. Fixes #18675 <!-- What's the purpose of the change? What does it do, and why? --> ## Test Plan Add regression tests. <!-- How was it tested? -->
This commit is contained in:
parent
32c54189cb
commit
a1579d82d0
3 changed files with 64 additions and 12 deletions
|
@ -57,3 +57,7 @@ _ = lambda x: z(lambda y: x + y)(x)
|
|||
# lambda uses an additional keyword
|
||||
_ = lambda *args: f(*args, y=1)
|
||||
_ = lambda *args: f(*args, y=x)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18675
|
||||
_ = lambda x: (string := str)(x)
|
||||
_ = lambda x: ((x := 1) and str)(x)
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, ExprLambda, Parameter, ParameterWithDef
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::{AlwaysFixableViolation, Applicability, Edit, Fix};
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `lambda` definitions that consist of a single function call
|
||||
|
@ -46,14 +46,16 @@ use crate::{AlwaysFixableViolation, Applicability, Edit, Fix};
|
|||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct UnnecessaryLambda;
|
||||
|
||||
impl AlwaysFixableViolation for UnnecessaryLambda {
|
||||
impl Violation for UnnecessaryLambda {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Lambda may be unnecessary; consider inlining inner function".to_string()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
"Inline function call".to_string()
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Inline function call".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +201,7 @@ pub(crate) fn unnecessary_lambda(checker: &Checker, lambda: &ExprLambda) {
|
|||
finder.names
|
||||
};
|
||||
|
||||
for name in names {
|
||||
for name in &names {
|
||||
if let Some(binding_id) = checker.semantic().resolve_name(name) {
|
||||
let binding = checker.semantic().binding(binding_id);
|
||||
if checker.semantic().is_current_scope(binding.scope) {
|
||||
|
@ -209,13 +211,33 @@ pub(crate) fn unnecessary_lambda(checker: &Checker, lambda: &ExprLambda) {
|
|||
}
|
||||
|
||||
let mut diagnostic = checker.report_diagnostic(UnnecessaryLambda, lambda.range());
|
||||
diagnostic.set_fix(Fix::applicable_edit(
|
||||
Edit::range_replacement(
|
||||
checker.locator().slice(func.as_ref()).to_string(),
|
||||
lambda.range(),
|
||||
),
|
||||
Applicability::Unsafe,
|
||||
));
|
||||
// Suppress the fix if the assignment expression target shadows one of the lambda's parameters.
|
||||
// This is necessary to avoid introducing a change in the behavior of the program.
|
||||
for name in names {
|
||||
if let Some(binding_id) = checker.semantic().lookup_symbol(name.id()) {
|
||||
let binding = checker.semantic().binding(binding_id);
|
||||
if checker
|
||||
.semantic()
|
||||
.current_scope()
|
||||
.shadowed_binding(binding_id)
|
||||
.is_some()
|
||||
&& binding
|
||||
.expression(checker.semantic())
|
||||
.is_some_and(Expr::is_named_expr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
if func.is_named_expr() {
|
||||
format!("({})", checker.locator().slice(func.as_ref()))
|
||||
} else {
|
||||
checker.locator().slice(func.as_ref()).to_string()
|
||||
},
|
||||
lambda.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
/// Identify all `Expr::Name` nodes in an AST.
|
||||
|
|
|
@ -155,3 +155,29 @@ unnecessary_lambda.py:10:5: PLW0108 [*] Lambda may be unnecessary; consider inli
|
|||
11 11 |
|
||||
12 12 | # default value in lambda parameters
|
||||
13 13 | _ = lambda x=42: print(x)
|
||||
|
||||
unnecessary_lambda.py:62:5: PLW0108 [*] Lambda may be unnecessary; consider inlining inner function
|
||||
|
|
||||
61 | # https://github.com/astral-sh/ruff/issues/18675
|
||||
62 | _ = lambda x: (string := str)(x)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
|
||||
63 | _ = lambda x: ((x := 1) and str)(x)
|
||||
|
|
||||
= help: Inline function call
|
||||
|
||||
ℹ Unsafe fix
|
||||
59 59 | _ = lambda *args: f(*args, y=x)
|
||||
60 60 |
|
||||
61 61 | # https://github.com/astral-sh/ruff/issues/18675
|
||||
62 |-_ = lambda x: (string := str)(x)
|
||||
62 |+_ = (string := str)
|
||||
63 63 | _ = lambda x: ((x := 1) and str)(x)
|
||||
|
||||
unnecessary_lambda.py:63:5: PLW0108 Lambda may be unnecessary; consider inlining inner function
|
||||
|
|
||||
61 | # https://github.com/astral-sh/ruff/issues/18675
|
||||
62 | _ = lambda x: (string := str)(x)
|
||||
63 | _ = lambda x: ((x := 1) and str)(x)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0108
|
||||
|
|
||||
= help: Inline function call
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue