wsgl parser: add expect_lt_gt_block(), use it

Reduces code, paves the way for multiple errors with resynchronization points.

Bug: tint:282
Change-Id: I68018ea8cabe4ec347afa21d1220126d6348d3d1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32280
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 03bdaf4..89c8a35 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -389,17 +389,9 @@
   if (dim.matched) {
     const char* use = "sampled texture type";
 
-    if (!expect(use, Token::Type::kLessThan))
-      return Failure::kErrored;
-
-    auto subtype = type_decl();
+    auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
     if (subtype.errored)
       return Failure::kErrored;
-    if (!subtype.matched)
-      return add_error(peek().source(), "invalid subtype", use);
-
-    if (!expect(use, Token::Type::kGreaterThan))
-      return Failure::kErrored;
 
     return ctx_.type_mgr().Get(std::make_unique<ast::type::SampledTextureType>(
         dim.value, subtype.value));
@@ -409,17 +401,9 @@
   if (ms_dim.matched) {
     const char* use = "multisampled texture type";
 
-    if (!expect(use, Token::Type::kLessThan))
-      return Failure::kErrored;
-
-    auto subtype = type_decl();
+    auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
     if (subtype.errored)
       return Failure::kErrored;
-    if (!subtype.matched)
-      return add_error(peek().source(), "invalid subtype", use);
-
-    if (!expect(use, Token::Type::kGreaterThan))
-      return Failure::kErrored;
 
     return ctx_.type_mgr().Get(
         std::make_unique<ast::type::MultisampledTextureType>(ms_dim.value,
@@ -430,16 +414,12 @@
   if (storage.matched) {
     const char* use = "storage texture type";
 
-    if (!expect(use, Token::Type::kLessThan))
-      return Failure::kErrored;
+    auto format =
+        expect_lt_gt_block(use, [&] { return expect_image_storage_type(use); });
 
-    auto format = expect_image_storage_type(use);
     if (format.errored)
       return Failure::kErrored;
 
-    if (!expect(use, Token::Type::kGreaterThan))
-      return Failure::kErrored;
-
     return ctx_.type_mgr().Get(std::make_unique<ast::type::StorageTextureType>(
         storage->first, storage->second, format.value));
   }
@@ -773,16 +753,14 @@
 // variable_storage_decoration
 //   : LESS_THAN storage_class GREATER_THAN
 Maybe<ast::StorageClass> ParserImpl::variable_storage_decoration() {
-  if (!match(Token::Type::kLessThan))
+  if (!peek().IsLessThan())
     return Failure::kNoMatch;
 
   const char* use = "variable decoration";
 
-  auto sc = expect_storage_class(use);
-  if (sc.errored)
-    return Failure::kErrored;
+  auto sc = expect_lt_gt_block(use, [&] { return expect_storage_class(use); });
 
-  if (!expect(use, Token::Type::kGreaterThan))
+  if (sc.errored)
     return Failure::kErrored;
 
   return sc.value;
@@ -904,30 +882,33 @@
   return Failure::kNoMatch;
 }
 
+Expect<ast::type::Type*> ParserImpl::expect_type(const std::string& use) {
+  auto type = type_decl();
+  if (type.errored)
+    return Failure::kErrored;
+  if (!type.matched)
+    return add_error(peek().source(), "invalid type", use);
+  return type.value;
+}
+
 Expect<ast::type::Type*> ParserImpl::expect_type_decl_pointer() {
   const char* use = "ptr declaration";
 
-  if (!expect(use, Token::Type::kLessThan))
-    return Failure::kErrored;
+  return expect_lt_gt_block(use, [&]() -> Expect<ast::type::Type*> {
+    auto sc = expect_storage_class(use);
+    if (sc.errored)
+      return Failure::kErrored;
 
-  auto sc = expect_storage_class(use);
-  if (sc.errored)
-    return Failure::kErrored;
+    if (!expect(use, Token::Type::kComma))
+      return Failure::kErrored;
 
-  if (!expect(use, Token::Type::kComma))
-    return Failure::kErrored;
+    auto subtype = expect_type(use);
+    if (subtype.errored)
+      return Failure::kErrored;
 
-  auto subtype = type_decl();
-  if (subtype.errored)
-    return Failure::kErrored;
-  if (!subtype.matched)
-    return add_error(peek().source(), "missing type", use);
-
-  if (!expect(use, Token::Type::kGreaterThan))
-    return Failure::kErrored;
-
-  return ctx_.type_mgr().Get(
-      std::make_unique<ast::type::PointerType>(subtype.value, sc.value));
+    return ctx_.type_mgr().Get(
+        std::make_unique<ast::type::PointerType>(subtype.value, sc.value));
+  });
 }
 
 Expect<ast::type::Type*> ParserImpl::expect_type_decl_vector(Token t) {
@@ -939,17 +920,9 @@
 
   const char* use = "vector";
 
-  if (!expect(use, Token::Type::kLessThan))
-    return Failure::kErrored;
-
-  auto subtype = type_decl();
+  auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
   if (subtype.errored)
     return Failure::kErrored;
-  if (!subtype.matched)
-    return add_error(peek().source(), "unable to determine subtype", use);
-
-  if (!expect(use, Token::Type::kGreaterThan))
-    return Failure::kErrored;
 
   return ctx_.type_mgr().Get(
       std::make_unique<ast::type::VectorType>(subtype.value, count));
@@ -959,29 +932,23 @@
     ast::ArrayDecorationList decos) {
   const char* use = "array declaration";
 
-  if (!expect(use, Token::Type::kLessThan))
-    return Failure::kErrored;
-
-  auto subtype = type_decl();
-  if (subtype.errored)
-    return Failure::kErrored;
-  if (!subtype.matched)
-    return add_error(peek(), "invalid type for array declaration");
-
-  uint32_t size = 0;
-  if (match(Token::Type::kComma)) {
-    auto val = expect_nonzero_positive_sint("array size");
-    if (val.errored)
+  return expect_lt_gt_block(use, [&]() -> Expect<ast::type::Type*> {
+    auto subtype = expect_type(use);
+    if (subtype.errored)
       return Failure::kErrored;
-    size = val.value;
-  }
 
-  if (!expect(use, Token::Type::kGreaterThan))
-    return Failure::kErrored;
+    uint32_t size = 0;
+    if (match(Token::Type::kComma)) {
+      auto val = expect_nonzero_positive_sint("array size");
+      if (val.errored)
+        return Failure::kErrored;
+      size = val.value;
+    }
 
-  auto ty = std::make_unique<ast::type::ArrayType>(subtype.value, size);
-  ty->set_decorations(std::move(decos));
-  return ctx_.type_mgr().Get(std::move(ty));
+    auto ty = std::make_unique<ast::type::ArrayType>(subtype.value, size);
+    ty->set_decorations(std::move(decos));
+    return ctx_.type_mgr().Get(std::move(ty));
+  });
 }
 
 Expect<ast::type::Type*> ParserImpl::expect_type_decl_matrix(Token t) {
@@ -1000,17 +967,9 @@
 
   const char* use = "matrix";
 
-  if (!expect(use, Token::Type::kLessThan))
-    return Failure::kErrored;
-
-  auto subtype = type_decl();
+  auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
   if (subtype.errored)
     return Failure::kErrored;
-  if (!subtype.matched)
-    return add_error(peek().source(), "unable to determine subtype", use);
-
-  if (!expect(use, Token::Type::kGreaterThan))
-    return Failure::kErrored;
 
   return ctx_.type_mgr().Get(
       std::make_unique<ast::type::MatrixType>(subtype.value, rows, columns));
@@ -1948,17 +1907,9 @@
   if (match(Token::Type::kBitcast)) {
     const char* use = "bitcast expression";
 
-    if (!expect(use, Token::Type::kLessThan))
-      return Failure::kErrored;
-
-    auto type = type_decl();
+    auto type = expect_lt_gt_block(use, [&] { return expect_type(use); });
     if (type.errored)
       return Failure::kErrored;
-    if (!type.matched)
-      return add_error(peek().source(), "missing type", use);
-
-    if (!expect(use, Token::Type::kGreaterThan))
-      return Failure::kErrored;
 
     auto params = expect_paren_rhs_stmt();
     if (params.errored)
@@ -2927,6 +2878,12 @@
                       std::forward<F>(body));
 }
 
+template <typename F, typename T>
+T ParserImpl::expect_lt_gt_block(const std::string& use, F&& body) {
+  return expect_block(Token::Type::kLessThan, Token::Type::kGreaterThan, use,
+                      std::forward<F>(body));
+}
+
 }  // namespace wgsl
 }  // namespace reader
 }  // namespace tint
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 0dcaa37..b63d1eb 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -645,7 +645,7 @@
   /// @param end the token that ends the lexical block
   /// @param use a description of what was being parsed if an error was raised
   /// @param body a function or lambda that is called to parse the lexical block
-  /// body, with the signature: `Expect<Result>()`.
+  /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
   /// @return the value returned by |body| if no errors are raised, otherwise
   /// an Expect with error state.
   template <typename F, typename T = ReturnType<F>>
@@ -658,7 +658,7 @@
   /// and |end| arguments, respectively.
   /// @param use a description of what was being parsed if an error was raised
   /// @param body a function or lambda that is called to parse the lexical block
-  /// body, with the signature: `Expect<Result>()`.
+  /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
   /// @return the value returned by |body| if no errors are raised, otherwise
   /// an Expect with error state.
   template <typename F, typename T = ReturnType<F>>
@@ -668,11 +668,21 @@
   /// and |end| arguments, respectively.
   /// @param use a description of what was being parsed if an error was raised
   /// @param body a function or lambda that is called to parse the lexical block
-  /// body, with the signature: `Expect<Result>()`.
+  /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
   /// @return the value returned by |body| if no errors are raised, otherwise
   /// an Expect with error state.
   template <typename F, typename T = ReturnType<F>>
   T expect_brace_block(const std::string& use, F&& body);
+  /// A convenience function that calls |expect_block| passing
+  /// |Token::Type::kLessThan| and |Token::Type::kGreaterThan| for the |start|
+  /// and |end| arguments, respectively.
+  /// @param use a description of what was being parsed if an error was raised
+  /// @param body a function or lambda that is called to parse the lexical block
+  /// body, with the signature: `Expect<Result>()` or `Maybe<Result>()`.
+  /// @return the value returned by |body| if no errors are raised, otherwise
+  /// an Expect with error state.
+  template <typename F, typename T = ReturnType<F>>
+  T expect_lt_gt_block(const std::string& use, F&& body);
 
   /// Downcasts all the decorations in |list| to the type |T|, raising a parser
   /// error if any of the decorations aren't of the type |T|.
@@ -692,6 +702,8 @@
   Expect<std::unique_ptr<ast::ConstructorExpression>>
   expect_const_expr_internal(uint32_t depth);
 
+  Expect<ast::type::Type*> expect_type(const std::string& use);
+
   Maybe<std::unique_ptr<ast::Statement>> for_header_initializer();
   Maybe<std::unique_ptr<ast::Statement>> for_header_continuing();
 
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index 08d84eb..9f6bffe 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -105,7 +105,7 @@
 
 TEST_F(ParserImplErrorTest, BitcastExprMissingType) {
   EXPECT("fn f() -> void { x = bitcast<>(y); }",
-         "test.wgsl:1:30 error: missing type for bitcast expression\n"
+         "test.wgsl:1:30 error: invalid type for bitcast expression\n"
          "fn f() -> void { x = bitcast<>(y); }\n"
          "                             ^\n");
 }
@@ -548,7 +548,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureInvalidSubtype_Old) {
   EXPECT("var x : texture_sampled_1d<1>;",
-         "test.wgsl:1:28 error: invalid subtype for sampled texture type\n"
+         "test.wgsl:1:28 error: invalid type for sampled texture type\n"
          "var x : texture_sampled_1d<1>;\n"
          "                           ^\n");
 }
