Disallow f-strings in match pattern literal (#7857)

## Summary

This PR fixes a bug to disallow f-strings in match pattern literal.

```
literal_pattern ::=  signed_number
                     | signed_number "+" NUMBER
                     | signed_number "-" NUMBER
                     | strings
                     | "None"
                     | "True"
                     | "False"
                     | signed_number: NUMBER | "-" NUMBER
```

Source:
https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-literal_pattern

Also,

```console
$ python /tmp/t.py
  File "/tmp/t.py", line 4
    case "hello " f"{name}":
         ^^^^^^^^^^^^^^^^^^
SyntaxError: patterns may only match literals and attribute lookups
```

## Test Plan

Update existing test case and accordingly the snapshots. Also, add a new
test case to verify that the parser does raise an error.
This commit is contained in:
Dhruv Manilawala 2023-10-09 15:41:08 +05:30 committed by GitHub
parent 38f512d588
commit 43883b7a15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 13901 additions and 13810 deletions

View file

@ -1143,6 +1143,24 @@ match x:
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test]
fn test_match_pattern_fstring_literal() {
// F-string literal is not allowed in match pattern.
let parse_error = parse_suite(
r#"
match x:
case f"{y}":
pass
"#,
"<test>",
)
.err();
assert!(
parse_error.is_some(),
"expected parse error when f-string literal is used in match pattern"
);
}
#[test] #[test]
fn test_variadic_generics() { fn test_variadic_generics() {
let parse_ast = parse_suite( let parse_ast = parse_suite(
@ -1285,7 +1303,9 @@ f'{f"{3.1415=:.1f}":*^20}'
{"foo " f"bar {x + y} " "baz": 10} {"foo " f"bar {x + y} " "baz": 10}
match foo: match foo:
case "foo " f"bar {x + y} " "baz": case "one":
pass
case "implicitly " "concatenated":
pass pass
f"\{foo}\{bar:\}" f"\{foo}\{bar:\}"

View file

@ -668,7 +668,7 @@ LiteralPattern: ast::Pattern = {
value: Box::new(value.into()), value: Box::new(value.into()),
range: (location..end_location).into() range: (location..end_location).into()
}.into(), }.into(),
<location:@L> <strings:StringLiteralOrFString+> <end_location:@R> =>? Ok(ast::PatternMatchValue { <location:@L> <strings:StringLiteral+> <end_location:@R> =>? Ok(ast::PatternMatchValue {
value: Box::new(concatenate_strings(strings, (location..end_location).into())?), value: Box::new(concatenate_strings(strings, (location..end_location).into())?),
range: (location..end_location).into() range: (location..end_location).into()
}.into()), }.into()),

File diff suppressed because it is too large Load diff

View file

@ -644,7 +644,7 @@ expression: parse_ast
), ),
Match( Match(
StmtMatch { StmtMatch {
range: 207..269, range: 207..298,
subject: Name( subject: Name(
ExprName { ExprName {
range: 213..216, range: 213..216,
@ -654,68 +654,20 @@ expression: parse_ast
), ),
cases: [ cases: [
MatchCase { MatchCase {
range: 222..269, range: 222..246,
pattern: MatchValue( pattern: MatchValue(
PatternMatchValue { PatternMatchValue {
range: 227..255, range: 227..232,
value: FString( value: Constant(
ExprFString { ExprConstant {
range: 227..255, range: 227..232,
values: [ value: Str(
Constant( StringConstant {
ExprConstant { value: "one",
range: 228..240, unicode: false,
value: Str( implicit_concatenated: false,
StringConstant { },
value: "foo bar ", ),
unicode: false,
implicit_concatenated: true,
},
),
},
),
FormattedValue(
ExprFormattedValue {
range: 240..247,
value: BinOp(
ExprBinOp {
range: 241..246,
left: Name(
ExprName {
range: 241..242,
id: "x",
ctx: Load,
},
),
op: Add,
right: Name(
ExprName {
range: 245..246,
id: "y",
ctx: Load,
},
),
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
),
Constant(
ExprConstant {
range: 247..254,
value: Str(
StringConstant {
value: " baz",
unicode: false,
implicit_concatenated: true,
},
),
},
),
],
implicit_concatenated: true,
}, },
), ),
}, },
@ -724,7 +676,35 @@ expression: parse_ast
body: [ body: [
Pass( Pass(
StmtPass { StmtPass {
range: 265..269, range: 242..246,
},
),
],
},
MatchCase {
range: 251..298,
pattern: MatchValue(
PatternMatchValue {
range: 256..284,
value: Constant(
ExprConstant {
range: 256..284,
value: Str(
StringConstant {
value: "implicitly concatenated",
unicode: false,
implicit_concatenated: true,
},
),
},
),
},
),
guard: None,
body: [
Pass(
StmtPass {
range: 294..298,
}, },
), ),
], ],
@ -734,14 +714,14 @@ expression: parse_ast
), ),
Expr( Expr(
StmtExpr { StmtExpr {
range: 271..288, range: 300..317,
value: FString( value: FString(
ExprFString { ExprFString {
range: 271..288, range: 300..317,
values: [ values: [
Constant( Constant(
ExprConstant { ExprConstant {
range: 273..274, range: 302..303,
value: Str( value: Str(
StringConstant { StringConstant {
value: "\\", value: "\\",
@ -753,10 +733,10 @@ expression: parse_ast
), ),
FormattedValue( FormattedValue(
ExprFormattedValue { ExprFormattedValue {
range: 274..279, range: 303..308,
value: Name( value: Name(
ExprName { ExprName {
range: 275..278, range: 304..307,
id: "foo", id: "foo",
ctx: Load, ctx: Load,
}, },
@ -768,7 +748,7 @@ expression: parse_ast
), ),
Constant( Constant(
ExprConstant { ExprConstant {
range: 279..280, range: 308..309,
value: Str( value: Str(
StringConstant { StringConstant {
value: "\\", value: "\\",
@ -780,10 +760,10 @@ expression: parse_ast
), ),
FormattedValue( FormattedValue(
ExprFormattedValue { ExprFormattedValue {
range: 280..287, range: 309..316,
value: Name( value: Name(
ExprName { ExprName {
range: 281..284, range: 310..313,
id: "bar", id: "bar",
ctx: Load, ctx: Load,
}, },
@ -793,11 +773,11 @@ expression: parse_ast
format_spec: Some( format_spec: Some(
FString( FString(
ExprFString { ExprFString {
range: 285..286, range: 314..315,
values: [ values: [
Constant( Constant(
ExprConstant { ExprConstant {
range: 285..286, range: 314..315,
value: Str( value: Str(
StringConstant { StringConstant {
value: "\\", value: "\\",
@ -822,14 +802,14 @@ expression: parse_ast
), ),
Expr( Expr(
StmtExpr { StmtExpr {
range: 289..303, range: 318..332,
value: FString( value: FString(
ExprFString { ExprFString {
range: 289..303, range: 318..332,
values: [ values: [
Constant( Constant(
ExprConstant { ExprConstant {
range: 291..302, range: 320..331,
value: Str( value: Str(
StringConstant { StringConstant {
value: "\\{foo\\}", value: "\\{foo\\}",
@ -847,17 +827,17 @@ expression: parse_ast
), ),
Expr( Expr(
StmtExpr { StmtExpr {
range: 304..344, range: 333..373,
value: FString( value: FString(
ExprFString { ExprFString {
range: 304..344, range: 333..373,
values: [ values: [
FormattedValue( FormattedValue(
ExprFormattedValue { ExprFormattedValue {
range: 308..341, range: 337..370,
value: Name( value: Name(
ExprName { ExprName {
range: 314..317, range: 343..346,
id: "foo", id: "foo",
ctx: Load, ctx: Load,
}, },
@ -867,11 +847,11 @@ expression: parse_ast
format_spec: Some( format_spec: Some(
FString( FString(
ExprFString { ExprFString {
range: 318..340, range: 347..369,
values: [ values: [
Constant( Constant(
ExprConstant { ExprConstant {
range: 318..340, range: 347..369,
value: Str( value: Str(
StringConstant { StringConstant {
value: "x\n y\n z\n", value: "x\n y\n z\n",