Skip to content

Commit 76e59de

Browse files
committed
improve tuple literal code analysis
- resolve destructured variable declarations according to the initialization expression - semantic tokens will highlight destructured variable declarations - don't treat tuple literals as array literals (see #2065)
1 parent a5f518d commit 76e59de

File tree

7 files changed

+142
-83
lines changed

7 files changed

+142
-83
lines changed

src/analysis.zig

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,9 +1108,18 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi
11081108
}
11091109
}
11101110

1111-
fn resolveTupleFieldType(analyser: *Analyser, tuple: Type, index: usize) error{OutOfMemory}!?Type {
1111+
pub fn resolveTupleFieldType(analyser: *Analyser, tuple: Type, index: usize) error{OutOfMemory}!?Type {
11121112
const scope_handle = switch (tuple.data) {
11131113
.container => |s| s,
1114+
.other => |node| {
1115+
var buffer: [2]Ast.Node.Index = undefined;
1116+
const array_init_info = node.handle.tree.fullArrayInit(&buffer, node.node) orelse return null;
1117+
1118+
const elements = array_init_info.ast.elements;
1119+
if (index >= elements.len) return null;
1120+
1121+
return try analyser.resolveTypeOfNode(.{ .handle = node.handle, .node = elements[index] });
1122+
},
11141123
else => return null,
11151124
};
11161125
const node = scope_handle.toNode();
@@ -1692,26 +1701,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
16921701
var buffer: [2]Ast.Node.Index = undefined;
16931702
const array_init_info = tree.fullArrayInit(&buffer, node).?;
16941703

1695-
std.debug.assert(array_init_info.ast.elements.len != 0);
1696-
16971704
if (array_init_info.ast.type_expr != 0) blk: {
16981705
const array_ty = try analyser.resolveTypeOfNode(.{ .node = array_init_info.ast.type_expr, .handle = handle }) orelse break :blk;
16991706
return try array_ty.instanceTypeVal(analyser);
17001707
}
17011708

1702-
// try to infer the array type
1703-
const maybe_elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = array_init_info.ast.elements[0], .handle = handle });
1704-
const elem_ty = if (maybe_elem_ty) |elem_ty| elem_ty.typeOf(analyser) else try Type.typeValFromIP(analyser, .type_type);
1705-
1706-
const elem_ty_ptr = try analyser.arena.allocator().create(Type);
1707-
elem_ty_ptr.* = elem_ty;
1708-
17091709
return Type{
1710-
.data = .{ .array = .{
1711-
.elem_count = @intCast(array_init_info.ast.elements.len),
1712-
.sentinel = .none,
1713-
.elem_ty = elem_ty_ptr,
1714-
} },
1710+
.data = .{ .other = node_handle },
17151711
.is_type_val = false,
17161712
};
17171713
},
@@ -2372,6 +2368,8 @@ pub const Type = struct {
23722368

23732369
/// - Error type: `Foo || Bar`, `Foo!Bar`
23742370
/// - Function: `fn () Foo`, `fn foo() Foo`
2371+
/// - `.{a,b}`
2372+
/// - `start..end`
23752373
other: NodeWithHandle,
23762374

23772375
/// - `@compileError("")`
@@ -4011,7 +4009,7 @@ pub const DeclWithHandle = struct {
40114009
}) orelse return null;
40124010
break :blk switch (node.data) {
40134011
.array => |array_info| try array_info.elem_ty.instanceTypeVal(analyser),
4014-
.container => try analyser.resolveTupleFieldType(node, pay.index),
4012+
.container, .other => try analyser.resolveTupleFieldType(node, pay.index),
40154013
else => null,
40164014
};
40174015
},

