Skip to content

Commit e6b32f9

Browse files
Limit number of combinations when expanding conditional type
Set the maximum to 200 since VS Code's hover popup seems to be limited to ~220 lines anyways.
1 parent 6d73d54 commit e6b32f9

File tree

4 files changed

+118
-31
lines changed

4 files changed

+118
-31
lines changed

src/analysis.zig

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ collect_callsite_references: bool,
4040
resolve_number_literal_values: bool,
4141
/// handle of the doc where the request originated
4242
root_handle: ?*DocumentStore.Handle,
43+
max_conditional_combos: usize = 200,
4344

4445
const NodeSet = std.HashMapUnmanaged(NodeWithUri, void, NodeWithUri.Context, std.hash_map.default_max_load_percentage);
4546

@@ -3689,7 +3690,7 @@ pub const Type = struct {
36893690
/// Drops duplicates
36903691
pub fn getAllTypesWithHandles(ty: Type, analyser: *Analyser) error{OutOfMemory}![]const Type {
36913692
var all_types: ArraySet = .empty;
3692-
try ty.getAllTypesWithHandlesArraySet(analyser, &all_types);
3693+
_ = try ty.getAllTypesWithHandlesArraySet(analyser, &all_types);
36933694
return all_types.keys();
36943695
}
36953696

@@ -3732,12 +3733,15 @@ pub const Type = struct {
37323733
};
37333734
}
37343735

