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

Code before:
            )
        })
}

/// Return `true` if `call_expr` is a method call on an [`ast::ExprStringLiteral`]
/// in which `literal` is one of the [`ast::StringLiteral`] parts.
///
/// For example: `expr` is a node representing the expression `"{foo}".format(foo="bar")`,
/// and `literal` is the node representing the string literal `"{foo}"`.
fn is_method_call_on_literal(call_expr: &ast::ExprCall, literal: &ast::StringLiteral) -> bool {
    let ast::Expr::Attribute(ast::ExprAttribute { value, .. }) = &*call_expr.func else {
        return false;
    };
    let ast::Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &**value else {
        return false;
    };
    value.as_slice().contains(literal)
}

/// Returns `true` if `literal` is likely an f-string with a missing `f` prefix.
/// See [`MissingFStringSyntax`] for the validation criteria.
fn should_be_fstring(
    literal: &ast::StringLiteral,
    locator: &Locator,
    semantic: &SemanticModel,
    checker: &Checker,
) -> bool {
    if !has_brackets(&literal.value) {
        return false;
    }

    let fstring_expr = format!("f{}", locator.slice(literal));
    let Ok(parsed) = parse_expression(&fstring_expr) else {
        return false;
    };

    // Note: Range offsets for `value` are based on `fstring_expr`
    let ast::Expr::FString(ast::ExprFString { value, .. }) = parsed.expr() else {
        return false;
    };

    let mut arg_names = FxHashSet::default();
    for expr in semantic
        .current_expressions()
        .filter_map(ast::Expr::as_call_expr)
    {
        let ast::Arguments { keywords, args, .. } = &expr.arguments;
        for keyword in keywords {
            if let Some(ident) = keyword.arg.as_ref() {
                arg_names.insert(&ident.id);
            }

Review comment (suggestion):
I think we could drop the `locator` and `semantic` arguments if we're passing the `Checker` they both come from. Alternatively, we could pass only the `target_version`, which is the only additional field we're accessing now.

Code after:
        return true;
    }

    semantic
        .resolve_qualified_name(func)
        .is_some_and(|qualified_name| {
            matches!(
                qualified_name.segments(),
                ["gettext", "gettext" | "ngettext"] | ["builtins", "_"]
            )
        })
}

/// Return `true` if `call_expr` is a method call on an [`ast::ExprStringLiteral`]
/// in which `literal` is one of the [`ast::StringLiteral`] parts.
///
/// For example: `expr` is a node representing the expression `"{foo}".format(foo="bar")`,
/// and `literal` is the node representing the string literal `"{foo}"`.
fn is_method_call_on_literal(call_expr: &ast::ExprCall, literal: &ast::StringLiteral) -> bool {
    let ast::Expr::Attribute(ast::ExprAttribute { value, .. }) = &*call_expr.func else {
        return false;
    };
    let ast::Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &**value else {
        return false;
    };
    value.as_slice().contains(literal)
}

/// Returns `true` if `literal` is likely an f-string with a missing `f` prefix.
/// See [`MissingFStringSyntax`] for the validation criteria.
fn should_be_fstring(
    literal: &ast::StringLiteral,
    locator: &Locator,
    semantic: &SemanticModel,
    target_version: PythonVersion,
) -> bool {
    if !has_brackets(&literal.value) {
        return false;
    }

    let fstring_expr = format!("f{}", locator.slice(literal));
    let Ok(parsed) = parse_expression(&fstring_expr) else {
        return false;
    };

    // Note: Range offsets for `value` are based on `fstring_expr`
    let ast::Expr::FString(ast::ExprFString { value, .. }) = parsed.expr() else {
        return false;
    };

    let mut arg_names = FxHashSet::default();