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

@@ -272,6 +272,7 @@ pub const IfExpr = struct {
is_inline: bool, // true for `if cond then a else b`
is_comptime: bool = false, // true for `inline if` — compile-time branch elimination
binding_name: ?[]const u8 = null, // for `if val := expr { ... }` optional binding
binding_span: ?Span = null, // span of `binding_name` (set iff `binding_name` is)
};
pub const MatchExpr = struct {
@@ -285,6 +286,7 @@ pub const MatchArm = struct {
body: *Node,
is_break: bool,
capture: ?[]const u8 = null, // payload binding name: case .variant: (name) { ... }
capture_span: ?Span = null, // span of `capture` (set iff `capture` is)
};
pub const ConstDecl = struct {
@@ -295,6 +297,7 @@ pub const ConstDecl = struct {
pub const VarDecl = struct {
name: []const u8,
name_span: Span,
type_annotation: ?*Node,
value: ?*Node,
is_foreign: bool = false,
@@ -329,6 +332,7 @@ pub const MultiAssign = struct {
pub const DestructureDecl = struct {
names: []const []const u8,
name_spans: []const Span, // one per entry in `names`, same order
value: *Node,
};
@@ -449,6 +453,7 @@ pub const TryExpr = struct {
pub const CatchExpr = struct {
operand: *Node,
binding: ?[]const u8 = null,
binding_span: ?Span = null, // span of `binding` (set iff `binding` is)
body: *Node,
is_match_body: bool = false,
};
@@ -458,6 +463,7 @@ pub const CatchExpr = struct {
/// a bare expression (`onfail EXPR;`).
pub const OnFailStmt = struct {
binding: ?[]const u8 = null,
binding_span: ?Span = null, // span of `binding` (set iff `binding` is)
body: *Node,
};
@@ -551,13 +557,16 @@ pub const WhileExpr = struct {
condition: *Node,
body: *Node,
binding_name: ?[]const u8 = null, // for `while val := expr { ... }` optional binding
binding_span: ?Span = null, // span of `binding_name` (set iff `binding_name` is)
};
pub const ForExpr = struct {
iterable: *Node,
body: *Node,
capture_name: []const u8,
capture_span: ?Span = null, // span of `capture_name` (null when omitted, e.g. `for 0..N { }`)
index_name: ?[]const u8 = null,
index_span: ?Span = null, // span of `index_name` (set iff `index_name` is)
/// Range form `for start..end (i) { }`: `iterable` is the start, `range_end`
/// the (exclusive) end. Null for the iterate-a-collection form
/// (`for coll : (x) { }`). For the range form `capture_name` is the cursor
@@ -645,6 +654,7 @@ pub const ProtocolMethodDecl = struct {
name: []const u8,
params: []const *Node, // type_expr nodes for parameter types (excluding implicit self)
param_names: []const []const u8, // parameter names (excluding implicit self)
param_name_spans: []const Span = &.{}, // one per `param_names` entry; empty for synthesized methods
return_type: ?*Node, // null = void return
default_body: ?*Node, // null = required method, non-null = default implementation
};
@@ -670,6 +680,7 @@ pub const ForeignMethodDecl = struct {
name: []const u8,
params: []const *Node, // type_expr nodes — first is `*Self` for instance methods
param_names: []const []const u8,
param_name_spans: []const Span = &.{}, // one per `param_names` entry; empty for synthesized methods
return_type: ?*Node, // null = void
is_static: bool = false, // true for `static name :: ...`
jni_descriptor_override: ?[]const u8 = null, // `#jni_method_descriptor("(Sig)Ret")` — JNI runtime only