diff --git a/src/astgen.zig b/src/astgen.zig index 37b1eab9a..bef16445d 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -489,7 +489,6 @@ fn varDecl( node: *ast.Node.VarDecl, block_arena: *Allocator, ) InnerError!*Scope { - // TODO implement detection of shadowing if (node.getComptimeToken()) |comptime_token| { return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); } @@ -499,6 +498,34 @@ fn varDecl( const tree = scope.tree(); const name_src = tree.token_locs[node.name_token].start; const ident_name = try identifierTokenString(mod, scope, node.name_token); + + // Local variables shadowing detection, including function parameters. + { + var s = scope; + while (true) switch (s.tag) { + .local_val => { + const local_val = s.cast(Scope.LocalVal).?; + if (mem.eql(u8, local_val.name, ident_name)) { + return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + } + s = local_val.parent; + }, + .local_ptr => { + const local_ptr = s.cast(Scope.LocalPtr).?; + if (mem.eql(u8, local_ptr.name, ident_name)) { + return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + } + s = local_ptr.parent; + }, + .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + else => break, + }; + } + + // Namespace vars shadowing detection + if (mod.lookupDeclName(scope, ident_name)) |_| { + return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + } const init_node = node.getInitNode() orelse return mod.fail(scope, name_src, "variables must be initialized", .{}); diff --git a/test/stage2/test.zig b/test/stage2/test.zig index ac3b42638..a2daa933a 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -830,7 +830,7 @@ pub fn addCases(ctx: *TestContext) !void { // Character literals and multiline strings. case.addCompareOutput( \\export fn _start() noreturn { - \\ const ignore = + \\ const ignore = \\ \\ cool thx \\ \\ \\ ; @@ -1113,6 +1113,24 @@ pub fn addCases(ctx: *TestContext) !void { \\fn entry() void {} , &[_][]const u8{":2:4: error: redefinition of 'entry'"}); + { + var case = ctx.obj("variable shadowing", linux_x64); + case.addError( + \\export fn _start() noreturn { + \\ var i: u32 = 10; + \\ var i: u32 = 10; + \\ unreachable; + \\} + , &[_][]const u8{":3:9: error: redefinition of 'i'"}); + case.addError( + \\var testing: i64 = 10; + \\export fn _start() noreturn { + \\ var testing: i64 = 20; + \\ unreachable; + \\} + , &[_][]const u8{":3:9: error: redefinition of 'testing'"}); + } + { var case = ctx.obj("extern variable has no type", linux_x64); case.addError(