Skip to content

Commit 407d2e1

Browse files
author
José Valim
committed
Raise better child_spec error if module is not defined, closes #6565
1 parent 94e5c72 commit 407d2e1

File tree

2 files changed

+45
-33
lines changed

2 files changed

+45
-33
lines changed

lib/elixir/lib/supervisor.ex

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -591,38 +591,7 @@ defmodule Supervisor do
591591
e in UndefinedFunctionError ->
592592
case System.stacktrace do
593593
[{^module, :child_spec, [^arg], _} | _] ->
594-
raise ArgumentError, """
595-
The module #{inspect module} was given as a child to a supervisor
596-
but it does not implement child_spec/1.
597-
598-
If you own the given module, please define a child_spec/1 function
599-
that receives an argument and returns a child specification as a map.
600-
For example:
601-
602-
def child_spec(opts) do
603-
%{
604-
id: __MODULE__,
605-
start: {__MODULE__, :start_link, [opts]},
606-
type: :worker,
607-
restart: :permanent,
608-
shutdown: 500
609-
}
610-
end
611-
612-
Note that "use Agent", "use GenServer" and so on automatically define
613-
this function for you.
614-
615-
However, if you don't own the given module and it doesn't implement
616-
child_spec/1, instead of passing the module name directly as a supervisor
617-
child, you will have to pass a child specification as a map:
618-
619-
%{
620-
id: #{inspect module},
621-
start: {#{inspect module}, :start_link, [arg1, arg2]}
622-
}
623-
624-
See the Supervisor documentation for more information.
625-
"""
594+
raise ArgumentError, child_spec_error(module)
626595
stack ->
627596
reraise e, stack
628597
end
@@ -647,6 +616,45 @@ defmodule Supervisor do
647616
"""
648617
end
649618

619+
defp child_spec_error(module) do
620+
if Code.ensure_loaded?(module) do
621+
"""
622+
The module #{inspect module} was given as a child to a supervisor
623+
but it does not implement child_spec/1.
624+
625+
If you own the given module, please define a child_spec/1 function
626+
that receives an argument and returns a child specification as a map.
627+
For example:
628+
629+
def child_spec(opts) do
630+
%{
631+
id: __MODULE__,
632+
start: {__MODULE__, :start_link, [opts]},
633+
type: :worker,
634+
restart: :permanent,
635+
shutdown: 500
636+
}
637+
end
638+
639+
Note that "use Agent", "use GenServer" and so on automatically define
640+
this function for you.
641+
642+
However, if you don't own the given module and it doesn't implement
643+
child_spec/1, instead of passing the module name directly as a supervisor
644+
child, you will have to pass a child specification as a map:
645+
646+
%{
647+
id: #{inspect module},
648+
start: {#{inspect module}, :start_link, [arg1, arg2]}
649+
}
650+
651+
See the Supervisor documentation for more information.
652+
"""
653+
else
654+
"The module #{inspect module} was given as a child to a supervisor but it does not exist."
655+
end
656+
end
657+
650658
@doc """
651659
Builds and overrides a child specification.
652660

lib/elixir/test/elixir/supervisor_test.exs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ defmodule SupervisorTest do
8080
restart: :permanent, shutdown: :infinity) ==
8181
%{id: :foo, start: {:foo, :bar, []}, restart: :permanent, shutdown: :infinity}
8282

83-
assert_raise ArgumentError, ~r"The module Unknown was given as a child", fn ->
83+
assert_raise ArgumentError, ~r"The module SupervisorTest was given as a child.*\nbut it does not implement"m, fn ->
84+
Supervisor.child_spec(SupervisorTest, [])
85+
end
86+
87+
assert_raise ArgumentError, ~r"The module Unknown was given as a child.*but it does not exist"m, fn ->
8488
Supervisor.child_spec(Unknown, [])
8589
end
8690

0 commit comments

Comments
 (0)