Skip to content

Commit e55c61f

Browse files
committed
Improve error message for protocols with no implementation, closes #14364
1 parent fa51593 commit e55c61f

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

lib/elixir/lib/module/types/apply.ex

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,11 +1031,21 @@ defmodule Module.Types.Apply do
10311031

10321032
Code.ensure_loaded?(mod) and
10331033
Keyword.has_key?(mod.module_info(:attributes), :__protocol__) ->
1034-
# Protocol errors can be very verbose, so we collapse structs
1035-
"""
1036-
but expected a type that implements the #{inspect(mod)} protocol, it must be one of:
1037-
#{clauses_args_to_quoted_string(clauses, converter, collapse_structs: true)}
1038-
"""
1034+
if function_exported?(mod, :__protocol__, 1) and
1035+
mod.__protocol__(:impls) == {:consolidated, []} do
1036+
"""
1037+
but the protocol was not yet implemented for any type and therefore will always fail. \
1038+
This error typically happens within libraries that define protocols and will disappear as \
1039+
soon as there is one implementation. If you expect the protocol to be implemented later on, \
1040+
you can define an implementation specific for development/test.
1041+
"""
1042+
else
1043+
# Protocol errors can be very verbose, so we collapse structs
1044+
"""
1045+
but expected a type that implements the #{inspect(mod)} protocol, it must be one of:
1046+
#{clauses_args_to_quoted_string(clauses, converter, collapse_structs: true)}
1047+
"""
1048+
end
10391049

10401050
true ->
10411051
"""

lib/elixir/test/elixir/module/types/integration_test.exs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ defmodule Module.Types.IntegrationTest do
455455
assert_no_warnings(files)
456456
end
457457

458-
test "mismatched impl" do
458+
test "mismatched implementation" do
459459
files = %{
460460
"a.ex" => """
461461
defprotocol Itself do
@@ -491,6 +491,50 @@ defmodule Module.Types.IntegrationTest do
491491
assert_warnings(files, warnings)
492492
end
493493

494+
@tag :require_ast
495+
test "no implementation" do
496+
files = %{
497+
"a.ex" => """
498+
defprotocol NoImplProtocol do
499+
def callback(data)
500+
end
501+
""",
502+
"b.ex" => """
503+
defmodule NoImplProtocol.Caller do
504+
def run do
505+
NoImplProtocol.callback(:hello)
506+
end
507+
end
508+
"""
509+
}
510+
511+
warnings = [
512+
"""
513+
warning: incompatible types given to NoImplProtocol.callback/1:
514+
515+
NoImplProtocol.callback(:hello)
516+
517+
given types:
518+
519+
-:hello-
520+
521+
but the protocol was not yet implemented for any type and therefore will always fail. \
522+
This error typically happens within libraries that define protocols and will disappear as \
523+
soon as there is one implementation. If you expect the protocol to be implemented later on, \
524+
you can define an implementation specific for development/test.
525+
526+
typing violation found at:
527+
528+
3 │ NoImplProtocol.callback(:hello)
529+
│ ~
530+
531+
└─ b.ex:3:20: NoImplProtocol.Caller.run/0
532+
"""
533+
]
534+
535+
assert_warnings(files, warnings, consolidate_protocols: true)
536+
end
537+
494538
@tag :require_ast
495539
test "String.Chars protocol dispatch" do
496540
files = %{

0 commit comments

Comments
 (0)