Skip to content

Commit 8d06474

Browse files
authored
bpo-43897: Reject "_" captures and top-level MatchStar in the AST validator (GH-27432)
1 parent 92b5dc7 commit 8d06474

File tree

2 files changed

+33
-21
lines changed

2 files changed

+33
-21
lines changed

Lib/test/test_ast.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1627,7 +1627,11 @@ def test_stdlib_validates(self):
16271627
),
16281628
ast.MatchOr(
16291629
[pattern_1, pattern_x, ast.MatchSingleton('xxx')]
1630-
)
1630+
),
1631+
ast.MatchAs(name="_"),
1632+
ast.MatchStar(name="x"),
1633+
ast.MatchSequence([ast.MatchStar("_")]),
1634+
ast.MatchMapping([], [], rest="_"),
16311635
]
16321636

16331637
def test_match_validation_pattern(self):

Python/ast.c

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ static int validate_patterns(struct validator *, asdl_pattern_seq *, int);
2020
static int _validate_nonempty_seq(asdl_seq *, const char *, const char *);
2121
static int validate_stmt(struct validator *, stmt_ty);
2222
static int validate_expr(struct validator *, expr_ty, expr_context_ty);
23-
static int validate_pattern(struct validator *, pattern_ty);
23+
static int validate_pattern(struct validator *, pattern_ty, int);
2424

2525
static int
2626
validate_name(PyObject *name)
@@ -493,16 +493,24 @@ validate_pattern_match_value(struct validator *state, expr_ty exp)
493493
}
494494

495495
static int
496-
validate_pattern(struct validator *state, pattern_ty p)
496+
validate_capture(PyObject *name)
497+
{
498+
if (_PyUnicode_EqualToASCIIString(name, "_")) {
499+
PyErr_Format(PyExc_ValueError, "can't capture name '_' in patterns");
500+
return 0;
501+
}
502+
return validate_name(name);
503+
}
504+
505+
static int
506+
validate_pattern(struct validator *state, pattern_ty p, int star_ok)
497507
{
498508
int ret = -1;
499509
if (++state->recursion_depth > state->recursion_limit) {
500510
PyErr_SetString(PyExc_RecursionError,
501511
"maximum recursion depth exceeded during compilation");
502512
return 0;
503513
}
504-
// Coming soon: https://bugs.python.org/issue43897 (thanks Batuhan)!
505-
// TODO: Ensure no subnodes use "_" as an ordinary identifier
506514
switch (p->kind) {
507515
case MatchValue_kind:
508516
ret = validate_pattern_match_value(state, p->v.MatchValue.value);
@@ -525,7 +533,7 @@ validate_pattern(struct validator *state, pattern_ty p)
525533
break;
526534
}
527535

528-
if (p->v.MatchMapping.rest && !validate_name(p->v.MatchMapping.rest)) {
536+
if (p->v.MatchMapping.rest && !validate_capture(p->v.MatchMapping.rest)) {
529537
ret = 0;
530538
break;
531539
}
@@ -575,16 +583,16 @@ validate_pattern(struct validator *state, pattern_ty p)
575583
else {
576584
PyErr_SetString(PyExc_ValueError,
577585
"MatchClass cls field can only contain Name or Attribute nodes.");
578-
state->recursion_depth--;
579-
return 0;
586+
ret = 0;
587+
break;
580588
}
581589
}
582590

583591
for (Py_ssize_t i = 0; i < asdl_seq_LEN(p->v.MatchClass.kwd_attrs); i++) {
584592
PyObject *identifier = asdl_seq_GET(p->v.MatchClass.kwd_attrs, i);
585593
if (!validate_name(identifier)) {
586-
state->recursion_depth--;
587-
return 0;
594+
ret = 0;
595+
break;
588596
}
589597
}
590598

@@ -596,10 +604,15 @@ validate_pattern(struct validator *state, pattern_ty p)
596604
ret = validate_patterns(state, p->v.MatchClass.kwd_patterns, /*star_ok=*/0);
597605
break;
598606
case MatchStar_kind:
599-
ret = p->v.MatchStar.name == NULL || validate_name(p->v.MatchStar.name);
607+
if (!star_ok) {
608+
PyErr_SetString(PyExc_ValueError, "can't use MatchStar here");
609+
ret = 0;
610+
break;
611+
}
612+
ret = p->v.MatchStar.name == NULL || validate_capture(p->v.MatchStar.name);
600613
break;
601614
case MatchAs_kind:
602-
if (p->v.MatchAs.name && !validate_name(p->v.MatchAs.name)) {
615+
if (p->v.MatchAs.name && !validate_capture(p->v.MatchAs.name)) {
603616
ret = 0;
604617
break;
605618
}
@@ -609,10 +622,10 @@ validate_pattern(struct validator *state, pattern_ty p)
609622
else if (p->v.MatchAs.name == NULL) {
610623
PyErr_SetString(PyExc_ValueError,
611624
"MatchAs must specify a target name if a pattern is given");
612-
return 0;
625+
ret = 0;
613626
}
614627
else {
615-
ret = validate_pattern(state, p->v.MatchAs.pattern);
628+
ret = validate_pattern(state, p->v.MatchAs.pattern, /*star_ok=*/0);
616629
}
617630
break;
618631
case MatchOr_kind:
@@ -759,7 +772,7 @@ validate_stmt(struct validator *state, stmt_ty stmt)
759772
}
760773
for (i = 0; i < asdl_seq_LEN(stmt->v.Match.cases); i++) {
761774
match_case_ty m = asdl_seq_GET(stmt->v.Match.cases, i);
762-
if (!validate_pattern(state, m->pattern)
775+
if (!validate_pattern(state, m->pattern, /*star_ok=*/0)
763776
|| (m->guard && !validate_expr(state, m->guard, Load))
764777
|| !validate_body(state, m->body, "match_case")) {
765778
return 0;
@@ -894,12 +907,7 @@ validate_patterns(struct validator *state, asdl_pattern_seq *patterns, int star_
894907
Py_ssize_t i;
895908
for (i = 0; i < asdl_seq_LEN(patterns); i++) {
896909
pattern_ty pattern = asdl_seq_GET(patterns, i);
897-
if (pattern->kind == MatchStar_kind && !star_ok) {
898-
PyErr_SetString(PyExc_ValueError,
899-
"Can't use MatchStar within this sequence of patterns");
900-
return 0;
901-
}
902-
if (!validate_pattern(state, pattern)) {
910+
if (!validate_pattern(state, pattern, star_ok)) {
903911
return 0;
904912
}
905913
}

0 commit comments

Comments
 (0)