wgsl::ParserImpl: Use sync() for postfix_expr()

postfix_expr() handles `[]` array accessors and call expressions `()`.

Have postfix_expr() use sync to parse these:
* It will use the end bracket token to attempt to resynchronize the parser on error
* It also considers maximum parser recursion depth, avoiding stack overflows

Fixed: chromium:1180573
Change-Id: I8c1c62c68e24a564e0e4e7d0de9f5a3fa7032369
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/42222
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index fb9535b..07c22ec 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -2136,34 +2136,38 @@
 Maybe<ast::Expression*> ParserImpl::postfix_expr(ast::Expression* prefix) {
   Source source;
   if (match(Token::Type::kBracketLeft, &source)) {
-    auto param = logical_or_expression();
-    if (param.errored)
-      return Failure::kErrored;
-    if (!param.matched)
-      return add_error(peek(), "unable to parse expression inside []");
+    return sync(Token::Type::kBracketRight, [&]() -> Maybe<ast::Expression*> {
+      auto param = logical_or_expression();
+      if (param.errored)
+        return Failure::kErrored;
+      if (!param.matched)
+        return add_error(peek(), "unable to parse expression inside []");
 
-    if (!expect("array accessor", Token::Type::kBracketRight))
-      return Failure::kErrored;
+      if (!expect("array accessor", Token::Type::kBracketRight))
+        return Failure::kErrored;
 
-    return postfix_expr(
-        create<ast::ArrayAccessorExpression>(source, prefix, param.value));
+      return postfix_expr(
+          create<ast::ArrayAccessorExpression>(source, prefix, param.value));
+    });
   }
 
   if (match(Token::Type::kParenLeft, &source)) {
-    ast::ExpressionList params;
+    return sync(Token::Type::kParenRight, [&]() -> Maybe<ast::Expression*> {
+      ast::ExpressionList params;
 
-    auto t = peek();
-    if (!t.IsParenRight() && !t.IsEof()) {
-      auto list = expect_argument_expression_list();
-      if (list.errored)
+      auto t = peek();
+      if (!t.IsParenRight() && !t.IsEof()) {
+        auto list = expect_argument_expression_list();
+        if (list.errored)
+          return Failure::kErrored;
+        params = list.value;
+      }
+
+      if (!expect("call expression", Token::Type::kParenRight))
         return Failure::kErrored;
-      params = list.value;
-    }
 
-    if (!expect("call expression", Token::Type::kParenRight))
-      return Failure::kErrored;
-
-    return postfix_expr(create<ast::CallExpression>(source, prefix, params));
+      return postfix_expr(create<ast::CallExpression>(source, prefix, params));
+    });
   }
 
   if (match(Token::Type::kPeriod)) {