| 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 | const std = @import("std.zig"); |
| 7 | 7 | const tokenizer = @import("zig/tokenizer.zig"); |
| 8 | 8 | const fmt = @import("zig/fmt.zig"); |
| 9 | const assert = std.debug.assert; | |
| 9 | 10 | |
| 10 | 11 | pub const Token = tokenizer.Token; |
| 11 | 12 | pub const Tokenizer = tokenizer.Tokenizer; |
| ... | ... | @@ -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 | 207 | /// Only validates escape sequence characters. |
| 187 | 208 | /// Slice must be valid utf8 starting and ending with "'" and exactly one codepoint in between. |
| 188 | pub fn parseCharLiteral( | |
| 189 | slice: []const u8, | |
| 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] == '\''); | |
| 209 | pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral { | |
| 210 | assert(slice.len >= 3 and slice[0] == '\'' and slice[slice.len - 1] == '\''); | |
| 193 | 211 | |
| 194 | if (slice[1] == '\\') { | |
| 195 | switch (slice[2]) { | |
| 196 | 'n' => return '\n', | |
| 197 | 'r' => return '\r', | |
| 198 | '\\' => return '\\', | |
| 199 | 't' => return '\t', | |
| 200 | '\'' => return '\'', | |
| 201 | '"' => return '"', | |
| 212 | switch (slice[1]) { | |
| 213 | 0 => return .{ .invalid_character = 1 }, | |
| 214 | '\\' => switch (slice[2]) { | |
| 215 | 'n' => return .{ .success = '\n' }, | |
| 216 | 'r' => return .{ .success = '\r' }, | |
| 217 | '\\' => return .{ .success = '\\' }, | |
| 218 | 't' => return .{ .success = '\t' }, | |
| 219 | '\'' => return .{ .success = '\'' }, | |
| 220 | '"' => return .{ .success = '"' }, | |
| 202 | 221 | 'x' => { |
| 203 | if (slice.len != 6) { | |
| 204 | bad_index.* = slice.len - 2; | |
| 205 | return error.InvalidCharacter; | |
| 222 | if (slice.len < 4) { | |
| 223 | return .{ .expected_hex_digit = 3 }; | |
| 206 | 224 | } |
| 207 | 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 | 229 | switch (c) { |
| 210 | 230 | '0'...'9' => { |
| 211 | 231 | value *= 16; |
| ... | ... | @@ -220,20 +240,28 @@ pub fn parseCharLiteral( |
| 220 | 240 | value += c - 'A' + 10; |
| 221 | 241 | }, |
| 222 | 242 | else => { |
| 223 | bad_index.* = 3 + i; | |
| 224 | return error.InvalidCharacter; | |
| 243 | return .{ .expected_hex_digit = i }; | |
| 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 | 252 | 'u' => { |
| 231 | if (slice.len < "'\\u{0}'".len or slice[3] != '{' or slice[slice.len - 2] != '}') { | |
| 232 | bad_index.* = 2; | |
| 233 | return error.InvalidCharacter; | |
| 253 | var i: usize = 3; | |
| 254 | if (slice[i] != '{') { | |
| 255 | return .{ .expected_lbrace = i }; | |
| 234 | 256 | } |
| 257 | i += 1; | |
| 258 | if (slice[i] == '}') { | |
| 259 | return .{ .empty_unicode_escape_sequence = i }; | |
| 260 | } | |
| 261 | ||
| 235 | 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 | 265 | switch (c) { |
| 238 | 266 | '0'...'9' => { |
| 239 | 267 | value *= 16; |
| ... | ... | @@ -247,49 +275,112 @@ pub fn parseCharLiteral( |
| 247 | 275 | value *= 16; |
| 248 | 276 | value += c - 'A' + 10; |
| 249 | 277 | }, |
| 250 | else => { | |
| 251 | bad_index.* = 4 + i; | |
| 252 | return error.InvalidCharacter; | |
| 278 | '}' => { | |
| 279 | i += 1; | |
| 280 | break; | |
| 253 | 281 | }, |
| 282 | else => return .{ .expected_hex_digit_or_rbrace = i }, | |
| 254 | 283 | } |
| 255 | 284 | if (value > 0x10ffff) { |
| 256 | bad_index.* = 4 + i; | |
| 257 | return error.InvalidCharacter; | |
| 285 | return .{ .unicode_escape_overflow = i }; | |
| 258 | 286 | } |
| 259 | 287 | } |
| 260 | return value; | |
| 261 | }, | |
| 262 | else => { | |
| 263 | bad_index.* = 2; | |
| 264 | return error.InvalidCharacter; | |
| 288 | if (slice[i] != '\'') { | |
| 289 | return .{ .expected_end = i }; | |
| 290 | } | |
| 291 | return .{ .success = value }; | |
| 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 | 302 | test "parseCharLiteral" { |
| 272 | var bad_index: usize = undefined; | |
| 273 | try std.testing.expectEqual(try parseCharLiteral("'a'", &bad_index), 'a'); | |
| 274 | try std.testing.expectEqual(try parseCharLiteral("'ä'", &bad_index), 'ä'); | |
| 275 | try std.testing.expectEqual(try parseCharLiteral("'\\x00'", &bad_index), 0); | |
| 276 | try std.testing.expectEqual(try parseCharLiteral("'\\x4f'", &bad_index), 0x4f); | |
| 277 | try std.testing.expectEqual(try parseCharLiteral("'\\x4F'", &bad_index), 0x4f); | |
| 278 | try std.testing.expectEqual(try parseCharLiteral("'ぁ'", &bad_index), 0x3041); | |
| 279 | try std.testing.expectEqual(try parseCharLiteral("'\\u{0}'", &bad_index), 0); | |
| 280 | try std.testing.expectEqual(try parseCharLiteral("'\\u{3041}'", &bad_index), 0x3041); | |
| 281 | try std.testing.expectEqual(try parseCharLiteral("'\\u{7f}'", &bad_index), 0x7f); | |
| 282 | try std.testing.expectEqual(try parseCharLiteral("'\\u{7FFF}'", &bad_index), 0x7FFF); | |
| 303 | try std.testing.expectEqual( | |
| 304 | ParsedCharLiteral{ .success = 'a' }, | |
| 305 | parseCharLiteral("'a'"), | |
| 306 | ); | |
| 307 | try std.testing.expectEqual( | |
| 308 | ParsedCharLiteral{ .success = 'ä' }, | |
| 309 | parseCharLiteral("'ä'"), | |
| 310 | ); | |
| 311 | try std.testing.expectEqual( | |
| 312 | ParsedCharLiteral{ .success = 0 }, | |
| 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)); | |
| 285 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\x000'", &bad_index)); | |
| 286 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\y'", &bad_index)); | |
| 287 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u'", &bad_index)); | |
| 288 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\uFFFF'", &bad_index)); | |
| 289 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{}'", &bad_index)); | |
| 290 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFFFF}'", &bad_index)); | |
| 291 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF'", &bad_index)); | |
| 292 | try std.testing.expectError(error.InvalidCharacter, parseCharLiteral("'\\u{FFFF}x'", &bad_index)); | |
| 344 | try std.testing.expectEqual( | |
| 345 | ParsedCharLiteral{ .expected_hex_digit = 4 }, | |
| 346 | parseCharLiteral("'\\x0'"), | |
| 347 | ); | |
| 348 | try std.testing.expectEqual( | |
| 349 | ParsedCharLiteral{ .expected_end = 5 }, | |
| 350 | parseCharLiteral("'\\x000'"), | |
| 351 | ); | |
| 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 | 386 | test { |
lib/std/zig/tokenizer.zig+25-18| ... | ... | @@ -701,12 +701,19 @@ pub const Tokenizer = struct { |
| 701 | 701 | self.index += 1; |
| 702 | 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 | 712 | else => self.checkLiteralCharacter(), |
| 706 | 713 | }, |
| 707 | 714 | |
| 708 | 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 | 717 | else => { |
| 711 | 718 | state = .string_literal; |
| 712 | 719 | }, |
| ... | ... | @@ -774,7 +781,6 @@ pub const Tokenizer = struct { |
| 774 | 781 | .char_literal_unicode_escape_saw_u => switch (c) { |
| 775 | 782 | '{' => { |
| 776 | 783 | state = .char_literal_unicode_escape; |
| 777 | seen_escape_digits = 0; | |
| 778 | 784 | }, |
| 779 | 785 | else => { |
| 780 | 786 | result.tag = .invalid; |
| ... | ... | @@ -783,16 +789,9 @@ pub const Tokenizer = struct { |
| 783 | 789 | }, |
| 784 | 790 | |
| 785 | 791 | .char_literal_unicode_escape => switch (c) { |
| 786 | '0'...'9', 'a'...'f', 'A'...'F' => { | |
| 787 | seen_escape_digits += 1; | |
| 788 | }, | |
| 792 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | |
| 789 | 793 | '}' => { |
| 790 | if (seen_escape_digits == 0) { | |
| 791 | result.tag = .invalid; | |
| 792 | state = .char_literal_unicode_invalid; | |
| 793 | } else { | |
| 794 | state = .char_literal_end; | |
| 795 | } | |
| 794 | state = .char_literal_end; // too many/few digits handled later | |
| 796 | 795 | }, |
| 797 | 796 | else => { |
| 798 | 797 | result.tag = .invalid; |
| ... | ... | @@ -1026,7 +1025,13 @@ pub const Tokenizer = struct { |
| 1026 | 1025 | }, |
| 1027 | 1026 | }, |
| 1028 | 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 | 1036 | state = .doc_comment_start; |
| 1032 | 1037 | }, |
| ... | ... | @@ -1441,7 +1446,7 @@ test "tokenizer - code point literal with unicode escapes" { |
| 1441 | 1446 | , &.{ .invalid, .invalid }); |
| 1442 | 1447 | try testTokenize( |
| 1443 | 1448 | \\'\u{}' |
| 1444 | , &.{ .invalid, .invalid }); | |
| 1449 | , &.{.char_literal}); | |
| 1445 | 1450 | try testTokenize( |
| 1446 | 1451 | \\'\u{s}' |
| 1447 | 1452 | , &.{ .invalid, .invalid }); |
| ... | ... | @@ -1924,15 +1929,17 @@ test "tokenizer - invalid builtin identifiers" { |
| 1924 | 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 | 1933 | var tokenizer = Tokenizer.init(source); |
| 1929 | 1934 | for (expected_tokens) |expected_token_id| { |
| 1930 | 1935 | const token = tokenizer.next(); |
| 1931 | 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 | 1942 | const last_token = tokenizer.next(); |
| 1936 | try std.testing.expect(last_token.tag == .eof); | |
| 1937 | try std.testing.expect(last_token.loc.start == source.len); | |
| 1943 | try std.testing.expectEqual(Token.Tag.eof, last_token.tag); | |
| 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 | 6380 | const main_token = main_tokens[node]; |
| 6381 | 6381 | const slice = tree.tokenSlice(main_token); |
| 6382 | 6382 | |
| 6383 | var bad_index: usize = undefined; | |
| 6384 | const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { | |
| 6385 | error.InvalidCharacter => { | |
| 6386 | const bad_byte = slice[bad_index]; | |
| 6383 | switch (std.zig.parseCharLiteral(slice)) { | |
| 6384 | .success => |codepoint| { | |
| 6385 | const result = try gz.addInt(codepoint); | |
| 6386 | return rvalue(gz, rl, result, node); | |
| 6387 | }, | |
| 6388 | .invalid_escape_character => |bad_index| { | |
| 6387 | 6389 | return astgen.failOff( |
| 6388 | 6390 | main_token, |
| 6389 | 6391 | @intCast(u32, bad_index), |
| 6390 | "invalid character: '{c}'\n", | |
| 6391 | .{bad_byte}, | |
| 6392 | "invalid escape character: '{c}'", | |
| 6393 | .{slice[bad_index]}, | |
| 6392 | 6394 | ); |
| 6393 | 6395 | }, |
| 6394 | }; | |
| 6395 | const result = try gz.addInt(value); | |
| 6396 | return rvalue(gz, rl, result, node); | |
| 6396 | .expected_hex_digit => |bad_index| { | |
| 6397 | return astgen.failOff( | |
| 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 | 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 | 2466 | defer msg.deinit(); |
| 2467 | 2467 | |
| 2468 | 2468 | const token_starts = file.tree.tokens.items(.start); |
| 2469 | const token_tags = file.tree.tokens.items(.tag); | |
| 2469 | 2470 | |
| 2470 | 2471 | try file.tree.renderError(parse_err, msg.writer()); |
| 2471 | 2472 | const err_msg = try gpa.create(ErrorMsg); |
| ... | ... | @@ -2477,6 +2478,14 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void { |
| 2477 | 2478 | }, |
| 2478 | 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 | 2491 | const lock = comp.mutex.acquire(); |
src/main.zig+20| ... | ... | @@ -3380,6 +3380,7 @@ fn printErrMsgToStdErr( |
| 3380 | 3380 | color: Color, |
| 3381 | 3381 | ) !void { |
| 3382 | 3382 | const lok_token = parse_error.token; |
| 3383 | const token_tags = tree.tokens.items(.tag); | |
| 3383 | 3384 | const start_loc = tree.tokenLocation(0, lok_token); |
| 3384 | 3385 | const source_line = tree.source[start_loc.line_start..start_loc.line_end]; |
| 3385 | 3386 | |
| ... | ... | @@ -3389,6 +3390,24 @@ fn printErrMsgToStdErr( |
| 3389 | 3390 | try tree.renderError(parse_error, writer); |
| 3390 | 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 | 3411 | const message: Compilation.AllErrors.Message = .{ |
| 3393 | 3412 | .src = .{ |
| 3394 | 3413 | .src_path = path, |
| ... | ... | @@ -3397,6 +3416,7 @@ fn printErrMsgToStdErr( |
| 3397 | 3416 | .line = @intCast(u32, start_loc.line), |
| 3398 | 3417 | .column = @intCast(u32, start_loc.column), |
| 3399 | 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 | 1506 | \\ _ = bad; |
| 1507 | 1507 | \\} |
| 1508 | 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 | 1513 | ctx.objErrStage1("invalid exponent in float literal - 2", |
| ... | ... | @@ -1515,7 +1516,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1515 | 1516 | \\ _ = bad; |
| 1516 | 1517 | \\} |
| 1517 | 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 | 1523 | ctx.objErrStage1("invalid underscore placement in float literal - 1", |
| ... | ... | @@ -1524,7 +1526,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1524 | 1526 | \\ _ = bad; |
| 1525 | 1527 | \\} |
| 1526 | 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 | 1533 | ctx.objErrStage1("invalid underscore placement in float literal - 2", |
| ... | ... | @@ -1533,7 +1536,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1533 | 1536 | \\ _ = bad; |
| 1534 | 1537 | \\} |
| 1535 | 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 | 1543 | ctx.objErrStage1("invalid underscore placement in float literal - 3", |
| ... | ... | @@ -1542,7 +1546,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1542 | 1546 | \\ _ = bad; |
| 1543 | 1547 | \\} |
| 1544 | 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 | 1553 | ctx.objErrStage1("invalid underscore placement in float literal - 4", |
| ... | ... | @@ -1551,7 +1556,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1551 | 1556 | \\ _ = bad; |
| 1552 | 1557 | \\} |
| 1553 | 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 | 1563 | ctx.objErrStage1("invalid underscore placement in float literal - 5", |
| ... | ... | @@ -1560,7 +1566,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1560 | 1566 | \\ _ = bad; |
| 1561 | 1567 | \\} |
| 1562 | 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 | 1573 | ctx.objErrStage1("invalid underscore placement in float literal - 6", |
| ... | ... | @@ -1569,7 +1576,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1569 | 1576 | \\ _ = bad; |
| 1570 | 1577 | \\} |
| 1571 | 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 | 1583 | ctx.objErrStage1("invalid underscore placement in float literal - 7", |
| ... | ... | @@ -1578,7 +1586,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1578 | 1586 | \\ _ = bad; |
| 1579 | 1587 | \\} |
| 1580 | 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 | 1593 | ctx.objErrStage1("invalid underscore placement in float literal - 9", |
| ... | ... | @@ -1587,7 +1596,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1587 | 1596 | \\ _ = bad; |
| 1588 | 1597 | \\} |
| 1589 | 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 | 1603 | ctx.objErrStage1("invalid underscore placement in float literal - 10", |
| ... | ... | @@ -1596,7 +1606,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1596 | 1606 | \\ _ = bad; |
| 1597 | 1607 | \\} |
| 1598 | 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 | 1613 | ctx.objErrStage1("invalid underscore placement in float literal - 11", |
| ... | ... | @@ -1605,7 +1616,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1605 | 1616 | \\ _ = bad; |
| 1606 | 1617 | \\} |
| 1607 | 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 | 1623 | ctx.objErrStage1("invalid underscore placement in float literal - 12", |
| ... | ... | @@ -1614,7 +1626,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1614 | 1626 | \\ _ = bad; |
| 1615 | 1627 | \\} |
| 1616 | 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 | 1633 | ctx.objErrStage1("invalid underscore placement in float literal - 13", |
| ... | ... | @@ -1623,7 +1636,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1623 | 1636 | \\ _ = bad; |
| 1624 | 1637 | \\} |
| 1625 | 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 | 1643 | ctx.objErrStage1("invalid underscore placement in float literal - 14", |
| ... | ... | @@ -1632,7 +1646,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1632 | 1646 | \\ _ = bad; |
| 1633 | 1647 | \\} |
| 1634 | 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 | 1653 | ctx.objErrStage1("invalid underscore placement in int literal - 1", |
| ... | ... | @@ -1641,7 +1656,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1641 | 1656 | \\ _ = bad; |
| 1642 | 1657 | \\} |
| 1643 | 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 | 1663 | ctx.objErrStage1("invalid underscore placement in int literal - 2", |
| ... | ... | @@ -1650,7 +1666,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1650 | 1666 | \\ _ = bad; |
| 1651 | 1667 | \\} |
| 1652 | 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 | 1673 | ctx.objErrStage1("invalid underscore placement in int literal - 3", |
| ... | ... | @@ -1659,7 +1676,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1659 | 1676 | \\ _ = bad; |
| 1660 | 1677 | \\} |
| 1661 | 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 | 1683 | ctx.objErrStage1("invalid underscore placement in int literal - 4", |
| ... | ... | @@ -1668,7 +1686,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1668 | 1686 | \\ _ = bad; |
| 1669 | 1687 | \\} |
| 1670 | 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 | 1693 | ctx.objErrStage1("comptime struct field, no init value", |
| ... | ... | @@ -7544,7 +7563,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 7544 | 7563 | \\ const a = '\U1234'; |
| 7545 | 7564 | \\} |
| 7546 | 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 | 7570 | ctx.objErrStage1("invalid empty unicode escape", |