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

Code before:
    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);
            }
        }
        for arg in args {
            if let ast::Expr::Name(ast::ExprName { id, .. }) = arg {
                arg_names.insert(id);
            }
        }
    }

    for f_string in value.f_strings() {
        let mut has_name = false;
        for element in f_string.elements.interpolations() {
            // Check if the interpolation expression contains backslashes or comments
            // F-strings with backslashes or comments in interpolations are only valid in Python 3.12+
            let interpolation_text = &fstring_expr[element.range()];
            if target_version < PythonVersion::PY312
                && (interpolation_text.contains('\\') || interpolation_text.contains('#'))
            {
                return false;
            }

            if let ast::Expr::Name(ast::ExprName { id, .. }) = element.expression.as_ref() {
                if arg_names.contains(id) {
                    return false;
                }
                if semantic
                    // the parsed expression nodes have incorrect ranges
                    // so we need to use the range of the literal for the
                    // lookup in order to get reasonable results.
                    .simulate_runtime_load_at_location_in_scope(
                        id,
                        literal.range(),
                        semantic.scope_id,
                        TypingOnlyBindingsStatus::Disallowed,
                    )
                    .is_none_or(|id| semantic.binding(id).kind.is_builtin())
                {
                    return false;
                }
                has_name = true;
            }
            if let Some(spec) = &element.format_spec {
                let spec = &fstring_expr[spec.range()];

Review comment (suggestion):
Number signs are only problematic when they would start comments. `f"{'#'}"` is fine in Python 3.11.

Code after:
            .any(|e| matches!(e.kind, UnsupportedSyntaxErrorKind::Pep701FString(_)));
        if has_pep701 {
            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);
            }
        }
        for arg in args {
            if let ast::Expr::Name(ast::ExprName { id, .. }) = arg {
                arg_names.insert(id);
            }
        }
    }

    for f_string in value.f_strings() {
        let mut has_name = false;
        for element in f_string.elements.interpolations() {
            if let ast::Expr::Name(ast::ExprName { id, .. }) = element.expression.as_ref() {
                if arg_names.contains(id) {
                    return false;
                }
                if semantic
                    // the parsed expression nodes have incorrect ranges
                    // so we need to use the range of the literal for the
                    // lookup in order to get reasonable results.
                    .simulate_runtime_load_at_location_in_scope(
                        id,
                        literal.range(),
                        semantic.scope_id,
                        TypingOnlyBindingsStatus::Disallowed,
                    )
                    .is_none_or(|id| semantic.binding(id).kind.is_builtin())
                {
                    return false;
                }
                has_name = true;