@@ -295,7 +295,6 @@ defmodule Module.Types.Expr do
295295
296296 def of_expr ( { :case , meta , [ case_expr , [ { :do , clauses } ] ] } , expected , expr , stack , context ) do
297297 { case_type , context } = of_expr ( case_expr , @ pending , case_expr , stack , context )
298- info = { :case , meta , case_type , case_expr }
299298
300299 added_meta =
301300 if Macro . quoted_literal? ( case_expr ) do
@@ -312,7 +311,7 @@ defmodule Module.Types.Expr do
312311 else
313312 clauses
314313 end
315- |> of_clauses ( [ case_type ] , expected , expr , info , stack , context , none ( ) )
314+ |> of_case_clauses ( case_type , expected , meta , case_expr , expr , stack , context )
316315 |> dynamic_unless_static ( stack )
317316 end
318317
@@ -726,6 +725,63 @@ defmodule Module.Types.Expr do
726725 end )
727726 end
728727
728+ defp of_case_clauses ( clauses , domain , expected , case_meta , case_expr , expr , stack , original ) do
729+ % { failed: failed? } = original
730+
731+ { result , _previous , context } =
732+ Enum . reduce ( clauses , { none ( ) , none ( ) , original } , fn
733+ { :-> , meta , [ [ clause ] = head , body ] } , { acc , previous , context } ->
734+ { failed? , context } = reset_failed ( context , failed? )
735+ { patterns , guards } = extract_head ( head )
736+
737+ clause_domain = difference ( domain , previous )
738+ info = { :case , case_meta , clause_domain , case_expr , previous }
739+
740+ { trees , precise? , context } =
741+ Pattern . of_head ( patterns , guards , [ clause_domain ] , info , meta , stack , context )
742+
743+ # It failed, let's try to detect if it was due a previous or current clause.
744+ # The current clause will be easier to understand, so we prefer that
745+ { [ { clause_tree , _ , _ } ] , precise? , context } =
746+ if context . failed and previous != none ( ) do
747+ info = { :case , case_meta , domain , case_expr , nil }
748+
749+ case Pattern . of_head ( patterns , guards , [ domain ] , info , meta , stack , context ) do
750+ { _ , _ , % { failed: true } } = result -> result
751+ _ -> { trees , precise? , context }
752+ end
753+ else
754+ { trees , precise? , context }
755+ end
756+
757+ { previous , context } =
758+ if context . failed do
759+ { previous , context }
760+ else
761+ clause_type = Pattern . of_pattern_tree ( clause_tree , stack , context ) |> upper_bound ( )
762+
763+ cond do
764+ stack . mode != :infer and previous != none ( ) and subtype? ( clause_type , previous ) ->
765+ stack = % { stack | meta: meta }
766+ { previous , Pattern . badpattern_error ( clause , 0 , info , stack , context ) }
767+
768+ precise? ->
769+ { union ( previous , clause_type ) , context }
770+
771+ true ->
772+ { previous , context }
773+ end
774+ end
775+
776+ { result , context } = of_expr ( body , expected , expr || body , stack , context )
777+
778+ { union ( result , acc ) , previous ,
779+ context |> set_failed ( failed? ) |> Of . reset_vars ( original ) }
780+ end )
781+
782+ { result , context }
783+ end
784+
729785 defp reset_failed ( % { failed: true } = context , false ) , do: { true , % { context | failed: false } }
730786 defp reset_failed ( context , _ ) , do: { false , context }
731787
@@ -773,6 +829,25 @@ defmodule Module.Types.Expr do
773829
774830 ## Warning formatting
775831
832+ def format_diagnostic ( { :badclause , { :case , meta , type , expr } , [ head ] , context } ) do
833+ { expr , message } =
834+ if meta [ :type_check ] == :expr do
835+ { expr ,
836+ """
837+ the following conditional expression will always evaluate to #{ to_quoted_string ( type ) } :
838+
839+ #{ expr_to_string ( expr ) |> indent ( 4 ) }
840+ """ }
841+ else
842+ { head , "the following clause has no effect because a previous clause will always match\n " }
843+ end
844+
845+ % {
846+ details: % { typing_traces: collect_traces ( expr , context ) } ,
847+ message: message
848+ }
849+ end
850+
776851 def format_diagnostic ( { :badupdate , type , expr , context } ) do
777852 { :% , _ , [ module , { :%{} , _ , [ { :| , _ , [ map , _ ] } ] } ] } = expr
778853 traces = collect_traces ( map , context )
0 commit comments