Commit 8fee41b1d5

Andrew Kelley <andrew@ziglang.org>
2021-02-20 02:04:52
stage2: AST: clean up parse errors
* struct instead of tagged union * delete dead code * simplify parser code * remove unnecessary metaprogramming
1 parent 7487856
Changed files (5)
lib/std/zig/ast.zig
@@ -132,113 +132,160 @@ pub const Tree = struct {
     }
 
     pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
-        const tokens = tree.tokens.items(.tag);
-        switch (parse_error) {
-            .InvalidToken => |*x| return x.render(tokens, stream),
-            .ExpectedContainerMembers => |*x| return x.render(tokens, stream),
-            .ExpectedStringLiteral => |*x| return x.render(tokens, stream),
-            .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream),
-            .ExpectedPubItem => |*x| return x.render(tokens, stream),
-            .ExpectedIdentifier => |*x| return x.render(tokens, stream),
-            .ExpectedStatement => |*x| return x.render(tokens, stream),
-            .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
-            .ExpectedVarDecl => |*x| return x.render(tokens, stream),
-            .ExpectedFn => |*x| return x.render(tokens, stream),
-            .ExpectedReturnType => |*x| return x.render(tokens, stream),
-            .ExpectedAggregateKw => |*x| return x.render(tokens, stream),
-            .SameLineDocComment => |*x| return x.render(tokens, stream),
-            .UnattachedDocComment => |*x| return x.render(tokens, stream),
-            .ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
-            .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
-            .ExpectedSemiOrElse => |*x| return x.render(tokens, stream),
-            .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream),
-            .ExpectedLBrace => |*x| return x.render(tokens, stream),
-            .ExpectedColonOrRParen => |*x| return x.render(tokens, stream),
-            .ExpectedLabelable => |*x| return x.render(tokens, stream),
-            .ExpectedInlinable => |*x| return x.render(tokens, stream),
-            .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
-            .ExpectedCall => |x| return x.render(tree, stream),
-            .ExpectedCallOrFnProto => |x| return x.render(tree, stream),
-            .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),
-            .ExtraAlignQualifier => |*x| return x.render(tokens, stream),
-            .ExtraConstQualifier => |*x| return x.render(tokens, stream),
-            .ExtraVolatileQualifier => |*x| return x.render(tokens, stream),
-            .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream),
-            .ExpectedTypeExpr => |*x| return x.render(tokens, stream),
-            .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream),
-            .ExpectedParamType => |*x| return x.render(tokens, stream),
-            .ExpectedExpr => |*x| return x.render(tokens, stream),
-            .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),
-            .ExpectedToken => |*x| return x.render(tokens, stream),
-            .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),
-            .ExpectedParamList => |*x| return x.render(tokens, stream),
-            .ExpectedPayload => |*x| return x.render(tokens, stream),
-            .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream),
-            .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream),
-            .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream),
-            .ExpectedPrefixExpr => |*x| return x.render(tokens, stream),
-            .ExpectedLoopExpr => |*x| return x.render(tokens, stream),
-            .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream),
-            .ExpectedSuffixOp => |*x| return x.render(tokens, stream),
-            .ExpectedBlockOrField => |*x| return x.render(tokens, stream),
-            .DeclBetweenFields => |*x| return x.render(tokens, stream),
-            .InvalidAnd => |*x| return x.render(tokens, stream),
-            .AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream),
-        }
-    }
+        const token_tags = tree.tokens.items(.tag);
+        switch (parse_error.tag) {
+            .asterisk_after_ptr_deref => {
+                return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?");
+            },
+            .decl_between_fields => {
+                return stream.writeAll("declarations are not allowed between container fields");
+            },
+            .expected_block => {
+                return stream.print("expected block or field, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_block_or_assignment => {
+                return stream.print("expected block or assignment, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_block_or_expr => {
+                return stream.print("expected block or expression, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_block_or_field => {
+                return stream.print("expected block or field, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_container_members => {
+                return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_expr => {
+                return stream.print("expected expression, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_expr_or_assignment => {
+                return stream.print("expected expression or assignment, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_fn => {
+                return stream.print("expected function, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_inlinable => {
+                return stream.print("expected 'while' or 'for', found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_labelable => {
+                return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_param_list => {
+                return stream.print("expected parameter list, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_prefix_expr => {
+                return stream.print("expected prefix expression, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_primary_type_expr => {
+                return stream.print("expected primary type expression, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_return_type => {
+                return stream.print("expected return type expression, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_semi_or_else => {
+                return stream.print("expected ';' or 'else', found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_semi_or_lbrace => {
+                return stream.print("expected ';' or '{{', found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_statement => {
+                return stream.print("expected statement, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_string_literal => {
+                return stream.print("expected string literal, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_suffix_op => {
+                return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_type_expr => {
+                return stream.print("expected type expression, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_var_decl => {
+                return stream.print("expected variable declaration, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .expected_var_decl_or_fn => {
+                return stream.print("expected variable declaration or function, found '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .extra_align_qualifier => {
+                return stream.writeAll("extra align qualifier");
+            },
+            .extra_allowzero_qualifier => {
+                return stream.writeAll("extra allowzero qualifier");
+            },
+            .extra_const_qualifier => {
+                return stream.writeAll("extra const qualifier");
+            },
+            .extra_volatile_qualifier => {
+                return stream.writeAll("extra volatile qualifier");
+            },
+            .invalid_token => {
+                return stream.print("invalid token '{s}'", .{
+                    token_tags[parse_error.token].symbol(),
+                });
+            },
+            .same_line_doc_comment => {
+                return stream.writeAll("same line documentation comment");
+            },
+            .unattached_doc_comment => {
+                return stream.writeAll("unattached documentation comment");
+            },
 
-    pub fn errorToken(tree: Tree, parse_error: Error) TokenIndex {
-        switch (parse_error) {
-            .InvalidToken => |x| return x.token,
-            .ExpectedContainerMembers => |x| return x.token,
-            .ExpectedStringLiteral => |x| return x.token,
-            .ExpectedIntegerLiteral => |x| return x.token,
-            .ExpectedPubItem => |x| return x.token,
-            .ExpectedIdentifier => |x| return x.token,
-            .ExpectedStatement => |x| return x.token,
-            .ExpectedVarDeclOrFn => |x| return x.token,
-            .ExpectedVarDecl => |x| return x.token,
-            .ExpectedFn => |x| return x.token,
-            .ExpectedReturnType => |x| return x.token,
-            .ExpectedAggregateKw => |x| return x.token,
-            .SameLineDocComment => |x| return x.token,
-            .UnattachedDocComment => |x| return x.token,
-            .ExpectedEqOrSemi => |x| return x.token,
-            .ExpectedSemiOrLBrace => |x| return x.token,
-            .ExpectedSemiOrElse => |x| return x.token,
-            .ExpectedLabelOrLBrace => |x| return x.token,
-            .ExpectedLBrace => |x| return x.token,
-            .ExpectedColonOrRParen => |x| return x.token,
-            .ExpectedLabelable => |x| return x.token,
-            .ExpectedInlinable => |x| return x.token,
-            .ExpectedAsmOutputReturnOrType => |x| return x.token,
-            .ExpectedCall => |x| return tree.nodes.items(.main_token)[x.node],
-            .ExpectedCallOrFnProto => |x| return tree.nodes.items(.main_token)[x.node],
-            .ExpectedSliceOrRBracket => |x| return x.token,
-            .ExtraAlignQualifier => |x| return x.token,
-            .ExtraConstQualifier => |x| return x.token,
-            .ExtraVolatileQualifier => |x| return x.token,
-            .ExtraAllowZeroQualifier => |x| return x.token,
-            .ExpectedTypeExpr => |x| return x.token,
-            .ExpectedPrimaryTypeExpr => |x| return x.token,
-            .ExpectedParamType => |x| return x.token,
-            .ExpectedExpr => |x| return x.token,
-            .ExpectedPrimaryExpr => |x| return x.token,
-            .ExpectedToken => |x| return x.token,
-            .ExpectedCommaOrEnd => |x| return x.token,
-            .ExpectedParamList => |x| return x.token,
-            .ExpectedPayload => |x| return x.token,
-            .ExpectedBlockOrAssignment => |x| return x.token,
-            .ExpectedBlockOrExpression => |x| return x.token,
-            .ExpectedExprOrAssignment => |x| return x.token,
-            .ExpectedPrefixExpr => |x| return x.token,
-            .ExpectedLoopExpr => |x| return x.token,
-            .ExpectedDerefOrUnwrap => |x| return x.token,
-            .ExpectedSuffixOp => |x| return x.token,
-            .ExpectedBlockOrField => |x| return x.token,
-            .DeclBetweenFields => |x| return x.token,
-            .InvalidAnd => |x| return x.token,
-            .AsteriskAfterPointerDereference => |x| return x.token,
+            .expected_token => {
+                const found_tag = token_tags[parse_error.token];
+                const expected_symbol = parse_error.extra.expected_tag.symbol();
+                switch (found_tag) {
+                    .invalid => return stream.print("expected '{s}', found invalid bytes", .{
+                        expected_symbol,
+                    }),
+                    else => return stream.print("expected '{s}', found '{s}'", .{
+                        expected_symbol, found_tag.symbol(),
+                    }),
+                }
+            },
         }
     }
 
@@ -2239,236 +2286,50 @@ pub const full = struct {
     };
 };
 
-pub const Error = union(enum) {
-    InvalidToken: InvalidToken,
-    ExpectedContainerMembers: ExpectedContainerMembers,
-    ExpectedStringLiteral: ExpectedStringLiteral,
-    ExpectedIntegerLiteral: ExpectedIntegerLiteral,
-    ExpectedPubItem: ExpectedPubItem,
-    ExpectedIdentifier: ExpectedIdentifier,
-    ExpectedStatement: ExpectedStatement,
-    ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
-    ExpectedVarDecl: ExpectedVarDecl,
-    ExpectedFn: ExpectedFn,
-    ExpectedReturnType: ExpectedReturnType,
-    ExpectedAggregateKw: ExpectedAggregateKw,
-    SameLineDocComment: SameLineDocComment,
-    UnattachedDocComment: UnattachedDocComment,
-    ExpectedEqOrSemi: ExpectedEqOrSemi,
-    ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
-    ExpectedSemiOrElse: ExpectedSemiOrElse,
-    ExpectedLabelOrLBrace: ExpectedLabelOrLBrace,
-    ExpectedLBrace: ExpectedLBrace,
-    ExpectedColonOrRParen: ExpectedColonOrRParen,
-    ExpectedLabelable: ExpectedLabelable,
-    ExpectedInlinable: ExpectedInlinable,
-    ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
-    ExpectedCall: ExpectedCall,
-    ExpectedCallOrFnProto: ExpectedCallOrFnProto,
-    ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,
-    ExtraAlignQualifier: ExtraAlignQualifier,
-    ExtraConstQualifier: ExtraConstQualifier,
-    ExtraVolatileQualifier: ExtraVolatileQualifier,
-    ExtraAllowZeroQualifier: ExtraAllowZeroQualifier,
-    ExpectedTypeExpr: ExpectedTypeExpr,
-    ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr,
-    ExpectedParamType: ExpectedParamType,
-    ExpectedExpr: ExpectedExpr,
-    ExpectedPrimaryExpr: ExpectedPrimaryExpr,
-    ExpectedToken: ExpectedToken,
-    ExpectedCommaOrEnd: ExpectedCommaOrEnd,
-    ExpectedParamList: ExpectedParamList,
-    ExpectedPayload: ExpectedPayload,
-    ExpectedBlockOrAssignment: ExpectedBlockOrAssignment,
-    ExpectedBlockOrExpression: ExpectedBlockOrExpression,
-    ExpectedExprOrAssignment: ExpectedExprOrAssignment,
-    ExpectedPrefixExpr: ExpectedPrefixExpr,
-    ExpectedLoopExpr: ExpectedLoopExpr,
-    ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap,
-    ExpectedSuffixOp: ExpectedSuffixOp,
-    ExpectedBlockOrField: ExpectedBlockOrField,
-    DeclBetweenFields: DeclBetweenFields,
-    InvalidAnd: InvalidAnd,
-    AsteriskAfterPointerDereference: AsteriskAfterPointerDereference,
-
-    pub const InvalidToken = SingleTokenError("Invalid token '{s}'");
-    pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'");
-    pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'");
-    pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'");
-    pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'");
-    pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'");
-    pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'");
-    pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'");
-    pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'");
-    pub const ExpectedReturnType = SingleTokenError("Expected return type expression, found '{s}'");
-    pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Tag.keyword_struct.symbol() ++ "', '" ++ Token.Tag.keyword_union.symbol() ++ "', '" ++ Token.Tag.keyword_enum.symbol() ++ "', or '" ++ Token.Tag.keyword_opaque.symbol() ++ "', found '{s}'");
-    pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'");
-    pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'");
-    pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'");
-    pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'");
-    pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'");
-    pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'");
-    pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'");
-    pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'");
-    pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Tag.identifier.symbol() ++ "', found '{s}'");
-    pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'");
-    pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'");
-    pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'");
-    pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'");
-    pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'");
-    pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'");
-    pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'");
-    pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'");
-    pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'");
-    pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'");
-    pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'");
-    pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'");
-    pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'");
-    pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'");
-    pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'");
-
-    pub const ExpectedParamType = SimpleError("Expected parameter type");
-    pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub");
-    pub const SameLineDocComment = SimpleError("Same line documentation comment");
-    pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
-    pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
-    pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
-    pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier");
-    pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier");
-    pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields");
-    pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND.");
-    pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?");
-
-    pub const ExpectedCall = struct {
-        node: Node.Index,
-
-        pub fn render(self: ExpectedCall, tree: Tree, stream: anytype) !void {
-            const node_tag = tree.nodes.items(.tag)[self.node];
-            return stream.print("expected " ++ @tagName(Node.Tag.call) ++ ", found {s}", .{
-                @tagName(node_tag),
-            });
-        }
-    };
-
-    pub const ExpectedCallOrFnProto = struct {
-        node: Node.Index,
-
-        pub fn render(self: ExpectedCallOrFnProto, tree: Tree, stream: anytype) !void {
-            const node_tag = tree.nodes.items(.tag)[self.node];
-            return stream.print("expected " ++ @tagName(Node.Tag.call) ++ " or " ++
-                @tagName(Node.Tag.fn_proto) ++ ", found {s}", .{@tagName(node_tag)});
-        }
-    };
-
-    pub const ExpectedToken = struct {
-        token: TokenIndex,
-        expected_id: Token.Tag,
-
-        pub fn render(self: *const ExpectedToken, tokens: []const Token.Tag, stream: anytype) !void {
-            const found_token = tokens[self.token];
-            switch (found_token) {
-                .invalid => {
-                    return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()});
-                },
-                else => {
-                    const token_name = found_token.symbol();
-                    return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name });
-                },
-            }
-        }
-    };
-
-    pub const ExpectedCommaOrEnd = struct {
-        token: TokenIndex,
-        end_id: Token.Tag,
+pub const Error = struct {
+    tag: Tag,
+    token: TokenIndex,
+    extra: union {
+        none: void,
+        expected_tag: Token.Tag,
+    } = .{ .none = {} },
 
-        pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Tag, stream: anytype) !void {
-            const actual_token = tokens[self.token];
-            return stream.print("expected ',' or '{s}', found '{s}'", .{
-                self.end_id.symbol(),
-                actual_token.symbol(),
-            });
-        }
+    pub const Tag = enum {
+        asterisk_after_ptr_deref,
+        decl_between_fields,
+        expected_block,
+        expected_block_or_assignment,
+        expected_block_or_expr,
+        expected_block_or_field,
+        expected_container_members,
+        expected_expr,
+        expected_expr_or_assignment,
+        expected_fn,
+        expected_inlinable,
+        expected_labelable,
+        expected_param_list,
+        expected_prefix_expr,
+        expected_primary_type_expr,
+        expected_return_type,
+        expected_semi_or_else,
+        expected_semi_or_lbrace,
+        expected_statement,
+        expected_string_literal,
+        expected_suffix_op,
+        expected_type_expr,
+        expected_var_decl,
+        expected_var_decl_or_fn,
+        extra_align_qualifier,
+        extra_allowzero_qualifier,
+        extra_const_qualifier,
+        extra_volatile_qualifier,
+        invalid_token,
+        same_line_doc_comment,
+        unattached_doc_comment,
+
+        /// `expected_tag` is populated.
+        expected_token,
     };
-
-    fn SingleTokenError(comptime msg: []const u8) type {
-        return struct {
-            const ThisError = @This();
-
-            token: TokenIndex,
-
-            pub fn render(self: *const ThisError, tokens: []const Token.Tag, stream: anytype) !void {
-                const actual_token = tokens[self.token];
-                return stream.print(msg, .{actual_token.symbol()});
-            }
-        };
-    }
-
-    fn SimpleError(comptime msg: []const u8) type {
-        return struct {
-            const ThisError = @This();
-
-            token: TokenIndex,
-
-            pub fn render(self: *const ThisError, tokens: []const Token.Tag, stream: anytype) !void {
-                return stream.writeAll(msg);
-            }
-        };
-    }
-
-    pub fn loc(self: Error) TokenIndex {
-        switch (self) {
-            .InvalidToken => |x| return x.token,
-            .ExpectedContainerMembers => |x| return x.token,
-            .ExpectedStringLiteral => |x| return x.token,
-            .ExpectedIntegerLiteral => |x| return x.token,
-            .ExpectedPubItem => |x| return x.token,
-            .ExpectedIdentifier => |x| return x.token,
-            .ExpectedStatement => |x| return x.token,
-            .ExpectedVarDeclOrFn => |x| return x.token,
-            .ExpectedVarDecl => |x| return x.token,
-            .ExpectedFn => |x| return x.token,
-            .ExpectedReturnType => |x| return x.token,
-            .ExpectedAggregateKw => |x| return x.token,
-            .UnattachedDocComment => |x| return x.token,
-            .ExpectedEqOrSemi => |x| return x.token,
-            .ExpectedSemiOrLBrace => |x| return x.token,
-            .ExpectedSemiOrElse => |x| return x.token,
-            .ExpectedLabelOrLBrace => |x| return x.token,
-            .ExpectedLBrace => |x| return x.token,
-            .ExpectedColonOrRParen => |x| return x.token,
-            .ExpectedLabelable => |x| return x.token,
-            .ExpectedInlinable => |x| return x.token,
-            .ExpectedAsmOutputReturnOrType => |x| return x.token,
-            .ExpectedCall => |x| @panic("TODO redo ast errors"),
-            .ExpectedCallOrFnProto => |x| @panic("TODO redo ast errors"),
-            .ExpectedSliceOrRBracket => |x| return x.token,
-            .ExtraAlignQualifier => |x| return x.token,
-            .ExtraConstQualifier => |x| return x.token,
-            .ExtraVolatileQualifier => |x| return x.token,
-            .ExtraAllowZeroQualifier => |x| return x.token,
-            .ExpectedTypeExpr => |x| return x.token,
-            .ExpectedPrimaryTypeExpr => |x| return x.token,
-            .ExpectedParamType => |x| return x.token,
-            .ExpectedExpr => |x| return x.token,
-            .ExpectedPrimaryExpr => |x| return x.token,
-            .ExpectedToken => |x| return x.token,
-            .ExpectedCommaOrEnd => |x| return x.token,
-            .ExpectedParamList => |x| return x.token,
-            .ExpectedPayload => |x| return x.token,
-            .ExpectedBlockOrAssignment => |x| return x.token,
-            .ExpectedBlockOrExpression => |x| return x.token,
-            .ExpectedExprOrAssignment => |x| return x.token,
-            .ExpectedPrefixExpr => |x| return x.token,
-            .ExpectedLoopExpr => |x| return x.token,
-            .ExpectedDerefOrUnwrap => |x| return x.token,
-            .ExpectedSuffixOp => |x| return x.token,
-            .ExpectedBlockOrField => |x| return x.token,
-            .DeclBetweenFields => |x| return x.token,
-            .InvalidAnd => |x| return x.token,
-            .AsteriskAfterPointerDereference => |x| return x.token,
-        }
-    }
 };
 
 pub const Node = struct {
lib/std/zig/parse.zig
@@ -150,14 +150,41 @@ const Parser = struct {
         return result;
     }
 
-    fn warn(p: *Parser, msg: ast.Error) error{OutOfMemory}!void {
+    fn warn(p: *Parser, tag: ast.Error.Tag) error{OutOfMemory}!void {
+        @setCold(true);
+        try p.warnMsg(.{ .tag = tag, .token = p.tok_i });
+    }
+
+    fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void {
+        @setCold(true);
+        try p.warnMsg(.{
+            .tag = .expected_token,
+            .token = p.tok_i,
+            .extra = .{ .expected_tag = expected_token },
+        });
+    }
+    fn warnMsg(p: *Parser, msg: ast.Error) error{OutOfMemory}!void {
         @setCold(true);
         try p.errors.append(p.gpa, msg);
     }
 
-    fn fail(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } {
+    fn fail(p: *Parser, tag: ast.Error.Tag) error{ ParseError, OutOfMemory } {
+        @setCold(true);
+        return p.failMsg(.{ .tag = tag, .token = p.tok_i });
+    }
+
+    fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } {
         @setCold(true);
-        try p.warn(msg);
+        return p.failMsg(.{
+            .tag = .expected_token,
+            .token = p.tok_i,
+            .extra = .{ .expected_tag = expected_token },
+        });
+    }
+
+    fn failMsg(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } {
+        @setCold(true);
+        try p.warnMsg(msg);
         return error.ParseError;
     }
 
@@ -190,7 +217,7 @@ const Parser = struct {
 
         var trailing_comma = false;
         while (true) {
-            const doc_comment = try p.eatDocComments ();
+            const doc_comment = try p.eatDocComments();
 
             switch (p.token_tags[p.tok_i]) {
                 .keyword_test => {
@@ -212,8 +239,9 @@ const Parser = struct {
                                 .none => field_state = .seen,
                                 .err, .seen => {},
                                 .end => |node| {
-                                    try p.warn(.{
-                                        .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] },
+                                    try p.warnMsg(.{
+                                        .tag = .decl_between_fields,
+                                        .token = p.nodes.items(.main_token)[node],
                                     });
                                     // Continue parsing; error will be reported later.
                                     field_state = .err;
@@ -234,9 +262,7 @@ const Parser = struct {
                             }
                             // There is not allowed to be a decl after a field with no comma.
                             // Report error but recover parser.
-                            try p.warn(.{
-                                .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                            });
+                            try p.warnExpected(.comma);
                             p.findNextContainerMember();
                         }
                     },
@@ -267,7 +293,7 @@ const Parser = struct {
                     },
                     else => {
                         p.tok_i += 1;
-                        try p.warn(.{ .ExpectedBlockOrField = .{ .token = p.tok_i } });
+                        try p.warn(.expected_block_or_field);
                     },
                 },
                 .keyword_pub => {
@@ -316,8 +342,9 @@ const Parser = struct {
                             .none => field_state = .seen,
                             .err, .seen => {},
                             .end => |node| {
-                                try p.warn(.{
-                                    .DeclBetweenFields = .{ .token = p.nodes.items(.main_token)[node] },
+                                try p.warnMsg(.{
+                                    .tag = .decl_between_fields,
+                                    .token = p.nodes.items(.main_token)[node],
                                 });
                                 // Continue parsing; error will be reported later.
                                 field_state = .err;
@@ -338,20 +365,21 @@ const Parser = struct {
                         }
                         // There is not allowed to be a decl after a field with no comma.
                         // Report error but recover parser.
-                        try p.warn(.{
-                            .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                        });
+                        try p.warnExpected(.comma);
                         p.findNextContainerMember();
                     }
                 },
                 .eof, .r_brace => {
                     if (doc_comment) |tok| {
-                        try p.warn(.{ .UnattachedDocComment = .{ .token = tok } });
+                        try p.warnMsg(.{
+                            .tag = .unattached_doc_comment,
+                            .token = tok,
+                        });
                     }
                     break;
                 },
                 else => {
-                    try p.warn(.{ .ExpectedContainerMembers = .{ .token = p.tok_i } });
+                    try p.warn(.expected_container_members);
                     // This was likely not supposed to end yet; try to find the next declaration.
                     p.findNextContainerMember();
                 },
@@ -475,7 +503,7 @@ const Parser = struct {
         const test_token = p.assertToken(.keyword_test);
         const name_token = p.eatToken(.string_literal);
         const block_node = try p.parseBlock();
-        if (block_node == 0) return p.fail(.{ .ExpectedLBrace = .{ .token = p.tok_i } });
+        if (block_node == 0) return p.fail(.expected_block);
         return p.addNode(.{
             .tag = .test_decl,
             .main_token = test_token,
@@ -540,15 +568,13 @@ const Parser = struct {
                     // Since parseBlock only return error.ParseError on
                     // a missing '}' we can assume this function was
                     // supposed to end here.
-                    try p.warn(.{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } });
+                    try p.warn(.expected_semi_or_lbrace);
                     return null_node;
                 },
             }
         }
         if (expect_fn) {
-            try p.warn(.{
-                .ExpectedFn = .{ .token = p.tok_i },
-            });
+            try p.warn(.expected_fn);
             return error.ParseError;
         }
 
@@ -559,11 +585,11 @@ const Parser = struct {
             return var_decl;
         }
         if (thread_local_token != null) {
-            return p.fail(.{ .ExpectedVarDecl = .{ .token = p.tok_i } });
+            return p.fail(.expected_var_decl);
         }
 
         if (exported) {
-            return p.fail(.{ .ExpectedVarDeclOrFn = .{ .token = p.tok_i } });
+            return p.fail(.expected_var_decl_or_fn);
         }
 
         return p.expectUsingNamespace();
@@ -618,7 +644,7 @@ const Parser = struct {
         if (return_type_expr == 0) {
             // most likely the user forgot to specify the return type.
             // Mark return type as invalid and try to continue.
-            try p.warn(.{ .ExpectedReturnType = .{ .token = p.tok_i } });
+            try p.warn(.expected_return_type);
         }
 
         if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
@@ -901,7 +927,7 @@ const Parser = struct {
     fn expectStatement(p: *Parser) !Node.Index {
         const statement = try p.parseStatement();
         if (statement == 0) {
-            return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+            return p.fail(.expected_statement);
         }
         return statement;
     }
@@ -940,7 +966,7 @@ const Parser = struct {
             if (block_expr != 0) break :blk block_expr;
             const assign_expr = try p.parseAssignExpr();
             if (assign_expr == 0) {
-                return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } });
+                return p.fail(.expected_block_or_assignment);
             }
             if (p.eatToken(.semicolon)) |_| {
                 return p.addNode(.{
@@ -957,7 +983,7 @@ const Parser = struct {
         };
         const else_token = p.eatToken(.keyword_else) orelse {
             if (else_required) {
-                return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } });
+                return p.fail(.expected_semi_or_else);
             }
             return p.addNode(.{
                 .tag = .if_simple,
@@ -993,7 +1019,7 @@ const Parser = struct {
         if (loop_stmt != 0) return loop_stmt;
 
         if (label_token != 0) {
-            return p.fail(.{ .ExpectedLabelable = .{ .token = p.tok_i } });
+            return p.fail(.expected_labelable);
         }
 
         return null_node;
@@ -1012,7 +1038,7 @@ const Parser = struct {
         if (inline_token == null) return null_node;
 
         // If we've seen "inline", there should have been a "for" or "while"
-        return p.fail(.{ .ExpectedInlinable = .{ .token = p.tok_i } });
+        return p.fail(.expected_inlinable);
     }
 
     /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
@@ -1034,7 +1060,7 @@ const Parser = struct {
             if (block_expr != 0) break :blk block_expr;
             const assign_expr = try p.parseAssignExpr();
             if (assign_expr == 0) {
-                return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } });
+                return p.fail(.expected_block_or_assignment);
             }
             if (p.eatToken(.semicolon)) |_| {
                 return p.addNode(.{
@@ -1051,7 +1077,7 @@ const Parser = struct {
         };
         const else_token = p.eatToken(.keyword_else) orelse {
             if (else_required) {
-                return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } });
+                return p.fail(.expected_semi_or_else);
             }
             return p.addNode(.{
                 .tag = .for_simple,
@@ -1095,7 +1121,7 @@ const Parser = struct {
             if (block_expr != 0) break :blk block_expr;
             const assign_expr = try p.parseAssignExpr();
             if (assign_expr == 0) {
-                return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } });
+                return p.fail(.expected_block_or_assignment);
             }
             if (p.eatToken(.semicolon)) |_| {
                 if (cont_expr == 0) {
@@ -1126,7 +1152,7 @@ const Parser = struct {
         };
         const else_token = p.eatToken(.keyword_else) orelse {
             if (else_required) {
-                return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } });
+                return p.fail(.expected_semi_or_else);
             }
             if (cont_expr == 0) {
                 return p.addNode(.{
@@ -1186,7 +1212,7 @@ const Parser = struct {
     fn expectBlockExprStatement(p: *Parser) !Node.Index {
         const node = try p.parseBlockExprStatement();
         if (node == 0) {
-            return p.fail(.{ .ExpectedBlockOrExpression = .{ .token = p.tok_i } });
+            return p.fail(.expected_block_or_expr);
         }
         return node;
     }
@@ -1259,7 +1285,7 @@ const Parser = struct {
     fn expectAssignExpr(p: *Parser) !Node.Index {
         const expr = try p.parseAssignExpr();
         if (expr == 0) {
-            return p.fail(.{ .ExpectedExprOrAssignment = .{ .token = p.tok_i } });
+            return p.fail(.expected_expr_or_assignment);
         }
         return expr;
     }
@@ -1272,7 +1298,7 @@ const Parser = struct {
     fn expectExpr(p: *Parser) Error!Node.Index {
         const node = try p.parseExpr();
         if (node == 0) {
-            return p.fail(.{ .ExpectedExpr = .{ .token = p.tok_i } });
+            return p.fail(.expected_expr);
         } else {
             return node;
         }
@@ -1289,7 +1315,7 @@ const Parser = struct {
                     const or_token = p.nextToken();
                     const rhs = try p.parseBoolAndExpr();
                     if (rhs == 0) {
-                        return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+                        return p.fail(.invalid_token);
                     }
                     res = try p.addNode(.{
                         .tag = .bool_or,
@@ -1316,7 +1342,7 @@ const Parser = struct {
                     const and_token = p.nextToken();
                     const rhs = try p.parseCompareExpr();
                     if (rhs == 0) {
-                        return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+                        return p.fail(.invalid_token);
                     }
                     res = try p.addNode(.{
                         .tag = .bool_and,
@@ -1385,7 +1411,7 @@ const Parser = struct {
                     _ = try p.parsePayload();
                     const rhs = try p.parseBitShiftExpr();
                     if (rhs == 0) {
-                        return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+                        return p.fail(.invalid_token);
                     }
                     res = try p.addNode(.{
                         .tag = .@"catch",
@@ -1413,7 +1439,7 @@ const Parser = struct {
     fn expectBitwiseExpr(p: *Parser) Error!Node.Index {
         const node = try p.parseBitwiseExpr();
         if (node == 0) {
-            return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+            return p.fail(.invalid_token);
         } else {
             return node;
         }
@@ -1447,7 +1473,7 @@ const Parser = struct {
     fn expectBitShiftExpr(p: *Parser) Error!Node.Index {
         const node = try p.parseBitShiftExpr();
         if (node == 0) {
-            return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+            return p.fail(.invalid_token);
         } else {
             return node;
         }
@@ -1487,7 +1513,7 @@ const Parser = struct {
     fn expectAdditionExpr(p: *Parser) Error!Node.Index {
         const node = try p.parseAdditionExpr();
         if (node == 0) {
-            return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+            return p.fail(.invalid_token);
         }
         return node;
     }
@@ -1528,7 +1554,7 @@ const Parser = struct {
     fn expectMultiplyExpr(p: *Parser) Error!Node.Index {
         const node = try p.parseMultiplyExpr();
         if (node == 0) {
-            return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+            return p.fail(.invalid_token);
         }
         return node;
     }
@@ -1566,7 +1592,7 @@ const Parser = struct {
     fn expectPrefixExpr(p: *Parser) Error!Node.Index {
         const node = try p.parsePrefixExpr();
         if (node == 0) {
-            return p.fail(.{ .ExpectedPrefixExpr = .{ .token = p.tok_i } });
+            return p.fail(.expected_prefix_expr);
         }
         return node;
     }
@@ -1827,7 +1853,7 @@ const Parser = struct {
     fn expectTypeExpr(p: *Parser) Error!Node.Index {
         const node = try p.parseTypeExpr();
         if (node == 0) {
-            return p.fail(.{ .ExpectedTypeExpr = .{ .token = p.tok_i } });
+            return p.fail(.expected_type_expr);
         }
         return node;
     }
@@ -1922,9 +1948,7 @@ const Parser = struct {
                             switch (p.token_tags[p.tok_i]) {
                                 .keyword_for => return p.parseForExpr(),
                                 .keyword_while => return p.parseWhileExpr(),
-                                else => return p.fail(.{
-                                    .ExpectedInlinable = .{ .token = p.tok_i },
-                                }),
+                                else => return p.fail(.expected_inlinable),
                             }
                         },
                         .keyword_for => {
@@ -1950,9 +1974,7 @@ const Parser = struct {
                 switch (p.token_tags[p.tok_i]) {
                     .keyword_for => return p.parseForExpr(),
                     .keyword_while => return p.parseWhileExpr(),
-                    else => return p.fail(.{
-                        .ExpectedInlinable = .{ .token = p.tok_i },
-                    }),
+                    else => return p.fail(.expected_inlinable),
                 }
             },
             .keyword_for => return p.parseForExpr(),
@@ -2170,20 +2192,13 @@ const Parser = struct {
                     .r_brace => break,
                     .colon, .r_paren, .r_bracket => {
                         p.tok_i -= 1;
-                        return p.fail(.{
-                            .ExpectedToken = .{
-                                .token = p.tok_i,
-                                .expected_id = .r_brace,
-                            },
-                        });
+                        return p.failExpected(.r_brace);
                     },
                     else => {
                         // This is likely just a missing comma;
                         // give an error but continue parsing this list.
                         p.tok_i -= 1;
-                        try p.warn(.{
-                            .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                        });
+                        try p.warnExpected(.comma);
                     },
                 }
             }
@@ -2214,9 +2229,7 @@ const Parser = struct {
             });
         }
         if (comma_one == null) {
-            try p.warn(.{
-                .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-            });
+            try p.warnExpected(.comma);
         }
 
         var init_list = std.ArrayList(Node.Index).init(p.gpa);
@@ -2278,7 +2291,7 @@ const Parser = struct {
                 res = node;
             }
             const lparen = (try p.expectTokenRecoverable(.l_paren)) orelse {
-                try p.warn(.{ .ExpectedParamList = .{ .token = p.tok_i } });
+                try p.warn(.expected_param_list);
                 return res;
             };
             if (p.eatToken(.r_paren)) |_| {
@@ -2304,9 +2317,7 @@ const Parser = struct {
                 });
             }
             if (comma_one == null) {
-                try p.warn(.{
-                    .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                });
+                try p.warnExpected(.comma);
             }
 
             var param_list = std.ArrayList(Node.Index).init(p.gpa);
@@ -2352,21 +2363,11 @@ const Parser = struct {
                     },
                     .colon, .r_brace, .r_bracket => {
                         p.tok_i -= 1;
-                        return p.fail(.{
-                            .ExpectedToken = .{
-                                .token = p.tok_i,
-                                .expected_id = .r_paren,
-                            },
-                        });
+                        return p.failExpected(.r_paren);
                     },
                     else => {
                         p.tok_i -= 1;
-                        try p.warn(.{
-                            .ExpectedToken = .{
-                                .token = p.tok_i,
-                                .expected_id = .comma,
-                            },
-                        });
+                        try p.warnExpected(.comma);
                     },
                 }
             }
@@ -2405,9 +2406,7 @@ const Parser = struct {
                     });
                 }
                 if (comma_one == null) {
-                    try p.warn(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                    });
+                    try p.warnExpected(.comma);
                 }
 
                 var param_list = std.ArrayList(Node.Index).init(p.gpa);
@@ -2453,21 +2452,11 @@ const Parser = struct {
                         },
                         .colon, .r_brace, .r_bracket => {
                             p.tok_i -= 1;
-                            return p.fail(.{
-                                .ExpectedToken = .{
-                                    .token = p.tok_i,
-                                    .expected_id = .r_paren,
-                                },
-                            });
+                            return p.failExpected(.r_paren);
                         },
                         else => {
                             p.tok_i -= 1;
-                            try p.warn(.{
-                                .ExpectedToken = .{
-                                    .token = p.tok_i,
-                                    .expected_id = .comma,
-                                },
-                            });
+                            try p.warnExpected(.comma);
                         },
                     }
                 }
@@ -2645,9 +2634,7 @@ const Parser = struct {
                         switch (p.token_tags[p.tok_i]) {
                             .keyword_for => return p.parseForTypeExpr(),
                             .keyword_while => return p.parseWhileTypeExpr(),
-                            else => return p.fail(.{
-                                .ExpectedInlinable = .{ .token = p.tok_i },
-                            }),
+                            else => return p.fail(.expected_inlinable),
                         }
                     },
                     .keyword_for => {
@@ -2716,9 +2703,7 @@ const Parser = struct {
                             });
                         }
                         if (comma_one == null) {
-                            try p.warn(.{
-                                .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                            });
+                            try p.warnExpected(.comma);
                         }
                         const field_init_two = try p.expectFieldInit();
                         const comma_two = p.eatToken(.comma);
@@ -2733,9 +2718,7 @@ const Parser = struct {
                             });
                         }
                         if (comma_two == null) {
-                            try p.warn(.{
-                                .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                            });
+                            try p.warnExpected(.comma);
                         }
                         var init_list = std.ArrayList(Node.Index).init(p.gpa);
                         defer init_list.deinit();
@@ -2754,21 +2737,11 @@ const Parser = struct {
                                 .r_brace => break,
                                 .colon, .r_paren, .r_bracket => {
                                     p.tok_i -= 1;
-                                    return p.fail(.{
-                                        .ExpectedToken = .{
-                                            .token = p.tok_i,
-                                            .expected_id = .r_brace,
-                                        },
-                                    });
+                                    return p.failExpected(.r_brace);
                                 },
                                 else => {
                                     p.tok_i -= 1;
-                                    try p.warn(.{
-                                        .ExpectedToken = .{
-                                            .token = p.tok_i,
-                                            .expected_id = .comma,
-                                        },
-                                    });
+                                    try p.warnExpected(.comma);
                                 },
                             }
                         }
@@ -2797,9 +2770,7 @@ const Parser = struct {
                         });
                     }
                     if (comma_one == null) {
-                        try p.warn(.{
-                            .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                        });
+                        try p.warnExpected(.comma);
                     }
                     const elem_init_two = try p.expectExpr();
                     const comma_two = p.eatToken(.comma);
@@ -2814,9 +2785,7 @@ const Parser = struct {
                         });
                     }
                     if (comma_two == null) {
-                        try p.warn(.{
-                            .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                        });
+                        try p.warnExpected(.comma);
                     }
                     var init_list = std.ArrayList(Node.Index).init(p.gpa);
                     defer init_list.deinit();
@@ -2835,21 +2804,11 @@ const Parser = struct {
                             .r_brace => break,
                             .colon, .r_paren, .r_bracket => {
                                 p.tok_i -= 1;
-                                return p.fail(.{
-                                    .ExpectedToken = .{
-                                        .token = p.tok_i,
-                                        .expected_id = .r_brace,
-                                    },
-                                });
+                                return p.failExpected(.r_brace);
                             },
                             else => {
                                 p.tok_i -= 1;
-                                try p.warn(.{
-                                    .ExpectedToken = .{
-                                        .token = p.tok_i,
-                                        .expected_id = .comma,
-                                    },
-                                });
+                                try p.warnExpected(.comma);
                             },
                         }
                     }
@@ -2892,20 +2851,13 @@ const Parser = struct {
                             .r_brace => break,
                             .colon, .r_paren, .r_bracket => {
                                 p.tok_i -= 1;
-                                return p.fail(.{
-                                    .ExpectedToken = .{
-                                        .token = p.tok_i,
-                                        .expected_id = .r_brace,
-                                    },
-                                });
+                                return p.failExpected(.r_brace);
                             },
                             else => {
                                 // This is likely just a missing comma;
                                 // give an error but continue parsing this list.
                                 p.tok_i -= 1;
-                                try p.warn(.{
-                                    .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                                });
+                                try p.warnExpected(.comma);
                             },
                         }
                     }
@@ -2942,7 +2894,7 @@ const Parser = struct {
     fn expectPrimaryTypeExpr(p: *Parser) !Node.Index {
         const node = try p.parsePrimaryTypeExpr();
         if (node == 0) {
-            return p.fail(.{ .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i } });
+            return p.fail(.expected_primary_type_expr);
         }
         return node;
     }
@@ -3095,9 +3047,7 @@ const Parser = struct {
                 else => {
                     // This is likely just a missing comma;
                     // give an error but continue parsing this list.
-                    try p.warn(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                    });
+                    try p.warnExpected(.comma);
                 },
             }
         }
@@ -3112,9 +3062,7 @@ const Parser = struct {
                     else => {
                         // This is likely just a missing comma;
                         // give an error but continue parsing this list.
-                        try p.warn(.{
-                            .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                        });
+                        try p.warnExpected(.comma);
                     },
                 }
             }
@@ -3126,9 +3074,7 @@ const Parser = struct {
                         else => {
                             // This is likely just a missing comma;
                             // give an error but continue parsing this list.
-                            try p.warn(.{
-                                .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                            });
+                            try p.warnExpected(.comma);
                         },
                     }
                 }
@@ -3238,7 +3184,7 @@ const Parser = struct {
         _ = p.eatToken(.colon) orelse return null_node;
         _ = try p.expectToken(.l_paren);
         const node = try p.parseAssignExpr();
-        if (node == 0) return p.fail(.{ .ExpectedExprOrAssignment = .{ .token = p.tok_i } });
+        if (node == 0) return p.fail(.expected_expr_or_assignment);
         _ = try p.expectToken(.r_paren);
         return node;
     }
@@ -3418,9 +3364,7 @@ const Parser = struct {
             switch (p.token_tags[p.tok_i]) {
                 .keyword_align => {
                     if (result.align_node != 0) {
-                        try p.warn(.{
-                            .ExtraAlignQualifier = .{ .token = p.tok_i },
-                        });
+                        try p.warn(.extra_align_qualifier);
                     }
                     p.tok_i += 1;
                     _ = try p.expectToken(.l_paren);
@@ -3436,27 +3380,21 @@ const Parser = struct {
                 },
                 .keyword_const => {
                     if (saw_const) {
-                        try p.warn(.{
-                            .ExtraConstQualifier = .{ .token = p.tok_i },
-                        });
+                        try p.warn(.extra_const_qualifier);
                     }
                     p.tok_i += 1;
                     saw_const = true;
                 },
                 .keyword_volatile => {
                     if (saw_volatile) {
-                        try p.warn(.{
-                            .ExtraVolatileQualifier = .{ .token = p.tok_i },
-                        });
+                        try p.warn(.extra_volatile_qualifier);
                     }
                     p.tok_i += 1;
                     saw_volatile = true;
                 },
                 .keyword_allowzero => {
                     if (saw_allowzero) {
-                        try p.warn(.{
-                            .ExtraAllowZeroQualifier = .{ .token = p.tok_i },
-                        });
+                        try p.warn(.extra_allowzero_qualifier);
                     }
                     p.tok_i += 1;
                     saw_allowzero = true;
@@ -3539,11 +3477,10 @@ const Parser = struct {
                 },
             }),
             .invalid_periodasterisks => {
-                const period_asterisk = p.nextToken();
-                try p.warn(.{ .AsteriskAfterPointerDereference = .{ .token = period_asterisk } });
+                try p.warn(.asterisk_after_ptr_deref);
                 return p.addNode(.{
                     .tag = .deref,
-                    .main_token = period_asterisk,
+                    .main_token = p.nextToken(),
                     .data = .{
                         .lhs = lhs,
                         .rhs = undefined,
@@ -3569,7 +3506,7 @@ const Parser = struct {
                 }),
                 else => {
                     p.tok_i += 1;
-                    try p.warn(.{ .ExpectedSuffixOp = .{ .token = p.tok_i } });
+                    try p.warn(.expected_suffix_op);
                     return null_node;
                 },
             },
@@ -3743,9 +3680,7 @@ const Parser = struct {
                     // This is likely just a missing comma;
                     // give an error but continue parsing this list.
                     p.tok_i -= 1;
-                    try p.warn(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                    });
+                    try p.warnExpected(.comma);
                 },
             }
         } else unreachable;
@@ -3763,17 +3698,13 @@ const Parser = struct {
                 .r_paren => return SmallSpan{ .zero_or_one = param_one },
                 .colon, .r_brace, .r_bracket => {
                     p.tok_i -= 1;
-                    return p.fail(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .r_paren },
-                    });
+                    return p.failExpected(.r_paren);
                 },
                 else => {
                     // This is likely just a missing comma;
                     // give an error but continue parsing this list.
                     p.tok_i -= 1;
-                    try p.warn(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                    });
+                    try p.warnExpected(.comma);
                 },
             }
         } else unreachable;
@@ -3799,17 +3730,13 @@ const Parser = struct {
                 .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() },
                 .colon, .r_brace, .r_bracket => {
                     p.tok_i -= 1;
-                    return p.fail(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .r_paren },
-                    });
+                    return p.failExpected(.r_paren);
                 },
                 else => {
                     // This is likely just a missing comma;
                     // give an error but continue parsing this list.
                     p.tok_i -= 1;
-                    try p.warn(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                    });
+                    try p.warnExpected(.comma);
                 },
             }
         }
@@ -3836,9 +3763,7 @@ const Parser = struct {
                         else => {
                             // This is likely just a missing comma;
                             // give an error but continue parsing this list.
-                            try p.warn(.{
-                                .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                            });
+                            try p.warnExpected(.comma);
                         },
                     }
                 }
@@ -3852,9 +3777,7 @@ const Parser = struct {
     fn parseBuiltinCall(p: *Parser) !Node.Index {
         const builtin_token = p.assertToken(.builtin);
         _ = (try p.expectTokenRecoverable(.l_paren)) orelse {
-            try p.warn(.{
-                .ExpectedParamList = .{ .token = p.tok_i },
-            });
+            try p.warn(.expected_param_list);
             // Pretend this was an identifier so we can continue parsing.
             return p.addNode(.{
                 .tag = .identifier,
@@ -3901,9 +3824,7 @@ const Parser = struct {
                 // This is likely just a missing comma;
                 // give an error but continue parsing this list.
                 p.tok_i -= 1;
-                try p.warn(.{
-                    .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                });
+                try p.warnExpected(.comma);
             },
         }
         const param_two = try p.expectExpr();
@@ -3932,9 +3853,7 @@ const Parser = struct {
                 // This is likely just a missing comma;
                 // give an error but continue parsing this list.
                 p.tok_i -= 1;
-                try p.warn(.{
-                    .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                });
+                try p.warnExpected(.comma);
             },
         }
 
@@ -3976,9 +3895,7 @@ const Parser = struct {
                     // This is likely just a missing comma;
                     // give an error but continue parsing this list.
                     p.tok_i -= 1;
-                    try p.warn(.{
-                        .ExpectedToken = .{ .token = p.tok_i, .expected_id = .comma },
-                    });
+                    try p.warnExpected(.comma);
                 },
             }
         }
@@ -4019,7 +3936,7 @@ const Parser = struct {
     fn expectStringLiteral(p: *Parser) !Node.Index {
         const node = try p.parseStringLiteral();
         if (node == 0) {
-            return p.fail(.{ .ExpectedStringLiteral = .{ .token = p.tok_i } });
+            return p.fail(.expected_string_literal);
         }
         return node;
     }
@@ -4044,7 +3961,7 @@ const Parser = struct {
         const then_payload = try p.parsePtrPayload();
 
         const then_expr = try bodyParseFn(p);
-        if (then_expr == 0) return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+        if (then_expr == 0) return p.fail(.invalid_token);
 
         const else_token = p.eatToken(.keyword_else) orelse return p.addNode(.{
             .tag = .if_simple,
@@ -4056,7 +3973,7 @@ const Parser = struct {
         });
         const else_payload = try p.parsePayload();
         const else_expr = try bodyParseFn(p);
-        if (else_expr == 0) return p.fail(.{ .InvalidToken = .{ .token = p.tok_i } });
+        if (else_expr == 0) return p.fail(.invalid_token);
 
         return p.addNode(.{
             .tag = .@"if",
@@ -4076,7 +3993,10 @@ const Parser = struct {
         if (p.eatToken(.doc_comment)) |tok| {
             var first_line = tok;
             if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) {
-                try p.warn(.{ .SameLineDocComment = .{ .token = tok } });
+                try p.warnMsg(.{
+                    .tag = .same_line_doc_comment,
+                    .token = tok,
+                });
                 first_line = p.eatToken(.doc_comment) orelse return null;
             }
             while (p.eatToken(.doc_comment)) |_| {}
@@ -4102,16 +4022,18 @@ const Parser = struct {
     fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
         const token = p.nextToken();
         if (p.token_tags[token] != tag) {
-            return p.fail(.{ .ExpectedToken = .{ .token = token, .expected_id = tag } });
+            return p.failMsg(.{
+                .tag = .expected_token,
+                .token = token,
+                .extra = .{ .expected_tag = tag },
+            });
         }
         return token;
     }
 
     fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex {
         if (p.token_tags[p.tok_i] != tag) {
-            try p.warn(.{
-                .ExpectedToken = .{ .token = p.tok_i, .expected_id = tag },
-            });
+            try p.warnExpected(tag);
             return null;
         } else {
             return p.nextToken();
lib/std/zig/parser_test.zig
@@ -127,7 +127,7 @@ test "zig fmt: decl between fields" {
         \\    b: usize,
         \\};
     , &[_]Error{
-        .DeclBetweenFields,
+        .decl_between_fields,
     });
 }
 
@@ -135,7 +135,7 @@ test "zig fmt: eof after missing comma" {
     try testError(
         \\foo()
     , &[_]Error{
-        .ExpectedToken,
+        .expected_token,
     });
 }
 
@@ -3578,7 +3578,7 @@ test "zig fmt: file ends with struct field" {
 //        \\const container = extern {};
 //        \\
 //    , &[_]Error{
-//        .ExpectedExpr,
+//        .expected_expr,
 //        .ExpectedVarDeclOrFn,
 //    });
 //}
@@ -3598,12 +3598,12 @@ test "zig fmt: same line doc comment returns error" {
         \\/// comment
         \\
     , &[_]Error{
-        .SameLineDocComment,
-        .SameLineDocComment,
-        .UnattachedDocComment,
-        .SameLineDocComment,
-        .SameLineDocComment,
-        .UnattachedDocComment,
+        .same_line_doc_comment,
+        .same_line_doc_comment,
+        .unattached_doc_comment,
+        .same_line_doc_comment,
+        .same_line_doc_comment,
+        .unattached_doc_comment,
     });
 }
 
@@ -3678,10 +3678,10 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {
 }
 
 test "zig fmt: C var args" {
-   try testCanonical(
-       \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
-       \\
-   );
+    try testCanonical(
+        \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
+        \\
+    );
 }
 
 //test "zig fmt: Only indent multiline string literals in function calls" {
@@ -4037,8 +4037,8 @@ test "recovery: top level" {
         \\test "" {inline}
         \\test "" {inline}
     , &[_]Error{
-        .ExpectedInlinable,
-        .ExpectedInlinable,
+        .expected_inlinable,
+        .expected_inlinable,
     });
 }
 
@@ -4049,8 +4049,8 @@ test "recovery: block statements" {
         \\    inline;
         \\}
     , &[_]Error{
-        .InvalidToken,
-        .ExpectedInlinable,
+        .invalid_token,
+        .expected_inlinable,
     });
 }
 
@@ -4066,10 +4066,10 @@ test "recovery: block statements" {
 //        \\    }
 //        \\}
 //    , &[_]Error{
-//        .ExpectedToken,
-//        .ExpectedToken,
-//        .InvalidAnd,
-//        .InvalidToken,
+//        .expected_token,
+//        .expected_token,
+//        .invalid_and,
+//        .invalid_token,
 //    });
 //}
 
@@ -4078,8 +4078,8 @@ test "recovery: extra qualifier" {
         \\const a: *const const u8;
         \\test ""
     , &[_]Error{
-        .ExtraConstQualifier,
-        .ExpectedLBrace,
+        .extra_const_qualifier,
+        .expected_block,
     });
 }
 
@@ -4091,8 +4091,8 @@ test "recovery: extra qualifier" {
 //        \\test ""
 //    , &[_]Error{
 //        .ExpectedReturnType,
-//        .InvalidAnd,
-//        .ExpectedLBrace,
+//        .invalid_and,
+//        .expected_block,
 //    });
 //}
 
@@ -4105,10 +4105,10 @@ test "recovery: extra qualifier" {
 //        \\    async a && b;
 //        \\}
 //    , &[_]Error{
-//        .ExpectedToken,
+//        .expected_token,
 //        .ExpectedPubItem,
 //        .ExpectedParamList,
-//        .InvalidAnd,
+//        .invalid_and,
 //    });
 //    try testError(
 //        \\threadlocal test "" {
@@ -4117,7 +4117,7 @@ test "recovery: extra qualifier" {
 //    , &[_]Error{
 //        .ExpectedVarDecl,
 //        .ExpectedParamList,
-//        .InvalidAnd,
+//        .invalid_and,
 //    });
 //}
 
@@ -4126,13 +4126,13 @@ test "recovery: extra qualifier" {
 //        \\inline test "" { a && b; }
 //    , &[_]Error{
 //        .ExpectedFn,
-//        .InvalidAnd,
+//        .invalid_and,
 //    });
 //    try testError(
 //        \\extern "" test "" { a && b; }
 //    , &[_]Error{
 //        .ExpectedVarDeclOrFn,
-//        .InvalidAnd,
+//        .invalid_and,
 //    });
 //}
 
@@ -4144,12 +4144,12 @@ test "recovery: extra qualifier" {
 //        \\    @foo
 //        \\}
 //    , &[_]Error{
-//        .InvalidAnd,
-//        .ExpectedToken,
-//        .InvalidAnd,
-//        .ExpectedToken,
+//        .invalid_and,
+//        .expected_token,
+//        .invalid_and,
+//        .expected_token,
 //        .ExpectedParamList,
-//        .ExpectedToken,
+//        .expected_token,
 //    });
 //}
 
@@ -4163,12 +4163,12 @@ test "recovery: extra qualifier" {
 //        \\    a && b
 //        \\}
 //    , &[_]Error{
-//        .ExpectedExpr,
-//        .ExpectedToken,
-//        .ExpectedToken,
-//        .ExpectedContainerMembers,
-//        .InvalidAnd,
-//        .ExpectedToken,
+//        .expected_expr,
+//        .expected_token,
+//        .expected_token,
+//        .expected_container_members,
+//        .invalid_and,
+//        .expected_token,
 //    });
 //}
 
@@ -4178,7 +4178,7 @@ test "recovery: extra qualifier" {
 //        \\    a(comptime T: type)
 //        \\}
 //    , &[_]Error{
-//        .ExpectedToken,
+//        .expected_token,
 //    });
 //}
 
@@ -4189,10 +4189,10 @@ test "recovery: extra qualifier" {
 //        \\    a && b;
 //        \\}
 //    , &[_]Error{
-//        .ExpectedContainerMembers,
-//        .ExpectedContainerMembers,
-//        .ExpectedContainerMembers,
-//        .InvalidAnd,
+//        .expected_container_members,
+//        .expected_container_members,
+//        .expected_container_members,
+//        .invalid_and,
 //    });
 //}
 //
@@ -4202,7 +4202,7 @@ test "recovery: mismatched bracket at top level" {
         \\    arr: 128]?G
         \\};
     , &[_]Error{
-        .ExpectedToken,
+        .expected_token,
     });
 }
 
@@ -4212,9 +4212,9 @@ test "recovery: mismatched bracket at top level" {
 //        \\    error && foo;
 //        \\}
 //    , &[_]Error{
-//        .ExpectedToken,
+//        .expected_token,
 //        .ExpectedIdentifier,
-//        .InvalidAnd,
+//        .invalid_and,
 //    });
 //}
 
@@ -4224,15 +4224,15 @@ test "recovery: mismatched bracket at top level" {
 //        \\    var sequence = "repeat".*** 10;
 //        \\}
 //    , &[_]Error{
-//        .AsteriskAfterPointerDereference,
+//        .asterisk_after_ptr_deref,
 //    });
 //    try testError(
 //        \\test "" {
 //        \\    var sequence = "repeat".** 10&&a;
 //        \\}
 //    , &[_]Error{
-//        .AsteriskAfterPointerDereference,
-//        .InvalidAnd,
+//        .asterisk_after_ptr_deref,
+//        .invalid_and,
 //    });
 //}
 
@@ -4245,10 +4245,10 @@ test "recovery: mismatched bracket at top level" {
 //        \\    a && b;
 //        \\}
 //    , &[_]Error{
-//        .ExpectedSemiOrElse,
-//        .ExpectedSemiOrElse,
-//        .ExpectedSemiOrElse,
-//        .InvalidAnd,
+//        .expected_semi_or_else,
+//        .expected_semi_or_else,
+//        .expected_semi_or_else,
+//        .invalid_and,
 //    });
 //}
 
@@ -4256,7 +4256,7 @@ test "recovery: invalid comptime" {
     try testError(
         \\comptime
     , &[_]Error{
-        .ExpectedBlockOrField,
+        .expected_block_or_field,
     });
 }
 
@@ -4264,12 +4264,12 @@ test "recovery: missing block after for/while loops" {
     try testError(
         \\test "" { while (foo) }
     , &[_]Error{
-        .ExpectedBlockOrAssignment,
+        .expected_block_or_assignment,
     });
     try testError(
         \\test "" { for (foo) |bar| }
     , &[_]Error{
-        .ExpectedBlockOrAssignment,
+        .expected_block_or_assignment,
     });
 }
 
@@ -4288,9 +4288,8 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
     defer tree.deinit(allocator);
 
     for (tree.errors) |parse_error| {
-        const error_token = tree.errorToken(parse_error);
-        const token_start = tree.tokens.items(.start)[error_token];
-        const loc = tree.tokenLocation(0, error_token);
+        const token_start = tree.tokens.items(.start)[parse_error.token];
+        const loc = tree.tokenLocation(0, parse_error.token);
         try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });
         try tree.renderError(parse_error, stderr);
         try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});
@@ -4362,7 +4361,7 @@ fn testCanonical(source: []const u8) !void {
     return testTransform(source, source);
 }
 
-const Error = std.meta.Tag(std.zig.ast.Error);
+const Error = std.zig.ast.Error.Tag;
 
 fn testError(source: []const u8, expected_errors: []const Error) !void {
     var tree = try std.zig.parse(std.testing.allocator, source);
@@ -4370,6 +4369,6 @@ fn testError(source: []const u8, expected_errors: []const Error) !void {
 
     std.testing.expect(tree.errors.len == expected_errors.len);
     for (expected_errors) |expected, i| {
-        std.testing.expectEqual(expected, tree.errors[i]);
+        std.testing.expectEqual(expected, tree.errors[i].tag);
     }
 }
src/main.zig
@@ -2898,7 +2898,7 @@ fn printErrMsgToFile(
         .on => true,
         .off => false,
     };
-    const lok_token = parse_error.loc();
+    const lok_token = parse_error.token;
 
     const token_starts = tree.tokens.items(.start);
     const token_tags = tree.tokens.items(.tag);
src/Module.zig
@@ -1723,7 +1723,7 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree {
                 err_msg.* = .{
                     .src_loc = .{
                         .file_scope = root_scope,
-                        .byte_offset = tree.tokens.items(.start)[parse_err.loc()],
+                        .byte_offset = tree.tokens.items(.start)[parse_err.token],
                     },
                     .msg = msg.toOwnedSlice(),
                 };