Make unpacked assignment a flag rather than a BindingKind (#8595)

## Summary

An assignment can be _both_ (e.g.) a loop variable _and_ assigned via
unpacking. In other words, unpacking is a quality of an assignment, not
a _kind_.
This commit is contained in:
Charlie Marsh 2023-11-09 18:41:30 -08:00 committed by GitHub
parent 4ebd0bd31e
commit 0ac124acef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 27 additions and 51 deletions

View file

@ -1615,38 +1615,23 @@ impl<'a> Checker<'a> {
fn handle_node_store(&mut self, id: &'a str, expr: &Expr) {
let parent = self.semantic.current_statement();
let mut flags = BindingFlags::empty();
if helpers::is_unpacking_assignment(parent, expr) {
flags.insert(BindingFlags::UNPACKED_ASSIGNMENT);
}
// Match the left-hand side of an annotated assignment, like `x` in `x: int`.
if matches!(
parent,
Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. })
) && !self.semantic.in_annotation()
{
self.add_binding(
id,
expr.range(),
BindingKind::Annotation,
BindingFlags::empty(),
);
self.add_binding(id, expr.range(), BindingKind::Annotation, flags);
return;
}
if parent.is_for_stmt() {
self.add_binding(
id,
expr.range(),
BindingKind::LoopVar,
BindingFlags::empty(),
);
return;
}
if helpers::is_unpacking_assignment(parent, expr) {
self.add_binding(
id,
expr.range(),
BindingKind::UnpackedAssignment,
BindingFlags::empty(),
);
self.add_binding(id, expr.range(), BindingKind::LoopVar, flags);
return;
}
@ -1681,7 +1666,6 @@ impl<'a> Checker<'a> {
let (all_names, all_flags) =
extract_all_names(parent, |name| self.semantic.is_builtin(name));
let mut flags = BindingFlags::empty();
if all_flags.intersects(DunderAllFlags::INVALID_OBJECT) {
flags |= BindingFlags::INVALID_ALL_OBJECT;
}
@ -1705,21 +1689,11 @@ impl<'a> Checker<'a> {
.current_expressions()
.any(Expr::is_named_expr_expr)
{
self.add_binding(
id,
expr.range(),
BindingKind::NamedExprAssignment,
BindingFlags::empty(),
);
self.add_binding(id, expr.range(), BindingKind::NamedExprAssignment, flags);
return;
}
self.add_binding(
id,
expr.range(),
BindingKind::Assignment,
BindingFlags::empty(),
);
self.add_binding(id, expr.range(), BindingKind::Assignment, flags);
}
fn handle_node_delete(&mut self, expr: &'a Expr) {

View file

@ -245,7 +245,6 @@ impl Renamer {
| BindingKind::Argument
| BindingKind::TypeParam
| BindingKind::NamedExprAssignment
| BindingKind::UnpackedAssignment
| BindingKind::Assignment
| BindingKind::BoundException
| BindingKind::LoopVar

View file

@ -46,7 +46,6 @@ pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resoluti
BindingKind::Annotation
| BindingKind::Assignment
| BindingKind::NamedExprAssignment
| BindingKind::UnpackedAssignment
| BindingKind::LoopVar
| BindingKind::Global
| BindingKind::Nonlocal(_) => Resolution::RelevantLocal,

View file

@ -322,10 +322,9 @@ pub(crate) fn unused_variable(checker: &Checker, scope: &Scope, diagnostics: &mu
.bindings()
.map(|(name, binding_id)| (name, checker.semantic().binding(binding_id)))
.filter_map(|(name, binding)| {
if (binding.kind.is_assignment()
|| binding.kind.is_named_expr_assignment()
|| (matches!(checker.settings.preview, PreviewMode::Enabled)
&& binding.kind.is_unpacked_assignment()))
if (binding.kind.is_assignment() || binding.kind.is_named_expr_assignment())
&& (!binding.is_unpacked_assignment()
|| matches!(checker.settings.preview, PreviewMode::Enabled))
&& !binding.is_nonlocal()
&& !binding.is_global()
&& !binding.is_used()

View file

@ -49,7 +49,6 @@ pub(crate) fn non_ascii_name(binding: &Binding, locator: &Locator) -> Option<Dia
BindingKind::Annotation => Kind::Annotation,
BindingKind::Argument => Kind::Argument,
BindingKind::NamedExprAssignment => Kind::NamedExprAssignment,
BindingKind::UnpackedAssignment => Kind::UnpackedAssignment,
BindingKind::Assignment => Kind::Assignment,
BindingKind::TypeParam => Kind::TypeParam,
BindingKind::LoopVar => Kind::LoopVar,
@ -85,7 +84,6 @@ enum Kind {
Annotation,
Argument,
NamedExprAssignment,
UnpackedAssignment,
Assignment,
TypeParam,
LoopVar,
@ -102,7 +100,6 @@ impl fmt::Display for Kind {
Kind::Annotation => f.write_str("Annotation"),
Kind::Argument => f.write_str("Argument"),
Kind::NamedExprAssignment => f.write_str("Variable"),
Kind::UnpackedAssignment => f.write_str("Variable"),
Kind::Assignment => f.write_str("Variable"),
Kind::TypeParam => f.write_str("Type parameter"),
Kind::LoopVar => f.write_str("Variable"),

View file

@ -87,6 +87,12 @@ impl<'a> Binding<'a> {
self.flags.intersects(BindingFlags::INVALID_ALL_OBJECT)
}
/// Return `true` if this [`Binding`] represents an unpacked assignment (e.g., `x` in
/// `(x, y) = 1, 2`).
pub const fn is_unpacked_assignment(&self) -> bool {
self.flags.intersects(BindingFlags::UNPACKED_ASSIGNMENT)
}
/// Return `true` if this [`Binding`] represents an unbound variable
/// (e.g., `x` in `x = 1; del x`).
pub const fn is_unbound(&self) -> bool {
@ -212,7 +218,7 @@ impl<'a> Binding<'a> {
bitflags! {
/// Flags on a [`Binding`].
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct BindingFlags: u8 {
pub struct BindingFlags: u16 {
/// The binding represents an explicit re-export.
///
/// For example, the binding could be `FastAPI` in:
@ -284,6 +290,14 @@ bitflags! {
/// _T = "This is a private variable"
/// ```
const PRIVATE_DECLARATION = 1 << 7;
/// The binding represents an unpacked assignment.
///
/// For example, the binding could be `x` in:
/// ```python
/// (x, y) = 1, 2
/// ```
const UNPACKED_ASSIGNMENT = 1 << 8;
}
}
@ -393,12 +407,6 @@ pub enum BindingKind<'a> {
/// ```
NamedExprAssignment,
/// A binding for a unpacking-based assignment, like `x` in:
/// ```python
/// x, y = (1, 2)
/// ```
UnpackedAssignment,
/// A binding for a "standard" assignment, like `x` in:
/// ```python
/// x = 1