stage2 astgen: find unused vars

This commit is contained in:
Jacob G-W 2021-06-08 20:32:44 -04:00 committed by Andrew Kelley
parent 8a6de78e07
commit 75f7a8913e
2 changed files with 53 additions and 1 deletions

View File

@ -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 {

View File

@ -246,6 +246,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(