sax
February 17, 2022, 9:43pm
1
Every time I run into an issue with dialyzer, it’s my fault. I respect that, and predict that it’s also the case here. I’m stumped with this one, though.
I’m using a with
statement that may return two different matches. I use this same with
statement twice. In one of the statements, dialyzer only sees the return values from one of the function calls.
If anyone has some spare moments to take a look at the code, I’d appreciate any pointers.
lib/dyd.ex:101:pattern_match
The pattern can never match the type.
Pattern:
{:error, {:invalid_toml, _error}}
Type:
{:error, :invalid_time_unit | :not_a_datetime}
Here is the offending line, pinned to a specific commit:
System.halt(0)
end
defp run(%{mode: "diff"} = config) do
with {:ok, manifest} <- Dyd.Manifest.load(),
{:ok, since} <- manifest.since |> Dyd.Utils.to_datetime() do
Application.put_env(:dyd, :remotes, manifest.remotes)
Application.put_env(:dyd, :since, since)
Dyd.App.start(config)
else
{:error, {:invalid_toml, error}} ->
usage(error)
System.halt(1)
{:error, error} ->
usage(inspect(error))
System.halt(1)
end
end
defp run(%{mode: "info"}) do
Here is the second usage, which does not generate any warnings:
IO.puts("#{bright()}#{underline()}Configuration:#{reset()}\n")
IO.puts("Highlight repos changed since: #{manifest.since}")
IO.puts("\n#{bright()}#{underline()}Repos:#{reset()}\n")
Enum.each(manifest.remotes, fn remote ->
IO.puts("#{bright()}#{remote.name}#{reset()}: #{remote.origin}")
end)
0
else
{:error, {:invalid_toml, error}} ->
usage(error)
{:error, error} ->
usage(inspect(error))
end
end
defp usage(error) do
import IO.ANSI, only: [bright: 0, red: 0, reset: 0, underline: 0]
Here is the spec that should make the statement valid:
def transform(:since, since), do: since
def transform(k, v) do
Logger.error("other: #{k}, #{inspect(v)}")
v
end
end
@type manifest_t() :: %{remotes: [Remote.t()], since: String.t()}
@spec load() :: {:ok, manifest_t()} | Toml.error() | {:error, any()}
def load do
with {:ok, path} <- manifest_path(),
{:ok, manifest} <- File.read(path),
{:ok, %{remotes: _remotes} = config} <-
Toml.decode(manifest, keys: :atoms, transforms: [RemotesToList, Since]) do
config =
config
|> Map.put_new(:since, "1 week ago")
|> Map.put(:remotes, Enum.sort_by(config.remotes, & &1.name))
This is with the following versions:
OTP: 24.2.1
Elixir: 1.13.3
Dialyxir: 1.1.0
1 Like
al2o3cr
February 17, 2022, 11:25pm
2
This may not be the same underlying issue, but the situation (else
clauses with no_return
in each fail type analysis) sounds really similar:
opened 01:19PM - 02 Oct 20 UTC
closed 01:33PM - 02 Oct 20 UTC
## Environment
* Elixir & Erlang/OTP versions (elixir --version): Elixir 1.10… .4 (compiled with Erlang/OTP 21)
### Current behavior
I get a strange dialyzer warning when writing a custom mix task.
I don't know if it is really a bug or if is related to elixir so feel free to close it.
I want the task to exit with different exit codes based on the error, this is a minimal example showing my issue
```elixir
defmodule A do
def a do
with :ok <- File.cp("a", "b"),
{:ok, _} <- File.rm_rf("a") do
:ok
else
{:error, _err} ->
IO.puts("1")
exit({:shutdown, 1})
_ ->
IO.puts("2")
exit({:shutdown, 2})
end
end
end
```
and this is the warning I get on the `File.cp` line
```
The pattern can never match the type.
Pattern:
{:error, __err}
Type:
{:error, atom(), binary()}
```
The warning is related to the use of `:erlang.exit/1`, removing those calls the warning disappears. It seems that after encountering an `:erlang.exit/1` dialyzer stops and doesn't see the next clauses of the `else`.
I can remove the warning by moving the `:erlang.exit/1` call in a `case` statement but I was wondering why this happens and if it can be some sort of bug.
```elixir
defmodule A do
def a do
with :ok <- File.cp("a", "b"),
{:ok, _} <- File.rm_rf("a") do
:ok
else
{:error, _err} -> {:error, 1}
_ -> {:error, 2}
end
|> case do
:ok -> :ok
{:error, exit_code} -> exit({:shutdown, exit_code})
end
end
end
```
### Expected behavior
No dialyzer warnings
or this (also linked to the same open issue):
opened 01:37AM - 05 Jan 18 UTC
closed 09:31PM - 05 Jan 18 UTC
App:Elixir
Note:Needs more info
### Precheck
* Do not use the issues tracker for help or support (try Elixir … Forum, Stack Overflow, IRC, etc.)
* For proposing a new feature, please start a discussion on the Elixir Core mailing list
* For bugs, do a quick search and make sure the bug has not yet been reported
* Finally, be nice and have fun!
### Environment
* Elixir & Erlang versions (elixir --version): Elixir 1.5.2 & 1.6.0-rc.0 & 1.7.0-dev with Erlang/OTP 20
* Operating system: Linux 4.14.11-1-ARCH
### Current behavior
The following function will incorrectly be assumed to always return `{:error, _}` by dialyzer:
```elixir
@spec read(url :: String.t()) :: {:ok, [Downloader.Torrent.t()]} | {:error, String.t()}
def read(url) do
with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(url),
{:ok, %FeederEx.Feed{entries: entries}, _} <- FeederEx.parse(body) do
{:ok, Enum.map(entries, &Downloader.Torrent.from_entry/1)}
else
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
{:fatal_error, _, reason, _, _} ->
{:error, reason}
end
end
```
If one were to change the second `else` clause to return `{:fatal_error, reason}` it still will not recognize that there is one more possible return value, but will still insist that only `{:error, _}` can be returned.
When matching with the following pattern:
```elixir
case Downloader.RSS.read(state.url) do
{:ok, ts} ->
ts
{:error, reason} ->
Logger.error("Error reading '#{state.url}': #{reason}")
[]
end
```
will give a dialyzer error stating: `The pattern {'ok', Vts@1} can never match the type {'error',_}`
### Expected behavior
To have no dialyzer errors.
1 Like
sax
February 18, 2022, 4:52pm
3
Cool, thanks for the links! That gives me some ideas for how I could rewrite this to avoid the condition. Fortunately it’s a CLI app rather than a library, so I can let it sit for a little while.