| author | |
| committer | |
| log | 7090f0471c0169c60a9476b537b09eebe1bdf6af |
| tree | 2410a74f83f6a5f7a20c30b2b606da419edf8e96 |
| parent | 8033767082f2178416fba8cb4a4b03fef961d318 |
| parent | 2a3f3766a437faed13736c1ff505854b6737ae33 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
parser: add helpful error for C style container declarations5 files changed, 121 insertions(+), 37 deletions(-)
lib/std/zig/Ast.zig+12| ... | @@ -334,6 +334,16 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { | ... | @@ -334,6 +334,16 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { |
| 334 | .invalid_ampersand_ampersand => { | 334 | .invalid_ampersand_ampersand => { |
| 335 | return stream.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"); | 335 | return stream.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"); |
| 336 | }, | 336 | }, |
| 337 | .c_style_container => { | ||
| 338 | return stream.print("'{s} {s}' is invalid", .{ | ||
| 339 | parse_error.extra.expected_tag.symbol(), tree.tokenSlice(parse_error.token), | ||
| 340 | }); | ||
| 341 | }, | ||
| 342 | .zig_style_container => { | ||
| 343 | return stream.print("to declare a container do 'const {s} = {s}'", .{ | ||
| 344 | tree.tokenSlice(parse_error.token), parse_error.extra.expected_tag.symbol(), | ||
| 345 | }); | ||
| 346 | }, | ||
| 337 | .previous_field => { | 347 | .previous_field => { |
| 338 | return stream.writeAll("field before declarations here"); | 348 | return stream.writeAll("field before declarations here"); |
| 339 | }, | 349 | }, |
| ... | @@ -2541,7 +2551,9 @@ pub const Error = struct { | ... | @@ -2541,7 +2551,9 @@ pub const Error = struct { |
| 2541 | expected_initializer, | 2551 | expected_initializer, |
| 2542 | mismatched_binary_op_whitespace, | 2552 | mismatched_binary_op_whitespace, |
| 2543 | invalid_ampersand_ampersand, | 2553 | invalid_ampersand_ampersand, |
| 2554 | c_style_container, | ||
| 2544 | 2555 | ||
| 2556 | zig_style_container, | ||
| 2545 | previous_field, | 2557 | previous_field, |
| 2546 | next_field, | 2558 | next_field, |
| 2547 | 2559 |
lib/std/zig/parse.zig+56-4| ... | @@ -178,7 +178,6 @@ const Parser = struct { | ... | @@ -178,7 +178,6 @@ const Parser = struct { |
| 178 | .expected_block_or_assignment, | 178 | .expected_block_or_assignment, |
| 179 | .expected_block_or_expr, | 179 | .expected_block_or_expr, |
| 180 | .expected_block_or_field, | 180 | .expected_block_or_field, |
| 181 | .expected_container_members, | ||
| 182 | .expected_expr, | 181 | .expected_expr, |
| 183 | .expected_expr_or_assignment, | 182 | .expected_expr_or_assignment, |
| 184 | .expected_fn, | 183 | .expected_fn, |
| ... | @@ -401,10 +400,12 @@ const Parser = struct { | ... | @@ -401,10 +400,12 @@ const Parser = struct { |
| 401 | }); | 400 | }); |
| 402 | try p.warnMsg(.{ | 401 | try p.warnMsg(.{ |
| 403 | .tag = .previous_field, | 402 | .tag = .previous_field, |
| 403 | .is_note = true, | ||
| 404 | .token = last_field, | 404 | .token = last_field, |
| 405 | }); | 405 | }); |
| 406 | try p.warnMsg(.{ | 406 | try p.warnMsg(.{ |
| 407 | .tag = .next_field, | 407 | .tag = .next_field, |
| 408 | .is_note = true, | ||
| 408 | .token = identifier, | 409 | .token = identifier, |
| 409 | }); | 410 | }); |
| 410 | // Continue parsing; error will be reported later. | 411 | // Continue parsing; error will be reported later. |
| ... | @@ -440,9 +441,15 @@ const Parser = struct { | ... | @@ -440,9 +441,15 @@ const Parser = struct { |
| 440 | break; | 441 | break; |
| 441 | }, | 442 | }, |
| 442 | else => { | 443 | else => { |
| 443 | try p.warn(.expected_container_members); | 444 | const c_container = p.parseCStyleContainer() catch |err| switch (err) { |
| 444 | // This was likely not supposed to end yet; try to find the next declaration. | 445 | error.OutOfMemory => return error.OutOfMemory, |
| 445 | p.findNextContainerMember(); | 446 | error.ParseError => false, |
| 447 | }; | ||
| 448 | if (!c_container) { | ||
| 449 | try p.warn(.expected_container_members); | ||
| 450 | // This was likely not supposed to end yet; try to find the next declaration. | ||
| 451 | p.findNextContainerMember(); | ||
| 452 | } | ||
| 446 | }, | 453 | }, |
| 447 | } | 454 | } |
| 448 | } | 455 | } |
| ... | @@ -978,6 +985,20 @@ const Parser = struct { | ... | @@ -978,6 +985,20 @@ const Parser = struct { |
| 978 | }), | 985 | }), |
| 979 | .keyword_switch => return p.expectSwitchExpr(), | 986 | .keyword_switch => return p.expectSwitchExpr(), |
| 980 | .keyword_if => return p.expectIfStatement(), | 987 | .keyword_if => return p.expectIfStatement(), |
| 988 | .keyword_enum, .keyword_struct, .keyword_union => { | ||
| 989 | const identifier = p.tok_i + 1; | ||
| 990 | if (try p.parseCStyleContainer()) { | ||
| 991 | // Return something so that `expectStatement` is happy. | ||
| 992 | return p.addNode(.{ | ||
| 993 | .tag = .identifier, | ||
| 994 | .main_token = identifier, | ||
| 995 | .data = .{ | ||
| 996 | .lhs = undefined, | ||
| 997 | .rhs = undefined, | ||
| 998 | }, | ||
| 999 | }); | ||
| 1000 | } | ||
| 1001 | }, | ||
| 981 | else => {}, | 1002 | else => {}, |
| 982 | } | 1003 | } |
| 983 | 1004 | ||
| ... | @@ -3466,6 +3487,37 @@ const Parser = struct { | ... | @@ -3466,6 +3487,37 @@ const Parser = struct { |
| 3466 | } | 3487 | } |
| 3467 | } | 3488 | } |
| 3468 | 3489 | ||
| 3490 | /// Give a helpful error message for those transitioning from | ||
| 3491 | /// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'. | ||
| 3492 | fn parseCStyleContainer(p: *Parser) Error!bool { | ||
| 3493 | const main_token = p.tok_i; | ||
| 3494 | switch (p.token_tags[p.tok_i]) { | ||
| 3495 | .keyword_enum, .keyword_union, .keyword_struct => {}, | ||
| 3496 | else => return false, | ||
| 3497 | } | ||
| 3498 | const identifier = p.tok_i + 1; | ||
| 3499 | if (p.token_tags[identifier] != .identifier) return false; | ||
| 3500 | p.tok_i += 2; | ||
| 3501 | |||
| 3502 | try p.warnMsg(.{ | ||
| 3503 | .tag = .c_style_container, | ||
| 3504 | .token = identifier, | ||
| 3505 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | ||
| 3506 | }); | ||
| 3507 | try p.warnMsg(.{ | ||
| 3508 | .tag = .zig_style_container, | ||
| 3509 | .is_note = true, | ||
| 3510 | .token = identifier, | ||
| 3511 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | ||
| 3512 | }); | ||
| 3513 | |||
| 3514 | _ = try p.expectToken(.l_brace); | ||
| 3515 | _ = try p.parseContainerMembers(); | ||
| 3516 | _ = try p.expectToken(.r_brace); | ||
| 3517 | try p.expectSemicolon(.expected_semi_after_decl, true); | ||
| 3518 | return true; | ||
| 3519 | } | ||
| 3520 | |||
| 3469 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. | 3521 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. |
| 3470 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | 3522 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN |
| 3471 | fn parseByteAlign(p: *Parser) !Node.Index { | 3523 | fn parseByteAlign(p: *Parser) !Node.Index { |
lib/std/zig/parser_test.zig+21| ... | @@ -212,6 +212,27 @@ test "zig fmt: top-level fields" { | ... | @@ -212,6 +212,27 @@ test "zig fmt: top-level fields" { |
| 212 | ); | 212 | ); |
| 213 | } | 213 | } |
| 214 | 214 | ||
| 215 | test "zig fmt: C style containers" { | ||
| 216 | try testError( | ||
| 217 | \\struct Foo { | ||
| 218 | \\ a: u32, | ||
| 219 | \\}; | ||
| 220 | , &[_]Error{ | ||
| 221 | .c_style_container, | ||
| 222 | .zig_style_container, | ||
| 223 | }); | ||
| 224 | try testError( | ||
| 225 | \\test { | ||
| 226 | \\ struct Foo { | ||
| 227 | \\ a: u32, | ||
| 228 | \\ }; | ||
| 229 | \\} | ||
| 230 | , &[_]Error{ | ||
| 231 | .c_style_container, | ||
| 232 | .zig_style_container, | ||
| 233 | }); | ||
| 234 | } | ||
| 235 | |||
| 215 | test "zig fmt: decl between fields" { | 236 | test "zig fmt: decl between fields" { |
| 216 | try testError( | 237 | try testError( |
| 217 | \\const S = struct { | 238 | \\const S = struct { |
src/Module.zig+15-11| ... | @@ -3324,17 +3324,21 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3324,17 +3324,21 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3324 | .parent_decl_node = 0, | 3324 | .parent_decl_node = 0, |
| 3325 | .lazy = .{ .byte_abs = byte_abs }, | 3325 | .lazy = .{ .byte_abs = byte_abs }, |
| 3326 | }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); | 3326 | }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); |
| 3327 | } else if (parse_err.tag == .decl_between_fields) { | 3327 | } |
| 3328 | try mod.errNoteNonLazy(.{ | 3328 | |
| 3329 | .file_scope = file, | 3329 | for (file.tree.errors[1..]) |note| { |
| 3330 | .parent_decl_node = 0, | 3330 | if (!note.is_note) break; |
| 3331 | .lazy = .{ .byte_abs = token_starts[file.tree.errors[1].token] }, | 3331 | |
| 3332 | }, err_msg, "field before declarations here", .{}); | 3332 | try file.tree.renderError(note, msg.writer()); |
| 3333 | try mod.errNoteNonLazy(.{ | 3333 | err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1); |
| 3334 | .file_scope = file, | 3334 | err_msg.notes[err_msg.notes.len - 1] = .{ |
| 3335 | .parent_decl_node = 0, | 3335 | .src_loc = .{ |
| 3336 | .lazy = .{ .byte_abs = token_starts[file.tree.errors[2].token] }, | 3336 | .file_scope = file, |
| 3337 | }, err_msg, "field after declarations here", .{}); | 3337 | .parent_decl_node = 0, |
| 3338 | .lazy = .{ .byte_abs = token_starts[note.token] }, | ||
| 3339 | }, | ||
| 3340 | .msg = msg.toOwnedSlice(), | ||
| 3341 | }; | ||
| 3338 | } | 3342 | } |
| 3339 | 3343 | ||
| 3340 | { | 3344 | { |
src/main.zig+17-22| ... | @@ -4367,7 +4367,7 @@ fn printErrsMsgToStdErr( | ... | @@ -4367,7 +4367,7 @@ fn printErrsMsgToStdErr( |
| 4367 | defer text_buf.deinit(); | 4367 | defer text_buf.deinit(); |
| 4368 | const writer = text_buf.writer(); | 4368 | const writer = text_buf.writer(); |
| 4369 | try tree.renderError(parse_error, writer); | 4369 | try tree.renderError(parse_error, writer); |
| 4370 | const text = text_buf.items; | 4370 | const text = try arena.dupe(u8, text_buf.items); |
| 4371 | 4371 | ||
| 4372 | var notes_buffer: [2]Compilation.AllErrors.Message = undefined; | 4372 | var notes_buffer: [2]Compilation.AllErrors.Message = undefined; |
| 4373 | var notes_len: usize = 0; | 4373 | var notes_len: usize = 0; |
| ... | @@ -4388,31 +4388,26 @@ fn printErrsMsgToStdErr( | ... | @@ -4388,31 +4388,26 @@ fn printErrsMsgToStdErr( |
| 4388 | }, | 4388 | }, |
| 4389 | }; | 4389 | }; |
| 4390 | notes_len += 1; | 4390 | notes_len += 1; |
| 4391 | } else if (parse_error.tag == .decl_between_fields) { | 4391 | } |
| 4392 | const prev_loc = tree.tokenLocation(0, parse_errors[i + 1].token); | 4392 | |
| 4393 | notes_buffer[0] = .{ | 4393 | for (parse_errors[i + 1 ..]) |note| { |
| 4394 | .src = .{ | 4394 | if (!note.is_note) break; |
| 4395 | .src_path = path, | 4395 | |
| 4396 | .msg = "field before declarations here", | 4396 | text_buf.items.len = 0; |
| 4397 | .byte_offset = @intCast(u32, prev_loc.line_start), | 4397 | try tree.renderError(note, writer); |
| 4398 | .line = @intCast(u32, prev_loc.line), | 4398 | const note_loc = tree.tokenLocation(0, note.token); |
| 4399 | .column = @intCast(u32, prev_loc.column), | 4399 | notes_buffer[notes_len] = .{ |
| 4400 | .source_line = tree.source[prev_loc.line_start..prev_loc.line_end], | ||
| 4401 | }, | ||
| 4402 | }; | ||
| 4403 | const next_loc = tree.tokenLocation(0, parse_errors[i + 2].token); | ||
| 4404 | notes_buffer[1] = .{ | ||
| 4405 | .src = .{ | 4400 | .src = .{ |
| 4406 | .src_path = path, | 4401 | .src_path = path, |
| 4407 | .msg = "field after declarations here", | 4402 | .msg = try arena.dupe(u8, text_buf.items), |
| 4408 | .byte_offset = @intCast(u32, next_loc.line_start), | 4403 | .byte_offset = @intCast(u32, note_loc.line_start), |
| 4409 | .line = @intCast(u32, next_loc.line), | 4404 | .line = @intCast(u32, note_loc.line), |
| 4410 | .column = @intCast(u32, next_loc.column), | 4405 | .column = @intCast(u32, note_loc.column), |
| 4411 | .source_line = tree.source[next_loc.line_start..next_loc.line_end], | 4406 | .source_line = tree.source[note_loc.line_start..note_loc.line_end], |
| 4412 | }, | 4407 | }, |
| 4413 | }; | 4408 | }; |
| 4414 | notes_len = 2; | 4409 | i += 1; |
| 4415 | i += 2; | 4410 | notes_len += 1; |
| 4416 | } | 4411 | } |
| 4417 | 4412 | ||
| 4418 | const extra_offset = tree.errorOffset(parse_error); | 4413 | const extra_offset = tree.errorOffset(parse_error); |