2 * Copyright (c) 2018, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
16 module Cls
= Typing_classes_heap
17 module MakeType
= Typing_make_type
19 let get_constant tc
(seen
, has_default
) = function
20 | Default _
-> (seen
, true)
21 | Case
(((pos
, _
), Class_const
((_
, CI
(_
, cls
)), (_
, const
))), _
) ->
22 if cls
<> Cls.name tc
then
23 (Errors.enum_switch_wrong_class pos
(strip_ns
(Cls.name tc
)) (strip_ns cls
);
26 (match SMap.get const seen
with
27 | None
-> (SMap.add const pos seen
, has_default
)
29 Errors.enum_switch_redundant const old_pos pos
;
31 | Case
(((pos
, _
), _
), _
) ->
32 Errors.enum_switch_not_const pos
;
35 let check_enum_exhaustiveness pos tc caselist coming_from_unresolved
=
36 (* If this check comes from an enum inside a Tunresolved, then
37 don't punish for having an extra default case *)
38 let (seen
, has_default
) =
39 List.fold_left ~f
:(get_constant tc
) ~init
:(SMap.empty
, false) caselist
in
42 |> Sequence.map ~f
:fst
43 |> Sequence.filter ~f
:((<>) SN.Members.mClass
)
44 |> Sequence.filter ~f
:(fun id
-> not
(SMap.mem id seen
))
45 |> Sequence.to_list_rev
47 let all_cases_handled = List.is_empty
unhandled in
48 match (all_cases_handled, has_default
, coming_from_unresolved
) with
50 Errors.enum_switch_nonexhaustive pos
unhandled (Cls.pos tc
)
51 | true, true, false -> Errors.enum_switch_redundant_default pos
(Cls.pos tc
)
54 let rec check_exhaustiveness_ env pos ty caselist enum_coming_from_unresolved
=
55 (* Right now we only do exhaustiveness checking for enums. *)
56 (* This function has a built in hack where if Tunresolved has an enum
57 inside then it tells the enum exhaustiveness checker to
58 not punish for extra default *)
59 let env, (_
, ty
) = Env.expand_type
env ty
in
62 let new_enum = enum_coming_from_unresolved
||
63 (List.length tyl
> 1 && List.exists tyl ~f
:begin fun cur_ty
->
64 let _, (_, cur_ty
) = Env.expand_type
env cur_ty
in
66 | Tabstract
(AKenum
_, _) -> true
69 List.fold_left tyl ~init
:env ~f
:begin fun env ty
->
70 check_exhaustiveness_ env pos ty caselist
new_enum
72 | Tabstract
(AKenum id
, _) ->
73 let dep = Typing_deps.Dep.AllMembers id
in
74 let decl_env = Env.get_decl_env
env in
75 Option.iter
decl_env.Decl_env.droot
76 (fun root
-> Typing_deps.add_idep root
dep);
77 let tc = unsafe_opt
@@ Env.get_enum
env id
in
78 check_enum_exhaustiveness pos
tc
79 caselist enum_coming_from_unresolved
;
81 | Terr
| Tany
| Tnonnull
| Tarraykind
_ | Tclass
_ | Toption
_
82 | Tprim
_ | Tvar
_ | Tfun
_ | Tabstract
(_, _) | Ttuple
_ | Tanon
(_, _)
83 | Tobject
| Tshape
_ | Tdynamic
-> env
85 let check_exhaustiveness env pos ty caselist
=
86 ignore
(check_exhaustiveness_ env pos ty caselist
false)
88 let ensure_valid_switch_case_value_types env scrutinee_ty casel errorf
=
89 let is_subtype ty_sub ty_super
= snd
(Env.subtype
env ty_sub ty_super
) in
90 let ty_num = (Reason.Rnone
, Tprim
Nast.Tnum
) in
91 let ty_arraykey = (Reason.Rnone
, Tprim
Nast.Tarraykey
) in
92 let ty_mixed = MakeType.mixed
Reason.Rnone
in
93 let ty_traversable = MakeType.traversable
Typing_reason.Rnone
ty_mixed in
94 let compatible_types ty1 ty2
=
95 (is_subtype ty1
ty_num && is_subtype ty2
ty_num) ||
96 (is_subtype ty1
ty_arraykey && is_subtype ty2
ty_arraykey) ||
97 (is_subtype ty1
ty_traversable && is_subtype ty2
ty_traversable &&
98 (is_subtype ty1 ty2
|| is_subtype ty2 ty1
)) ||
99 (is_subtype ty1 ty2
&& is_subtype ty2 ty1
) in
100 let ensure_valid_switch_case_value_type = function
102 | Case
(((case_value_p
, case_value_ty
), _), _) ->
103 if not
(compatible_types case_value_ty scrutinee_ty
) then
104 errorf
(Env.get_tcopt
env) case_value_p
105 (Env.print_ty
env case_value_ty
) (Env.print_ty
env scrutinee_ty
) in
106 List.iter casel
ensure_valid_switch_case_value_type
108 let handler errorf
= object
109 inherit Tast_visitor.handler_base
111 method! at_stmt
env x
=
113 | Switch
(((scrutinee_pos
, scrutinee_ty
), _), casel
) ->
114 check_exhaustiveness env scrutinee_pos scrutinee_ty casel
;
115 ensure_valid_switch_case_value_types env scrutinee_ty casel errorf