Skip to content

Commit

Permalink
Replace AnyStringPart with StringLike
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Oct 16, 2024
1 parent 5e187f9 commit 9c93d20
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 267 deletions.
76 changes: 75 additions & 1 deletion crates/ruff_python_ast/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::iter::FusedIterator;

use ruff_text_size::{Ranged, TextRange};

use crate::{self as ast, AnyNodeRef, AnyStringFlags, Expr};
use crate::{
self as ast, AnyNodeRef, AnyStringFlags, Expr, ExprBytesLiteral, ExprFString,
ExprStringLiteral, StringFlags,
};

/// Unowned pendant to [`ast::Expr`] that stores a reference instead of a owned value.
#[derive(Copy, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -405,6 +408,10 @@ pub enum StringLike<'a> {
}

impl<'a> StringLike<'a> {
pub const fn is_fstring(self) -> bool {
matches!(self, Self::FString(_))
}

/// Returns an iterator over the [`StringLikePart`] contained in this string-like expression.
pub fn parts(&self) -> StringLikePartIter<'_> {
match self {
Expand All @@ -413,6 +420,15 @@ impl<'a> StringLike<'a> {
StringLike::FString(expr) => StringLikePartIter::FString(expr.value.iter()),
}
}

/// Returns `true` if the string is implicitly concatenated.
pub fn is_implicit_concatenated(self) -> bool {
match self {
Self::String(ExprStringLiteral { value, .. }) => value.is_implicit_concatenated(),
Self::Bytes(ExprBytesLiteral { value, .. }) => value.is_implicit_concatenated(),
Self::FString(ExprFString { value, .. }) => value.is_implicit_concatenated(),
}
}
}

impl<'a> From<&'a ast::ExprStringLiteral> for StringLike<'a> {
Expand All @@ -433,6 +449,45 @@ impl<'a> From<&'a ast::ExprFString> for StringLike<'a> {
}
}

impl<'a> From<&StringLike<'a>> for ExpressionRef<'a> {
fn from(value: &StringLike<'a>) -> Self {
match value {
StringLike::String(expr) => ExpressionRef::StringLiteral(expr),
StringLike::Bytes(expr) => ExpressionRef::BytesLiteral(expr),
StringLike::FString(expr) => ExpressionRef::FString(expr),
}
}
}

impl<'a> From<StringLike<'a>> for AnyNodeRef<'a> {
fn from(value: StringLike<'a>) -> Self {
AnyNodeRef::from(&value)
}
}

impl<'a> From<&StringLike<'a>> for AnyNodeRef<'a> {
fn from(value: &StringLike<'a>) -> Self {
match value {
StringLike::String(expr) => AnyNodeRef::ExprStringLiteral(expr),
StringLike::Bytes(expr) => AnyNodeRef::ExprBytesLiteral(expr),
StringLike::FString(expr) => AnyNodeRef::ExprFString(expr),
}
}
}

impl<'a> TryFrom<&'a Expr> for StringLike<'a> {
type Error = ();

fn try_from(value: &'a Expr) -> Result<Self, Self::Error> {
match value {
Expr::StringLiteral(value) => Ok(Self::String(value)),
Expr::BytesLiteral(value) => Ok(Self::Bytes(value)),
Expr::FString(value) => Ok(Self::FString(value)),
_ => Err(()),
}
}
}

impl Ranged for StringLike<'_> {
fn range(&self) -> TextRange {
match self {
Expand Down Expand Up @@ -460,6 +515,15 @@ impl StringLikePart<'_> {
StringLikePart::FString(f_string) => AnyStringFlags::from(f_string.flags),
}
}

/// Returns the range of the string's content in the source (minus prefix and quotes).
pub fn content_range(self) -> TextRange {
let kind = self.flags();
TextRange::new(
self.start() + kind.opener_len(),
self.end() - kind.closer_len(),
)
}
}

impl<'a> From<&'a ast::StringLiteral> for StringLikePart<'a> {
Expand All @@ -480,6 +544,16 @@ impl<'a> From<&'a ast::FString> for StringLikePart<'a> {
}
}

