| author | |
| committer | |
| log | 24c432608f6b07020fa0b18fc9c868ad6abd9b15 |
| tree | f9650d8c9aa36d6fddd45b1f70847304da16c0f5 |
| parent | 3f680abbe2c4d2eeefd0eb73b8af25d1768e6ceb |
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 @@ |
| 6 | const std = @import("std.zig"); | 6 | const std = @import("std.zig"); |
| 7 | const tokenizer = @import("zig/tokenizer.zig"); | 7 | const tokenizer = @import("zig/tokenizer.zig"); |
| 8 | const fmt = @import("zig/fmt.zig"); | 8 | const fmt = @import("zig/fmt.zig"); |
| 9 | const assert = std.debug.assert; | ||
| 9 | 10 | ||
| 10 | pub const Token = tokenizer.Token; | 11 | pub const Token = tokenizer.Token; |
| 11 | pub const Tokenizer = tokenizer.Tokenizer; | 12 | pub 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 | } |
| 185 | 186 | ||
| 187 | pub 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. |
| 188 | pub fn parseCharLiteral( | 209 | pub 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] == '\''); | ||
| 193 | 211 | ||
| 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 | } |
| 270 | 301 | ||
| 271 | test "parseCharLiteral" { | 302 | test "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 | ); | ||
| 283 | 343 | ||
| 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 | } |
| 294 | 385 | ||
| 295 | test { | 386 | test { |
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 | }, |
| 707 | 714 | ||
| 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 | }, |
| 784 | 790 | ||
| 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 | } |
| 1926 | 1931 | ||
| 1927 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Tag) !void { | 1932 | fn 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); |
| 6382 | 6382 | ||
| 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 | } |
| 6398 | 6454 | ||
| 6399 | fn integerLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir.Inst.Ref { | 6455 | fn 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(); |
| 2467 | 2467 | ||
| 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); | ||
| 2469 | 2470 | ||
| 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 | } | ||
| 2480 | 2489 | ||
| 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]; |
| 3385 | 3386 | ||
| ... | @@ -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; |
| 3391 | 3392 | ||
| 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 | }; |
| 3402 | 3422 |
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 | }); |
| 1511 | 1512 | ||
| 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 | }); |
| 1520 | 1522 | ||
| 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 | }); |
| 1529 | 1532 | ||
| 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 | }); |
| 1538 | 1542 | ||
| 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 | }); |
| 1547 | 1552 | ||
| 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 | }); |
| 1556 | 1562 | ||
| 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 | }); |
| 1565 | 1572 | ||
| 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 | }); |
| 1574 | 1582 | ||
| 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 | }); |
| 1583 | 1592 | ||
| 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 | }); |
| 1592 | 1602 | ||
| 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 | }); |
| 1601 | 1612 | ||
| 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 | }); |
| 1610 | 1622 | ||
| 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 | }); |
| 1619 | 1632 | ||
| 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 | }); |
| 1628 | 1642 | ||
| 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 | }); |
| 1637 | 1652 | ||
| 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 | }); |
| 1646 | 1662 | ||
| 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 | }); |
| 1655 | 1672 | ||
| 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 | }); |
| 1664 | 1682 | ||
| 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 | }); |
| 1673 | 1692 | ||
| 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 | }); |
| 7549 | 7569 | ||
| 7550 | ctx.objErrStage1("invalid empty unicode escape", | 7570 | ctx.objErrStage1("invalid empty unicode escape", |