Skip to content

Commit 2b428da

Browse files
miss-islingtonTwix1288encukoujohnslavik
authored
[3.15] gh-152708: Fix AST generation asymmetry for unary positive in match statements (GH-152712) (#158079)
gh-152708: Fix AST generation asymmetry for unary positive in match statements (GH-152712) (cherry picked from commit 7884f48) Co-authored-by: Rishit Agnihotri <67520802+Twix1288@users.noreply.github.com> Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: johnslavik <bartosz@ilikepython.com>
1 parent 5563404 commit 2b428da

6 files changed

Lines changed: 49 additions & 12 deletions

File tree

‎Grammar/python.gram‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,12 +555,12 @@ complex_number[expr_ty]:
555555

556556
signed_number[expr_ty]:
557557
| NUMBER
558-
| '+' number=NUMBER { number }
558+
| '+' number=NUMBER { _PyAST_UnaryOp(UAdd, number, EXTRA) }
559559
| '-' number=NUMBER { _PyAST_UnaryOp(USub, number, EXTRA) }
560560

561561
signed_real_number[expr_ty]:
562562
| real_number
563-
| '+' real=real_number { real }
563+
| '+' real=real_number { _PyAST_UnaryOp(UAdd, real, EXTRA) }
564564
| '-' real=real_number { _PyAST_UnaryOp(USub, real, EXTRA) }
565565

566566
real_number[expr_ty]:

‎Lib/test/test_unparse.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,24 @@ def test_unparse_interactive_integrity_3(self):
693693

694694
self.check_src_roundtrip(src, out, mode='single')
695695

696+
@test.support.subTests('case', [
697+
"case 'a string':",
698+
"case True:",
699+
"case _:",
700+
"case [*_]:",
701+
"case tuple():",
702+
"case cls(arg, more=arg2):",
703+
"case tuple() | list():",
704+
"case [tuple() as obj, _, {'a': a, **more}]:",
705+
"case -2:",
706+
"case +2:",
707+
"case 2 + 3j:",
708+
"case +2 - 3j:",
709+
])
710+
def test_unparse_match(self, case):
711+
src = 'match x:\n ' + case + '\n pass'
712+
self.check_src_roundtrip(src)
713+
696714

697715
class CosmeticTestCase(ASTTestCase):
698716
"""Test if there are cosmetic issues caused by unnecessary additions"""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix AST generation asymmetry where unary positive ``+`` was implicitly dropped in pattern match expressions, restoring parsing parity with unary negative ``-``.

‎Parser/parser.c‎

Lines changed: 20 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/ast.c‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,11 @@ ensure_literal_number(expr_ty exp, bool allow_real, bool allow_imaginary)
426426
}
427427

428428
static int
429-
ensure_literal_negative(expr_ty exp, bool allow_real, bool allow_imaginary)
429+
ensure_literal_signed(expr_ty exp, bool allow_real, bool allow_imaginary)
430430
{
431431
assert(exp->kind == UnaryOp_kind);
432-
// Must be negation ...
433-
if (exp->v.UnaryOp.op != USub) {
432+
// Must be negation or positive ...
433+
if (exp->v.UnaryOp.op != USub && exp->v.UnaryOp.op != UAdd) {
434434
return 0;
435435
}
436436
// ... of a constant ...
@@ -461,7 +461,7 @@ ensure_literal_complex(expr_ty exp)
461461
}
462462
break;
463463
case UnaryOp_kind:
464-
if (!ensure_literal_negative(left, /*real=*/true, /*imaginary=*/false)) {
464+
if (!ensure_literal_signed(left, /*real=*/true, /*imaginary=*/false)) {
465465
return 0;
466466
}
467467
break;
@@ -512,9 +512,9 @@ validate_pattern_match_value(expr_ty exp)
512512
// Constants and attribute lookups are always permitted
513513
return 1;
514514
case UnaryOp_kind:
515-
// Negated numbers are permitted (whether real or imaginary)
515+
// Signed numbers are permitted (whether real or imaginary)
516516
// Compiler will complain if AST folding doesn't create a constant
517-
if (ensure_literal_negative(exp, /*real=*/true, /*imaginary=*/true)) {
517+
if (ensure_literal_signed(exp, /*real=*/true, /*imaginary=*/true)) {
518518
return 1;
519519
}
520520
break;

‎Python/ast_preprocess.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -867,11 +867,11 @@ fold_const_match_patterns(expr_ty node, PyArena *ctx_, _PyASTPreprocessState *st
867867
{
868868
case UnaryOp_kind:
869869
{
870-
if (node->v.UnaryOp.op == USub &&
870+
if ((node->v.UnaryOp.op == USub || node->v.UnaryOp.op == UAdd) &&
871871
node->v.UnaryOp.operand->kind == Constant_kind)
872872
{
873873
PyObject *operand = node->v.UnaryOp.operand->v.Constant.value;
874-
PyObject *folded = PyNumber_Negative(operand);
874+
PyObject *folded = node->v.UnaryOp.op == USub ? PyNumber_Negative(operand) : PyNumber_Positive(operand);
875875
return make_const(node, folded, ctx_);
876876
}
877877
break;

0 commit comments

Comments
 (0)