Skip to content

Commit

Permalink
leave parens in typespecs
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaszsamson committed Aug 31, 2023
1 parent 28de664 commit ac4194e
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.TypeSpec.ContractTranslator
|> drop_macro_env(is_macro)
|> improve_defprotocol_spec(mod, fun)
|> Macro.to_string()
|> String.replace("()", "")
|> Code.format_string!(line_length: :infinity)
|> IO.iodata_to_binary()
|> String.replace_prefix("foo", to_string(fun))
Expand Down
6 changes: 3 additions & 3 deletions apps/language_server/test/dialyzer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -458,12 +458,12 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
"fun" => "myfun",
"line" => 2,
"mod" => "Elixir.C",
"spec" => "myfun :: 1",
"spec" => "myfun() :: 1",
"uri" => ^file_c
}
],
"command" => command = "spec:" <> _,
"title" => "@spec myfun :: 1"
"title" => "@spec myfun() :: 1"
},
"range" => %{
"end" => %{"character" => 0, "line" => 1},
Expand All @@ -485,7 +485,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
"changes" => %{
^file_c => [
%{
"newText" => " @spec myfun :: 1\n",
"newText" => " @spec myfun() :: 1\n",
"range" => %{
"end" => %{"character" => 0, "line" => 1},
"start" => %{"character" => 0, "line" => 1}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,152 +5,164 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.TypeSpec.ContractTranslator
test "translate struct when struct.t type exists" do
contract = ~c"() -> \#{'__struct__':='Elixir.DateTime'}"

assert "foo :: DateTime.t()" ==
assert "foo() :: DateTime.t()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "don't translate struct when struct.t type does not exist" do
contract = ~c"() -> \#{'__struct__':='Elixir.SomeOtherStruct'}"

assert "foo :: %SomeOtherStruct{}" ==
assert "foo() :: %SomeOtherStruct{}" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "struct" do
contract = ~c"() -> \#{'__struct__':=atom(), atom()=>any()}"
assert "foo :: struct" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo() :: struct()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "drop macro env argument" do
contract = ~c"(any(), integer()) -> integer()"

assert "foo(any, integer) :: integer" ==
assert "foo(any(), integer()) :: integer()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(integer) :: integer" ==
assert "foo(integer()) :: integer()" ==
ContractTranslator.translate_contract(:foo, contract, true, Atom)
end

test "atom :ok" do
contract = ~c"(any()) -> ok"
assert "foo(any) :: :ok" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(any()) :: :ok" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "atom true" do
contract = ~c"(any()) -> true"

assert "foo(any) :: true" ==
assert "foo(any()) :: true" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "atom _ substitution" do
contract = ~c"(_) -> false"

assert "foo(any) :: false" ==
assert "foo(any()) :: false" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "do not drop when substitutions" do
contract = ~c"(X) -> atom() when X :: any()"

assert "foo(x) :: atom when x: any" ==
assert "foo(x) :: atom() when x: any()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "keyword" do
contract = ~c"(any()) -> list({atom(), any()})"

assert "foo(any) :: keyword" ==
assert "foo(any()) :: keyword()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)

contract = ~c"(any()) -> list({atom(), _})"

assert "foo(any) :: keyword" ==
assert "foo(any()) :: keyword()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "keyword(t)" do
contract = ~c"(any()) -> list({atom(), integer()})"

assert "foo(any) :: keyword(integer)" ==
assert "foo(any()) :: keyword(integer())" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "[type]" do
contract = ~c"(any()) -> list(atom())"

assert "foo(any) :: [atom]" ==
assert "foo(any()) :: [atom()]" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "list" do
contract = ~c"(any()) -> list(any())"

assert "foo(any) :: list" ==
assert "foo(any()) :: list()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "empty list" do
contract = ~c"(any()) -> []"
assert "foo(any) :: []" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(any()) :: []" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "[...]" do
contract = ~c"(any()) -> nonempty_list(any())"

assert "foo(any) :: [...]" ==
assert "foo(any()) :: [...]" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)

contract = ~c"(any()) -> nonempty_list(_)"

assert "foo(any) :: [...]" ==
assert "foo(any()) :: [...]" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "[type, ...]" do
contract = ~c"(any()) -> nonempty_list(atom())"

assert "foo(any) :: [atom, ...]" ==
assert "foo(any()) :: [atom(), ...]" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "undoes conversion of :_ to any inside bitstring" do
contract = ~c"(any()) -> <<_:2, _:_*3>>"

assert "foo(any) :: <<_::2, _::_*3>>" ==
assert "foo(any()) :: <<_::2, _::_*3>>" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "function" do
contract = ~c"(any()) -> fun((...) -> ok)"

assert "foo(any) :: (... -> :ok)" ==
assert "foo(any()) :: (... -> :ok)" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "fun" do
contract = ~c"(any()) -> fun((...) -> any())"
assert "foo(any) :: fun" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(any()) :: fun()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "empty map" do
contract = ~c"(any()) -> \#{}"
assert "foo(any) :: %{}" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(any()) :: %{}" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "map" do
contract = ~c"(any()) -> \#{any()=>any()}"
assert "foo(any) :: map" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(any()) :: map()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "map with fields" do
contract = ~c"(any()) -> \#{integer()=>any(), 1:=atom(), abc:=4}"

expected =
if Version.match?(System.version(), "< 1.13.0") do
"foo(any) :: %{optional(integer) => any, 1 => atom, :abc => 4}"
"foo(any()) :: %{optional(integer()) => any(), 1 => atom(), :abc => 4}"
else
"foo(any) :: %{optional(integer) => any, 1 => atom, abc: 4}"
"foo(any()) :: %{optional(integer()) => any(), 1 => atom(), abc: 4}"
end

assert expected ==
Expand All @@ -160,32 +172,34 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.TypeSpec.ContractTranslator
test "defprotocol type t" do
contract = ~c"(any()) -> any()"

assert "foo(t) :: any" ==
assert "foo(t()) :: any()" ==
ContractTranslator.translate_contract(:foo, contract, false, Enumerable)

contract = ~c"(any(), any()) -> any()"

assert "foo(t, any) :: any" ==
assert "foo(t(), any()) :: any()" ==
ContractTranslator.translate_contract(:foo, contract, false, Enumerable)

contract = ~c"(any()) -> any()"
assert "foo(any) :: any" == ContractTranslator.translate_contract(:foo, contract, false, Atom)

assert "foo(any()) :: any()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)

contract = ~c"(any(), any()) -> any()"

assert "foo(any, any) :: any" ==
assert "foo(any(), any()) :: any()" ==
ContractTranslator.translate_contract(:foo, contract, false, Atom)
end

test "defimpl first arg" do
contract = ~c"(any()) -> any()"

assert "count(list) :: any" ==
assert "count(list()) :: any()" ==
ContractTranslator.translate_contract(:count, contract, false, Enumerable.List)

contract = ~c"(any()) -> any()"

assert "count(Date.Range.t()) :: any" ==
assert "count(Date.Range.t()) :: any()" ==
ContractTranslator.translate_contract(:count, contract, false, Enumerable.Date.Range)
end
end

0 comments on commit ac4194e

Please sign in to comment.