2 * Copyright (c) 2015, 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.
11 (* This module performs checks after the naming has been done.
12 Basically any check that doesn't fall in the typing category. *)
13 (* Check of type application arity *)
14 (* Check no `new AbstractClass` (or trait, or interface) *)
15 (* Check no top-level break / continue *)
17 (* NOTE: since the typing environment does not generally contain
18 information about non-Hack code, some of these checks can
19 only be done in strict (i.e. "everything is Hack") mode. Use
20 `if Env.is_strict env.tenv` style checks accordingly.
29 module Env
= Typing_env
30 module Inst
= Decl_instantiate
31 module Phase
= Typing_phase
32 module SN
= Naming_special_names
33 module TGenConstraint
= Typing_generic_constraint
34 module Subst
= Decl_subst
35 module TUtils
= Typing_utils
36 module Cls
= Typing_classes_heap
39 type control_context
=
46 function_name
: string option;
47 class_name
: string option;
48 class_kind
: Ast.class_kind
option;
49 imm_ctrl_ctx
: control_context
;
50 typedef_tparams
: Nast.tparam list
;
51 is_array_append_allowed
: bool;
52 is_reactive
: bool; (* The enclosing function is reactive *)
56 module CheckFunctionBody
= struct
57 let rec stmt f_type env st
= match f_type
, st
with
58 | Ast.FSync
, Return
(_
, None
)
59 | Ast.FAsync
, Return
(_
, None
) -> ()
60 | Ast.FSync
, Return
(_
, Some e
)
61 | Ast.FAsync
, Return
(_
, Some e
) ->
62 expr_allow_await f_type env e
;
64 | (Ast.FGenerator
| Ast.FAsyncGenerator
), Return
(p
, e
) ->
67 | Some _
-> Errors.return_in_gen p
);
69 | Ast.FCoroutine
, Return
(_
, e
) ->
70 Option.iter e ~f
:(expr f_type env
)
74 expr_allow_await_or_rx_move f_type env e
;
76 | _
, ( Noop
| Fallthrough
| GotoLabel _
| Goto _
| Break _
| Continue _
77 | Static_var _
| Global_var _
| Unsafe_block _
) -> ()
78 | _
, Awaitall
(_
, el
) ->
79 List.iter el
(fun (x
, y
) ->
81 | Some x
-> expr f_type env x
;
86 | _
, If
(cond
, b1
, b2
) ->
99 | _
, Using
{ us_has_await
= has_await
; us_expr
= e
; us_block
= b
; _
} ->
100 if has_await
then found_await f_type
(fst e
);
101 expr_allow_await_list f_type env e
;
104 | _
, For
(init
, cond
, incr
, b
) ->
105 expr f_type env init
;
106 expr f_type env cond
;
107 expr f_type env incr
;
110 | _
, Switch
(e
, cl
) ->
112 List.iter cl
(case f_type env
);
114 | _
, Foreach
(_
, (Await_as_v
(p
, _
) | Await_as_kv
(p
, _
, _
)), _
) ->
117 | _
, Foreach
(v
, _
, b
) ->
121 | _
, Try
(b
, cl
, fb
) ->
123 List.iter cl
(catch f_type env
);
124 block f_type
{ env
with t_is_finally
= true } fb
;
126 | _
, Def_inline _
-> ()
127 | _
, Let
((p
, x
), _
, e
) ->
128 (* We treat let statement as an assignment expression *)
129 let fake_expr = (p
, Binop
(Ast.Eq None
, (p
, Lvar
(p
, x
)), e
)) in
130 expr_allow_await f_type env
fake_expr;
132 | _
, Block b
-> block f_type env b
;
133 | _
, Markup
(_
, eopt
) -> (match eopt
with Some e
-> expr f_type env e
| None
-> ())
134 | _
, Declare
(_
, e
, b
) ->
138 and found_await ftype p
=
140 | Ast.FCoroutine
-> Errors.await_in_coroutine p
141 | Ast.FSync
| Ast.FGenerator
-> Errors.await_in_sync_function p
144 and block f_type env stl
=
145 List.iter stl
(stmt f_type env
)
147 and start f_type env stl
=
149 | [If
((_
, Id
(_
, c
)), then_stmt
, else_stmt
) ]
151 (* this is the only case when HH\Rx\IS_ENABLED can appear in
152 function body, other occurences are considered errors *)
154 if (HH\Rx\IS_ENABLED
) {}
158 when c
= SN.Rx.is_enabled
->
159 block f_type env then_stmt
;
160 block f_type env else_stmt
;
164 and case f_type env
= function
165 | Default b
-> block f_type env b
167 expr f_type env cond
;
171 and catch f_type env
(_
, _
, b
) = block f_type env b
173 and afield f_type env
= function
174 | AFvalue e
-> expr f_type env e
175 | AFkvalue
(e1
, e2
) -> expr2 f_type env
(e1
, e2
)
177 and expr f_type env
(p
, e
) =
180 and expr_allow_await_list f_type env
((_
, exp
) as e
) =
183 List.iter el
(expr_allow_await f_type env
)
185 expr_allow_await f_type env e
187 and expr_allow_await ?
(is_rhs
=false) f_type env
(p
, exp
) = match f_type
, exp
with
188 | Ast.FAsync
, Await e
189 | Ast.FAsyncGenerator
, Await e
-> expr f_type env e
; ()
190 | Ast.FAsync
, Binop
(Ast.Eq None
, e1
, (_
, Await e
))
191 | Ast.FAsyncGenerator
, Binop
(Ast.Eq None
, e1
, (_
, Await e
)) when not is_rhs
->
195 | _
-> expr_ p f_type env exp
; ()
197 and expr_allow_rx_move orelse f_type env exp
=
199 | _
, Call
(_
, e
, _
, el
, uel
) when is_rx_move e
->
201 List.iter el ~f
:(expr f_type env
);
202 List.iter uel ~f
:(expr f_type env
);
205 orelse f_type env exp
207 and expr_allow_await_or_rx_move f_type env exp
=
209 | _
, Binop
(Ast.Eq None
, e1
, rhs
) ->
211 expr_allow_rx_move
(expr_allow_await ~is_rhs
:true) f_type env rhs
213 expr_allow_await f_type env exp
217 | _
, Id
(_
, v
) -> v
= SN.Rx.move
220 and expr2 f_type env
(e1
, e2
) =
225 and expr_ p f_type env exp
= match f_type
, exp
with
232 | _
, ParenthesizedExpr _
-> failwith
"AST should not contain these nodes after naming"
243 | _
, Dollardollar _
-> ()
244 | _
, Id
(pos
, const
) when const
= SN.Rx.is_enabled
->
245 Errors.rx_is_enabled_invalid_location pos
251 | _
, Pipe
(_
, l
, r
) ->
255 List.iter afl
(afield f_type env
);
258 List.iter afl
(expr2 f_type env
);
261 List.iter afl
(expr f_type env
);
263 | _
, ValCollection
(_
, el
) ->
264 List.iter el
(expr f_type env
);
266 | _
, KeyValCollection
(_
, fdl
) ->
267 List.iter fdl
(expr2 f_type env
);
269 | _
, Clone e
-> expr f_type env e
; ()
270 | _
, Obj_get
(e
, (_
, Id _s
), _
) ->
273 | _
, Obj_get
(e1
, e2
, _
) ->
274 expr2 f_type env
(e1
, e2
);
276 | _
, Array_get
(e
, eopt
) ->
278 maybe
(expr f_type
) env eopt
;
280 | _
, Call
(_
, e
, _
, _
, _
) when is_rx_move e
->
281 Errors.rx_move_invalid_location
(fst e
);
283 | _
, Call
(_
, e
, _
, el
, uel
) ->
285 List.iter el
(expr_allow_rx_move expr f_type env
);
286 List.iter uel
(expr_allow_rx_move expr f_type env
);
288 | _
, True
| _
, False
| _
, Int _
289 | _
, Float _
| _
, Null
| _
, String _
-> ()
290 | _
, PrefixedString
(_
, e
) ->
294 List.iter el
(expr f_type env
);
297 List.iter el
(expr f_type env
);
299 | _
, Pair
(e1
, e2
) ->
300 expr2 f_type env
(e1
, e2
);
303 List.iter el
(expr f_type env
);
305 | _
, Unop
(_
, e
) -> expr f_type env e
306 | _
, Binop
(_
, e1
, e2
) ->
307 expr2 f_type env
(e1
, e2
);
309 | _
, Eif
(e1
, None
, e3
) ->
310 expr2 f_type env
(e1
, e3
);
312 | _
, Eif
(e1
, Some e2
, e3
) ->
313 List.iter
[e1
; e2
; e3
] (expr f_type env
);
315 | _
, New
(_
, _
, el
, uel
, _
) ->
316 List.iter el
(expr f_type env
);
317 List.iter uel
(expr f_type env
);
319 | _
, InstanceOf
(e
, _
)
327 | Ast.FGenerator
, Yield_break
328 | Ast.FAsyncGenerator
, Yield_break
-> ()
329 | Ast.FGenerator
, Yield af
330 | Ast.FAsyncGenerator
, Yield af
-> afield f_type env af
; ()
331 | Ast.FGenerator
, Yield_from e
332 | Ast.FAsyncGenerator
, Yield_from e
-> expr f_type env e
; ()
333 (* Should never happen -- presence of yield should make us FGenerator or
334 * FAsyncGenerator. *)
335 | (Ast.FSync
| Ast.FAsync
), (Yield _
| Yield_from _
| Yield_break
) -> assert false
337 | (Ast.FGenerator
| Ast.FSync
| Ast.FCoroutine
), Await _
->
340 | Ast.FAsync
, Await _
341 | Ast.FAsyncGenerator
, Await _
-> Errors.await_not_allowed p
343 | Ast.FCoroutine
, (Yield _
| Yield_break
| Yield_from _
) ->
344 Errors.yield_in_coroutine p
345 | (Ast.FSync
| Ast.FAsync
| Ast.FGenerator
| Ast.FAsyncGenerator
), Suspend _
->
346 Errors.suspend_outside_of_coroutine p
347 | Ast.FCoroutine
, Suspend _
->
349 then Errors.suspend_in_finally p
350 | _
, Special_func func
->
353 | Gen_array_rec e
-> expr f_type env e
354 | Genva el
-> List.iter el
(expr f_type env
));
356 | _
, Xml
(_
, attrl
, el
) ->
357 List.iter attrl
(fun attr
-> expr f_type env
(get_xhp_attr_expr attr
));
358 List.iter el
(expr f_type env
);
360 | _
, Unsafe_expr _
-> ()
361 | _
, Callconv
(_
, e
) ->
364 | _
, Execution_operator _
-> ()
365 | _
, Assert
(AE_assert e
) ->
369 List.iter ~f
:(fun (_
, v
) -> expr f_type env v
) fdm
;
375 let h = Caml.Hashtbl.create
23 in
376 let a x
= Caml.Hashtbl.add
h x
true in
377 let _ = SSet.iter
(fun m
-> if m
<> SN.Members.__toString
then a m
) SN.Members.as_set
in
384 let check_conditionally_reactive_annotation_params p params ~is_method
=
386 | [_, Class_const
(_, (_, prop
))] when prop
= "class" -> ()
387 | _ -> Errors.conditionally_reactive_annotation_invalid_arguments ~is_method p
389 let check_conditionally_reactive_annotations is_reactive p method_name user_attributes
=
390 let rec check l seen
=
393 | { ua_name
= (_, name
); ua_params
} :: xs
when name
= SN.UserAttributes.uaOnlyRxIfImpl
->
396 then Errors.multiple_conditionally_reactive_annotations p method_name
398 then check_conditionally_reactive_annotation_params ~is_method
:true p ua_params
;
401 | _ :: xs
-> check xs seen
in
402 check user_attributes
false
404 let is_some_reactivity_attribute { ua_name
= (_, name
); _ } =
405 name
= SN.UserAttributes.uaReactive
||
406 name
= SN.UserAttributes.uaLocalReactive
||
407 name
= SN.UserAttributes.uaShallowReactive
409 (* During NastCheck, all reactivity kinds are the same *)
410 let fun_is_reactive user_attributes
=
411 List.exists user_attributes ~f
:is_some_reactivity_attribute
413 let rec fun_ tenv f named_body
=
414 let env = { t_is_finally
= false;
415 is_array_append_allowed
= false;
416 class_name
= None
; class_kind
= None
;
417 imm_ctrl_ctx
= Toplevel
;
418 typedef_tparams
= [];
420 function_name
= None
;
421 is_reactive
= fun_is_reactive f
.f_user_attributes
423 func
env f named_body
425 and func
env f named_body
=
426 let p, fname
= f
.f_name
in
427 let fname_lower = String.lowercase
(strip_ns fname
) in
428 if fname_lower = SN.Members.__construct
|| fname_lower = "using"
429 then Errors.illegal_function_name
p fname
;
430 (* Add type parameters to typing environment and localize the bounds *)
431 let tenv, constraints
=
432 Phase.localize_generic_parameters_with_bounds
env.tenv f
.f_tparams
433 ~ety_env
:(Phase.env_with_self
env.tenv) in
434 let tenv = add_constraints
p tenv constraints
in
436 tenv = Env.set_mode
tenv f
.f_mode
;
437 t_is_finally
= false;
438 is_reactive
= env.is_reactive
|| fun_is_reactive f
.f_user_attributes
;
440 maybe hint
env f
.f_ret
;
441 (* Functions can't be mutable, only methods can *)
442 if Attributes.mem
SN.UserAttributes.uaMutable f
.f_user_attributes
then
443 Errors.mutable_attribute_on_function
p;
444 if Attributes.mem
SN.UserAttributes.uaMaybeMutable f
.f_user_attributes
then
445 Errors.maybe_mutable_attribute_on_function
p;
446 if Attributes.mem
SN.UserAttributes.uaMutableReturn f
.f_user_attributes
447 && not
env.is_reactive
then
448 Errors.mutable_return_annotated_decls_must_be_reactive
"function" p fname
;
450 check_maybe_rx_attributes_on_params
env f
.f_user_attributes f
.f_params
;
452 List.iter f
.f_tparams
(tparam
env);
453 let byref = List.find f
.f_params ~f
:(fun x
-> x
.param_is_reference
) in
454 List.iter f
.f_params
(fun_param
env f
.f_name f
.f_fun_kind
byref);
455 let inout = List.find f
.f_params ~f
:(
456 fun x
-> x
.param_callconv
= Some
Ast.Pinout
) in
459 if Attributes.mem
SN.UserAttributes.uaMemoize f
.f_user_attributes
||
460 Attributes.mem
SN.UserAttributes.uaMemoizeLSB f
.f_user_attributes
461 then Errors.inout_params_memoize
p param
.param_pos
;
465 (match f
.f_variadic
with
466 | FVvariadicArg vparam
->
467 if vparam
.param_is_reference
then
468 Errors.variadic_byref_param vparam
.param_pos
471 block
env named_body
.fb_ast
;
472 CheckFunctionBody.start
478 List.iter t
.tp_constraints
(fun (_, h) -> hint
env h)
480 and hint
env (p, h) =
483 and hint_
env p = function
484 | Hany
| Hmixed
| Hnonnull
| Hprim
_ | Hthis
| Haccess
_ | Habstr
_ | Hdynamic
->
486 | Harray
(ty1
, ty2
) ->
489 | Hdarray
(ty1
, ty2
) ->
492 | Hvarray_or_darray ty
495 | Htuple hl
-> List.iter hl
(hint
env)
498 | Hfun
(_, _, hl
, _, _, variadic_hint
, h, _) ->
499 List.iter hl
(hint
env);
501 begin match variadic_hint
with
502 | Hvariadic
(Some
h) -> hint
env h;
503 | Hvariadic
(None
) when Env.is_strict
env.tenv ->
504 Errors.ellipsis_strict_mode ~require
:`Type
p;
507 | Happly
((_, x
), hl
) as h when Env.is_typedef x
->
508 begin match Typing_lazy_heap.get_typedef
(Env.get_tcopt
env.tenv) x
with
510 check_happly
env.typedef_tparams
env.tenv (p, h);
511 List.iter hl
(hint
env)
514 | Happly
((_, x
), hl
) as h ->
515 (match Env.get_class
env.tenv x
with
518 check_happly
env.typedef_tparams
env.tenv (p, h);
519 List.iter hl
(hint
env)
522 | Hshape
{ nsi_allows_unknown_fields
=_; nsi_field_map
} ->
523 let compute_hint_for_shape_field_info _ { sfi_hint
; _; } =
526 ShapeMap.iter
compute_hint_for_shape_field_info nsi_field_map
532 and check_happly unchecked_tparams
env h =
533 let env = { env with Env.pos
= (fst
h) } in
534 let decl_ty = Decl_hint.hint
env.Env.decl_env
h in
535 let unchecked_tparams =
536 List.map
unchecked_tparams begin fun t
->
537 let cstrl = List.map t
.tp_constraints
(fun (ck
, cstr
) ->
538 let cstr = Decl_hint.hint
env.Env.decl_env
cstr in
541 Typing_defs.tp_variance
= t
.tp_variance
;
543 tp_constraints
= cstrl;
544 tp_reified
= t
.tp_reified
;
545 tp_user_attributes
= t
.tp_user_attributes
;
548 let tyl = List.map
unchecked_tparams (fun t
-> Reason.Rwitness
(fst t
.tp_name
), Tany
) in
549 let subst = Inst.make_subst
unchecked_tparams tyl in
550 let decl_ty = Inst.instantiate
subst decl_ty in
552 | _, Tapply
(_, tyl) when tyl <> [] ->
553 let env, locl_ty
= Phase.localize_with_self
env decl_ty in
554 begin match TUtils.get_base_type
env locl_ty
with
555 | _, Tclass
(cls
, _, tyl) ->
556 (match Env.get_class
env (snd cls
) with
558 let tc_tparams = Cls.tparams cls
in
559 (* We want to instantiate the class type parameters with the
560 * type list of the class we are localizing. We do not want to
561 * add any more constraints when we localize the constraints
562 * stored in the class_type since it may lead to infinite
566 { (Phase.env_with_self
env) with
567 substs
= Subst.make
tc_tparams tyl;
569 iter2_shortest
begin fun { tp_name
= (p, x
); tp_constraints
= cstrl; _ } ty
->
570 List.iter
cstrl (fun (ck
, cstr_ty
) ->
571 let r = Reason.Rwitness
p in
572 let env, cstr_ty
= Phase.localize ~
ety_env env cstr_ty
in
573 ignore
@@ Errors.try_
575 TGenConstraint.check_constraint
env ck ty ~cstr_ty
578 Reason.explain_generic_constraint
env.Env.pos
r x l
;
589 let cname = Some
(snd c
.c_name
) in
590 let env = { t_is_finally
= false;
591 is_array_append_allowed
= false;
593 class_kind
= Some c
.c_kind
;
594 imm_ctrl_ctx
= Toplevel
;
595 typedef_tparams
= [];
597 function_name
= None
;
599 (* Add type parameters to typing environment and localize the bounds *)
600 let tenv, constraints
= Phase.localize_generic_parameters_with_bounds
601 tenv c
.c_tparams
.c_tparam_list
602 ~
ety_env:(Phase.env_with_self
tenv) in
603 let tenv = add_constraints
(fst c
.c_name
) tenv constraints
in
604 let env = { env with tenv = Env.set_mode
tenv c
.c_mode
} in
607 * prevent for abstract final classes, traits, and interfaces
609 if Attributes.mem
SN.UserAttributes.uaConst c
.c_user_attributes
610 then begin match c
.c_kind
, c
.c_final
with
611 | Ast.Cabstract
, true
615 Errors.const_attribute_prohibited
616 (fst c
.c_name
) (Typing_print.class_kind c
.c_kind c
.c_final
);
617 | Ast.Cabstract
, false
618 | Ast.Cnormal
, _ -> ();
620 if c
.c_kind
= Ast.Cabstract
&& c
.c_final
then begin
621 List.iter c
.c_methods
(fun m
-> Errors.nonstatic_method_in_abstract_final_class
(fst m
.m_name
));
622 Option.iter c
.c_constructor
623 (fun m
-> Errors.nonstatic_method_in_abstract_final_class
(fst m
.m_name
))
626 if c
.c_kind
= Ast.Cinterface
then begin
630 maybe method_
(env, true) c
.c_constructor
;
632 List.iter c
.c_tparams
.c_tparam_list
(tparam
env);
633 List.iter c
.c_extends
(hint
env);
634 List.iter c
.c_implements
(hint
env);
635 List.iter c
.c_consts
(class_const
env);
636 List.iter c
.c_typeconsts
(typeconst
(env, c
.c_tparams
.c_tparam_list
));
637 List.iter c
.c_static_vars
(class_var
env);
638 List.iter c
.c_vars
(class_var
env);
639 List.iter c
.c_static_methods
(method_
(env, true));
640 List.iter c
.c_methods
(method_
(env, false));
641 List.iter c
.c_implements
(check_is_interface
(env, "implement"));
642 List.iter c
.c_req_implements
643 (check_is_interface
(env, "require implementation of"));
644 List.iter c
.c_req_extends
(check_is_class
env);
645 List.iter c
.c_uses
(check_is_trait
env);
647 (** Make sure that the given hint points to an interface *)
648 and check_is_interface
(env, error_verb
) (x
: hint
) =
651 (match Env.get_class
env.tenv (snd id
) with
653 (* in partial mode, we can fail to find the class if it's
655 (* in strict mode, we catch the unknown class error before
656 even reaching here. *)
658 | Some cls
when Cls.kind cls
= Ast.Cinterface
-> ()
660 Errors.non_interface
(fst x
) (Cls.name cls
) error_verb
663 Errors.non_interface
(fst x
) "generic" error_verb
665 Errors.non_interface
(fst x
) "invalid type hint" error_verb
667 (** Make sure that the given hint points to a non-final class *)
668 and check_is_class
env (x
: hint
) =
671 (match Env.get_class
env.tenv (snd id
) with
673 (* in partial mode, we can fail to find the class if it's
675 (* in strict mode, we catch the unknown class error before
676 even reaching here. *)
679 let kind = Cls.kind cls
in
680 let name = Cls.name cls
in
682 | Ast.(Cabstract
| Cnormal
) ->
683 if Cls.final cls
then Errors.requires_final_class
(fst x
) name
685 Errors.requires_non_class
(fst x
) name (Ast.string_of_class_kind
kind)
688 Errors.requires_non_class
(fst x
) name "a generic"
690 Errors.requires_non_class
(fst x
) "This" "an invalid type hint"
693 * Make sure that all "use"s are with traits, and not
694 * classes, interfaces, etc.
696 and check_is_trait
env (h : hint
) =
697 (* Second part of a hint contains Happly information *)
699 (* An Happly contains identifying info (sid) and hint list (which we *)
700 (* do not care about at this time *)
701 | Happly
(pos_and_name
, _) ->
702 (* Env.get_class will get the type info associated with the name *)
703 let type_info = Env.get_class
env.tenv (snd pos_and_name
) in
704 (match type_info with
705 (* in partial mode, it's possible to not find the trait, because the *)
706 (* trait may live in PHP land. In strict mode, we catch the unknown *)
707 (* trait error before even reaching here. so it's ok to just return *)
710 (* tc_kind is part of the type_info. If we are a trait, all is good *)
711 | Some cls
when Cls.kind cls
= Ast.Ctrait
-> ()
712 (* Anything other than a trait we are going to throw an error *)
713 (* using the tc_kind and tc_name fields of our type_info *)
715 let name = Cls.name cls
in
716 let kind = Cls.kind cls
in
717 Errors.uses_non_trait
(fst
h) name (Ast.string_of_class_kind
kind)
719 | _ -> failwith
"assertion failure: trait isn't an Happly"
722 (* Class properties can only be initialized with a static literal expression. *)
723 and check_class_property_initialization prop
=
724 (* Only do the check if property is initialized. *)
725 Option.iter prop
.cv_expr ~f
:begin fun e
->
726 let rec rec_assert_static_literal e
=
734 | ParenthesizedExpr
_ -> failwith
"AST should not contain these nodes after naming"
736 | Id
_ | Class_const
_ | True
| False
| Int
_ | Float
_
737 | Null
| String
_ | PrefixedString
_ | Unsafe_expr
_
738 | Execution_operator
_ ->
740 | Array field_list
->
741 List.iter field_list
begin function
742 | AFvalue expr
-> rec_assert_static_literal expr
743 | AFkvalue
(expr1
, expr2
) ->
744 rec_assert_static_literal expr1
;
745 rec_assert_static_literal expr2
;
747 | Darray fl
-> List.iter fl assert_static_literal_for_field_list
748 | Varray el
-> List.iter el
rec_assert_static_literal
749 | Shape fl
-> List.iter ~f
:(fun (_, e
) -> rec_assert_static_literal e
) fl
753 | ValCollection
(_, el
) -> List.iter el
rec_assert_static_literal
754 | Pair
(expr1
, expr2
)
755 | Binop
(_, expr1
, expr2
) ->
756 rec_assert_static_literal expr1
;
757 rec_assert_static_literal expr2
;
758 | KeyValCollection
(_, field_list
) ->
759 List.iter field_list assert_static_literal_for_field_list
762 rec_assert_static_literal e
;
763 | Eif
(expr1
, optional_expr
, expr2
) ->
764 rec_assert_static_literal expr1
;
765 Option.iter optional_expr
rec_assert_static_literal;
766 rec_assert_static_literal expr2
;
767 | This
| Lvar
_ | ImmutableVar
_ | Lplaceholder
_ | Dollardollar
_ | Fun_id
_
769 | Method_caller
_ | Smethod_id
_ | Obj_get
_ | Array_get
_ | Class_get
_
770 | Call
_ | Special_func
_ | Yield_break
| Yield
_ | Yield_from
_ | Suspend
_
771 | Await
_ | InstanceOf
_ | Is
_ | New
_ | Efun
_ | Xml
_ | Callconv
_
772 | Assert
_ | Clone
_ | As
_ | Pipe
_ ->
773 Errors.class_property_only_static_literal
(fst e
)
774 and assert_static_literal_for_field_list
(expr1
, expr2
) =
775 rec_assert_static_literal expr1
;
776 rec_assert_static_literal expr2
778 rec_assert_static_literal e
;
782 let enforce_no_body = begin fun m
->
783 match m
.m_body
.fb_ast
with
785 if m
.m_visibility
= Private
786 then Errors.not_public_or_protected_interface
(fst m
.m_name
)
788 | _ -> Errors.abstract_body
(fst m
.m_name
)
790 (* make sure that interface methods are not async, in line with HHVM *)
791 let enforce_not_async = begin fun m
->
792 match m
.m_fun_kind
with
793 | Ast.FAsync
-> Errors.async_in_interface
(fst m
.m_name
)
794 | Ast.FAsyncGenerator
-> Errors.async_in_interface
(fst m
.m_name
)
797 (* make sure that interfaces only have empty public methods *)
798 List.iter
(c
.c_static_methods
@ c
.c_methods
) enforce_no_body;
799 List.iter
(c
.c_static_methods
@ c
.c_methods
) enforce_not_async;
800 (* make sure constructor has no body *)
801 Option.iter c
.c_constructor
enforce_no_body;
802 Option.iter c
.c_constructor
enforce_not_async;
803 List.iter
(c
.c_uses
) (fun (p, _) ->
804 Errors.interface_use_trait
p
806 (* make sure that interfaces don't have any member variables *)
809 let pos = fst
(hd
.cv_id
) in
810 Errors.interface_with_member_variable
pos
812 (* make sure that interfaces don't have static variables *)
813 match c
.c_static_vars
with
815 let pos = fst
(hd
.cv_id
) in
816 Errors.interface_with_static_member_variable
pos
818 (* make sure interfaces do not contain partially abstract type constants *)
819 List.iter c
.c_typeconsts
begin fun tc
->
820 if tc
.c_tconst_constraint
<> None
&& tc
.c_tconst_type
<> None
then
821 Errors.interface_with_partial_typeconst
(fst tc
.c_tconst_name
)
824 and class_const
env (h, _, e
) =
829 and typeconst
(env, class_tparams
) tconst
=
830 maybe hint
env tconst
.c_tconst_type
;
831 maybe hint
env tconst
.c_tconst_constraint
;
832 (* need to ensure that tconst.c_tconst_type is not Habstr *)
833 maybe check_no_class_tparams class_tparams tconst
.c_tconst_type
;
834 maybe check_no_class_tparams class_tparams tconst
.c_tconst_constraint
836 (* Check to make sure we are not using class type params for type const decls *)
837 and check_no_class_tparams class_tparams
(pos, ty
) =
838 let check_tparams = check_no_class_tparams class_tparams
in
839 let maybe_check_tparams = maybe check_no_class_tparams class_tparams
in
840 let matches_class_tparam tp_name
=
841 List.iter class_tparams
begin fun { tp_name
= (c_tp_pos
, c_tp_name
); _ } ->
842 if c_tp_name
= tp_name
843 then Errors.typeconst_depends_on_external_tparam
pos c_tp_pos c_tp_name
846 | Hany
| Hmixed
| Hnonnull
| Hprim
_ | Hthis
| Hdynamic
-> ()
847 (* We have found a type parameter. Make sure its name does not match
848 * a name in class_tparams *)
849 | Habstr tparam_name
->
850 matches_class_tparam tparam_name
851 | Harray
(ty1
, ty2
) ->
852 maybe_check_tparams ty1
;
853 maybe_check_tparams ty2
854 | Hdarray
(ty1
, ty2
) ->
857 | Hvarray_or_darray ty
860 | Htuple
tyl -> List.iter
tyl check_tparams
861 | Hoption ty_
-> check_tparams ty_
862 | Hfun
(_, _, tyl, _, _, _, ty_
, _) ->
863 List.iter
tyl check_tparams;
865 | Happly
(_, tyl) -> List.iter
tyl check_tparams
866 | Hshape
{ nsi_allows_unknown_fields
=_; nsi_field_map
} ->
867 ShapeMap.iter
(fun _ v
-> check_tparams v
.sfi_hint
) nsi_field_map
868 | Haccess
(root_ty
, _) ->
869 check_tparams root_ty
870 | Hsoft ty_
-> check_tparams ty_
871 | Hreified ty_
-> check_tparams ty_
873 and class_var
env cv
=
874 check_class_property_initialization cv
;
876 (* If this is an XHP attribute and we're in strict mode,
877 relax to partial mode to allow the use of generic
878 classes without specifying type parameters. This is
879 a temporary hack to support existing code for now. *)
880 (* Task #5815945: Get rid of this Hack *)
881 if cv
.cv_is_xhp
&& (Typing_env.is_strict
env.tenv)
882 then { env with tenv = Typing_env.set_mode
env.tenv FileInfo.Mpartial
}
884 maybe hint
hint_env cv
.cv_type
;
885 maybe expr
env cv
.cv_expr
;
888 and check__toString m is_static
=
889 if snd m
.m_name
= SN.Members.__toString
891 if m
.m_visibility
<> Public
|| is_static
892 then Errors.toString_visibility
(fst m
.m_name
);
894 | Some
(_, Hprim Tstring
) -> ()
895 | Some
(p, _) -> Errors.toString_returns_string
p
899 and add_constraint
pos tenv (ty1
, ck
, ty2
) =
900 Typing_subtype.add_constraint
pos tenv ck ty1 ty2
902 and add_constraints
pos tenv (cstrs
: locl where_constraint list
) =
903 List.fold_left cstrs ~init
:tenv ~f
:(add_constraint
pos)
905 and check_static_memoized_function m
=
906 if Attributes.mem
SN.UserAttributes.uaMemoize m
.m_user_attributes
||
907 Attributes.mem
SN.UserAttributes.uaMemoizeLSB m
.m_user_attributes
then
908 Errors.static_memoized_function
(fst m
.m_name
);
911 and method_
(env, is_static
) m
=
913 { env with is_reactive
= fun_is_reactive m
.m_user_attributes
} in
914 let named_body = assert_named_body m
.m_body
in
915 check__toString m is_static
;
916 let env = { env with function_name
= Some
(snd m
.m_name
) } in
917 let p, name = m
.m_name
in
918 (* Add method type parameters to environment and localize the bounds *)
919 let tenv, constraints
=
920 Phase.localize_generic_parameters_with_bounds
env.tenv m
.m_tparams
921 ~
ety_env:(Phase.env_with_self
env.tenv) in
922 let tenv = add_constraints
(fst m
.m_name
) tenv constraints
in
923 let env = { env with tenv = tenv } in
925 (* If this is a destructor make sure it is allowed *)
926 if name = SN.Members.__destruct
927 && not
(Attributes.mem
SN.UserAttributes.uaOptionalDestruct m
.m_user_attributes
)
928 then Errors.illegal_destructor
p;
931 Attributes.mem
SN.UserAttributes.uaMutable m
.m_user_attributes
in
933 let is_maybe_mutable =
934 Attributes.mem
SN.UserAttributes.uaMaybeMutable m
.m_user_attributes
in
936 (* Mutable methods must be reactive *)
937 if not
env.is_reactive
then begin
939 then Errors.mutable_methods_must_be_reactive
p name;
941 then Errors.maybe_mutable_methods_must_be_reactive
p name;
946 then Errors.conflicting_mutable_and_maybe_mutable_attributes
p;
949 (*Methods annotated with MutableReturn attribute must be reactive *)
950 if Attributes.mem
SN.UserAttributes.uaMutableReturn m
.m_user_attributes
951 && not
env.is_reactive
then
952 Errors.mutable_return_annotated_decls_must_be_reactive
"method" p name;
954 check_conditionally_reactive_annotations env.is_reactive
p name m
.m_user_attributes
;
955 check_maybe_rx_attributes_on_params
env m
.m_user_attributes m
.m_params
;
957 let byref = List.find m
.m_params ~f
:(fun x
-> x
.param_is_reference
) in
958 List.iter m
.m_params
(fun_param
env m
.m_name m
.m_fun_kind
byref);
959 let inout = List.find m
.m_params ~f
:(
960 fun x
-> x
.param_callconv
= Some
Ast.Pinout
) in
963 if Attributes.mem
SN.UserAttributes.uaMemoize m
.m_user_attributes
||
964 Attributes.mem
SN.UserAttributes.uaMemoizeLSB m
.m_user_attributes
965 then Errors.inout_params_memoize
p param
.param_pos
;
969 (match m
.m_variadic
with
970 | FVvariadicArg vparam
->
971 if vparam
.param_is_reference
then
972 Errors.variadic_byref_param vparam
.param_pos
975 List.iter m
.m_tparams
(tparam
env);
976 block
env named_body.fb_ast
;
977 maybe hint
env m
.m_ret
;
978 CheckFunctionBody.start
982 (match env.class_name
with
984 let p, mname
= m
.m_name
in
985 if String.lowercase
(strip_ns
cname) = String.lowercase mname
986 && env.class_kind
<> Some
Ast.Ctrait
987 then Errors.dangerous_method_name
p
989 | None
-> assert false)
991 and check_maybe_rx_attributes_on_params
env parent_attrs params
=
992 let parent_only_rx_if_args =
993 Attributes.find
SN.UserAttributes.uaAtMostRxAsArgs parent_attrs
in
994 let check_param seen_atmost_rx_as_rxfunc
p =
995 let only_rx_if_rxfunc_attr =
996 Attributes.find
SN.UserAttributes.uaAtMostRxAsFunc
p.param_user_attributes
in
997 let only_rx_if_impl_attr =
998 Attributes.find
SN.UserAttributes.uaOnlyRxIfImpl
p.param_user_attributes
in
999 match only_rx_if_rxfunc_attr, only_rx_if_impl_attr with
1000 | Some
{ ua_name
= (p, _); _ }, _ ->
1001 if parent_only_rx_if_args = None
|| not
env.is_reactive
1002 then Errors.atmost_rx_as_rxfunc_invalid_location
p;
1004 | _, Some
{ ua_name
= (p, _); ua_params
; _ } ->
1005 if parent_only_rx_if_args = None
|| not
env.is_reactive
1006 then Errors.atmost_rx_as_rxfunc_invalid_location
p
1007 else check_conditionally_reactive_annotation_params ~is_method
:false p ua_params
;
1009 | _ -> seen_atmost_rx_as_rxfunc
in
1010 let has_param_with_atmost_rx_as_rxfunc =
1011 List.fold_left params ~init
:false ~f
:check_param in
1012 match parent_only_rx_if_args, has_param_with_atmost_rx_as_rxfunc with
1013 | Some
{ ua_name
= (p, _); _ }, false ->
1014 Errors.no_atmost_rx_as_rxfunc_for_rx_if_args
p
1017 and param_is_mutable
p =
1018 Attributes.mem
SN.UserAttributes.uaMutable
p.param_user_attributes
1020 and param_is_maybe_mutable
p =
1021 Attributes.mem
SN.UserAttributes.uaMaybeMutable
p.param_user_attributes
1023 and fun_param
env (_pos
, name) f_type
byref param
=
1024 maybe hint
env param
.param_hint
;
1025 maybe expr
env param
.param_expr
;
1026 let is_mutable = param_is_mutable param
in
1027 let is_maybe_mutable = param_is_maybe_mutable param
in
1028 if not
env.is_reactive
then begin
1030 then Errors.mutable_methods_must_be_reactive param
.param_pos
name;
1032 then Errors.maybe_mutable_methods_must_be_reactive param
.param_pos
name;
1035 match param
.param_callconv
with
1037 | Some
Ast.Pinout
->
1038 let pos = param
.param_pos
in
1039 if f_type
<> Ast.FSync
then Errors.inout_params_outside_of_sync
pos;
1040 if SSet.mem
name SN.Members.as_set
then Errors.inout_params_special
pos;
1041 Option.iter
byref ~f
:(fun param
->
1042 Errors.inout_params_mix_byref
pos param
.param_pos
);
1045 and stmt env = function
1046 | Return
(p, _) when env.t_is_finally
->
1047 Errors.return_in_finally
p; ()
1055 match env.imm_ctrl_ctx
with
1056 | Toplevel
-> Errors.toplevel_break
p
1059 | Continue
p -> begin
1060 match env.imm_ctrl_ctx
with
1061 | Toplevel
-> Errors.toplevel_continue
p
1062 | SwitchContext
-> Errors.continue_in_switch
p
1065 | Return
(_, Some e
)
1066 | Expr e
| Throw
(_, e
) ->
1072 | Awaitall
(_, el
) ->
1073 List.iter el
(fun (x
, y
) ->
1075 | Some x
-> expr
env x
1086 block
{ env with imm_ctrl_ctx
= LoopContext
} b
;
1091 block
{ env with imm_ctrl_ctx
= LoopContext
} b
;
1093 | Using
{ us_expr
= e
; us_block
= b
; _ } ->
1097 | For
(e1
, e2
, e3
, b
) ->
1101 block
{ env with imm_ctrl_ctx
= LoopContext
} b
;
1105 List.iter cl
(case
{ env with imm_ctrl_ctx
= SwitchContext
});
1107 | Foreach
(e1
, ae
, b
) ->
1110 block
{ env with imm_ctrl_ctx
= LoopContext
} b
;
1112 | Try
(b
, cl
, fb
) ->
1114 List.iter cl
(catch
env);
1115 block
{ env with t_is_finally
= true } fb
;
1117 | Def_inline
_ -> ()
1118 | Let
((p, x
), _, e
) ->
1119 (* We treat let statement as assignment expresssions *)
1120 let fake_expr = (p, Binop
(Ast.Eq None
, (p, Lvar
(p, x
)), e
)) in
1123 | Block b
-> block
env b
;
1124 | Markup
(_, eopt
) -> (match eopt
with Some e
-> expr
env e
| None
-> ())
1125 | Declare
(_, e
, b
) ->
1129 and as_expr
env = function
1131 | Await_as_v
(_, e
) -> expr
env e
1133 | Await_as_kv
(_, e1
, e2
) ->
1138 and afield
env = function
1139 | AFvalue e
-> expr
env e
1140 | AFkvalue
(e1
, e2
) -> expr
env e1
; expr
env e2
;
1143 List.iter stl
(stmt env)
1145 and expr
env (p, e
) =
1148 and expr_
env p = function
1155 | ParenthesizedExpr
_ -> failwith
"AST should not contain these nodes after naming"
1167 | Unsafe_expr
_ -> ()
1168 | Class_const
(cid
, ((_, m_name
) as mid
)) ->
1169 let func_name = env.function_name
in
1171 (not
(is_parent cid
) || func_name <> Some m_name
)
1172 then Errors.magic mid
;
1174 | Pipe
(_, e1
, e2
) ->
1179 (* Check that __CLASS__ and __TRAIT__ are used appropriately *)
1180 | Id
(pos, const
) ->
1181 if SN.PseudoConsts.is_pseudo_const const
then
1182 if const
= SN.PseudoConsts.g__CLASS__
then
1183 (match env.class_kind
with
1185 | _ -> Errors.illegal_CLASS
pos)
1186 else if const
= SN.PseudoConsts.g__TRAIT__
then
1187 (match env.class_kind
with
1188 | Some
Ast.Ctrait
-> ()
1189 | _ -> Errors.illegal_TRAIT
pos);
1192 List.iter afl
(afield
env);
1195 List.iter fdl
(field
env);
1198 List.iter el
(expr
env);
1200 | ValCollection
(_, el
) ->
1201 List.iter el
(expr
env);
1203 | KeyValCollection
(_, fdl
) ->
1204 List.iter fdl
(field
env);
1206 | Clone e
-> expr
env e
; ()
1207 | Obj_get
(e
, (_, Id s
), _) ->
1209 then Errors.magic s
;
1210 let env'
= {env with is_array_append_allowed
= false} in
1213 | Obj_get
(e1
, e2
, _) ->
1214 let env'
= {env with is_array_append_allowed
= false} in
1218 | Array_get
((p, _), None
) when not
env.is_array_append_allowed
->
1219 Errors.reading_from_append
p;
1221 | Array_get
(e
, eopt
) ->
1222 let env'
= {env with is_array_append_allowed
= false} in
1224 maybe expr
env' eopt
;
1226 | Call
(_, e
, _, el
, uel
) ->
1228 List.iter el
(expr
env);
1229 List.iter uel
(expr
env);
1231 | True
| False
| Int
_
1232 | Float
_ | Null
| String
_ | PrefixedString
_ -> ()
1234 List.iter el
(expr
env);
1236 | Unop
(Ast.Uref
, e
) ->
1238 begin match snd e
with
1240 Errors.illegal_by_ref_expr
p SN.SpecialIdents.this
1241 | Dollardollar
(_, id
)
1242 when Local_id.to_string id
= SN.SpecialIdents.dollardollar
->
1243 Errors.illegal_by_ref_expr
p SN.SpecialIdents.dollardollar
1246 | Unop
(_, e
) -> expr
env e
;
1248 | Special_func func
->
1251 | Gen_array_rec e
->
1254 List.iter el
(expr
env));
1269 List.iter el
(expr
env);
1276 List.iter el
(expr
env);
1282 | Binop
(op
, e1
, e2
) ->
1283 let lvalue_env = match op
with
1284 | Ast.Eq
_ -> { env with is_array_append_allowed
= true }
1289 | Eif
(e1
, None
, e3
) ->
1293 | Eif
(e1
, Some e2
, e3
) ->
1298 | Assert
(AE_assert e
) ->
1301 | InstanceOf
(e
, e2
) ->
1303 | _, CIexpr
(_, Class_const
((_, CI
(_, classname
)), (p, "class"))) ->
1304 Errors.classname_const_instanceof
(Utils.strip_ns classname
) p;
1313 | New
(_, _, el
, uel
, _) ->
1314 List.iter el
(expr
env);
1315 List.iter uel
(expr
env);
1318 let env = { env with imm_ctrl_ctx
= Toplevel
} in
1319 let body = Nast.assert_named_body f
.f_body
in
1321 | Xml
(_, attrl
, el
) ->
1322 List.iter attrl
(fun attr
-> expr
env (get_xhp_attr_expr attr
));
1323 List.iter el
(expr
env);
1325 | Callconv
(_, e
) ->
1327 let rec aux = function
1329 | _, Array_get
(e1
, Some
_) -> aux e1
1332 if not
(aux e
) then Errors.inout_argument_bad_expr
(fst e
);
1334 | Execution_operator
_ -> ()
1336 List.iter ~f
:(fun (_, v
) -> expr
env v
) fdm
1338 and case
env = function
1339 | Default b
-> block
env b
1345 and catch
env (_, _, b
) = block
env b
1346 and field
env (e1
, e2
) =
1351 let typedef tenv t
=
1352 let env = { t_is_finally
= false;
1353 is_array_append_allowed
= false;
1354 class_name
= None
; class_kind
= None
;
1355 imm_ctrl_ctx
= Toplevel
;
1356 function_name
= None
;
1357 (* Since typedefs cannot have constraints we shouldn't check
1358 * if its type params satisfy the constraints of any tapply it
1361 typedef_tparams
= t
.t_tparams
;
1365 maybe hint
env t
.t_constraint
;