Warnings using defoverridable - warning: this clause for foo/1 cannot match because a previous clause at line 8 always matches

I seem to have stumbled into something similar to what was mentioned in this thread: Function overriding causes warning
however, I can’t spot the trouble.

I have a “base” module that provides some default implementations of functions (i.e. not functions that are optional in any way that could be notated via @optional_callbacks). 90% of the time, the default implementation is what’s used. But when I do need to override the one of these functions, I keep seeing warnings, .e.g

warning: this clause for foo/1 cannot match because a previous clause at line 8 always matches
  lib/my_app/foo.ex:130

My tests are passing – the proper code is getting executed. Can anyone shed light on the source of the warning? I have the proper use statement in my modules, so I’m wondering if there’s something else I’ve missed.

1 Like

Can you provide a working example? It’s hard to tell what’s going on here, but I suspect its due to how the use macro is being expanded.

1 Like

It boils down to something like this

defmodule Defaults do

  defmacro __using__(opts) do
    quote do
      @impl true
      def foo do
        Defaults.parent_foo(unquote(opts[:x]), unquote(opts[:y]))
      end

      defoverridable foo: 0
    end
  end

  def parent_foo(x, y) do
    x + y
  end
end
defmodule Child1 do

  use Defaults, x: 12, y: 14

end
defmodule Child2 do

  use Defaults, x: 13, y: 15

   def foo do
      Defaults.parent_foo(1, 3)
   end

end

Which seems to be working fine without warnings… so this gives me something to hack on. I’ll build this POC up until it matches my actual code or it generates the warning.

Without the detail of the warning, I can only guess the cause. Does the following modification help?

defmodule Defaults do
  @callback foo() :: integer()  # <-- add this line

  defmacro __using__(opts) do
    quote do
      @behaviour unquote(__MODULE__)  # <-- and this line
      #...
    end
  end
end

You’ll want to use @before_compile to put the default implementations at the end of the module instead of at the top (where use … is located). Then any custom implementation comes before your generated one. If you still get a warning you can use generated: true on the quote do block with the generated default implementation to prevent that.

2 Likes

I’m not sure exactly what I did that made this go away, but modified my defoverridable to reference the behavioiur instead of specific callbacks, e.g.

defoverridable MyBehaviour

I also had to clean up my @callbacks so they included a final argument for a Keyword of opts… after doing that, the compile warnings disappeared.