Improve the message for PLW2901: use "outer" and "inner" judiciously (#3263)

This commit is contained in:
Matthew Lloyd 2023-02-28 11:33:01 -05:00 committed by GitHub
parent f5f09b489b
commit 1c79dff3bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,3 +1,10 @@
use std::{fmt, iter};
use rustpython_parser::ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind, Withitem};
use serde::{Deserialize, Serialize};
use ruff_macros::{define_violation, derive_message_formats};
use crate::ast::comparable::ComparableExpr; use crate::ast::comparable::ComparableExpr;
use crate::ast::helpers::unparse_expr; use crate::ast::helpers::unparse_expr;
use crate::ast::types::{Node, Range}; use crate::ast::types::{Node, Range};
@ -7,28 +14,49 @@ use crate::checkers::ast::Checker;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::settings::hashable::HashableRegex; use crate::settings::hashable::HashableRegex;
use crate::violation::Violation; use crate::violation::Violation;
use ruff_macros::{define_violation, derive_message_formats};
use rustpython_parser::ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind, Withitem};
use serde::{Deserialize, Serialize};
use std::{fmt, iter};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub enum BindingKind { pub enum OuterBindingKind {
For,
With,
}
impl fmt::Display for OuterBindingKind {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
OuterBindingKind::For => fmt.write_str("`for` loop"),
OuterBindingKind::With => fmt.write_str("`with` statement"),
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub enum InnerBindingKind {
For, For,
With, With,
Assignment, Assignment,
} }
impl fmt::Display for BindingKind { impl fmt::Display for InnerBindingKind {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
BindingKind::For => fmt.write_str("for loop"), InnerBindingKind::For => fmt.write_str("`for` loop"),
BindingKind::With => fmt.write_str("with statement"), InnerBindingKind::With => fmt.write_str("`with` statement"),
BindingKind::Assignment => fmt.write_str("assignment"), InnerBindingKind::Assignment => fmt.write_str("assignment"),
} }
} }
} }
impl PartialEq<InnerBindingKind> for OuterBindingKind {
fn eq(&self, other: &InnerBindingKind) -> bool {
matches!(
(self, other),
(OuterBindingKind::For, InnerBindingKind::For)
| (OuterBindingKind::With, InnerBindingKind::With)
)
}
}
define_violation!( define_violation!(
/// ## What it does /// ## What it does
/// Checks for variables defined in `for` loops and `with` statements that /// Checks for variables defined in `for` loops and `with` statements that
@ -68,8 +96,8 @@ define_violation!(
/// ``` /// ```
pub struct RedefinedLoopName { pub struct RedefinedLoopName {
pub name: String, pub name: String,
pub outer_kind: BindingKind, pub outer_kind: OuterBindingKind,
pub inner_kind: BindingKind, pub inner_kind: InnerBindingKind,
} }
); );
impl Violation for RedefinedLoopName { impl Violation for RedefinedLoopName {
@ -80,18 +108,42 @@ impl Violation for RedefinedLoopName {
outer_kind, outer_kind,
inner_kind, inner_kind,
} = self; } = self;
// Prefix the nouns describing the outer and inner kinds with "outer" and "inner"
// to better distinguish them, but to avoid confusion, only do so if the outer and inner
// kinds are equal. For example, instead of:
//
// "Outer `for` loop variable `i` overwritten by inner assignment target."
//
// We have:
//
// "`for` loop variable `i` overwritten by assignment target."
//
// While at the same time, we have:
//
// "Outer `for` loop variable `i` overwritten by inner `for` loop target."
// "Outer `with` statement variable `f` overwritten by inner `with` statement target."
if outer_kind == inner_kind {
format!("Outer {outer_kind} variable `{name}` overwritten by inner {inner_kind} target") format!("Outer {outer_kind} variable `{name}` overwritten by inner {inner_kind} target")
} else {
format!("{outer_kind} variable `{name}` overwritten by {inner_kind} target")
}
} }
} }
struct ExprWithBindingKind<'a> { struct ExprWithOuterBindingKind<'a> {
expr: &'a Expr, expr: &'a Expr,
binding_kind: BindingKind, binding_kind: OuterBindingKind,
}
struct ExprWithInnerBindingKind<'a> {
expr: &'a Expr,
binding_kind: InnerBindingKind,
} }
struct InnerForWithAssignTargetsVisitor<'a> { struct InnerForWithAssignTargetsVisitor<'a> {
dummy_variable_rgx: &'a HashableRegex, dummy_variable_rgx: &'a HashableRegex,
assignment_targets: Vec<ExprWithBindingKind<'a>>, assignment_targets: Vec<ExprWithInnerBindingKind<'a>>,
} }
impl<'a, 'b> Visitor<'b> for InnerForWithAssignTargetsVisitor<'a> impl<'a, 'b> Visitor<'b> for InnerForWithAssignTargetsVisitor<'a>
@ -105,9 +157,9 @@ where
StmtKind::For { target, .. } | StmtKind::AsyncFor { target, .. } => { StmtKind::For { target, .. } | StmtKind::AsyncFor { target, .. } => {
self.assignment_targets.extend( self.assignment_targets.extend(
assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| { assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| {
ExprWithBindingKind { ExprWithInnerBindingKind {
expr, expr,
binding_kind: BindingKind::For, binding_kind: InnerBindingKind::For,
} }
}), }),
); );
@ -116,9 +168,9 @@ where
StmtKind::With { items, .. } => { StmtKind::With { items, .. } => {
self.assignment_targets.extend( self.assignment_targets.extend(
assignment_targets_from_with_items(items, self.dummy_variable_rgx).map( assignment_targets_from_with_items(items, self.dummy_variable_rgx).map(
|expr| ExprWithBindingKind { |expr| ExprWithInnerBindingKind {
expr, expr,
binding_kind: BindingKind::With, binding_kind: InnerBindingKind::With,
}, },
), ),
); );
@ -127,9 +179,9 @@ where
StmtKind::Assign { targets, .. } => { StmtKind::Assign { targets, .. } => {
self.assignment_targets.extend( self.assignment_targets.extend(
assignment_targets_from_assign_targets(targets, self.dummy_variable_rgx).map( assignment_targets_from_assign_targets(targets, self.dummy_variable_rgx).map(
|expr| ExprWithBindingKind { |expr| ExprWithInnerBindingKind {
expr, expr,
binding_kind: BindingKind::Assignment, binding_kind: InnerBindingKind::Assignment,
}, },
), ),
); );
@ -137,9 +189,9 @@ where
StmtKind::AugAssign { target, .. } | StmtKind::AnnAssign { target, .. } => { StmtKind::AugAssign { target, .. } | StmtKind::AnnAssign { target, .. } => {
self.assignment_targets.extend( self.assignment_targets.extend(
assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| { assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| {
ExprWithBindingKind { ExprWithInnerBindingKind {
expr, expr,
binding_kind: BindingKind::Assignment, binding_kind: InnerBindingKind::Assignment,
} }
}), }),
); );
@ -241,11 +293,11 @@ pub fn redefined_loop_name<'a, 'b>(checker: &'a mut Checker<'b>, node: &Node<'b>
Node::Stmt(stmt) => match &stmt.node { Node::Stmt(stmt) => match &stmt.node {
// With. // With.
StmtKind::With { items, body, .. } => { StmtKind::With { items, body, .. } => {
let outer_assignment_targets: Vec<ExprWithBindingKind<'a>> = let outer_assignment_targets: Vec<ExprWithOuterBindingKind<'a>> =
assignment_targets_from_with_items(items, &checker.settings.dummy_variable_rgx) assignment_targets_from_with_items(items, &checker.settings.dummy_variable_rgx)
.map(|expr| ExprWithBindingKind { .map(|expr| ExprWithOuterBindingKind {
expr, expr,
binding_kind: BindingKind::With, binding_kind: OuterBindingKind::With,
}) })
.collect(); .collect();
let mut visitor = InnerForWithAssignTargetsVisitor { let mut visitor = InnerForWithAssignTargetsVisitor {
@ -259,11 +311,11 @@ pub fn redefined_loop_name<'a, 'b>(checker: &'a mut Checker<'b>, node: &Node<'b>
} }
// For and async for. // For and async for.
StmtKind::For { target, body, .. } | StmtKind::AsyncFor { target, body, .. } => { StmtKind::For { target, body, .. } | StmtKind::AsyncFor { target, body, .. } => {
let outer_assignment_targets: Vec<ExprWithBindingKind<'a>> = let outer_assignment_targets: Vec<ExprWithOuterBindingKind<'a>> =
assignment_targets_from_expr(target, &checker.settings.dummy_variable_rgx) assignment_targets_from_expr(target, &checker.settings.dummy_variable_rgx)
.map(|expr| ExprWithBindingKind { .map(|expr| ExprWithOuterBindingKind {
expr, expr,
binding_kind: BindingKind::For, binding_kind: OuterBindingKind::For,
}) })
.collect(); .collect();
let mut visitor = InnerForWithAssignTargetsVisitor { let mut visitor = InnerForWithAssignTargetsVisitor {