wgsl parser: Replace set_error() with add_error()

Have slightly different overloads.

Long term we will want to be able to emit more than one error in a single parse.

Bug: tint:282
Bug: tint:291
Change-Id: Ide61c6ca75d45065e917b8fa16a097048397e31b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31723
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 583c3ea..5252e96 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -125,18 +125,29 @@
 
 ParserImpl::~ParserImpl() = default;
 
-void ParserImpl::set_error(const Token& t, const std::string& err) {
+void ParserImpl::add_error(const Token& t,
+                           const std::string& err,
+                           const std::string& use) {
+  std::stringstream msg;
+  msg << err;
+  if (!use.empty()) {
+    msg << " for " << use;
+  }
+  add_error(t, msg.str());
+}
+
+void ParserImpl::add_error(const Token& t, const std::string& err) {
+  add_error(t.source(), err);
+}
+
+void ParserImpl::add_error(const Source& source, const std::string& err) {
   diag::Diagnostic diagnostic;
   diagnostic.severity = diag::Severity::Error;
   diagnostic.message = err;
-  diagnostic.source = t.source();
+  diagnostic.source = source;
   diags_.add(std::move(diagnostic));
 }
 
-void ParserImpl::set_error(const Token& t) {
-  set_error(t, "invalid token (" + t.to_name() + ") encountered");
-}
-
 Token ParserImpl::next() {
   if (!token_queue_.empty()) {
     auto t = token_queue_.front();
@@ -215,7 +226,7 @@
   if (gv != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ';' for variable declaration");
+      add_error(t, "missing ';' for variable declaration");
       return;
     }
     module_.AddGlobalVariable(std::move(gv));
@@ -229,7 +240,7 @@
   if (gc != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ';' for constant declaration");
+      add_error(t, "missing ';' for constant declaration");
       return;
     }
     module_.AddGlobalVariable(std::move(gc));
@@ -243,7 +254,7 @@
   if (ta != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ';' for type alias");
+      add_error(t, "missing ';' for type alias");
       return;
     }
     module_.AddConstructedType(ta);
@@ -257,7 +268,7 @@
   if (str != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ';' for struct declaration");
+      add_error(t, "missing ';' for struct declaration");
       return;
     }
     auto* type = ctx_.type_mgr().Get(std::move(str));
@@ -275,7 +286,7 @@
     return;
   }
 
-  set_error(t);
+  add_error(t, "invalid token");
 }
 
 // global_variable_decl
@@ -300,7 +311,7 @@
     return nullptr;
   if (var == nullptr) {
     if (decos.size() > 0)
-      set_error(peek(), "error parsing variable declaration");
+      add_error(peek(), "error parsing variable declaration");
 
     return nullptr;
   }
@@ -320,7 +331,7 @@
     if (has_error())
       return nullptr;
     if (expr == nullptr) {
-      set_error(peek(), "invalid expression");
+      add_error(peek(), "invalid expression");
       return nullptr;
     }
 
@@ -342,7 +353,7 @@
   if (has_error())
     return nullptr;
   if (decl.name.empty() || decl.type == nullptr) {
-    set_error(peek(), "error parsing constant variable identifier");
+    add_error(peek(), "error parsing constant variable identifier");
     return nullptr;
   }
 
@@ -352,7 +363,7 @@
 
   t = next();
   if (!t.IsEqual()) {
-    set_error(t, "missing = for const declaration");
+    add_error(t, "missing = for const declaration");
     return nullptr;
   }
 
@@ -360,7 +371,7 @@
   if (has_error())
     return nullptr;
   if (init == nullptr) {
-    set_error(peek(), "error parsing scalar constructor");
+    add_error(peek(), "error parsing scalar constructor");
     return nullptr;
   }
   var->set_constructor(std::move(init));
@@ -379,7 +390,7 @@
   // Check the empty list before verifying the contents
   t = peek(1);
   if (t.IsAttrRight()) {
-    set_error(t, "empty variable decoration list");
+    add_error(t, "empty variable decoration list");
     return false;
   }
 
@@ -395,7 +406,7 @@
     return false;
   }
   if (deco == nullptr) {
-    set_error(peek(), "missing variable decoration for decoration list");
+    add_error(peek(), "missing variable decoration for decoration list");
     return false;
   }
   for (;;) {
@@ -412,7 +423,7 @@
       return false;
     }
     if (deco == nullptr) {
-      set_error(peek(), "missing variable decoration after comma");
+      add_error(peek(), "missing variable decoration after comma");
       return false;
     }
   }
@@ -421,10 +432,10 @@
   if (!t.IsAttrRight()) {
     deco = variable_decoration();
     if (deco != nullptr) {
-      set_error(t, "missing comma in variable decoration list");
+      add_error(t, "missing comma in variable decoration list");
       return false;
     }
-    set_error(t, "missing ]] for variable decoration");
+    add_error(t, "missing ]] for variable decoration");
     return false;
   }
   next();  // consume the peek
@@ -445,20 +456,20 @@
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for location decoration");
+      add_error(t, "missing ( for location decoration");
       return {};
     }
 
     t = next();
     if (!t.IsSintLiteral()) {
-      set_error(t, "invalid value for location decoration");
+      add_error(t, "invalid value for location decoration");
       return {};
     }
     int32_t val = t.to_i32();
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for location decoration");
+      add_error(t, "missing ) for location decoration");
       return {};
     }
     return std::make_unique<ast::LocationDecoration>(val, source);
