wgsl: Make colon optional for case statements

Fixed: tint:1485
Change-Id: I76af5a284cb455170bed27e852a4bab47c5dc491
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/85525
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/docs/tint/origin-trial-changes.md b/docs/tint/origin-trial-changes.md
index 3bb1e4e..3f3c61d 100644
--- a/docs/tint/origin-trial-changes.md
+++ b/docs/tint/origin-trial-changes.md
@@ -6,6 +6,7 @@
 
 * Parentheses are no longer required around expressions for if and switch statements [tint:1424](crbug.com/tint/1424)
 * Compound assignment statements are now supported. [tint:1325](https://crbug.com/tint/1325)
+* The colon in case statements is now optional. [tint:1485](crbug.com/tint/1485)
 
 ### Breaking changes
 
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 03d6ca3..2798c77 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -1761,8 +1761,8 @@
 }
 
 // switch_body
-//   : CASE case_selectors COLON BRACKET_LEFT case_body BRACKET_RIGHT
-//   | DEFAULT COLON BRACKET_LEFT case_body BRACKET_RIGHT
+//   : CASE case_selectors COLON? BRACKET_LEFT case_body BRACKET_RIGHT
+//   | DEFAULT COLON? BRACKET_LEFT case_body BRACKET_RIGHT
 Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
   if (!peek_is(Token::Type::kCase) && !peek_is(Token::Type::kDefault))
     return Failure::kNoMatch;
@@ -1779,11 +1779,10 @@
     selector_list = std::move(selectors.value);
   }
 
+  // Consume the optional colon if present.
+  match(Token::Type::kColon);
+
   const char* use = "case statement";
-
-  if (!expect(use, Token::Type::kColon))
-    return Failure::kErrored;
-
   auto body = expect_brace_block(use, [&] { return case_body(); });
 
   if (body.errored)
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index 8610ae5..f1444eb 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -1174,14 +1174,6 @@
 )");
 }
 
