diff --git a/crates/ruff/src/rules/pyflakes/rules/f_string_missing_placeholders.rs b/crates/ruff/src/rules/pyflakes/rules/f_string_missing_placeholders.rs index cd53757b61..341fee3f97 100644 --- a/crates/ruff/src/rules/pyflakes/rules/f_string_missing_placeholders.rs +++ b/crates/ruff/src/rules/pyflakes/rules/f_string_missing_placeholders.rs @@ -1,8 +1,9 @@ -use rustpython_parser::ast::{Expr, ExprKind}; +use rustpython_parser::ast::{Expr, ExprKind, Location}; +use rustpython_parser::{lexer, Mode, StringKind, Tok}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::find_useless_f_strings; +use ruff_python_ast::source_code::Locator; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; @@ -47,6 +48,45 @@ impl AlwaysAutofixableViolation for FStringMissingPlaceholders { } } +/// Find f-strings that don't contain any formatted values in a `JoinedStr`. +fn find_useless_f_strings<'a>( + expr: &'a Expr, + locator: &'a Locator, +) -> impl Iterator + 'a { + let contents = locator.slice(expr); + lexer::lex_located(contents, Mode::Module, expr.location) + .flatten() + .filter_map(|(location, tok, end_location)| match tok { + Tok::String { + kind: StringKind::FString | StringKind::RawFString, + .. + } => { + let first_char = locator.slice(Range { + location, + end_location: Location::new(location.row(), location.column() + 1), + }); + // f"..." => f_position = 0 + // fr"..." => f_position = 0 + // rf"..." => f_position = 1 + let f_position = usize::from(!(first_char == "f" || first_char == "F")); + Some(( + Range { + location: Location::new(location.row(), location.column() + f_position), + end_location: Location::new( + location.row(), + location.column() + f_position + 1, + ), + }, + Range { + location, + end_location, + }, + )) + } + _ => None, + }) +} + fn unescape_f_string(content: &str) -> String { content.replace("{{", "{").replace("}}", "}") } diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 1195600781..6e89fd4afd 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -9,7 +9,7 @@ use rustpython_parser::ast::{ Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, KeywordData, Located, Location, MatchCase, Pattern, PatternKind, Stmt, StmtKind, }; -use rustpython_parser::{lexer, Mode, StringKind, Tok}; +use rustpython_parser::{lexer, Mode, Tok}; use smallvec::{smallvec, SmallVec}; use crate::context::Context; @@ -1055,43 +1055,6 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range { range } -/// Find f-strings that don't contain any formatted values in a `JoinedStr`. -pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> { - let contents = locator.slice(expr); - lexer::lex_located(contents, Mode::Module, expr.location) - .flatten() - .filter_map(|(location, tok, end_location)| match tok { - Tok::String { - kind: StringKind::FString | StringKind::RawFString, - .. - } => { - let first_char = locator.slice(Range { - location, - end_location: Location::new(location.row(), location.column() + 1), - }); - // f"..." => f_position = 0 - // fr"..." => f_position = 0 - // rf"..." => f_position = 1 - let f_position = usize::from(!(first_char == "f" || first_char == "F")); - Some(( - Range { - location: Location::new(location.row(), location.column() + f_position), - end_location: Location::new( - location.row(), - location.column() + f_position + 1, - ), - }, - Range { - location, - end_location, - }, - )) - } - _ => None, - }) - .collect() -} - /// Return the `Range` of `else` in `For`, `AsyncFor`, and `While` statements. pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option { match &stmt.node {