@@ -569,7 +569,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureInvalidSubtype) {
   EXPECT("var x : texture_1d<1>;",
-         "test.wgsl:1:20 error: invalid subtype for sampled texture type\n"
+         "test.wgsl:1:20 error: invalid type for sampled texture type\n"
          "var x : texture_1d<1>;\n"
          "                   ^\n");
 }
@@ -590,7 +590,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclMultisampledTextureInvalidSubtype) {
   EXPECT("var x : texture_multisampled_2d<1>;",
-         "test.wgsl:1:33 error: invalid subtype for multisampled texture type\n"
+         "test.wgsl:1:33 error: invalid type for multisampled texture type\n"
          "var x : texture_multisampled_2d<1>;\n"
          "                                ^\n");
 }
@@ -1009,7 +1009,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarMatrixMissingType) {
   EXPECT("var i : mat4x4<1>;",
-         "test.wgsl:1:16 error: unable to determine subtype for matrix\n"
+         "test.wgsl:1:16 error: invalid type for matrix\n"
          "var i : mat4x4<1>;\n"
          "               ^\n");
 }
@@ -1051,7 +1051,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingType) {
   EXPECT("var i : ptr<in, 1>;",
-         "test.wgsl:1:17 error: missing type for ptr declaration\n"
+         "test.wgsl:1:17 error: invalid type for ptr declaration\n"
          "var i : ptr<in, 1>;\n"
          "                ^\n");
 }