@@ -468,25 +479,25 @@
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for builtin decoration");
+      add_error(t, "missing ( for builtin decoration");
       return {};
     }
 
     t = next();
     if (!t.IsIdentifier() || t.to_str().empty()) {
-      set_error(t, "expected identifier for builtin");
+      add_error(t, "expected identifier for builtin");
       return {};
     }
 
     ast::Builtin builtin = ident_to_builtin(t.to_str());
     if (builtin == ast::Builtin::kNone) {
-      set_error(t, "invalid value for builtin decoration");
+      add_error(t, "invalid value for builtin decoration");
       return {};
     }
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for builtin decoration");
+      add_error(t, "missing ) for builtin decoration");
       return {};
     }
     return std::make_unique<ast::BuiltinDecoration>(builtin, source);
@@ -496,20 +507,20 @@
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for binding decoration");
+      add_error(t, "missing ( for binding decoration");
       return {};
     }
 
     t = next();
     if (!t.IsSintLiteral()) {
-      set_error(t, "invalid value for binding decoration");
+      add_error(t, "invalid value for binding decoration");
       return {};
     }
     int32_t val = t.to_i32();
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for binding decoration");
+      add_error(t, "missing ) for binding decoration");
       return {};
     }
 
@@ -520,20 +531,20 @@
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for set decoration");
+      add_error(t, "missing ( for set decoration");
       return {};
     }
 
     t = next();
     if (!t.IsSintLiteral()) {
-      set_error(t, "invalid value for set decoration");
+      add_error(t, "invalid value for set decoration");
       return {};
     }
     uint32_t val = t.to_i32();
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for set decoration");
+      add_error(t, "missing ) for set decoration");
       return {};
     }
 
@@ -560,7 +571,7 @@
   if (has_error())
     return nullptr;
   if (decl.name.empty() || decl.type == nullptr) {
-    set_error(peek(), "invalid identifier declaration");
+    add_error(peek(), "invalid identifier declaration");
     return nullptr;
   }
 
