fix(diagnostics): point reserved-type-name binding errors at the binding (issue 0076)

The reserved-type-name binding diagnostic fired correctly but underlined the
enclosing statement / if / while / for / match / protocol / #objc_class block
because every binding-name check reused the parent `node.span`.

Thread each binding name's own span through the AST and parser, and pass it to
`checkBindingNames`:

- ast: add name spans to VarDecl, DestructureDecl, If/WhileExpr, ForExpr
  (capture + index), MatchArm, Catch/OnFailStmt, Protocol/ForeignMethodDecl.
- parser: populate each span at the binding site from the name token's loc;
  destructure reuses each target identifier's own span.
- semantic_diagnostics: every checkBindingName call now passes the binding's
  own span — no site falls back to node.span. fn/lambda params already used
  Param.name_span.

Carets now land on the offending identifier itself. New regression
examples/1125 asserts the protocol default-body and sx-defined #objc_class
method param spans; 0125/1119-1124 expected updated to the precise carets.
This commit is contained in:
agra
2026-06-03 22:06:56 +03:00
parent fcc76b9391
commit 6433eb6155
15 changed files with 144 additions and 51 deletions

View File

@@ -117,11 +117,11 @@ pub const UnknownTypeChecker = struct {
switch (node.data) {
// ── Binding-introducing nodes: check the name(s), then recurse. ──
.var_decl => |vd| {
self.checkBindingName(vd.name, node.span);
self.checkBindingName(vd.name, vd.name_span);
if (vd.value) |v| self.checkBindingNames(v);
},
.destructure_decl => |dd| {
for (dd.names) |n| self.checkBindingName(n, node.span);
for (dd.names, dd.name_spans) |n, sp| self.checkBindingName(n, sp);
self.checkBindingNames(dd.value);
},
.fn_decl => |fd| {
@@ -137,19 +137,19 @@ pub const UnknownTypeChecker = struct {
if (p.default_expr) |de| self.checkBindingNames(de);
},
.if_expr => |ie| {
if (ie.binding_name) |bn| self.checkBindingName(bn, node.span);
if (ie.binding_name) |bn| self.checkBindingName(bn, ie.binding_span);
self.checkBindingNames(ie.condition);
self.checkBindingNames(ie.then_branch);
if (ie.else_branch) |e| self.checkBindingNames(e);
},
.while_expr => |we| {
if (we.binding_name) |bn| self.checkBindingName(bn, node.span);
if (we.binding_name) |bn| self.checkBindingName(bn, we.binding_span);
self.checkBindingNames(we.condition);
self.checkBindingNames(we.body);
},
.for_expr => |fe| {
if (fe.capture_name.len != 0) self.checkBindingName(fe.capture_name, node.span);
if (fe.index_name) |idx| self.checkBindingName(idx, node.span);
if (fe.capture_name.len != 0) self.checkBindingName(fe.capture_name, fe.capture_span);
if (fe.index_name) |idx| self.checkBindingName(idx, fe.index_span);
self.checkBindingNames(fe.iterable);
if (fe.range_end) |re| self.checkBindingNames(re);
self.checkBindingNames(fe.body);
@@ -157,23 +157,23 @@ pub const UnknownTypeChecker = struct {
.match_expr => |me| {
self.checkBindingNames(me.subject);
for (me.arms) |arm| {
if (arm.capture) |cap| self.checkBindingName(cap, node.span);
if (arm.capture) |cap| self.checkBindingName(cap, arm.capture_span);
if (arm.pattern) |p| self.checkBindingNames(p);
self.checkBindingNames(arm.body);
}
},
.match_arm => |arm| {
if (arm.capture) |cap| self.checkBindingName(cap, node.span);
if (arm.capture) |cap| self.checkBindingName(cap, arm.capture_span);
if (arm.pattern) |p| self.checkBindingNames(p);
self.checkBindingNames(arm.body);
},
.catch_expr => |ce| {
if (ce.binding) |b| self.checkBindingName(b, node.span);
if (ce.binding) |b| self.checkBindingName(b, ce.binding_span);
self.checkBindingNames(ce.operand);
self.checkBindingNames(ce.body);
},
.onfail_stmt => |os| {
if (os.binding) |b| self.checkBindingName(b, node.span);
if (os.binding) |b| self.checkBindingName(b, os.binding_span);
self.checkBindingNames(os.body);
},
// impl / protocol-default / foreign-class method bodies: each
@@ -183,13 +183,13 @@ pub const UnknownTypeChecker = struct {
.impl_block => |ib| for (ib.methods) |m| self.checkBindingNames(m),
.protocol_decl => |pd| for (pd.methods) |m| {
if (m.default_body) |body| {
for (m.param_names) |pn| self.checkBindingName(pn, node.span);
for (m.param_names, m.param_name_spans) |pn, sp| self.checkBindingName(pn, sp);
self.checkBindingNames(body);
}
},
.foreign_class_decl => |fcd| for (fcd.members) |member| switch (member) {
.method => |m| if (m.body) |body| {
for (m.param_names) |pn| self.checkBindingName(pn, node.span);
for (m.param_names, m.param_name_spans) |pn, sp| self.checkBindingName(pn, sp);
self.checkBindingNames(body);
},
.field, .extends, .implements => {},