wsgl parser: pipeline_stage() -> expect_pipeline_stage()

The only place that calls `pipeline_stage()` expects a stage to exist, so follow the `expect_` pattern.

Bug: tint:282
Change-Id: Ie18d24ed25a5f882e66a8e553e53b4fb52dcf6fa
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31734
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 fc28af3..526ca77 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -1669,14 +1669,10 @@
     if (!expect("stage decoration", Token::Type::kParenLeft))
       return nullptr;
 
-    auto stage = pipeline_stage();
-    if (has_error()) {
+    ast::PipelineStage stage;
+    std::tie(stage, source) = expect_pipeline_stage();
+    if (stage == ast::PipelineStage::kNone)
       return nullptr;
-    }
-    if (stage == ast::PipelineStage::kNone) {
-      add_error(peek(), "invalid value for stage decoration");
-      return nullptr;
-    }
 
     if (!expect("stage decoration", Token::Type::kParenRight))
       return nullptr;
@@ -1784,21 +1780,20 @@
 //   : VERTEX
 //   | FRAGMENT
 //   | COMPUTE
-ast::PipelineStage ParserImpl::pipeline_stage() {
+std::pair<ast::PipelineStage, Source> ParserImpl::expect_pipeline_stage() {
+  Source source;
+  if (match(Token::Type::kVertex, &source))
+    return {ast::PipelineStage::kVertex, source};
+
+  if (match(Token::Type::kFragment, &source))
+    return {ast::PipelineStage::kFragment, source};
+
+  if (match(Token::Type::kCompute, &source))
+    return {ast::PipelineStage::kCompute, source};
+
   auto t = peek();
-  if (t.IsVertex()) {
-    next();  // consume the peek
-    return ast::PipelineStage::kVertex;
-  }
-  if (t.IsFragment()) {
-    next();  // consume the peek
-    return ast::PipelineStage::kFragment;
-  }
-  if (t.IsCompute()) {
-    next();  // consume the peek
-    return ast::PipelineStage::kCompute;
-  }
-  return ast::PipelineStage::kNone;
+  add_error(t, "invalid value for stage decoration");
+  return {ast::PipelineStage::kNone, t.source()};
 }
 
 // body_stmt
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 3cf023f..221c36d 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -257,9 +257,11 @@
   /// Parses a `param_list` grammar element
   /// @returns the parsed variables
   ast::VariableList param_list();
-  /// Parses a `pipeline_stage` grammar element
-  /// @returns the pipeline stage or PipelineStage::kNone if none matched
-  ast::PipelineStage pipeline_stage();
+  /// Parses a `pipeline_stage` grammar element, erroring if the next token does
+  /// not match a stage name.
+  /// @returns the pipeline stage or PipelineStage::kNone if none matched, along
+  /// with the source location for the stage.
+  std::pair<ast::PipelineStage, Source> expect_pipeline_stage();
   /// Parses a `body_stmt` grammar element
   /// @returns the parsed statements
   std::unique_ptr<ast::BlockStatement> body_stmt();
diff --git a/src/reader/wgsl/parser_impl_pipeline_stage_test.cc b/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
index d1c2852..0fcffc4 100644
--- a/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
+++ b/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
@@ -23,12 +23,11 @@
 namespace {
 
 struct PipelineStageData {
-  const char* input;
+  std::string input;
   ast::PipelineStage result;
 };
 inline std::ostream& operator<<(std::ostream& out, PipelineStageData data) {
-  out << std::string(data.input);
-  return out;
+  return out << data.input;
 }
 
 class PipelineStageTest : public ParserImplTestWithParam<PipelineStageData> {};
@@ -37,9 +36,15 @@
   auto params = GetParam();
   auto* p = parser(params.input);
 
-  auto stage = p->pipeline_stage();
-  ASSERT_FALSE(p->has_error());
+  ast::PipelineStage stage;
+  Source source;
+  std::tie(stage, source) = p->expect_pipeline_stage();
+  ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_EQ(stage, params.result);
+  EXPECT_EQ(source.range.begin.line, 1u);
+  EXPECT_EQ(source.range.begin.column, 1u);
+  EXPECT_EQ(source.range.end.line, 1u);
+  EXPECT_EQ(source.range.end.column, 1u + params.input.size());
 
   auto t = p->next();
   EXPECT_TRUE(t.IsEof());
@@ -54,12 +59,16 @@
 
 TEST_F(ParserImplTest, PipelineStage_NoMatch) {
   auto* p = parser("not-a-stage");
-  auto stage = p->pipeline_stage();
+  ast::PipelineStage stage;
+  Source source;
+  std::tie(stage, source) = p->expect_pipeline_stage();
+  ASSERT_TRUE(p->has_error());
+  ASSERT_EQ(p->error(), "1:1: invalid value for stage decoration");
   ASSERT_EQ(stage, ast::PipelineStage::kNone);
-
-  auto t = p->next();
-  EXPECT_TRUE(t.IsIdentifier());
-  EXPECT_EQ(t.to_str(), "not");
+  EXPECT_EQ(source.range.begin.line, 1u);
+  EXPECT_EQ(source.range.begin.column, 1u);
+  EXPECT_EQ(source.range.end.line, 1u);
+  EXPECT_EQ(source.range.end.column, 4u);
 }
 
 }  // namespace