wsgl parser: Use expect() for semicolon checks

Keeps error message consistent. Reduces code.

Bug: tint:282
Change-Id: Ifddb63b65d44427e0680bb25837fcb24ca854eb0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31725
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 f383dee..84af7b5 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -224,11 +224,9 @@
     return;
   }
   if (gv != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ';' for variable declaration");
+    if (!expect("variable declaration", Token::Type::kSemicolon))
       return;
-    }
+
     module_.AddGlobalVariable(std::move(gv));
     return;
   }
@@ -238,11 +236,9 @@
     return;
   }
   if (gc != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ';' for constant declaration");
+    if (!expect("constant declaration", Token::Type::kSemicolon))
       return;
-    }
+
     module_.AddGlobalVariable(std::move(gc));
     return;
   }
@@ -252,11 +248,9 @@
     return;
   }
   if (ta != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ';' for type alias");
+    if (!expect("type alias", Token::Type::kSemicolon))
       return;
-    }
+
     module_.AddConstructedType(ta);
     return;
   }
@@ -266,11 +260,9 @@
     return;
   }
   if (str != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ';' for struct declaration");
+    if (!expect("struct declaration", Token::Type::kSemicolon))
       return;
-    }
+
     auto* type = ctx_.type_mgr().Get(std::move(str));
     register_constructed(type->AsStruct()->name(), type);
     module_.AddConstructedType(type);
@@ -1634,11 +1626,8 @@
     return nullptr;
   }
 
-  t = next();
-  if (!t.IsSemicolon()) {
-    add_error(t, "missing ; for struct member");
+  if (!expect("struct member", Token::Type::kSemicolon))
     return nullptr;
-  }
 
   return std::make_unique<ast::StructMember>(decl.source, decl.name, decl.type,
                                              std::move(decos));
