mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
Create a newtype wrapper around Vec<FStringElement>
(#11400)
## Summary This PR adds a newtype wrapper around `Vec<FStringElement>` that derefs to a `&Vec<FStringElement>`. Both f-string and format specifier are made up of `Vec<FStringElement>`. By creating a newtype wrapper around it, we can share the methods for both parent types.
This commit is contained in:
parent
0dc130e841
commit
4b41e4de7f
15 changed files with 71 additions and 39 deletions
|
@ -57,7 +57,7 @@ pub(crate) fn hardcoded_bind_all_interfaces(checker: &mut Checker, string: Strin
|
|||
}
|
||||
}
|
||||
ast::FStringPart::FString(f_string) => {
|
||||
for literal in f_string.literals() {
|
||||
for literal in f_string.elements.literals() {
|
||||
if &**literal == "0.0.0.0" {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
HardcodedBindAllInterfaces,
|
||||
|
|
|
@ -64,7 +64,7 @@ pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: StringLike)
|
|||
check(checker, literal, literal.range());
|
||||
}
|
||||
ast::FStringPart::FString(f_string) => {
|
||||
for literal in f_string.literals() {
|
||||
for literal in f_string.elements.literals() {
|
||||
check(checker, literal, literal.range());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,11 @@ pub(crate) fn call_datetime_strptime_without_zone(checker: &mut Checker, call: &
|
|||
}
|
||||
}
|
||||
ast::FStringPart::FString(f_string) => {
|
||||
if f_string.literals().any(|literal| literal.contains("%z")) {
|
||||
if f_string
|
||||
.elements
|
||||
.literals()
|
||||
.any(|literal| literal.contains("%z"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ impl Visitor<'_> for AvoidableEscapedQuoteChecker<'_> {
|
|||
// f"'normal' {f'\'nested\' {x} "double quotes"'} normal"
|
||||
// ```
|
||||
if !f_string
|
||||
.elements
|
||||
.literals()
|
||||
.any(|literal| contains_quote(literal, opposite_quote_char))
|
||||
{
|
||||
|
@ -269,7 +270,7 @@ fn check_f_string(
|
|||
let opposite_quote_char = quotes_settings.inline_quotes.opposite().as_char();
|
||||
|
||||
let mut edits = vec![];
|
||||
for literal in f_string.literals() {
|
||||
for literal in f_string.elements.literals() {
|
||||
let content = locator.slice(literal);
|
||||
if !contains_escaped_quote(content, quote_char) {
|
||||
continue;
|
||||
|
|
|
@ -124,7 +124,7 @@ fn check_f_string(locator: &Locator, f_string: &ast::FString) -> Option<Diagnost
|
|||
let opposite_quote_char = flags.quote_style().opposite().as_char();
|
||||
|
||||
let mut edits = vec![];
|
||||
for literal in f_string.literals() {
|
||||
for literal in f_string.elements.literals() {
|
||||
let content = locator.slice(literal);
|
||||
if !contains_escaped_quote(content, opposite_quote_char) {
|
||||
continue;
|
||||
|
|
|
@ -96,7 +96,7 @@ fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
|
|||
}
|
||||
|
||||
let node = ast::FString {
|
||||
elements: f_string_elements,
|
||||
elements: f_string_elements.into(),
|
||||
range: TextRange::default(),
|
||||
flags: FStringFlags::default(),
|
||||
};
|
||||
|
|
|
@ -206,7 +206,7 @@ pub(crate) fn ambiguous_unicode_character_string(checker: &mut Checker, string_l
|
|||
}
|
||||
ast::StringLikePart::Bytes(_) => {}
|
||||
ast::StringLikePart::FString(f_string) => {
|
||||
for literal in f_string.literals() {
|
||||
for literal in f_string.elements.literals() {
|
||||
let text = checker.locator().slice(literal);
|
||||
ambiguous_unicode_character(
|
||||
&mut checker.diagnostics,
|
||||
|
|
|
@ -168,7 +168,7 @@ fn should_be_fstring(
|
|||
|
||||
for f_string in value.f_strings() {
|
||||
let mut has_name = false;
|
||||
for element in f_string.expressions() {
|
||||
for element in f_string.elements.expressions() {
|
||||
if let ast::Expr::Name(ast::ExprName { id, .. }) = element.expression.as_ref() {
|
||||
if arg_names.contains(id.as_str()) {
|
||||
return false;
|
||||
|
|
|
@ -97,7 +97,7 @@ fn contains_message(expr: &Expr) -> bool {
|
|||
}
|
||||
}
|
||||
ast::FStringPart::FString(f_string) => {
|
||||
for literal in f_string.literals() {
|
||||
for literal in f_string.elements.literals() {
|
||||
if literal.chars().any(char::is_whitespace) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2814,7 +2814,7 @@ impl AstNode for ast::FStringFormatSpec {
|
|||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
for element in &self.elements {
|
||||
for element in self.elements.iter() {
|
||||
visitor.visit_f_string_element(element);
|
||||
}
|
||||
}
|
||||
|
@ -2864,7 +2864,7 @@ impl AstNode for ast::FStringExpressionElement {
|
|||
visitor.visit_expr(expression);
|
||||
|
||||
if let Some(format_spec) = format_spec {
|
||||
for spec_part in &format_spec.elements {
|
||||
for spec_part in format_spec.elements.iter() {
|
||||
visitor.visit_f_string_element(spec_part);
|
||||
}
|
||||
}
|
||||
|
@ -4800,7 +4800,7 @@ impl AstNode for ast::FString {
|
|||
flags: _,
|
||||
} = self;
|
||||
|
||||
for fstring_element in elements {
|
||||
for fstring_element in elements.iter() {
|
||||
visitor.visit_f_string_element(fstring_element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::iter::FusedIterator;
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::slice::{Iter, IterMut};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
|
@ -1084,7 +1084,7 @@ impl From<ExprCall> for Expr {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FStringFormatSpec {
|
||||
pub range: TextRange,
|
||||
pub elements: Vec<FStringElement>,
|
||||
pub elements: FStringElements,
|
||||
}
|
||||
|
||||
impl Ranged for FStringFormatSpec {
|
||||
|
@ -1484,26 +1484,10 @@ impl fmt::Debug for FStringFlags {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FString {
|
||||
pub range: TextRange,
|
||||
pub elements: Vec<FStringElement>,
|
||||
pub elements: FStringElements,
|
||||
pub flags: FStringFlags,
|
||||
}
|
||||
|
||||
impl FString {
|
||||
/// Returns an iterator over all the [`FStringLiteralElement`] nodes contained in this f-string.
|
||||
pub fn literals(&self) -> impl Iterator<Item = &FStringLiteralElement> {
|
||||
self.elements
|
||||
.iter()
|
||||
.filter_map(|element| element.as_literal())
|
||||
}
|
||||
|
||||
/// Returns an iterator over all the [`FStringExpressionElement`] nodes contained in this f-string.
|
||||
pub fn expressions(&self) -> impl Iterator<Item = &FStringExpressionElement> {
|
||||
self.elements
|
||||
.iter()
|
||||
.filter_map(|element| element.as_expression())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for FString {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
|
@ -1520,6 +1504,48 @@ impl From<FString> for Expr {
|
|||
}
|
||||
}
|
||||
|
||||
/// A newtype wrapper around a list of [`FStringElement`].
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
pub struct FStringElements(Vec<FStringElement>);
|
||||
|
||||
impl FStringElements {
|
||||
/// Returns an iterator over all the [`FStringLiteralElement`] nodes contained in this f-string.
|
||||
pub fn literals(&self) -> impl Iterator<Item = &FStringLiteralElement> {
|
||||
self.iter().filter_map(|element| element.as_literal())
|
||||
}
|
||||
|
||||
/// Returns an iterator over all the [`FStringExpressionElement`] nodes contained in this f-string.
|
||||
pub fn expressions(&self) -> impl Iterator<Item = &FStringExpressionElement> {
|
||||
self.iter().filter_map(|element| element.as_expression())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<FStringElement>> for FStringElements {
|
||||
fn from(elements: Vec<FStringElement>) -> Self {
|
||||
FStringElements(elements)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FStringElements {
|
||||
type Target = Vec<FStringElement>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FStringElements {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FStringElements {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, is_macro::Is)]
|
||||
pub enum FStringElement {
|
||||
Literal(FStringLiteralElement),
|
||||
|
|
|
@ -739,7 +739,7 @@ pub fn walk_pattern_keyword<'a, V: Visitor<'a> + ?Sized>(
|
|||
}
|
||||
|
||||
pub fn walk_f_string<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, f_string: &'a FString) {
|
||||
for f_string_element in &f_string.elements {
|
||||
for f_string_element in f_string.elements.iter() {
|
||||
visitor.visit_f_string_element(f_string_element);
|
||||
}
|
||||
}
|
||||
|
@ -756,7 +756,7 @@ pub fn walk_f_string_element<'a, V: Visitor<'a> + ?Sized>(
|
|||
{
|
||||
visitor.visit_expr(expression);
|
||||
if let Some(format_spec) = format_spec {
|
||||
for spec_element in &format_spec.elements {
|
||||
for spec_element in format_spec.elements.iter() {
|
||||
visitor.visit_f_string_element(spec_element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -746,7 +746,7 @@ pub fn walk_pattern_keyword<V: Transformer + ?Sized>(
|
|||
}
|
||||
|
||||
pub fn walk_f_string<V: Transformer + ?Sized>(visitor: &V, f_string: &mut FString) {
|
||||
for element in &mut f_string.elements {
|
||||
for element in f_string.elements.iter_mut() {
|
||||
visitor.visit_f_string_element(element);
|
||||
}
|
||||
}
|
||||
|
@ -763,7 +763,7 @@ pub fn walk_f_string_element<V: Transformer + ?Sized>(
|
|||
{
|
||||
visitor.visit_expr(expression);
|
||||
if let Some(format_spec) = format_spec {
|
||||
for spec_element in &mut format_spec.elements {
|
||||
for spec_element in format_spec.elements.iter_mut() {
|
||||
visitor.visit_f_string_element(spec_element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ impl FStringLayout {
|
|||
//
|
||||
// Reference: https://prettier.io/docs/en/next/rationale.html#template-literals
|
||||
if f_string
|
||||
.elements
|
||||
.expressions()
|
||||
.any(|expr| memchr::memchr2(b'\n', b'\r', locator.slice(expr).as_bytes()).is_some())
|
||||
{
|
||||
|
|
|
@ -6,8 +6,8 @@ use bitflags::bitflags;
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use ruff_python_ast::{
|
||||
self as ast, BoolOp, CmpOp, ConversionFlag, Expr, ExprContext, FStringElement, IpyEscapeKind,
|
||||
Number, Operator, UnaryOp,
|
||||
self as ast, BoolOp, CmpOp, ConversionFlag, Expr, ExprContext, FStringElement, FStringElements,
|
||||
IpyEscapeKind, Number, Operator, UnaryOp,
|
||||
};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
|
||||
|
@ -1297,8 +1297,8 @@ impl<'src> Parser<'src> {
|
|||
/// # Panics
|
||||
///
|
||||
/// If the parser isn't positioned at a `{` or `FStringMiddle` token.
|
||||
fn parse_fstring_elements(&mut self) -> Vec<FStringElement> {
|
||||
let mut elements = vec![];
|
||||
fn parse_fstring_elements(&mut self) -> FStringElements {
|
||||
let mut elements = FStringElements::default();
|
||||
|
||||
self.parse_list(RecoveryContextKind::FStringElements, |parser| {
|
||||
let element = match parser.current_token_kind() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue