@@ -379,15 +379,20 @@ expand({'^', Meta, [Arg]}, #elixir_ex{prematch={Prematch, _, _}, vars={_, Write}
379379
380380 _ ->
381381 function_error (Meta , E , ? MODULE , {invalid_arg_for_pin , Arg }),
382- {{'^' , Meta , [Arg ]}, S , E }
382+ {{'^' , Meta , [Arg ]}, S # elixir_ex { tainted_function = true } , E }
383383 end ;
384384expand ({'^' , Meta , [Arg ]}, S , E ) ->
385385 function_error (Meta , E , ? MODULE , {pin_outside_of_match , Arg }),
386- {{'^' , Meta , [Arg ]}, S , E };
386+ {{'^' , Meta , [Arg ]}, S # elixir_ex { tainted_function = true } , E };
387387
388388expand ({'_' , Meta , Kind } = Var , S , #{context := Context } = E ) when is_atom (Kind ) ->
389- (Context /= match ) andalso function_error (Meta , E , ? MODULE , unbound_underscore ),
390- {Var , S , E };
389+ case Context of
390+ match ->
391+ {Var , S , E };
392+ _ ->
393+ function_error (Meta , E , ? MODULE , unbound_underscore ),
394+ {Var , S # elixir_ex {tainted_function = true }, E }
395+ end ;
391396
392397expand ({Name , Meta , Kind }, S , #{context := match } = E ) when is_atom (Name ), is_atom (Kind ) ->
393398 # elixir_ex {
@@ -476,7 +481,7 @@ expand({Name, Meta, Kind}, S, E) when is_atom(Name), is_atom(Kind) ->
476481 % % TODO: Remove this clause on v2.0 as we will raise by default
477482 {if_undefined , raise } ->
478483 function_error (Meta , E , ? MODULE , {undefined_var , Name , Kind }),
479- {{Name , Meta , Kind }, S , E };
484+ {{Name , Meta , Kind }, S # elixir_ex { tainted_function = true } , E };
480485
481486 % % TODO: Remove this clause on v2.0 as we will no longer support warn
482487 _ when Error == warn ->
@@ -485,12 +490,12 @@ expand({Name, Meta, Kind}, S, E) when is_atom(Name), is_atom(Kind) ->
485490
486491 _ when Error == pin ->
487492 function_error (Meta , E , ? MODULE , {undefined_var_pin , Name , Kind }),
488- {{Name , Meta , Kind }, S , E };
493+ {{Name , Meta , Kind }, S # elixir_ex { tainted_function = true } , E };
489494
490495 _ when Error == raise ->
491496 SpanMeta = elixir_env :calculate_span (Meta , Name ),
492497 function_error (SpanMeta , E , ? MODULE , {undefined_var , Name , Kind }),
493- {{Name , SpanMeta , Kind }, S , E }
498+ {{Name , SpanMeta , Kind }, S # elixir_ex { tainted_function = true } , E }
494499 end
495500 end ;
496501
@@ -950,6 +955,11 @@ expand_remote(Receiver, DotMeta, Right, Meta, Args, S, SL, #{context := Context}
950955 Context =:= nil ->
951956 AttachedMeta = attach_runtime_module (Receiver , Meta , S , E ),
952957 {EArgs , {SA , _ }, EA } = mapfold (fun expand_arg /3 , {SL , S }, E , Args ),
958+
959+ SA # elixir_ex .tainted_function andalso is_atom (Receiver ) andalso
960+ (not is_loaded_and_exported (Receiver , Right , Args )) andalso
961+ elixir_errors :file_warn (Meta , E , ? MODULE , {undefined_function , Receiver , Right , length (Args )}),
962+
953963 Rewritten = elixir_rewrite :rewrite (Receiver , DotMeta , Right , AttachedMeta , EArgs ),
954964 {Rewritten , elixir_env :close_write (SA , S ), EA };
955965
@@ -970,6 +980,10 @@ expand_remote(Receiver, DotMeta, Right, Meta, Args, _, _, E) ->
970980 Call = {{'.' , DotMeta , [Receiver , Right ]}, Meta , Args },
971981 file_error (Meta , E , ? MODULE , {invalid_call , Call }).
972982
983+ is_loaded_and_exported (Receiver , Fun , Args ) ->
984+ (code :ensure_loaded (Receiver ) =:= {module , Receiver }) andalso
985+ erlang :function_exported (Receiver , Fun , length (Args )).
986+
973987attach_runtime_module (Receiver , Meta , S , _E ) ->
974988 case lists :member (Receiver , S # elixir_ex .runtime_modules ) of
975989 true -> [{runtime_module , true } | Meta ];
@@ -1127,6 +1141,11 @@ assert_no_underscore_clause_in_cond(_Other, _E) ->
11271141
11281142% % Errors
11291143
1144+ format_error ({undefined_function , Module , Fun , Arity }) ->
1145+ Opts = [{module , Module }, {function , Fun }, {arity , Arity }],
1146+ Exception = 'Elixir.UndefinedFunctionError' :exception (Opts ),
1147+ {BlamedException , _ } = 'Elixir.UndefinedFunctionError' :blame (Exception , []),
1148+ 'Elixir.UndefinedFunctionError' :message (BlamedException );
11301149format_error (invalid_match_on_zero_float ) ->
11311150 " pattern matching on 0.0 is equivalent to matching only on +0.0. Instead you must match on +0.0 or -0.0" ;
11321151format_error ({useless_literal , Term }) ->
0 commit comments