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

Code before:
        let (import_edit, binding) = checker.importer().get_or_import_symbol(
            &ImportRequest::import("pathlib", "Path"),
            call.start(),
            checker.semantic(),
        )?;

        let replacement = if is_pathlib_path_call(checker, arg) {
            format!("{arg_code}.{attr}")
        } else {
            format!("{binding}({arg_code}).{attr}")
        };

        let edit = Edit::range_replacement(replacement, range);

        let fix = match applicability {
            Some(Applicability::Unsafe) => Fix::unsafe_edits(edit, [import_edit]),
            _ => {
                let determined_applicability = if checker.comment_ranges().intersects(range) {
                    Applicability::Unsafe
                } else {
                    // applicability is Some(Applicability::Safe), use it
                    applicability.unwrap_or(Applicability::Safe)
                };
                Fix::applicable_edits(edit, [import_edit], determined_applicability)
            }
        };

        Ok(fix)
    });
}

pub(crate) fn get_name_expr(expr: &Expr) -> Option<&ast::ExprName> {
    match expr {
        Expr::Name(name) => Some(name),
        Expr::Call(ExprCall { func, .. }) => get_name_expr(func),
        _ => None,
    }
}

/// Returns `true` if the given expression looks like a file descriptor, i.e., if it is an integer.
pub(crate) fn is_file_descriptor(expr: &Expr, semantic: &SemanticModel) -> bool {
    if matches!(
        expr,
        Expr::NumberLiteral(ast::ExprNumberLiteral {
            value: ast::Number::Int(_),
            ..
        })
    ) {
        return true;
    }


Review comment (suggestion):
See my other comment about the `Option`, but I think we should also do something more like this to account for the possibility of `DisplayOnly` fixes, which shouldn't be "elevated" to unsafe if there are comments. This is unlikely to happen in these rules, but it's not really more code to handle it properly.

```suggestion
        let applicability = match applicability {
            Applicability::DisplayOnly => Applicability::DisplayOnly,
            _ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
            _ => applicability,
        };
```

Code after:
        let (import_edit, binding) = checker.importer().get_or_import_symbol(
            &ImportRequest::import("pathlib", "Path"),
            call.start(),
            checker.semantic(),
        )?;

        let replacement = if is_pathlib_path_call(checker, arg) {
            format!("{arg_code}.{attr}")
        } else {
            format!("{binding}({arg_code}).{attr}")
        };

        let edit = Edit::range_replacement(replacement, range);

        let applicability = match applicability {
            Applicability::DisplayOnly => Applicability::DisplayOnly,
            _ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
            _ => applicability,
        };

        let fix = Fix::applicable_edits(edit, [import_edit], applicability);

        Ok(fix)
    });
}

pub(crate) fn get_name_expr(expr: &Expr) -> Option<&ast::ExprName> {
    match expr {
        Expr::Name(name) => Some(name),
        Expr::Call(ExprCall { func, .. }) => get_name_expr(func),
        _ => None,
    }
}

/// Returns `true` if the given expression looks like a file descriptor, i.e., if it is an integer.
pub(crate) fn is_file_descriptor(expr: &Expr, semantic: &SemanticModel) -> bool {
    if matches!(
        expr,
        Expr::NumberLiteral(ast::ExprNumberLiteral {
            value: ast::Number::Int(_),
            ..
        })
    ) {
        return true;
    }

    let Some(name) = get_name_expr(expr) else {
        return false;
    };

    let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else {