| author | |
| committer | |
| log | 5d22204d2dbf686d8b827eca15253914ef9543ab |
| tree | 9c5e331329593fc8bc896261416513c0d5acddbb |
| parent | 7d2e14267985df0226a5deee96d0c17c94bf6eb2 |
```zig
// a.zig
struct Foo {
a: u32,
};
```
before:
```
a.zig:1:1: error: expected test, comptime, var decl, or container field, found 'struct'
struct Foo {
^
```
after:
```
a.zig:1:8: error: 'struct Foo' is invalid
struct Foo {
^
a.zig:1:8: note: to declare a container do 'const Foo = struct'
struct Foo {
^
```5 files changed, 114 insertions(+), 3 deletions(-)
lib/std/zig/Ast.zig+12| ... | ... | @@ -334,6 +334,16 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { |
| 334 | 334 | .invalid_ampersand_ampersand => { |
| 335 | 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 | 347 | .previous_field => { |
| 338 | 348 | return stream.writeAll("field before declarations here"); |
| 339 | 349 | }, |
| ... | ... | @@ -2541,7 +2551,9 @@ pub const Error = struct { |
| 2541 | 2551 | expected_initializer, |
| 2542 | 2552 | mismatched_binary_op_whitespace, |
| 2543 | 2553 | invalid_ampersand_ampersand, |
| 2554 | c_style_container, | |
| 2544 | 2555 | |
| 2556 | zig_style_container, | |
| 2545 | 2557 | previous_field, |
| 2546 | 2558 | next_field, |
| 2547 | 2559 |
lib/std/zig/parse.zig+54-3| ... | ... | @@ -440,9 +440,15 @@ const Parser = struct { |
| 440 | 440 | break; |
| 441 | 441 | }, |
| 442 | 442 | else => { |
| 443 | try p.warn(.expected_container_members); | |
| 444 | // This was likely not supposed to end yet; try to find the next declaration. | |
| 445 | p.findNextContainerMember(); | |
| 443 | const c_container = p.parseCStyleContainer() catch |err| switch (err) { | |
| 444 | error.OutOfMemory => return error.OutOfMemory, | |
| 445 | error.ParseError => false, | |
| 446 | }; | |
| 447 | if (!c_container) { | |
| 448 | try p.warn(.expected_container_members); | |
| 449 | // This was likely not supposed to end yet; try to find the next declaration. | |
| 450 | p.findNextContainerMember(); | |
| 451 | } | |
| 446 | 452 | }, |
| 447 | 453 | } |
| 448 | 454 | } |
| ... | ... | @@ -978,6 +984,20 @@ const Parser = struct { |
| 978 | 984 | }), |
| 979 | 985 | .keyword_switch => return p.expectSwitchExpr(), |
| 980 | 986 | .keyword_if => return p.expectIfStatement(), |
| 987 | .keyword_enum, .keyword_struct, .keyword_union => { | |
| 988 | const identifier = p.tok_i + 2; | |
| 989 | if (try p.parseCStyleContainer()) { | |
| 990 | // Return something so that `expectStatement` is happy. | |
| 991 | return p.addNode(.{ | |
| 992 | .tag = .identifier, | |
| 993 | .main_token = identifier, | |
| 994 | .data = .{ | |
| 995 | .lhs = undefined, | |
| 996 | .rhs = undefined, | |
| 997 | }, | |
| 998 | }); | |
| 999 | } | |
| 1000 | }, | |
| 981 | 1001 | else => {}, |
| 982 | 1002 | } |
| 983 | 1003 | |
| ... | ... | @@ -3466,6 +3486,37 @@ const Parser = struct { |
| 3466 | 3486 | } |
| 3467 | 3487 | } |
| 3468 | 3488 | |
| 3489 | /// Give a helpful error message for those transitioning from | |
| 3490 | /// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'. | |
| 3491 | fn parseCStyleContainer(p: *Parser) Error!bool { | |
| 3492 | const main_token = p.tok_i; | |
| 3493 | switch (p.token_tags[p.tok_i]) { | |
| 3494 | .keyword_enum, .keyword_union, .keyword_struct => {}, | |
| 3495 | else => return false, | |
| 3496 | } | |
| 3497 | const identifier = p.tok_i + 1; | |
| 3498 | if (p.token_tags[identifier] != .identifier) return false; | |
| 3499 | p.tok_i += 2; | |
| 3500 | ||
| 3501 | try p.warnMsg(.{ | |
| 3502 | .tag = .c_style_container, | |
| 3503 | .token = identifier, | |
| 3504 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | |
| 3505 | }); | |
| 3506 | try p.warnMsg(.{ | |
| 3507 | .tag = .zig_style_container, | |
| 3508 | .is_note = true, | |
| 3509 | .token = identifier, | |
| 3510 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | |
| 3511 | }); | |
| 3512 | ||
| 3513 | _ = try p.expectToken(.l_brace); | |
| 3514 | _ = try p.parseContainerMembers(); | |
| 3515 | _ = try p.expectToken(.r_brace); | |
| 3516 | try p.expectSemicolon(.expected_semi_after_decl, true); | |
| 3517 | return true; | |
| 3518 | } | |
| 3519 | ||
| 3469 | 3520 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. |
| 3470 | 3521 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN |
| 3471 | 3522 | fn parseByteAlign(p: *Parser) !Node.Index { |
lib/std/zig/parser_test.zig+21| ... | ... | @@ -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 | 236 | test "zig fmt: decl between fields" { |
| 216 | 237 | try testError( |
| 217 | 238 | \\const S = struct { |
src/Module.zig+9| ... | ... | @@ -3335,6 +3335,15 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3335 | 3335 | .parent_decl_node = 0, |
| 3336 | 3336 | .lazy = .{ .byte_abs = token_starts[file.tree.errors[2].token] }, |
| 3337 | 3337 | }, err_msg, "field after declarations here", .{}); |
| 3338 | } else if (parse_err.tag == .c_style_container) { | |
| 3339 | const note = file.tree.errors[1]; | |
| 3340 | try mod.errNoteNonLazy(.{ | |
| 3341 | .file_scope = file, | |
| 3342 | .parent_decl_node = 0, | |
| 3343 | .lazy = .{ .byte_abs = token_starts[note.token] }, | |
| 3344 | }, err_msg, "to declare a container do 'const {s} = {s}'", .{ | |
| 3345 | file.tree.tokenSlice(note.token), note.extra.expected_tag.symbol(), | |
| 3346 | }); | |
| 3338 | 3347 | } |
| 3339 | 3348 | |
| 3340 | 3349 | { |
src/main.zig+18| ... | ... | @@ -4413,6 +4413,24 @@ fn printErrsMsgToStdErr( |
| 4413 | 4413 | }; |
| 4414 | 4414 | notes_len = 2; |
| 4415 | 4415 | i += 2; |
| 4416 | } else if (parse_error.tag == .c_style_container) { | |
| 4417 | const note = tree.errors[i + 1]; | |
| 4418 | ||
| 4419 | const prev_loc = tree.tokenLocation(0, parse_errors[i + 1].token); | |
| 4420 | notes_buffer[0] = .{ | |
| 4421 | .src = .{ | |
| 4422 | .src_path = path, | |
| 4423 | .msg = try std.fmt.allocPrint(arena, "to declare a container do 'const {s} = {s}'", .{ | |
| 4424 | tree.tokenSlice(note.token), note.extra.expected_tag.symbol(), | |
| 4425 | }), | |
| 4426 | .byte_offset = @intCast(u32, prev_loc.line_start), | |
| 4427 | .line = @intCast(u32, prev_loc.line), | |
| 4428 | .column = @intCast(u32, prev_loc.column), | |
| 4429 | .source_line = tree.source[prev_loc.line_start..prev_loc.line_end], | |
| 4430 | }, | |
| 4431 | }; | |
| 4432 | notes_len = 1; | |
| 4433 | i += 1; | |
| 4416 | 4434 | } |
| 4417 | 4435 | |
| 4418 | 4436 | const extra_offset = tree.errorOffset(parse_error); |