diff --git a/doc/langref.html.in b/doc/langref.html.in index 24b976ee42..491e7f8f6c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -10405,7 +10405,7 @@ pub fn main() !void {
String literals such as {#syntax#}"foo"{#endsyntax#} are in the global constant data section. This is why it is an error to pass a string literal to a mutable slice, like this:
- {#code_begin|test_err|expected type '[]u8'#} + {#code_begin|test_err|cannot cast pointer to array literal to slice type '[]u8'#} fn foo(s: []u8) void { _ = s; } diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index cecdc3adeb..a68959837a 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -66,20 +66,11 @@ pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void /// Returns an extra offset for column and byte offset of errors that /// should point after the token in the error message. -pub fn errorOffset(tree: Ast, error_tag: Error.Tag, token: TokenIndex) u32 { - return switch (error_tag) { - .expected_semi_after_decl, - .expected_semi_after_stmt, - .expected_comma_after_field, - .expected_comma_after_arg, - .expected_comma_after_param, - .expected_comma_after_initializer, - .expected_comma_after_switch_prong, - .expected_semi_or_else, - .expected_semi_or_lbrace, - => @intCast(u32, tree.tokenSlice(token).len), - else => 0, - }; +pub fn errorOffset(tree: Ast, parse_error: Error) u32 { + return if (parse_error.token_is_prev) + @intCast(u32, tree.tokenSlice(parse_error.token).len) + else + 0; } pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location { @@ -162,22 +153,22 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { }, .expected_block => { return stream.print("expected block or field, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_block_or_assignment => { return stream.print("expected block or assignment, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_block_or_expr => { return stream.print("expected block or expression, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_block_or_field => { return stream.print("expected block or field, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_container_members => { @@ -187,42 +178,42 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { }, .expected_expr => { return stream.print("expected expression, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_expr_or_assignment => { return stream.print("expected expression or assignment, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_fn => { return stream.print("expected function, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_inlinable => { return stream.print("expected 'while' or 'for', found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_labelable => { return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_param_list => { return stream.print("expected parameter list, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_prefix_expr => { return stream.print("expected prefix expression, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_primary_type_expr => { return stream.print("expected primary type expression, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_pub_item => { @@ -230,7 +221,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { }, .expected_return_type => { return stream.print("expected return type expression, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_semi_or_else => { @@ -244,39 +235,34 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { 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(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_type_expr => { return stream.print("expected type expression, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_var_decl => { return stream.print("expected variable declaration, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_var_decl_or_fn => { return stream.print("expected variable declaration or function, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_loop_payload => { return stream.print("expected loop payload, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .expected_container => { return stream.print("expected a struct, enum or union, found '{s}'", .{ - token_tags[parse_error.token].symbol(), + token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), }); }, .extern_fn_body => { @@ -305,11 +291,6 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { .invalid_bit_range => { return stream.writeAll("bit range not allowed on slices and arrays"); }, - .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"); }, @@ -319,6 +300,9 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { .varargs_nonfinal => { return stream.writeAll("function prototype has parameter after varargs"); }, + .expected_continue_expr => { + return stream.writeAll("expected ':' before while continue expression"); + }, .expected_semi_after_decl => { return stream.writeAll("expected ';' after declaration"); @@ -341,9 +325,19 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { .expected_comma_after_switch_prong => { return stream.writeAll("expected ',' after switch prong"); }, + .expected_initializer => { + return stream.writeAll("expected field initializer"); + }, + + .previous_field => { + return stream.writeAll("field before declarations here"); + }, + .next_field => { + return stream.writeAll("field after declarations here"); + }, .expected_token => { - const found_tag = token_tags[parse_error.token]; + const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)]; const expected_symbol = parse_error.extra.expected_tag.symbol(); switch (found_tag) { .invalid => return stream.print("expected '{s}', found invalid bytes", .{ @@ -2483,6 +2477,9 @@ pub const full = struct { pub const Error = struct { tag: Tag, + is_note: bool = false, + /// True if `token` points to the token before the token causing an issue. + token_is_prev: bool = false, token: TokenIndex, extra: union { none: void, @@ -2511,7 +2508,6 @@ pub const Error = struct { expected_semi_or_else, expected_semi_or_lbrace, expected_statement, - expected_string_literal, expected_suffix_op, expected_type_expr, expected_var_decl, @@ -2526,12 +2522,10 @@ pub const Error = struct { extra_volatile_qualifier, ptr_mod_on_array_child_type, invalid_bit_range, - invalid_token, same_line_doc_comment, unattached_doc_comment, varargs_nonfinal, - - // these have `token` set to token after which a semicolon was expected + expected_continue_expr, expected_semi_after_decl, expected_semi_after_stmt, expected_comma_after_field, @@ -2539,6 +2533,10 @@ pub const Error = struct { expected_comma_after_param, expected_comma_after_initializer, expected_comma_after_switch_prong, + expected_initializer, + + previous_field, + next_field, /// `expected_tag` is populated. expected_token, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 9050ba1bc4..2578629af5 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -91,6 +91,9 @@ const Parser = struct { extra_data: std.ArrayListUnmanaged(Node.Index), scratch: std.ArrayListUnmanaged(Node.Index), + /// Used for the error note of decl_between_fields error. + last_field: TokenIndex = undefined, + const SmallSpan = union(enum) { zero_or_one: Node.Index, multi: Node.SubRange, @@ -147,11 +150,6 @@ const Parser = struct { return result; } - 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(.{ @@ -161,13 +159,53 @@ const Parser = struct { }); } - fn warnExpectedAfter(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void { + fn warn(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void { @setCold(true); - try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i - 1 }); + try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i }); } fn warnMsg(p: *Parser, msg: Ast.Error) error{OutOfMemory}!void { @setCold(true); + switch (msg.tag) { + .expected_semi_after_decl, + .expected_semi_after_stmt, + .expected_comma_after_field, + .expected_comma_after_arg, + .expected_comma_after_param, + .expected_comma_after_initializer, + .expected_comma_after_switch_prong, + .expected_semi_or_else, + .expected_semi_or_lbrace, + .expected_token, + .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_pub_item, + .expected_return_type, + .expected_suffix_op, + .expected_type_expr, + .expected_var_decl, + .expected_var_decl_or_fn, + .expected_loop_payload, + .expected_container, + => if (msg.token != 0 and !p.tokensOnSameLine(msg.token - 1, msg.token)) { + var copy = msg; + copy.token_is_prev = true; + copy.token -= 1; + return p.errors.append(p.gpa, copy); + }, + else => {}, + } try p.errors.append(p.gpa, msg); } @@ -235,6 +273,8 @@ const Parser = struct { .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) { .identifier => { p.tok_i += 1; + const identifier = p.tok_i; + defer p.last_field = identifier; const container_field = try p.expectContainerFieldRecoverable(); if (container_field != 0) { switch (field_state) { @@ -245,6 +285,16 @@ const Parser = struct { .tag = .decl_between_fields, .token = p.nodes.items(.main_token)[node], }); + try p.warnMsg(.{ + .tag = .previous_field, + .is_note = true, + .token = p.last_field, + }); + try p.warnMsg(.{ + .tag = .next_field, + .is_note = true, + .token = identifier, + }); // Continue parsing; error will be reported later. field_state = .err; }, @@ -264,7 +314,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.warnExpectedAfter(.expected_comma_after_field); + try p.warn(.expected_comma_after_field); p.findNextContainerMember(); } }, @@ -338,6 +388,8 @@ const Parser = struct { trailing = p.token_tags[p.tok_i - 1] == .semicolon; }, .identifier => { + const identifier = p.tok_i; + defer p.last_field = identifier; const container_field = try p.expectContainerFieldRecoverable(); if (container_field != 0) { switch (field_state) { @@ -348,6 +400,14 @@ const Parser = struct { .tag = .decl_between_fields, .token = p.nodes.items(.main_token)[node], }); + try p.warnMsg(.{ + .tag = .previous_field, + .token = p.last_field, + }); + try p.warnMsg(.{ + .tag = .next_field, + .token = identifier, + }); // Continue parsing; error will be reported later. field_state = .err; }, @@ -367,7 +427,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.warnExpectedAfter(.expected_comma_after_field); + try p.warn(.expected_comma_after_field); p.findNextContainerMember(); } }, @@ -585,7 +645,7 @@ const Parser = struct { // Since parseBlock only return error.ParseError on // a missing '}' we can assume this function was // supposed to end here. - try p.warnExpectedAfter(.expected_semi_or_lbrace); + try p.warn(.expected_semi_or_lbrace); return null_node; }, } @@ -996,7 +1056,7 @@ const Parser = struct { }; _ = p.eatToken(.keyword_else) orelse { if (else_required) { - try p.warnExpectedAfter(.expected_semi_or_else); + try p.warn(.expected_semi_or_else); } return p.addNode(.{ .tag = .if_simple, @@ -1091,7 +1151,7 @@ const Parser = struct { }; _ = p.eatToken(.keyword_else) orelse { if (else_required) { - try p.warnExpectedAfter(.expected_semi_or_else); + try p.warn(.expected_semi_or_else); } return p.addNode(.{ .tag = .for_simple, @@ -1166,7 +1226,7 @@ const Parser = struct { }; _ = p.eatToken(.keyword_else) orelse { if (else_required) { - try p.warnExpectedAfter(.expected_semi_or_else); + try p.warn(.expected_semi_or_else); } if (cont_expr == 0) { return p.addNode(.{ @@ -1402,7 +1462,8 @@ const Parser = struct { } const rhs = try p.parseExprPrecedence(info.prec + 1); if (rhs == 0) { - return p.fail(.invalid_token); + try p.warn(.expected_expr); + return node; } node = try p.addNode(.{ @@ -1881,7 +1942,7 @@ const Parser = struct { /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? fn parseIfExpr(p: *Parser) !Node.Index { - return p.parseIf(parseExpr); + return p.parseIf(expectExpr); } /// Block <- LBRACE Statement* RBRACE @@ -2050,7 +2111,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_initializer), + else => try p.warn(.expected_comma_after_initializer), } if (p.eatToken(.r_brace)) |_| break; const next = try p.expectFieldInit(); @@ -2091,7 +2152,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_initializer), + else => try p.warn(.expected_comma_after_initializer), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2170,7 +2231,7 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_arg), + else => try p.warn(.expected_comma_after_arg), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2226,7 +2287,7 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_arg), + else => try p.warn(.expected_comma_after_arg), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2349,7 +2410,7 @@ const Parser = struct { .builtin => return p.parseBuiltinCall(), .keyword_fn => return p.parseFnProto(), - .keyword_if => return p.parseIf(parseTypeExpr), + .keyword_if => return p.parseIf(expectTypeExpr), .keyword_switch => return p.expectSwitchExpr(), .keyword_extern, @@ -2467,7 +2528,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_initializer), + else => try p.warn(.expected_comma_after_initializer), } if (p.eatToken(.r_brace)) |_| break; const next = try p.expectFieldInit(); @@ -2519,7 +2580,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_initializer), + else => try p.warn(.expected_comma_after_initializer), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2580,7 +2641,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_field), + else => try p.warn(.expected_comma_after_field), } } return p.addNode(.{ @@ -2879,7 +2940,7 @@ const Parser = struct { p.tok_i += 2; return identifier; } - return 0; + return null_node; } /// FieldInit <- DOT IDENTIFIER EQUAL Expr @@ -2896,15 +2957,23 @@ const Parser = struct { } fn expectFieldInit(p: *Parser) !Node.Index { - _ = try p.expectToken(.period); - _ = try p.expectToken(.identifier); - _ = try p.expectToken(.equal); + if (p.token_tags[p.tok_i] != .period or + p.token_tags[p.tok_i + 1] != .identifier or + p.token_tags[p.tok_i + 2] != .equal) + return p.fail(.expected_initializer); + + p.tok_i += 3; return p.expectExpr(); } /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN fn parseWhileContinueExpr(p: *Parser) !Node.Index { - _ = p.eatToken(.colon) orelse return null_node; + _ = p.eatToken(.colon) orelse { + if (p.token_tags[p.tok_i] == .l_paren and + p.tokensOnSameLine(p.tok_i - 1, p.tok_i)) + return p.fail(.expected_continue_expr); + return null_node; + }; _ = try p.expectToken(.l_paren); const node = try p.parseAssignExpr(); if (node == 0) return p.fail(.expected_expr_or_assignment); @@ -3413,7 +3482,7 @@ const Parser = struct { // All possible delimiters. .colon, .r_paren, .r_brace, .r_bracket => break, // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_switch_prong), + else => try p.warn(.expected_comma_after_switch_prong), } } return p.listToSpan(p.scratch.items[scratch_top..]); @@ -3442,7 +3511,7 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_param), + else => try p.warn(.expected_comma_after_param), } } if (varargs == .nonfinal) { @@ -3486,7 +3555,7 @@ const Parser = struct { break; }, // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpectedAfter(.expected_comma_after_arg), + else => try p.warn(.expected_comma_after_arg), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -3530,57 +3599,6 @@ const Parser = struct { } } - // string literal or multiline string literal - fn parseStringLiteral(p: *Parser) !Node.Index { - switch (p.token_tags[p.tok_i]) { - .string_literal => { - const main_token = p.nextToken(); - return p.addNode(.{ - .tag = .string_literal, - .main_token = main_token, - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, - }); - }, - .multiline_string_literal_line => { - const first_line = p.nextToken(); - while (p.token_tags[p.tok_i] == .multiline_string_literal_line) { - p.tok_i += 1; - } - return p.addNode(.{ - .tag = .multiline_string_literal, - .main_token = first_line, - .data = .{ - .lhs = first_line, - .rhs = p.tok_i - 1, - }, - }); - }, - else => return null_node, - } - } - - fn expectStringLiteral(p: *Parser) !Node.Index { - const node = try p.parseStringLiteral(); - if (node == 0) { - return p.fail(.expected_string_literal); - } - return node; - } - - fn expectIntegerLiteral(p: *Parser) !Node.Index { - return p.addNode(.{ - .tag = .integer_literal, - .main_token = try p.expectToken(.integer_literal), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, - }); - } - /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)? fn parseIf(p: *Parser, bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { const if_token = p.eatToken(.keyword_if) orelse return null_node; @@ -3590,7 +3608,7 @@ const Parser = struct { _ = try p.parsePtrPayload(); const then_expr = try bodyParseFn(p); - if (then_expr == 0) return p.fail(.invalid_token); + assert(then_expr != 0); _ = p.eatToken(.keyword_else) orelse return p.addNode(.{ .tag = .if_simple, @@ -3602,7 +3620,7 @@ const Parser = struct { }); _ = try p.parsePayload(); const else_expr = try bodyParseFn(p); - if (else_expr == 0) return p.fail(.invalid_token); + assert(then_expr != 0); return p.addNode(.{ .tag = .@"if", @@ -3649,25 +3667,14 @@ const Parser = struct { } fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex { - const token = p.nextToken(); - if (p.token_tags[token] != tag) { - p.tok_i -= 1; // Go back so that we can recover properly. + if (p.token_tags[p.tok_i] != tag) { return p.failMsg(.{ .tag = .expected_token, - .token = token, + .token = p.tok_i, .extra = .{ .expected_tag = tag }, }); } - return token; - } - - fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex { - if (p.token_tags[p.tok_i] != tag) { - try p.warnExpected(tag); - return null; - } else { - return p.nextToken(); - } + return p.nextToken(); } fn expectSemicolon(p: *Parser, error_tag: AstError.Tag, recoverable: bool) Error!void { @@ -3675,7 +3682,7 @@ const Parser = struct { _ = p.nextToken(); return; } - try p.warnExpectedAfter(error_tag); + try p.warn(error_tag); if (!recoverable) return error.ParseError; } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 5eca272b62..0c79b3b187 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -226,6 +226,8 @@ test "zig fmt: decl between fields" { \\}; , &[_]Error{ .decl_between_fields, + .previous_field, + .next_field, }); } @@ -5018,6 +5020,25 @@ test "zig fmt: make single-line if no trailing comma" { ); } +test "zig fmt: while continue expr" { + try testCanonical( + \\test { + \\ while (i > 0) + \\ (i * 2); + \\} + \\ + ); + try testError( + \\test { + \\ while (i > 0) (i -= 1) { + \\ print("test123", .{}); + \\ } + \\} + , &[_]Error{ + .expected_continue_expr, + }); +} + test "zig fmt: error for invalid bit range" { try testError( \\var x: []align(0:0:0)u8 = bar; @@ -5057,7 +5078,9 @@ test "recovery: block statements" { \\ inline; \\} , &[_]Error{ - .invalid_token, + .expected_expr, + .expected_semi_after_stmt, + .expected_statement, .expected_inlinable, }); } @@ -5076,7 +5099,7 @@ test "recovery: missing comma" { , &[_]Error{ .expected_comma_after_switch_prong, .expected_comma_after_switch_prong, - .invalid_token, + .expected_expr, }); } diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index b6e1ced061..76b14df877 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -322,7 +322,18 @@ pub const Token = struct { } pub fn symbol(tag: Tag) []const u8 { - return tag.lexeme() orelse @tagName(tag); + return tag.lexeme() orelse switch (tag) { + .invalid => "invalid bytes", + .identifier => "an identifier", + .string_literal, .multiline_string_literal_line => "a string literal", + .char_literal => "a character literal", + .eof => "EOF", + .builtin => "a builtin function", + .integer_literal => "an integer literal", + .float_literal => "a floating point literal", + .doc_comment, .container_doc_comment => "a document comment", + else => unreachable, + }; } }; }; diff --git a/src/Module.zig b/src/Module.zig index e973c42a7d..524e8402cd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2995,7 +2995,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { const token_starts = file.tree.tokens.items(.start); const token_tags = file.tree.tokens.items(.tag); - const extra_offset = file.tree.errorOffset(parse_err.tag, parse_err.token); + const extra_offset = file.tree.errorOffset(parse_err); try file.tree.renderError(parse_err, msg.writer()); const err_msg = try gpa.create(ErrorMsg); err_msg.* = .{ @@ -3006,14 +3006,25 @@ pub fn astGenFile(mod: *Module, file: *File) !void { }, .msg = msg.toOwnedSlice(), }; - if (token_tags[parse_err.token] == .invalid) { - const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token).len); - const byte_abs = token_starts[parse_err.token] + bad_off; + if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { + const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); + const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; try mod.errNoteNonLazy(.{ .file_scope = file, .parent_decl_node = 0, .lazy = .{ .byte_abs = byte_abs }, }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); + } else if (parse_err.tag == .decl_between_fields) { + try mod.errNoteNonLazy(.{ + .file_scope = file, + .parent_decl_node = 0, + .lazy = .{ .byte_abs = token_starts[file.tree.errors[1].token] }, + }, err_msg, "field before declarations here", .{}); + try mod.errNoteNonLazy(.{ + .file_scope = file, + .parent_decl_node = 0, + .lazy = .{ .byte_abs = token_starts[file.tree.errors[2].token] }, + }, err_msg, "field after declarations here", .{}); } { diff --git a/src/main.zig b/src/main.zig index 248ae2dd76..2b120f8d9a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3799,9 +3799,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void }; defer tree.deinit(gpa); - for (tree.errors) |parse_error| { - try printErrMsgToStdErr(gpa, arena, parse_error, tree, "