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

Code before:

        Some(format!(
            "Use `{first_var}, {second_var} = {second_var}, {first_var}` 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, stmt_c.range.end());
            if is_variable_reused_later {
                continue;
            }

            let diagnostic = SwapWithTemporaryVariable {
                first_var: stmt_b.target,
                second_var: stmt_c.target,
            };
            let edit_range = TextRange::new(stmt_a.range.start(), stmt_c.range.end());
            let edit = Edit::range_replacement(
                format!(
                    "{0}, {1} = {1}, {0}",
                    &diagnostic.first_var, &diagnostic.second_var
                ),
                edit_range,
            );
            let mut diagnostic_guard = checker.report_diagnostic(diagnostic, edit_range);

            // The quick fix would remove comments, hence it's unsafe if there are any comments in the relevant code part.
            let applicability = if checker.comment_ranges().intersects(edit.range()) {
                Applicability::Unsafe
            } else {
                Applicability::Safe
            };
            diagnostic_guard.set_fix(Fix::applicable_edit(edit, applicability));

Review comment (suggestion):
The statement itself should implement `Ranged`:


```suggestion
                is_variable_read_after(checker, &stmt_a, stmt_c.end());
```

Code after:
    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;
            let edit_range = TextRange::new(stmt_a.start(), stmt_c.end());
            let edit = Edit::range_replacement(
                format!("{first}, {second} = {second}, {first}"),
                edit_range,
            );

            // The quick fix would remove comments, hence it's unsafe if there are any comments in the relevant code part.
            let applicability = if checker.comment_ranges().intersects(edit.range()) {
                Applicability::Unsafe
            } else {
                Applicability::Safe
            };

            checker
                .report_diagnostic(SwapWithTemporaryVariable { first, second }, edit_range)
                .set_fix(Fix::applicable_edit(edit, applicability));
        }
    }
}

/// Match consecutive assignment statements.
///
/// Also see the `repeated_append` rule for a similar use case.
fn match_consecutive_assignments<'a>(
    semantic: &'a SemanticModel<'a>,
    scope_id: ScopeId,
    binding_id: BindingId,
) -> Option<(