impl<'a> From<&StringLikePart<'a>> for AnyNodeRef<'a> {
fn from(value: &StringLikePart<'a>) -> Self {
match value {
StringLikePart::String(part) => AnyNodeRef::StringLiteral(part),
StringLikePart::Bytes(part) => AnyNodeRef::BytesLiteral(part),
StringLikePart::FString(part) => AnyNodeRef::FString(part),
}
}
}

impl Ranged for StringLikePart<'_> {
fn range(&self) -> TextRange {
match self {
Expand Down
7 changes: 4 additions & 3 deletions crates/ruff_python_formatter/src/expression/binary_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use smallvec::SmallVec;

use ruff_formatter::write;
use ruff_python_ast::{
Expr, ExprAttribute, ExprBinOp, ExprBoolOp, ExprCompare, ExprUnaryOp, UnaryOp,
Expr, ExprAttribute, ExprBinOp, ExprBoolOp, ExprCompare, ExprUnaryOp, StringLike, UnaryOp,
};
use ruff_python_trivia::CommentRanges;
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
Expand All @@ -20,7 +20,7 @@ use crate::expression::parentheses::{
};
use crate::expression::OperatorPrecedence;
use crate::prelude::*;
use crate::string::{AnyString, FormatImplicitConcatenatedString};
use crate::string::FormatImplicitConcatenatedString;

#[derive(Copy, Clone, Debug)]
pub(super) enum BinaryLike<'a> {
Expand Down Expand Up @@ -293,7 +293,8 @@ impl Format<PyFormatContext<'_>> for BinaryLike<'_> {
let mut string_operands = flat_binary
.operands()
.filter_map(|(index, operand)| {
AnyString::from_expression(operand.expression())
StringLike::try_from(operand.expression())
.ok()
.filter(|string| {
string.is_implicit_concatenated()
&& !is_expression_parenthesized(
Expand Down
6 changes: 3 additions & 3 deletions crates/ruff_python_formatter/src/expression/expr_bin_op.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::ExprBinOp;
use ruff_python_ast::{AnyNodeRef, StringLike};

use crate::expression::binary_like::BinaryLike;
use crate::expression::has_parentheses;
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
use crate::prelude::*;
use crate::string::AnyString;
use crate::string::StringLikeExtensions;

#[derive(Default)]
pub struct FormatExprBinOp;
Expand All @@ -25,7 +25,7 @@ impl NeedsParentheses for ExprBinOp {
) -> OptionalParentheses {
if parent.is_expr_await() {
OptionalParentheses::Always
} else if let Some(string) = AnyString::from_expression(&self.left) {
} else if let Ok(string) = StringLike::try_from(&*self.left) {
// Multiline strings are guaranteed to never fit, avoid adding unnecessary parentheses
if !string.is_implicit_concatenated()
&& string.is_multiline(context.source())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::ExprBytesLiteral;
use ruff_python_ast::{AnyNodeRef, StringLike};

use crate::expression::parentheses::{
in_parentheses_only_group, NeedsParentheses, OptionalParentheses,
};
use crate::prelude::*;
use crate::string::{AnyString, FormatImplicitConcatenatedString};
use crate::string::{FormatImplicitConcatenatedString, StringLikeExtensions};

#[derive(Default)]
pub struct FormatExprBytesLiteral;
Expand All @@ -29,7 +29,7 @@ impl NeedsParentheses for ExprBytesLiteral {
) -> OptionalParentheses {
if self.value.is_implicit_concatenated() {
OptionalParentheses::Multiline
} else if AnyString::Bytes(self).is_multiline(context.source()) {
} else if StringLike::Bytes(self).is_multiline(context.source()) {
OptionalParentheses::Never
} else {
OptionalParentheses::BestFit
Expand Down
6 changes: 3 additions & 3 deletions crates/ruff_python_formatter/src/expression/expr_compare.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule};
use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::{AnyNodeRef, StringLike};
use ruff_python_ast::{CmpOp, ExprCompare};

use crate::expression::binary_like::BinaryLike;
use crate::expression::has_parentheses;
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
use crate::prelude::*;
use crate::string::AnyString;
use crate::string::StringLikeExtensions;

#[derive(Default)]
pub struct FormatExprCompare;
Expand All @@ -26,7 +26,7 @@ impl NeedsParentheses for ExprCompare {
) -> OptionalParentheses {
if parent.is_expr_await() {
OptionalParentheses::Always
} else if let Some(string) = AnyString::from_expression(&self.left) {
} else if let Ok(string) = StringLike::try_from(&*self.left) {
// Multiline strings are guaranteed to never fit, avoid adding unnecessary parentheses
if !string.is_implicit_concatenated()
&& string.is_multiline(context.source())
Expand Down
6 changes: 3 additions & 3 deletions crates/ruff_python_formatter/src/expression/expr_f_string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ruff_python_ast::{AnyNodeRef, ExprFString};
use ruff_python_ast::{AnyNodeRef, ExprFString, StringLike};
use ruff_source_file::Locator;
use ruff_text_size::Ranged;

Expand All @@ -7,7 +7,7 @@ use crate::expression::parentheses::{
};
use crate::other::f_string_part::FormatFStringPart;
use crate::prelude::*;
use crate::string::{AnyString, FormatImplicitConcatenatedString, Quoting};
use crate::string::{FormatImplicitConcatenatedString, Quoting, StringLikeExtensions};

#[derive(Default)]
pub struct FormatExprFString;
Expand Down Expand Up @@ -53,7 +53,7 @@ impl NeedsParentheses for ExprFString {
// ```
// This isn't decided yet, refer to the relevant discussion:
// https://github.com/astral-sh/ruff/discussions/9785
} else if AnyString::FString(self).is_multiline(context.source()) {
} else if StringLike::FString(self).is_multiline(context.source()) {
OptionalParentheses::Never
} else {
OptionalParentheses::BestFit
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use ruff_formatter::FormatRuleWithOptions;
use ruff_python_ast::{AnyNodeRef, ExprStringLiteral};
use ruff_python_ast::{AnyNodeRef, ExprStringLiteral, StringLike};

use crate::expression::parentheses::{
in_parentheses_only_group, NeedsParentheses, OptionalParentheses,
};
use crate::other::string_literal::StringLiteralKind;
use crate::prelude::*;
use crate::string::{AnyString, FormatImplicitConcatenatedString};
use crate::string::{FormatImplicitConcatenatedString, StringLikeExtensions};

#[derive(Default)]
pub struct FormatExprStringLiteral {
Expand Down Expand Up @@ -48,7 +48,7 @@ impl NeedsParentheses for ExprStringLiteral {
) -> OptionalParentheses {
if self.value.is_implicit_concatenated() {
OptionalParentheses::Multiline
} else if AnyString::String(self).is_multiline(context.source()) {
} else if StringLike::String(self).is_multiline(context.source()) {
OptionalParentheses::Never
} else {
OptionalParentheses::BestFit
Expand Down
10 changes: 5 additions & 5 deletions crates/ruff_python_formatter/src/other/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ruff_formatter::{write, FormatContext};
use ruff_python_ast::{ArgOrKeyword, Arguments, Expr};
use ruff_python_ast::{ArgOrKeyword, Arguments, Expr, StringLike};
use ruff_python_trivia::{PythonWhitespace, SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};

Expand All @@ -8,7 +8,7 @@ use crate::expression::is_expression_huggable;
use crate::expression::parentheses::{empty_parenthesized, parenthesized, Parentheses};
use crate::other::commas;
use crate::prelude::*;
use crate::string::AnyString;
use crate::string::StringLikeExtensions;

#[derive(Default)]
pub struct FormatArguments;
Expand Down Expand Up @@ -179,8 +179,8 @@ fn is_arguments_huggable(arguments: &Arguments, context: &PyFormatContext) -> bo

// If the expression itself isn't huggable, then we can't hug it.
if !(is_expression_huggable(arg, context)
|| AnyString::from_expression(arg)
.is_some_and(|string| is_huggable_string_argument(string, arguments, context)))
|| StringLike::try_from(arg)
.is_ok_and(|string| is_huggable_string_argument(string, arguments, context)))
{
return false;
}
Expand Down Expand Up @@ -219,7 +219,7 @@ fn is_arguments_huggable(arguments: &Arguments, context: &PyFormatContext) -> bo
/// )
/// ```
fn is_huggable_string_argument(
string: AnyString,
string: StringLike,
arguments: &Arguments,
context: &PyFormatContext,
) -> bool {
Expand Down
Loading

0 comments on commit 9c93d20

Please sign in to comment.