From 6a26b0818fcb62c29f8f8d908b1726ffe4620e54 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Mon, 13 May 2024 14:17:23 +0530 Subject: [PATCH] Create a newtype wrapper around `Vec` --- .../rules/hardcoded_bind_all_interfaces.rs | 2 +- .../rules/hardcoded_tmp_directory.rs | 2 +- .../call_datetime_strptime_without_zone.rs | 6 +- .../rules/avoidable_escaped_quote.rs | 3 +- .../rules/unnecessary_escaped_quote.rs | 2 +- .../flynt/rules/static_join_to_fstring.rs | 2 +- .../ruff/rules/ambiguous_unicode_character.rs | 2 +- .../ruff/rules/missing_fstring_syntax.rs | 2 +- .../tryceratops/rules/raise_vanilla_args.rs | 2 +- crates/ruff_python_ast/src/node.rs | 6 +- crates/ruff_python_ast/src/nodes.rs | 64 +++++++++++++------ crates/ruff_python_ast/src/visitor.rs | 4 +- .../src/visitor/transformer.rs | 4 +- .../src/other/f_string.rs | 1 + .../src/parser/expression.rs | 8 +-- 15 files changed, 71 insertions(+), 39 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_bind_all_interfaces.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_bind_all_interfaces.rs index aea1850771de03..ea37397430d16a 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_bind_all_interfaces.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_bind_all_interfaces.rs @@ -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, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs index 4304e1482907c7..cccfb58880336f 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs @@ -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()); } } diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs index 1c39b1604f9bdf..420104871febeb 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs @@ -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; } } diff --git a/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs b/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs index 846ea75bc70c11..785d21da6d09f8 100644 --- a/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs +++ b/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs @@ -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; diff --git a/crates/ruff_linter/src/rules/flake8_quotes/rules/unnecessary_escaped_quote.rs b/crates/ruff_linter/src/rules/flake8_quotes/rules/unnecessary_escaped_quote.rs index fa34cf11b7666b..00590a2a64f8f0 100644 --- a/crates/ruff_linter/src/rules/flake8_quotes/rules/unnecessary_escaped_quote.rs +++ b/crates/ruff_linter/src/rules/flake8_quotes/rules/unnecessary_escaped_quote.rs @@ -124,7 +124,7 @@ fn check_f_string(locator: &Locator, f_string: &ast::FString) -> Option Option { } let node = ast::FString { - elements: f_string_elements, + elements: f_string_elements.into(), range: TextRange::default(), flags: FStringFlags::default(), }; diff --git a/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs b/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs index 2dc499c9d3e262..9940707c0fb661 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/ambiguous_unicode_character.rs @@ -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, diff --git a/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs b/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs index 09cd172cf715c2..35976dbf5bf0c1 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs @@ -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; diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs b/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs index 0d56ca090cd854..d21371efb53734 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs @@ -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; } diff --git a/crates/ruff_python_ast/src/node.rs b/crates/ruff_python_ast/src/node.rs index d8c2e3eacf6305..cfdbe7113fcc6a 100644 --- a/crates/ruff_python_ast/src/node.rs +++ b/crates/ruff_python_ast/src/node.rs @@ -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); } } diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index babba89fc262fe..8924f20443e006 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -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 for Expr { #[derive(Clone, Debug, PartialEq)] pub struct FStringFormatSpec { pub range: TextRange, - pub elements: Vec, + 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, + 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 { - 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 { - 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 for Expr { } } +/// A newtype wrapper around a list of [`FStringElement`]. +#[derive(Clone, Default, PartialEq)] +pub struct FStringElements(Vec); + +impl FStringElements { + /// Returns an iterator over all the [`FStringLiteralElement`] nodes contained in this f-string. + pub fn literals(&self) -> impl Iterator { + 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 { + self.iter().filter_map(|element| element.as_expression()) + } +} + +impl From> for FStringElements { + fn from(elements: Vec) -> Self { + FStringElements(elements) + } +} + +impl Deref for FStringElements { + type Target = Vec; + + 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), diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index bf14e5a0cf6130..f4680088a3b82d 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -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); } } diff --git a/crates/ruff_python_ast/src/visitor/transformer.rs b/crates/ruff_python_ast/src/visitor/transformer.rs index 9589617ee06c27..aa309dabbf5766 100644 --- a/crates/ruff_python_ast/src/visitor/transformer.rs +++ b/crates/ruff_python_ast/src/visitor/transformer.rs @@ -746,7 +746,7 @@ pub fn walk_pattern_keyword( } pub fn walk_f_string(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( { 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); } } diff --git a/crates/ruff_python_formatter/src/other/f_string.rs b/crates/ruff_python_formatter/src/other/f_string.rs index 63c8f62cbd48e8..77763257e8742a 100644 --- a/crates/ruff_python_formatter/src/other/f_string.rs +++ b/crates/ruff_python_formatter/src/other/f_string.rs @@ -126,6 +126,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()) { diff --git a/crates/ruff_python_parser/src/parser/expression.rs b/crates/ruff_python_parser/src/parser/expression.rs index 5e35eb4d8eab70..5b44aebe4bf3b0 100644 --- a/crates/ruff_python_parser/src/parser/expression.rs +++ b/crates/ruff_python_parser/src/parser/expression.rs @@ -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 { - 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() {