authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-17 22:14:45+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-17 22:16:26+02:00
log6b65590715d0871c11635fc49cb1fc471a60ea59
treef0de6e9c50c86a48df5656efb6ae963a8765d2ed
parent92f276781417d7e710081470d97606e26cf764d6

parser: add notes to decl_between_fields error


6 files changed, 131 insertions(+), 61 deletions(-)

lib/std/zig/Ast.zig+11
...@@ -329,6 +329,13 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {...@@ -329,6 +329,13 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
329 return stream.writeAll("expected field initializer");329 return stream.writeAll("expected field initializer");
330 },330 },
331331
332 .previous_field => {
333 return stream.writeAll("field before declarations here");
334 },
335 .next_field => {
336 return stream.writeAll("field after declarations here");
337 },
338
332 .expected_token => {339 .expected_token => {
333 const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)];340 const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)];
334 const expected_symbol = parse_error.extra.expected_tag.symbol();341 const expected_symbol = parse_error.extra.expected_tag.symbol();
...@@ -2470,6 +2477,7 @@ pub const full = struct {...@@ -2470,6 +2477,7 @@ pub const full = struct {
24702477
2471pub const Error = struct {2478pub const Error = struct {
2472 tag: Tag,2479 tag: Tag,
2480 is_note: bool = false,
2473 /// True if `token` points to the token before the token causing an issue.2481 /// True if `token` points to the token before the token causing an issue.
2474 token_is_prev: bool = false,2482 token_is_prev: bool = false,
2475 token: TokenIndex,2483 token: TokenIndex,
...@@ -2527,6 +2535,9 @@ pub const Error = struct {...@@ -2527,6 +2535,9 @@ pub const Error = struct {
2527 expected_comma_after_switch_prong,2535 expected_comma_after_switch_prong,
2528 expected_initializer,2536 expected_initializer,
25292537
2538 previous_field,
2539 next_field,
2540
2530 /// `expected_tag` is populated.2541 /// `expected_tag` is populated.
2531 expected_token,2542 expected_token,
2532 };2543 };
lib/std/zig/parse.zig+25
...@@ -91,6 +91,9 @@ const Parser = struct {...@@ -91,6 +91,9 @@ const Parser = struct {
91 extra_data: std.ArrayListUnmanaged(Node.Index),91 extra_data: std.ArrayListUnmanaged(Node.Index),
92 scratch: std.ArrayListUnmanaged(Node.Index),92 scratch: std.ArrayListUnmanaged(Node.Index),
9393
94 /// Used for the error note of decl_between_fields error.
95 last_field: TokenIndex = undefined,
96
94 const SmallSpan = union(enum) {97 const SmallSpan = union(enum) {
95 zero_or_one: Node.Index,98 zero_or_one: Node.Index,
96 multi: Node.SubRange,99 multi: Node.SubRange,
...@@ -270,6 +273,8 @@ const Parser = struct {...@@ -270,6 +273,8 @@ const Parser = struct {
270 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {273 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
271 .identifier => {274 .identifier => {
272 p.tok_i += 1;275 p.tok_i += 1;
276 const identifier = p.tok_i;
277 defer p.last_field = identifier;
273 const container_field = try p.expectContainerFieldRecoverable();278 const container_field = try p.expectContainerFieldRecoverable();
274 if (container_field != 0) {279 if (container_field != 0) {
275 switch (field_state) {280 switch (field_state) {
...@@ -280,6 +285,16 @@ const Parser = struct {...@@ -280,6 +285,16 @@ const Parser = struct {
280 .tag = .decl_between_fields,285 .tag = .decl_between_fields,
281 .token = p.nodes.items(.main_token)[node],286 .token = p.nodes.items(.main_token)[node],
282 });287 });
288 try p.warnMsg(.{
289 .tag = .previous_field,
290 .is_note = true,
291 .token = p.last_field,
292 });
293 try p.warnMsg(.{
294 .tag = .next_field,
295 .is_note = true,
296 .token = identifier,
297 });
283 // Continue parsing; error will be reported later.298 // Continue parsing; error will be reported later.
284 field_state = .err;299 field_state = .err;
285 },300 },
...@@ -373,6 +388,8 @@ const Parser = struct {...@@ -373,6 +388,8 @@ const Parser = struct {
373 trailing = p.token_tags[p.tok_i - 1] == .semicolon;388 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
374 },389 },
375 .identifier => {390 .identifier => {
391 const identifier = p.tok_i;
392 defer p.last_field = identifier;
376 const container_field = try p.expectContainerFieldRecoverable();393 const container_field = try p.expectContainerFieldRecoverable();
377 if (container_field != 0) {394 if (container_field != 0) {
378 switch (field_state) {395 switch (field_state) {
...@@ -383,6 +400,14 @@ const Parser = struct {...@@ -383,6 +400,14 @@ const Parser = struct {
383 .tag = .decl_between_fields,400 .tag = .decl_between_fields,
384 .token = p.nodes.items(.main_token)[node],401 .token = p.nodes.items(.main_token)[node],
385 });402 });
403 try p.warnMsg(.{
404 .tag = .previous_field,
405 .token = p.last_field,
406 });
407 try p.warnMsg(.{
408 .tag = .next_field,
409 .token = identifier,
410 });
386 // Continue parsing; error will be reported later.411 // Continue parsing; error will be reported later.
387 field_state = .err;412 field_state = .err;
388 },413 },
lib/std/zig/parser_test.zig+2
...@@ -226,6 +226,8 @@ test "zig fmt: decl between fields" {...@@ -226,6 +226,8 @@ test "zig fmt: decl between fields" {
226 \\};226 \\};
227 , &[_]Error{227 , &[_]Error{
228 .decl_between_fields,228 .decl_between_fields,
229 .previous_field,
230 .next_field,
229 });231 });
230}232}
231233
src/Module.zig+11
...@@ -3014,6 +3014,17 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3014,6 +3014,17 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3014 .parent_decl_node = 0,3014 .parent_decl_node = 0,
3015 .lazy = .{ .byte_abs = byte_abs },3015 .lazy = .{ .byte_abs = byte_abs },
3016 }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])});3016 }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])});
3017 } else if (parse_err.tag == .decl_between_fields) {
3018 try mod.errNoteNonLazy(.{
3019 .file_scope = file,
3020 .parent_decl_node = 0,
3021 .lazy = .{ .byte_abs = token_starts[file.tree.errors[1].token] },
3022 }, err_msg, "field before declarations here", .{});
3023 try mod.errNoteNonLazy(.{
3024 .file_scope = file,
3025 .parent_decl_node = 0,
3026 .lazy = .{ .byte_abs = token_starts[file.tree.errors[2].token] },
3027 }, err_msg, "field after declarations here", .{});
3017 }3028 }
30183029
3019 {3030 {
src/main.zig+80-61
...@@ -3769,9 +3769,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -3769,9 +3769,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
3769 };3769 };
3770 defer tree.deinit(gpa);3770 defer tree.deinit(gpa);
37713771
3772 for (tree.errors) |parse_error| {3772 try printErrsMsgToStdErr(gpa, arena, tree.errors, tree, "<stdin>", color);
3773 try printErrMsgToStdErr(gpa, arena, parse_error, tree, "<stdin>", color);
3774 }
3775 var has_ast_error = false;3773 var has_ast_error = false;
3776 if (check_ast_flag) {3774 if (check_ast_flag) {
3777 const Module = @import("Module.zig");3775 const Module = @import("Module.zig");
...@@ -3959,9 +3957,7 @@ fn fmtPathFile(...@@ -3959,9 +3957,7 @@ fn fmtPathFile(
3959 var tree = try std.zig.parse(fmt.gpa, source_code);3957 var tree = try std.zig.parse(fmt.gpa, source_code);
3960 defer tree.deinit(fmt.gpa);3958 defer tree.deinit(fmt.gpa);
39613959
3962 for (tree.errors) |parse_error| {3960 try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree.errors, tree, file_path, fmt.color);
3963 try printErrMsgToStdErr(fmt.gpa, fmt.arena, parse_error, tree, file_path, fmt.color);
3964 }
3965 if (tree.errors.len != 0) {3961 if (tree.errors.len != 0) {
3966 fmt.any_error = true;3962 fmt.any_error = true;
3967 return;3963 return;
...@@ -4041,66 +4037,95 @@ fn fmtPathFile(...@@ -4041,66 +4037,95 @@ fn fmtPathFile(
4041 }4037 }
4042}4038}
40434039
4044fn printErrMsgToStdErr(4040fn printErrsMsgToStdErr(
4045 gpa: mem.Allocator,4041 gpa: mem.Allocator,
4046 arena: mem.Allocator,4042 arena: mem.Allocator,
4047 parse_error: Ast.Error,4043 parse_errors: []const Ast.Error,
4048 tree: Ast,4044 tree: Ast,
4049 path: []const u8,4045 path: []const u8,
4050 color: Color,4046 color: Color,
4051) !void {4047) !void {
4052 const lok_token = parse_error.token;4048 var i: usize = 0;
4053 const token_tags = tree.tokens.items(.tag);4049 while (i < parse_errors.len) : (i += 1) {
4054 const start_loc = tree.tokenLocation(0, lok_token);4050 const parse_error = parse_errors[i];
4055 const source_line = tree.source[start_loc.line_start..start_loc.line_end];4051 const lok_token = parse_error.token;
40564052 const token_tags = tree.tokens.items(.tag);
4057 var text_buf = std.ArrayList(u8).init(gpa);4053 const start_loc = tree.tokenLocation(0, lok_token);
4058 defer text_buf.deinit();4054 const source_line = tree.source[start_loc.line_start..start_loc.line_end];
4059 const writer = text_buf.writer();4055
4060 try tree.renderError(parse_error, writer);4056 var text_buf = std.ArrayList(u8).init(gpa);
4061 const text = text_buf.items;4057 defer text_buf.deinit();
40624058 const writer = text_buf.writer();
4063 var notes_buffer: [1]Compilation.AllErrors.Message = undefined;4059 try tree.renderError(parse_error, writer);
4064 var notes_len: usize = 0;4060 const text = text_buf.items;
40654061
4066 if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) {4062 var notes_buffer: [2]Compilation.AllErrors.Message = undefined;
4067 const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len);4063 var notes_len: usize = 0;
4068 const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off;4064
4069 notes_buffer[notes_len] = .{4065 if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) {
4066 const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len);
4067 const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off;
4068 notes_buffer[notes_len] = .{
4069 .src = .{
4070 .src_path = path,
4071 .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{
4072 std.zig.fmtEscapes(tree.source[byte_offset..][0..1]),
4073 }),
4074 .byte_offset = byte_offset,
4075 .line = @intCast(u32, start_loc.line),
4076 .column = @intCast(u32, start_loc.column) + bad_off,
4077 .source_line = source_line,
4078 },
4079 };
4080 notes_len += 1;
4081 } else if (parse_error.tag == .decl_between_fields) {
4082 const prev_loc = tree.tokenLocation(0, parse_errors[i + 1].token);
4083 notes_buffer[0] = .{
4084 .src = .{
4085 .src_path = path,
4086 .msg = "field before declarations here",
4087 .byte_offset = @intCast(u32, prev_loc.line_start),
4088 .line = @intCast(u32, prev_loc.line),
4089 .column = @intCast(u32, prev_loc.column),
4090 .source_line = tree.source[prev_loc.line_start..prev_loc.line_end],
4091 },
4092 };
4093 const next_loc = tree.tokenLocation(0, parse_errors[i + 2].token);
4094 notes_buffer[1] = .{
4095 .src = .{
4096 .src_path = path,
4097 .msg = "field after declarations here",
4098 .byte_offset = @intCast(u32, next_loc.line_start),
4099 .line = @intCast(u32, next_loc.line),
4100 .column = @intCast(u32, next_loc.column),
4101 .source_line = tree.source[next_loc.line_start..next_loc.line_end],
4102 },
4103 };
4104 notes_len = 2;
4105 i += 2;
4106 }
4107
4108 const extra_offset = tree.errorOffset(parse_error);
4109 const message: Compilation.AllErrors.Message = .{
4070 .src = .{4110 .src = .{
4071 .src_path = path,4111 .src_path = path,
4072 .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{4112 .msg = text,
4073 std.zig.fmtEscapes(tree.source[byte_offset..][0..1]),4113 .byte_offset = @intCast(u32, start_loc.line_start) + extra_offset,
4074 }),
4075 .byte_offset = byte_offset,
4076 .line = @intCast(u32, start_loc.line),4114 .line = @intCast(u32, start_loc.line),
4077 .column = @intCast(u32, start_loc.column) + bad_off,4115 .column = @intCast(u32, start_loc.column) + extra_offset,
4078 .source_line = source_line,4116 .source_line = source_line,
4117 .notes = notes_buffer[0..notes_len],
4079 },4118 },
4080 };4119 };
4081 notes_len += 1;
4082 }
40834120
4084 const extra_offset = tree.errorOffset(parse_error);4121 const ttyconf: std.debug.TTY.Config = switch (color) {
4085 const message: Compilation.AllErrors.Message = .{4122 .auto => std.debug.detectTTYConfig(),
4086 .src = .{4123 .on => .escape_codes,
4087 .src_path = path,4124 .off => .no_color,
4088 .msg = text,4125 };
4089 .byte_offset = @intCast(u32, start_loc.line_start) + extra_offset,
4090 .line = @intCast(u32, start_loc.line),
4091 .column = @intCast(u32, start_loc.column) + extra_offset,
4092 .source_line = source_line,
4093 .notes = notes_buffer[0..notes_len],
4094 },
4095 };
4096
4097 const ttyconf: std.debug.TTY.Config = switch (color) {
4098 .auto => std.debug.detectTTYConfig(),
4099 .on => .escape_codes,
4100 .off => .no_color,
4101 };
41024126
4103 message.renderToStdErr(ttyconf);4127 message.renderToStdErr(ttyconf);
4128 }
4104}4129}
41054130
4106pub const info_zen =4131pub const info_zen =
...@@ -4658,9 +4683,7 @@ pub fn cmdAstCheck(...@@ -4658,9 +4683,7 @@ pub fn cmdAstCheck(
4658 file.tree_loaded = true;4683 file.tree_loaded = true;
4659 defer file.tree.deinit(gpa);4684 defer file.tree.deinit(gpa);
46604685
4661 for (file.tree.errors) |parse_error| {4686 try printErrsMsgToStdErr(gpa, arena, file.tree.errors, file.tree, file.sub_file_path, color);
4662 try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, file.sub_file_path, color);
4663 }
4664 if (file.tree.errors.len != 0) {4687 if (file.tree.errors.len != 0) {
4665 process.exit(1);4688 process.exit(1);
4666 }4689 }
...@@ -4786,9 +4809,7 @@ pub fn cmdChangelist(...@@ -4786,9 +4809,7 @@ pub fn cmdChangelist(
4786 file.tree_loaded = true;4809 file.tree_loaded = true;
4787 defer file.tree.deinit(gpa);4810 defer file.tree.deinit(gpa);
47884811
4789 for (file.tree.errors) |parse_error| {4812 try printErrsMsgToStdErr(gpa, arena, file.tree.errors, file.tree, old_source_file, .auto);
4790 try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, old_source_file, .auto);
4791 }
4792 if (file.tree.errors.len != 0) {4813 if (file.tree.errors.len != 0) {
4793 process.exit(1);4814 process.exit(1);
4794 }4815 }
...@@ -4825,9 +4846,7 @@ pub fn cmdChangelist(...@@ -4825,9 +4846,7 @@ pub fn cmdChangelist(
4825 var new_tree = try std.zig.parse(gpa, new_source);4846 var new_tree = try std.zig.parse(gpa, new_source);
4826 defer new_tree.deinit(gpa);4847 defer new_tree.deinit(gpa);
48274848
4828 for (new_tree.errors) |parse_error| {4849 try printErrsMsgToStdErr(gpa, arena, new_tree.errors, new_tree, new_source_file, .auto);
4829 try printErrMsgToStdErr(gpa, arena, parse_error, new_tree, new_source_file, .auto);
4830 }
4831 if (new_tree.errors.len != 0) {4850 if (new_tree.errors.len != 0) {
4832 process.exit(1);4851 process.exit(1);
4833 }4852 }
test/compile_errors.zig+2
...@@ -877,6 +877,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -877,6 +877,8 @@ pub fn addCases(ctx: *TestContext) !void {
877 \\}877 \\}
878 , &[_][]const u8{878 , &[_][]const u8{
879 "tmp.zig:6:5: error: declarations are not allowed between container fields",879 "tmp.zig:6:5: error: declarations are not allowed between container fields",
880 "tmp.zig:5:5: note: field before declarations here",
881 "tmp.zig:9:5: note: field after declarations here",
880 });882 });
881883
882 ctx.objErrStage1("non-extern function with var args",884 ctx.objErrStage1("non-extern function with var args",