lsp: resolve for-loop capture types (go-to-def + inlay hints)
The sema analyzer bound a for-loop capture with no type, so navigating or hinting through it (m.flag, m: Move) failed. Instantiate generic field types (legal_moves: List(Move)) and infer the capture's element type from the iterable — List-like structs, slices, arrays, many- pointers, and a pointer followed to its pointee. By-ref captures bind a pointer to the element; range cursors bind s64. Inlay hints now descend into struct method bodies and emit a type label for the capture itself.
This commit is contained in:
@@ -1241,8 +1241,14 @@ pub const Server = struct {
|
||||
collectInlayHints(allocator, we.body, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.for_expr => |fe| {
|
||||
if (!std.mem.eql(u8, fe.capture_name, "_")) {
|
||||
addForCaptureHint(allocator, fe.capture_name, node.span, symbols, source, hints);
|
||||
}
|
||||
collectInlayHints(allocator, fe.body, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.struct_decl => |sd| {
|
||||
for (sd.methods) |m| collectInlayHints(allocator, m, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
// Only show hint when type is inferred (:= syntax)
|
||||
if (vd.type_annotation != null) return;
|
||||
@@ -1265,6 +1271,14 @@ pub const Server = struct {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Struct methods carry their own bodies — descend so locals
|
||||
// (and `for` captures) inside them get hints too.
|
||||
if (cd.value.data == .struct_decl) {
|
||||
for (cd.value.data.struct_decl.methods) |m| {
|
||||
collectInlayHints(allocator, m, symbols, fn_signatures, source, hints);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Skip functions, types, structs, enums, unions, comptime, foreign, library
|
||||
switch (cd.value.data) {
|
||||
.fn_decl, .type_expr, .struct_decl, .enum_decl, .union_decl,
|
||||
@@ -1371,6 +1385,38 @@ pub const Server = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Hint for a `for` loop capture (`for xs: (m)` → `m: T`). The capture has
|
||||
/// no `:=`/`::`, so the label carries its own colon and lands right after
|
||||
/// the capture name, whose slice points into `source`.
|
||||
fn addForCaptureHint(
|
||||
allocator: std.mem.Allocator,
|
||||
name: []const u8,
|
||||
span: sx.ast.Span,
|
||||
symbols: []const sx.sema.Symbol,
|
||||
source: [:0]const u8,
|
||||
hints: *std.ArrayList(lsp.InlayHint),
|
||||
) void {
|
||||
const sym = findSymbolAtSpan(symbols, span.start, name) orelse return;
|
||||
const ty = sym.ty orelse return;
|
||||
if (ty == .void_type) return;
|
||||
const type_name = ty.displayName(allocator) catch return;
|
||||
const label = std.fmt.allocPrint(allocator, ": {s}", .{type_name}) catch return;
|
||||
|
||||
const base = @intFromPtr(source.ptr);
|
||||
const np = @intFromPtr(name.ptr);
|
||||
if (np < base or np + name.len > base + source.len) return;
|
||||
const end_offset: u32 = @intCast(np - base + name.len);
|
||||
const loc = sx.errors.SourceLoc.compute(source, end_offset);
|
||||
if (loc.line == 0 or loc.col == 0) return;
|
||||
hints.append(allocator, .{
|
||||
.line = loc.line - 1,
|
||||
.character = loc.col - 1,
|
||||
.label = label,
|
||||
.padding_left = false,
|
||||
.padding_right = false,
|
||||
}) catch {};
|
||||
}
|
||||
|
||||
fn addReturnTypeHint(
|
||||
allocator: std.mem.Allocator,
|
||||
span: sx.ast.Span,
|
||||
|
||||
Reference in New Issue
Block a user