authorgravatar for will.lillis24@gmail.comWill Lillis <will.lillis24@gmail.com> 2025-02-05 04:10:11-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-05 11:10:11+02:00
logcf059ee08716300e924bced08ebdd5bd8f97d789
tree7d948ad7249e7c8ca4a4ae87e319ec219d5e8fc8
parentd72f3d353f771daced78af70c049e3c5075b3529
signaturebadge-check Signed by PGP key B5690EEEBB952194

AstGen: improve error for invalid bytes in strings and comments


8 files changed, 93 insertions(+), 4 deletions(-)

lib/std/zig/Ast.zig+17
...@@ -458,6 +458,19 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {...@@ -458,6 +458,19 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
458 return stream.writeAll("for input is not captured");458 return stream.writeAll("for input is not captured");
459 },459 },
460460
461 .invalid_byte => {
462 const tok_slice = tree.source[tree.tokens.items(.start)[parse_error.token]..];
463 return stream.print("{s} contains invalid byte: '{'}'", .{
464 switch (tok_slice[0]) {
465 '\'' => "character literal",
466 '"', '\\' => "string literal",
467 '/' => "comment",
468 else => unreachable,
469 },
470 std.zig.fmtEscapes(tok_slice[parse_error.extra.offset..][0..1]),
471 });
472 },
473
461 .expected_token => {474 .expected_token => {
462 const found_tag = token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)];475 const found_tag = token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)];
463 const expected_symbol = parse_error.extra.expected_tag.symbol();476 const expected_symbol = parse_error.extra.expected_tag.symbol();
...@@ -2926,6 +2939,7 @@ pub const Error = struct {...@@ -2926,6 +2939,7 @@ pub const Error = struct {
2926 extra: union {2939 extra: union {
2927 none: void,2940 none: void,
2928 expected_tag: Token.Tag,2941 expected_tag: Token.Tag,
2942 offset: usize,
2929 } = .{ .none = {} },2943 } = .{ .none = {} },
29302944
2931 pub const Tag = enum {2945 pub const Tag = enum {
...@@ -2996,6 +3010,9 @@ pub const Error = struct {...@@ -2996,6 +3010,9 @@ pub const Error = struct {
29963010
2997 /// `expected_tag` is populated.3011 /// `expected_tag` is populated.
2998 expected_token,3012 expected_token,
3013
3014 /// `offset` is populated
3015 invalid_byte,
2999 };3016 };
3000};3017};
30013018
lib/std/zig/AstGen.zig+33
...@@ -14017,6 +14017,39 @@ fn lowerAstErrors(astgen: *AstGen) !void {...@@ -14017,6 +14017,39 @@ fn lowerAstErrors(astgen: *AstGen) !void {
14017 var notes: std.ArrayListUnmanaged(u32) = .empty;14017 var notes: std.ArrayListUnmanaged(u32) = .empty;
14018 defer notes.deinit(gpa);14018 defer notes.deinit(gpa);
1401914019
14020 const token_starts = tree.tokens.items(.start);
14021 const token_tags = tree.tokens.items(.tag);
14022 const parse_err = tree.errors[0];
14023 const tok = parse_err.token + @intFromBool(parse_err.token_is_prev);
14024 const tok_start = token_starts[tok];
14025 const start_char = tree.source[tok_start];
14026
14027 if (token_tags[tok] == .invalid and
14028 (start_char == '\"' or start_char == '\'' or start_char == '/' or mem.startsWith(u8, tree.source[tok_start..], "\\\\")))
14029 {
14030 const tok_len: u32 = @intCast(tree.tokenSlice(tok).len);
14031 const tok_end = tok_start + tok_len;
14032 const bad_off = blk: {
14033 var idx = tok_start;
14034 while (idx < tok_end) : (idx += 1) {
14035 switch (tree.source[idx]) {
14036 0x00...0x09, 0x0b...0x1f, 0x7f => break,
14037 else => {},
14038 }
14039 }
14040 break :blk idx - tok_start;
14041 };
14042
14043 const err: Ast.Error = .{
14044 .tag = Ast.Error.Tag.invalid_byte,
14045 .token = tok,
14046 .extra = .{ .offset = bad_off },
14047 };
14048 msg.clearRetainingCapacity();
14049 try tree.renderError(err, msg.writer(gpa));
14050 return try astgen.appendErrorTokNotesOff(tok, bad_off, "{s}", .{msg.items}, notes.items);
14051 }
14052
14020 var cur_err = tree.errors[0];14053 var cur_err = tree.errors[0];
14021 for (tree.errors[1..]) |err| {14054 for (tree.errors[1..]) |err| {
14022 if (err.is_note) {14055 if (err.is_note) {
test/cases/compile_errors/normal_string_with_newline.zig+1-1
...@@ -5,4 +5,4 @@ b";...@@ -5,4 +5,4 @@ b";
5// backend=stage25// backend=stage2
6// target=native6// target=native
7//7//
8// :1:13: error: expected expression, found 'invalid token'8// :1:15: error: string literal contains invalid byte: '\n'
test/cases/compile_errors/tab_inside_comment.zig created+8
...@@ -0,0 +1,8 @@
1// Some comment
2export fn entry() void {}
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:8: error: comment contains invalid byte: '\t'
test/cases/compile_errors/tab_inside_doc_comment.zig created+8
...@@ -0,0 +1,8 @@
1/// Some doc comment
2export fn entry() void {}
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:13: error: comment contains invalid byte: '\t'
test/cases/compile_errors/tab_inside_multiline_string.zig created+13
...@@ -0,0 +1,13 @@
1export fn entry() void {
2 const foo =
3 \\const S = struct {
4 \\ // hello
5 \\}
6 ;
7 _ = foo;
8}
9// error
10// backend=stage2
11// target=native
12//
13// :4:11: error: string literal contains invalid byte: '\t'
test/cases/compile_errors/tab_inside_string.zig created+10
...@@ -0,0 +1,10 @@
1export fn entry() void {
2 const foo = " hello";
3 _ = foo;
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :2:18: error: string literal contains invalid byte: '\t'
test/compile_errors.zig+3-3
...@@ -217,7 +217,7 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {...@@ -217,7 +217,7 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {
217 const case = ctx.obj("invalid byte in string", b.graph.host);217 const case = ctx.obj("invalid byte in string", b.graph.host);
218218
219 case.addError("_ = \"\x01Q\";", &[_][]const u8{219 case.addError("_ = \"\x01Q\";", &[_][]const u8{
220 ":1:5: error: expected expression, found 'invalid token'",220 ":1:6: error: string literal contains invalid byte: '\\x01'",
221 });221 });
222 }222 }
223223
...@@ -225,7 +225,7 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {...@@ -225,7 +225,7 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {
225 const case = ctx.obj("invalid byte in comment", b.graph.host);225 const case = ctx.obj("invalid byte in comment", b.graph.host);
226226
227 case.addError("//\x01Q", &[_][]const u8{227 case.addError("//\x01Q", &[_][]const u8{
228 ":1:1: error: expected type expression, found 'invalid token'",228 ":1:3: error: comment contains invalid byte: '\\x01'",
229 });229 });
230 }230 }
231231
...@@ -233,7 +233,7 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {...@@ -233,7 +233,7 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {
233 const case = ctx.obj("control character in character literal", b.graph.host);233 const case = ctx.obj("control character in character literal", b.graph.host);
234234
235 case.addError("const c = '\x01';", &[_][]const u8{235 case.addError("const c = '\x01';", &[_][]const u8{
236 ":1:11: error: expected expression, found 'invalid token'",236 ":1:12: error: character literal contains invalid byte: '\\x01'",
237 });237 });
238 }238 }
239239