diff --git a/src/analysis.zig b/src/analysis.zig index 4c433a976..726515e84 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1108,9 +1108,18 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi } } -fn resolveTupleFieldType(analyser: *Analyser, tuple: Type, index: usize) error{OutOfMemory}!?Type { +pub fn resolveTupleFieldType(analyser: *Analyser, tuple: Type, index: usize) error{OutOfMemory}!?Type { const scope_handle = switch (tuple.data) { .container => |s| s, + .other => |node| { + var buffer: [2]Ast.Node.Index = undefined; + const array_init_info = node.handle.tree.fullArrayInit(&buffer, node.node) orelse return null; + + const elements = array_init_info.ast.elements; + if (index >= elements.len) return null; + + return try analyser.resolveTypeOfNode(.{ .handle = node.handle, .node = elements[index] }); + }, else => return null, }; const node = scope_handle.toNode(); @@ -1692,26 +1701,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e var buffer: [2]Ast.Node.Index = undefined; const array_init_info = tree.fullArrayInit(&buffer, node).?; - std.debug.assert(array_init_info.ast.elements.len != 0); - if (array_init_info.ast.type_expr != 0) blk: { const array_ty = try analyser.resolveTypeOfNode(.{ .node = array_init_info.ast.type_expr, .handle = handle }) orelse break :blk; return try array_ty.instanceTypeVal(analyser); } - // try to infer the array type - const maybe_elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = array_init_info.ast.elements[0], .handle = handle }); - const elem_ty = if (maybe_elem_ty) |elem_ty| elem_ty.typeOf(analyser) else try Type.typeValFromIP(analyser, .type_type); - - const elem_ty_ptr = try analyser.arena.allocator().create(Type); - elem_ty_ptr.* = elem_ty; - return Type{ - .data = .{ .array = .{ - .elem_count = @intCast(array_init_info.ast.elements.len), - .sentinel = .none, - .elem_ty = elem_ty_ptr, - } }, + .data = .{ .other = node_handle }, .is_type_val = false, }; }, @@ -2373,6 +2369,8 @@ pub const Type = struct { /// - Error type: `Foo || Bar`, `Foo!Bar` /// - Function: `fn () Foo`, `fn foo() Foo` + /// - `.{a,b}` + /// - `start..end` other: NodeWithHandle, /// - `@compileError("")` @@ -4012,7 +4010,7 @@ pub const DeclWithHandle = struct { }) orelse return null; break :blk switch (node.data) { .array => |array_info| try array_info.elem_ty.instanceTypeVal(analyser), - .container => try analyser.resolveTupleFieldType(node, pay.index), + .container, .other => try analyser.resolveTupleFieldType(node, pay.index), else => null, }; }, diff --git a/src/features/completions.zig b/src/features/completions.zig index a0180fd99..4e6c34af6 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -121,14 +121,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi try typeToCompletion(builder, rhs_ty); } }, - - .fn_proto, - .fn_proto_multi, - .fn_proto_one, - .fn_proto_simple, - .fn_decl, - => {}, - else => unreachable, + else => {}, }, .ip_index => |payload| try analyser_completions.dotCompletions( builder.arena, diff --git a/src/features/semantic_tokens.zig b/src/features/semantic_tokens.zig index 9b58dccfb..bb8de59f4 100644 --- a/src/features/semantic_tokens.zig +++ b/src/features/semantic_tokens.zig @@ -312,30 +312,8 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v .aligned_var_decl, => { const var_decl = tree.fullVarDecl(node).?; - try writeToken(builder, var_decl.visib_token, .keyword); - try writeToken(builder, var_decl.extern_export_token, .keyword); - try writeToken(builder, var_decl.threadlocal_token, .keyword); - try writeToken(builder, var_decl.comptime_token, .keyword); - try writeToken(builder, var_decl.ast.mut_token, .keyword); - - if (try builder.analyser.resolveTypeOfNode(.{ .node = node, .handle = handle })) |decl_type| { - try colorIdentifierBasedOnType(builder, decl_type, var_decl.ast.mut_token + 1, false, .{ .declaration = true }); - } else { - try writeTokenMod(builder, var_decl.ast.mut_token + 1, .variable, .{ .declaration = true }); - } - - try writeNodeTokens(builder, var_decl.ast.type_node); - try writeNodeTokens(builder, var_decl.ast.align_node); - try writeNodeTokens(builder, var_decl.ast.section_node); - - if (var_decl.ast.init_node != 0) { - const equal_token = tree.firstToken(var_decl.ast.init_node) - 1; - if (token_tags[equal_token] == .equal) { - try writeToken(builder, equal_token, .operator); - } - } - - try writeNodeTokens(builder, var_decl.ast.init_node); + const resolved_type = try builder.analyser.resolveTypeOfNode(.{ .node = node, .handle = handle }); + try writeVarDecl(builder, var_decl, resolved_type); }, .@"usingnamespace" => { const first_token = tree.firstToken(node); @@ -829,9 +807,14 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v .assign_destructure => { const lhs_count = tree.extra_data[node_data[node].lhs]; const lhs_exprs = tree.extra_data[node_data[node].lhs + 1 ..][0..lhs_count]; + const init_expr = node_data[node].rhs; - for (lhs_exprs) |lhs_node| { - try writeNodeTokens(builder, lhs_node); + const resolved_type = try builder.analyser.resolveTypeOfNode(.{ .node = init_expr, .handle = handle }); + + for (lhs_exprs, 0..) |lhs_node, index| { + const var_decl = tree.fullVarDecl(lhs_node).?; + const field_type = if (resolved_type) |ty| try builder.analyser.resolveTupleFieldType(ty, index) else null; + try writeVarDecl(builder, var_decl, field_type); } try writeToken(builder, main_token, .operator); @@ -989,6 +972,35 @@ fn writeContainerField(builder: *Builder, node: Ast.Node.Index, container_decl: } } +fn writeVarDecl(builder: *Builder, var_decl: Ast.full.VarDecl, resolved_type: ?Analyser.Type) error{OutOfMemory}!void { + const tree = builder.handle.tree; + const token_tags = tree.tokens.items(.tag); + + try writeToken(builder, var_decl.visib_token, .keyword); + try writeToken(builder, var_decl.extern_export_token, .keyword); + try writeToken(builder, var_decl.threadlocal_token, .keyword); + try writeToken(builder, var_decl.comptime_token, .keyword); + try writeToken(builder, var_decl.ast.mut_token, .keyword); + + if (resolved_type) |decl_type| { + try colorIdentifierBasedOnType(builder, decl_type, var_decl.ast.mut_token + 1, false, .{ .declaration = true }); + } else { + try writeTokenMod(builder, var_decl.ast.mut_token + 1, .variable, .{ .declaration = true }); + } + + try writeNodeTokens(builder, var_decl.ast.type_node); + try writeNodeTokens(builder, var_decl.ast.align_node); + try writeNodeTokens(builder, var_decl.ast.section_node); + + if (var_decl.ast.init_node != 0) { + const equal_token = tree.firstToken(var_decl.ast.init_node) - 1; + if (token_tags[equal_token] == .equal) { + try writeToken(builder, equal_token, .operator); + } + try writeNodeTokens(builder, var_decl.ast.init_node); + } +} + fn writeIdentifier(builder: *Builder, name_token: Ast.Node.Index) error{OutOfMemory}!void { const handle = builder.handle; const tree = handle.tree; diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig index 4d4bccb4a..ab8dd0538 100644 --- a/tests/lsp_features/completion.zig +++ b/tests/lsp_features/completion.zig @@ -181,7 +181,7 @@ test "symbol lookup on identifier named after primitive" { }); } -test "assign destructure" { +test "var decl destructuring" { try testCompletion( \\test { \\ const foo, var bar: u32 = .{42, 7}; @@ -191,7 +191,15 @@ test "assign destructure" { .{ .label = "foo", .kind = .Constant, .detail = "comptime_int" }, .{ .label = "bar", .kind = .Variable, .detail = "u32" }, }); - if (true) return error.SkipZigTest; // TODO + try testCompletion( + \\test { + \\ var foo, const bar = .{@as(u32, 42), @as(u64, 7)}; + \\ + \\} + , &.{ + .{ .label = "foo", .kind = .Variable, .detail = "u32" }, + .{ .label = "bar", .kind = .Constant, .detail = "u64" }, + }); try testCompletion( \\test { \\ const S, const E = .{struct{}, enum{}}; @@ -201,6 +209,16 @@ test "assign destructure" { .{ .label = "S", .kind = .Struct, .detail = "type" }, .{ .label = "E", .kind = .Enum, .detail = "type" }, }); + try testCompletion( + \\test { + \\ const foo, const bar: u64, var baz = [_]u32{1, 2, 3}; + \\ + \\} + , &.{ + .{ .label = "foo", .kind = .Constant, .detail = "u32" }, + .{ .label = "bar", .kind = .Constant, .detail = "u64" }, + .{ .label = "baz", .kind = .Variable, .detail = "u32" }, + }); } test "function" { diff --git a/tests/lsp_features/hover.zig b/tests/lsp_features/hover.zig index dbb84007c..80c36acd8 100644 --- a/tests/lsp_features/hover.zig +++ b/tests/lsp_features/hover.zig @@ -1111,48 +1111,45 @@ test "var decl alias" { ); } -test "hover - destructuring" { +test "var decl destructuring" { try testHover( - \\fn func() void { - \\ const foo, const bar = .{ 1, 2 }; + \\test { + \\ const foo, const bar = .{ @as(u8, 1), @as(u16, 2), @as(u24, 3) }; \\} , \\```zig \\foo \\``` \\```zig - \\(comptime_int) + \\(u8) \\``` ); try testHover( - \\fn func() void { - \\ const foo, const bar, const baz = .{ 1, 2, 3 }; + \\test { + \\ const foo, const bar, const baz = .{ @as(u8, 1), @as(u16, 2), @as(u24, 3) }; \\} , \\```zig \\bar \\``` \\```zig - \\(comptime_int) + \\(u16) \\``` ); try testHover( - \\fn thing() !struct {usize, isize} { - \\ return .{1, 2}; - \\} - \\fn ex() void { - \\ const foo, const bar = try thing(); + \\test { + \\ const foo, var bar: u32 = .{ 1, 2 }; \\} , \\```zig - \\foo + \\bar \\``` \\```zig - \\(usize) + \\(u32) \\``` ); try testHover( - \\fn func() void { + \\test { \\ const foo, const bar: u32, const baz = undefined; \\} , @@ -1163,6 +1160,21 @@ test "hover - destructuring" { \\(u32) \\``` ); + try testHover( + \\fn thing() !struct {usize, isize} { + \\ return .{1, 2}; + \\} + \\test { + \\ const foo, const bar = try thing(); + \\} + , + \\```zig + \\foo + \\``` + \\```zig + \\(usize) + \\``` + ); } test "escaped identifier" { diff --git a/tests/lsp_features/inlay_hints.zig b/tests/lsp_features/inlay_hints.zig index db4f622f4..1f6d0f918 100644 --- a/tests/lsp_features/inlay_hints.zig +++ b/tests/lsp_features/inlay_hints.zig @@ -208,18 +208,7 @@ test "hide redundant parameter names" { .hide_redundant_param_names_last_token = true, }); } -test "inlay destructuring" { - try testInlayHints( - \\fn func() void { - \\ const foo, const bar = .{1, 2}; - \\} - , .{ .kind = .Type }); - try testInlayHints( - \\fn func() void { - \\ const foo: comptime_int, const bar = .{1, 2}; - \\} - , .{ .kind = .Type }); -} + test "var decl" { try testInlayHints( \\const a<@Vector(2,u8)> = @Vector(2, u8){1,2}; @@ -278,6 +267,24 @@ test "var decl" { , .{ .kind = .Type }); } +test "var decl destructuring" { + try testInlayHints( + \\test { + \\ const foo, const bar = .{@as(u32, 1), 2}; + \\} + , .{ .kind = .Type }); + try testInlayHints( + \\test { + \\ const foo: comptime_int, const bar = .{1, @as(u64, 7)}; + \\} + , .{ .kind = .Type }); + try testInlayHints( + \\test { + \\ const foo, const bar: u64, var baz = [_]u32{1, 2, 3}; + \\} + , .{ .kind = .Type }); +} + test "function alias" { try testInlayHints( \\fn foo(alpha: u32) void { diff --git a/tests/lsp_features/semantic_tokens.zig b/tests/lsp_features/semantic_tokens.zig index 549f9edbb..fa606e68e 100644 --- a/tests/lsp_features/semantic_tokens.zig +++ b/tests/lsp_features/semantic_tokens.zig @@ -185,22 +185,41 @@ test "var decl" { test "var decl destructure" { try testSemanticTokens( - \\const foo = { + \\test { \\ var alpha: bool, var beta = .{ 1, 2 }; \\}; , &.{ - .{ "const", .keyword, .{} }, - .{ "foo", .variable, .{ .declaration = true } }, - .{ "=", .operator, .{} }, + .{ "test", .keyword, .{} }, + .{ "var", .keyword, .{} }, .{ "alpha", .variable, .{ .declaration = true } }, .{ "bool", .type, .{} }, + .{ "var", .keyword, .{} }, .{ "beta", .variable, .{ .declaration = true } }, + .{ "=", .operator, .{} }, .{ "1", .number, .{} }, .{ "2", .number, .{} }, }); + try testSemanticTokens( + \\test { + \\ const S, const E = .{ struct {}, enum {} }; + \\}; + , &.{ + .{ "test", .keyword, .{} }, + + .{ "const", .keyword, .{} }, + .{ "S", .namespace, .{ .declaration = true } }, + + .{ "const", .keyword, .{} }, + .{ "E", .@"enum", .{ .declaration = true } }, + + .{ "=", .operator, .{} }, + + .{ "struct", .keyword, .{} }, + .{ "enum", .keyword, .{} }, + }); } test "local var decl" {