Repository: astral-sh/ruff
File: crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs (Rust)

Code before:
    )));
}

/// Returns true if the binding is a dictionary where each key is a tuple with two elements.
fn is_dict_key_tuple_with_two_elements(binding: &Binding, semantic: &SemanticModel) -> bool {
    let Some(statement) = binding.statement(semantic) else {
        return false;
    };

    let (dict_expr, annotation) = match statement {
        Stmt::Assign(assign_stmt) => {
            let Expr::Dict(dict_expr) = &*assign_stmt.value else {
                return false;
            };
            (dict_expr, None)
        }
        Stmt::AnnAssign(ann_assign_stmt) => {
            let Some(value) = ann_assign_stmt.value.as_ref() else {
                return false;
            };
            let Expr::Dict(dict_expr) = value.as_ref() else {
                return false;
            };
            (dict_expr, Some(ann_assign_stmt.annotation.as_ref()))
        }
        _ => return false,
    };

    // Check if dict is empty
    let is_empty = dict_expr.iter_keys().next().is_none();

    if is_empty {
        // For empty dicts, check type annotation
        if let Some(annotation) = annotation {
            // Check if annotation is dict[tuple[...], ...] where tuple has 2 elements
            return is_annotation_dict_with_tuple_keys(annotation, semantic);
        }
        // Empty dict without annotation should allow the fix
        return false;
    }

    // For non-empty dicts, check if all keys are 2-tuples
    dict_expr
        .iter_keys()
        .all(|key| matches!(key, Some(Expr::Tuple(tuple)) if tuple.len() == 2))
}

/// Returns true if the annotation is `dict[tuple[T1, T2], ...]` where tuple has exactly 2 elements.
fn is_annotation_dict_with_tuple_keys(annotation: &Expr, semantic: &SemanticModel) -> bool {
    // Handle stringized annotations
    let annotation = if let Expr::StringLiteral(_) = annotation {

Review comment (suggestion):
```suggestion
    let (value, annotation) = match statement {
        Stmt::Assign(assign_stmt) => {
            (assign_stmt.value, None)
        }
        Stmt::AnnAssign(ann_assign_stmt) => {
            let Some(value) = ann_assign_stmt.value.as_ref() else {
                return false;
            };
            (value, Some(ann_assign_stmt.annotation.as_ref()))
        }
        _ => return false,
```

It might even be nicer with something like:

```rust
        Stmt::AnnAssign(ast::StmtAnnAssign{ value: Some(value), annotation, .. }) => {
            (value, Some(annotation))
        }
```

and then we can keep/share the `Expr::Dict` check below instead of duplicating it in both match arms.

Code after:
    )));
}

/// Returns true if the binding is a dictionary where each key is a tuple with two elements.
fn is_dict_key_tuple_with_two_elements(binding: &Binding, semantic: &SemanticModel) -> bool {
    let Some(statement) = binding.statement(semantic) else {
        return false;
    };

    let (value, annotation) = match statement {
        Stmt::Assign(assign_stmt) => (assign_stmt.value.as_ref(), None),
        Stmt::AnnAssign(ast::StmtAnnAssign {
            value: Some(value),
            annotation,
            ..
        }) => (value.as_ref(), Some(annotation.as_ref())),
        _ => return false,
    };

    let Expr::Dict(dict_expr) = value else {
        return false;
    };

    // Check if dict is empty
    let is_empty = dict_expr.is_empty();

    if is_empty {
        // For empty dicts, check type annotation
        return annotation
            .is_some_and(|annotation| is_annotation_dict_with_tuple_keys(annotation, semantic));
    }

    // For non-empty dicts, check if all keys are 2-tuples
    dict_expr
        .iter_keys()
        .all(|key| matches!(key, Some(Expr::Tuple(tuple)) if tuple.len() == 2))
}

/// Returns true if the annotation is `dict[tuple[T1, T2], ...]` where tuple has exactly 2 elements.
fn is_annotation_dict_with_tuple_keys(annotation: &Expr, semantic: &SemanticModel) -> bool {
    // Check if it's a subscript: dict[...]
    let Expr::Subscript(subscript) = annotation else {
        return false;
    };

    // Check if it's dict or typing.Dict
    if !semantic.match_builtin_expr(subscript.value.as_ref(), "dict")
        && !semantic.match_typing_expr(subscript.value.as_ref(), "Dict")
    {
        return false;
    }