Improved error recovery for unclosed strings (including f- and t-strings) (#20848)
This commit is contained in:
@@ -735,6 +735,8 @@ pub trait StringFlags: Copy {
|
||||
|
||||
fn prefix(self) -> AnyStringPrefix;
|
||||
|
||||
fn is_unclosed(self) -> bool;
|
||||
|
||||
/// Is the string triple-quoted, i.e.,
|
||||
/// does it begin and end with three consecutive quote characters?
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
@@ -779,6 +781,7 @@ pub trait StringFlags: Copy {
|
||||
|
||||
fn as_any_string_flags(self) -> AnyStringFlags {
|
||||
AnyStringFlags::new(self.prefix(), self.quote_style(), self.triple_quotes())
|
||||
.with_unclosed(self.is_unclosed())
|
||||
}
|
||||
|
||||
fn display_contents(self, contents: &str) -> DisplayFlags<'_> {
|
||||
@@ -829,6 +832,10 @@ bitflags! {
|
||||
/// for why we track the casing of the `r` prefix,
|
||||
/// but not for any other prefix
|
||||
const R_PREFIX_UPPER = 1 << 3;
|
||||
|
||||
/// The f-string is unclosed, meaning it is missing a closing quote.
|
||||
/// For example: `f"{bar`
|
||||
const UNCLOSED = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,6 +894,12 @@ impl FStringFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(InterpolatedStringFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(mut self, prefix: FStringPrefix) -> Self {
|
||||
match prefix {
|
||||
@@ -984,6 +997,12 @@ impl TStringFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(InterpolatedStringFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(mut self, prefix: TStringPrefix) -> Self {
|
||||
match prefix {
|
||||
@@ -1051,6 +1070,10 @@ impl StringFlags for FStringFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Format(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(InterpolatedStringFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FStringFlags {
|
||||
@@ -1059,6 +1082,7 @@ impl fmt::Debug for FStringFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1090,6 +1114,10 @@ impl StringFlags for TStringFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Template(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(InterpolatedStringFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TStringFlags {
|
||||
@@ -1098,6 +1126,7 @@ impl fmt::Debug for TStringFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1427,6 +1456,9 @@ bitflags! {
|
||||
|
||||
/// The string was deemed invalid by the parser.
|
||||
const INVALID = 1 << 5;
|
||||
|
||||
/// The string literal misses the matching closing quote(s).
|
||||
const UNCLOSED = 1 << 6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,6 +1511,12 @@ impl StringLiteralFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(StringLiteralFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(self, prefix: StringLiteralPrefix) -> Self {
|
||||
let StringLiteralFlags(flags) = self;
|
||||
@@ -1560,6 +1598,10 @@ impl StringFlags for StringLiteralFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Regular(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(StringLiteralFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StringLiteralFlags {
|
||||
@@ -1568,6 +1610,7 @@ impl fmt::Debug for StringLiteralFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1846,6 +1889,9 @@ bitflags! {
|
||||
|
||||
/// The bytestring was deemed invalid by the parser.
|
||||
const INVALID = 1 << 4;
|
||||
|
||||
/// The byte string misses the matching closing quote(s).
|
||||
const UNCLOSED = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1897,6 +1943,12 @@ impl BytesLiteralFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(BytesLiteralFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(mut self, prefix: ByteStringPrefix) -> Self {
|
||||
match prefix {
|
||||
@@ -1959,6 +2011,10 @@ impl StringFlags for BytesLiteralFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Bytes(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(BytesLiteralFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BytesLiteralFlags {
|
||||
@@ -1967,6 +2023,7 @@ impl fmt::Debug for BytesLiteralFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -2028,7 +2085,7 @@ bitflags! {
|
||||
/// prefix flags is by calling the `as_flags()` method on the
|
||||
/// `StringPrefix` enum.
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct AnyStringFlagsInner: u8 {
|
||||
struct AnyStringFlagsInner: u16 {
|
||||
/// The string uses double quotes (`"`).
|
||||
/// If this flag is not set, the string uses single quotes (`'`).
|
||||
const DOUBLE = 1 << 0;
|
||||
@@ -2071,6 +2128,9 @@ bitflags! {
|
||||
/// for why we track the casing of the `r` prefix,
|
||||
/// but not for any other prefix
|
||||
const R_PREFIX_UPPER = 1 << 7;
|
||||
|
||||
/// String without matching closing quote(s).
|
||||
const UNCLOSED = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2166,6 +2226,12 @@ impl AnyStringFlags {
|
||||
.set(AnyStringFlagsInner::TRIPLE_QUOTED, triple_quotes.is_yes());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(AnyStringFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl StringFlags for AnyStringFlags {
|
||||
@@ -2234,6 +2300,10 @@ impl StringFlags for AnyStringFlags {
|
||||
}
|
||||
AnyStringPrefix::Regular(StringLiteralPrefix::Empty)
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(AnyStringFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyStringFlags {
|
||||
@@ -2242,6 +2312,7 @@ impl fmt::Debug for AnyStringFlags {
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -2258,6 +2329,7 @@ impl From<AnyStringFlags> for StringLiteralFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2279,6 +2351,7 @@ impl From<AnyStringFlags> for BytesLiteralFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(bytestring_prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2300,6 +2373,7 @@ impl From<AnyStringFlags> for FStringFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2321,6 +2395,7 @@ impl From<AnyStringFlags> for TStringFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user