@@ -588,7 +599,7 @@
   if (dim != ast::type::TextureDimension::kNone) {
     auto t = next();
     if (!t.IsLessThan()) {
-      set_error(peek(), "missing '<' for sampled texture type");
+      add_error(peek(), "missing '<' for sampled texture type");
       return nullptr;
     }
 
@@ -596,13 +607,13 @@
     if (has_error())
       return nullptr;
     if (subtype == nullptr) {
-      set_error(peek(), "invalid subtype for sampled texture type");
+      add_error(peek(), "invalid subtype for sampled texture type");
       return nullptr;
     }
 
     t = next();
     if (!t.IsGreaterThan()) {
-      set_error(peek(), "missing '>' for sampled texture type");
+      add_error(peek(), "missing '>' for sampled texture type");
       return nullptr;
     }
 
@@ -614,7 +625,7 @@
   if (dim != ast::type::TextureDimension::kNone) {
     auto t = next();
     if (!t.IsLessThan()) {
-      set_error(peek(), "missing '<' for multisampled texture type");
+      add_error(peek(), "missing '<' for multisampled texture type");
       return nullptr;
     }
 
@@ -622,13 +633,13 @@
     if (has_error())
       return nullptr;
     if (subtype == nullptr) {
-      set_error(peek(), "invalid subtype for multisampled texture type");
+      add_error(peek(), "invalid subtype for multisampled texture type");
       return nullptr;
     }
 
     t = next();
     if (!t.IsGreaterThan()) {
-      set_error(peek(), "missing '>' for multisampled texture type");
+      add_error(peek(), "missing '>' for multisampled texture type");
       return nullptr;
     }
 
@@ -642,7 +653,7 @@
   if (storage_dim != ast::type::TextureDimension::kNone) {
     auto t = next();
     if (!t.IsLessThan()) {
-      set_error(peek(), "missing '<' for storage texture type");
+      add_error(peek(), "missing '<' for storage texture type");
       return nullptr;
     }
 
@@ -650,13 +661,13 @@
     if (has_error())
       return nullptr;
     if (format == ast::type::ImageFormat::kNone) {
-      set_error(peek(), "invalid format for storage texture type");
+      add_error(peek(), "invalid format for storage texture type");
       return nullptr;
     }
 
     t = next();
     if (!t.IsGreaterThan()) {
-      set_error(peek(), "missing '>' for storage texture type");
+      add_error(peek(), "missing '>' for storage texture type");
       return nullptr;
     }
 
@@ -1022,7 +1033,7 @@
 
   t = next();
   if (!t.IsColon()) {
-    set_error(t, "missing : for identifier declaration");
+    add_error(t, "missing : for identifier declaration");
     return {};
   }
 
@@ -1030,7 +1041,7 @@
   if (has_error())
     return {};
   if (type == nullptr) {
-    set_error(peek(), "invalid type for identifier declaration");
+    add_error(peek(), "invalid type for identifier declaration");
     return {};
   }
 
@@ -1050,13 +1061,13 @@
   if (has_error())
     return sc;
   if (sc == ast::StorageClass::kNone) {
-    set_error(peek(), "invalid storage class for variable decoration");
+    add_error(peek(), "invalid storage class for variable decoration");
     return sc;
   }
 
   t = next();
   if (!t.IsGreaterThan()) {
-    set_error(t, "missing > for variable decoration");
+    add_error(t, "missing > for variable decoration");
     return ast::StorageClass::kNone;
   }
 
@@ -1074,14 +1085,14 @@
 
   t = next();
   if (!t.IsIdentifier()) {
-    set_error(t, "missing identifier for type alias");
+    add_error(t, "missing identifier for type alias");
     return nullptr;
   }
   auto name = t.to_str();
 
   t = next();
   if (!t.IsEqual()) {
-    set_error(t, "missing = for type alias");
+    add_error(t, "missing = for type alias");
     return nullptr;
   }
 
@@ -1089,7 +1100,7 @@
   if (has_error())
     return nullptr;
   if (type == nullptr) {
-    set_error(peek(), "invalid type alias");
+    add_error(peek(), "invalid type alias");
     return nullptr;
   }
 
@@ -1130,7 +1141,7 @@
     next();  // Consume the peek
     auto* ty = get_constructed(t.to_str());
     if (ty == nullptr) {
-      set_error(t, "unknown constructed type '" + t.to_str() + "'");
+      add_error(t, "unknown constructed type '" + t.to_str() + "'");
       return nullptr;
     }
     return ty;
@@ -1175,7 +1186,7 @@
     t = peek();
   }
   if (!decos.empty() && !t.IsArray()) {
-    set_error(t, "found array decoration but no array");
+    add_error(t, "found array decoration but no array");
     return nullptr;
   }
   if (t.IsArray()) {
@@ -1203,7 +1214,7 @@
 
   t = next();
   if (!t.IsLessThan()) {
-    set_error(t, "missing < for ptr declaration");
+    add_error(t, "missing < for ptr declaration");
     return nullptr;
   }
 
@@ -1211,13 +1222,13 @@
   if (has_error())
     return nullptr;
   if (sc == ast::StorageClass::kNone) {
-    set_error(peek(), "missing storage class for ptr declaration");
+    add_error(peek(), "missing storage class for ptr declaration");
     return nullptr;
   }
 
   t = next();
   if (!t.IsComma()) {
-    set_error(t, "missing , for ptr declaration");
+    add_error(t, "missing , for ptr declaration");
     return nullptr;
   }
 
@@ -1225,13 +1236,13 @@
   if (has_error())
     return nullptr;
   if (subtype == nullptr) {
-    set_error(peek(), "missing type for ptr declaration");
+    add_error(peek(), "missing type for ptr declaration");
     return nullptr;
   }
 
   t = next();
   if (!t.IsGreaterThan()) {
-    set_error(t, "missing > for ptr declaration");
+    add_error(t, "missing > for ptr declaration");
     return nullptr;
   }
 
@@ -1250,7 +1261,7 @@
 
   t = next();
   if (!t.IsLessThan()) {
-    set_error(t, "missing < for vector");
+    add_error(t, "missing < for vector");
     return nullptr;
   }
 
@@ -1258,13 +1269,13 @@
   if (has_error())
     return nullptr;
   if (subtype == nullptr) {
-    set_error(peek(), "unable to determine subtype for vector");
+    add_error(peek(), "unable to determine subtype for vector");
     return nullptr;
   }
 
   t = next();
   if (!t.IsGreaterThan()) {
-    set_error(t, "missing > for vector");
+    add_error(t, "missing > for vector");
     return nullptr;
   }
 
@@ -1278,7 +1289,7 @@
 
   t = next();
   if (!t.IsLessThan()) {
-    set_error(t, "missing < for array declaration");
+    add_error(t, "missing < for array declaration");
     return nullptr;
   }
 
@@ -1286,7 +1297,7 @@
   if (has_error())
     return nullptr;
   if (subtype == nullptr) {
-    set_error(peek(), "invalid type for array declaration");
+    add_error(peek(), "invalid type for array declaration");
     return nullptr;
   }
 
@@ -1295,18 +1306,18 @@
   if (t.IsComma()) {
     t = next();
     if (!t.IsSintLiteral()) {
-      set_error(t, "missing size of array declaration");
+      add_error(t, "missing size of array declaration");
       return nullptr;
     }
     if (t.to_i32() <= 0) {
-      set_error(t, "invalid size for array declaration");
+      add_error(t, "invalid size for array declaration");
       return nullptr;
     }
     size = static_cast<uint32_t>(t.to_i32());
     t = next();
   }
   if (!t.IsGreaterThan()) {
-    set_error(t, "missing > for array declaration");
+    add_error(t, "missing > for array declaration");
     return nullptr;
   }
 
@@ -1340,23 +1351,23 @@
     auto source = t.source();
 
     if (!t.IsStride()) {
-      set_error(t, "unknown array decoration");
+      add_error(t, "unknown array decoration");
       return false;
     }
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for stride attribute");
+      add_error(t, "missing ( for stride attribute");
       return false;
     }
 
     t = next();
     if (!t.IsSintLiteral()) {
-      set_error(t, "missing value for stride decoration");
+      add_error(t, "missing value for stride decoration");
       return false;
     }
     if (t.to_i32() < 0) {
-      set_error(t, "invalid stride value: " + t.to_str());
+      add_error(t, "invalid stride value: " + t.to_str());
       return false;
     }
     uint32_t stride = static_cast<uint32_t>(t.to_i32());
@@ -1364,7 +1375,7 @@
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for stride attribute");
+      add_error(t, "missing ) for stride attribute");
       return false;
     }
 
@@ -1377,7 +1388,7 @@
 
   t = next();
   if (!t.IsAttrRight()) {
-    set_error(t, "missing ]] for array decoration");
+    add_error(t, "missing ]] for array decoration");
     return false;
   }
   return true;
@@ -1401,7 +1412,7 @@
 
   t = next();
   if (!t.IsLessThan()) {
-    set_error(t, "missing < for matrix");
+    add_error(t, "missing < for matrix");
     return nullptr;
   }
 
@@ -1409,13 +1420,13 @@
   if (has_error())
     return nullptr;
   if (subtype == nullptr) {
-    set_error(peek(), "unable to determine subtype for matrix");
+    add_error(peek(), "unable to determine subtype for matrix");
     return nullptr;
   }
 
   t = next();
   if (!t.IsGreaterThan()) {
-    set_error(t, "missing > for matrix");
+    add_error(t, "missing > for matrix");
     return nullptr;
   }
 
@@ -1493,7 +1504,7 @@
 
   t = peek();
   if (!decos.empty() && !t.IsStruct()) {
-    set_error(t, "missing struct declaration");
+    add_error(t, "missing struct declaration");
     return nullptr;
   } else if (!t.IsStruct()) {
     return nullptr;
@@ -1502,7 +1513,7 @@
 
   t = next();
   if (!t.IsIdentifier()) {
-    set_error(t, "missing identifier for struct declaration");
+    add_error(t, "missing identifier for struct declaration");
     return nullptr;
   }
   auto name = t.to_str();
@@ -1539,7 +1550,7 @@
 
   t = next();
   if (!t.IsAttrRight()) {
-    set_error(t, "missing ]] for struct decoration");
+    add_error(t, "missing ]] for struct decoration");
     return false;
   }
 
@@ -1560,7 +1571,7 @@
 ast::StructMemberList ParserImpl::struct_body_decl() {
   auto t = peek();
   if (!t.IsBraceLeft()) {
-    set_error(t, "missing { for struct declaration");
+    add_error(t, "missing { for struct declaration");
     return {};
   }
   next();  // Consume the peek
@@ -1577,7 +1588,7 @@
     if (has_error())
       return {};
     if (mem == nullptr) {
-      set_error(peek(), "invalid struct member");
+      add_error(peek(), "invalid struct member");
       return {};
     }
 
@@ -1590,7 +1601,7 @@
 
   t = next();
   if (!t.IsBraceRight()) {
-    set_error(t, "missing } for struct declaration");
+    add_error(t, "missing } for struct declaration");
     return {};
   }
 
@@ -1619,13 +1630,13 @@
   if (has_error())
     return nullptr;
   if (decl.name.empty() || decl.type == nullptr) {
-    set_error(peek(), "invalid identifier declaration");
+    add_error(peek(), "invalid identifier declaration");
     return nullptr;
   }
 
   t = next();
   if (!t.IsSemicolon()) {
-    set_error(t, "missing ; for struct member");
+    add_error(t, "missing ; for struct member");
     return nullptr;
   }
 
@@ -1648,7 +1659,7 @@
 
   t = peek();
   if (t.IsAttrRight()) {
-    set_error(t, "empty struct member decoration found");
+    add_error(t, "empty struct member decoration found");
     return false;
   }
 
@@ -1667,7 +1678,7 @@
   }
 
   if (!t.IsAttrRight()) {
-    set_error(t, "missing ]] for struct member decoration");
+    add_error(t, "missing ]] for struct member decoration");
     return false;
   }
   return true;
@@ -1687,24 +1698,24 @@
 
   t = next();
   if (!t.IsParenLeft()) {
-    set_error(t, "missing ( for offset");
+    add_error(t, "missing ( for offset");
     return nullptr;
   }
 
   t = next();
   if (!t.IsSintLiteral()) {
-    set_error(t, "invalid value for offset decoration");
+    add_error(t, "invalid value for offset decoration");
     return nullptr;
   }
   int32_t val = t.to_i32();
   if (val < 0) {
-    set_error(t, "offset value must be >= 0");
+    add_error(t, "offset value must be >= 0");
     return nullptr;
   }
 
   t = next();
   if (!t.IsParenRight()) {
-    set_error(t, "missing ) for offset");
+    add_error(t, "missing ) for offset");
     return nullptr;
   }
 
@@ -1730,7 +1741,7 @@
     return nullptr;
   if (f == nullptr) {
     if (decos.size() > 0) {
-      set_error(peek(), "error parsing function declaration");
+      add_error(peek(), "error parsing function declaration");
     }
     return nullptr;
   }
@@ -1754,7 +1765,7 @@
   // Handle error on empty attributes before the type check
   t = peek(1);
   if (t.IsAttrRight()) {
-    set_error(t, "missing decorations for function decoration block");
+    add_error(t, "missing decorations for function decoration block");
     return false;
   }
 
@@ -1772,7 +1783,7 @@
       return false;
     }
     if (deco == nullptr) {
-      set_error(peek(), "expected decoration but none found");
+      add_error(peek(), "expected decoration but none found");
       return false;
     }
     decos.push_back(std::move(deco));
@@ -1785,13 +1796,13 @@
     next();  // Consume the peek
   }
   if (count == 0) {
-    set_error(peek(), "missing decorations for function decoration block");
+    add_error(peek(), "missing decorations for function decoration block");
     return false;
   }
 
   t = next();
   if (!t.IsAttrRight()) {
-    set_error(t, "missing ]] for function decorations");
+    add_error(t, "missing ]] for function decorations");
     return false;
   }
   return true;
