lok0613
Weird dialyzer warning: the pattern can never match the type
This dialyzer warning just happened randomly once I apply defmacro in my module.
I got 2 modules, the issues only appear in StoreFront module.
defmodule Store do
defmacro __using__(_opt) do
quote do
import unquote(__MODULE__)
@before_compile unquote(__MODULE__)
end
end
defmacro fruits(do: block) do
fn_name = String.to_atom("run_fruits")
quote do
@fn_names unquote(fn_name)
def unquote(fn_name)(), do: unquote(block)
end
end
defmacro apple(clause) do
quote do
if unquote(clause) in [true, :ok] do
:ok
else
:failure
end
end
end
defmacro __before_compile__(_env) do
quote do
def run() do
apply(__MODULE__, :run_fruits, [])
|> IO.inspect
end
end
end
end
defmodule StoreFront do
use Store
fruits do
apple true # emit warning
apple :ok # emit warning
apple false # emit warning
end
apple true # no warning
end
The complete dialyzer warning messages:
The pattern
'false' can never match the type
'true'ElixirLS Dialyzer
The test
'ok' =:=
'true' can never evaluate to 'true'
Most Liked
Qqwy
To check what code you exactly end up with, you might want to use the following incancation by the way:
f = './_build/dev/lib/your_library_name/ebin/Elixir.YourModuleName.beam'
result = :beam_lib.chunks(f,[:abstract_code])
{:ok,{_,[{:abstract_code,{_,ac}}]}} = result
IO.puts :erl_prettypr.format(:erl_syntax.form_list(ac))
(where your_library_name and YourModuleName are replaced by the mix project and module name you’re working on, respectively)
This will show you the core Erlang that ends up being generated after all macros (both your macros and the built-in ones) are expanded. This is actually what Dialyzer is looking at.
(If someone knows a more concise or clean way to look at the core erlang of a module, I’d love to know, by he way!)
NobbZ
Writing functions which do roughly what your macro does, is not writing the code the way it had been generated by the macro call.
So it becomes this:
defmodule StoreFront do
use Store # its okay to leave this as is, as it is not mentioned in the warnings
@fn_names :run_fruits
def run_fruits() do
case true === true or true === :ok do # apple true
x in [false, nil] -> :failure
_ -> :ok
end
case :ok === true or :ok === :ok do # apple :ok
x in [false, nil] -> :failure
_ -> :ok
end
case false === true or false === :ok do
x in [false, nil] -> :failure
_ -> :ok
end
end
# apple true # no need to expand this, dialyzer won't see it anyway, as it does not expand into a function
end
And dialyzer is wondering why you say or true === :ok, when you already know that true === true statically.
It asks why you have a x in [false, nil] clause when you already know in advance, that the condition will always be true.
mudasobwa
The dialyzer has no clue about elixir macros, it sees the compiled code anyway, meaning it sees somewhat like
case :dev do
:test -> ...
...
end
One should not leak testing abstractions to the code in any case. The proper solution would be to introduce a behaviour and mock it in tests.
defmodule ReqIdGen do
@callback generate :: binary()
end
defmodule My do
@behaviour ReqIdGen
@impl ReqIdGen
def generate, do: Ecto.UUID.generate()
@generator Application.compile_env(:my_app, :req_id_gen, __MODULE__)
defp generate_request_id(generator \\ @generator) do
generator.generate()
end
end
And mock it in tests.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








