mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
Move f-string identification into rule module (#3838)
This commit is contained in:
parent
66d72b1c7b
commit
b6276e2d95
2 changed files with 43 additions and 40 deletions
|
@ -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_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
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 ruff_python_ast::types::Range;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
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<Item = (Range, Range)> + '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 {
|
fn unescape_f_string(content: &str) -> String {
|
||||||
content.replace("{{", "{").replace("}}", "}")
|
content.replace("{{", "{").replace("}}", "}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustpython_parser::ast::{
|
||||||
Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, KeywordData,
|
Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, KeywordData,
|
||||||
Located, Location, MatchCase, Pattern, PatternKind, Stmt, StmtKind,
|
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 smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
@ -1055,43 +1055,6 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||||
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.
|
/// Return the `Range` of `else` in `For`, `AsyncFor`, and `While` statements.
|
||||||
pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue