@@ -924,14 +924,38 @@ defmodule UndefinedFunctionError do
924924
925925 defp message ( :"function not exported" , module , function , arity ) do
926926 formatted_fun = Exception . format_mfa ( module , function , arity )
927- { "function #{ formatted_fun } is undefined or private" , true }
927+ fun_message = "function #{ formatted_fun } is undefined or private"
928+ behaviour_hint = behaviour_hint ( module , function , arity )
929+ { fun_message <> behaviour_hint , true }
928930 end
929931
930932 defp message ( reason , module , function , arity ) do
931933 formatted_fun = Exception . format_mfa ( module , function , arity )
932934 { "function #{ formatted_fun } is undefined (#{ reason } )" , false }
933935 end
934936
937+ defp behaviour_hint ( module , function , arity ) do
938+ case behaviours_for ( module ) do
939+ [ ] ->
940+ ""
941+
942+ behaviours ->
943+ case Enum . find ( behaviours , & expects_callback? ( & 1 , function , arity ) ) do
944+ nil -> ""
945+ behaviour -> ", but the behaviour #{ inspect ( behaviour ) } expects it to be present"
946+ end
947+ end
948+ rescue
949+ # In case the module was removed while we are computing this
950+ UndefinedFunctionError ->
951+ [ ]
952+ end
953+
954+ defp expects_callback? ( behaviour , function , arity ) do
955+ callbacks = behaviour . behaviour_info ( :callbacks )
956+ Enum . member? ( callbacks , { function , arity } )
957+ end
958+
935959 @ impl true
936960 def blame ( exception , stacktrace ) do
937961 % { reason: reason , module: module , function: function , arity: arity } = exception
@@ -997,6 +1021,12 @@ defmodule UndefinedFunctionError do
9971021 [ " * " , Code.Identifier . inspect_as_function ( fun ) , ?/ , Integer . to_string ( arity ) , ?\n ]
9981022 end
9991023
1024+ defp behaviours_for ( module ) do
1025+ :attributes
1026+ |> module . module_info ( )
1027+ |> Keyword . get ( :behaviour , [ ] )
1028+ end
1029+
10001030 defp exports_for ( module ) do
10011031 if function_exported? ( module , :__info__ , 1 ) do
10021032 module . __info__ ( :macros ) ++ module . __info__ ( :functions )
0 commit comments