Local variables shadowing detection, including function parameters.
fn detectLocalShadowing(
astgen: *AstGen,
scope: *Scope,
ident_name: Zir.NullTerminatedString,
name_token: Ast.TokenIndex,
ident_name_raw: []const u8,
id_cat: Scope.IdCat,
) !void
fn detectLocalShadowing(
astgen: *AstGen,
scope: *Scope,
ident_name: Zir.NullTerminatedString,
name_token: Ast.TokenIndex,
ident_name_raw: []const u8,
id_cat: Scope.IdCat,
) !void {
const gpa = astgen.gpa;
if (ident_name_raw[0] != '@' and isPrimitive(ident_name_raw)) {
return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
ident_name_raw,
}, &[_]u32{
try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
ident_name_raw,
}),
});
}
var outer_scope = false;
find_scope: switch (scope.unwrap()) {
.local_val => |local_val| {
if (local_val.name == ident_name) {
const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
const name = try gpa.dupe(u8, name_slice);
defer gpa.free(name);
if (outer_scope) {
return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
@tagName(id_cat), name, @tagName(local_val.id_cat),
}, &[_]u32{
try astgen.errNoteTok(
local_val.token_src,
"previous declaration here",
.{},
),
});
}
return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
@tagName(local_val.id_cat), name,
}, &[_]u32{
try astgen.errNoteTok(
local_val.token_src,
"previous declaration here",
.{},
),
});
}
continue :find_scope local_val.parent.unwrap();
},
.local_ptr => |local_ptr| {
if (local_ptr.name == ident_name) {
const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
const name = try gpa.dupe(u8, name_slice);
defer gpa.free(name);
if (outer_scope) {
return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
@tagName(id_cat), name, @tagName(local_ptr.id_cat),
}, &[_]u32{
try astgen.errNoteTok(
local_ptr.token_src,
"previous declaration here",
.{},
),
});
}
return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
@tagName(local_ptr.id_cat), name,
}, &[_]u32{
try astgen.errNoteTok(
local_ptr.token_src,
"previous declaration here",
.{},
),
});
}
continue :find_scope local_ptr.parent.unwrap();
},
.namespace => |ns| {
outer_scope = true;
const decl_node = ns.decls.get(ident_name) orelse {
continue :find_scope ns.parent.unwrap();
};
const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
const name = try gpa.dupe(u8, name_slice);
defer gpa.free(name);
return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
@tagName(id_cat), name,
}, &[_]u32{
try astgen.errNoteNode(decl_node, "declared here", .{}),
});
},
.gen_zir => |gen_zir| {
outer_scope = true;
continue :find_scope gen_zir.parent.unwrap();
},
.defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
.top => break :find_scope,
}
}