@@ -1809,17 +1820,17 @@
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for workgroup_size");
+      add_error(t, "missing ( for workgroup_size");
       return nullptr;
     }
 
     t = next();
     if (!t.IsSintLiteral()) {
-      set_error(t, "missing x value for workgroup_size");
+      add_error(t, "missing x value for workgroup_size");
       return nullptr;
     }
     if (t.to_i32() <= 0) {
-      set_error(t, "invalid value for workgroup_size x parameter");
+      add_error(t, "invalid value for workgroup_size x parameter");
       return nullptr;
     }
     int32_t x = t.to_i32();
@@ -1832,11 +1843,11 @@
 
       t = next();
       if (!t.IsSintLiteral()) {
-        set_error(t, "missing y value for workgroup_size");
+        add_error(t, "missing y value for workgroup_size");
         return nullptr;
       }
       if (t.to_i32() <= 0) {
-        set_error(t, "invalid value for workgroup_size y parameter");
+        add_error(t, "invalid value for workgroup_size y parameter");
         return nullptr;
       }
       y = t.to_i32();
@@ -1847,11 +1858,11 @@
 
         t = next();
         if (!t.IsSintLiteral()) {
-          set_error(t, "missing z value for workgroup_size");
+          add_error(t, "missing z value for workgroup_size");
           return nullptr;
         }
         if (t.to_i32() <= 0) {
-          set_error(t, "invalid value for workgroup_size z parameter");
+          add_error(t, "invalid value for workgroup_size z parameter");
           return nullptr;
         }
         z = t.to_i32();
