diff --git a/lib/elixir/lib/module/types.ex b/lib/elixir/lib/module/types.ex index a5469d49e7..94ea500a8c 100644 --- a/lib/elixir/lib/module/types.ex +++ b/lib/elixir/lib/module/types.ex @@ -136,8 +136,8 @@ defmodule Module.Types do defp impl_for(attrs) do case List.keyfind(attrs, :__impl__, 0) do {:__impl__, [protocol: protocol, for: for]} -> - if Code.ensure_loaded?(protocol) and function_exported?(protocol, :behaviour_info, 1) do - {for, protocol.behaviour_info(:callbacks)} + if Code.ensure_loaded?(protocol) and function_exported?(protocol, :__protocol__, 1) do + {for, protocol.__protocol__(:functions)} else nil end diff --git a/lib/elixir/test/elixir/module/types/integration_test.exs b/lib/elixir/test/elixir/module/types/integration_test.exs index 6624bcd6cf..e6dbe439f3 100644 --- a/lib/elixir/test/elixir/module/types/integration_test.exs +++ b/lib/elixir/test/elixir/module/types/integration_test.exs @@ -154,6 +154,50 @@ defmodule Module.Types.IntegrationTest do assert itself_arg.(Itself.Unknown) == dynamic(open_map(__struct__: atom([Unknown]))) end + + test "ignores additional callbacks on implementations" do + files = %{ + "p.ex" => """ + defmodule InjectCallback do + defmacro __before_compile__(_env) do + quote do + @callback extra() :: term() + end + end + end + + defprotocol Injected do + @before_compile InjectCallback + def f(x) + end + + defimpl Injected, for: Atom do + def f(_), do: :ok + def extra(), do: :extra + end + + defprotocol Explicit do + @callback extra() :: term() + def f(x) + end + + defimpl Explicit, for: Atom do + def f(_), do: :ok + def extra(), do: :extra + end + """ + } + + assert capture_compile_warnings(files, []) == """ + warning: cannot define @callback extra/0 inside protocol, use def/1 to outline your protocol definition + │ + 20 │ @callback extra() :: term() + │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + │ + └─ p.ex:20: Explicit (module) + + """ + end end describe "type checking" do