-TEST_F(ParserImplErrorTest, SwitchStmtCaseMissingColon) {
-  EXPECT("fn f() { switch(1) { case 1 {} } }",
-         R"(test.wgsl:1:29 error: expected ':' for case statement
-fn f() { switch(1) { case 1 {} } }
-                            ^
-)");
-}
-
 TEST_F(ParserImplErrorTest, SwitchStmtCaseMissingLBrace) {
   EXPECT("fn f() { switch(1) { case 1: } }",
          R"(test.wgsl:1:30 error: expected '{' for case statement
diff --git a/src/tint/reader/wgsl/parser_impl_switch_body_test.cc b/src/tint/reader/wgsl/parser_impl_switch_body_test.cc
index 3f9ffc9..e983cfa 100644
--- a/src/tint/reader/wgsl/parser_impl_switch_body_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_switch_body_test.cc
@@ -20,6 +20,22 @@
 namespace {
 
 TEST_F(ParserImplTest, SwitchBody_Case) {
+  auto p = parser("case 1 { a = 4; }");
+  auto e = p->switch_body();
+  EXPECT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(e.matched);
+  EXPECT_FALSE(e.errored);
+  ASSERT_NE(e.value, nullptr);
+  ASSERT_TRUE(e->Is<ast::CaseStatement>());
+  EXPECT_FALSE(e->IsDefault());
+  auto* stmt = e->As<ast::CaseStatement>();
+  ASSERT_EQ(stmt->selectors.size(), 1u);
+  EXPECT_EQ(stmt->selectors[0]->ValueAsU32(), 1u);
+  ASSERT_EQ(e->body->statements.size(), 1u);
+  EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_WithColon) {
   auto p = parser("case 1: { a = 4; }");
   auto e = p->switch_body();
   EXPECT_FALSE(p->has_error()) << p->error();
@@ -36,6 +52,21 @@
 }
 
 TEST_F(ParserImplTest, SwitchBody_Case_TrailingComma) {
+  auto p = parser("case 1, 2, { }");
+  auto e = p->switch_body();
+  EXPECT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(e.matched);
+  EXPECT_FALSE(e.errored);
+  ASSERT_NE(e.value, nullptr);
+  ASSERT_TRUE(e->Is<ast::CaseStatement>());
+  EXPECT_FALSE(e->IsDefault());
+  auto* stmt = e->As<ast::CaseStatement>();
+  ASSERT_EQ(stmt->selectors.size(), 2u);
+  EXPECT_EQ(stmt->selectors[0]->ValueAsU32(), 1u);
+  EXPECT_EQ(stmt->selectors[1]->ValueAsU32(), 2u);
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_TrailingComma_WithColon) {
   auto p = parser("case 1, 2,: { }");
   auto e = p->switch_body();
   EXPECT_FALSE(p->has_error()) << p->error();
@@ -80,17 +111,17 @@
   EXPECT_EQ(p->error(), "1:5: unable to parse case selectors");
 }
 
-TEST_F(ParserImplTest, SwitchBody_Case_MissingColon) {
-  auto p = parser("case 1 { a = 4; }");
+TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketLeft) {
+  auto p = parser("case 1 a = 4; }");
   auto e = p->switch_body();
   EXPECT_TRUE(p->has_error());
   EXPECT_TRUE(e.errored);
   EXPECT_FALSE(e.matched);
   EXPECT_EQ(e.value, nullptr);
-  EXPECT_EQ(p->error(), "1:8: expected ':' for case statement");
+  EXPECT_EQ(p->error(), "1:8: expected '{' for case statement");
 }
 
-TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketLeft) {
+TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketLeft_WithColon) {
   auto p = parser("case 1: a = 4; }");
   auto e = p->switch_body();
   EXPECT_TRUE(p->has_error());
@@ -121,6 +152,21 @@
 }
 
 TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectors) {
+  auto p = parser("case 1, 2 { }");
+  auto e = p->switch_body();
+  EXPECT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(e.matched);
+  EXPECT_FALSE(e.errored);
+  ASSERT_NE(e.value, nullptr);
+  ASSERT_TRUE(e->Is<ast::CaseStatement>());
+  EXPECT_FALSE(e->IsDefault());
+  ASSERT_EQ(e->body->statements.size(), 0u);
+  ASSERT_EQ(e->selectors.size(), 2u);
+  ASSERT_EQ(e->selectors[0]->ValueAsI32(), 1);
+  ASSERT_EQ(e->selectors[1]->ValueAsI32(), 2);
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectors_WithColon) {
   auto p = parser("case 1, 2: { }");
   auto e = p->switch_body();
   EXPECT_FALSE(p->has_error()) << p->error();
@@ -135,16 +181,6 @@
   ASSERT_EQ(e->selectors[1]->ValueAsI32(), 2);
 }
 
-TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsMissingColon) {
-  auto p = parser("case 1, 2 { }");
-  auto e = p->switch_body();
-  EXPECT_TRUE(p->has_error());
-  EXPECT_TRUE(e.errored);
-  EXPECT_FALSE(e.matched);
-  EXPECT_EQ(e.value, nullptr);
-  EXPECT_EQ(p->error(), "1:11: expected ':' for case statement");
-}
-
 TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsMissingComma) {
   auto p = parser("case 1 2: { }");
   auto e = p->switch_body();
@@ -152,7 +188,7 @@
   EXPECT_TRUE(e.errored);
   EXPECT_FALSE(e.matched);
   EXPECT_EQ(e.value, nullptr);
-  EXPECT_EQ(p->error(), "1:8: expected ':' for case statement");
+  EXPECT_EQ(p->error(), "1:8: expected '{' for case statement");
 }
 
 TEST_F(ParserImplTest, SwitchBody_Case_MultipleSelectorsStartsWithComma) {
@@ -166,6 +202,19 @@
 }
 
 TEST_F(ParserImplTest, SwitchBody_Default) {
+  auto p = parser("default { a = 4; }");
+  auto e = p->switch_body();
+  EXPECT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(e.matched);
+  EXPECT_FALSE(e.errored);
+  ASSERT_NE(e.value, nullptr);
+  ASSERT_TRUE(e->Is<ast::CaseStatement>());
+  EXPECT_TRUE(e->IsDefault());
+  ASSERT_EQ(e->body->statements.size(), 1u);
+  EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
+}
+
+TEST_F(ParserImplTest, SwitchBody_Default_WithColon) {
   auto p = parser("default: { a = 4; }");
   auto e = p->switch_body();
   EXPECT_FALSE(p->has_error()) << p->error();
@@ -178,17 +227,17 @@
   EXPECT_TRUE(e->body->statements[0]->Is<ast::AssignmentStatement>());
 }
 
-TEST_F(ParserImplTest, SwitchBody_Default_MissingColon) {
-  auto p = parser("default { a = 4; }");
+TEST_F(ParserImplTest, SwitchBody_Default_MissingBracketLeft) {
+  auto p = parser("default a = 4; }");
   auto e = p->switch_body();
   EXPECT_TRUE(p->has_error());
   EXPECT_TRUE(e.errored);
   EXPECT_FALSE(e.matched);
   EXPECT_EQ(e.value, nullptr);
-  EXPECT_EQ(p->error(), "1:9: expected ':' for case statement");
+  EXPECT_EQ(p->error(), "1:9: expected '{' for case statement");
 }
 
-TEST_F(ParserImplTest, SwitchBody_Default_MissingBracketLeft) {
+TEST_F(ParserImplTest, SwitchBody_Default_MissingBracketLeft_WithColon) {
   auto p = parser("default: a = 4; }");
   auto e = p->switch_body();
   EXPECT_TRUE(p->has_error());