Dialyzer with a macro injected case statement

I’ve created a macro operator ~> that injects a case that will bypass the right statement if the left is :error or { :error, _ }, and pipeline to the right otherwise. It’s a conditional |> pipe operator.
The problem is that dialyzer complains that “The pattern can never match the type” because not all functions can produce both kinds of error values. Such as:

lib/dc_example.ex:16:pattern_match
The pattern can never match the type.

Pattern:
:error

Type:
<<_::64, _::size(8)>>

In this instance, it can return a string or an error tuple, but not a bare :error.

For clarity, dialyzer is doing the right thing from an analysis standpoint. Is there a way that I can provide dialyzer with a hint or exclusion that won’t disable pattern match checks?

Here is a working example:

defmodule DcExample do

  defmacro left ~> right do
    quote do
      case unquote( left ) do
        { :error, _ } = err -> err
        :error -> :error
        val ->
          unquote( Macro.pipe( (quote do: val), right, 0 ) )
      end
    end
  end

  def hello( it ) do
    good( it )
    ~> bad
    |> IO.inspect
  end

  def good(it) do
    case :crypto.rand_uniform(1,10) > 5 do
      true -> "(#{it}) alls well"
      false -> { :error, :oops }
    end
  end

  def bad( arg ) do
    case :crypto.rand_uniform(1,10) > 5 do
      true -> :error
      false -> "input plus " <> arg
    end
  end

  def run do
    Enum.each(1..10, &hello/1)
  end
end
2 Likes

You might try generated: true. I’m not sure though if that setting is somehow persisted in a way that even dialyzer recognizes it.

Otherwise there is nothing but using @dyalizer attributes.

1 Like

I don’t “think” it does sadly… @dyalizer attributes is always what I’ve had to do (unless I can hoist it to another function call), but I’m hoping someone responds with a better suggestion here. ^.^;

I just tried generated: true and it seems to do the trick. Thanks!

3 Likes

I have been having a similar problem to the one described in this post, but I can’t find any usage documentation about the @dyalizer attribute or setting generated: true.

What are these two options? what do they mean? Are there any resources where I can read about them?

I think I found them. Sorry to cause noise.

It would be good if you could link them so that others with the same problem later can find them easier. :slight_smile:

2 Likes

Good idea:

Elixir documentation: Modules and Module Attributes: @dialyzer
Erlang’s dialyzer documentation

#Usage
defmodule
  @dialyzer generated: true
  
  # ...
end
1 Like

@neenjaw I don’t see generated: true option for the @dialyzer module attribute documented in either of these documents, did you find it anywhere? I only found some code in elixir that uses it as an option for quote, tested here and documented here

I couldn’t find anything specific, but read that @dialyzer takes atom, tuples. So given the conversation above I just put it at the top of my file that was generating the error and it went away.

When I comment out the line, the error comes back.

The error I was specifically trying to suppress was being thrown by a macro to create a function with branching logic based on a specified module attributes. So I would get a true =:= false, true can never equal false warning.

The closest that I can find documentation on this is in a test case for modules found in the source