Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type info not able to properly expand var types #181

Open
lukaszsamson opened this issue Mar 4, 2023 · 1 comment
Open

Type info not able to properly expand var types #181

lukaszsamson opened this issue Mar 4, 2023 · 1 comment

Comments

@lukaszsamson
Copy link
Collaborator

lukaszsamson commented Mar 4, 2023

var params in fun, remote and local type expansion is broken in TypeInfo.
Maybe we should remove it completely. It's used only in param options completions

@lukaszsamson
Copy link
Collaborator Author

Some examples of broken stuff

defmodule ElixirSenseExample.ModuleWithTypespecs do
  defmodule Remote do
    @typedoc "Remote type"
    @type remote_t :: atom

    @typedoc "Remote type with params"
    @type remote_t(a, b) :: {a, b}

    @typedoc "Remote list type"
    @type remote_list_t :: [remote_t]

    @type remote_option_t :: {:remote_option_1, remote_t} | {:remote_option_2, remote_list_t}
  end

  defmodule OtherRemote do
    @type other :: Remote.remote_option_t()
    @type bounded_type(t) :: {t, integer}
    @type some :: :a
  end

  defmodule Local do
    alias Remote, as: R

    @typep private_t :: atom

    @typedoc "Local opaque type"
    @opaque opaque_t :: atom

    @typedoc "Local type"
    @type local_t :: atom

    @typedoc "Local type with params"
    @type local_t(a, b) :: {a, b}

    @typedoc "Local union type"
    @type union_t :: atom | integer

    @typedoc "Local list type"
    @type list_t :: [:trace | :log]

    @typedoc "Local type with large spec"
    @type large_t :: pid | port | (registered_name :: atom) | {registered_name :: atom, node}

    @typedoc "Remote type from aliased module"
    @type remote_aliased_t :: R.remote_t() | R.remote_list_t()

    @type tuple_opt_t :: {:opt_name, :opt_value}

    @typedoc "Local keyword-value type"
    @type option_t ::
            {:local_o, local_t}
            | {:local_with_params_o, local_t(atom, integer)}
            | {:union_o, union_t}
            | {:inline_union_o, :a | :b}
            | {:list_o, list_t}
            | {:inline_list_o, [:trace | :log]}
            | {:basic_o, pid}
            | {:basic_with_params_o, nonempty_list(atom)}
            | {:builtin_o, keyword}
            | {:builtin_with_params_o, keyword(term)}
            | {:remote_o, Remote.remote_t()}
            | {:remote_with_params_o, Remote.remote_t(atom, integer)}
            | {:remote_aliased_o, remote_aliased_t}
            | {:remote_aliased_inline_o, R.remote_t()}
            | {:private_o, private_t}
            | {:opaque_o, opaque_t}
            | {:non_existent_o, Remote.non_existent()}
            | {:large_o, large_t}

    @typedoc "Extra option"
    @type extra_option_t :: {:option_1, atom} | {:option_2, integer}

    @typedoc "Options"
    @type options_t :: [option_t]

    @typedoc "Option | Extra option"
    @type option_or_extra_option_t ::
            {:option_1, boolean} | {:option_2, timeout} | Remote.remote_option_t()

    @type extra_option_1_t :: extra_option_t

    @type atom_opt_t :: :atom_opt

    @type bounded_type(t) :: {t, integer}


    @spec func_with_options(options_t) :: any
    def func_with_options(options) do
      options
    end

    @spec func_with_union_of_options([option_t | extra_option_t]) :: any
    def func_with_union_of_options(options) do
      options
    end

    @spec func_with_union_of_options_as_type([option_or_extra_option_t]) :: any
    def func_with_union_of_options_as_type(options) do
      options
    end

    @spec func_with_union_of_options_inline([{:option_1, atom} | {:option_2, integer} | option_t]) ::
            any
    def func_with_union_of_options_inline(options) do
      options
    end

    @spec func_with_named_options(options :: options_t) :: any
    def func_with_named_options(options) do
      options
    end

    @spec func_with_options_as_inline_list([{:local_o, local_t} | {:builtin_o, keyword}]) :: any
    def func_with_options_as_inline_list(options) do
      options
    end

    @spec func_with_option_var_defined_in_when([opt]) :: any when opt: option_t
    def func_with_option_var_defined_in_when(options) do
      options
    end

    @spec func_with_options_var_defined_in_when(opts) :: any when opts: [option_t]
    def func_with_options_var_defined_in_when(options) do
      options
    end

    @spec func_with_one_option([{:option_1, integer}]) :: any
    def func_with_one_option(options) do
      options
    end

    @spec fun_without_options([integer]) :: integer
    def fun_without_options(a), do: length(a)

    @spec fun_with_atom_option([:option_name]) :: any
    def fun_with_atom_option(a), do: a

    @spec fun_with_atom_option_in_when(opts) :: any when opts: [:option_name]
    def fun_with_atom_option_in_when(a), do: a

    @spec fun_with_recursive_remote_type_option([OtherRemote.other()]) :: any
    def fun_with_recursive_remote_type_option(a), do: a

    @spec fun_with_recursive_user_type_option([extra_option_1_t]) :: any
    def fun_with_recursive_user_type_option(a), do: a

    @spec fun_with_tuple_option_in_when(opt) :: any when opt: [tuple_opt_t]
    def fun_with_tuple_option_in_when(a), do: a

    @spec fun_with_tuple_option([tuple_opt_t]) :: any
    def fun_with_tuple_option(a), do: a

    @spec fun_with_atom_user_type_option_in_when(opt) :: any when opt: [atom_opt_t]
    def fun_with_atom_user_type_option_in_when(a), do: a

    @spec fun_with_atom_user_type_option([atom_opt_t]) :: any
    def fun_with_atom_user_type_option(a), do: a

    @spec fun_with_list_of_lists([opt]) :: any when opt: [tuple_opt_t]
    def fun_with_list_of_lists(a), do: a

    @spec fun_with_recursive_type(opt) :: any when opt: [term :: opt]
    def fun_with_recursive_type(a), do: a

    @spec fun_with_multiple_specs(nil) :: any
    @spec fun_with_multiple_specs([tuple_opt_t]) :: any
    def fun_with_multiple_specs(a), do: a

    @spec fun_with_multiple_specs_when(nil) :: any
    @spec fun_with_multiple_specs_when([opts]) :: any when opts: tuple_opt_t
    def fun_with_multiple_specs_when(a), do: a

    @spec fun_with_bounded_type([bounded_type(:a | :b)]) :: any
    def fun_with_bounded_type(a), do: a

    @spec fun_with_bounded_type_when([opt]) :: any when opt: bounded_type(:a | :b)
    def fun_with_bounded_type_when(a), do: a

    @spec fun_with_bounded_type_remote([OtherRemote.bounded_type(:a | :b)]) :: any
    def fun_with_bounded_type_remote(a), do: a

    @spec fun_with_bounded_type_remote_local_arg([OtherRemote.bounded_type(atom_opt_t)]) :: any
    def fun_with_bounded_type_remote_local_arg(a), do: a

    @spec fun_with_bounded_type_remote_local_arg_union([OtherRemote.bounded_type(atom_opt_t | :l)]) :: any
    def fun_with_bounded_type_remote_local_arg_union(a), do: a

    @spec fun_with_bounded_type_remote_arg([bounded_type(OtherRemote.some)]) :: any
    def fun_with_bounded_type_remote_arg(a), do: a

    @spec fun_with_keyword_list([key: integer]) :: any
    def fun_with_keyword_list(a), do: a

    @spec fun_with_bounded_type_arg_from_when([local_t(v, integer | binary)]) :: any when v: :a | :b
    def fun_with_bounded_type_arg_from_when(a), do: a

    @spec fun_with_remote_bounded_type_arg_from_when([OtherRemote.bounded_type(v)]) :: any when v: :a | :b
    def fun_with_remote_bounded_type_arg_from_when(a), do: a


    @type m_true(l, _r)::l
    @type m_false(_l, r)::r

    @type m_0(_l, r)::r
    @type m_1(l, r)::l(r)
  end