src/features/completions.zig

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi
121121
try typeToCompletion(builder, rhs_ty);
122122
}
123123
},
124-
125-
.fn_proto,
126-
.fn_proto_multi,
127-
.fn_proto_one,
128-
.fn_proto_simple,
129-
.fn_decl,
130-
=> {},
131-
else => unreachable,
124+
else => {},
132125
},
133126
.ip_index => |payload| try analyser_completions.dotCompletions(
134127
builder.arena,

src/features/semantic_tokens.zig

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -312,30 +312,8 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v
312312
.aligned_var_decl,
313313
=> {
314314
const var_decl = tree.fullVarDecl(node).?;
315-
try writeToken(builder, var_decl.visib_token, .keyword);
316-
try writeToken(builder, var_decl.extern_export_token, .keyword);
317-
try writeToken(builder, var_decl.threadlocal_token, .keyword);
318-
try writeToken(builder, var_decl.comptime_token, .keyword);
319-
try writeToken(builder, var_decl.ast.mut_token, .keyword);
320-
321-
if (try builder.analyser.resolveTypeOfNode(.{ .node = node, .handle = handle })) |decl_type| {
322-
try colorIdentifierBasedOnType(builder, decl_type, var_decl.ast.mut_token + 1, false, .{ .declaration = true });
323-
} else {
324-
try writeTokenMod(builder, var_decl.ast.mut_token + 1, .variable, .{ .declaration = true });
325-
}
326-
327-
try writeNodeTokens(builder, var_decl.ast.type_node);
328-
try writeNodeTokens(builder, var_decl.ast.align_node);
329-
try writeNodeTokens(builder, var_decl.ast.section_node);
330-
331-
if (var_decl.ast.init_node != 0) {
332-
const equal_token = tree.firstToken(var_decl.ast.init_node) - 1;
333-
if (token_tags[equal_token] == .equal) {
334-
try writeToken(builder, equal_token, .operator);
335-
}
336-
}
337-
338-
try writeNodeTokens(builder, var_decl.ast.init_node);
315+
const resolved_type = try builder.analyser.resolveTypeOfNode(.{ .node = node, .handle = handle });
316+
try writeVarDecl(builder, var_decl, resolved_type);
339317
},
340318
.@"usingnamespace" => {
341319
const first_token = tree.firstToken(node);
@@ -829,9 +807,14 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v
829807
.assign_destructure => {
830808
const lhs_count = tree.extra_data[node_data[node].lhs];
831809
const lhs_exprs = tree.extra_data[node_data[node].lhs + 1 ..][0..lhs_count];
810+
const init_expr = node_data[node].rhs;
832811

833-
for (lhs_exprs) |lhs_node| {
834-
try writeNodeTokens(builder, lhs_node);
812+
const resolved_type = try builder.analyser.resolveTypeOfNode(.{ .node = init_expr, .handle = handle });
813+
814+
for (lhs_exprs, 0..) |lhs_node, index| {
815+
const var_decl = tree.fullVarDecl(lhs_node).?;
816+
const field_type = if (resolved_type) |ty| try builder.analyser.resolveTupleFieldType(ty, index) else null;
817+
try writeVarDecl(builder, var_decl, field_type);
835818
}
836819

837820
try writeToken(builder, main_token, .operator);
@@ -989,6 +972,35 @@ fn writeContainerField(builder: *Builder, node: Ast.Node.Index, container_decl:
989972
}
990973
}
991974

975+
fn writeVarDecl(builder: *Builder, var_decl: Ast.full.VarDecl, resolved_type: ?Analyser.Type) error{OutOfMemory}!void {
976+
const tree = builder.handle.tree;
977+
const token_tags = tree.tokens.items(.tag);
978+
979+
try writeToken(builder, var_decl.visib_token, .keyword);
980+
try writeToken(builder, var_decl.extern_export_token, .keyword);
981+
try writeToken(builder, var_decl.threadlocal_token, .keyword);
982+
try writeToken(builder, var_decl.comptime_token, .keyword);
983+
try writeToken(builder, var_decl.ast.mut_token, .keyword);
984+
985+
if (resolved_type) |decl_type| {
986+
try colorIdentifierBasedOnType(builder, decl_type, var_decl.ast.mut_token + 1, false, .{ .declaration = true });
987+
} else {
988+
try writeTokenMod(builder, var_decl.ast.mut_token + 1, .variable, .{ .declaration = true });
989+
}
990+
991+
try writeNodeTokens(builder, var_decl.ast.type_node);
992+
try writeNodeTokens(builder, var_decl.ast.align_node);
993+
try writeNodeTokens(builder, var_decl.ast.section_node);
994+
995+
if (var_decl.ast.init_node != 0) {
996+
const equal_token = tree.firstToken(var_decl.ast.init_node) - 1;
997+
if (token_tags[equal_token] == .equal) {
998+
try writeToken(builder, equal_token, .operator);
999+
}
1000+
try writeNodeTokens(builder, var_decl.ast.init_node);
1001+
}
1002+
}
1003+
9921004
fn writeIdentifier(builder: *Builder, name_token: Ast.Node.Index) error{OutOfMemory}!void {
9931005
const handle = builder.handle;
9941006
const tree = handle.tree;

tests/lsp_features/completion.zig

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ test "symbol lookup on identifier named after primitive" {
181181
});
182182
}
183183

184-
test "assign destructure" {
184+
test "var decl destructuring" {
185185
try testCompletion(
186186
\\test {
187187
\\ const foo, var bar: u32 = .{42, 7};
@@ -191,7 +191,15 @@ test "assign destructure" {
191191
.{ .label = "foo", .kind = .Constant, .detail = "comptime_int" },
192192
.{ .label = "bar", .kind = .Variable, .detail = "u32" },
193193
});
194-
if (true) return error.SkipZigTest; // TODO
194+
try testCompletion(
195+
\\test {
196+
\\ var foo, const bar = .{@as(u32, 42), @as(u64, 7)};
197+
\\ <cursor>
198+
\\}
199+
, &.{
200+
.{ .label = "foo", .kind = .Variable, .detail = "u32" },
201+
.{ .label = "bar", .kind = .Constant, .detail = "u64" },
202+
});
195203
try testCompletion(
196204
\\test {
197205
\\ const S, const E = .{struct{}, enum{}};
@@ -201,6 +209,16 @@ test "assign destructure" {
201209
.{ .label = "S", .kind = .Struct, .detail = "type" },
202210
.{ .label = "E", .kind = .Enum, .detail = "type" },
203211
});
212+
try testCompletion(
213+
\\test {
214+
\\ const foo, const bar: u64, var baz = [_]u32{1, 2, 3};
215+
\\ <cursor>
216+
\\}
217+
, &.{
218+
.{ .label = "foo", .kind = .Constant, .detail = "u32" },
219+
.{ .label = "bar", .kind = .Constant, .detail = "u64" },
220+
.{ .label = "baz", .kind = .Variable, .detail = "u32" },
221+
});
204222
}
205223

206224
test "function" {

tests/lsp_features/hover.zig

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,48 +1111,45 @@ test "var decl alias" {
11111111
);
11121112
}
11131113

1114-
test "hover - destructuring" {
1114+
test "var decl destructuring" {
11151115
try testHover(
1116-
\\fn func() void {
1117-
\\ const f<cursor>oo, const bar = .{ 1, 2 };
1116+
\\test {
1117+
\\ const f<cursor>oo, const bar = .{ @as(u8, 1), @as(u16, 2), @as(u24, 3) };
11181118
\\}
11191119
,
11201120
\\```zig
11211121
\\foo
11221122
\\```
11231123
\\```zig
1124-
\\(comptime_int)
1124+
\\(u8)
11251125
\\```
11261126
);
11271127
try testHover(
1128-
\\fn func() void {
1129-
\\ const foo, const b<cursor>ar, const baz = .{ 1, 2, 3 };
1128+
\\test {
1129+
\\ const foo, const b<cursor>ar, const baz = .{ @as(u8, 1), @as(u16, 2), @as(u24, 3) };
11301130
\\}
11311131
,
11321132
\\```zig
11331133
\\bar
11341134
\\```
11351135
\\```zig
1136-
\\(comptime_int)
1136+
\\(u16)
11371137
\\```
11381138
);
11391139
try testHover(
1140-
\\fn thing() !struct {usize, isize} {
1141-
\\ return .{1, 2};
1142-
\\}
1143-
\\fn ex() void {
1144-
\\ const f<cursor>oo, const bar = try thing();
1140+
\\test {
1141+
\\ const foo, var b<cursor>ar: u32 = .{ 1, 2 };
11451142
\\}
11461143
,
11471144
\\```zig
1148-
\\foo
1145+
\\bar
11491146
\\```
11501147
\\```zig
1151-
\\(usize)
1148+
\\(u32)
11521149
\\```
11531150
);
11541151
try testHover(
1155-
\\fn func() void {
1152+
\\test {
11561153
\\ const foo, const b<cursor>ar: u32, const baz = undefined;
11571154
\\}
11581155
,
@@ -1163,6 +1160,21 @@ test "hover - destructuring" {
11631160
\\(u32)
11641161
\\```
11651162
);
1163+
try testHover(
1164+
\\fn thing() !struct {usize, isize} {
1165+
\\ return .{1, 2};
1166+
\\}
1167+
\\test {
1168+
\\ const f<cursor>oo, const bar = try thing();
1169+
\\}
1170+
,
1171+
\\```zig
1172+
\\foo
1173+
\\```
1174+
\\```zig
1175+
\\(usize)
1176+
\\```
1177+
);
11661178
}
11671179

11681180
test "escaped identifier" {

tests/lsp_features/inlay_hints.zig

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,7 @@ test "hide redundant parameter names" {
208208
.hide_redundant_param_names_last_token = true,
209209
});
210210
}
211-
test "inlay destructuring" {
212-
try testInlayHints(
213-
\\fn func() void {
214-
\\ const foo<comptime_int>, const bar<comptime_int> = .{1, 2};
215-
\\}
216-
, .{ .kind = .Type });
217-
try testInlayHints(
218-
\\fn func() void {
219-
\\ const foo: comptime_int, const bar<comptime_int> = .{1, 2};
220-
\\}
221-
, .{ .kind = .Type });
222-
}
211+
223212
test "var decl" {
224213
try testInlayHints(
225214
\\const a<@Vector(2,u8)> = @Vector(2, u8){1,2};
@@ -278,6 +267,24 @@ test "var decl" {
278267
, .{ .kind = .Type });
279268
}
280269

270+
test "var decl destructuring" {
271+
try testInlayHints(
272+
\\test {
273+
\\ const foo<u32>, const bar<comptime_int> = .{@as(u32, 1), 2};
274+
\\}
275+
, .{ .kind = .Type });
276+
try testInlayHints(
277+
\\test {
278+
\\ const foo: comptime_int, const bar<u64> = .{1, @as(u64, 7)};
279+
\\}
280+
, .{ .kind = .Type });
281+
try testInlayHints(
282+
\\test {
283+
\\ const foo<u32>, const bar: u64, var baz<u32> = [_]u32{1, 2, 3};
284+
\\}
285+
, .{ .kind = .Type });
286+
}
287+
281288
test "function alias" {
282289
try testInlayHints(
283290
\\fn foo(alpha: u32) void {

tests/lsp_features/semantic_tokens.zig

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,41 @@ test "var decl" {
185185

186186
test "var decl destructure" {
187187
try testSemanticTokens(
188-
\\const foo = {
188+
\\test {
189189
\\ var alpha: bool, var beta = .{ 1, 2 };
190190
\\};
191191
, &.{
192-
.{ "const", .keyword, .{} },
193-
.{ "foo", .variable, .{ .declaration = true } },
194-
.{ "=", .operator, .{} },
192+
.{ "test", .keyword, .{} },
193+
195194
.{ "var", .keyword, .{} },
196195
.{ "alpha", .variable, .{ .declaration = true } },
197196
.{ "bool", .type, .{} },
197+
198198
.{ "var", .keyword, .{} },
199199
.{ "beta", .variable, .{ .declaration = true } },
200+
200201
.{ "=", .operator, .{} },
201202
.{ "1", .number, .{} },
202203
.{ "2", .number, .{} },
203204
});
205+
try testSemanticTokens(
206+
\\test {
207+
\\ const S, const E = .{ struct {}, enum {} };
208+
\\};
209+
, &.{
210+
.{ "test", .keyword, .{} },
211+
212+
.{ "const", .keyword, .{} },
213+
.{ "S", .namespace, .{ .declaration = true } },
214+
215+
.{ "const", .keyword, .{} },
216+
.{ "E", .@"enum", .{ .declaration = true } },
217+
218+
.{ "=", .operator, .{} },
219+
220+
.{ "struct", .keyword, .{} },
221+
.{ "enum", .keyword, .{} },
222+
});
204223
}
205224

206225
test "local var decl" {

0 commit comments

Comments
 (0)