@@ -2115,21 +2104,18 @@
 //   | assignment_stmt SEMICOLON
 //   | body_stmt?
 std::unique_ptr<ast::Statement> ParserImpl::statement() {
-  auto t = peek();
-  if (t.IsSemicolon()) {
-    next();  // Consume the peek
-    return statement();
+  while (match(Token::Type::kSemicolon)) {
+    // Skip empty statements
   }
 
+  auto t = peek();
   auto ret_stmt = return_stmt();
   if (has_error())
     return nullptr;
   if (ret_stmt != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("return statement", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return ret_stmt;
   }
 
@@ -2161,11 +2147,9 @@
   if (has_error())
     return nullptr;
   if (func != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("function call", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return func;
   }
 
@@ -2173,11 +2157,9 @@
   if (has_error())
     return nullptr;
   if (var != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("variable declaration", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return var;
   }
 
@@ -2185,11 +2167,9 @@
   if (has_error())
     return nullptr;
   if (b != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("break statement", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return b;
   }
 
@@ -2197,11 +2177,9 @@
   if (has_error())
     return nullptr;
   if (cont != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("continue statement", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return cont;
   }
 
@@ -2209,11 +2187,9 @@
     auto source = t.source();
     next();  // Consume the peek
 
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("discard statement", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return std::make_unique<ast::DiscardStatement>(source);
   }
 
@@ -2221,11 +2197,9 @@
   if (has_error())
     return nullptr;
   if (assign != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      add_error(t, "missing ;");
+    if (!expect("assignment statement", Token::Type::kSemicolon))
       return nullptr;
-    }
+
     return assign;
   }
 
@@ -2547,11 +2521,8 @@
       auto source = t.source();
       next();  // Consume the peek
 
-      t = next();
-      if (!t.IsSemicolon()) {
-        add_error(t, "missing ;");
-        return {};
-      }
+      if (!expect("fallthrough statement", Token::Type::kSemicolon))
+        return nullptr;
 
       ret->append(std::make_unique<ast::FallthroughStatement>(source));
       break;
@@ -2638,22 +2609,16 @@
     }
   }
 
-  auto t = next();
-  if (!t.IsSemicolon()) {
-    add_error(t, "missing ';' after initializer in for loop");
+  if (!expect("initializer in for loop", Token::Type::kSemicolon))
     return nullptr;
-  }
 
   auto condition = logical_or_expression();
   if (has_error()) {
     return nullptr;
   }
 
-  t = next();
-  if (!t.IsSemicolon()) {
-    add_error(t, "missing ';' after condition in for loop");
+  if (!expect("condition in for loop", Token::Type::kSemicolon))
     return nullptr;
-  }
 
   std::unique_ptr<ast::Statement> continuing = nullptr;
   if (continuing == nullptr) {
@@ -3634,7 +3599,7 @@
   auto t = next();
   if (!t.Is(tok)) {
     std::stringstream err;
-    err << "expected " << Token::TypeToName(tok);
+    err << "expected '" << Token::TypeToName(tok) << "'";
     if (!use.empty()) {
       err << " for " << use;
     }
diff --git a/src/reader/wgsl/parser_impl_call_stmt_test.cc b/src/reader/wgsl/parser_impl_call_stmt_test.cc
index 1b49049..9f567e9 100644
--- a/src/reader/wgsl/parser_impl_call_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_call_stmt_test.cc
@@ -70,7 +70,7 @@
   auto* p = parser("a()");
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:4: missing ;");
+  EXPECT_EQ(p->error(), "1:4: expected ';' for function call");
 }
 
 TEST_F(ParserImplTest, Statement_Call_Bad_ArgList) {
diff --git a/src/reader/wgsl/parser_impl_case_body_test.cc b/src/reader/wgsl/parser_impl_case_body_test.cc
index 61c6ec0..b878037 100644
--- a/src/reader/wgsl/parser_impl_case_body_test.cc
+++ b/src/reader/wgsl/parser_impl_case_body_test.cc
@@ -60,7 +60,7 @@
   auto e = p->case_body();
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:12: missing ;");
+  EXPECT_EQ(p->error(), "1:12: expected ';' for fallthrough statement");
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_continuing_stmt_test.cc b/src/reader/wgsl/parser_impl_continuing_stmt_test.cc
index c570d84..9f2ab58 100644
--- a/src/reader/wgsl/parser_impl_continuing_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_continuing_stmt_test.cc
@@ -34,7 +34,7 @@
   auto e = p->continuing_stmt();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:22: missing ;");
+  EXPECT_EQ(p->error(), "1:22: expected ';' for discard statement");
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index 3f403a4..c73857d 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -77,7 +77,7 @@
 
 TEST_F(ParserImplErrorTest, AssignmentStmtMissingSemicolon) {
   EXPECT("fn f() -> void { a = 1 }",
-         "test.wgsl:1:24 error: missing ;\n"
+         "test.wgsl:1:24 error: expected ';' for assignment statement\n"
          "fn f() -> void { a = 1 }\n"
          "                       ^\n");
 }
@@ -112,7 +112,7 @@
 
 TEST_F(ParserImplErrorTest, BreakStmtMissingSemicolon) {
   EXPECT("fn f() -> void { loop { break } }",
-         "test.wgsl:1:31 error: missing ;\n"
+         "test.wgsl:1:31 error: expected ';' for break statement\n"
          "fn f() -> void { loop { break } }\n"
          "                              ^\n");
 }
@@ -148,7 +148,7 @@
 
 TEST_F(ParserImplErrorTest, CallStmtMissingSemicolon) {
   EXPECT("fn f() -> void { f() }",
-         "test.wgsl:1:22 error: missing ;\n"
+         "test.wgsl:1:22 error: expected ';' for function call\n"
          "fn f() -> void { f() }\n"
          "                     ^\n");
 }
@@ -190,14 +190,14 @@
 
 TEST_F(ParserImplErrorTest, ContinueStmtMissingSemicolon) {
   EXPECT("fn f() -> void { loop { continue } }",
-         "test.wgsl:1:34 error: missing ;\n"
+         "test.wgsl:1:34 error: expected ';' for continue statement\n"
          "fn f() -> void { loop { continue } }\n"
          "                                 ^\n");
 }
 
 TEST_F(ParserImplErrorTest, DiscardStmtMissingSemicolon) {
   EXPECT("fn f() -> void { discard }",
-         "test.wgsl:1:26 error: missing ;\n"
+         "test.wgsl:1:26 error: expected ';' for discard statement\n"
          "fn f() -> void { discard }\n"
          "                         ^\n");
 }
@@ -211,14 +211,14 @@
 
 TEST_F(ParserImplErrorTest, ForLoopInitializerMissingSemicolon) {
   EXPECT("fn f() -> void { for (var i : i32 = 0 i < 8; i=i+1) {} }",
-         "test.wgsl:1:39 error: missing ';' after initializer in for loop\n"
+         "test.wgsl:1:39 error: expected ';' for initializer in for loop\n"
          "fn f() -> void { for (var i : i32 = 0 i < 8; i=i+1) {} }\n"
          "                                      ^\n");
 }
 
 TEST_F(ParserImplErrorTest, ForLoopConditionMissingSemicolon) {
   EXPECT("fn f() -> void { for (var i : i32 = 0; i < 8 i=i+1) {} }",
-         "test.wgsl:1:46 error: missing ';' after condition in for loop\n"
+         "test.wgsl:1:46 error: expected ';' for condition in for loop\n"
          "fn f() -> void { for (var i : i32 = 0; i < 8 i=i+1) {} }\n"
          "                                             ^\n");
 }
@@ -429,7 +429,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstMissingSemicolon) {
   EXPECT("const i : i32 = 1",
-         "test.wgsl:1:18 error: missing ';' for constant declaration\n"
+         "test.wgsl:1:18 error: expected ';' for constant declaration\n"
          "const i : i32 = 1\n"
          "                 ^\n");
 }
@@ -589,7 +589,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclStructDeclMissingSemicolon) {
   EXPECT("struct S {}",
-         "test.wgsl:1:12 error: missing ';' for struct declaration\n"
+         "test.wgsl:1:12 error: expected ';' for struct declaration\n"
          "struct S {}\n"
          "           ^\n");
 }
@@ -631,7 +631,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclStructMemberMissingSemicolon) {
   EXPECT("struct S { i : i32 };",
-         "test.wgsl:1:20 error: missing ; for struct member\n"
+         "test.wgsl:1:20 error: expected ';' for struct member\n"
          "struct S { i : i32 };\n"
          "                   ^\n");
 }
@@ -687,7 +687,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingSemicolon) {
   EXPECT("type meow = f32",
-         "test.wgsl:1:16 error: missing ';' for type alias\n"
+         "test.wgsl:1:16 error: expected ';' for type alias\n"
          "type meow = f32\n"
          "               ^\n");
 }
@@ -932,7 +932,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarMissingSemicolon) {
   EXPECT("var i : i32",
-         "test.wgsl:1:12 error: missing ';' for variable declaration\n"
+         "test.wgsl:1:12 error: expected ';' for variable declaration\n"
          "var i : i32\n"
          "           ^\n");
 }
@@ -1086,7 +1086,7 @@
 
 TEST_F(ParserImplErrorTest, ReturnStmtMissingSemicolon) {
   EXPECT("fn f() -> void { return }",
-         "test.wgsl:1:25 error: missing ;\n"
+         "test.wgsl:1:25 error: expected ';' for return statement\n"
          "fn f() -> void { return }\n"
          "                        ^\n");
 }
@@ -1150,14 +1150,14 @@
 
 TEST_F(ParserImplErrorTest, SwitchStmtCaseFallthroughMissingSemicolon) {
   EXPECT("fn f() -> void { switch(1) { case 1: { fallthrough } case 2: {} } }",
-         "test.wgsl:1:52 error: missing ;\n"
+         "test.wgsl:1:52 error: expected ';' for fallthrough statement\n"
          "fn f() -> void { switch(1) { case 1: { fallthrough } case 2: {} } }\n"
          "                                                   ^\n");
 }
 
 TEST_F(ParserImplErrorTest, VarStmtMissingSemicolon) {
   EXPECT("fn f() -> void { var a : u32 }",
-         "test.wgsl:1:30 error: missing ;\n"
+         "test.wgsl:1:30 error: expected ';' for variable declaration\n"
          "fn f() -> void { var a : u32 }\n"
          "                             ^\n");
 }
diff --git a/src/reader/wgsl/parser_impl_for_stmt_test.cc b/src/reader/wgsl/parser_impl_for_stmt_test.cc
index f0b2435..bbd036b 100644
--- a/src/reader/wgsl/parser_impl_for_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_for_stmt_test.cc
@@ -176,7 +176,7 @@
 // Test a for loop with missing first semicolon is invalid.
 TEST_F(ForStmtErrorTest, MissingFirstSemicolon) {
   std::string for_str = "for () {}";
-  std::string error_str = "1:6: missing ';' after initializer in for loop";
+  std::string error_str = "1:6: expected ';' for initializer in for loop";
 
   TestForWithError(for_str, error_str);
 }
@@ -184,7 +184,7 @@
 // Test a for loop with missing second semicolon is invalid.
 TEST_F(ForStmtErrorTest, MissingSecondSemicolon) {
   std::string for_str = "for (;) {}";
-  std::string error_str = "1:7: missing ';' after condition in for loop";
+  std::string error_str = "1:7: expected ';' for condition in for loop";
 
   TestForWithError(for_str, error_str);
 }
@@ -225,7 +225,7 @@
 // variable_stmt | assignment_stmt | func_call_stmt.
 TEST_F(ForStmtErrorTest, InvalidInitializerMatch) {
   std::string for_str = "for (if (true) {} ;;) { }";
-  std::string error_str = "1:6: missing ';' after initializer in for loop";
+  std::string error_str = "1:6: expected ';' for initializer in for loop";
 
   TestForWithError(for_str, error_str);
 }
@@ -242,7 +242,7 @@
 // logical_or_expression.
 TEST_F(ForStmtErrorTest, InvalidBreakConditionMatch) {
   std::string for_str = "for (; var i: i32 = 0;) { }";
-  std::string error_str = "1:8: missing ';' after condition in for loop";
+  std::string error_str = "1:8: expected ';' for condition in for loop";
 
   TestForWithError(for_str, error_str);
 }
diff --git a/src/reader/wgsl/parser_impl_function_decl_test.cc b/src/reader/wgsl/parser_impl_function_decl_test.cc
index 00558a7..79720a1 100644
--- a/src/reader/wgsl/parser_impl_function_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_function_decl_test.cc
@@ -166,7 +166,7 @@
   auto f = p->function_decl();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(f, nullptr);
-  EXPECT_EQ(p->error(), "1:28: missing ;");
+  EXPECT_EQ(p->error(), "1:28: expected ';' for return statement");
 }
 
 TEST_F(ParserImplTest, FunctionDecl_MissingLeftBrace) {
diff --git a/src/reader/wgsl/parser_impl_global_decl_test.cc b/src/reader/wgsl/parser_impl_global_decl_test.cc
index cc93b06..fc41e26 100644
--- a/src/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_global_decl_test.cc
@@ -52,7 +52,7 @@
   auto* p = parser("var<out> a : vec2<i32>");
   p->global_decl();
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:23: missing ';' for variable declaration");
+  EXPECT_EQ(p->error(), "1:23: expected ';' for variable declaration");
 }
 
 TEST_F(ParserImplTest, GlobalDecl_GlobalConstant) {
@@ -78,7 +78,7 @@
   auto* p = parser("const a : vec2<i32> = vec2<i32>(1, 2)");
   p->global_decl();
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:38: missing ';' for constant declaration");
+  EXPECT_EQ(p->error(), "1:38: expected ';' for constant declaration");
 }
 
 TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
