authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-01 00:14:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-02 13:27:35-07:00
log24c432608f6b07020fa0b18fc9c868ad6abd9b15
treef9650d8c9aa36d6fddd45b1f70847304da16c0f5
parent3f680abbe2c4d2eeefd0eb73b8af25d1768e6ceb

stage2: improve compile errors from tokenizer

In order to not regress the quality of compile errors, some improvements had to be made. * std.zig.parseCharLiteral is improved to return more detailed parse failure information. * tokenizer is improved to handle null bytes in the middle of strings, character literals, and line comments. * validating how many unicode escape digits in string literals is moved to std.zig.parseStringLiteral rather than handled in the tokenizer. * when a tokenizer error occurs, if the reported token is the 'invalid' tag, an error note is added to point to the invalid byte location. Further improvements would be: - Mention the expected set of allowed bytes at this location. - Display the invalid byte (if printable, print it, otherwise escape-print it).

6 files changed, 306 insertions(+), 103 deletions(-)

lib/std/zig.zig+147-56
...@@ -6,6 +6,7 @@...@@ -6,6 +6,7 @@
6const std = @import("std.zig");6const std = @import("std.zig");
7const tokenizer = @import("zig/tokenizer.zig");7const tokenizer = @import("zig/tokenizer.zig");
8const fmt = @import("zig/fmt.zig");8const fmt = @import("zig/fmt.zig");
9const assert = std.debug.assert;
910
10pub const Token = tokenizer.Token;11pub const Token = tokenizer.Token;
11pub const Tokenizer = tokenizer.Tokenizer;12pub const Tokenizer = tokenizer.Tokenizer;
...@@ -183,29 +184,48 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro...@@ -183,29 +184,48 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
183 }184 }
184}185}
185186
187pub const ParsedCharLiteral = union(enum) {
188 success: u32,
189 /// The character after backslash is not recognized.
190 invalid_escape_character: usize,
191 /// Expected hex digit at this index.
192 expected_hex_digit: usize,
193 /// Unicode escape sequence had no digits with rbrace at this index.
194 empty_unicode_escape_sequence: usize,
195 /// Expected hex digit or '}' at this index.
196 expected_hex_digit_or_rbrace: usize,
197 /// The unicode point is outside the range of Unicode codepoints.
198 unicode_escape_overflow: usize,
199 /// Expected '{' at this index.
200 expected_lbrace: usize,
201 /// Expected the terminating single quote at this index.
202 expected_end: usize,
203 /// The character at this index cannot be represented without an escape sequence.
204 invalid_character: usize,
205};
206
186/// Only validates escape sequence characters.207/// Only validates escape sequence characters.
187/// Slice must be valid utf8 starting and ending with "'" and exactly one codepoint in between.208/// Slice must be valid utf8 starting and ending with "'" and exactly one codepoint in between.
188pub fn parseCharLiteral(209pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral {
189 slice: []const u8,210 assert(slice.len >= 3 and slice[0] == '\'' and slice[slice.len - 1] == '\'');
190 bad_index: *usize, // populated if error.InvalidCharacter is returned
191) error{InvalidCharacter}!u32 {
192 std.debug.assert(slice.len >= 3 and slice[0] == '\'' and slice[slice.len - 1] == '\'');
193211
194 if (slice[1] == '\\') {212 switch (slice[1]) {
195 switch (slice[2]) {213 0 => return .{ .invalid_character = 1 },
196 'n' => return '\n',214 '\\' => switch (slice[2]) {
197 'r' => return '\r',215 'n' => return .{ .success = '\n' },
198 '\\' => return '\\',216 'r' => return .{ .success = '\r' },
199 't' => return '\t',217 '\\' => return .{ .success = '\\' },
200 '\'' => return '\'',218 't' => return .{ .success = '\t' },
201 '"' => return '"',219 '\'' => return .{ .success = '\'' },
220 '"' => return .{ .success = '"' },
202 'x' => {221 'x' => {
203 if (slice.len != 6) {222 if (slice.len < 4) {
204 bad_index.* = slice.len - 2;223 return .{ .expected_hex_digit = 3 };
205 return error.InvalidCharacter;
206 }224 }
207 var value: u32 = 0;225 var value: u32 = 0;
208 for (slice[3..5]) |c, i| {226 var i: usize = 3;
227 while (i < 5) : (i += 1) {
228 const c = slice[i];
209 switch (c) {229 switch (c) {
210 '0'...'9' => {230 '0'...'9' => {
211 value *= 16;231 value *= 16;
...@@ -220,20 +240,28 @@ pub fn parseCharLiteral(...@@ -220,20 +240,28 @@ pub fn parseCharLiteral(
220 value += c - 'A' + 10;240 value += c - 'A' + 10;
221 },241 },
222 else => {242 else => {
223 bad_index.* = 3 + i;243 return .{ .expected_hex_digit = i };
224 return error.InvalidCharacter;
225 },244 },
226 }245 }
227 }246 }
228 return value;247 if (slice[i] != '\'') {
248 return .{ .expected_end = i };
249 }
250 return .{ .success = value };
229 },251 },
230 'u' => {252 'u' => {
231 if (slice.len < "'\\u{0}'".len or slice[3] != '{' or slice[slice.len - 2] != '}') {253 var i: usize = 3;
232 bad_index.* = 2;254 if (slice[i] != '{') {
233 return error.InvalidCharacter;255 return .{ .expected_lbrace = i };
234 }256 }
257 i += 1;
258 if (slice[i] == '}') {
259 return .{ .empty_unicode_escape_sequence = i };
260 }
261
235 var value: u32 = 0;262 var value: u32 = 0;
236 for (slice[4 .. slice.len - 2]) |c, i| {263 while (i < slice.len) : (i += 1) {
264 const c = slice[i];
237 switch (c) {265 switch (c) {
238 '0'...'9' => {266 '0'...'9' => {
239 value *= 16;267 value *= 16;
...@@ -247,49 +275,112 @@ pub fn parseCharLiteral(...@@ -247,49 +275,112 @@ pub fn parseCharLiteral(
247 value *= 16;275 value *= 16;
248 value += c - 'A' + 10;276 value += c - 'A' + 10;
249 },277 },
250 else => {278 '}' => {
251 bad_index.* = 4 + i;279 i += 1;
252 return error.InvalidCharacter;280 break;
253 },281 },
282 else => return .{ .expected_hex_digit_or_rbrace = i },
254 }283 }
255 if (value > 0x10ffff) {284 if (value > 0x10ffff) {
256 bad_index.* = 4 + i;285 return .{ .unicode_escape_overflow = i };
257 return error.InvalidCharacter;
258 }286 }
259 }287 }
260 return value;288 if (slice[i] != '\'') {
261 },289 return .{ .expected_end = i };
262 else => {290 }
263 bad_index.* = 2;291 return .{ .success = value };
264 return error.InvalidCharacter;
265 },292 },
266 }293 else => return .{ .invalid_escape_character = 2 },
294 },
295 else => {
296 const codepoint = std.unicode.utf8Decode(slice[1 .. slice.len - 1]) catch unreachable;
297 return .{ .success = codepoint };
298 },
267 }299 }
268 return std.unicode.utf8Decode(slice[1 .. slice.len - 1]) catch unreachable;
269}300}
270301
271test "parseCharLiteral" {302test "parseCharLiteral" {
272 var bad_index: usize = undefined;303 try std.testing.expectEqual(
273 try std.testing.expectEqual(try parseCharLiteral("'a'", &bad_index), 'a');304 ParsedCharLiteral{ .success = 'a' },
274 try std.testing.expectEqual(try parseCharLiteral("'ä'", &bad_index), 'ä');305 parseCharLiteral("'a'"),
275 try std.testing.expectEqual(try parseCharLiteral("'\\x00'", &bad_index), 0);306 );
276 try std.testing.expectEqual(try parseCharLiteral("'\\x4f'", &bad_index), 0x4f);307 try std.testing.expectEqual(
277 try std.testing.expectEqual(try parseCharLiteral("'\\x4F'", &bad_index), 0x4f);308 ParsedCharLiteral{ .success = 'ä' },
278 try std.testing.expectEqual(try parseCharLiteral("'ぁ'", &bad_index), 0x3041);309 parseCharLiteral("'ä'"),
279 try std.testing.expectEqual(try parseCharLiteral("'\\u{0}'", &bad_index), 0);310 );
280 try std.testing.expectEqual(try parseCharLiteral("'\\u{3041}'", &bad_index), 0x3041);311 try std.testing.expectEqual(
281 try std.testing.expectEqual(try parseCharLiteral("'\\u{7f}'", &bad_index), 0x7f);312 ParsedCharLiteral{ .success = 0 },
282 try std.testing.expectEqual(try parseCharLiteral("'\\u{7FFF}'", &bad_index), 0x7FFF);313 parseCharLiteral("'\\x00'"),
314 );
315 try std.testing.expectEqual(
316 ParsedCharLiteral{ .success = 0x4f },
317 parseCharLiteral("'\\x4f'"),
318 );
319 try std.testing.expectEqual(
320 ParsedCharLiteral{ .success = 0x4f },
321 parseCharLiteral("'\\x4F'"),
322 );
323 try std.testing.expectEqual(
324 ParsedCharLiteral{ .success = 0x3041 },
325 parseCharLiteral("'ぁ'"),
326 );
327 try std.testing.expectEqual(
328 ParsedCharLiteral{ .success = 0 },
329 parseCharLiteral("'\\u{0}'"),
330 );
331 try std.testing.expectEqual(
332 ParsedCharLiteral{ .success = 0x3041 },
333 parseCharLiteral("'\\u{3041}'"),
334 );
335 try std.testing.expectEqual(
336 ParsedCharLiteral{ .success = 0x7f },
337 parseCharLiteral("'\\u{7f}'"),
338 );
339 try std.testing.expectEqual(
340 ParsedCharLiteral{ .success = 0x7fff },
341 parseCharLiteral("'\\u{7FFF}'"),
342 );
283343
284 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\x0'", &bad_index));344 try std.testing.expectEqual(
285 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\x000'", &bad_index));345 ParsedCharLiteral{ .expected_hex_digit = 4 },
286 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\y'", &bad_index));346 parseCharLiteral("'\\x0'"),
287 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u'", &bad_index));347 );
288 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\uFFFF'", &bad_index));348 try std.testing.expectEqual(
289 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{}'", &bad_index));349 ParsedCharLiteral{ .expected_end = 5 },
290 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFFFF}'", &bad_index));350 parseCharLiteral("'\\x000'"),
291 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF'", &bad_index));351 );
292 try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF}x'", &bad_index));352 try std.testing.expectEqual(
353 ParsedCharLiteral{ .invalid_escape_character = 2 },
354 parseCharLiteral("'\\y'"),
355 );
356 try std.testing.expectEqual(
357 ParsedCharLiteral{ .expected_lbrace = 3 },
358 parseCharLiteral("'\\u'"),
359 );
360 try std.testing.expectEqual(
361 ParsedCharLiteral{ .expected_lbrace = 3 },
362 parseCharLiteral("'\\uFFFF'"),
363 );
364 try std.testing.expectEqual(
365 ParsedCharLiteral{ .empty_unicode_escape_sequence = 4 },
366 parseCharLiteral("'\\u{}'"),
367 );
368 try std.testing.expectEqual(
369 ParsedCharLiteral{ .unicode_escape_overflow = 9 },
370 parseCharLiteral("'\\u{FFFFFF}'"),
371 );
372 try std.testing.expectEqual(
373 ParsedCharLiteral{ .expected_hex_digit_or_rbrace = 8 },
374 parseCharLiteral("'\\u{FFFF'"),
375 );
376 try std.testing.expectEqual(
377 ParsedCharLiteral{ .expected_end = 9 },
378 parseCharLiteral("'\\u{FFFF}x'"),
379 );
380 try std.testing.expectEqual(
381 ParsedCharLiteral{ .invalid_character = 1 },
382 parseCharLiteral("'\x00'"),
383 );
293}384}
294385
295test {386test {
lib/std/zig/tokenizer.zig+25-18
...@@ -701,12 +701,19 @@ pub const Tokenizer = struct {...@@ -701,12 +701,19 @@ pub const Tokenizer = struct {
701 self.index += 1;701 self.index += 1;
702 break;702 break;
703 },703 },
704 0, '\n', '\r' => break, // Look for this error later.704 0 => {
705 if (self.index == self.buffer.len) {
706 break;
707 } else {
708 self.checkLiteralCharacter();
709 }
710 },
711 '\n', '\r' => break, // Look for this error later.
705 else => self.checkLiteralCharacter(),712 else => self.checkLiteralCharacter(),
706 },713 },
707714
708 .string_literal_backslash => switch (c) {715 .string_literal_backslash => switch (c) {
709 0, '\n', '\r' => break, // Look for this error later.716 '\n', '\r' => break, // Look for this error later.
710 else => {717 else => {
711 state = .string_literal;718 state = .string_literal;
712 },719 },
...@@ -774,7 +781,6 @@ pub const Tokenizer = struct {...@@ -774,7 +781,6 @@ pub const Tokenizer = struct {
774 .char_literal_unicode_escape_saw_u => switch (c) {781 .char_literal_unicode_escape_saw_u => switch (c) {
775 '{' => {782 '{' => {
776 state = .char_literal_unicode_escape;783 state = .char_literal_unicode_escape;
777 seen_escape_digits = 0;
778 },784 },
779 else => {785 else => {
780 result.tag = .invalid;786 result.tag = .invalid;
...@@ -783,16 +789,9 @@ pub const Tokenizer = struct {...@@ -783,16 +789,9 @@ pub const Tokenizer = struct {
783 },789 },
784790
785 .char_literal_unicode_escape => switch (c) {791 .char_literal_unicode_escape => switch (c) {
786 '0'...'9', 'a'...'f', 'A'...'F' => {792 '0'...'9', 'a'...'f', 'A'...'F' => {},
787 seen_escape_digits += 1;
788 },
789 '}' => {793 '}' => {
790 if (seen_escape_digits == 0) {794 state = .char_literal_end; // too many/few digits handled later
791 result.tag = .invalid;
792 state = .char_literal_unicode_invalid;
793 } else {
794 state = .char_literal_end;
795 }
796 },795 },
797 else => {796 else => {
798 result.tag = .invalid;797 result.tag = .invalid;
...@@ -1026,7 +1025,13 @@ pub const Tokenizer = struct {...@@ -1026,7 +1025,13 @@ pub const Tokenizer = struct {
1026 },1025 },
1027 },1026 },
1028 .line_comment_start => switch (c) {1027 .line_comment_start => switch (c) {
1029 0 => break,1028 0 => {
1029 if (self.index != self.buffer.len) {
1030 result.tag = .invalid;
1031 self.index += 1;
1032 }
1033 break;
1034 },
1030 '/' => {1035 '/' => {
1031 state = .doc_comment_start;1036 state = .doc_comment_start;
1032 },1037 },
...@@ -1441,7 +1446,7 @@ test "tokenizer - code point literal with unicode escapes" {...@@ -1441,7 +1446,7 @@ test "tokenizer - code point literal with unicode escapes" {
1441 , &.{ .invalid, .invalid });1446 , &.{ .invalid, .invalid });
1442 try testTokenize(1447 try testTokenize(
1443 \\'\u{}'1448 \\'\u{}'
1444 , &.{ .invalid, .invalid });1449 , &.{.char_literal});
1445 try testTokenize(1450 try testTokenize(
1446 \\'\u{s}'1451 \\'\u{s}'
1447 , &.{ .invalid, .invalid });1452 , &.{ .invalid, .invalid });
...@@ -1924,15 +1929,17 @@ test "tokenizer - invalid builtin identifiers" {...@@ -1924,15 +1929,17 @@ test "tokenizer - invalid builtin identifiers" {
1924 try testTokenize("@0()", &.{ .invalid, .integer_literal, .l_paren, .r_paren });1929 try testTokenize("@0()", &.{ .invalid, .integer_literal, .l_paren, .r_paren });
1925}1930}
19261931
1927fn testTokenize(source: []const u8, expected_tokens: []const Token.Tag) !void {1932fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {
1928 var tokenizer = Tokenizer.init(source);1933 var tokenizer = Tokenizer.init(source);
1929 for (expected_tokens) |expected_token_id| {1934 for (expected_tokens) |expected_token_id| {
1930 const token = tokenizer.next();1935 const token = tokenizer.next();
1931 if (token.tag != expected_token_id) {1936 if (token.tag != expected_token_id) {
1932 std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.tag) });1937 std.debug.panic("expected {s}, found {s}\n", .{
1938 @tagName(expected_token_id), @tagName(token.tag),
1939 });
1933 }1940 }
1934 }1941 }
1935 const last_token = tokenizer.next();1942 const last_token = tokenizer.next();
1936 try std.testing.expect(last_token.tag == .eof);1943 try std.testing.expectEqual(Token.Tag.eof, last_token.tag);
1937 try std.testing.expect(last_token.loc.start == source.len);1944 try std.testing.expectEqual(source.len, last_token.loc.start);
1938}1945}
src/AstGen.zig+65-9
...@@ -6380,20 +6380,76 @@ fn charLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) !Zir.Inst.Ref {...@@ -6380,20 +6380,76 @@ fn charLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) !Zir.Inst.Ref {
6380 const main_token = main_tokens[node];6380 const main_token = main_tokens[node];
6381 const slice = tree.tokenSlice(main_token);6381 const slice = tree.tokenSlice(main_token);
63826382
6383 var bad_index: usize = undefined;6383 switch (std.zig.parseCharLiteral(slice)) {
6384 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {6384 .success => |codepoint| {
6385 error.InvalidCharacter => {6385 const result = try gz.addInt(codepoint);
6386 const bad_byte = slice[bad_index];6386 return rvalue(gz, rl, result, node);
6387 },
6388 .invalid_escape_character => |bad_index| {
6387 return astgen.failOff(6389 return astgen.failOff(
6388 main_token,6390 main_token,
6389 @intCast(u32, bad_index),6391 @intCast(u32, bad_index),
6390 "invalid character: '{c}'\n",6392 "invalid escape character: '{c}'",
6391 .{bad_byte},6393 .{slice[bad_index]},
6392 );6394 );
6393 },6395 },
6394 };6396 .expected_hex_digit => |bad_index| {
6395 const result = try gz.addInt(value);6397 return astgen.failOff(
6396 return rvalue(gz, rl, result, node);6398 main_token,
6399 @intCast(u32, bad_index),
6400 "expected hex digit, found '{c}'",
6401 .{slice[bad_index]},
6402 );
6403 },
6404 .empty_unicode_escape_sequence => |bad_index| {
6405 return astgen.failOff(
6406 main_token,
6407 @intCast(u32, bad_index),
6408 "empty unicode escape sequence",
6409 .{},
6410 );
6411 },
6412 .expected_hex_digit_or_rbrace => |bad_index| {
6413 return astgen.failOff(
6414 main_token,
6415 @intCast(u32, bad_index),
6416 "expected hex digit or '}}', found '{c}'",
6417 .{slice[bad_index]},
6418 );
6419 },
6420 .unicode_escape_overflow => |bad_index| {
6421 return astgen.failOff(
6422 main_token,
6423 @intCast(u32, bad_index),
6424 "unicode escape too large to be a valid codepoint",
6425 .{},
6426 );
6427 },
6428 .expected_lbrace => |bad_index| {
6429 return astgen.failOff(
6430 main_token,
6431 @intCast(u32, bad_index),
6432 "expected '{{', found '{c}",
6433 .{slice[bad_index]},
6434 );
6435 },
6436 .expected_end => |bad_index| {
6437 return astgen.failOff(
6438 main_token,
6439 @intCast(u32, bad_index),
6440 "expected ending single quote ('), found '{c}",
6441 .{slice[bad_index]},
6442 );
6443 },
6444 .invalid_character => |bad_index| {
6445 return astgen.failOff(
6446 main_token,
6447 @intCast(u32, bad_index),
6448 "invalid byte in character literal: '{c}'",
6449 .{slice[bad_index]},
6450 );
6451 },
6452 }
6397}6453}
63986454
6399fn integerLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir.Inst.Ref {6455fn integerLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir.Inst.Ref {
src/Module.zig+9
...@@ -2466,6 +2466,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {...@@ -2466,6 +2466,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {
2466 defer msg.deinit();2466 defer msg.deinit();
24672467
2468 const token_starts = file.tree.tokens.items(.start);2468 const token_starts = file.tree.tokens.items(.start);
2469 const token_tags = file.tree.tokens.items(.tag);
24692470
2470 try file.tree.renderError(parse_err, msg.writer());2471 try file.tree.renderError(parse_err, msg.writer());
2471 const err_msg = try gpa.create(ErrorMsg);2472 const err_msg = try gpa.create(ErrorMsg);
...@@ -2477,6 +2478,14 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {...@@ -2477,6 +2478,14 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {
2477 },2478 },
2478 .msg = msg.toOwnedSlice(),2479 .msg = msg.toOwnedSlice(),
2479 };2480 };
2481 if (token_tags[parse_err.token] == .invalid) {
2482 const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token).len);
2483 try mod.errNoteNonLazy(.{
2484 .file_scope = file,
2485 .parent_decl_node = 0,
2486 .lazy = .{ .byte_abs = token_starts[parse_err.token] + bad_off },
2487 }, err_msg, "invalid byte here", .{});
2488 }
24802489
2481 {2490 {
2482 const lock = comp.mutex.acquire();2491 const lock = comp.mutex.acquire();
src/main.zig+20
...@@ -3380,6 +3380,7 @@ fn printErrMsgToStdErr(...@@ -3380,6 +3380,7 @@ fn printErrMsgToStdErr(
3380 color: Color,3380 color: Color,
3381) !void {3381) !void {
3382 const lok_token = parse_error.token;3382 const lok_token = parse_error.token;
3383 const token_tags = tree.tokens.items(.tag);
3383 const start_loc = tree.tokenLocation(0, lok_token);3384 const start_loc = tree.tokenLocation(0, lok_token);
3384 const source_line = tree.source[start_loc.line_start..start_loc.line_end];3385 const source_line = tree.source[start_loc.line_start..start_loc.line_end];
33853386
...@@ -3389,6 +3390,24 @@ fn printErrMsgToStdErr(...@@ -3389,6 +3390,24 @@ fn printErrMsgToStdErr(
3389 try tree.renderError(parse_error, writer);3390 try tree.renderError(parse_error, writer);
3390 const text = text_buf.items;3391 const text = text_buf.items;
33913392
3393 var notes_buffer: [1]Compilation.AllErrors.Message = undefined;
3394 var notes_len: usize = 0;
3395
3396 if (token_tags[parse_error.token] == .invalid) {
3397 const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token).len);
3398 notes_buffer[notes_len] = .{
3399 .src = .{
3400 .src_path = path,
3401 .msg = "invalid byte here",
3402 .byte_offset = @intCast(u32, start_loc.line_start) + bad_off,
3403 .line = @intCast(u32, start_loc.line),
3404 .column = @intCast(u32, start_loc.column) + bad_off,
3405 .source_line = source_line,
3406 },
3407 };
3408 notes_len += 1;
3409 }
3410
3392 const message: Compilation.AllErrors.Message = .{3411 const message: Compilation.AllErrors.Message = .{
3393 .src = .{3412 .src = .{
3394 .src_path = path,3413 .src_path = path,
...@@ -3397,6 +3416,7 @@ fn printErrMsgToStdErr(...@@ -3397,6 +3416,7 @@ fn printErrMsgToStdErr(
3397 .line = @intCast(u32, start_loc.line),3416 .line = @intCast(u32, start_loc.line),
3398 .column = @intCast(u32, start_loc.column),3417 .column = @intCast(u32, start_loc.column),
3399 .source_line = source_line,3418 .source_line = source_line,
3419 .notes = notes_buffer[0..notes_len],
3400 },3420 },
3401 };3421 };
34023422
test/compile_errors.zig+40-20
...@@ -1506,7 +1506,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1506,7 +1506,8 @@ pub fn addCases(ctx: *TestContext) !void {
1506 \\ _ = bad;1506 \\ _ = bad;
1507 \\}1507 \\}
1508 , &[_][]const u8{1508 , &[_][]const u8{
1509 "tmp.zig:2:28: error: invalid character: 'a'",1509 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1510 "tmp.zig:2:28: note: invalid byte here",
1510 });1511 });
15111512
1512 ctx.objErrStage1("invalid exponent in float literal - 2",1513 ctx.objErrStage1("invalid exponent in float literal - 2",
...@@ -1515,7 +1516,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1515,7 +1516,8 @@ pub fn addCases(ctx: *TestContext) !void {
1515 \\ _ = bad;1516 \\ _ = bad;
1516 \\}1517 \\}
1517 , &[_][]const u8{1518 , &[_][]const u8{
1518 "tmp.zig:2:29: error: invalid character: 'F'",1519 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1520 "tmp.zig:2:29: note: invalid byte here",
1519 });1521 });
15201522
1521 ctx.objErrStage1("invalid underscore placement in float literal - 1",1523 ctx.objErrStage1("invalid underscore placement in float literal - 1",
...@@ -1524,7 +1526,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1524,7 +1526,8 @@ pub fn addCases(ctx: *TestContext) !void {
1524 \\ _ = bad;1526 \\ _ = bad;
1525 \\}1527 \\}
1526 , &[_][]const u8{1528 , &[_][]const u8{
1527 "tmp.zig:2:23: error: invalid character: '_'",1529 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1530 "tmp.zig:2:23: note: invalid byte here",
1528 });1531 });
15291532
1530 ctx.objErrStage1("invalid underscore placement in float literal - 2",1533 ctx.objErrStage1("invalid underscore placement in float literal - 2",
...@@ -1533,7 +1536,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1533,7 +1536,8 @@ pub fn addCases(ctx: *TestContext) !void {
1533 \\ _ = bad;1536 \\ _ = bad;
1534 \\}1537 \\}
1535 , &[_][]const u8{1538 , &[_][]const u8{
1536 "tmp.zig:2:23: error: invalid character: '.'",1539 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1540 "tmp.zig:2:23: note: invalid byte here",
1537 });1541 });
15381542
1539 ctx.objErrStage1("invalid underscore placement in float literal - 3",1543 ctx.objErrStage1("invalid underscore placement in float literal - 3",
...@@ -1542,7 +1546,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1542,7 +1546,8 @@ pub fn addCases(ctx: *TestContext) !void {
1542 \\ _ = bad;1546 \\ _ = bad;
1543 \\}1547 \\}
1544 , &[_][]const u8{1548 , &[_][]const u8{
1545 "tmp.zig:2:25: error: invalid character: ';'",1549 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1550 "tmp.zig:2:25: note: invalid byte here",
1546 });1551 });
15471552
1548 ctx.objErrStage1("invalid underscore placement in float literal - 4",1553 ctx.objErrStage1("invalid underscore placement in float literal - 4",
...@@ -1551,7 +1556,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1551,7 +1556,8 @@ pub fn addCases(ctx: *TestContext) !void {
1551 \\ _ = bad;1556 \\ _ = bad;
1552 \\}1557 \\}
1553 , &[_][]const u8{1558 , &[_][]const u8{
1554 "tmp.zig:2:25: error: invalid character: '_'",1559 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1560 "tmp.zig:2:25: note: invalid byte here",
1555 });1561 });
15561562
1557 ctx.objErrStage1("invalid underscore placement in float literal - 5",1563 ctx.objErrStage1("invalid underscore placement in float literal - 5",
...@@ -1560,7 +1566,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1560,7 +1566,8 @@ pub fn addCases(ctx: *TestContext) !void {
1560 \\ _ = bad;1566 \\ _ = bad;
1561 \\}1567 \\}
1562 , &[_][]const u8{1568 , &[_][]const u8{
1563 "tmp.zig:2:26: error: invalid character: '_'",1569 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1570 "tmp.zig:2:26: note: invalid byte here",
1564 });1571 });
15651572
1566 ctx.objErrStage1("invalid underscore placement in float literal - 6",1573 ctx.objErrStage1("invalid underscore placement in float literal - 6",
...@@ -1569,7 +1576,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1569,7 +1576,8 @@ pub fn addCases(ctx: *TestContext) !void {
1569 \\ _ = bad;1576 \\ _ = bad;
1570 \\}1577 \\}
1571 , &[_][]const u8{1578 , &[_][]const u8{
1572 "tmp.zig:2:26: error: invalid character: '_'",1579 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1580 "tmp.zig:2:26: note: invalid byte here",
1573 });1581 });
15741582
1575 ctx.objErrStage1("invalid underscore placement in float literal - 7",1583 ctx.objErrStage1("invalid underscore placement in float literal - 7",
...@@ -1578,7 +1586,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1578,7 +1586,8 @@ pub fn addCases(ctx: *TestContext) !void {
1578 \\ _ = bad;1586 \\ _ = bad;
1579 \\}1587 \\}
1580 , &[_][]const u8{1588 , &[_][]const u8{
1581 "tmp.zig:2:28: error: invalid character: ';'",1589 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1590 "tmp.zig:2:28: note: invalid byte here",
1582 });1591 });
15831592
1584 ctx.objErrStage1("invalid underscore placement in float literal - 9",1593 ctx.objErrStage1("invalid underscore placement in float literal - 9",
...@@ -1587,7 +1596,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1587,7 +1596,8 @@ pub fn addCases(ctx: *TestContext) !void {
1587 \\ _ = bad;1596 \\ _ = bad;
1588 \\}1597 \\}
1589 , &[_][]const u8{1598 , &[_][]const u8{
1590 "tmp.zig:2:23: error: invalid character: '_'",1599 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1600 "tmp.zig:2:23: note: invalid byte here",
1591 });1601 });
15921602
1593 ctx.objErrStage1("invalid underscore placement in float literal - 10",1603 ctx.objErrStage1("invalid underscore placement in float literal - 10",
...@@ -1596,7 +1606,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1596,7 +1606,8 @@ pub fn addCases(ctx: *TestContext) !void {
1596 \\ _ = bad;1606 \\ _ = bad;
1597 \\}1607 \\}
1598 , &[_][]const u8{1608 , &[_][]const u8{
1599 "tmp.zig:2:25: error: invalid character: '_'",1609 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1610 "tmp.zig:2:25: note: invalid byte here",
1600 });1611 });
16011612
1602 ctx.objErrStage1("invalid underscore placement in float literal - 11",1613 ctx.objErrStage1("invalid underscore placement in float literal - 11",
...@@ -1605,7 +1616,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1605,7 +1616,8 @@ pub fn addCases(ctx: *TestContext) !void {
1605 \\ _ = bad;1616 \\ _ = bad;
1606 \\}1617 \\}
1607 , &[_][]const u8{1618 , &[_][]const u8{
1608 "tmp.zig:2:28: error: invalid character: '_'",1619 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1620 "tmp.zig:2:28: note: invalid byte here",
1609 });1621 });
16101622
1611 ctx.objErrStage1("invalid underscore placement in float literal - 12",1623 ctx.objErrStage1("invalid underscore placement in float literal - 12",
...@@ -1614,7 +1626,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1614,7 +1626,8 @@ pub fn addCases(ctx: *TestContext) !void {
1614 \\ _ = bad;1626 \\ _ = bad;
1615 \\}1627 \\}
1616 , &[_][]const u8{1628 , &[_][]const u8{
1617 "tmp.zig:2:23: error: invalid character: 'x'",1629 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1630 "tmp.zig:2:23: note: invalid byte here",
1618 });1631 });
16191632
1620 ctx.objErrStage1("invalid underscore placement in float literal - 13",1633 ctx.objErrStage1("invalid underscore placement in float literal - 13",
...@@ -1623,7 +1636,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1623,7 +1636,8 @@ pub fn addCases(ctx: *TestContext) !void {
1623 \\ _ = bad;1636 \\ _ = bad;
1624 \\}1637 \\}
1625 , &[_][]const u8{1638 , &[_][]const u8{
1626 "tmp.zig:2:23: error: invalid character: '_'",1639 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1640 "tmp.zig:2:23: note: invalid byte here",
1627 });1641 });
16281642
1629 ctx.objErrStage1("invalid underscore placement in float literal - 14",1643 ctx.objErrStage1("invalid underscore placement in float literal - 14",
...@@ -1632,7 +1646,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1632,7 +1646,8 @@ pub fn addCases(ctx: *TestContext) !void {
1632 \\ _ = bad;1646 \\ _ = bad;
1633 \\}1647 \\}
1634 , &[_][]const u8{1648 , &[_][]const u8{
1635 "tmp.zig:2:27: error: invalid character: 'p'",1649 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1650 "tmp.zig:2:27: note: invalid byte here",
1636 });1651 });
16371652
1638 ctx.objErrStage1("invalid underscore placement in int literal - 1",1653 ctx.objErrStage1("invalid underscore placement in int literal - 1",
...@@ -1641,7 +1656,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1641,7 +1656,8 @@ pub fn addCases(ctx: *TestContext) !void {
1641 \\ _ = bad;1656 \\ _ = bad;
1642 \\}1657 \\}
1643 , &[_][]const u8{1658 , &[_][]const u8{
1644 "tmp.zig:2:26: error: invalid character: ';'",1659 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1660 "tmp.zig:2:26: note: invalid byte here",
1645 });1661 });
16461662
1647 ctx.objErrStage1("invalid underscore placement in int literal - 2",1663 ctx.objErrStage1("invalid underscore placement in int literal - 2",
...@@ -1650,7 +1666,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1650,7 +1666,8 @@ pub fn addCases(ctx: *TestContext) !void {
1650 \\ _ = bad;1666 \\ _ = bad;
1651 \\}1667 \\}
1652 , &[_][]const u8{1668 , &[_][]const u8{
1653 "tmp.zig:2:28: error: invalid character: ';'",1669 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1670 "tmp.zig:2:28: note: invalid byte here",
1654 });1671 });
16551672
1656 ctx.objErrStage1("invalid underscore placement in int literal - 3",1673 ctx.objErrStage1("invalid underscore placement in int literal - 3",
...@@ -1659,7 +1676,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1659,7 +1676,8 @@ pub fn addCases(ctx: *TestContext) !void {
1659 \\ _ = bad;1676 \\ _ = bad;
1660 \\}1677 \\}
1661 , &[_][]const u8{1678 , &[_][]const u8{
1662 "tmp.zig:2:28: error: invalid character: ';'",1679 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1680 "tmp.zig:2:28: note: invalid byte here",
1663 });1681 });
16641682
1665 ctx.objErrStage1("invalid underscore placement in int literal - 4",1683 ctx.objErrStage1("invalid underscore placement in int literal - 4",
...@@ -1668,7 +1686,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1668,7 +1686,8 @@ pub fn addCases(ctx: *TestContext) !void {
1668 \\ _ = bad;1686 \\ _ = bad;
1669 \\}1687 \\}
1670 , &[_][]const u8{1688 , &[_][]const u8{
1671 "tmp.zig:2:28: error: invalid character: ';'",1689 "tmp.zig:2:21: error: expected expression, found 'invalid'",
1690 "tmp.zig:2:28: note: invalid byte here",
1672 });1691 });
16731692
1674 ctx.objErrStage1("comptime struct field, no init value",1693 ctx.objErrStage1("comptime struct field, no init value",
...@@ -7544,7 +7563,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -7544,7 +7563,8 @@ pub fn addCases(ctx: *TestContext) !void {
7544 \\ const a = '\U1234';7563 \\ const a = '\U1234';
7545 \\}7564 \\}
7546 , &[_][]const u8{7565 , &[_][]const u8{
7547 "tmp.zig:2:17: error: invalid character: 'U'",7566 "tmp.zig:2:15: error: expected expression, found 'invalid'",
7567 "tmp.zig:2:18: note: invalid byte here",
7548 });7568 });
75497569
7550 ctx.objErrStage1("invalid empty unicode escape",7570 ctx.objErrStage1("invalid empty unicode escape",