Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6767,6 +6767,11 @@ pub fn resolveExpressionTypeFromAncestors(
if (try analyser.resolveDerefType(expr_ty)) |ty| {
return ty;
}
if (try analyser.resolveOptionalUnwrap(expr_ty)) |ty| {
if (try analyser.resolveDerefType(ty)) |typ| {
return typ;
}
}

switch (expr_ty.data) {
.pointer => |info| switch (info.size) {
Expand Down
22 changes: 17 additions & 5 deletions src/features/completions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,7 @@ fn getEnumLiteralContext(

var dot_context: EnumLiteralContext = .{ .likely = .enum_literal };

switch (tree.tokenTag(token_index)) {
tag: switch (tree.tokenTag(token_index)) {
.equal => {
token_index -= 1;
dot_context.need_ret_type = tree.tokenTag(token_index) == .r_paren;
Expand Down Expand Up @@ -1231,6 +1231,10 @@ fn getEnumLiteralContext(
.l_brace, .comma, .l_paren => {
dot_context = getSwitchOrStructInitContext(tree, dot_token_index, nodes) orelse return null;
},
.ampersand => {
token_index -= 1;
continue :tag tree.tokenTag(token_index);
},
else => return null,
}
return dot_context;
Expand Down Expand Up @@ -1482,8 +1486,14 @@ fn collectContainerFields(
container: Analyser.Type,
omit_members: std.BufSet,
) Analyser.Error!void {
const info, const type_maybe = switch (container.data) {
const info, const type_maybe = info: switch (container.data) {
.container => |info| .{ info, null },
.optional => |opt| {
continue :info opt.data;
},
.pointer => |ptr| {
continue :info ptr.elem_ty.data;
},
.union_tag => |union_ty| blk: {
const info = union_ty.data.container;
const ty = try container.instanceTypeVal(builder.analyser) orelse container;
Expand Down Expand Up @@ -1735,7 +1745,9 @@ fn collectVarAccessContainerNodes(

const symbol_decl = try analyser.lookupSymbolGlobal(handle, handle.tree.source[loc.start..loc.end], loc.end) orelse return;
const result = try symbol_decl.resolveType(analyser) orelse return;
const type_expr = try analyser.resolveDerefType(result) orelse result;
var type_expr = try analyser.resolveDerefType(result) orelse result;
if (type_expr.is_type_val) type_expr = type_expr.resolveDeclLiteralResultType();

if (!type_expr.isFunc()) {
_ = try type_expr.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
return;
Expand All @@ -1751,7 +1763,7 @@ fn collectVarAccessContainerNodes(
}
const param_index = dot_context.fn_arg_index;
if (param_index >= info.parameters.len) return;
const param_type = info.parameters[param_index].type;
const param_type = info.parameters[param_index].type.resolveDeclLiteralResultType();
_ = try param_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
}

Expand Down Expand Up @@ -1785,7 +1797,7 @@ fn collectFieldAccessTypes(
const params = info.parameters;
const param_index = dot_context.fn_arg_index + @intFromBool(has_self_param);
if (param_index >= params.len) return;
const param_type = params[param_index].type;
const param_type = params[param_index].type.resolveDeclLiteralResultType();
_ = try param_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
}

Expand Down
122 changes: 122 additions & 0 deletions tests/lsp_features/completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,67 @@ test "decl literal function call" {
});
}

test "function call struct parameter completion" {
try testCompletion(
\\fn foo(s: struct {field: u32}) void {}
\\fn bar() void {
\\ foo(.<cursor>);
\\}
, &.{
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\fn foo(s: struct {field: u32}) void {}
\\fn bar() void {
\\ foo(.{.<cursor>);
\\}
, &.{
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\fn foo(s: ?struct {field: u32}) void {}
\\fn bar() void {
\\ foo(.<cursor>);
\\}
, &.{
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\fn foo(s: ?struct {field: u32}) void {}
\\fn bar() void {
\\ foo(.{.<cursor>);
\\}
, &.{
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\fn foo(s: *const struct {field: u32}) void {}
\\fn bar() void {
\\ foo(.<cursor>);
\\}
, &.{
// While the completion won't actually result in code that will compile due to creating not a pointer
// This will still assist user in actually writing code - the compiler will do the rest.
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\fn foo(s: *const struct {field: u32}) void {}
\\fn bar() void {
\\ foo(.{.<cursor>);
\\}
, &.{
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\fn foo(s: *const struct {field: u32}) void {}
\\fn bar() void {
\\ foo(&.<cursor>);
\\}
, &.{
.{ .label = "field", .kind = .Field, .detail = "u32" },
});
}

test "enum literal" {
try testCompletion(
\\const literal = .foo;
Expand Down Expand Up @@ -2558,6 +2619,67 @@ test "structinit - fields with and without default value" {
});
}

test "structinit - anonymous" {
try testCompletion(
\\fn foo() E {
\\ const foo2: struct { alpha: u32 } = .a<cursor>
\\}
, &.{
.{ .label = "alpha", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\var foo: struct { alpha: u32 } = undefined;
\\foo = .<cursor>
, &.{
.{ .label = "alpha", .kind = .Field, .detail = "u32" },
});
// try testCompletion(
// \\fn foo() E {
// \\ const foo2: struct { alpha: u32 } = .{.<cursor>
// \\}
// , &.{
// .{ .label = "alpha", .kind = .Field, .detail = "u32" },
// });
// try testCompletion(
// \\fn foo() E {
// \\ const foo2: struct { alpha: u32 } = .<cursor>
// \\}
// , &.{
// .{ .label = "alpha", .kind = .Field, .detail = "u32" },
// });
// try testCompletion(
// \\const foo2: struct { alpha: u32 } = .a<cursor>
// , &.{
// .{ .label = "alpha", .kind = .Field, .detail = "u32" },
// });
}

test "structinit - address of" {
try testCompletion(
\\const T = struct { a: u32 };
\\var t: ?*const T = &.<cursor>
, &.{
.{ .label = "a", .kind = .Field, .detail = "u32" },
});
try testCompletion(
\\const T = struct { a: u32 };
\\var t: ?*const T = undefined;
\\t = &.<cursor>
, &.{
.{ .label = "a", .kind = .Field, .detail = "u32" },
});
// try testCompletion(
// \\var t: *const struct { a: u32 } = &.<cursor>
// , &.{
// .{ .label = "a", .kind = .Field, .detail = "u32" },
// });
// try testCompletion(
// \\var t: ?*const struct { a: u32 } = &.<cursor>
// , &.{
// .{ .label = "a", .kind = .Field, .detail = "u32" },
// });
}

test "return - enum" {
try testCompletion(
\\const E = enum {
Expand Down
33 changes: 33 additions & 0 deletions tests/lsp_features/hover.zig
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,39 @@ test "inferred struct init" {
\\
\\Go to [S](untitled:///Untitled-0.zig#L1)
);
try testHover(
\\const S = struct { a: u32 };
\\var t: ?*const S = &.{ .a<cursor> = 5 };
,
\\```zig
\\a: u32
\\```
\\```zig
\\(u32)
\\```
);
try testHover(
\\var t: ?*const struct { a: u32 } = &.{ .a<cursor> = 5 };
,
\\```zig
\\a: u32
\\```
\\```zig
\\(u32)
\\```
);
try testHover(
\\const S = struct { a: u32 };
\\var t: ?*const S = undefined;
\\fn f(_: S) void { t = &.{ .a<cursor> = 5 }; }
,
\\```zig
\\a: u32
\\```
\\```zig
\\(u32)
\\```
);
}

test "decl literal" {
Expand Down
Loading