From 7e9b24390fdb87299aafddc9bbd9ce8ddc6dca2c Mon Sep 17 00:00:00 2001 From: Jon Meow <46229924+jonmeow@users.noreply.github.com> Date: Wed, 16 Feb 2022 09:05:55 -0800 Subject: [PATCH] Handle recursion limit errors in postfix expressions (#1081) --- toolchain/parser/parse_tree_test.cpp | 15 +++++++++++++++ toolchain/parser/parser_impl.cpp | 11 +++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/toolchain/parser/parse_tree_test.cpp b/toolchain/parser/parse_tree_test.cpp index 4bd9ab6514a0..4b2d6d40ac16 100644 --- a/toolchain/parser/parse_tree_test.cpp +++ b/toolchain/parser/parse_tree_test.cpp @@ -1265,5 +1265,20 @@ TEST_F(ParseTreeTest, RecursionLimit) { EXPECT_TRUE(tree.HasErrors()); } +TEST_F(ParseTreeTest, ParsePostfixExpressionRegression) { + // Stack depth errors could cause ParsePostfixExpression to infinitely loop + // when calling children and those children error. Because of the fragility of + // stack depth, this tries a few different values. + for (int n = 0; n <= 10; ++n) { + std::string code = "var x: auto = "; + code.append(ParseTree::StackDepthLimit - n, '*'); + code += "(z);"; + TokenizedBuffer tokens = GetTokenizedBuffer(code); + ASSERT_FALSE(tokens.HasErrors()); + ParseTree tree = ParseTree::Parse(tokens, consumer); + EXPECT_TRUE(tree.HasErrors()); + } +} + } // namespace } // namespace Carbon::Testing diff --git a/toolchain/parser/parser_impl.cpp b/toolchain/parser/parser_impl.cpp index 24d8f0eebaa5..b710dce8266d 100644 --- a/toolchain/parser/parser_impl.cpp +++ b/toolchain/parser/parser_impl.cpp @@ -912,6 +912,7 @@ auto ParseTree::Parser::ParsePostfixExpression() -> llvm::Optional { auto start = GetSubtreeStartPosition(); llvm::Optional expression = ParsePrimaryExpression(); + TokenizedBuffer::TokenIterator last_position = position_; while (true) { switch (NextTokenKind()) { case TokenKind::Period(): @@ -923,10 +924,16 @@ auto ParseTree::Parser::ParsePostfixExpression() -> llvm::Optional { expression = ParseCallExpression(start, !expression); break; - default: { + default: return expression; - } } + // This is subject to an infinite loop if a child call fails, so monitor for + // stalling. + if (last_position == position_) { + CHECK(expression == llvm::None); + return expression; + } + last_position = position_; } }