diff options
| -rw-r--r-- | src/AstGen.zig | 46 | ||||
| -rw-r--r-- | test/stage2/test.zig | 8 |
2 files changed, 53 insertions, 1 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index 1eb12af99d..48b1ee5579 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1826,6 +1826,7 @@ fn blockExprStmts( } try genDefers(gz, parent_scope, scope, .none); + try checkUsed(gz, parent_scope, scope); } fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) InnerError!void { @@ -2154,6 +2155,36 @@ fn genDefers( } } +fn checkUsed( + gz: *GenZir, + outer_scope: *Scope, + inner_scope: *Scope, +) InnerError!void { + const astgen = gz.astgen; + const tree = astgen.tree; + const node_datas = tree.nodes.items(.data); + + var scope = inner_scope; + while (scope != outer_scope) { + switch (scope.tag) { + .gen_zir => scope = scope.cast(GenZir).?.parent, + .local_val => { + const s = scope.cast(Scope.LocalVal).?; + if (!s.used) return astgen.failTok(s.token_src, "unused local constant", .{}); + scope = s.parent; + }, + .local_ptr => { + const s = scope.cast(Scope.LocalPtr).?; + if (!s.used) return astgen.failTok(s.token_src, "unused local variable", .{}); + scope = s.parent; + }, + .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, + .namespace => unreachable, + .top => unreachable, + } + } +} + fn deferStmt( gz: *GenZir, scope: *Scope, @@ -2930,6 +2961,7 @@ fn fnDecl( .name = param_name, .inst = arg_inst, .token_src = name_token, + // TODO make function paramater have different message instead of unused constant }; params_scope = &sub_scope.base; @@ -3370,6 +3402,7 @@ fn structDeclInner( }; defer block_scope.instructions.deinit(gpa); + // TODO should we change this to scope in other places too? var namespace: Scope.Namespace = .{ .parent = scope }; defer namespace.decls.deinit(gpa); @@ -6131,10 +6164,14 @@ fn identifier( while (true) switch (s.tag) { .local_val => { const local_val = s.cast(Scope.LocalVal).?; + if (local_val.name == name_str_index) { + local_val.used = true; + } if (hit_namespace) { // captures of non-locals need to be emitted as decl_val or decl_ref // This *might* be capturable depending on if it is comptime known - break; + s = local_val.parent; + continue; } if (local_val.name == name_str_index) { return rvalue(gz, scope, rl, local_val.inst, ident); @@ -6144,6 +6181,7 @@ fn identifier( .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (local_ptr.name == name_str_index) { + local_ptr.used = true; if (hit_namespace) { if (local_ptr.is_comptime) break @@ -6151,6 +6189,8 @@ fn identifier( return astgen.failNodeNotes(ident, "'{s}' not accessible from inner function", .{ident_name}, &.{ try astgen.errNoteTok(local_ptr.token_src, "declared here", .{}), // TODO add crossed function definition here note. + // Maybe add a note to the error about it being because of the var, + // maybe recommend copying it into a const variable. -SpexGuy }); } switch (rl) { @@ -8341,6 +8381,8 @@ const Scope = struct { token_src: ast.TokenIndex, /// String table index. name: u32, + /// has this variable been referenced? + used: bool = false, }; /// This could be a `const` or `var` local. It has a pointer instead of a value. @@ -8358,6 +8400,8 @@ const Scope = struct { /// String table index. name: u32, is_comptime: bool, + /// has this variable been referenced? + used: bool = false, }; const Defer = struct { diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 977f5f9ebf..0365c8863a 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -247,6 +247,14 @@ pub fn addCases(ctx: *TestContext) !void { ); } { + var case = ctx.exe("unused vars", linux_x64); + case.addError( + \\pub fn main() void { + \\ const x = 1; + \\} + , &.{":2:11: error: unused local constant"}); + } + { var case = ctx.exe("@TypeOf", linux_x64); case.addCompareOutput( \\pub fn main() void { |
