Skip to content

Commit 6d1d9a0

Browse files
authored
Merge pull request #2 from sascha-wolf/fix/dialyzer-warnings
Resolve dialyzer warnings
2 parents 2c27121 + b70dfff commit 6d1d9a0

File tree

11 files changed

+158
-9
lines changed

11 files changed

+158
-9
lines changed

.github/workflows/ci.yml

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ jobs:
114114
env:
115115
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116116

117-
check_all:
118-
name: Check Formatting & Run Credo
117+
check_formatting:
118+
name: Check Formatting
119119
runs-on: ubuntu-latest
120120
container: hexpm/elixir:1.14.0-erlang-24.3.4.5-alpine-3.16.2
121121
env:
@@ -146,5 +146,82 @@ jobs:
146146
key: ${{ env.CACHE_PREFIX_BUILD }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-${{ hashFiles('**/mix.lock') }}
147147
restore-keys: ${{ env.CACHE_PREFIX_BUILD }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-
148148

149-
- run: mix compile --warnings-as-errors
150-
- run: mix check.all
149+
- run: mix compile
150+
- run: mix format --check-formatted
151+
152+
check_style:
153+
name: Check Style with Credo
154+
runs-on: ubuntu-latest
155+
container: hexpm/elixir:1.14.0-erlang-24.3.4.5-alpine-3.16.2
156+
env:
157+
MIX_ENV: dev
158+
VERSION_ALPINE: 3.16.2
159+
VERSION_ELIXIR: 1.14.0
160+
VERSION_OTP: 24.3.4.5
161+
steps:
162+
- uses: actions/checkout@v2
163+
164+
- name: Cache - deps/
165+
uses: actions/cache@v1
166+
with:
167+
path: deps/
168+
key: ${{ env.CACHE_PREFIX_DEPS }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-${{ hashFiles('**/mix.lock') }}
169+
restore-keys: ${{ env.CACHE_PREFIX_DEPS }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-
170+
171+
- name: Install Dependencies
172+
run: |
173+
mix local.rebar --force
174+
mix local.hex --force
175+
mix deps.get --only "$MIX_ENV"
176+
177+
- name: Cache - _build/
178+
uses: actions/cache@v1
179+
with:
180+
path: _build/
181+
key: ${{ env.CACHE_PREFIX_BUILD }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-${{ hashFiles('**/mix.lock') }}
182+
restore-keys: ${{ env.CACHE_PREFIX_BUILD }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-
183+
184+
- run: mix compile
185+
- run: mix credo
186+
187+
check_types:
188+
name: Check Types with Dialyzer (including generated)
189+
runs-on: ubuntu-latest
190+
container: hexpm/elixir:1.14.0-erlang-24.3.4.5-alpine-3.16.2
191+
env:
192+
MIX_ENV: dev
193+
VERSION_ALPINE: 3.16.2
194+
VERSION_ELIXIR: 1.14.0
195+
VERSION_OTP: 24.3.4.5
196+
steps:
197+
- uses: actions/checkout@v2
198+
199+
- name: Cache - deps/
200+
uses: actions/cache@v1
201+
with:
202+
path: deps/
203+
key: ${{ env.CACHE_PREFIX_DEPS }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-${{ hashFiles('**/mix.lock') }}
204+
restore-keys: ${{ env.CACHE_PREFIX_DEPS }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-
205+
206+
- name: Install Dependencies
207+
run: |
208+
mix local.rebar --force
209+
mix local.hex --force
210+
mix deps.get --only "$MIX_ENV"
211+
212+
- name: Cache - _build/
213+
uses: actions/cache@v1
214+
with:
215+
path: _build/
216+
key: ${{ env.CACHE_PREFIX_BUILD }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-${{ hashFiles('**/mix.lock') }}
217+
restore-keys: ${{ env.CACHE_PREFIX_BUILD }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}-
218+
219+
- run: mix compile
220+
221+
- name: Cache - Dialyzer PLTs
222+
uses: actions/cache@v1
223+
with:
224+
path: .dialyzer/
225+
key: ${{ env.CACHE_PREFIX_DIALYZER }}-env:${{ env.MIX_ENV }}-alpine:${{ env.VERSION_ALPINE }}-elixir:${{ env.VERSION_ELIXIR }}-otp:${{ env.VERSION_OTP }}
226+
227+
- run: mix dialyzer

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ ex_union-*.tar
2424

2525
# Temporary files, for example, from tests.
2626
/tmp/
27+
28+
# Dialyzer cache (contains the PLTs - persistent lookup tables)
29+
/.dialyzer/

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99
### Added
1010
### Changed
11+
- [#2](https://github.com/sascha-wolf/ex_union/pull/2): Resolve dialyzer warnings ([@sascha-wolf])
12+
1113
### Removed
1214

1315
## [v0.1.0] - 2022-10-16
@@ -20,4 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2022
- Generate a guard whose name is inferred from the top-level module
2123

2224
[Unreleased]: https://github.com/sascha-wolf/ex_union/compare/v0.1.0...main
23-
[v0.1.0]: https://github.com/sascha-wolf/ex_union/compare/744dd7dc078c5e9d2311f11a223f326665d9a38b...v0.1.0
25+
[v0.1.0]: https://github.com/sascha-wolf/ex_union/compare/744dd7dc078c5e9d2311f11a223f326665d9a38b...v0.1.0
26+
27+
[@sascha-wolf]: https://github.com/sascha-wolf

examples/color.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
defmodule Color do
2+
import ExUnion
3+
4+
defunion hex(string :: String.t)
5+
| rgb(red :: 0..255, green :: 0..255, blue :: 0..255)
6+
| rgba(red :: 0..255, green :: 0..255, blue :: 0..255, alpha :: float)
7+
| hsl(hue :: 0..360, saturation :: float, lightness :: float)
8+
| hsla(hue :: 0..360, saturation :: float, lightness :: float, alpha :: float)
9+
end

examples/integer_tree.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
defmodule IntegerTree do
2+
import ExUnion
3+
4+
# You can also use `t` instead of `union` if you prefer
5+
defunion leaf | node(integer :: integer, left :: union, right :: union)
6+
end

examples/maybe.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
defmodule Maybe do
2+
import ExUnion
3+
4+
defunion some(value) | none
5+
end

examples/wrapped.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
defmodule Wrapped do
2+
import ExUnion
3+
4+
defunion tuple(value :: {:ok, union})
5+
| map(value :: %{any => union})
6+
| function(value :: (union_keyword -> union_map))
7+
| list(value :: [union])
8+
| keyword(value :: [{atom, union}])
9+
end

lib/ex_union/definition.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule ExUnion.Definition do
44
alias __MODULE__.{Block, Type}
55

66
@type t :: %__MODULE__{
7-
name: atom,
7+
name: String.t(),
88
module: module,
99
types: list(Type.t())
1010
}

lib/ex_union/definition/type.ex

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,26 +98,47 @@ defmodule ExUnion.Definition.Type do
9898
arguments_with_types = Enum.map(fields, &{:"::", [], [&1.var, &1.type]})
9999
arguments_mapped_to_struct_fields = Enum.map(fields, &{&1.name, &1.var})
100100

101+
# When we only have a single field we can easily generate an "overloaded contract"
102+
# where the "simple new/1" spec is a superset of the "matching new/1".
103+
# While this isn't an issue it does produce an "Overloaded contract" warning from
104+
# dialyzer, since dialyzer doesn't support this, but since nothing breaks we still
105+
# generate the @spec and silence this specific dialyzer warning to retain the type
106+
# information
107+
maybe_ignore_dialyzer_warning =
108+
if length(fields) == 1 do
109+
quote do
110+
@dialyzer {:no_contracts, new: 1}
111+
end
112+
end
113+
101114
quote do
115+
unquote(maybe_ignore_dialyzer_warning)
102116
@spec new(unquote_splicing(arguments_with_types)) :: t()
103117
def new(unquote_splicing(arguments)) do
104118
%__MODULE__{unquote_splicing(arguments_mapped_to_struct_fields)}
105119
end
106120
end
107121
end
108122

109-
def to_shortcut_function(%__MODULE__{} = type) do
110-
arguments = Enum.map(type.fields, & &1.var)
123+
def to_shortcut_function(%__MODULE__{fields: fields} = type) do
124+
arguments = Enum.map(fields, & &1.var)
125+
arguments_with_types = Enum.map(fields, &{:"::", [], [&1.var, &1.type]})
126+
field_types = Enum.map(fields, &{&1.name, &1.type})
111127

112128
arity_1_shortcut =
113129
quote do
130+
@spec unquote(type.name)(fields :: %{unquote_splicing(field_types)}) ::
131+
unquote(type.module).t()
132+
@spec unquote(type.name)(fields :: unquote(field_types)) :: unquote(type.module).t()
114133
defdelegate unquote(type.name)(fields),
115134
to: unquote(type.module),
116135
as: :new
117136
end
118137

119138
arity_n_shortcut =
120139
quote do
140+
@spec unquote(type.name)(unquote_splicing(arguments_with_types)) ::
141+
unquote(type.module).t()
121142
defdelegate unquote(type.name)(unquote_splicing(arguments)),
122143
to: unquote(type.module),
123144
as: :new

mix.exs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule ExUnion.MixProject do
88
app: :ex_union,
99
version: version(),
1010
elixir: "~> 1.10",
11+
elixirc_paths: elixirc_paths(Mix.env()),
1112
preferred_cli_env: [
1213
coveralls: :test,
1314
"coveralls.detail": :test,
@@ -18,6 +19,7 @@ defmodule ExUnion.MixProject do
1819
start_permanent: Mix.env() == :prod,
1920
aliases: aliases(),
2021
deps: deps(),
22+
dialyzer: dialyzer(),
2123

2224
# Docs
2325
name: "ExUnion",
@@ -32,6 +34,9 @@ defmodule ExUnion.MixProject do
3234
]
3335
end
3436

37+
defp elixirc_paths(:dev), do: ["examples", "lib"]
38+
defp elixirc_paths(_), do: ["lib"]
39+
3540
# Run "mix help compile.app" to learn about applications.
3641
def application do
3742
[
@@ -43,7 +48,7 @@ defmodule ExUnion.MixProject do
4348
# See the documentation for `Mix` for more info on aliases.
4449
defp aliases do
4550
[
46-
"check.all": ["format --check-formatted", "credo"]
51+
"check.all": ["format --check-formatted", "credo", "dialyzer"]
4752
]
4853
end
4954

@@ -52,13 +57,21 @@ defmodule ExUnion.MixProject do
5257
[
5358
# No Runtime
5459
{:credo, ">= 1.0.0", only: :dev, runtime: false},
60+
{:dialyxir, "~> 1.0", only: :dev, runtime: false},
5561
{:ex_doc, "~> 0.23", only: :dev, runtime: false},
5662

5763
# Test
5864
{:excoveralls, "~> 0.13", only: :test}
5965
]
6066
end
6167

68+
defp dialyzer do
69+
[
70+
# ignore_warnings: "dialyzer/ignore.exs",
71+
plt_file: {:no_warn, ".dialyzer/dialyzer.plt"}
72+
]
73+
end
74+
6275
#######
6376
# Hex #
6477
#######

0 commit comments

Comments
 (0)