mirror of https://github.com/astral-sh/ruff
Rename `ExprStringLiteral::as_unconcatenated_string()` to `ExprStringLiteral::as_single_part_string()` (#16253)
This commit is contained in:
parent
97d0659ce3
commit
25920fe489
|
|
@ -138,7 +138,7 @@ pub(crate) fn parse_string_annotation(
|
||||||
|
|
||||||
let source = source_text(db.upcast(), file);
|
let source = source_text(db.upcast(), file);
|
||||||
|
|
||||||
if let Some(string_literal) = string_expr.as_unconcatenated_literal() {
|
if let Some(string_literal) = string_expr.as_single_part_string() {
|
||||||
let prefix = string_literal.flags.prefix();
|
let prefix = string_literal.flags.prefix();
|
||||||
if prefix.is_raw() {
|
if prefix.is_raw() {
|
||||||
context.report_lint(
|
context.report_lint(
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// We don't recognise implicitly concatenated strings as valid docstrings in our model currently.
|
// We don't recognise implicitly concatenated strings as valid docstrings in our model currently.
|
||||||
let Some(sole_string_part) = string_literal.as_unconcatenated_literal() else {
|
let Some(sole_string_part) = string_literal.as_single_part_string() else {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let location = checker
|
let location = checker
|
||||||
.locator
|
.locator
|
||||||
|
|
|
||||||
|
|
@ -290,11 +290,9 @@ fn match_open_keywords(
|
||||||
|
|
||||||
/// Match open mode to see if it is supported.
|
/// Match open mode to see if it is supported.
|
||||||
fn match_open_mode(mode: &Expr) -> Option<OpenMode> {
|
fn match_open_mode(mode: &Expr) -> Option<OpenMode> {
|
||||||
let ast::ExprStringLiteral { value, .. } = mode.as_string_literal_expr()?;
|
let mode = mode.as_string_literal_expr()?.as_single_part_string()?;
|
||||||
if value.is_implicit_concatenated() {
|
|
||||||
return None;
|
match &*mode.value {
|
||||||
}
|
|
||||||
match value.to_str() {
|
|
||||||
"r" => Some(OpenMode::ReadText),
|
"r" => Some(OpenMode::ReadText),
|
||||||
"rb" => Some(OpenMode::ReadBytes),
|
"rb" => Some(OpenMode::ReadBytes),
|
||||||
"w" => Some(OpenMode::WriteText),
|
"w" => Some(OpenMode::WriteText),
|
||||||
|
|
|
||||||
|
|
@ -300,8 +300,10 @@ impl<'a> SortClassification<'a> {
|
||||||
let Some(string_node) = expr.as_string_literal_expr() else {
|
let Some(string_node) = expr.as_string_literal_expr() else {
|
||||||
return Self::NotAListOfStringLiterals;
|
return Self::NotAListOfStringLiterals;
|
||||||
};
|
};
|
||||||
any_implicit_concatenation |= string_node.value.is_implicit_concatenated();
|
match string_node.as_single_part_string() {
|
||||||
items.push(string_node.value.to_str());
|
Some(literal) => items.push(&*literal.value),
|
||||||
|
None => any_implicit_concatenation = true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if any_implicit_concatenation {
|
if any_implicit_concatenation {
|
||||||
return Self::UnsortedButUnfixable;
|
return Self::UnsortedButUnfixable;
|
||||||
|
|
|
||||||
|
|
@ -824,6 +824,17 @@ pub struct ExprFString {
|
||||||
pub value: FStringValue,
|
pub value: FStringValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExprFString {
|
||||||
|
/// Returns the single [`FString`] if the f-string isn't implicitly concatenated, [`None`]
|
||||||
|
/// otherwise.
|
||||||
|
pub const fn as_single_part_fstring(&self) -> Option<&FString> {
|
||||||
|
match &self.value.inner {
|
||||||
|
FStringValueInner::Single(FStringPart::FString(fstring)) => Some(fstring),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The value representing an [`ExprFString`].
|
/// The value representing an [`ExprFString`].
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct FStringValue {
|
pub struct FStringValue {
|
||||||
|
|
@ -856,15 +867,6 @@ impl FStringValue {
|
||||||
matches!(self.inner, FStringValueInner::Concatenated(_))
|
matches!(self.inner, FStringValueInner::Concatenated(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the single [`FString`] if the f-string isn't implicitly concatenated, [`None`]
|
|
||||||
/// otherwise.
|
|
||||||
pub fn as_single(&self) -> Option<&FString> {
|
|
||||||
match &self.inner {
|
|
||||||
FStringValueInner::Single(FStringPart::FString(fstring)) => Some(fstring),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a slice of all the [`FStringPart`]s contained in this value.
|
/// Returns a slice of all the [`FStringPart`]s contained in this value.
|
||||||
pub fn as_slice(&self) -> &[FStringPart] {
|
pub fn as_slice(&self) -> &[FStringPart] {
|
||||||
match &self.inner {
|
match &self.inner {
|
||||||
|
|
@ -1290,7 +1292,7 @@ pub struct ExprStringLiteral {
|
||||||
impl ExprStringLiteral {
|
impl ExprStringLiteral {
|
||||||
/// Return `Some(literal)` if the string only consists of a single `StringLiteral` part
|
/// Return `Some(literal)` if the string only consists of a single `StringLiteral` part
|
||||||
/// (indicating that it is not implicitly concatenated). Otherwise, return `None`.
|
/// (indicating that it is not implicitly concatenated). Otherwise, return `None`.
|
||||||
pub fn as_unconcatenated_literal(&self) -> Option<&StringLiteral> {
|
pub fn as_single_part_string(&self) -> Option<&StringLiteral> {
|
||||||
match &self.value.inner {
|
match &self.value.inner {
|
||||||
StringLiteralValueInner::Single(value) => Some(value),
|
StringLiteralValueInner::Single(value) => Some(value),
|
||||||
StringLiteralValueInner::Concatenated(_) => None,
|
StringLiteralValueInner::Concatenated(_) => None,
|
||||||
|
|
@ -1728,6 +1730,17 @@ pub struct ExprBytesLiteral {
|
||||||
pub value: BytesLiteralValue,
|
pub value: BytesLiteralValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExprBytesLiteral {
|
||||||
|
/// Return `Some(literal)` if the bytestring only consists of a single `BytesLiteral` part
|
||||||
|
/// (indicating that it is not implicitly concatenated). Otherwise, return `None`.
|
||||||
|
pub const fn as_single_part_bytestring(&self) -> Option<&BytesLiteral> {
|
||||||
|
match &self.value.inner {
|
||||||
|
BytesLiteralValueInner::Single(value) => Some(value),
|
||||||
|
BytesLiteralValueInner::Concatenated(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The value representing a [`ExprBytesLiteral`].
|
/// The value representing a [`ExprBytesLiteral`].
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct BytesLiteralValue {
|
pub struct BytesLiteralValue {
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,7 @@ pub struct FormatExprBytesLiteral;
|
||||||
|
|
||||||
impl FormatNodeRule<ExprBytesLiteral> for FormatExprBytesLiteral {
|
impl FormatNodeRule<ExprBytesLiteral> for FormatExprBytesLiteral {
|
||||||
fn fmt_fields(&self, item: &ExprBytesLiteral, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprBytesLiteral, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
let ExprBytesLiteral { value, .. } = item;
|
if let Some(bytes_literal) = item.as_single_part_bytestring() {
|
||||||
|
|
||||||
if let [bytes_literal] = value.as_slice() {
|
|
||||||
bytes_literal.format().fmt(f)
|
bytes_literal.format().fmt(f)
|
||||||
} else {
|
} else {
|
||||||
// Always join byte literals that aren't parenthesized and thus, always on a single line.
|
// Always join byte literals that aren't parenthesized and thus, always on a single line.
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,7 @@ pub struct FormatExprFString;
|
||||||
|
|
||||||
impl FormatNodeRule<ExprFString> for FormatExprFString {
|
impl FormatNodeRule<ExprFString> for FormatExprFString {
|
||||||
fn fmt_fields(&self, item: &ExprFString, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprFString, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
let ExprFString { value, .. } = item;
|
if let Some(f_string) = item.as_single_part_fstring() {
|
||||||
|
|
||||||
if let [f_string_part] = value.as_slice() {
|
|
||||||
// SAFETY: A single string literal cannot be an f-string. This is guaranteed by the
|
|
||||||
// [`ruff_python_ast::FStringValue::single`] constructor.
|
|
||||||
let f_string = f_string_part.as_f_string().unwrap();
|
|
||||||
|
|
||||||
f_string.format().fmt(f)
|
f_string.format().fmt(f)
|
||||||
} else {
|
} else {
|
||||||
// Always join fstrings that aren't parenthesized and thus, are always on a single line.
|
// Always join fstrings that aren't parenthesized and thus, are always on a single line.
|
||||||
|
|
@ -44,16 +38,18 @@ impl NeedsParentheses for ExprFString {
|
||||||
_parent: AnyNodeRef,
|
_parent: AnyNodeRef,
|
||||||
context: &PyFormatContext,
|
context: &PyFormatContext,
|
||||||
) -> OptionalParentheses {
|
) -> OptionalParentheses {
|
||||||
if self.value.is_implicit_concatenated() {
|
if let Some(fstring_part) = self.as_single_part_fstring() {
|
||||||
OptionalParentheses::Multiline
|
// The f-string is not implicitly concatenated
|
||||||
} else if StringLike::FString(self).is_multiline(context)
|
if StringLike::FString(self).is_multiline(context)
|
||||||
|| self.value.as_single().is_some_and(|f_string| {
|
|| FStringLayout::from_f_string(fstring_part, context.source()).is_multiline()
|
||||||
FStringLayout::from_f_string(f_string, context.source()).is_multiline()
|
{
|
||||||
})
|
OptionalParentheses::Never
|
||||||
{
|
} else {
|
||||||
OptionalParentheses::Never
|
OptionalParentheses::BestFit
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
OptionalParentheses::BestFit
|
// The f-string is implicitly concatenated
|
||||||
|
OptionalParentheses::Multiline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ impl FormatRuleWithOptions<ExprStringLiteral, PyFormatContext<'_>> for FormatExp
|
||||||
|
|
||||||
impl FormatNodeRule<ExprStringLiteral> for FormatExprStringLiteral {
|
impl FormatNodeRule<ExprStringLiteral> for FormatExprStringLiteral {
|
||||||
fn fmt_fields(&self, item: &ExprStringLiteral, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprStringLiteral, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
if let Some(string_literal) = item.as_unconcatenated_literal() {
|
if let Some(string_literal) = item.as_single_part_string() {
|
||||||
string_literal.format().with_options(self.kind).fmt(f)
|
string_literal.format().with_options(self.kind).fmt(f)
|
||||||
} else {
|
} else {
|
||||||
// Always join strings that aren't parenthesized and thus, always on a single line.
|
// Always join strings that aren't parenthesized and thus, always on a single line.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_formatter::{format_args, write, FormatError, RemoveSoftLinesBuffer};
|
use ruff_formatter::{format_args, write, FormatError, RemoveSoftLinesBuffer};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
AnyNodeRef, Expr, ExprAttribute, ExprCall, FString, FStringPart, Operator, StmtAssign,
|
AnyNodeRef, Expr, ExprAttribute, ExprCall, FString, Operator, StmtAssign, StringLike,
|
||||||
StringLike, TypeParams,
|
TypeParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::builders::parenthesize_if_expands;
|
use crate::builders::parenthesize_if_expands;
|
||||||
|
|
@ -1107,9 +1107,7 @@ fn format_f_string_assignment<'a>(
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let [FStringPart::FString(f_string)] = expr.value.as_slice() else {
|
let f_string = expr.as_single_part_fstring()?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the f-string is flat, there are no breakpoints from which it can be made multiline.
|
// If the f-string is flat, there are no breakpoints from which it can be made multiline.
|
||||||
// This is the case when the f-string has no expressions or if it does then the expressions
|
// This is the case when the f-string has no expressions or if it does then the expressions
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ pub fn parse_type_annotation(
|
||||||
string_expr: &ExprStringLiteral,
|
string_expr: &ExprStringLiteral,
|
||||||
source: &str,
|
source: &str,
|
||||||
) -> AnnotationParseResult {
|
) -> AnnotationParseResult {
|
||||||
if let Some(string_literal) = string_expr.as_unconcatenated_literal() {
|
if let Some(string_literal) = string_expr.as_single_part_string() {
|
||||||
// Compare the raw contents (without quotes) of the expression with the parsed contents
|
// Compare the raw contents (without quotes) of the expression with the parsed contents
|
||||||
// contained in the string literal.
|
// contained in the string literal.
|
||||||
if &source[string_literal.content_range()] == string_literal.as_str() {
|
if &source[string_literal.content_range()] == string_literal.as_str() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue