-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathgradient.ex
110 lines (95 loc) · 2.77 KB
/
gradient.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
defmodule Gradient do
@moduledoc """
Documentation for `Gradient`.
Options:
- `app_path` - Path to the app that contains file with code (for umbrella apps).
- `code_path` - Path to a file with code (e.g. when beam was compiled without project).
- `no_gradualizer_check` - Skip Gradualizer checks if true.
- `no_ex_check` - Skip Elixir checks if true.
- `no_specify` - Skip AST specifying if true.
"""
alias Gradient.ElixirFileUtils
alias Gradient.ElixirFmt
alias Gradient.AstSpecifier
alias Gradient.ElixirChecker
@type options() :: [{:app_path, String.t()}, {:code_path, String.t()}]
@spec type_check_file(String.t(), options()) :: :ok | :error
def type_check_file(file, opts \\ []) do
opts = Keyword.put(opts, :return_errors, true)
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
[] ->
:ok
errors ->
opts = Keyword.put(opts, :forms, forms)
ElixirFmt.print_errors(errors, opts)
:error
end
else
{:erlang, forms} ->
opts = Keyword.put(opts, :return_errors, false)
case maybe_gradualizer_check(forms, opts) do
:nok -> :error
_ -> :ok
end
error ->
IO.puts(IO.ANSI.format([:red, "Can't load file - #{inspect(error)}"]))
:error
end
end
defp maybe_gradualizer_check(forms, opts) do
unless opts[:no_gradualizer_check] do
try do
:gradualizer.type_check_forms(forms, opts)
catch
err ->
{:attribute, _, :file, {path, _}} = hd(forms)
[{path, err}]
end
else
[]
end
end
defp maybe_gradient_check(forms, opts) do
unless opts[:no_ex_check] do
ElixirChecker.check(forms, opts)
else
[]
end
end
defp maybe_specify_forms(forms, opts) do
unless opts[:no_specify] do
forms
|> put_code_path(opts)
|> AstSpecifier.specify()
else
forms
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 ->
case opts[:app_path] do
nil ->
forms
app_path ->
{:attribute, anno, :file, {path, line}} = hd(forms)
[
{:attribute, anno, :file, {String.to_charlist(app_path) ++ '/' ++ path, line}}
| tl(forms)
]
end
path ->
[{:attribute, 1, :file, {path, 1}} | tl(forms)]
end
end
end