To reproduce:
mix new Foocd foofoo/lib/foo.ex to contain the following:defprotocol Foo do
@doc """
## Examples
iex> Foo.bar([])
:ok
"""
def bar(elem)
end
defimpl Foo, for: List do
def bar(elem), do: :ok
end
mix testThe output:
Compiling 1 file (.ex)
warning: variable "elem" is unused
lib/foo.ex:13
Generated foo app
** (FunctionClauseError) no function clause matching in IO.chardata_to_string/1
(elixir) lib/io.ex:445: IO.chardata_to_string(nil)
(elixir) lib/path.ex:289: Path.relative_to/2
test/foo_test.exs:3: (module)
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/code.ex:370: Code.require_file/2
(elixir) lib/kernel/parallel_require.ex:57: anonymous fn/2 in Kernel.ParallelRequire.spawn_requires/5
I'd expect the doctests to work and mix test to run normally.
I think the problem might be caused here in ExUnit: https://github.com/elixir-lang/elixir/blob/master/lib/ex_unit/lib/ex_unit/doc_test.ex#L197
This attempts to look up the source file where the module is defined by using
mod.__info__(:compile)[:source]
For a normal module, the :source key in mod.__info(:compile) exists, but for a protocol, it does not, and thus nil is returned (And this is where Path.relative_to stumbles over.)
Possibly related to this is what happens when you do r ProtocolName (in the example: r Foo) in IEx. This will look up the module in the same way (but using module.module_info(:compile)[:source] rather than module.__info__(:compile)[:source]):
** (ArgumentError) could not find source for module: Foo
(iex) lib/iex/helpers.ex:426: IEx.Helpers.do_r/1
(iex) lib/iex/helpers.ex:415: IEx.Helpers.r/1
This might be caused in some way by the protocol consolidation, or at least by the way protocol modules are compiled.
I think that module.__info__(:compile) ought to contain the :source for things that want to introspect the original source (like the doctests that ExUnit wants to do).
@Qqwy good catch. so it is an issue with protocol consolidation most likely, which is discarding the source. We should also change doctest to not choke on those cases as well.
Indeed, it is a consolidation problem, as setting consolidate_protocols: false in the mix.exs of the project makes the doctests work as intended.
I now set it to consolidate_protocols: Mix.env != :test for the time being as a temporary workaround to allow my libraries to have doctests in their protocols.
Glad to see this reported, I experienced the other day and was convinced I did something wrong, and moved on 馃槀
:heart_eyes: Thank you!