Skip to content
Merged
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
7 changes: 4 additions & 3 deletions lib/gradient/elixir_checker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ defmodule Gradient.ElixirChecker do
# Spec name doesn't match the function name
{fun, [{:spec_error, :wrong_spec_name, anno, n, a} | errors]}

{:spec, {n, a}, anno} = s1, {{:spec, _, _}, errors} ->
# Only one spec per function clause is allowed
{s1, [{:spec_error, :spec_after_spec, anno, n, a} | errors]}
{:spec, {n, a}, anno} = s1, {{:spec, {n2, a2}, _}, errors} when n != n2 or a != a2 ->
# Specs with diffrent name/arity are mixed
{s1, [{:spec_error, :mixed_specs, anno, n, a} | errors]}

x, {_, errors} ->
{x, errors}
end)
|> elem(1)
|> Enum.map(&{file, &1})
|> Enum.reverse()
end

# Filter out __info__ and other generated functions with the same name pattern
Expand Down
4 changes: 2 additions & 2 deletions lib/gradient/elixir_fmt.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ defmodule Gradient.ElixirFmt do
)
end

def format_type_error({:spec_error, :spec_after_spec, anno, name, arity}, opts) do
def format_type_error({:spec_error, :mixed_specs, anno, name, arity}, opts) do
:io_lib.format(
"~sThe spec ~p/~p~s follows another spec, but only one spec per function clause is allowed~n",
"~sThe spec ~p/~p~s follows a spec with different name/arity~n",
[
format_location(anno, :brief, opts),
name,
Expand Down
9 changes: 9 additions & 0 deletions test/examples/spec_correct.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,13 @@ defmodule CorrectSpec do
def convert(int) when is_integer(int), do: int / 1
@spec convert(atom()) :: binary()
def convert(atom) when is_atom(atom), do: to_string(atom)

@spec encode(integer()) :: float()
@spec encode(atom()) :: binary()
def encode(val) do
case val do
_ when is_integer(val) -> val / 1
_ when is_atom(val) -> to_string(val)
end
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule SpecAfterSpec do
defmodule SpecMixed do
@spec convert(integer()) :: float()
@spec convert(atom()) :: binary()
@spec encode(atom()) :: binary()
def convert(int) when is_integer(int), do: int / 1
def convert(atom) when is_atom(atom), do: to_string(atom)

def encode(atom) when is_atom(atom), do: to_string(atom)
end
11 changes: 6 additions & 5 deletions test/gradient/elixir_checker_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ defmodule Gradient.ElixirCheckerTest do
ast = load("Elixir.SpecWrongName.beam")

assert [
{_, {:spec_error, :wrong_spec_name, 11, :last_two, 1}},
{_, {:spec_error, :wrong_spec_name, 5, :convert, 1}}
{_, {:spec_error, :wrong_spec_name, 5, :convert, 1}},
{_, {:spec_error, :wrong_spec_name, 11, :last_two, 1}}
] = ElixirChecker.check(ast, [])
end

test "more than one spec per function clause is not allowed" do
ast = load("Elixir.SpecAfterSpec.beam")
test "mixing specs names is not allowed" do
ast = load("Elixir.SpecMixed.beam")

assert [
{_, {:spec_error, :spec_after_spec, 3, :convert, 1}}
{_, {:spec_error, :mixed_specs, 3, :encode, 1}},
{_, {:spec_error, :wrong_spec_name, 3, :encode, 1}}
] = ElixirChecker.check(ast, [])
end
end
4 changes: 2 additions & 2 deletions test/gradient/elixir_fmt_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,11 @@ defmodule Gradient.ElixirFmtTest do

test "follows another spec" do
msg =
{:spec_error, :spec_after_spec, 3, :convert, 1}
{:spec_error, :mixed_specs, 3, :encode, 1}
|> ElixirFmt.format_error([])
|> :erlang.iolist_to_binary()

assert "The spec convert/1 on line 3 follows another spec, but only one spec per function clause is allowed\n" =
assert "The spec encode/1 on line 3 follows a spec with different name/arity\n" =
msg
end
end
Expand Down
4 changes: 2 additions & 2 deletions test/mix/tasks/gradient_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ defmodule Mix.Tasks.GradientTest do
end

test "--no-ex-check option" do
beam = Path.join(@build_path, "Elixir.SpecAfterSpec.beam")
ex_spec_error_msg = "The spec convert/1 on line"
beam = Path.join(@build_path, "Elixir.SpecMixed.beam")
ex_spec_error_msg = "The spec encode/1 on line"

output = run_task(test_opts([beam]))
assert String.contains?(output, ex_spec_error_msg)
Expand Down