Unused var warning in block passed to a macro

Consider the following script:

defmodule Macros do
  defmacro macro(param, do: body) do
    param = Macro.escape(param, unquote: true)
    body = Macro.escape(body, unquote: true)

    quote bind_quoted: [param: param, body: body] do
      def fun(unquote(param)) do
        IO.inspect(unquote(param))
        unquote(body)
        IO.puts("----------")
      end
    end
  end
end

defmodule Script do
  import Macros

  macro({:ok, value} = param) do
    IO.inspect(value)
    IO.inspect(param)
  end

  macro([head | tail]) do
    IO.inspect(head)
    IO.inspect(tail)
  end

  macro(param) do
    IO.inspect(param)
  end
end

Script.fun(123)

Script.fun([1, 2, 3])

Script.fun({:ok, 123})

Everything works correctly - the funs generate expected output, but compiler emits a warning related to the first macro call:

warning: variable “value” is unused (if the variable is not meant to be used, prefix it with an underscore)
script.exs:19: Script.fun/1

Can anybody explain what is going on and how to fix that?

First of all, remove bind_quoted and Macro.escape. Make a macro look like this:

  defmacro macro(param, do: body) do
    quote do
      def fun(unquote(param)) do
        IO.inspect(unquote(param))
        unquote(body)
        IO.puts("----------")
      end
    end

Second, let’s see what code gets generated. I added this pipe after quote

    |> tap(& IO.puts Code.format_string! Macro.to_string &1)

And it returns the

def fun({:ok, value} = param) do
  IO.inspect({:ok, value} = param)

  (
    IO.inspect(value)
    IO.inspect(param)
  )

  IO.puts("----------")
end

And here you can see that the value in fun argument is unused because it is shadowed on the next line.


Please, don’t forget to mark the solution

1 Like

Thank you for the hints! I managed to fix the overshadowing issue by adding additional match operator to the function param, like this:

defmacro macro(param, do: body) do
  quote do
    def fun(unquote(param) = evaluated_param) do
      IO.inspect(evaluated_param)
      unquote(body)
      IO.puts("----------")
    end
  end
end