@@ -1860,7 +1871,7 @@
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for workgroup_size");
+      add_error(t, "missing ) for workgroup_size");
       return nullptr;
     }
 
@@ -1872,7 +1883,7 @@
 
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for stage decoration");
+      add_error(t, "missing ( for stage decoration");
       return nullptr;
     }
 
@@ -1881,13 +1892,13 @@
       return nullptr;
     }
     if (stage == ast::PipelineStage::kNone) {
-      set_error(peek(), "invalid value for stage decoration");
+      add_error(peek(), "invalid value for stage decoration");
       return nullptr;
     }
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for stage decoration");
+      add_error(t, "missing ) for stage decoration");
       return nullptr;
     }
     return std::make_unique<ast::StageDecoration>(stage, source);
@@ -1919,14 +1930,14 @@
 
   t = next();
   if (!t.IsIdentifier()) {
-    set_error(t, "missing identifier for function");
+    add_error(t, "missing identifier for function");
     return nullptr;
   }
   auto name = t.to_str();
 
   t = next();
   if (!t.IsParenLeft()) {
-    set_error(t, "missing ( for function declaration");
+    add_error(t, "missing ( for function declaration");
     return nullptr;
   }
 
@@ -1936,13 +1947,13 @@
 
   t = next();
   if (!t.IsParenRight()) {
-    set_error(t, "missing ) for function declaration");
+    add_error(t, "missing ) for function declaration");
     return nullptr;
   }
 
   t = next();
   if (!t.IsArrow()) {
-    set_error(t, "missing -> for function declaration");
+    add_error(t, "missing -> for function declaration");
     return nullptr;
   }
 
@@ -1950,7 +1961,7 @@
   if (has_error())
     return nullptr;
   if (type == nullptr) {
-    set_error(peek(), "unable to determine function return type");
+    add_error(peek(), "unable to determine function return type");
     return nullptr;
   }
 
@@ -1991,7 +2002,7 @@
     if (has_error())
       return {};
     if (decl.name.empty() || decl.type == nullptr) {
-      set_error(t, "found , but no variable declaration");
+      add_error(t, "found , but no variable declaration");
       return {};
     }
   }
