mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:48 +00:00
Disallow newlines in format specifiers of single quoted f- or t-strings (#18708)
This commit is contained in:
parent
23261a38a0
commit
1188ffccc4
17 changed files with 521 additions and 513 deletions
|
@ -74,8 +74,7 @@ x = f"a{2+2:=^{foo(x+y**2):something else}one more}b"
|
||||||
f'{(abc:=10)}'
|
f'{(abc:=10)}'
|
||||||
|
|
||||||
f"This is a really long string, but just make sure that you reflow fstrings {
|
f"This is a really long string, but just make sure that you reflow fstrings {
|
||||||
2+2:d
|
2+2:d}"
|
||||||
}"
|
|
||||||
f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}"
|
f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}"
|
||||||
|
|
||||||
f"{2+2=}"
|
f"{2+2=}"
|
||||||
|
|
|
@ -278,16 +278,7 @@ x = f"aaaaaaaaa { x = !r }"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = f"{x = !s
|
x = f"{x = !s
|
||||||
:>0
|
:>0}"
|
||||||
|
|
||||||
}"
|
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = f"{x !s
|
|
||||||
:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -295,6 +286,13 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
|
@ -311,14 +309,14 @@ x = f"""{"foo " + # comment 24
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
f"{ # comment 26
|
f"""{ # comment 26
|
||||||
foo # after foo
|
foo # after foo
|
||||||
:>{
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
|
|
||||||
f"""{foo
|
f"""{foo
|
||||||
|
@ -332,8 +330,7 @@ f"""{foo
|
||||||
f"{
|
f"{
|
||||||
# comment 31
|
# comment 31
|
||||||
foo
|
foo
|
||||||
:>
|
:>}"
|
||||||
}"
|
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
|
@ -487,13 +484,11 @@ aaaaa[aaaaaaaaaaa] = (
|
||||||
|
|
||||||
# This is not a multiline f-string even though it has a newline after the format specifier.
|
# This is not a multiline f-string even though it has a newline after the format specifier.
|
||||||
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = (
|
aaaaaaaaaaaaaaaaaa = (
|
||||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted f-string.
|
# The newline is only considered when it's a tripled-quoted f-string.
|
||||||
|
|
|
@ -274,16 +274,7 @@ x = t"aaaaaaaaa { x = !r }"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = t"{x = !s
|
x = t"{x = !s
|
||||||
:>0
|
:>0}"
|
||||||
|
|
||||||
}"
|
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = t"{x !s
|
|
||||||
:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -291,6 +282,13 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
x = t"""
|
x = t"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -306,14 +304,14 @@ x = t"""{"foo " + # comment 24
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
t"{ # comment 26
|
t"""{ # comment 26
|
||||||
foo # after foo
|
foo # after foo
|
||||||
:>{
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
|
@ -467,13 +465,11 @@ aaaaa[aaaaaaaaaaa] = (
|
||||||
|
|
||||||
# This is not a multiline t-string even though it has a newline after the format specifier.
|
# This is not a multiline t-string even though it has a newline after the format specifier.
|
||||||
aaaaaaaaaaaaaaaaaa = t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
aaaaaaaaaaaaaaaaaa = t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = (
|
aaaaaaaaaaaaaaaaaa = (
|
||||||
t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted t-string.
|
# The newline is only considered when it's a tripled-quoted t-string.
|
||||||
|
|
|
@ -323,8 +323,6 @@ fn handle_enclosed_comment<'a>(
|
||||||
AnyNodeRef::TString(tstring) => CommentPlacement::dangling(tstring, comment),
|
AnyNodeRef::TString(tstring) => CommentPlacement::dangling(tstring, comment),
|
||||||
AnyNodeRef::InterpolatedElement(element) => {
|
AnyNodeRef::InterpolatedElement(element) => {
|
||||||
if let Some(preceding) = comment.preceding_node() {
|
if let Some(preceding) = comment.preceding_node() {
|
||||||
if comment.line_position().is_own_line() && element.format_spec.is_some() {
|
|
||||||
return if comment.following_node().is_some() {
|
|
||||||
// Own line comment before format specifier
|
// Own line comment before format specifier
|
||||||
// ```py
|
// ```py
|
||||||
// aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
// aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
|
@ -332,18 +330,11 @@ fn handle_enclosed_comment<'a>(
|
||||||
// # comment
|
// # comment
|
||||||
// :.3f} cccccccccc"""
|
// :.3f} cccccccccc"""
|
||||||
// ```
|
// ```
|
||||||
CommentPlacement::trailing(preceding, comment)
|
if comment.line_position().is_own_line()
|
||||||
} else {
|
&& element.format_spec.is_some()
|
||||||
// TODO: This can be removed once format specifiers with a newline are a syntax error.
|
&& comment.following_node().is_some()
|
||||||
// This is to handle cases like:
|
{
|
||||||
// ```py
|
return CommentPlacement::trailing(preceding, comment);
|
||||||
// x = f"{x !s
|
|
||||||
// :>0
|
|
||||||
// # comment 21
|
|
||||||
// }"
|
|
||||||
// ```
|
|
||||||
CommentPlacement::trailing(element, comment)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ruff_python_ast::{
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextSlice};
|
use ruff_text_size::{Ranged, TextSlice};
|
||||||
|
|
||||||
use crate::comments::{dangling_open_parenthesis_comments, trailing_comments};
|
use crate::comments::dangling_open_parenthesis_comments;
|
||||||
use crate::context::{
|
use crate::context::{
|
||||||
InterpolatedStringState, NodeLevel, WithInterpolatedStringState, WithNodeLevel,
|
InterpolatedStringState, NodeLevel, WithInterpolatedStringState, WithNodeLevel,
|
||||||
};
|
};
|
||||||
|
@ -203,7 +203,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
// # comment 27
|
// # comment 27
|
||||||
// :test}"
|
// :test}"
|
||||||
// ```
|
// ```
|
||||||
if comments.has_trailing_own_line(expression) {
|
if comments.has_trailing(expression) {
|
||||||
soft_line_break().fmt(f)?;
|
soft_line_break().fmt(f)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,31 +214,6 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// These trailing comments can only occur if the format specifier is
|
|
||||||
// present. For example,
|
|
||||||
//
|
|
||||||
// ```python
|
|
||||||
// f"{
|
|
||||||
// x:.3f
|
|
||||||
// # comment
|
|
||||||
// }"
|
|
||||||
// ```
|
|
||||||
|
|
||||||
// This can also be triggered outside of a format spec, at
|
|
||||||
// least until https://github.com/astral-sh/ruff/issues/18632 is a syntax error
|
|
||||||
// TODO(https://github.com/astral-sh/ruff/issues/18632) Remove this
|
|
||||||
// and double check if it is still necessary for the triple quoted case
|
|
||||||
// once this is a syntax error.
|
|
||||||
// ```py
|
|
||||||
// f"{
|
|
||||||
// foo
|
|
||||||
// :{x}
|
|
||||||
// # comment 28
|
|
||||||
// } woah {x}"
|
|
||||||
// ```
|
|
||||||
// Any other trailing comments are attached to the expression itself.
|
|
||||||
trailing_comments(comments.trailing(self.element)).fmt(f)?;
|
|
||||||
|
|
||||||
if conversion.is_none() && format_spec.is_none() {
|
if conversion.is_none() && format_spec.is_none() {
|
||||||
bracket_spacing.fmt(f)?;
|
bracket_spacing.fmt(f)?;
|
||||||
}
|
}
|
||||||
|
@ -258,15 +233,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
|
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
|
||||||
|
|
||||||
if self.context.is_multiline() {
|
if self.context.is_multiline() {
|
||||||
// TODO: The `or comments.has_trailing...` can be removed once newlines in format specs are a syntax error.
|
if format_spec.is_none() {
|
||||||
// This is to support the following case:
|
|
||||||
// ```py
|
|
||||||
// x = f"{x !s
|
|
||||||
// :>0
|
|
||||||
// # comment 21
|
|
||||||
// }"
|
|
||||||
// ```
|
|
||||||
if format_spec.is_none() || comments.has_trailing_own_line(self.element) {
|
|
||||||
group(&format_args![
|
group(&format_args![
|
||||||
open_parenthesis_comments,
|
open_parenthesis_comments,
|
||||||
soft_block_indent(&item)
|
soft_block_indent(&item)
|
||||||
|
@ -276,6 +243,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
// For strings ending with a format spec, don't add a newline between the end of the format spec
|
// For strings ending with a format spec, don't add a newline between the end of the format spec
|
||||||
// and closing curly brace because that is invalid syntax for single quoted strings and
|
// and closing curly brace because that is invalid syntax for single quoted strings and
|
||||||
// the newline is preserved as part of the format spec for triple quoted strings.
|
// the newline is preserved as part of the format spec for triple quoted strings.
|
||||||
|
|
||||||
group(&format_args![
|
group(&format_args![
|
||||||
open_parenthesis_comments,
|
open_parenthesis_comments,
|
||||||
indent(&format_args![soft_line_break(), item])
|
indent(&format_args![soft_line_break(), item])
|
||||||
|
|
|
@ -324,7 +324,12 @@ fn format_file(source: &str, options: &PyFormatOptions, input_path: &Path) -> St
|
||||||
|
|
||||||
(Cow::Owned(without_markers), content)
|
(Cow::Owned(without_markers), content)
|
||||||
} else {
|
} else {
|
||||||
let printed = format_module_source(source, options.clone()).expect("Formatting to succeed");
|
let printed = format_module_source(source, options.clone()).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Formatting `{input_path} was expected to succeed but it failed: {err}",
|
||||||
|
input_path = input_path.display()
|
||||||
|
)
|
||||||
|
});
|
||||||
let formatted_code = printed.into_code();
|
let formatted_code = printed.into_code();
|
||||||
|
|
||||||
ensure_stability_when_formatting_twice(&formatted_code, options, input_path);
|
ensure_stability_when_formatting_twice(&formatted_code, options, input_path);
|
||||||
|
|
|
@ -81,8 +81,7 @@ x = f"a{2+2:=^{foo(x+y**2):something else}one more}b"
|
||||||
f'{(abc:=10)}'
|
f'{(abc:=10)}'
|
||||||
|
|
||||||
f"This is a really long string, but just make sure that you reflow fstrings {
|
f"This is a really long string, but just make sure that you reflow fstrings {
|
||||||
2+2:d
|
2+2:d}"
|
||||||
}"
|
|
||||||
f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}"
|
f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}"
|
||||||
|
|
||||||
f"{2+2=}"
|
f"{2+2=}"
|
||||||
|
|
|
@ -284,16 +284,7 @@ x = f"aaaaaaaaa { x = !r }"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = f"{x = !s
|
x = f"{x = !s
|
||||||
:>0
|
:>0}"
|
||||||
|
|
||||||
}"
|
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = f"{x !s
|
|
||||||
:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -301,6 +292,13 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
|
@ -317,14 +315,14 @@ x = f"""{"foo " + # comment 24
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
f"{ # comment 26
|
f"""{ # comment 26
|
||||||
foo # after foo
|
foo # after foo
|
||||||
:>{
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
|
|
||||||
f"""{foo
|
f"""{foo
|
||||||
|
@ -338,8 +336,7 @@ f"""{foo
|
||||||
f"{
|
f"{
|
||||||
# comment 31
|
# comment 31
|
||||||
foo
|
foo
|
||||||
:>
|
:>}"
|
||||||
}"
|
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
|
@ -493,13 +490,11 @@ aaaaa[aaaaaaaaaaa] = (
|
||||||
|
|
||||||
# This is not a multiline f-string even though it has a newline after the format specifier.
|
# This is not a multiline f-string even though it has a newline after the format specifier.
|
||||||
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = (
|
aaaaaaaaaaaaaaaaaa = (
|
||||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted f-string.
|
# The newline is only considered when it's a tripled-quoted f-string.
|
||||||
|
@ -1071,13 +1066,6 @@ x = f"aaaaaaaaa { x = !r}"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = f"{x = !s:>0}"
|
x = f"{x = !s:>0}"
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = f"{
|
|
||||||
x!s:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -1085,6 +1073,15 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{
|
||||||
|
1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{
|
||||||
|
1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
|
@ -1102,13 +1099,14 @@ x = f"""{
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
f"{ # comment 26
|
f"""{ # comment 26
|
||||||
foo:>{ # after foo
|
foo # after foo
|
||||||
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
|
|
||||||
f"""{
|
f"""{
|
||||||
|
@ -1895,13 +1893,6 @@ x = f"aaaaaaaaa { x = !r}"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = f"{x = !s:>0}"
|
x = f"{x = !s:>0}"
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = f"{
|
|
||||||
x!s:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -1909,6 +1900,15 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{
|
||||||
|
1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{
|
||||||
|
1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
|
@ -1926,13 +1926,14 @@ x = f"""{
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
f"{ # comment 26
|
f"""{ # comment 26
|
||||||
foo:>{ # after foo
|
foo # after foo
|
||||||
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
|
|
||||||
f"""{
|
f"""{
|
||||||
|
|
|
@ -280,16 +280,7 @@ x = t"aaaaaaaaa { x = !r }"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = t"{x = !s
|
x = t"{x = !s
|
||||||
:>0
|
:>0}"
|
||||||
|
|
||||||
}"
|
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = t"{x !s
|
|
||||||
:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -297,6 +288,13 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
x = t"""
|
x = t"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -312,14 +310,14 @@ x = t"""{"foo " + # comment 24
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
t"{ # comment 26
|
t"""{ # comment 26
|
||||||
foo # after foo
|
foo # after foo
|
||||||
:>{
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
|
@ -473,13 +471,11 @@ aaaaa[aaaaaaaaaaa] = (
|
||||||
|
|
||||||
# This is not a multiline t-string even though it has a newline after the format specifier.
|
# This is not a multiline t-string even though it has a newline after the format specifier.
|
||||||
aaaaaaaaaaaaaaaaaa = t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
aaaaaaaaaaaaaaaaaa = t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = (
|
aaaaaaaaaaaaaaaaaa = (
|
||||||
t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
t"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
a:.3f
|
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
|
||||||
}moreeeeeeeeeeeeeeeeeetest" # comment
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted t-string.
|
# The newline is only considered when it's a tripled-quoted t-string.
|
||||||
|
@ -1045,13 +1041,6 @@ x = t"aaaaaaaaa { x = !r}"
|
||||||
|
|
||||||
# Combine conversion flags with format specifiers
|
# Combine conversion flags with format specifiers
|
||||||
x = t"{x = !s:>0}"
|
x = t"{x = !s:>0}"
|
||||||
# This is interesting. There can be a comment after the format specifier but only if it's
|
|
||||||
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
|
|
||||||
# We'll format is as trailing comments.
|
|
||||||
x = t"{
|
|
||||||
x!s:>0
|
|
||||||
# comment 21
|
|
||||||
}"
|
|
||||||
|
|
||||||
x = f"{
|
x = f"{
|
||||||
x!s:>{
|
x!s:>{
|
||||||
|
@ -1059,6 +1048,15 @@ x = f"{
|
||||||
# comment 21-2
|
# comment 21-2
|
||||||
}}"
|
}}"
|
||||||
|
|
||||||
|
f"{
|
||||||
|
1
|
||||||
|
# comment 21-3
|
||||||
|
:}"
|
||||||
|
|
||||||
|
f"{
|
||||||
|
1 # comment 21-4
|
||||||
|
:} a"
|
||||||
|
|
||||||
x = t"""
|
x = t"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -1075,13 +1073,14 @@ x = t"""{
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Mix of various features.
|
# Mix of various features.
|
||||||
t"{ # comment 26
|
t"""{ # comment 26
|
||||||
foo:>{ # after foo
|
foo # after foo
|
||||||
|
:>{
|
||||||
x # after x
|
x # after x
|
||||||
}
|
}
|
||||||
# comment 27
|
# comment 27
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"""
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
|
|
|
@ -65,28 +65,31 @@ pub enum InterpolatedStringErrorType {
|
||||||
LambdaWithoutParentheses,
|
LambdaWithoutParentheses,
|
||||||
/// Conversion flag does not immediately follow exclamation.
|
/// Conversion flag does not immediately follow exclamation.
|
||||||
ConversionFlagNotImmediatelyAfterExclamation,
|
ConversionFlagNotImmediatelyAfterExclamation,
|
||||||
|
/// Newline inside of a format spec for a single quoted f- or t-string.
|
||||||
|
NewlineInFormatSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for InterpolatedStringErrorType {
|
impl std::fmt::Display for InterpolatedStringErrorType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
use InterpolatedStringErrorType::{
|
|
||||||
ConversionFlagNotImmediatelyAfterExclamation, InvalidConversionFlag,
|
|
||||||
LambdaWithoutParentheses, SingleRbrace, UnclosedLbrace, UnterminatedString,
|
|
||||||
UnterminatedTripleQuotedString,
|
|
||||||
};
|
|
||||||
match self {
|
match self {
|
||||||
UnclosedLbrace => write!(f, "expecting '}}'"),
|
Self::UnclosedLbrace => write!(f, "expecting '}}'"),
|
||||||
InvalidConversionFlag => write!(f, "invalid conversion character"),
|
Self::InvalidConversionFlag => write!(f, "invalid conversion character"),
|
||||||
SingleRbrace => write!(f, "single '}}' is not allowed"),
|
Self::SingleRbrace => write!(f, "single '}}' is not allowed"),
|
||||||
UnterminatedString => write!(f, "unterminated string"),
|
Self::UnterminatedString => write!(f, "unterminated string"),
|
||||||
UnterminatedTripleQuotedString => write!(f, "unterminated triple-quoted string"),
|
Self::UnterminatedTripleQuotedString => write!(f, "unterminated triple-quoted string"),
|
||||||
LambdaWithoutParentheses => {
|
Self::LambdaWithoutParentheses => {
|
||||||
write!(f, "lambda expressions are not allowed without parentheses")
|
write!(f, "lambda expressions are not allowed without parentheses")
|
||||||
}
|
}
|
||||||
ConversionFlagNotImmediatelyAfterExclamation => write!(
|
Self::ConversionFlagNotImmediatelyAfterExclamation => write!(
|
||||||
f,
|
f,
|
||||||
"conversion type must come right after the exclamation mark"
|
"conversion type must come right after the exclamation mark"
|
||||||
),
|
),
|
||||||
|
Self::NewlineInFormatSpec => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"newlines are not allowed in format specifiers when using single quotes"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,31 +433,31 @@ impl LexicalErrorType {
|
||||||
impl std::fmt::Display for LexicalErrorType {
|
impl std::fmt::Display for LexicalErrorType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
LexicalErrorType::StringError => write!(f, "Got unexpected string"),
|
Self::StringError => write!(f, "Got unexpected string"),
|
||||||
LexicalErrorType::FStringError(error) => write!(f, "f-string: {error}"),
|
Self::FStringError(error) => write!(f, "f-string: {error}"),
|
||||||
LexicalErrorType::TStringError(error) => write!(f, "t-string: {error}"),
|
Self::TStringError(error) => write!(f, "t-string: {error}"),
|
||||||
LexicalErrorType::InvalidByteLiteral => {
|
Self::InvalidByteLiteral => {
|
||||||
write!(f, "bytes can only contain ASCII literal characters")
|
write!(f, "bytes can only contain ASCII literal characters")
|
||||||
}
|
}
|
||||||
LexicalErrorType::UnicodeError => write!(f, "Got unexpected unicode"),
|
Self::UnicodeError => write!(f, "Got unexpected unicode"),
|
||||||
LexicalErrorType::IndentationError => {
|
Self::IndentationError => {
|
||||||
write!(f, "unindent does not match any outer indentation level")
|
write!(f, "unindent does not match any outer indentation level")
|
||||||
}
|
}
|
||||||
LexicalErrorType::UnrecognizedToken { tok } => {
|
Self::UnrecognizedToken { tok } => {
|
||||||
write!(f, "Got unexpected token {tok}")
|
write!(f, "Got unexpected token {tok}")
|
||||||
}
|
}
|
||||||
LexicalErrorType::LineContinuationError => {
|
Self::LineContinuationError => {
|
||||||
write!(f, "Expected a newline after line continuation character")
|
write!(f, "Expected a newline after line continuation character")
|
||||||
}
|
}
|
||||||
LexicalErrorType::Eof => write!(f, "unexpected EOF while parsing"),
|
Self::Eof => write!(f, "unexpected EOF while parsing"),
|
||||||
LexicalErrorType::OtherError(msg) => write!(f, "{msg}"),
|
Self::OtherError(msg) => write!(f, "{msg}"),
|
||||||
LexicalErrorType::UnclosedStringError => {
|
Self::UnclosedStringError => {
|
||||||
write!(f, "missing closing quote in string literal")
|
write!(f, "missing closing quote in string literal")
|
||||||
}
|
}
|
||||||
LexicalErrorType::MissingUnicodeLbrace => {
|
Self::MissingUnicodeLbrace => {
|
||||||
write!(f, "Missing `{{` in Unicode escape sequence")
|
write!(f, "Missing `{{` in Unicode escape sequence")
|
||||||
}
|
}
|
||||||
LexicalErrorType::MissingUnicodeRbrace => {
|
Self::MissingUnicodeRbrace => {
|
||||||
write!(f, "Missing `}}` in Unicode escape sequence")
|
write!(f, "Missing `}}` in Unicode escape sequence")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -826,19 +826,17 @@ impl<'src> Lexer<'src> {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
'\n' | '\r' if !interpolated_string.is_triple_quoted() => {
|
'\n' | '\r' if !interpolated_string.is_triple_quoted() => {
|
||||||
// If we encounter a newline while we're in a format spec, then
|
// https://github.com/astral-sh/ruff/issues/18632
|
||||||
// we stop here and let the lexer emit the newline token.
|
|
||||||
//
|
|
||||||
// Relevant discussion: https://github.com/python/cpython/issues/110259
|
|
||||||
if in_format_spec {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
self.interpolated_strings.pop();
|
self.interpolated_strings.pop();
|
||||||
|
|
||||||
|
let error_type = if in_format_spec {
|
||||||
|
InterpolatedStringErrorType::NewlineInFormatSpec
|
||||||
|
} else {
|
||||||
|
InterpolatedStringErrorType::UnterminatedString
|
||||||
|
};
|
||||||
|
|
||||||
return Some(self.push_error(LexicalError::new(
|
return Some(self.push_error(LexicalError::new(
|
||||||
LexicalErrorType::from_interpolated_string_error(
|
LexicalErrorType::from_interpolated_string_error(error_type, string_kind),
|
||||||
InterpolatedStringErrorType::UnterminatedString,
|
|
||||||
string_kind,
|
|
||||||
),
|
|
||||||
self.token_range(),
|
self.token_range(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -1768,6 +1766,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn lex_valid(source: &str, mode: Mode, start_offset: TextSize) -> LexerOutput {
|
fn lex_valid(source: &str, mode: Mode, start_offset: TextSize) -> LexerOutput {
|
||||||
let output = lex(source, mode, start_offset);
|
let output = lex(source, mode, start_offset);
|
||||||
|
|
||||||
|
@ -1783,6 +1782,7 @@ mod tests {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn lex_invalid(source: &str, mode: Mode) -> LexerOutput {
|
fn lex_invalid(source: &str, mode: Mode) -> LexerOutput {
|
||||||
let output = lex(source, mode, TextSize::default());
|
let output = lex(source, mode, TextSize::default());
|
||||||
|
|
||||||
|
@ -1794,14 +1794,17 @@ mod tests {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn lex_source(source: &str) -> LexerOutput {
|
fn lex_source(source: &str) -> LexerOutput {
|
||||||
lex_valid(source, Mode::Module, TextSize::default())
|
lex_valid(source, Mode::Module, TextSize::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn lex_source_with_offset(source: &str, start_offset: TextSize) -> LexerOutput {
|
fn lex_source_with_offset(source: &str, start_offset: TextSize) -> LexerOutput {
|
||||||
lex_valid(source, Mode::Module, start_offset)
|
lex_valid(source, Mode::Module, start_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn lex_jupyter_source(source: &str) -> LexerOutput {
|
fn lex_jupyter_source(source: &str) -> LexerOutput {
|
||||||
lex_valid(source, Mode::Ipython, TextSize::default())
|
lex_valid(source, Mode::Ipython, TextSize::default())
|
||||||
}
|
}
|
||||||
|
@ -2394,6 +2397,13 @@ f'''__{
|
||||||
b
|
b
|
||||||
c
|
c
|
||||||
}__'''
|
}__'''
|
||||||
|
";
|
||||||
|
assert_snapshot!(lex_source(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fstring_newline_format_spec() {
|
||||||
|
let source = r"
|
||||||
f'__{
|
f'__{
|
||||||
x:d
|
x:d
|
||||||
}__'
|
}__'
|
||||||
|
@ -2402,7 +2412,7 @@ f'__{
|
||||||
b
|
b
|
||||||
}__'
|
}__'
|
||||||
";
|
";
|
||||||
assert_snapshot!(lex_source(source));
|
assert_snapshot!(lex_invalid(source, Mode::Module));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2572,6 +2582,13 @@ t'''__{
|
||||||
b
|
b
|
||||||
c
|
c
|
||||||
}__'''
|
}__'''
|
||||||
|
";
|
||||||
|
assert_snapshot!(lex_source(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tstring_newline_format_spec() {
|
||||||
|
let source = r"
|
||||||
t'__{
|
t'__{
|
||||||
x:d
|
x:d
|
||||||
}__'
|
}__'
|
||||||
|
@ -2580,7 +2597,7 @@ t'__{
|
||||||
b
|
b
|
||||||
}__'
|
}__'
|
||||||
";
|
";
|
||||||
assert_snapshot!(lex_source(source));
|
assert_snapshot!(lex_invalid(source, Mode::Module));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/src/lexer.rs
|
||||||
|
expression: "lex_invalid(source, Mode::Module)"
|
||||||
|
---
|
||||||
|
## Tokens
|
||||||
|
```
|
||||||
|
[
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
0..1,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
FStringStart,
|
||||||
|
1..3,
|
||||||
|
TokenFlags(
|
||||||
|
F_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
InterpolatedStringMiddle(
|
||||||
|
"__",
|
||||||
|
),
|
||||||
|
3..5,
|
||||||
|
TokenFlags(
|
||||||
|
F_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Lbrace,
|
||||||
|
5..6,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
6..7,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("x"),
|
||||||
|
),
|
||||||
|
11..12,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Colon,
|
||||||
|
12..13,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
13..14,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
14..15,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Rbrace,
|
||||||
|
15..16,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("__"),
|
||||||
|
),
|
||||||
|
16..18,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
18..19,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Newline,
|
||||||
|
19..20,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
FStringStart,
|
||||||
|
20..22,
|
||||||
|
TokenFlags(
|
||||||
|
F_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
InterpolatedStringMiddle(
|
||||||
|
"__",
|
||||||
|
),
|
||||||
|
22..24,
|
||||||
|
TokenFlags(
|
||||||
|
F_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Lbrace,
|
||||||
|
24..25,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
25..26,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("x"),
|
||||||
|
),
|
||||||
|
30..31,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Colon,
|
||||||
|
31..32,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
32..33,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
33..34,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("b"),
|
||||||
|
),
|
||||||
|
42..43,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
43..44,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Rbrace,
|
||||||
|
44..45,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("__"),
|
||||||
|
),
|
||||||
|
45..47,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
47..48,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Newline,
|
||||||
|
48..49,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
## Errors
|
||||||
|
```
|
||||||
|
[
|
||||||
|
LexicalError {
|
||||||
|
error: FStringError(
|
||||||
|
NewlineInFormatSpec,
|
||||||
|
),
|
||||||
|
location: 13..14,
|
||||||
|
},
|
||||||
|
LexicalError {
|
||||||
|
error: UnclosedStringError,
|
||||||
|
location: 18..19,
|
||||||
|
},
|
||||||
|
LexicalError {
|
||||||
|
error: FStringError(
|
||||||
|
NewlineInFormatSpec,
|
||||||
|
),
|
||||||
|
location: 32..33,
|
||||||
|
},
|
||||||
|
LexicalError {
|
||||||
|
error: UnclosedStringError,
|
||||||
|
location: 47..48,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
```
|
|
@ -139,157 +139,5 @@ expression: lex_source(source)
|
||||||
Newline,
|
Newline,
|
||||||
67..68,
|
67..68,
|
||||||
),
|
),
|
||||||
(
|
|
||||||
FStringStart,
|
|
||||||
68..70,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
70..72,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Lbrace,
|
|
||||||
72..73,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
73..74,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Name(
|
|
||||||
Name("x"),
|
|
||||||
),
|
|
||||||
78..79,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Colon,
|
|
||||||
79..80,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"d",
|
|
||||||
),
|
|
||||||
80..81,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
81..82,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Rbrace,
|
|
||||||
82..83,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
83..85,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FStringEnd,
|
|
||||||
85..86,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Newline,
|
|
||||||
86..87,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FStringStart,
|
|
||||||
87..89,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
89..91,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Lbrace,
|
|
||||||
91..92,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
92..93,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Name(
|
|
||||||
Name("x"),
|
|
||||||
),
|
|
||||||
97..98,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Colon,
|
|
||||||
98..99,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"a",
|
|
||||||
),
|
|
||||||
99..100,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
100..101,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Name(
|
|
||||||
Name("b"),
|
|
||||||
),
|
|
||||||
109..110,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
110..111,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Rbrace,
|
|
||||||
111..112,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
112..114,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FStringEnd,
|
|
||||||
114..115,
|
|
||||||
TokenFlags(
|
|
||||||
F_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Newline,
|
|
||||||
115..116,
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/src/lexer.rs
|
||||||
|
expression: "lex_invalid(source, Mode::Module)"
|
||||||
|
---
|
||||||
|
## Tokens
|
||||||
|
```
|
||||||
|
[
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
0..1,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
TStringStart,
|
||||||
|
1..3,
|
||||||
|
TokenFlags(
|
||||||
|
T_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
InterpolatedStringMiddle(
|
||||||
|
"__",
|
||||||
|
),
|
||||||
|
3..5,
|
||||||
|
TokenFlags(
|
||||||
|
T_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Lbrace,
|
||||||
|
5..6,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
6..7,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("x"),
|
||||||
|
),
|
||||||
|
11..12,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Colon,
|
||||||
|
12..13,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
13..14,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
14..15,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Rbrace,
|
||||||
|
15..16,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("__"),
|
||||||
|
),
|
||||||
|
16..18,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
18..19,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Newline,
|
||||||
|
19..20,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
TStringStart,
|
||||||
|
20..22,
|
||||||
|
TokenFlags(
|
||||||
|
T_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
InterpolatedStringMiddle(
|
||||||
|
"__",
|
||||||
|
),
|
||||||
|
22..24,
|
||||||
|
TokenFlags(
|
||||||
|
T_STRING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Lbrace,
|
||||||
|
24..25,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
25..26,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("x"),
|
||||||
|
),
|
||||||
|
30..31,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Colon,
|
||||||
|
31..32,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
32..33,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
33..34,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("b"),
|
||||||
|
),
|
||||||
|
42..43,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NonLogicalNewline,
|
||||||
|
43..44,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Rbrace,
|
||||||
|
44..45,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Name(
|
||||||
|
Name("__"),
|
||||||
|
),
|
||||||
|
45..47,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Unknown,
|
||||||
|
47..48,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Newline,
|
||||||
|
48..49,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
## Errors
|
||||||
|
```
|
||||||
|
[
|
||||||
|
LexicalError {
|
||||||
|
error: TStringError(
|
||||||
|
NewlineInFormatSpec,
|
||||||
|
),
|
||||||
|
location: 13..14,
|
||||||
|
},
|
||||||
|
LexicalError {
|
||||||
|
error: UnclosedStringError,
|
||||||
|
location: 18..19,
|
||||||
|
},
|
||||||
|
LexicalError {
|
||||||
|
error: TStringError(
|
||||||
|
NewlineInFormatSpec,
|
||||||
|
),
|
||||||
|
location: 32..33,
|
||||||
|
},
|
||||||
|
LexicalError {
|
||||||
|
error: UnclosedStringError,
|
||||||
|
location: 47..48,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
```
|
|
@ -139,157 +139,5 @@ expression: lex_source(source)
|
||||||
Newline,
|
Newline,
|
||||||
67..68,
|
67..68,
|
||||||
),
|
),
|
||||||
(
|
|
||||||
TStringStart,
|
|
||||||
68..70,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
70..72,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Lbrace,
|
|
||||||
72..73,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
73..74,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Name(
|
|
||||||
Name("x"),
|
|
||||||
),
|
|
||||||
78..79,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Colon,
|
|
||||||
79..80,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"d",
|
|
||||||
),
|
|
||||||
80..81,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
81..82,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Rbrace,
|
|
||||||
82..83,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
83..85,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
TStringEnd,
|
|
||||||
85..86,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Newline,
|
|
||||||
86..87,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
TStringStart,
|
|
||||||
87..89,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
89..91,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Lbrace,
|
|
||||||
91..92,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
92..93,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Name(
|
|
||||||
Name("x"),
|
|
||||||
),
|
|
||||||
97..98,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Colon,
|
|
||||||
98..99,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"a",
|
|
||||||
),
|
|
||||||
99..100,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
100..101,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Name(
|
|
||||||
Name("b"),
|
|
||||||
),
|
|
||||||
109..110,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NonLogicalNewline,
|
|
||||||
110..111,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Rbrace,
|
|
||||||
111..112,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
InterpolatedStringMiddle(
|
|
||||||
"__",
|
|
||||||
),
|
|
||||||
112..114,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
TStringEnd,
|
|
||||||
114..115,
|
|
||||||
TokenFlags(
|
|
||||||
T_STRING,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Newline,
|
|
||||||
115..116,
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
|
@ -169,15 +169,7 @@ Module(
|
||||||
InterpolatedStringFormatSpec {
|
InterpolatedStringFormatSpec {
|
||||||
range: 226..228,
|
range: 226..228,
|
||||||
node_index: AtomicNodeIndex(..),
|
node_index: AtomicNodeIndex(..),
|
||||||
elements: [
|
elements: [],
|
||||||
Literal(
|
|
||||||
InterpolatedStringLiteralElement {
|
|
||||||
range: 226..228,
|
|
||||||
node_index: AtomicNodeIndex(..),
|
|
||||||
value: "\\",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -385,11 +377,22 @@ Module(
|
||||||
6 | 'format spec'}
|
6 | 'format spec'}
|
||||||
7 |
|
7 |
|
||||||
8 | f'middle {'string':\\
|
8 | f'middle {'string':\\
|
||||||
| ^ Syntax Error: f-string: unterminated string
|
| ^^ Syntax Error: f-string: newlines are not allowed in format specifiers when using single quotes
|
||||||
9 | 'format spec'}
|
9 | 'format spec'}
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
6 | 'format spec'}
|
||||||
|
7 |
|
||||||
|
8 | f'middle {'string':\\
|
||||||
|
| ^ Syntax Error: f-string: expecting '}'
|
||||||
|
9 | 'format spec'}
|
||||||
|
10 |
|
||||||
|
11 | f'middle {'string':\\\
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
|
||||||
8 | f'middle {'string':\\
|
8 | f'middle {'string':\\
|
||||||
9 | 'format spec'}
|
9 | 'format spec'}
|
||||||
|
|
|
@ -384,7 +384,7 @@ Module(
|
||||||
3 | f"hello {x
|
3 | f"hello {x
|
||||||
4 | 2 + 2
|
4 | 2 + 2
|
||||||
5 | f"hello {x:
|
5 | f"hello {x:
|
||||||
| ^ Syntax Error: f-string: unterminated string
|
| ^ Syntax Error: f-string: newlines are not allowed in format specifiers when using single quotes
|
||||||
6 | 3 + 3
|
6 | 3 + 3
|
||||||
7 | f"hello {x}
|
7 | f"hello {x}
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue