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

Code before:
/// ## 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;
        };

        // Detect patterns like:
        // temp = x
        // x = y
        // y = temp
        if stmt_a.value_var_name == stmt_b.target_var_name
            && stmt_b.value_var_name == stmt_c.target_var_name
            && stmt_a.target_var_name == stmt_c.value_var_name

Review comment (suggestion):
```suggestion
        format!("Consider swapping `{first_var}` and `{second_var}` by using tuple unpacking")
```

Code after:
/// ## 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 -
            // in this case the automatic hotfix would result in broken code, because
            // this later read would attempt to read from a variable that no longer exists
            let is_variable_reused_later = is_variable_read_after(checker, &stmt_a);
            if is_variable_reused_later {
                continue;
            }

            let first = stmt_b.target;
            let second = stmt_c.target;