3735-
// is infinite recursion possible here?
3736-
pub fn getAllTypesWithHandlesArraySet(ty: Type, analyser: *Analyser, all_types: *ArraySet) !void {
3736+
/// Returns true if we have reached the limit for analyzing combinations
3737+
pub fn getAllTypesWithHandlesArraySet(ty: Type, analyser: *Analyser, all_types: *ArraySet) !bool {
3738+
if (all_types.count() >= analyser.max_conditional_combos) {
3739+
return true;
3740+
}
37373741
const arena = analyser.arena;
37383742
if (!ty.isConditional()) {
37393743
try all_types.put(arena, ty, {});
3740-
return;
3744+
return false;
37413745
}
37423746
switch (ty.data) {
37433747
.union_tag,
@@ -3749,24 +3753,34 @@ pub const Type = struct {
37493753
.either => |entries| {
37503754
for (entries) |entry| {
37513755
const entry_ty: Type = .{ .data = entry.type_data, .is_type_val = ty.is_type_val };
3752-
try entry_ty.getAllTypesWithHandlesArraySet(analyser, all_types);
3756+
if (try entry_ty.getAllTypesWithHandlesArraySet(analyser, all_types)) {
3757+
return true;
3758+
}
37533759
}
37543760
},
37553761
.anytype_parameter => |info| {
37563762
if (info.type_from_callsite_references) |t| {
3757-
try t.getAllTypesWithHandlesArraySet(analyser, all_types);
3763+
if (try t.getAllTypesWithHandlesArraySet(analyser, all_types)) {
3764+
return true;
3765+
}
37583766
} else {
37593767
try all_types.put(arena, ty, {});
37603768
}
37613769
},
37623770
.optional => |child_ty| {
37633771
for (try child_ty.getAllTypesWithHandles(analyser)) |t| {
3772+
if (all_types.count() >= analyser.max_conditional_combos) {
3773+
return true;
3774+
}
37643775
const new_child_ty = try analyser.allocType(t);
37653776
try all_types.put(arena, .{ .data = .{ .optional = new_child_ty }, .is_type_val = ty.is_type_val }, {});
37663777
}
37673778
},
37683779
inline .pointer, .array => |info, tag| {
37693780
for (try info.elem_ty.getAllTypesWithHandles(analyser)) |t| {
3781+
if (all_types.count() >= analyser.max_conditional_combos) {
3782+
return true;
3783+
}
37703784
var new_info = info;
37713785
new_info.elem_ty = try analyser.allocType(t);
37723786
const data = @unionInit(Type.Data, @tagName(tag), new_info);
@@ -3780,6 +3794,9 @@ pub const Type = struct {
37803794
}
37813795
var iter: ComboIterator = try .init(arena, &possible_types);
37823796
while (iter.next()) |combo| {
3797+
if (all_types.count() >= analyser.max_conditional_combos) {
3798+
return true;
3799+
}
37833800
const new_types = try arena.alloc(Type, types.len);
37843801
for (new_types, types) |*new, old| new.* = combo.get(old).?;
37853802
try all_types.put(arena, .{ .data = .{ .tuple = new_types }, .is_type_val = ty.is_type_val }, {});
@@ -3793,6 +3810,9 @@ pub const Type = struct {
37933810
}
37943811
var iter: ComboIterator = try .init(arena, &possible_types);
37953812
while (iter.next()) |combo| {
3813+
if (all_types.count() >= analyser.max_conditional_combos) {
3814+
return true;
3815+
}
37963816
const new_types = try arena.alloc(Type, types.len);
37973817
for (new_types, types) |*new, old| new.* = combo.get(old).?;
37983818
var new_info = info;
@@ -3808,6 +3828,9 @@ pub const Type = struct {
38083828
}
38093829
var iter: ComboIterator = try .init(arena, &possible_types);
38103830
while (iter.next()) |combo| {
3831+
if (all_types.count() >= analyser.max_conditional_combos) {
3832+
return true;
3833+
}
38113834
var new_info = info;
38123835
new_info.payload = try analyser.allocType(combo.get(info.payload.*).?);
38133836
if (info.error_set) |t| {
@@ -3830,6 +3853,9 @@ pub const Type = struct {
38303853
}
38313854
var iter: ComboIterator = try .init(arena, &possible_types);
38323855
while (iter.next()) |combo| {
3856+
if (all_types.count() >= analyser.max_conditional_combos) {
3857+
return true;
3858+
}
38333859
var new_info = info;
38343860
new_info.container_type = try analyser.allocType(combo.get(info.container_type.*).?);
38353861
new_info.parameters = try arena.alloc(Data.Parameter, info.parameters.len);
@@ -3848,6 +3874,7 @@ pub const Type = struct {
38483874
}
38493875
},
38503876
}
3877+
return false;
38513878
}
38523879

38533880
const ComboIterator = struct {

src/features/completions.zig

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ fn collectContainerNodes(
15121512
.identifier_token_index => |token| token,
15131513
.expr_node_index => |node| {
15141514
if (try builder.analyser.resolveTypeOfNode(.of(node, handle))) |ty| {
1515-
try ty.getAllTypesWithHandlesArraySet(builder.analyser, &types_with_handles);
1515+
_ = try ty.getAllTypesWithHandlesArraySet(builder.analyser, &types_with_handles);
15161516
}
15171517
return types_with_handles.keys();
15181518
},
@@ -1532,7 +1532,7 @@ fn collectContainerNodes(
15321532
const var_decl = handle.tree.fullVarDecl(nodes[1]).?;
15331533
if (nodes[0].toOptional() == var_decl.ast.type_node) {
15341534
if (try builder.analyser.resolveTypeOfNode(.of(nodes[0], handle))) |ty| {
1535-
try ty.getAllTypesWithHandlesArraySet(builder.analyser, &types_with_handles);
1535+
_ = try ty.getAllTypesWithHandlesArraySet(builder.analyser, &types_with_handles);
15361536
return types_with_handles.keys();
15371537
}
15381538
}
@@ -1578,7 +1578,7 @@ fn collectBuiltinContainerNodes(
15781578
dot_context.fn_arg_index,
15791579
handle.tree.source[loc.start..loc.end],
15801580
)) |ty| {
1581-
try ty.getAllTypesWithHandlesArraySet(builder.analyser, types_with_handles);
1581+
_ = try ty.getAllTypesWithHandlesArraySet(builder.analyser, types_with_handles);
15821582
}
15831583
}
15841584

@@ -1595,7 +1595,7 @@ fn collectVarAccessContainerNodes(
15951595
const result = try symbol_decl.resolveType(analyser) orelse return;
15961596
const type_expr = try analyser.resolveDerefType(result) orelse result;
15971597
if (!type_expr.isFunc()) {
1598-
try type_expr.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1598+
_ = try type_expr.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
15991599
return;
16001600
}
16011601

@@ -1604,13 +1604,13 @@ fn collectVarAccessContainerNodes(
16041604
if (dot_context.likely == .enum_comparison or dot_context.need_ret_type) { // => we need f()'s return type
16051605
var node_type = try analyser.resolveReturnType(type_expr) orelse return;
16061606
if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped;
1607-
try node_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1607+
_ = try node_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16081608
return;
16091609
}
16101610
const param_index = dot_context.fn_arg_index;
16111611
if (param_index >= info.parameters.len) return;
16121612
const param_type = info.parameters[param_index].type;
1613-
try param_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1613+
_ = try param_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16141614
}
16151615

16161616
fn collectFieldAccessContainerNodes(
@@ -1632,12 +1632,12 @@ fn collectFieldAccessContainerNodes(
16321632
const container = try analyser.resolveDerefType(result) orelse result;
16331633
if (try analyser.resolveUnwrapErrorUnionType(container, .payload)) |unwrapped| {
16341634
if (unwrapped.isEnumType() or unwrapped.isUnionType()) {
1635-
try unwrapped.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1635+
_ = try unwrapped.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16361636
return;
16371637
}
16381638
}
16391639
// if (dot_context.likely == .enum_literal and !(container.isEnumType() or container.isUnionType())) return;
1640-
try container.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1640+
_ = try container.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16411641
return;
16421642
};
16431643
const name = offsets.locToSlice(handle.tree.source, name_loc);
@@ -1649,7 +1649,7 @@ fn collectFieldAccessContainerNodes(
16491649
if (try analyser.resolveOptionalUnwrap(node_type)) |unwrapped| node_type = unwrapped;
16501650
}
16511651
if (!node_type.isFunc()) {
1652-
try node_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1652+
_ = try node_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16531653
continue;
16541654
}
16551655

@@ -1658,7 +1658,7 @@ fn collectFieldAccessContainerNodes(
16581658
if (dot_context.need_ret_type) { // => we need f()'s return type
16591659
node_type = try analyser.resolveReturnType(node_type) orelse continue;
16601660
if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped;
1661-
try node_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1661+
_ = try node_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16621662
continue;
16631663
}
16641664
// don't have the luxury of referencing an `Ast.full.Call`
@@ -1681,7 +1681,7 @@ fn collectFieldAccessContainerNodes(
16811681
const param_index = dot_context.fn_arg_index + additional_index;
16821682
if (param_index >= params.len) continue;
16831683
const param_type = params[param_index].type;
1684-
try param_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1684+
_ = try param_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
16851685
}
16861686
}
16871687

@@ -1703,7 +1703,7 @@ fn collectEnumLiteralContainerNodes(
17031703
var member_type = try member_decl.resolveType(analyser) orelse continue;
17041704
// Unwrap `x{ .fld_w_opt_type =`
17051705
if (try analyser.resolveOptionalUnwrap(member_type)) |unwrapped| member_type = unwrapped;
1706-
try member_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
1706+
_ = try member_type.getAllTypesWithHandlesArraySet(analyser, types_with_handles);
17071707
}
17081708
}
17091709

@@ -1727,5 +1727,5 @@ fn collectKeywordFnContainerNodes(
17271727
}
17281728
};
17291729
const ty = try builder.analyser.instanceStdBuiltinType(builtin_type_name) orelse return;
1730-
try ty.getAllTypesWithHandlesArraySet(builder.analyser, types_with_handles);
1730+
_ = try ty.getAllTypesWithHandlesArraySet(builder.analyser, types_with_handles);
17311731
}

src/features/hover.zig

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,17 @@ fn hoverSymbolRecursive(
101101

102102
var referenced: Analyser.ReferencedType.Set = .empty;
103103
var resolved_type_strings: std.ArrayListUnmanaged([]const u8) = .empty;
104+
var has_more = false;
104105
if (try decl_handle.resolveType(analyser)) |resolved_type| {
105106
if (try resolved_type.docComments(arena)) |doc|
106107
try doc_strings.append(arena, doc);
107108
const typeof = try resolved_type.typeOf(analyser);
108-
const possible_types = try typeof.getAllTypesWithHandles(analyser);
109-
for (possible_types) |ty| {
109+
var possible_types: Analyser.Type.ArraySet = .empty;
110+
has_more = try typeof.getAllTypesWithHandlesArraySet(analyser, &possible_types);
111+
for (possible_types.keys()) |ty| {
110112
try resolved_type_strings.append(arena, try std.fmt.allocPrint(arena, "{}", .{ty.fmtTypeVal(analyser, .{
111113
.referenced = &referenced,
112-
.truncate_container_decls = possible_types.len > 1,
114+
.truncate_container_decls = possible_types.count() > 1,
113115
})}));
114116
}
115117
}
@@ -120,6 +122,7 @@ fn hoverSymbolRecursive(
120122
doc_strings.items,
121123
def_str,
122124
resolved_type_strings.items,
125+
has_more,
123126
referenced_types,
124127
);
125128
}
@@ -130,6 +133,7 @@ fn hoverSymbolResolved(
130133
doc_strings: []const []const u8,
131134
def_str: []const u8,
132135
resolved_type_strings: []const []const u8,
136+
has_more: bool,
133137
referenced_types: []const Analyser.ReferencedType,
134138
) error{OutOfMemory}![]const u8 {
135139
var hover_text: std.ArrayListUnmanaged(u8) = .empty;
@@ -140,6 +144,8 @@ fn hoverSymbolResolved(
140144
try writer.print("\n```zig\n({s})\n```", .{resolved_type_str});
141145
if (resolved_type_strings.len == 0)
142146
try writer.writeAll("\n```zig\n(unknown)\n```");
147+
if (has_more)
148+
try writer.print("\n```txt\n(...)\n```", .{});
143149
if (referenced_types.len > 0)
144150
try writer.print("\n\n" ++ "Go to ", .{});
145151
for (referenced_types, 0..) |ref, index| {
@@ -155,6 +161,8 @@ fn hoverSymbolResolved(
155161
try writer.print("\n({s})", .{resolved_type_str});
156162
if (resolved_type_strings.len == 0)
157163
try writer.writeAll("\n(unknown)");
164+
if (has_more)
165+
try writer.print("\n(...)", .{});
158166
}
159167

160168
if (doc_strings.len > 0) {
@@ -288,7 +296,7 @@ fn hoverDefinitionGlobal(
288296
if (std.mem.eql(u8, name, "_")) return null;
289297
if (try analyser.resolvePrimitive(name)) |ip_index| {
290298
const resolved_type_str = try std.fmt.allocPrint(arena, "{}", .{analyser.ip.typeOf(ip_index).fmt(analyser.ip)});
291-
break :blk try hoverSymbolResolved(arena, markup_kind, &.{}, name, &.{resolved_type_str}, &.{});
299+
break :blk try hoverSymbolResolved(arena, markup_kind, &.{}, name, &.{resolved_type_str}, false, &.{});
292300
}
293301
}
294302
const decl = (try analyser.lookupSymbolGlobal(handle, name, pos_index)) orelse return null;

0 commit comments

Comments
 (0)