@@ -1086,7 +1086,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarVectorMissingType) {
   EXPECT("var i : vec3<1>;",
-         "test.wgsl:1:14 error: unable to determine subtype for vector\n"
+         "test.wgsl:1:14 error: invalid type for vector\n"
          "var i : vec3<1>;\n"
          "             ^\n");
 }
diff --git a/src/reader/wgsl/parser_impl_primary_expression_test.cc b/src/reader/wgsl/parser_impl_primary_expression_test.cc
index 249105e..ab77de9 100644
--- a/src/reader/wgsl/parser_impl_primary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -103,7 +103,7 @@
   EXPECT_TRUE(e.errored);
   EXPECT_EQ(e.value, nullptr);
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:6: unable to determine subtype for vector");
+  EXPECT_EQ(p->error(), "1:6: invalid type for vector");
 }
 
 TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_MissingLeftParen) {
@@ -245,7 +245,7 @@
   EXPECT_TRUE(e.errored);
   EXPECT_EQ(e.value, nullptr);
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:9: missing type for bitcast expression");
+  EXPECT_EQ(p->error(), "1:9: invalid type for bitcast expression");
 }
 
 TEST_F(ParserImplTest, PrimaryExpression_Bitcast_InvalidType) {
diff --git a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
index 2913771..bfc4474 100644
--- a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
+++ b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
@@ -123,7 +123,7 @@
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
-  EXPECT_EQ(p->error(), "1:20: invalid subtype for sampled texture type");
+  EXPECT_EQ(p->error(), "1:20: invalid type for sampled texture type");
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan_Old) {
@@ -203,7 +203,7 @@
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
-  EXPECT_EQ(p->error(), "1:12: invalid subtype for sampled texture type");
+  EXPECT_EQ(p->error(), "1:12: invalid type for sampled texture type");
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan) {
@@ -256,7 +256,7 @@
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
-  EXPECT_EQ(p->error(), "1:25: invalid subtype for multisampled texture type");
+  EXPECT_EQ(p->error(), "1:25: invalid type for multisampled texture type");
 }
 
 TEST_F(ParserImplTest,
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index a3d6eae..587b09d 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -221,7 +221,7 @@
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(p->error(), "1:6: unable to determine subtype for vector");
+  ASSERT_EQ(p->error(), "1:6: invalid type for vector");
 }
 INSTANTIATE_TEST_SUITE_P(ParserImplTest,
                          VecMissingType,
@@ -318,7 +318,7 @@
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(p->error(), "1:14: missing type for ptr declaration");
+  ASSERT_EQ(p->error(), "1:14: invalid type for ptr declaration");
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) {
@@ -718,7 +718,7 @@
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(p->error(), "1:8: unable to determine subtype for matrix");
+  ASSERT_EQ(p->error(), "1:8: invalid type for matrix");
 }
 INSTANTIATE_TEST_SUITE_P(ParserImplTest,
                          MatrixMissingType,