From fb5a3931b7270f6923c61c001e8be07d765a8574 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Fri, 18 Sep 2026 16:14:12 -0700 Subject: [PATCH] Stop parsing immediately on recursion limit in PrattParser and add ternary depth tests. Avoid emitting spurious syntax errors while unwinding after hitting the recursion limit, and add ternary recursion depth tests to `CelParserImplTest`. PiperOrigin-RevId: 984121814 --- parser/src/main/java/dev/cel/parser/PrattParser.java | 7 ++++--- .../src/test/java/dev/cel/parser/CelParserImplTest.java | 9 ++++++++- parser/src/test/resources/parser_errors.baseline | 9 --------- parser/src/test/resources/pratt_parser_errors.baseline | 6 ------ 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/parser/src/main/java/dev/cel/parser/PrattParser.java b/parser/src/main/java/dev/cel/parser/PrattParser.java index 829ac1dcc..2f1e9ebeb 100644 --- a/parser/src/main/java/dev/cel/parser/PrattParser.java +++ b/parser/src/main/java/dev/cel/parser/PrattParser.java @@ -253,7 +253,7 @@ private boolean expect(Lexer.TokenType type, String msg) { nextToken(); return true; } - if (isRecoveryLimitExceeded()) { + if (recursionLimitExceeded || isRecoveryLimitExceeded()) { return false; } if (peekToken.type != Lexer.TokenType.ERROR) { @@ -274,7 +274,7 @@ private boolean expect(Lexer.TokenType type, String msg) { // Find the next delimiter to prevent a cascade of spurious secondary errors. private void synchronizeOnDelimiter() { - if (isRecoveryLimitExceeded()) { + if (recursionLimitExceeded || isRecoveryLimitExceeded()) { peekToken = END_TOKEN; return; } @@ -412,6 +412,7 @@ private void reportRecursionLimit(int position) { LOCALE, "Expression recursion limit exceeded. limit: %d", options.maxParseRecursionDepth())); + peekToken = END_TOKEN; } } @@ -433,7 +434,7 @@ private CelExpr parseExpr() { private CelExpr parseBinaryAndTernary(int minPrec) { CelExpr lhs = parseSelectorChain(); int chainDepth = currentLhsDepth; - while (true) { + while (!recursionLimitExceeded && !isRecoveryLimitExceeded()) { Lexer.TokenType tok = peekToken.type; if (tok == Lexer.TokenType.QUESTION && minPrec <= 0) { lhs = parseTernary(lhs); diff --git a/parser/src/test/java/dev/cel/parser/CelParserImplTest.java b/parser/src/test/java/dev/cel/parser/CelParserImplTest.java index 09d578a36..6d70596d6 100644 --- a/parser/src/test/java/dev/cel/parser/CelParserImplTest.java +++ b/parser/src/test/java/dev/cel/parser/CelParserImplTest.java @@ -229,7 +229,14 @@ private enum MaxParseRecursionDepthTestCase { + " a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=" + " a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=" + " a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=" - + " a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]"); + + " a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]"), + TERNARY( + "a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b :" + + " a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b :" + + " a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b :" + + " a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : a ? b : c"), + TERNARY_TRUE_BRANCH_PARENS( + "a ? ((((((((((((((((((((((((((((((((b)))))))))))))))))))))))))))))))) : c"); static final int MAX_RECURSION_LIMIT = 32; final String source; diff --git a/parser/src/test/resources/parser_errors.baseline b/parser/src/test/resources/parser_errors.baseline index cbd9f087b..4f23e4a9c 100644 --- a/parser/src/test/resources/parser_errors.baseline +++ b/parser/src/test/resources/parser_errors.baseline @@ -1098,9 +1098,6 @@ E/A: Expression recursion limit exceeded. limit: 250 E/P: ERROR: :1:251: Expression recursion limit exceeded. limit: 250 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] | ..........................................................................................................................................................................................................................................................^ -ERROR: :1:251: Syntax error: expected ']' - | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] - | ..........................................................................................................................................................................................................................................................^ I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ »»»[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]] @@ -1110,9 +1107,6 @@ E/A: Expression recursion limit exceeded. limit: 32 E/P: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ | ................................^ -ERROR: :1:33: Syntax error: expected ']' - | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - | ................................^ I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] =====> @@ -1120,9 +1114,6 @@ E/A: Expression recursion limit exceeded. limit: 32 E/P: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] | ................................^ -ERROR: :1:33: Syntax error: expected ']' - | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] - | ................................^ I: a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H =====> diff --git a/parser/src/test/resources/pratt_parser_errors.baseline b/parser/src/test/resources/pratt_parser_errors.baseline index d21e65ab3..541f9e14f 100644 --- a/parser/src/test/resources/pratt_parser_errors.baseline +++ b/parser/src/test/resources/pratt_parser_errors.baseline @@ -398,18 +398,12 @@ I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ E: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ | ................................^ -ERROR: :1:33: Syntax error: expected ']' - | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - | ................................^ I: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] =====> E: ERROR: :1:33: Expression recursion limit exceeded. limit: 32 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] | ................................^ -ERROR: :1:33: Syntax error: expected ']' - | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] - | ................................^ I: a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H =====>