From 93f206223985b78c60faad1d965986089c08d424 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sun, 21 Jul 2024 05:50:56 -0700 Subject: [PATCH] feat(transform): include a warning for leading zero in numbers --- CHANGELOG.md | 1 + src/core/Ast.zig | 2 +- src/core/Parse.zig | 2 +- src/core/Report.zig | 59 +++++++++++++++++++++++++++++++----------- src/core/Sema.zig | 6 ++--- src/core/Tokenizer.zig | 5 +--- src/core/Transform.zig | 7 ++++- 7 files changed, 57 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e274cb7de0b7170ca347e7439a3d2e78e987c3f8..155e6db9170c549615b492f72d313e96f5247013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - feat: implement string identifiers - feat: implement basic struct types - feat: include a `README.html` alongside compiler with documentation and code samples. +- feat(transform): include a warning for leading zero in numbers - fix(sema): miscompilation using left side of all binary expression for both sides - fix(sema): handle recursive function calls - fix(sema): quit block analysis when hitting `noreturn` diff --git a/src/core/Ast.zig b/src/core/Ast.zig index 7a7116ca29b77ad1ba337e8468e6195454883ce6..248c0a8b2cf9e36e67ac1f6c1b3e67ca64d46e61 100644 --- a/src/core/Ast.zig +++ b/src/core/Ast.zig @@ -763,7 +763,7 @@ pub fn extraData(tree: Ast, index: usize, comptime T: type) T { OptionalExtraIndex, => 1, SubList => 2, - else => comptime unreachable, + else => @compileError(unreachable), }; } return result; diff --git a/src/core/Parse.zig b/src/core/Parse.zig index dddfed2145a4df8221c666bd0d13fc5c7772f66d..8cd2d2fc08b3ffd4c3eecb3c7990bb0df94c4fb3 100644 --- a/src/core/Parse.zig +++ b/src/core/Parse.zig @@ -867,7 +867,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { .kw_comptime => .@"comptime", .kw_pure => .pure, .kw_assert => .assert, - else => comptime unreachable, + else => @compileError(unreachable), }, .main_token = main_token, .data = .{ .un = .{ diff --git a/src/core/Report.zig b/src/core/Report.zig index 6a7cea3a71c2c985c5b5c04676278965c3b7ccf0..49bb6ebd1250af3acd43aa534357a5d3cd4d3197 100644 --- a/src/core/Report.zig +++ b/src/core/Report.zig @@ -424,6 +424,25 @@ pub const Warn = union(enum) { try ctx.msgAlone("if this was intentional, discard it with \"_ = {};\"", .{formatIdCode(id)}); } }, + zero_prefixed_literal: struct { + token: TokenIndex, + + fn build(data: @This(), ctx: *MsgContext) !void { + try ctx.msg(.{ + .token_substring = .{ + .token = data.token, + .start = 0, + .len = brk: { + const slice = ctx.ast.tokenSlice(data.token); + var i: u32 = 0; + while (slice[i] == '0') i += 1; + if (slice[i] == '.') i -= 1; + break :brk i; + }, + }, + }, "avoid redundant zeros in numbers", .{}); + } + }, not_mutated_var: struct { node: NodeIndex, @@ -814,19 +833,21 @@ const Component = struct { }; pub const Highlight = union(enum) { - // No source view + /// No source view alone, - // Highlight one token + /// Highlight one token token: TokenIndex, - // Highlight one token without referring to the Ast + /// Highlight one token without referring to the Ast token_stored: Tokenizer.Token, - // Highlight two tokens + /// Highlight two tokens two_tokens: TokenIndex, - // Highlight a Node + /// Highlight a substring of a token + token_substring: struct { token: TokenIndex, start: ByteOffset, len: u32 }, + /// Highlight a Node node: NodeIndex, - // Highlight a codepoint + /// Highlight a codepoint codepoint: ByteOffset, - // Highlight from the start of a codepoint to '\n' + /// Highlight from the start of a codepoint to '\n' codepoint_eol: ByteOffset, pub fn loc(highlight: Highlight, ast: *const Ast) ?SliceLoc { @@ -834,22 +855,30 @@ pub const Highlight = union(enum) { .alone => null, inline .node, .token, + .token_substring, .two_tokens, => |payload, tag| loc: { const token_index: TokenIndex = switch (tag) { .node => ast.nodeToken(payload), .token, .two_tokens => payload, - else => comptime unreachable, + .token_substring => payload.token, + else => @compileError(unreachable), }; - const location = ast.tokenLocation(0, token_index); const token = ast.tokens.get(token_index); - break :loc .{ - .loc = location, - .slice = token.slice(ast.utf8_source), - .byte = token.offset, - }; + var location = ast.tokenLocation(0, token_index); + var slice = token.slice(ast.utf8_source); + var byte = token.offset; + + if (tag == .token_substring) { + location.column = location.column.add(payload.start); + slice.ptr += payload.start; + slice.len = payload.len; + byte += payload.start; + } + + break :loc .{ .loc = location, .slice = slice, .byte = byte }; }, .token_stored => |token| loc: { const location = Tokenizer.tokenLocation(ast.utf8_source, 0, token.offset); @@ -869,7 +898,7 @@ pub const Highlight = union(enum) { n else (ast.utf8_source.len - b), - else => comptime unreachable, + else => @compileError(unreachable), }; break :loc .{ .byte = b, diff --git a/src/core/Sema.zig b/src/core/Sema.zig index 6c8bda188678d81b17f6e8ec702d2974140d6686..7c1afb15eccec51a1da66e4a658b372cac5c0dae 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -1328,7 +1328,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { .sub => l - r, .mul => l * r, .div => l / r, - else => comptime unreachable, + else => @compileError(unreachable), }; return try s.makeValue(if (left_ty == .numeric_literal) @@ -1346,7 +1346,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { .sub => std.math.sub(i32, l, r), .mul => std.math.mul(i32, l, r), .div => return try s.makeValue(.{ .float = @as(f64, @floatFromInt(l)) / @as(f64, @floatFromInt(r)) }), - else => comptime unreachable, + else => @compileError(unreachable), } catch @panic("TODO: out of range"); return try s.makeValue(.{ .int32 = result }); @@ -1362,7 +1362,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { const result = switch (tag) { .lt => l < r, .gt => l > r, - else => comptime unreachable, + else => @compileError(unreachable), }; return @as( InternPool.Value, diff --git a/src/core/Tokenizer.zig b/src/core/Tokenizer.zig index 9f55efbc4d357bfc303c0b3e146714ea45220499..d8583b1b8e274716d41c8ab53ddaf14e725e2d29 100644 --- a/src/core/Tokenizer.zig +++ b/src/core/Tokenizer.zig @@ -612,9 +612,7 @@ fn tokenizeIdentifier(t: *Tokenizer, start_byte: u32) !Token { /// - May use 0x, 0b, 0o prefixes for hex, binary, octal numbers. /// The token is tagged as `number_int` or `number_float` depending /// on if it contained a `.` This is to help later re-parses. -// -// TODO: is it a zig compiler bug that `?` is required here? -fn tokenizeNumber(t: *Tokenizer) !?Token { +fn tokenizeNumber(t: *Tokenizer) !Token { const trace = T.trace(@src()); defer trace.end(); @@ -646,7 +644,6 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { t.pos += 2; try t.consumeIntegerRadix(.octal); }, - '0' => @panic("TODO: no double zero"), else => break :alt_base, } diff --git a/src/core/Transform.zig b/src/core/Transform.zig index e2cbb961dcb9394a1bc6e1b3103211f199d9009e..e0013ca2c468039ed62ee36581986f8920dc52cb 100644 --- a/src/core/Transform.zig +++ b/src/core/Transform.zig @@ -797,8 +797,13 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc const value = switch (tag) { .number_int => std.fmt.parseFloat(f64, text) catch @panic("TODO: write my own float parser"), .number_float => std.fmt.parseFloat(f64, text) catch @panic("TODO: write my own float parser"), - else => comptime unreachable, + else => @compileError(unreachable), }; + + if (value != 0 and text[0] == '0' and (text.len == 1 or text[1] != '.')) { + try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) }); + } + return try t.addInstRef(.{ .tag = .number, .data = EncodedData.encode(.number, .{ -- 2.54.0