@@ -124,7 +124,7 @@
   auto* p = parser("type A = i32");
   p->global_decl();
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:13: missing ';' for type alias");
+  EXPECT_EQ(p->error(), "1:13: expected ';' for type alias");
 }
 
 TEST_F(ParserImplTest, GlobalDecl_Function) {
@@ -225,7 +225,7 @@
   auto* p = parser("[[block]] struct A {}");
   p->global_decl();
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:22: missing ';' for struct declaration");
+  EXPECT_EQ(p->error(), "1:22: expected ';' for struct declaration");
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_loop_stmt_test.cc b/src/reader/wgsl/parser_impl_loop_stmt_test.cc
index f8cc432..71962dd 100644
--- a/src/reader/wgsl/parser_impl_loop_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_loop_stmt_test.cc
@@ -86,7 +86,7 @@
   auto e = p->loop_stmt();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:16: missing ;");
+  EXPECT_EQ(p->error(), "1:16: expected ';' for discard statement");
 }
 
 TEST_F(ParserImplTest, LoopStmt_InvalidContinuing) {
@@ -94,7 +94,7 @@
   auto e = p->loop_stmt();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:29: missing ;");
+  EXPECT_EQ(p->error(), "1:29: expected ';' for discard statement");
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_statement_test.cc b/src/reader/wgsl/parser_impl_statement_test.cc
index fdd4425..ee6df75 100644
--- a/src/reader/wgsl/parser_impl_statement_test.cc
+++ b/src/reader/wgsl/parser_impl_statement_test.cc
@@ -66,7 +66,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:7: missing ;");
+  EXPECT_EQ(p->error(), "1:7: expected ';' for return statement");
 }
 
 TEST_F(ParserImplTest, Statement_Return_Invalid) {
@@ -74,7 +74,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ;");
+  EXPECT_EQ(p->error(), "1:8: expected ';' for return statement");
 }
 
 TEST_F(ParserImplTest, Statement_If) {
@@ -114,7 +114,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:12: missing ;");
+  EXPECT_EQ(p->error(), "1:12: expected ';' for variable declaration");
 }
 
 TEST_F(ParserImplTest, Statement_Switch) {
@@ -170,7 +170,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ;");
+  EXPECT_EQ(p->error(), "1:6: expected ';' for assignment statement");
 }
 
 TEST_F(ParserImplTest, Statement_Break) {
@@ -186,7 +186,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ;");
+  EXPECT_EQ(p->error(), "1:6: expected ';' for break statement");
 }
 
 TEST_F(ParserImplTest, Statement_Continue) {
@@ -202,7 +202,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:9: missing ;");
+  EXPECT_EQ(p->error(), "1:9: expected ';' for continue statement");
 }
 
 TEST_F(ParserImplTest, Statement_Discard) {
@@ -218,7 +218,7 @@
   auto e = p->statement();
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ;");
+  EXPECT_EQ(p->error(), "1:8: expected ';' for discard statement");
 }
 
 TEST_F(ParserImplTest, Statement_Body) {
diff --git a/src/reader/wgsl/parser_impl_struct_member_test.cc b/src/reader/wgsl/parser_impl_struct_member_test.cc
index d4913c3..4c2c3a6 100644
--- a/src/reader/wgsl/parser_impl_struct_member_test.cc
+++ b/src/reader/wgsl/parser_impl_struct_member_test.cc
@@ -106,7 +106,7 @@
   auto m = p->struct_member();
   ASSERT_TRUE(p->has_error());
   ASSERT_EQ(m, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ; for struct member");
+  EXPECT_EQ(p->error(), "1:8: expected ';' for struct member");
 }
 
 }  // namespace