2
votes

I want to generate a module which using a macro created function, everything works as expected but except for the context on unquote(block) seems works out of expectation. The testing code are the following.

    defmodule A do
      defmacro wrap(head,opts \\[],do: block) do
        {name,args} = case head do
          name when is_atom(name) ->
          {name,[]}
          h ->
          Macro.decompose_call h
        end
        quote do
          def unquote(name)(unquote_splicing(args)) do
            IO.inspect __ENV__
            unquote(block)
          end
        end
      end
    end

    defmodule B do
      import A,only: [wrap: 2]
      alias B.C
      wrap test do
        C.test2
      end
    end

    defmodule C do
      def test do
        quote do
          import A,only: [wrap: 2]
          alias B.C
          wrap test do
            IO.inspect __ENV__
            C.test2
          end
        end
      end
    end

    defmodule B.C do
      def test2 do
        "good"
      end
    end
Module.create D,C.test,__ENV__

module B should be identical to D,except for D is a dynamically generated module and B is a predefined module. When calling B.test, it correctly resolves C to B.C and returning back "good" as result. But when calling D.test, it raised out exception where C cannot being found((UndefinedFunctionError) undefined function C.test2/0)

Is there anyone who have some insights on the root of the problem here? Any help would be real appreciated. Thx in advance for the help ;)

Update

Confirmed as a bug and is fixed

1

1 Answers

0
votes

Essentially, the quote block is not properly resolving the alias B.C. If you directly refer to it inside of the block, the problem evaporates. I'm not exactly sure why this is the behavior. It is possible that the alias with the same current module name makes the call ambiguous. Either way, you can fix it by rewriting your C module to the following:

defmodule C do
  def test do
    quote do
      import A, only: [wrap: 2]
      wrap test do
        IO.inspect __ENV__
        B.C.test2
      end
    end
  end
end

Here's the result:

iex(1)> D.test
"good"

** Update:

This alias issue does appear to be a bug. With the alias inserted, the macro printout shows that both C and B.C are listed as aliases and yet it still doesn't work. I also verified that the current module name was not causing a collision issue.