authorgravatar for 58830309+g-w1@users.noreply.github.comg-w1 <58830309+g-w1@users.noreply.github.com> 2020-12-06 12:36:49-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-06 19:36:49+02:00
log6294c1136cb44285156c04f9f89f62b0f9e8e471
tree241f9488c7b45faa096fb87f4f06d3c1fc0495f1
parent0268f54fcfbb8a1c9f258b0d6f9c2a87ab597531
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

stage2: variable shadowing detection (#6969)


2 files changed, 47 insertions(+), 2 deletions(-)

src/astgen.zig+28-1
......@@ -489,7 +489,6 @@ fn varDecl(
489489 node: *ast.Node.VarDecl,
490490 block_arena: *Allocator,
491491) InnerError!*Scope {
492 // TODO implement detection of shadowing
493492 if (node.getComptimeToken()) |comptime_token| {
494493 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
495494 }
......@@ -499,6 +498,34 @@ fn varDecl(
499498 const tree = scope.tree();
500499 const name_src = tree.token_locs[node.name_token].start;
501500 const ident_name = try identifierTokenString(mod, scope, node.name_token);
501
502 // Local variables shadowing detection, including function parameters.
503 {
504 var s = scope;
505 while (true) switch (s.tag) {
506 .local_val => {
507 const local_val = s.cast(Scope.LocalVal).?;
508 if (mem.eql(u8, local_val.name, ident_name)) {
509 return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name});
510 }
511 s = local_val.parent;
512 },
513 .local_ptr => {
514 const local_ptr = s.cast(Scope.LocalPtr).?;
515 if (mem.eql(u8, local_ptr.name, ident_name)) {
516 return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name});
517 }
518 s = local_ptr.parent;
519 },
520 .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
521 else => break,
522 };
523 }
524
525 // Namespace vars shadowing detection
526 if (mod.lookupDeclName(scope, ident_name)) |_| {
527 return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name});
528 }
502529 const init_node = node.getInitNode() orelse
503530 return mod.fail(scope, name_src, "variables must be initialized", .{});
504531
test/stage2/test.zig+19-1
......@@ -830,7 +830,7 @@ pub fn addCases(ctx: *TestContext) !void {
830830 // Character literals and multiline strings.
831831 case.addCompareOutput(
832832 \\export fn _start() noreturn {
833 \\ const ignore =
833 \\ const ignore =
834834 \\ \\ cool thx
835835 \\ \\
836836 \\ ;
......@@ -1113,6 +1113,24 @@ pub fn addCases(ctx: *TestContext) !void {
11131113 \\fn entry() void {}
11141114 , &[_][]const u8{":2:4: error: redefinition of 'entry'"});
11151115
1116 {
1117 var case = ctx.obj("variable shadowing", linux_x64);
1118 case.addError(
1119 \\export fn _start() noreturn {
1120 \\ var i: u32 = 10;
1121 \\ var i: u32 = 10;
1122 \\ unreachable;
1123 \\}
1124 , &[_][]const u8{":3:9: error: redefinition of 'i'"});
1125 case.addError(
1126 \\var testing: i64 = 10;
1127 \\export fn _start() noreturn {
1128 \\ var testing: i64 = 20;
1129 \\ unreachable;
1130 \\}
1131 , &[_][]const u8{":3:9: error: redefinition of 'testing'"});
1132 }
1133
11161134 {
11171135 var case = ctx.obj("extern variable has no type", linux_x64);
11181136 case.addError(