end
  test "fun_with_bounded_type" do
    assert [
      {Local, :a, {:type, _, :integer, []}},
      {Local, :b, {:type, _, :integer, []}}
      ] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type, 0)
  end

  test "fun_with_bounded_type_when" do
    assert [
      {Local, :a, {:type, _, :integer, []}},
      {Local, :b, {:type, _, :integer, []}}
      ] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type_when, 0)
  end

  test "fun_with_bounded_type_remote" do
    assert [
      {OtherRemote, :a, {:type, _, :integer, []}},
      {OtherRemote, :b, {:type, _, :integer, []}}
      ] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type_remote, 0)
  end

  test "fun_with_bounded_type_remote_arg" do
    assert [{Local, :a, {:type, _, :integer, []}}] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type_remote_arg, 0)
  end

  test "fun_with_bounded_type_remote_local_arg" do
    assert [{OtherRemote, :atom_opt, {:type, _, :integer, []}}] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type_remote_local_arg, 0)
  end

  test "fun_with_bounded_type_remote_local_arg_union" do
    assert [{OtherRemote, :atom_opt, {:type, _, :integer, []}}] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type_remote_local_arg_union, 0)
  end

  test "fun_with_bounded_type_arg_from_when" do
    assert [
      {Local, :a, {:type, _, :union, [{:type, _, :integer, []}, {:type, _, :binary, []}]}},
      {Local, :b, {:type, _, :union, [{:type, _, :integer, []}, {:type, _, :binary, []}]}}
      ] = TypeInfo.extract_param_options(Local, :fun_with_bounded_type_arg_from_when, 0)
  end

  test "fun_with_remote_bounded_type_arg_from_when" do
    assert [
      {OtherRemote, :a, {:type, _, :integer, []}},
      {OtherRemote, :b, {:type, _, :integer, []}}
      ] = TypeInfo.extract_param_options(Local, :fun_with_remote_bounded_type_arg_from_when, 0)
  end

  test "fun_with_keyword_list" do
    assert [{Local, :key, {:type, _, :integer, []}}] = TypeInfo.extract_param_options(Local, :fun_with_keyword_list, 0)
  end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant