Skip to content

Commit 0dd9222

Browse files
committed
Avoid compile warnings when implementing an empty protocol
Implementing a protocol creates a module that implements the behaviour given by the protocol. A protocol that defines no functions by extension defines no callbacks, and so won't be recognized as a behaviour by the Erlang compiler. This means that Elixir will log a warning when implementing such a protocol, because behaviour_info/1 won't be defined on it, and so Elixir also won't recognize it as a behaviour. Erlang offers no mechanism to explicitly denote a module as defining a behaviour, but in this case we know that every protocol implementation must define an impl/1 function. By adding a callback that describes this function to all protocols, the warning can be avoided.
1 parent b401d15 commit 0dd9222

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

lib/elixir/lib/protocol.ex

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ defmodule Protocol do
764764
end
765765

766766
# TODO: Convert the following warnings into errors in future Elixir versions
767-
def __before_compile__(env) do
767+
defmacro __before_compile__(env) do
768768
# Callbacks
769769
callback_metas = callback_metas(env.module, :callback)
770770
callbacks = :maps.keys(callback_metas)
@@ -806,6 +806,16 @@ defmodule Protocol do
806806
nil
807807
)
808808
end
809+
810+
quote do
811+
# Register the __impl__/1 callback to ensure the protocol is a behaviour.
812+
# Without this, a protocol that defines no functions won't be detected
813+
# as a behaviour by the Erlang compiler, and any implementations of that
814+
# protocol will log a warning.
815+
@callback __impl__(:for) :: module()
816+
@callback __impl__(:target) :: module()
817+
@callback __impl__(:protocol) :: module()
818+
end
809819
end
810820

811821
defp after_defprotocol do

lib/elixir/test/elixir/protocol_test.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ defmodule ProtocolTest do
150150
assert args == [{:type, 23, :product, [{:user_type, 23, :t, []}]}, {:type, 23, :term, []}]
151151
end
152152

153+
test "protocol with no functions is a behaviour" do
154+
defprotocol ProtoNoFunctions do
155+
end
156+
157+
defimpl ProtoNoFunctions, for: Integer do
158+
end
159+
end
160+
153161
test "protocol defines functions and attributes" do
154162
assert Sample.__protocol__(:module) == Sample
155163
assert Sample.__protocol__(:functions) == [ok: 1]

0 commit comments

Comments
 (0)