Commit 6294c1136c

g-w1 <58830309+g-w1@users.noreply.github.com>
2020-12-06 18:36:49
stage2: variable shadowing detection (#6969)
1 parent 0268f54
Changed files (2)
src
test
stage2
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", .{});
 
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(