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

Code before:
///
/// Use instead:
/// ```python
/// def function(x, y):
///     if x > y:
///         x, y = y, x
///     assert x <= y
/// ```
///
/// ## Fix safety
/// The rule's fix is marked as safe, unless it contains comments. In this
/// exception case, applying the quick fix would remove comments between the
/// assignment statements.
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.14.11")]
pub(crate) struct SwapWithTemporaryVariable<'a> {
    first_var: &'a Name,
    second_var: &'a Name,
}

#[derive(Eq, PartialEq, Debug, Clone)]
struct VarToVarAssignment {
    target_var_name: Name,
    value_var_name: Name,
    range: TextRange,
}

impl AlwaysFixableViolation for SwapWithTemporaryVariable<'_> {
    #[derive_message_formats]
    fn message(&self) -> String {
        let SwapWithTemporaryVariable {
            first_var,
            second_var,
        } = self;
        format!(r#"Consider swapping `{first_var}` and `{second_var}` by using tuple unpacking"#,)
    }

    fn fix_title(&self) -> String {
        let SwapWithTemporaryVariable {
            first_var,
            second_var,
        } = self;
        format!("Use `{first_var}, {second_var} = {second_var}, {first_var}` instead")
    }
}

pub(crate) fn swap_with_temporary_variable(checker: &Checker, stmts: &[Stmt]) {
    for stmt_sequence in stmts.iter().map(var_to_var_assignment).tuple_windows() {
        // if unwrapping fails, one of the statements hasn't been a var to var assignment
        let (Some(stmt_a), Some(stmt_b), Some(stmt_c)) = stmt_sequence else {
            continue;

Review comment (suggestion):
A couple of nits on the naming here:

```suggestion
struct SwapAssignment {
    target: Name,
    value: Name,
    range: TextRange,
}
```

Or something else is fine too, we just typically try to avoid abbreviations like `var`. Can you put this below the main rule implementation just above your `var_to_var_assignment` function? That function could also be a `from_stmt` method on this struct, which would fix the `var` abbreviations in that name too.

Code after:
///
/// Use instead:
/// ```python
/// def function(x, y):
///     if x > y:
///         x, y = y, x
///     assert x <= y
/// ```
///
/// ## Fix safety
/// The rule's fix is marked as safe, unless the replacement range contains comments
/// that would be removed.
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "NEXT_RUFF_VERSION")]
pub(crate) struct SwapWithTemporaryVariable<'a> {
    first: &'a Name,
    second: &'a Name,
}

impl Violation for SwapWithTemporaryVariable<'_> {
    const FIX_AVAILABILITY: crate::FixAvailability = crate::FixAvailability::Sometimes;

    #[derive_message_formats]
    fn message(&self) -> String {
        "Unnecessary temporary variable".to_string()
    }

    fn fix_title(&self) -> Option<String> {
        let SwapWithTemporaryVariable { first, second } = self;

        Some(format!(
            "Use `{first}, {second} = {second}, {first}` instead"
        ))
    }
}

pub(crate) fn swap_with_temporary_variable(checker: &Checker, scope_id: ScopeId, scope: &Scope) {
    let consecutive_assignments = scope.binding_ids().filter_map(|binding_id| {
        match_consecutive_assignments(checker.semantic(), scope_id, binding_id)
    });

    for (stmt_a, stmt_b, stmt_c) in consecutive_assignments {
        // Detect patterns like:
        // temp = x
        // x = y
        // y = temp
        if stmt_a.value == stmt_b.target
            && stmt_b.value == stmt_c.target
            && stmt_a.target == stmt_c.value
        {
            // check whether there is any later read reference to the temporary variable -