From e6012232efadf1f9c8c91c3fbab4161af18f9b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Tue, 24 May 2022 17:02:41 +0200 Subject: [PATCH 1/2] Support typechecking for erlang files from `src/` directory --- lib/gradient.ex | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/gradient.ex b/lib/gradient.ex index 913a851f..e8bae2fc 100644 --- a/lib/gradient.ex +++ b/lib/gradient.ex @@ -23,7 +23,8 @@ defmodule Gradient do def type_check_file(file, opts \\ []) do opts = Keyword.put(opts, :return_errors, true) - with {:ok, forms} <- ElixirFileUtils.get_forms(file) do + with {:ok, forms} <- ElixirFileUtils.get_forms(file), + {:elixir, _} <- wrap_language_name(forms) do forms = maybe_specify_forms(forms, opts) case maybe_gradient_check(forms, opts) ++ maybe_gradualizer_check(forms, opts) do @@ -36,6 +37,12 @@ defmodule Gradient do :error end else + {:erlang, forms} -> + opts = Keyword.put(opts, :return_errors, false) + case maybe_gradualizer_check(forms, opts) do + :nok -> :error + _ -> :ok + end error -> Logger.error("Can't load file - #{inspect(error)}") :error @@ -74,6 +81,14 @@ defmodule Gradient do end end + defp wrap_language_name([{:attribute, _, :file, {file_name, _}} | _] = forms) do + if :string.str(file_name, '.erl') > 0 do + {:erlang, forms} + else + {:elixir, forms} + end + end + defp put_code_path(forms, opts) do case opts[:code_path] do nil -> From 87cc585564c5e958faf1a0f82ee4d36a23d342ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Tue, 24 May 2022 19:26:05 +0200 Subject: [PATCH 2/2] Test typechecking erlang and elixir beam files --- .gitignore | 5 ++++- test/examples/erlang/test.erl | 9 +++++++++ test/examples/erlang/test_err.erl | 9 +++++++++ test/gradient_test.exs | 27 +++++++++++++++++++++++++++ test/test_helper.exs | 17 +++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 test/examples/erlang/test.erl create mode 100644 test/examples/erlang/test_err.erl diff --git a/.gitignore b/.gitignore index c3381e7a..3cd1f34d 100644 --- a/.gitignore +++ b/.gitignore @@ -25,8 +25,11 @@ gradient-*.tar # Temporary files, for example, from tests. /tmp/ -# Beam files compiled from examples +# Elixir beam files compiled from examples test/examples/_build/ +# Erlang beam files compiled from examples +test/examples/erlang/_build/ + # MacOS DS_Store .DS_Store diff --git a/test/examples/erlang/test.erl b/test/examples/erlang/test.erl new file mode 100644 index 00000000..597ce5cc --- /dev/null +++ b/test/examples/erlang/test.erl @@ -0,0 +1,9 @@ +-module(test). + +-export([positive/1]). + +-spec positive(integer()) -> ok | error. +positive(A) when A > 0 -> + ok; +positive(_) -> + error. diff --git a/test/examples/erlang/test_err.erl b/test/examples/erlang/test_err.erl new file mode 100644 index 00000000..96296a32 --- /dev/null +++ b/test/examples/erlang/test_err.erl @@ -0,0 +1,9 @@ +-module(test_err). + +-export([positive/1]). + +-spec positive(integer()) -> integer(). +positive(A) when A > 0 -> + ok; +positive(_) -> + error. diff --git a/test/gradient_test.exs b/test/gradient_test.exs index f3b0fa64..a5cb0530 100644 --- a/test/gradient_test.exs +++ b/test/gradient_test.exs @@ -1,4 +1,31 @@ defmodule GradientTest do use ExUnit.Case doctest Gradient + + import Gradient.TestHelpers + import ExUnit.CaptureIO + + test "typecheck erlang beam" do + # typecheck file with errors + path = "test/examples/erlang/_build/test_err.beam" + erl_path = "test/examples/erlang/test_err.erl" + io_data = capture_io(fn -> assert :error = Gradient.type_check_file(path) end) + assert String.contains?(io_data, erl_path) + # typecheck correct file + capture_io(fn -> + assert :ok = Gradient.type_check_file("test/examples/erlang/_build/test.beam") + end) + end + + test "typecheck elixir beam" do + # typecheck file with errors + path = "test/examples/_build/Elixir.WrongRet.beam" + ex_path = "test/examples/type/wrong_ret.ex" + io_data = capture_io(fn -> assert :error = Gradient.type_check_file(path) end) + assert String.contains?(io_data, ex_path) + # typecheck correct file + capture_io(fn -> + assert :ok = Gradient.type_check_file("test/examples/_build/Elixir.Basic.beam") + end) + end end diff --git a/test/test_helper.exs b/test/test_helper.exs index 44a85b4f..4ee4190e 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,5 +1,6 @@ defmodule ExamplesCompiler do @build_path "test/examples/_build/" + @erl_build_path "test/examples/erlang/_build/" @version_step 0.01 @@ -24,6 +25,20 @@ defmodule ExamplesCompiler do end end + def erl_compile(pattern) do + case File.mkdir(@erl_build_path) do + :ok -> + pattern + |> Path.wildcard() + |> Enum.each(fn p -> + :compile.file(to_charlist(p), [:debug_info, {:outdir, to_charlist(@erl_build_path)}]) + end) + + _ -> + :error + end + end + def excluded_version_tags do case @version do 1.11 -> @@ -58,4 +73,6 @@ end ExamplesCompiler.compile("test/examples/**/*.ex") exlcude = ExamplesCompiler.excluded_version_tags() +ExamplesCompiler.erl_compile("test/examples/erlang/**/*.erl") + ExUnit.start(exclude: exlcude)