mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-18 09:30:35 +00:00
Preserve triple quotes and prefixes for strings (#15818)
## Summary This is a follow-up to #15726, #15778, and #15794 to preserve the triple quote and prefix flags in plain strings, bytestrings, and f-strings. I also added a `StringLiteralFlags::without_triple_quotes` method to avoid passing along triple quotes in rules like SIM905 where it might not make sense, as discussed [here](https://github.com/astral-sh/ruff/pull/15726#discussion_r1930532426). ## Test Plan Existing tests, plus many new cases in the `generator::tests::quote` test that should cover all combinations of quotes and prefixes, at least for simple string bodies. Closes #7799 when combined with #15694, #15726, #15778, and #15794. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
9a33924a65
commit
b5e5271adf
16 changed files with 318 additions and 141 deletions
|
@ -13,10 +13,10 @@ use itertools::Itertools;
|
|||
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
|
||||
use crate::name::Name;
|
||||
use crate::{
|
||||
int,
|
||||
str::Quote,
|
||||
name::Name,
|
||||
str::{Quote, TripleQuotes},
|
||||
str_prefix::{AnyStringPrefix, ByteStringPrefix, FStringPrefix, StringLiteralPrefix},
|
||||
ExceptHandler, Expr, FStringElement, LiteralExpressionRef, Pattern, Stmt, TypeParam,
|
||||
};
|
||||
|
@ -981,25 +981,24 @@ pub trait StringFlags: Copy {
|
|||
/// Does the string use single or double quotes in its opener and closer?
|
||||
fn quote_style(self) -> Quote;
|
||||
|
||||
/// Is the string triple-quoted, i.e.,
|
||||
/// does it begin and end with three consecutive quote characters?
|
||||
fn is_triple_quoted(self) -> bool;
|
||||
fn triple_quotes(self) -> TripleQuotes;
|
||||
|
||||
fn prefix(self) -> AnyStringPrefix;
|
||||
|
||||
/// Is the string triple-quoted, i.e.,
|
||||
/// does it begin and end with three consecutive quote characters?
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
self.triple_quotes().is_yes()
|
||||
}
|
||||
|
||||
/// A `str` representation of the quotes used to start and close.
|
||||
/// This does not include any prefixes the string has in its opener.
|
||||
fn quote_str(self) -> &'static str {
|
||||
if self.is_triple_quoted() {
|
||||
match self.quote_style() {
|
||||
Quote::Single => "'''",
|
||||
Quote::Double => r#"""""#,
|
||||
}
|
||||
} else {
|
||||
match self.quote_style() {
|
||||
Quote::Single => "'",
|
||||
Quote::Double => "\"",
|
||||
}
|
||||
match (self.triple_quotes(), self.quote_style()) {
|
||||
(TripleQuotes::Yes, Quote::Single) => "'''",
|
||||
(TripleQuotes::Yes, Quote::Double) => r#"""""#,
|
||||
(TripleQuotes::No, Quote::Single) => "'",
|
||||
(TripleQuotes::No, Quote::Double) => "\"",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1028,10 +1027,30 @@ pub trait StringFlags: Copy {
|
|||
self.quote_len()
|
||||
}
|
||||
|
||||
fn format_string_contents(self, contents: &str) -> String {
|
||||
let prefix = self.prefix();
|
||||
let quote_str = self.quote_str();
|
||||
format!("{prefix}{quote_str}{contents}{quote_str}")
|
||||
fn display_contents(self, contents: &str) -> DisplayFlags {
|
||||
DisplayFlags {
|
||||
prefix: self.prefix(),
|
||||
quote_str: self.quote_str(),
|
||||
contents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayFlags<'a> {
|
||||
prefix: AnyStringPrefix,
|
||||
quote_str: &'a str,
|
||||
contents: &'a str,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DisplayFlags<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{prefix}{quote}{contents}{quote}",
|
||||
prefix = self.prefix,
|
||||
quote = self.quote_str,
|
||||
contents = self.contents
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1097,8 +1116,9 @@ impl FStringFlags {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_triple_quotes(mut self) -> Self {
|
||||
self.0 |= FStringFlagsInner::TRIPLE_QUOTED;
|
||||
pub fn with_triple_quotes(mut self, triple_quotes: TripleQuotes) -> Self {
|
||||
self.0
|
||||
.set(FStringFlagsInner::TRIPLE_QUOTED, triple_quotes.is_yes());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -1132,8 +1152,12 @@ impl StringFlags for FStringFlags {
|
|||
/// Return `true` if the f-string is triple-quoted, i.e.,
|
||||
/// it begins and ends with three consecutive quote characters.
|
||||
/// For example: `f"""{bar}"""`
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
self.0.contains(FStringFlagsInner::TRIPLE_QUOTED)
|
||||
fn triple_quotes(self) -> TripleQuotes {
|
||||
if self.0.contains(FStringFlagsInner::TRIPLE_QUOTED) {
|
||||
TripleQuotes::Yes
|
||||
} else {
|
||||
TripleQuotes::No
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the quoting style (single or double quotes)
|
||||
|
@ -1477,8 +1501,11 @@ impl StringLiteralFlags {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_triple_quotes(mut self) -> Self {
|
||||
self.0 |= StringLiteralFlagsInner::TRIPLE_QUOTED;
|
||||
pub fn with_triple_quotes(mut self, triple_quotes: TripleQuotes) -> Self {
|
||||
self.0.set(
|
||||
StringLiteralFlagsInner::TRIPLE_QUOTED,
|
||||
triple_quotes.is_yes(),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -1550,8 +1577,12 @@ impl StringFlags for StringLiteralFlags {
|
|||
/// Return `true` if the string is triple-quoted, i.e.,
|
||||
/// it begins and ends with three consecutive quote characters.
|
||||
/// For example: `"""bar"""`
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
self.0.contains(StringLiteralFlagsInner::TRIPLE_QUOTED)
|
||||
fn triple_quotes(self) -> TripleQuotes {
|
||||
if self.0.contains(StringLiteralFlagsInner::TRIPLE_QUOTED) {
|
||||
TripleQuotes::Yes
|
||||
} else {
|
||||
TripleQuotes::No
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
|
@ -1866,8 +1897,11 @@ impl BytesLiteralFlags {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_triple_quotes(mut self) -> Self {
|
||||
self.0 |= BytesLiteralFlagsInner::TRIPLE_QUOTED;
|
||||
pub fn with_triple_quotes(mut self, triple_quotes: TripleQuotes) -> Self {
|
||||
self.0.set(
|
||||
BytesLiteralFlagsInner::TRIPLE_QUOTED,
|
||||
triple_quotes.is_yes(),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -1910,8 +1944,12 @@ impl StringFlags for BytesLiteralFlags {
|
|||
/// Return `true` if the bytestring is triple-quoted, i.e.,
|
||||
/// it begins and ends with three consecutive quote characters.
|
||||
/// For example: `b"""{bar}"""`
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
self.0.contains(BytesLiteralFlagsInner::TRIPLE_QUOTED)
|
||||
fn triple_quotes(self) -> TripleQuotes {
|
||||
if self.0.contains(BytesLiteralFlagsInner::TRIPLE_QUOTED) {
|
||||
TripleQuotes::Yes
|
||||
} else {
|
||||
TripleQuotes::No
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the quoting style (single or double quotes)
|
||||
|
@ -2035,7 +2073,7 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct AnyStringFlags(AnyStringFlagsInner);
|
||||
|
||||
impl AnyStringFlags {
|
||||
|
@ -2073,13 +2111,11 @@ impl AnyStringFlags {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn new(prefix: AnyStringPrefix, quotes: Quote, triple_quoted: bool) -> Self {
|
||||
let new = Self::default().with_prefix(prefix).with_quote_style(quotes);
|
||||
if triple_quoted {
|
||||
new.with_triple_quotes()
|
||||
} else {
|
||||
new
|
||||
}
|
||||
pub fn new(prefix: AnyStringPrefix, quotes: Quote, triple_quotes: TripleQuotes) -> Self {
|
||||
Self(AnyStringFlagsInner::empty())
|
||||
.with_prefix(prefix)
|
||||
.with_quote_style(quotes)
|
||||
.with_triple_quotes(triple_quotes)
|
||||
}
|
||||
|
||||
/// Does the string have a `u` or `U` prefix?
|
||||
|
@ -2114,8 +2150,9 @@ impl AnyStringFlags {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_triple_quotes(mut self) -> Self {
|
||||
self.0 |= AnyStringFlagsInner::TRIPLE_QUOTED;
|
||||
pub fn with_triple_quotes(mut self, triple_quotes: TripleQuotes) -> Self {
|
||||
self.0
|
||||
.set(AnyStringFlagsInner::TRIPLE_QUOTED, triple_quotes.is_yes());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -2130,10 +2167,12 @@ impl StringFlags for AnyStringFlags {
|
|||
}
|
||||
}
|
||||
|
||||
/// Is the string triple-quoted, i.e.,
|
||||
/// does it begin and end with three consecutive quote characters?
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
self.0.contains(AnyStringFlagsInner::TRIPLE_QUOTED)
|
||||
fn triple_quotes(self) -> TripleQuotes {
|
||||
if self.0.contains(AnyStringFlagsInner::TRIPLE_QUOTED) {
|
||||
TripleQuotes::Yes
|
||||
} else {
|
||||
TripleQuotes::No
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
|
@ -2193,14 +2232,10 @@ impl From<AnyStringFlags> for StringLiteralFlags {
|
|||
value.prefix()
|
||||
)
|
||||
};
|
||||
let new = StringLiteralFlags::empty()
|
||||
StringLiteralFlags::empty()
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix);
|
||||
if value.is_triple_quoted() {
|
||||
new.with_triple_quotes()
|
||||
} else {
|
||||
new
|
||||
}
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2209,7 +2244,7 @@ impl From<StringLiteralFlags> for AnyStringFlags {
|
|||
Self::new(
|
||||
AnyStringPrefix::Regular(value.prefix()),
|
||||
value.quote_style(),
|
||||
value.is_triple_quoted(),
|
||||
value.triple_quotes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2222,14 +2257,10 @@ impl From<AnyStringFlags> for BytesLiteralFlags {
|
|||
value.prefix()
|
||||
)
|
||||
};
|
||||
let new = BytesLiteralFlags::empty()
|
||||
BytesLiteralFlags::empty()
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(bytestring_prefix);
|
||||
if value.is_triple_quoted() {
|
||||
new.with_triple_quotes()
|
||||
} else {
|
||||
new
|
||||
}
|
||||
.with_prefix(bytestring_prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2238,7 +2269,7 @@ impl From<BytesLiteralFlags> for AnyStringFlags {
|
|||
Self::new(
|
||||
AnyStringPrefix::Bytes(value.prefix()),
|
||||
value.quote_style(),
|
||||
value.is_triple_quoted(),
|
||||
value.triple_quotes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2251,14 +2282,10 @@ impl From<AnyStringFlags> for FStringFlags {
|
|||
value.prefix()
|
||||
)
|
||||
};
|
||||
let new = FStringFlags::empty()
|
||||
FStringFlags::empty()
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(fstring_prefix);
|
||||
if value.is_triple_quoted() {
|
||||
new.with_triple_quotes()
|
||||
} else {
|
||||
new
|
||||
}
|
||||
.with_prefix(fstring_prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2267,7 +2294,7 @@ impl From<FStringFlags> for AnyStringFlags {
|
|||
Self::new(
|
||||
AnyStringPrefix::Format(value.prefix()),
|
||||
value.quote_style(),
|
||||
value.is_triple_quoted(),
|
||||
value.triple_quotes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue