From 2d542bdc8589ff2ee8d0dd99aacc24c651d37f5d Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Fri, 27 Sep 2024 17:10:43 +0200 Subject: [PATCH] refactor: Remove `StringLiteralKind::InImplicitConcatenatedFString` kind --- crates/ruff_python_formatter/generate.py | 3 +- .../src/expression/expr_string_literal.rs | 30 ++------------- crates/ruff_python_formatter/src/generated.rs | 36 ++++++++++++++++++ .../src/other/f_string_part.rs | 10 +---- .../src/other/string_literal.rs | 37 +++++++------------ .../src/statement/suite.rs | 4 +- .../ruff_python_formatter/src/string/any.rs | 28 +++++--------- 7 files changed, 68 insertions(+), 80 deletions(-) diff --git a/crates/ruff_python_formatter/generate.py b/crates/ruff_python_formatter/generate.py index bf89ac1a4b523..818f6e62c8d98 100755 --- a/crates/ruff_python_formatter/generate.py +++ b/crates/ruff_python_formatter/generate.py @@ -38,9 +38,10 @@ def rustfmt(code: str) -> str: # implementation. if node in ( "FString", - "StringLiteral", "FStringLiteralElement", "FStringExpressionElement", + "FStringFormatSpec", + "Identifier", ): continue nodes.append(node) diff --git a/crates/ruff_python_formatter/src/expression/expr_string_literal.rs b/crates/ruff_python_formatter/src/expression/expr_string_literal.rs index 460c1519dd6da..23e8eab633eb5 100644 --- a/crates/ruff_python_formatter/src/expression/expr_string_literal.rs +++ b/crates/ruff_python_formatter/src/expression/expr_string_literal.rs @@ -4,37 +4,17 @@ use ruff_python_ast::{AnyNodeRef, ExprStringLiteral}; use crate::expression::parentheses::{ in_parentheses_only_group, NeedsParentheses, OptionalParentheses, }; -use crate::other::string_literal::{FormatStringLiteral, StringLiteralKind}; +use crate::other::string_literal::StringLiteralKind; use crate::prelude::*; use crate::string::{AnyString, FormatImplicitConcatenatedString}; #[derive(Default)] pub struct FormatExprStringLiteral { - kind: ExprStringLiteralKind, -} - -#[derive(Default, Copy, Clone, Debug)] -pub enum ExprStringLiteralKind { - #[default] - String, - Docstring, -} - -impl ExprStringLiteralKind { - const fn string_literal_kind(self) -> StringLiteralKind { - match self { - ExprStringLiteralKind::String => StringLiteralKind::String, - ExprStringLiteralKind::Docstring => StringLiteralKind::Docstring, - } - } - - const fn is_docstring(self) -> bool { - matches!(self, ExprStringLiteralKind::Docstring) - } + kind: StringLiteralKind, } impl FormatRuleWithOptions> for FormatExprStringLiteral { - type Options = ExprStringLiteralKind; + type Options = StringLiteralKind; fn with_options(mut self, options: Self::Options) -> Self { self.kind = options; @@ -47,9 +27,7 @@ impl FormatNodeRule for FormatExprStringLiteral { let ExprStringLiteral { value, .. } = item; match value.as_slice() { - [string_literal] => { - FormatStringLiteral::new(string_literal, self.kind.string_literal_kind()).fmt(f) - } + [string_literal] => string_literal.format().with_options(self.kind).fmt(f), _ => { // This is just a sanity check because [`DocstringStmt::try_from_statement`] // ensures that the docstring is a *single* string literal. diff --git a/crates/ruff_python_formatter/src/generated.rs b/crates/ruff_python_formatter/src/generated.rs index 63667ac5095dc..90ea7b00dd51e 100644 --- a/crates/ruff_python_formatter/src/generated.rs +++ b/crates/ruff_python_formatter/src/generated.rs @@ -2935,6 +2935,42 @@ impl<'ast> IntoFormat> for ast::TypeParamParamSpec { } } +impl FormatRule> + for crate::other::string_literal::FormatStringLiteral +{ + #[inline] + fn fmt(&self, node: &ast::StringLiteral, f: &mut PyFormatter) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl<'ast> AsFormat> for ast::StringLiteral { + type Format<'a> = FormatRefWithRule< + 'a, + ast::StringLiteral, + crate::other::string_literal::FormatStringLiteral, + PyFormatContext<'ast>, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::other::string_literal::FormatStringLiteral::default(), + ) + } +} +impl<'ast> IntoFormat> for ast::StringLiteral { + type Format = FormatOwnedWithRule< + ast::StringLiteral, + crate::other::string_literal::FormatStringLiteral, + PyFormatContext<'ast>, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::other::string_literal::FormatStringLiteral::default(), + ) + } +} + impl FormatRule> for crate::other::bytes_literal::FormatBytesLiteral { diff --git a/crates/ruff_python_formatter/src/other/f_string_part.rs b/crates/ruff_python_formatter/src/other/f_string_part.rs index c471b5fc8cd4f..0421ea2a0ac78 100644 --- a/crates/ruff_python_formatter/src/other/f_string_part.rs +++ b/crates/ruff_python_formatter/src/other/f_string_part.rs @@ -1,7 +1,6 @@ use ruff_python_ast::FStringPart; use crate::other::f_string::FormatFString; -use crate::other::string_literal::{FormatStringLiteral, StringLiteralKind}; use crate::prelude::*; use crate::string::Quoting; @@ -25,14 +24,7 @@ impl<'a> FormatFStringPart<'a> { impl Format> for FormatFStringPart<'_> { fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { match self.part { - FStringPart::Literal(string_literal) => FormatStringLiteral::new( - string_literal, - // If an f-string part is a string literal, the f-string is always - // implicitly concatenated e.g., `"foo" f"bar {x}"`. A standalone - // string literal would be a string expression, not an f-string. - StringLiteralKind::InImplicitlyConcatenatedFString(self.quoting), - ) - .fmt(f), + FStringPart::Literal(string_literal) => string_literal.format().fmt(f), FStringPart::FString(f_string) => FormatFString::new(f_string, self.quoting).fmt(f), } } diff --git a/crates/ruff_python_formatter/src/other/string_literal.rs b/crates/ruff_python_formatter/src/other/string_literal.rs index 2d3d752d434b8..b1fbe18df33ad 100644 --- a/crates/ruff_python_formatter/src/other/string_literal.rs +++ b/crates/ruff_python_formatter/src/other/string_literal.rs @@ -1,32 +1,32 @@ +use ruff_formatter::FormatRuleWithOptions; use ruff_python_ast::StringLiteral; use crate::prelude::*; -use crate::string::{docstring, Quoting, StringNormalizer}; +use crate::string::{docstring, StringNormalizer}; use crate::QuoteStyle; -pub(crate) struct FormatStringLiteral<'a> { - value: &'a StringLiteral, +#[derive(Default)] +pub struct FormatStringLiteral { layout: StringLiteralKind, } -impl<'a> FormatStringLiteral<'a> { - pub(crate) fn new(value: &'a StringLiteral, layout: StringLiteralKind) -> Self { - Self { value, layout } +impl FormatRuleWithOptions> for FormatStringLiteral { + type Options = StringLiteralKind; + + fn with_options(mut self, layout: StringLiteralKind) -> Self { + self.layout = layout; + self } } /// The kind of a string literal. #[derive(Copy, Clone, Debug, Default)] -pub(crate) enum StringLiteralKind { +pub enum StringLiteralKind { /// A normal string literal e.g., `"foo"`. #[default] String, /// A string literal used as a docstring. Docstring, - /// A string literal that is implicitly concatenated with an f-string. This - /// makes the overall expression an f-string whose quoting detection comes - /// from the parent node (f-string expression). - InImplicitlyConcatenatedFString(Quoting), } impl StringLiteralKind { @@ -34,18 +34,10 @@ impl StringLiteralKind { pub(crate) const fn is_docstring(self) -> bool { matches!(self, StringLiteralKind::Docstring) } - - /// Returns the quoting to be used for this string literal. - fn quoting(self) -> Quoting { - match self { - StringLiteralKind::String | StringLiteralKind::Docstring => Quoting::CanChange, - StringLiteralKind::InImplicitlyConcatenatedFString(quoting) => quoting, - } - } } -impl Format> for FormatStringLiteral<'_> { - fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { +impl FormatNodeRule for FormatStringLiteral { + fn fmt_fields(&self, item: &StringLiteral, f: &mut PyFormatter) -> FormatResult<()> { let quote_style = f.options().quote_style(); let quote_style = if self.layout.is_docstring() && !quote_style.is_preserve() { // Per PEP 8 and PEP 257, always prefer double quotes for docstrings, @@ -56,9 +48,8 @@ impl Format> for FormatStringLiteral<'_> { }; let normalized = StringNormalizer::from_context(f.context()) - .with_quoting(self.layout.quoting()) .with_preferred_quote_style(quote_style) - .normalize(self.value.into()); + .normalize(item.into()); if self.layout.is_docstring() { docstring::format(&normalized, f) diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index d0d89839ccf73..c483f917e2395 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -11,7 +11,7 @@ use crate::comments::{ leading_comments, trailing_comments, Comments, LeadingDanglingTrailingComments, }; use crate::context::{NodeLevel, TopLevelStatementPosition, WithIndentLevel, WithNodeLevel}; -use crate::expression::expr_string_literal::ExprStringLiteralKind; +use crate::other::string_literal::StringLiteralKind; use crate::prelude::*; use crate::statement::stmt_expr::FormatStmtExpr; use crate::verbatim::{ @@ -850,7 +850,7 @@ impl Format> for DocstringStmt<'_> { .then_some(source_position(self.docstring.start())), string_literal .format() - .with_options(ExprStringLiteralKind::Docstring), + .with_options(StringLiteralKind::Docstring), f.options() .source_map_generation() .is_enabled() diff --git a/crates/ruff_python_formatter/src/string/any.rs b/crates/ruff_python_formatter/src/string/any.rs index b86b3b4fc03de..58f66ca2af731 100644 --- a/crates/ruff_python_formatter/src/string/any.rs +++ b/crates/ruff_python_formatter/src/string/any.rs @@ -11,7 +11,6 @@ use ruff_text_size::{Ranged, TextRange}; use crate::expression::expr_f_string::f_string_quoting; use crate::other::f_string::FormatFString; -use crate::other::string_literal::{FormatStringLiteral, StringLiteralKind}; use crate::prelude::*; use crate::string::Quoting; @@ -149,19 +148,15 @@ impl<'a> Iterator for AnyStringPartsIter<'a> { let part = match self { Self::String(inner) => { let part = inner.next()?; - AnyStringPart::String { - part, - layout: StringLiteralKind::String, - } + AnyStringPart::String(part) } Self::Bytes(inner) => AnyStringPart::Bytes(inner.next()?), Self::FString(inner, quoting) => { let part = inner.next()?; match part { - ast::FStringPart::Literal(string_literal) => AnyStringPart::String { - part: string_literal, - layout: StringLiteralKind::InImplicitlyConcatenatedFString(*quoting), - }, + ast::FStringPart::Literal(string_literal) => { + AnyStringPart::String(string_literal) + } ast::FStringPart::FString(f_string) => AnyStringPart::FString { part: f_string, quoting: *quoting, @@ -182,10 +177,7 @@ impl FusedIterator for AnyStringPartsIter<'_> {} /// This is constructed from the [`AnyString::parts`] method on [`AnyString`]. #[derive(Clone, Debug)] pub(super) enum AnyStringPart<'a> { - String { - part: &'a ast::StringLiteral, - layout: StringLiteralKind, - }, + String(&'a ast::StringLiteral), Bytes(&'a ast::BytesLiteral), FString { part: &'a ast::FString, @@ -196,7 +188,7 @@ pub(super) enum AnyStringPart<'a> { impl AnyStringPart<'_> { fn flags(&self) -> AnyStringFlags { match self { - Self::String { part, .. } => part.flags.into(), + Self::String(part) => part.flags.into(), Self::Bytes(bytes_literal) => bytes_literal.flags.into(), Self::FString { part, .. } => part.flags.into(), } @@ -206,7 +198,7 @@ impl AnyStringPart<'_> { impl<'a> From<&AnyStringPart<'a>> for AnyNodeRef<'a> { fn from(value: &AnyStringPart<'a>) -> Self { match value { - AnyStringPart::String { part, .. } => AnyNodeRef::StringLiteral(part), + AnyStringPart::String(part) => AnyNodeRef::StringLiteral(part), AnyStringPart::Bytes(part) => AnyNodeRef::BytesLiteral(part), AnyStringPart::FString { part, .. } => AnyNodeRef::FString(part), } @@ -216,7 +208,7 @@ impl<'a> From<&AnyStringPart<'a>> for AnyNodeRef<'a> { impl Ranged for AnyStringPart<'_> { fn range(&self) -> TextRange { match self { - Self::String { part, .. } => part.range(), + Self::String(part) => part.range(), Self::Bytes(part) => part.range(), Self::FString { part, .. } => part.range(), } @@ -226,9 +218,7 @@ impl Ranged for AnyStringPart<'_> { impl Format> for AnyStringPart<'_> { fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { match self { - AnyStringPart::String { part, layout } => { - FormatStringLiteral::new(part, *layout).fmt(f) - } + AnyStringPart::String(part) => part.format().fmt(f), AnyStringPart::Bytes(bytes_literal) => bytes_literal.format().fmt(f), AnyStringPart::FString { part, quoting } => FormatFString::new(part, *quoting).fmt(f), }