@@ -2025,7 +2036,7 @@
 std::unique_ptr<ast::BlockStatement> ParserImpl::body_stmt() {
   auto t = peek();
   if (!t.IsBraceLeft()) {
-    set_error(t, "missing {");
+    add_error(t, "missing {");
     return nullptr;
   }
 
@@ -2037,7 +2048,7 @@
 
   t = next();
   if (!t.IsBraceRight()) {
-    set_error(t, "missing }");
+    add_error(t, "missing }");
     return nullptr;
   }
 
@@ -2049,7 +2060,7 @@
 std::unique_ptr<ast::Expression> ParserImpl::paren_rhs_stmt() {
   auto t = peek();
   if (!t.IsParenLeft()) {
-    set_error(t, "expected (");
+    add_error(t, "expected (");
     return nullptr;
   }
   next();  // Consume the peek
@@ -2058,13 +2069,13 @@
   if (has_error())
     return nullptr;
   if (expr == nullptr) {
-    set_error(peek(), "unable to parse expression");
+    add_error(peek(), "unable to parse expression");
     return nullptr;
   }
 
   t = next();
   if (!t.IsParenRight()) {
-    set_error(t, "expected )");
+    add_error(t, "expected )");
     return nullptr;
   }
 
@@ -2116,7 +2127,7 @@
   if (ret_stmt != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return ret_stmt;
@@ -2152,7 +2163,7 @@
   if (func != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return func;
@@ -2164,7 +2175,7 @@
   if (var != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return var;
@@ -2176,7 +2187,7 @@
   if (b != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return b;
@@ -2188,7 +2199,7 @@
   if (cont != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return cont;
@@ -2200,7 +2211,7 @@
 
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return std::make_unique<ast::DiscardStatement>(source);
@@ -2212,7 +2223,7 @@
   if (assign != nullptr) {
     t = next();
     if (!t.IsSemicolon()) {
-      set_error(t, "missing ;");
+      add_error(t, "missing ;");
       return nullptr;
     }
     return assign;
@@ -2263,13 +2274,13 @@
     if (has_error())
       return nullptr;
     if (decl.name.empty() || decl.type == nullptr) {
-      set_error(peek(), "unable to parse variable declaration");
+      add_error(peek(), "unable to parse variable declaration");
       return nullptr;
     }
 
     t = next();
     if (!t.IsEqual()) {
-      set_error(t, "missing = for constant declaration");
+      add_error(t, "missing = for constant declaration");
       return nullptr;
     }
 
@@ -2277,7 +2288,7 @@
     if (has_error())
       return nullptr;
     if (constructor == nullptr) {
-      set_error(peek(), "missing constructor for const declaration");
+      add_error(peek(), "missing constructor for const declaration");
       return nullptr;
     }
 
@@ -2303,7 +2314,7 @@
     if (has_error())
       return nullptr;
     if (constructor == nullptr) {
-      set_error(peek(), "missing constructor for variable declaration");
+      add_error(peek(), "missing constructor for variable declaration");
       return nullptr;
     }
     var->set_constructor(std::move(constructor));
@@ -2327,7 +2338,7 @@
   if (has_error())
     return nullptr;
   if (condition == nullptr) {
-    set_error(peek(), "unable to parse if condition");
+    add_error(peek(), "unable to parse if condition");
     return nullptr;
   }
 
@@ -2369,7 +2380,7 @@
     if (has_error())
       return {};
     if (condition == nullptr) {
-      set_error(peek(), "unable to parse condition expression");
+      add_error(peek(), "unable to parse condition expression");
       return {};
     }
 
@@ -2419,13 +2430,13 @@
   if (has_error())
     return nullptr;
   if (condition == nullptr) {
-    set_error(peek(), "unable to parse switch expression");
+    add_error(peek(), "unable to parse switch expression");
     return nullptr;
   }
 
   t = next();
   if (!t.IsBraceLeft()) {
-    set_error(t, "missing { for switch statement");
+    add_error(t, "missing { for switch statement");
     return nullptr;
   }
 
@@ -2442,7 +2453,7 @@
 
   t = next();
   if (!t.IsBraceRight()) {
-    set_error(t, "missing } for switch statement");
+    add_error(t, "missing } for switch statement");
     return nullptr;
   }
   return std::make_unique<ast::SwitchStatement>(source, std::move(condition),
@@ -2467,7 +2478,7 @@
     if (has_error())
       return nullptr;
     if (selectors.empty()) {
-      set_error(peek(), "unable to parse case selectors");
+      add_error(peek(), "unable to parse case selectors");
       return nullptr;
     }
     stmt->set_selectors(std::move(selectors));
@@ -2475,13 +2486,13 @@
 
   t = next();
   if (!t.IsColon()) {
-    set_error(t, "missing : for case statement");
+    add_error(t, "missing : for case statement");
     return nullptr;
   }
 
   t = next();
   if (!t.IsBraceLeft()) {
-    set_error(t, "missing { for case statement");
+    add_error(t, "missing { for case statement");
     return nullptr;
   }
 
@@ -2493,7 +2504,7 @@
 
   t = next();
   if (!t.IsBraceRight()) {
-    set_error(t, "missing } for case statement");
+    add_error(t, "missing } for case statement");
     return nullptr;
   }
 
@@ -2513,7 +2524,7 @@
     if (cond == nullptr)
       break;
     if (!cond->IsInt()) {
-      set_error(t, "invalid case selector must be an integer value");
+      add_error(t, "invalid case selector must be an integer value");
       return {};
     }
 
@@ -2538,7 +2549,7 @@
 
       t = next();
       if (!t.IsSemicolon()) {
-        set_error(t, "missing ;");
+        add_error(t, "missing ;");
         return {};
       }
 
@@ -2570,7 +2581,7 @@
 
   t = next();
   if (!t.IsBraceLeft()) {
-    set_error(t, "missing { for loop");
+    add_error(t, "missing { for loop");
     return nullptr;
   }
 
@@ -2584,7 +2595,7 @@
 
   t = next();
   if (!t.IsBraceRight()) {
-    set_error(t, "missing } for loop");
+    add_error(t, "missing } for loop");
     return nullptr;
   }
 
@@ -2629,7 +2640,7 @@
 
   auto t = next();
   if (!t.IsSemicolon()) {
-    set_error(t, "missing ';' after initializer in for loop");
+    add_error(t, "missing ';' after initializer in for loop");
     return nullptr;
   }
 
@@ -2640,7 +2651,7 @@
 
   t = next();
   if (!t.IsSemicolon()) {
-    set_error(t, "missing ';' after condition in for loop");
+    add_error(t, "missing ';' after condition in for loop");
     return nullptr;
   }
 
@@ -2674,7 +2685,7 @@
 
   t = next();
   if (!t.IsParenLeft()) {
-    set_error(t, "missing for loop (");
+    add_error(t, "missing for loop (");
     return nullptr;
   }
 
@@ -2684,13 +2695,13 @@
 
   t = next();
   if (!t.IsParenRight()) {
-    set_error(t, "missing for loop )");
+    add_error(t, "missing for loop )");
     return nullptr;
   }
 
   t = next();
   if (!t.IsBraceLeft()) {
-    set_error(t, "missing for loop {");
+    add_error(t, "missing for loop {");
     return nullptr;
   }
 
@@ -2700,7 +2711,7 @@
 
   t = next();
   if (!t.IsBraceRight()) {
-    set_error(t, "missing for loop }");
+    add_error(t, "missing for loop }");
     return nullptr;
   }
 
@@ -2771,7 +2782,7 @@
 
   t = next();
   if (!t.IsParenRight()) {
-    set_error(t, "missing ) for call statement");
+    add_error(t, "missing ) for call statement");
     return nullptr;
   }
 
@@ -2849,7 +2860,7 @@
 
     t = next();
     if (!t.IsLessThan()) {
-      set_error(t, "missing < for bitcast expression");
+      add_error(t, "missing < for bitcast expression");
       return nullptr;
     }
 
@@ -2857,13 +2868,13 @@
     if (has_error())
       return nullptr;
     if (type == nullptr) {
-      set_error(peek(), "missing type for bitcast expression");
+      add_error(peek(), "missing type for bitcast expression");
       return nullptr;
     }
 
     t = next();
     if (!t.IsGreaterThan()) {
-      set_error(t, "missing > for bitcast expression");
+      add_error(t, "missing > for bitcast expression");
       return nullptr;
     }
 
@@ -2871,7 +2882,7 @@
     if (has_error())
       return nullptr;
     if (params == nullptr) {
-      set_error(peek(), "unable to parse parameters");
+      add_error(peek(), "unable to parse parameters");
       return nullptr;
     }
 
@@ -2889,7 +2900,7 @@
   if (type != nullptr) {
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for type constructor");
+      add_error(t, "missing ( for type constructor");
       return nullptr;
     }
 
@@ -2903,7 +2914,7 @@
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for type constructor");
+      add_error(t, "missing ) for type constructor");
       return nullptr;
     }
     return std::make_unique<ast::TypeConstructorExpression>(source, type,
@@ -2930,13 +2941,13 @@
     if (has_error())
       return nullptr;
     if (param == nullptr) {
-      set_error(peek(), "unable to parse expression inside []");
+      add_error(peek(), "unable to parse expression inside []");
       return nullptr;
     }
 
     t = next();
     if (!t.IsBracketRight()) {
-      set_error(t, "missing ] for array accessor");
+      add_error(t, "missing ] for array accessor");
       return nullptr;
     }
     expr = std::make_unique<ast::ArrayAccessorExpression>(
@@ -2955,7 +2966,7 @@
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for call expression");
+      add_error(t, "missing ) for call expression");
       return nullptr;
     }
     expr = std::make_unique<ast::CallExpression>(source, std::move(prefix),
@@ -2965,7 +2976,7 @@
 
     t = next();
     if (!t.IsIdentifier()) {
-      set_error(t, "missing identifier for member accessor");
+      add_error(t, "missing identifier for member accessor");
       return nullptr;
     }
 
@@ -2997,7 +3008,7 @@
   if (has_error())
     return {};
   if (arg == nullptr) {
-    set_error(peek(), "unable to parse argument expression");
+    add_error(peek(), "unable to parse argument expression");
     return {};
   }
 
@@ -3015,7 +3026,7 @@
     if (has_error())
       return {};
     if (arg == nullptr) {
-      set_error(peek(), "unable to parse argument expression after comma");
+      add_error(peek(), "unable to parse argument expression after comma");
       return {};
     }
     ret.push_back(std::move(arg));
@@ -3043,7 +3054,7 @@
     if (has_error())
       return nullptr;
     if (expr == nullptr) {
-      set_error(peek(),
+      add_error(peek(),
                 "unable to parse right side of " + name + " expression");
       return nullptr;
     }
@@ -3080,7 +3091,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of " + name + " expression");
+    add_error(peek(), "unable to parse right side of " + name + " expression");
     return nullptr;
   }
   return multiplicative_expr(std::make_unique<ast::BinaryExpression>(
@@ -3122,7 +3133,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of + expression");
+    add_error(peek(), "unable to parse right side of + expression");
     return nullptr;
   }
   return additive_expr(std::make_unique<ast::BinaryExpression>(
@@ -3171,7 +3182,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), std::string("unable to parse right side of ") + name +
+    add_error(peek(), std::string("unable to parse right side of ") + name +
                           " expression");
     return nullptr;
   }
@@ -3220,7 +3231,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of " + name + " expression");
+    add_error(peek(), "unable to parse right side of " + name + " expression");
     return nullptr;
   }
   return relational_expr(std::make_unique<ast::BinaryExpression>(
@@ -3262,7 +3273,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of " + name + " expression");
+    add_error(peek(), "unable to parse right side of " + name + " expression");
     return nullptr;
   }
   return equality_expr(std::make_unique<ast::BinaryExpression>(
@@ -3297,7 +3308,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of & expression");
+    add_error(peek(), "unable to parse right side of & expression");
     return nullptr;
   }
   return and_expr(std::make_unique<ast::BinaryExpression>(
@@ -3332,7 +3343,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of ^ expression");
+    add_error(peek(), "unable to parse right side of ^ expression");
     return nullptr;
   }
   return exclusive_or_expr(std::make_unique<ast::BinaryExpression>(
@@ -3367,7 +3378,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of | expression");
+    add_error(peek(), "unable to parse right side of | expression");
     return nullptr;
   }
   return inclusive_or_expr(std::make_unique<ast::BinaryExpression>(
@@ -3402,7 +3413,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of && expression");
+    add_error(peek(), "unable to parse right side of && expression");
     return nullptr;
   }
   return logical_and_expr(std::make_unique<ast::BinaryExpression>(
@@ -3437,7 +3448,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of || expression");
+    add_error(peek(), "unable to parse right side of || expression");
     return nullptr;
   }
   return logical_or_expr(std::make_unique<ast::BinaryExpression>(
@@ -3470,7 +3481,7 @@
 
   t = next();
   if (!t.IsEqual()) {
-    set_error(t, "missing = for assignment");
+    add_error(t, "missing = for assignment");
     return nullptr;
   }
 
@@ -3478,7 +3489,7 @@
   if (has_error())
     return nullptr;
   if (rhs == nullptr) {
-    set_error(peek(), "unable to parse right side of assignment");
+    add_error(peek(), "unable to parse right side of assignment");
     return nullptr;
   }
 
@@ -3550,7 +3561,7 @@
   auto t = peek();
 
   if (depth > kMaxConstExprDepth) {
-    set_error(t, "max const_expr depth reached");
+    add_error(t, "max const_expr depth reached");
     return nullptr;
   }
 
@@ -3560,7 +3571,7 @@
   if (type != nullptr) {
     t = next();
     if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for type constructor");
+      add_error(t, "missing ( for type constructor");
       return nullptr;
     }
 
@@ -3569,7 +3580,7 @@
     if (has_error())
       return nullptr;
     if (param == nullptr) {
-      set_error(peek(), "unable to parse constant expression");
+      add_error(peek(), "unable to parse constant expression");
       return nullptr;
     }
     params.push_back(std::move(param));
@@ -3584,7 +3595,7 @@
       if (has_error())
         return nullptr;
       if (param == nullptr) {
-        set_error(peek(), "unable to parse constant expression");
+        add_error(peek(), "unable to parse constant expression");
         return nullptr;
       }
       params.push_back(std::move(param));
@@ -3592,7 +3603,7 @@
 
     t = next();
     if (!t.IsParenRight()) {
-      set_error(t, "missing ) for type constructor");
+      add_error(t, "missing ) for type constructor");
       return nullptr;
     }
     return std::make_unique<ast::TypeConstructorExpression>(source, type,
@@ -3603,7 +3614,7 @@
   if (has_error())
     return nullptr;
   if (lit == nullptr) {
-    set_error(peek(), "unable to parse const literal");
+    add_error(peek(), "unable to parse const literal");
     return nullptr;
   }
   return std::make_unique<ast::ScalarConstructorExpression>(source,
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 4133f4b..eb0a77f 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -125,13 +125,22 @@
   /// @param idx the index of the token to return
   /// @returns the token |idx| positions ahead without advancing
   Token peek(size_t idx);
-  /// Sets the error from |t|
-  /// @param t the token to set the error from
-  void set_error(const Token& t);
-  /// Sets the error from |t| or |msg| if |t| is not in error
-  /// @param t the token to set the error from
+  /// Appends an error at |t| with the message |msg|
+  /// @param t the token to associate the error with
   /// @param msg the error message
-  void set_error(const Token& t, const std::string& msg);
+  void add_error(const Token& t, const std::string& msg);
+  /// Appends an error raised when parsing |use| at |t| with the message |msg|
+  /// @param t the token to associate the error with
+  /// @param msg the error message
+  /// @param use a description of what was being parsed when the error was
+  /// raised.
+  void add_error(const Token& t,
+                 const std::string& msg,
+                 const std::string& use);
+  /// Appends an error at |source| with the message |msg|
+  /// @param source the source to associate the error with
+  /// @param msg the error message
+  void add_error(const Source& source, const std::string& msg);
 
   /// Registers a constructed type into the parser
   /// @param name the constructed name
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index 90d367f..3f403a4 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -289,7 +289,7 @@
 TEST_F(ParserImplErrorTest, FunctionDeclDecoStageTypeInvalid) {
   // TODO(bclayton) - BUG(https://crbug.com/tint/291)
   EXPECT("[[shader(vertex)]] fn main() -> void {}",
-         "test.wgsl:1:1 error: invalid token ([[) encountered\n"
+         "test.wgsl:1:1 error: invalid token\n"
          "[[shader(vertex)]] fn main() -> void {}\n"
          "^^\n");
 }
@@ -1178,7 +1178,7 @@
 
 TEST_F(ParserImplErrorTest, UnexpectedToken) {
   EXPECT("unexpected",
-         "test.wgsl:1:1 error: invalid token (kIdentifier) encountered\n"
+         "test.wgsl:1:1 error: invalid token\n"
          "unexpected\n"
          "^^^^^^^^^^\n");
 }