Skip to content

Commit 7982c48

Browse files
lukaszsamsonmsaraiva
authored andcommitted
Stability improvements in TypeInfo (#59)
* failing tests added * fix numerous crashes * expand var_params recursively, stop if recursive type detected * handle cases when more than one spec variant defined * return suggestions for atom only options
1 parent a3e7336 commit 7982c48

File tree

5 files changed

+504
-16
lines changed

5 files changed

+504
-16
lines changed

lib/elixir_sense/core/type_info.ex

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ defmodule ElixirSense.Core.TypeInfo do
273273

274274
defp get_param_type_specs(func_specs, npar) do
275275
for func_spec <- func_specs,
276-
params_types = extract_params_types(func_spec),
276+
params_types <- extract_params_types_variants(func_spec),
277277
length(params_types) > npar do
278278
params_types |> Enum.at(npar)
279279
end
@@ -341,6 +341,31 @@ defmodule ElixirSense.Core.TypeInfo do
341341
extract_tagged_tuple_name_and_type({mod, type})
342342
end
343343

344+
defp extract_union_options_name_and_type({mod, {_kind, {_, {:atom, _, name}, _}}}) do
345+
[{mod, name}]
346+
end
347+
348+
defp extract_union_options_name_and_type(
349+
{mod, {_kind, {_name, {:remote_type, _, _} = type, _}}}
350+
) do
351+
extract_tagged_tuple_name_and_type({mod, type})
352+
end
353+
354+
defp extract_union_options_name_and_type(
355+
{mod, {_kind, {_name, {:user_type, _, _, _} = type, _}}}
356+
) do
357+
extract_tagged_tuple_name_and_type({mod, type})
358+
end
359+
360+
defp extract_union_options_name_and_type({mod, {:atom, _, atom}}) when is_atom(atom) do
361+
[{mod, atom}]
362+
end
363+
364+
# skip unknown type
365+
defp extract_union_options_name_and_type(_) do
366+
[]
367+
end
368+
344369
defp extract_tagged_tuple_name_and_type({mod, {:type, _, :tuple, [{:atom, _, name}, type]}}) do
345370
[{mod, name, type}]
346371
end
@@ -350,6 +375,9 @@ defmodule ElixirSense.Core.TypeInfo do
350375
{_mod, {_kind, {_name, {:type, _, :union, _}, _}}} = expanded_type ->
351376
extract_union_options_name_and_type(expanded_type)
352377

378+
{mod, {:atom, _, name}} ->
379+
[{mod, name}]
380+
353381
_ ->
354382
[]
355383
end
@@ -395,13 +423,16 @@ defmodule ElixirSense.Core.TypeInfo do
395423
false
396424
end
397425

398-
defp extract_params_types(
399-
{:spec, {_, [{:type, _, :fun, [{:type, _, :product, params_types}, _]}]}}
400-
) do
426+
defp extract_params_types_variants({:spec, {_, list}}) do
427+
list
428+
|> Enum.map(&extract_params_types/1)
429+
end
430+
431+
defp extract_params_types({:type, _, :fun, [{:type, _, :product, params_types}, _]}) do
401432
params_types
402433
end
403434

404-
defp extract_params_types({:spec, {_, [{:type, _, :bounded_fun, [type, constraints]}]}}) do
435+
defp extract_params_types({:type, _, :bounded_fun, [type, constraints]}) do
405436
{:type, _, :fun, [{:type, _, :product, params}, _]} = type
406437

407438
vars_types =
@@ -411,16 +442,41 @@ defmodule ElixirSense.Core.TypeInfo do
411442
{var, var_type}
412443
end
413444

414-
Enum.map(params, fn
415-
{:var, _, name} ->
416-
vars_types[name]
445+
params
446+
|> Enum.map(&expand_var_types(&1, vars_types, []))
447+
# reject failed expansions
448+
|> Enum.reject(&is_nil/1)
449+
end
417450

418-
{:type, l, :list, [{:var, _, name}]} ->
419-
{:type, l, :list, [vars_types[name]]}
451+
defp expand_var_types(var_type, vars_types, expanded_types) do
452+
if var_type in expanded_types do
453+
# break recursive type expansion
454+
nil
455+
else
456+
do_expand_var_types(var_type, vars_types, [var_type | expanded_types])
457+
end
458+
end
420459

421-
type ->
422-
type
423-
end)
460+
defp do_expand_var_types({:var, _, name}, vars_types, expanded_types) do
461+
expand_var_types(vars_types[name], vars_types, expanded_types)
462+
end
463+
464+
defp do_expand_var_types({:type, l, kind, tuple_elements}, vars_types, expanded_types)
465+
when kind in [:list, :tuple, :union] and is_list(tuple_elements) do
466+
expanded =
467+
for(element <- tuple_elements, do: expand_var_types(element, vars_types, expanded_types))
468+
# reject failed expansions
469+
|> Enum.reject(&is_nil/1)
470+
471+
{:type, l, kind, expanded}
472+
end
473+
474+
defp do_expand_var_types({:ann_type, _l, [{:var, _, _}, type]}, vars_types, expanded_types) do
475+
expand_var_types(type, vars_types, expanded_types)
476+
end
477+
478+
defp do_expand_var_types(type, _vars_types, _expanded_types) do
479+
type
424480
end
425481

426482
defp starts_with_type_def?(str, kind) do

lib/elixir_sense/providers/suggestion.ex

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,20 @@ defmodule ElixirSense.Providers.Suggestion do
441441
end
442442

443443
defp options_to_suggestions(options, original_module) do
444-
Enum.map(options, fn {mod, name, type} ->
445-
TypeInfo.get_type_info(mod, type, original_module)
446-
|> Map.merge(%{type: :param_option, name: name})
444+
Enum.map(options, fn
445+
{mod, name, type} ->
446+
TypeInfo.get_type_info(mod, type, original_module)
447+
|> Map.merge(%{type: :param_option, name: name})
448+
449+
{mod, name} ->
450+
%{
451+
doc: "",
452+
expanded_spec: "",
453+
name: name,
454+
origin: inspect(mod),
455+
type: :param_option,
456+
type_spec: ""
457+
}
447458
end)
448459
end
449460

0 commit comments

Comments
 (0)