mirror of https://github.com/python/cpython
gh-93671: Avoid exponential backtracking in deeply nested sequence patterns in match statements (GH-93680)
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
21a9a85ff4
commit
53a8b17895
|
@ -471,7 +471,7 @@ or_pattern[pattern_ty]:
|
|||
| patterns[asdl_pattern_seq*]='|'.closed_pattern+ {
|
||||
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _PyAST_MatchOr(patterns, EXTRA) }
|
||||
|
||||
closed_pattern[pattern_ty]:
|
||||
closed_pattern[pattern_ty] (memo):
|
||||
| literal_pattern
|
||||
| capture_pattern
|
||||
| wildcard_pattern
|
||||
|
@ -558,7 +558,7 @@ maybe_star_pattern[pattern_ty]:
|
|||
| star_pattern
|
||||
| pattern
|
||||
|
||||
star_pattern[pattern_ty]:
|
||||
star_pattern[pattern_ty] (memo):
|
||||
| '*' target=pattern_capture_target {
|
||||
_PyAST_MatchStar(target->v.Name.id, EXTRA) }
|
||||
| '*' wildcard_pattern {
|
||||
|
@ -1312,4 +1312,4 @@ invalid_kvpair:
|
|||
| a=expression !(':') {
|
||||
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") }
|
||||
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
|
||||
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
|
||||
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
|
||||
|
|
|
@ -3151,6 +3151,27 @@ class TestTracing(unittest.TestCase):
|
|||
self.assertListEqual(self._trace(f, "go x"), [1, 2, 3])
|
||||
self.assertListEqual(self._trace(f, "spam"), [1, 2, 3])
|
||||
|
||||
def test_parser_deeply_nested_patterns(self):
|
||||
# Deeply nested patterns can cause exponential backtracking when parsing.
|
||||
# See gh-93671 for more information.
|
||||
|
||||
levels = 100
|
||||
|
||||
patterns = [
|
||||
"A" + "(" * levels + ")" * levels,
|
||||
"{1:" * levels + "1" + "}" * levels,
|
||||
"[" * levels + "1" + "]" * levels,
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
with self.subTest(pattern):
|
||||
code = inspect.cleandoc("""
|
||||
match None:
|
||||
case {}:
|
||||
pass
|
||||
""".format(pattern))
|
||||
compile(code, "<string>", "exec")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Fix some exponential backtrace case happening with deeply nested sequence
|
||||
patterns in match statements. Patch by Pablo Galindo
|
|
@ -7945,6 +7945,10 @@ closed_pattern_rule(Parser *p)
|
|||
return NULL;
|
||||
}
|
||||
pattern_ty _res = NULL;
|
||||
if (_PyPegen_is_memoized(p, closed_pattern_type, &_res)) {
|
||||
p->level--;
|
||||
return _res;
|
||||
}
|
||||
int _mark = p->mark;
|
||||
{ // literal_pattern
|
||||
if (p->error_indicator) {
|
||||
|
@ -8100,6 +8104,7 @@ closed_pattern_rule(Parser *p)
|
|||
}
|
||||
_res = NULL;
|
||||
done:
|
||||
_PyPegen_insert_memo(p, _mark, closed_pattern_type, _res);
|
||||
p->level--;
|
||||
return _res;
|
||||
}
|
||||
|
@ -9623,6 +9628,10 @@ star_pattern_rule(Parser *p)
|
|||
return NULL;
|
||||
}
|
||||
pattern_ty _res = NULL;
|
||||
if (_PyPegen_is_memoized(p, star_pattern_type, &_res)) {
|
||||
p->level--;
|
||||
return _res;
|
||||
}
|
||||
int _mark = p->mark;
|
||||
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
|
||||
p->error_indicator = 1;
|
||||
|
@ -9707,6 +9716,7 @@ star_pattern_rule(Parser *p)
|
|||
}
|
||||
_res = NULL;
|
||||
done:
|
||||
_PyPegen_insert_memo(p, _mark, star_pattern_type, _res);
|
||||
p->level--;
|
||||
return _res;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue