Ban calling magic methods
[hiphop-php.git] / hphp / hack / src / typing / nastCheck.ml
blob0adac2ea8f0a6ee69b7ff7a186c1d4aa704c072b
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
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.
24 open Core_kernel
25 open Autocomplete
26 open Nast
27 open String_utils
28 open Typing_defs
29 open Utils
31 module Env = Typing_env
32 module Inst = Decl_instantiate
33 module Phase = Typing_phase
34 module SN = Naming_special_names
35 module TGenConstraint = Typing_generic_constraint
36 module Subst = Decl_subst
37 module TUtils = Typing_utils
40 type control_context =
41 | Toplevel
42 | LoopContext
43 | SwitchContext
45 type env = {
46 t_is_finally: bool;
47 function_name: string option;
48 class_name: string option;
49 class_kind: Ast.class_kind option;
50 imm_ctrl_ctx: control_context;
51 typedef_tparams : Nast.tparam list;
52 is_array_append_allowed: bool;
53 is_reactive: bool; (* The enclosing function is reactive *)
54 tenv: Env.env;
57 let coroutines_enabled env =
58 TypecheckerOptions.experimental_feature_enabled
59 (Env.get_options env.tenv)
60 TypecheckerOptions.experimental_coroutines
62 let report_coroutines_not_enabled p =
63 Errors.experimental_feature p "coroutines"
65 let check_coroutines_enabled condition env p =
66 if condition && not (coroutines_enabled env)
67 then report_coroutines_not_enabled p
69 let check_coroutine_constructor name is_coroutine p =
70 if name = SN.Members.__construct && is_coroutine
71 then Errors.coroutine_in_constructor p
73 let error_on_attr env attrs attr f =
74 if not (TypecheckerOptions.unsafe_rx (Env.get_options env.tenv))
75 then match Attributes.find attr attrs with
76 | Some { ua_name = (p, _); _ } -> f p;
77 | _ -> ()
79 let error_if_has_atmost_rx_as_rxfunc_attribute env attrs =
80 error_on_attr env attrs
81 SN.UserAttributes.uaOnlyRxIfRxFunc_do_not_use Errors.atmost_rx_as_rxfunc_invalid_location;
82 error_on_attr env attrs
83 SN.UserAttributes.uaAtMostRxAsFunc Errors.atmost_rx_as_rxfunc_invalid_location;
85 module CheckFunctionBody = struct
86 let rec stmt f_type env st = match f_type, st with
87 | Ast.FSync, Return (_, None)
88 | Ast.FAsync, Return (_, None) -> ()
89 | Ast.FSync, Return (_, Some e)
90 | Ast.FAsync, Return (_, Some e) ->
91 expr_allow_await f_type env e;
93 | (Ast.FGenerator | Ast.FAsyncGenerator), Return (p, e) ->
94 (match e with
95 None -> ()
96 | Some _ -> Errors.return_in_gen p);
98 | Ast.FCoroutine, Return (_, e) ->
99 Option.iter e ~f:(expr f_type env)
100 | _, Throw (_, e) ->
101 expr f_type env e
102 | _, Expr e ->
103 expr_allow_await_or_rx_move f_type env e;
105 | _, ( Noop | Fallthrough | GotoLabel _ | Goto _ | Break _ | Continue _
106 | Static_var _ | Global_var _ | Unsafe_block _ ) -> ()
107 | _, If (cond, b1, b2) ->
108 expr f_type env cond;
109 block f_type env b1;
110 block f_type env b2;
112 | _, Do (b, e) ->
113 block f_type env b;
114 expr f_type env e;
116 | _, While (e, b) ->
117 expr f_type env e;
118 block f_type env b;
120 | _, Using (has_await, e, b) ->
121 if has_await then found_await f_type (fst e);
122 expr_allow_await_list f_type env e;
123 block f_type env b;
125 | _, For (init, cond, incr, b) ->
126 expr f_type env init;
127 expr f_type env cond;
128 expr f_type env incr;
129 block f_type env b;
131 | _, Switch (e, cl) ->
132 expr f_type env e;
133 List.iter cl (case f_type env);
135 | _, Foreach (_, (Await_as_v (p, _) | Await_as_kv (p, _, _)), _) ->
136 found_await f_type p
138 | _, Foreach (v, _, b) ->
139 expr f_type env v;
140 block f_type env b;
142 | _, Try (b, cl, fb) ->
143 block f_type env b;
144 List.iter cl (catch f_type env);
145 block f_type { env with t_is_finally = true } fb;
147 | _, Let ((p, x), _, e) ->
148 (* We treat let statement as an assignment expression *)
149 let fake_expr = (p, Binop (Ast.Eq None, (p, Lvar (p, x)), e)) in
150 expr_allow_await f_type env fake_expr;
153 and found_await ftype p =
154 match ftype with
155 | Ast.FCoroutine -> Errors.await_in_coroutine p
156 | Ast.FSync | Ast.FGenerator -> Errors.await_in_sync_function p
157 | _ -> ()
159 and block f_type env stl =
160 List.iter stl (stmt f_type env)
162 and start f_type env stl =
163 match stl with
164 | [If ((_, Id (_, c)), then_stmt, else_stmt ) ]
166 (* this is the only case when HH\Rx\IS_ENABLED can appear in
167 function body, other occurences are considered errors *)
169 if (HH\Rx\IS_ENABLED) {}
170 else {}
173 when c = SN.Rx.is_enabled ->
174 block f_type env then_stmt;
175 block f_type env else_stmt;
176 | _ ->
177 block f_type env stl
179 and case f_type env = function
180 | Default b -> block f_type env b
181 | Case (cond, b) ->
182 expr f_type env cond;
183 block f_type env b;
186 and catch f_type env (_, _, b) = block f_type env b
188 and afield f_type env = function
189 | AFvalue e -> expr f_type env e
190 | AFkvalue (e1, e2) -> expr2 f_type env (e1, e2)
192 and expr f_type env (p, e) =
193 expr_ p f_type env e
195 and expr_allow_await_list f_type env ((_, exp) as e) =
196 match exp with
197 | Expr_list el ->
198 List.iter el (expr_allow_await f_type env)
199 | _ ->
200 expr_allow_await f_type env e
202 and expr_allow_await ?(is_rhs=false) f_type env (p, exp) = match f_type, exp with
203 | Ast.FAsync, Await e
204 | Ast.FAsyncGenerator, Await e -> expr f_type env e; ()
205 | Ast.FAsync, Binop (Ast.Eq None, e1, (_, Await e))
206 | Ast.FAsyncGenerator, Binop (Ast.Eq None, e1, (_, Await e)) when not is_rhs ->
207 expr f_type env e1;
208 expr f_type env e;
210 | _ -> expr_ p f_type env exp; ()
212 and expr_allow_rx_move orelse f_type env exp =
213 match exp with
214 | _, Call (_, e, _, el, uel) when is_rx_move e ->
215 expr f_type env e;
216 List.iter el ~f:(expr f_type env);
217 List.iter uel ~f:(expr f_type env);
219 | _ ->
220 orelse f_type env exp
222 and expr_allow_await_or_rx_move f_type env exp =
223 match exp with
224 | _, Binop (Ast.Eq None, e1, rhs) ->
225 expr f_type env e1;
226 expr_allow_rx_move (expr_allow_await ~is_rhs:true) f_type env rhs
227 | _ ->
228 expr_allow_await f_type env exp
230 and is_rx_move e =
231 match e with
232 | _, Id (_, v) -> v = SN.Rx.move
233 | _ -> false
235 and expr2 f_type env (e1, e2) =
236 expr f_type env e1;
237 expr f_type env e2;
240 and expr_ p f_type env exp = match f_type, exp with
241 | _, Any -> ()
242 | _, Class_const ((_pos, CIparent), ((_, construct)))
243 when construct = SN.Members.__construct ->
244 let () = match Env.get_class env.tenv (Env.get_parent_id env.tenv) with
245 | Some parent_class ->
246 begin match fst parent_class.tc_construct with
247 | None when parent_class.tc_kind = Ast.Cabstract ->
248 Errors.parent_abstract_call construct p parent_class.tc_pos;
249 | _ -> ()
251 | _ -> () in
253 | _, Class_const _
254 | _, Fun_id _
255 | _, Method_id _
256 | _, Smethod_id _
257 | _, Method_caller _
258 | _, This
259 | _, Class_get _
260 | _, Typename _
261 | _, Lplaceholder _
262 | _, Dollardollar _ -> ()
263 | _, Id (pos, const) when const = SN.Rx.is_enabled ->
264 Errors.rx_is_enabled_invalid_location pos
265 | _, Id _ -> ()
267 | _, Dollar e ->
268 expr f_type env e
269 | _, ImmutableVar _
270 | _, Lvar _ ->
272 | _, Pipe (_, l, r) ->
273 expr f_type env l;
274 expr f_type env r;
275 | _, Array afl ->
276 List.iter afl (afield f_type env);
278 | _, Darray afl ->
279 List.iter afl (expr2 f_type env);
281 | _, Varray afl ->
282 List.iter afl (expr f_type env);
284 | _, ValCollection (_, el) ->
285 List.iter el (expr f_type env);
287 | _, KeyValCollection (_, fdl) ->
288 List.iter fdl (expr2 f_type env);
290 | _, Clone e -> expr f_type env e; ()
291 | _, Obj_get (e, (_, Id _s), _) ->
292 expr f_type env e;
294 | _, Obj_get (e1, e2, _) ->
295 expr2 f_type env (e1, e2);
297 | _, Array_get (e, eopt) ->
298 expr f_type env e;
299 maybe (expr f_type) env eopt;
301 | _, Call (_, e, _, _, _) when is_rx_move e ->
302 Errors.rx_move_invalid_location (fst e);
304 | _, Call (_, e, _, el, uel) ->
305 expr f_type env e;
306 List.iter el (expr_allow_rx_move expr f_type env);
307 List.iter uel (expr_allow_rx_move expr f_type env);
309 | _, True | _, False | _, Int _
310 | _, Float _ | _, Null | _, String _ -> ()
311 | _, PrefixedString (_, e) ->
312 expr f_type env e;
314 | _, String2 el ->
315 List.iter el (expr f_type env);
317 | _, List el ->
318 List.iter el (expr f_type env);
320 | _, Pair (e1, e2) ->
321 expr2 f_type env (e1, e2);
323 | _, Expr_list el ->
324 List.iter el (expr f_type env);
326 | _, Unop (_, e) -> expr f_type env e
327 | _, Binop (_, e1, e2) ->
328 expr2 f_type env (e1, e2);
330 | _, Eif (e1, None, e3) ->
331 expr2 f_type env (e1, e3);
333 | _, Eif (e1, Some e2, e3) ->
334 List.iter [e1; e2; e3] (expr f_type env);
336 | _, New (_, el, uel) ->
337 List.iter el (expr f_type env);
338 List.iter uel (expr f_type env);
340 | _, InstanceOf (e, _)
341 | _, Is (e, _)
342 | _, As (e, _, _)
343 | _, Cast (_, e) ->
344 expr f_type env e;
346 | _, Efun _ -> ()
348 | Ast.FGenerator, Yield_break
349 | Ast.FAsyncGenerator, Yield_break -> ()
350 | Ast.FGenerator, Yield af
351 | Ast.FAsyncGenerator, Yield af -> afield f_type env af; ()
352 | Ast.FGenerator, Yield_from e
353 | Ast.FAsyncGenerator, Yield_from e -> expr f_type env e; ()
354 (* Should never happen -- presence of yield should make us FGenerator or
355 * FAsyncGenerator. *)
356 | (Ast.FSync | Ast.FAsync), (Yield _ | Yield_from _ | Yield_break) -> assert false
358 | (Ast.FGenerator | Ast.FSync | Ast.FCoroutine), Await _ ->
359 found_await f_type p
361 | Ast.FAsync, Await _
362 | Ast.FAsyncGenerator, Await _ -> Errors.await_not_allowed p
364 | Ast.FCoroutine, (Yield _ | Yield_break | Yield_from _) ->
365 Errors.yield_in_coroutine p
366 | (Ast.FSync | Ast.FAsync | Ast.FGenerator | Ast.FAsyncGenerator), Suspend _ ->
367 if not (coroutines_enabled env)
368 then report_coroutines_not_enabled p
369 else Errors.suspend_outside_of_coroutine p
370 | Ast.FCoroutine, Suspend _ ->
371 if not (coroutines_enabled env)
372 then report_coroutines_not_enabled p
373 else if env.t_is_finally
374 then Errors.suspend_in_finally p;
375 | _, Special_func func ->
376 (match func with
377 | Gena e
378 | Gen_array_rec e -> expr f_type env e
379 | Genva el -> List.iter el (expr f_type env));
381 | _, Xml (_, attrl, el) ->
382 List.iter attrl (fun attr -> expr f_type env (get_xhp_attr_expr attr));
383 List.iter el (expr f_type env);
385 | _, Unsafe_expr _ -> ()
386 | _, Callconv (_, e) ->
387 expr f_type env e;
389 | _, Execution_operator _ -> ()
390 | _, Assert (AE_assert e) ->
391 expr f_type env e;
393 | _, Shape fdm ->
394 ShapeMap.iter (fun _ v -> expr f_type env v) fdm;
399 let is_magic =
400 let h = Caml.Hashtbl.create 23 in
401 let a x = Caml.Hashtbl.add h x true in
402 let _ = SSet.iter (fun m -> if m <> SN.Members.__toString then a m) SN.Members.as_set in
403 fun (_, s) ->
404 Caml.Hashtbl.mem h s
406 let is_parent e =
407 snd e = CIparent
409 let check_conditionally_reactive_annotation_params p params ~is_method =
410 match params with
411 | [_, Class_const (_, (_, prop))] when prop = "class" -> ()
412 | _ -> Errors.conditionally_reactive_annotation_invalid_arguments ~is_method p
414 let check_conditionally_reactive_annotations is_reactive p method_name user_attributes =
415 let rec check l seen =
416 match l with
417 | [] -> ()
418 | { ua_name = (_, name); ua_params } :: xs when name = SN.UserAttributes.uaOnlyRxIfImpl ->
419 begin
420 if seen
421 then Errors.multiple_conditionally_reactive_annotations p method_name
422 else if is_reactive
423 then check_conditionally_reactive_annotation_params ~is_method:true p ua_params;
424 check xs true
426 | _ :: xs -> check xs seen in
427 check user_attributes false
429 let is_some_reactivity_attribute { ua_name = (_, name); _ } =
430 name = SN.UserAttributes.uaReactive ||
431 name = SN.UserAttributes.uaLocalReactive ||
432 name = SN.UserAttributes.uaShallowReactive
434 (* During NastCheck, all reactivity kinds are the same *)
435 let fun_is_reactive user_attributes =
436 List.exists user_attributes ~f:is_some_reactivity_attribute
438 let rec fun_ tenv f named_body =
439 if !auto_complete then ()
440 else begin
441 let env = { t_is_finally = false;
442 is_array_append_allowed = false;
443 class_name = None; class_kind = None;
444 imm_ctrl_ctx = Toplevel;
445 typedef_tparams = [];
446 tenv = tenv;
447 function_name = None;
448 is_reactive = fun_is_reactive f.f_user_attributes
449 } in
450 func env f named_body
453 and func env f named_body =
454 let p, fname = f.f_name in
455 let fname_lower = String.lowercase (strip_ns fname) in
456 if fname_lower = SN.Members.__construct || fname_lower = "using"
457 then Errors.illegal_function_name p fname;
458 check_coroutines_enabled (f.f_fun_kind = Ast.FCoroutine) env p;
459 (* Add type parameters to typing environment and localize the bounds *)
460 let tenv, constraints =
461 Phase.localize_generic_parameters_with_bounds env.tenv f.f_tparams
462 ~ety_env:(Phase.env_with_self env.tenv) in
463 let tenv = add_constraints p tenv constraints in
464 Typing_instantiability.check_tparams_instantiable tenv f.f_tparams;
465 Typing_instantiability.check_params_instantiable tenv f.f_params;
466 let env = { env with
467 tenv = Env.set_mode tenv f.f_mode;
468 t_is_finally = false;
469 is_reactive = fun_is_reactive f.f_user_attributes;
470 } in
471 maybe hint env f.f_ret;
472 (* Functions can't be mutable, only methods can *)
473 if Attributes.mem SN.UserAttributes.uaMutable f.f_user_attributes then
474 Errors.mutable_attribute_on_function p;
475 if Attributes.mem SN.UserAttributes.uaMaybeMutable f.f_user_attributes then
476 Errors.maybe_mutable_attribute_on_function p;
477 if Attributes.mem SN.UserAttributes.uaMutableReturn f.f_user_attributes
478 && not env.is_reactive then
479 Errors.mutable_return_annotated_decls_must_be_reactive "function" p fname;
481 error_if_has_atmost_rx_as_rxfunc_attribute env f.f_user_attributes;
482 check_maybe_rx_attributes_on_params env f.f_user_attributes f.f_params;
484 if f.f_ret_by_ref
485 && env.is_reactive
486 && not (TypecheckerOptions.unsafe_rx (Env.get_options env.tenv))
487 then Errors.reference_in_rx p;
489 List.iter f.f_tparams (tparam env);
490 let byref = List.find f.f_params ~f:(fun x -> x.param_is_reference) in
491 List.iter f.f_params (fun_param env f.f_name f.f_fun_kind byref);
492 if f.f_ret_by_ref &&
493 TypecheckerOptions.disallow_return_by_ref (Env.get_options env.tenv)
494 then Errors.illegal_return_by_ref p;
495 let inout = List.find f.f_params ~f:(
496 fun x -> x.param_callconv = Some Ast.Pinout) in
497 (match inout with
498 | Some param ->
499 if Attributes.mem SN.UserAttributes.uaMemoize f.f_user_attributes ||
500 Attributes.mem SN.UserAttributes.uaMemoizeLSB f.f_user_attributes
501 then Errors.inout_params_memoize p param.param_pos;
502 if f.f_ret_by_ref then Errors.inout_params_ret_by_ref p param.param_pos;
504 | _ -> ()
506 (match f.f_variadic with
507 | FVvariadicArg vparam ->
508 Typing_instantiability.check_param_instantiable tenv vparam;
509 if vparam.param_is_reference then
510 Errors.variadic_byref_param vparam.param_pos
511 | _ -> ()
513 block env named_body.fnb_nast;
514 CheckFunctionBody.start
515 f.f_fun_kind
517 named_body.fnb_nast
519 and tparam env (_, _, cstrl, _) =
520 List.iter cstrl (fun (_, h) -> hint env h)
522 and hint env (p, h) =
523 hint_ env p h
525 and hint_ env p = function
526 | Hany | Hmixed | Hnonnull | Hprim _ | Hthis | Haccess _ | Habstr _ | Hdynamic ->
528 | Harray (ty1, ty2) ->
529 maybe hint env ty1;
530 maybe hint env ty2
531 | Hdarray (ty1, ty2) ->
532 hint env ty1;
533 hint env ty2
534 | Hvarray_or_darray ty
535 | Hvarray ty ->
536 hint env ty
537 | Htuple hl -> List.iter hl (hint env)
538 | Hoption h ->
539 hint env h; ()
540 | Hfun (_, is_coroutine, hl, _, variadic_hint, h) ->
541 check_coroutines_enabled is_coroutine env p;
542 List.iter hl (hint env);
543 hint env h;
544 begin match variadic_hint with
545 | Hvariadic (Some h) -> hint env h;
546 | Hvariadic (None) when Env.is_strict env.tenv ->
547 Errors.ellipsis_strict_mode ~require:`Type p;
548 | _ -> ()
550 | Happly ((_, x), hl) as h when Env.is_typedef x ->
551 begin match Typing_lazy_heap.get_typedef (Env.get_options env.tenv) x with
552 | Some {td_tparams; _} ->
553 check_happly env.typedef_tparams env.tenv (p, h);
554 check_tparams env p x td_tparams hl
555 | None -> ()
557 | Happly ((_, x), hl) as h ->
558 (match Env.get_class env.tenv x with
559 | None -> ()
560 | Some class_ ->
561 check_happly env.typedef_tparams env.tenv (p, h);
562 check_tparams env p x class_.tc_tparams hl
565 | Hshape { nsi_allows_unknown_fields=_; nsi_field_map } ->
566 let compute_hint_for_shape_field_info _ { sfi_hint; _; } =
567 Typing_instantiability.check_instantiable env.tenv sfi_hint;
568 hint env sfi_hint in
570 ShapeMap.iter compute_hint_for_shape_field_info nsi_field_map
572 and check_tparams env p x tparams hl =
573 let arity = List.length tparams in
574 check_arity env p x arity (List.length hl);
575 List.iter hl (hint env);
577 and check_arity env p tname arity size =
578 if size = arity then () else
579 if size = 0 && not (Typing_env.is_strict env.tenv)
580 && not (TypecheckerOptions.experimental_feature_enabled (Env.get_options env.tenv)
581 TypecheckerOptions.experimental_generics_arity)
582 then ()
583 else
584 let nargs = soi arity in
585 Errors.type_arity p tname nargs
587 and check_happly unchecked_tparams env h =
588 let env = { env with Env.pos = (fst h) } in
589 let decl_ty = Decl_hint.hint env.Env.decl_env h in
590 let unchecked_tparams =
591 List.map unchecked_tparams begin fun (v, sid, cstrl, reified) ->
592 let cstrl = List.map cstrl (fun (ck, cstr) ->
593 let cstr = Decl_hint.hint env.Env.decl_env cstr in
594 (ck, cstr)) in
595 (v, sid, cstrl, reified)
596 end in
597 let tyl =
598 List.map
599 unchecked_tparams
600 (fun (_, (p, _), _, _) -> Reason.Rwitness p, Tany) in
601 let subst = Inst.make_subst unchecked_tparams tyl in
602 let decl_ty = Inst.instantiate subst decl_ty in
603 match decl_ty with
604 | _, Tapply (_, tyl) when tyl <> [] ->
605 let env, locl_ty = Phase.localize_with_self env decl_ty in
606 begin match TUtils.get_base_type env locl_ty with
607 | _, Tclass (cls, tyl) ->
608 (match Env.get_class env (snd cls) with
609 | Some { tc_tparams; _ } ->
610 (* We want to instantiate the class type parameters with the
611 * type list of the class we are localizing. We do not want to
612 * add any more constraints when we localize the constraints
613 * stored in the class_type since it may lead to infinite
614 * recursion
616 let ety_env =
617 { (Phase.env_with_self env) with
618 substs = Subst.make tc_tparams tyl;
619 } in
620 iter2_shortest begin fun (_, (p, x), cstrl, _) ty ->
621 List.iter cstrl (fun (ck, cstr_ty) ->
622 let r = Reason.Rwitness p in
623 let env, cstr_ty = Phase.localize ~ety_env env cstr_ty in
624 ignore @@ Errors.try_
625 (fun () ->
626 TGenConstraint.check_constraint env ck cstr_ty ty
628 (fun l ->
629 Reason.explain_generic_constraint env.Env.pos r x l;
632 end tc_tparams tyl
633 | _ -> ()
635 | _ -> ()
637 | _ -> ()
639 and class_ tenv c =
640 if !auto_complete then () else begin
641 let cname = Some (snd c.c_name) in
642 let env = { t_is_finally = false;
643 is_array_append_allowed = false;
644 class_name = cname;
645 class_kind = Some c.c_kind;
646 imm_ctrl_ctx = Toplevel;
647 typedef_tparams = [];
648 is_reactive = false;
649 function_name = None;
650 tenv = tenv } in
651 (* Add type parameters to typing environment and localize the bounds *)
652 let tenv, constraints = Phase.localize_generic_parameters_with_bounds
653 tenv (fst c.c_tparams)
654 ~ety_env:(Phase.env_with_self tenv) in
655 let tenv = add_constraints (fst c.c_name) tenv constraints in
656 Typing_instantiability.check_tparams_instantiable tenv (fst c.c_tparams);
657 let env = { env with tenv = Env.set_mode tenv c.c_mode } in
659 error_if_has_atmost_rx_as_rxfunc_attribute env c.c_user_attributes;
661 (* Const handling:
662 * prevent for abstract final classes, traits, and interfaces
664 if Attributes.mem SN.UserAttributes.uaConst c.c_user_attributes
665 then begin match c.c_kind, c.c_final with
666 | Ast.Cabstract, true
667 | Ast.Cinterface, _
668 | Ast.Ctrait, _
669 | Ast.Cenum, _ ->
670 Errors.const_attribute_prohibited
671 (fst c.c_name) (Typing_print.class_kind c.c_kind c.c_final);
672 | Ast.Cabstract, false
673 | Ast.Cnormal, _ -> ();
674 end;
675 if c.c_kind = Ast.Cabstract && c.c_final then begin
676 List.iter c.c_methods (fun m -> Errors.nonstatic_method_in_abstract_final_class (fst m.m_name));
677 Option.iter c.c_constructor
678 (fun m -> Errors.nonstatic_method_in_abstract_final_class (fst m.m_name))
679 end;
681 if c.c_kind = Ast.Cinterface then begin
682 interface c;
684 else begin
685 maybe method_ (env, true) c.c_constructor;
686 end;
687 List.iter (fst c.c_tparams) (tparam env);
688 List.iter c.c_extends (hint env);
689 List.iter c.c_implements (hint env);
690 List.iter c.c_consts (class_const env);
691 List.iter c.c_typeconsts (typeconst (env, (fst c.c_tparams)));
692 List.iter c.c_static_vars (class_var env);
693 List.iter c.c_vars (class_var env);
694 List.iter c.c_static_methods (method_ (env, true));
695 List.iter c.c_methods (method_ (env, false));
696 List.iter c.c_implements (check_is_interface (env, "implement"));
697 List.iter c.c_req_implements
698 (check_is_interface (env, "require implementation of"));
699 List.iter c.c_req_extends (check_is_class env);
700 List.iter c.c_uses (check_is_trait env);
701 let disallow_static_memoized = TypecheckerOptions.experimental_feature_enabled
702 (Env.get_options env.tenv)
703 TypecheckerOptions.experimental_disallow_static_memoized in
704 if disallow_static_memoized && not c.c_final then
705 begin
706 List.iter c.c_static_methods (check_static_memoized_function);
707 maybe (fun _ m -> check_static_memoized_function m) () c.c_constructor
709 end;
712 (** Make sure that the given hint points to an interface *)
713 and check_is_interface (env, error_verb) (x : hint) =
714 match (snd x) with
715 | Happly (id, _) ->
716 (match Env.get_class env.tenv (snd id) with
717 | None ->
718 (* in partial mode, we can fail to find the class if it's
719 defined in PHP. *)
720 (* in strict mode, we catch the unknown class error before
721 even reaching here. *)
723 | Some { tc_kind = Ast.Cinterface; _ } -> ()
724 | Some { tc_name; _ } ->
725 Errors.non_interface (fst x) tc_name error_verb
727 | Habstr _ ->
728 Errors.non_interface (fst x) "generic" error_verb
729 | _ ->
730 Errors.non_interface (fst x) "invalid type hint" error_verb
732 (** Make sure that the given hint points to a non-final class *)
733 and check_is_class env (x : hint) =
734 match (snd x) with
735 | Happly (id, _) ->
736 (match Env.get_class env.tenv (snd id) with
737 | None ->
738 (* in partial mode, we can fail to find the class if it's
739 defined in PHP. *)
740 (* in strict mode, we catch the unknown class error before
741 even reaching here. *)
743 | Some { tc_kind = Ast.(Cabstract | Cnormal); tc_final; tc_name; _ } ->
744 if tc_final then Errors.requires_final_class (fst x) tc_name
745 | Some { tc_kind; tc_name; _ } ->
746 Errors.requires_non_class (fst x) tc_name (Ast.string_of_class_kind tc_kind)
748 | Habstr name ->
749 Errors.requires_non_class (fst x) name "a generic"
750 | _ ->
751 Errors.requires_non_class (fst x) "This" "an invalid type hint"
754 * Make sure that all "use"s are with traits, and not
755 * classes, interfaces, etc.
757 and check_is_trait env (h : hint) =
758 (* Second part of a hint contains Happly information *)
759 (match (snd h) with
760 (* An Happly contains identifying info (sid) and hint list (which we *)
761 (* do not care about at this time *)
762 | Happly (pos_and_name, _) ->
763 (* Env.get_class will get the type info associated with the name *)
764 let type_info = Env.get_class env.tenv (snd pos_and_name) in
765 (match type_info with
766 (* in partial mode, it's possible to not find the trait, because the *)
767 (* trait may live in PHP land. In strict mode, we catch the unknown *)
768 (* trait error before even reaching here. so it's ok to just return *)
769 (* unit. *)
770 | None -> ()
771 (* tc_kind is part of the type_info. If we are a trait, all is good *)
772 | Some { tc_kind = Ast.Ctrait; _ } -> ()
773 (* Anything other than a trait we are going to throw an error *)
774 (* using the tc_kind and tc_name fields of our type_info *)
775 | Some { tc_kind; tc_name; _ } ->
776 Errors.uses_non_trait (fst h) tc_name (Ast.string_of_class_kind tc_kind)
778 | _ -> failwith "assertion failure: trait isn't an Happly"
781 (* Class properties can only be initialized with a static literal expression. *)
782 and check_class_property_initialization prop =
783 (* Only do the check if property is initialized. *)
784 Option.iter prop.cv_expr ~f:begin fun e ->
785 let rec rec_assert_static_literal e =
786 match (snd e) with
787 | Any | Typename _
788 | Id _ | Class_const _ | True | False | Int _ | Float _
789 | Null | String _ | PrefixedString _ | Unsafe_expr _
790 | Execution_operator _ ->
792 | Array field_list ->
793 List.iter field_list begin function
794 | AFvalue expr -> rec_assert_static_literal expr
795 | AFkvalue (expr1, expr2) ->
796 rec_assert_static_literal expr1;
797 rec_assert_static_literal expr2;
799 | Darray fl -> List.iter fl assert_static_literal_for_field_list
800 | Varray el -> List.iter el rec_assert_static_literal
801 | Shape fl -> ShapeMap.iter (fun _ -> rec_assert_static_literal) fl
802 | List el
803 | Expr_list el
804 | String2 el
805 | ValCollection (_, el) -> List.iter el rec_assert_static_literal
806 | Pair (expr1, expr2)
807 | Binop (_, expr1, expr2) ->
808 rec_assert_static_literal expr1;
809 rec_assert_static_literal expr2;
810 | KeyValCollection (_, field_list) ->
811 List.iter field_list assert_static_literal_for_field_list
812 | Cast (_, e)
813 | Unop (_, e) ->
814 rec_assert_static_literal e;
815 | Eif (expr1, optional_expr, expr2) ->
816 rec_assert_static_literal expr1;
817 Option.iter optional_expr rec_assert_static_literal;
818 rec_assert_static_literal expr2;
819 | This | Lvar _ | ImmutableVar _ | Lplaceholder _ | Dollardollar _ | Fun_id _
820 | Method_id _ | Dollar _
821 | Method_caller _ | Smethod_id _ | Obj_get _ | Array_get _ | Class_get _
822 | Call _ | Special_func _ | Yield_break | Yield _ | Yield_from _ | Suspend _
823 | Await _ | InstanceOf _ | Is _ | New _ | Efun _ | Xml _ | Callconv _
824 | Assert _ | Clone _ | As _ | Pipe _ ->
825 Errors.class_property_only_static_literal (fst e)
826 and assert_static_literal_for_field_list (expr1, expr2) =
827 rec_assert_static_literal expr1;
828 rec_assert_static_literal expr2
830 rec_assert_static_literal e;
833 and interface c =
834 let enforce_no_body = begin fun m ->
835 match m.m_body with
836 | UnnamedBody { fub_ast = [] ; _}
837 | NamedBody { fnb_nast = [] ; _} ->
838 if m.m_visibility = Private
839 then Errors.not_public_or_protected_interface (fst m.m_name)
840 else ()
841 | _ -> Errors.abstract_body (fst m.m_name)
842 end in
843 (* make sure that interface methods are not async, in line with HHVM *)
844 let enforce_not_async = begin fun m ->
845 match m.m_fun_kind with
846 | Ast.FAsync -> Errors.async_in_interface (fst m.m_name)
847 | Ast.FAsyncGenerator -> Errors.async_in_interface (fst m.m_name)
848 | _ -> ()
849 end in
850 (* make sure that interfaces only have empty public methods *)
851 List.iter (c.c_static_methods @ c.c_methods) enforce_no_body;
852 List.iter (c.c_static_methods @ c.c_methods) enforce_not_async;
853 (* make sure constructor has no body *)
854 Option.iter c.c_constructor enforce_no_body;
855 Option.iter c.c_constructor enforce_not_async;
856 List.iter (c.c_uses) (fun (p, _) ->
857 Errors.interface_use_trait p
859 (* make sure that interfaces don't have any member variables *)
860 match c.c_vars with
861 | hd::_ ->
862 let pos = fst (hd.cv_id) in
863 Errors.interface_with_member_variable pos
864 | _ -> ();
865 (* make sure that interfaces don't have static variables *)
866 match c.c_static_vars with
867 | hd::_ ->
868 let pos = fst (hd.cv_id) in
869 Errors.interface_with_static_member_variable pos
870 | _ -> ();
871 (* make sure interfaces do not contain partially abstract type constants *)
872 List.iter c.c_typeconsts begin fun tc ->
873 if tc.c_tconst_constraint <> None && tc.c_tconst_type <> None then
874 Errors.interface_with_partial_typeconst (fst tc.c_tconst_name)
877 and class_const env (h, _, e) =
878 maybe hint env h;
879 maybe expr env e;
882 and typeconst (env, class_tparams) tconst =
883 maybe hint env tconst.c_tconst_type;
884 maybe hint env tconst.c_tconst_constraint;
885 (* need to ensure that tconst.c_tconst_type is not Habstr *)
886 maybe check_no_class_tparams class_tparams tconst.c_tconst_type;
887 maybe check_no_class_tparams class_tparams tconst.c_tconst_constraint
889 (* Check to make sure we are not using class type params for type const decls *)
890 and check_no_class_tparams class_tparams (pos, ty) =
891 let check_tparams = check_no_class_tparams class_tparams in
892 let maybe_check_tparams = maybe check_no_class_tparams class_tparams in
893 let matches_class_tparam tp_name =
894 List.iter class_tparams begin fun (_, (c_tp_pos, c_tp_name), _, _) ->
895 if c_tp_name = tp_name
896 then Errors.typeconst_depends_on_external_tparam pos c_tp_pos c_tp_name
897 end in
898 match ty with
899 | Hany | Hmixed | Hnonnull | Hprim _ | Hthis | Hdynamic -> ()
900 (* We have found a type parameter. Make sure its name does not match
901 * a name in class_tparams *)
902 | Habstr tparam_name ->
903 matches_class_tparam tparam_name
904 | Harray (ty1, ty2) ->
905 maybe_check_tparams ty1;
906 maybe_check_tparams ty2
907 | Hdarray (ty1, ty2) ->
908 check_tparams ty1;
909 check_tparams ty2
910 | Hvarray_or_darray ty
911 | Hvarray ty ->
912 check_tparams ty
913 | Htuple tyl -> List.iter tyl check_tparams
914 | Hoption ty_ -> check_tparams ty_
915 | Hfun (_, _, tyl, _, _, ty_) ->
916 List.iter tyl check_tparams;
917 check_tparams ty_
918 | Happly (_, tyl) -> List.iter tyl check_tparams
919 | Hshape { nsi_allows_unknown_fields=_; nsi_field_map } ->
920 ShapeMap.iter (fun _ v -> check_tparams v.sfi_hint) nsi_field_map
921 | Haccess (root_ty, _) ->
922 check_tparams root_ty
924 and class_var env cv =
925 check_class_property_initialization cv;
926 let hint_env =
927 (* If this is an XHP attribute and we're in strict mode,
928 relax to partial mode to allow the use of generic
929 classes without specifying type parameters. This is
930 a temporary hack to support existing code for now. *)
931 (* Task #5815945: Get rid of this Hack *)
932 if cv.cv_is_xhp && (Typing_env.is_strict env.tenv)
933 then { env with tenv = Typing_env.set_mode env.tenv FileInfo.Mpartial }
934 else env in
935 maybe hint hint_env cv.cv_type;
936 maybe expr env cv.cv_expr;
939 and check__toString m is_static =
940 if snd m.m_name = SN.Members.__toString
941 then begin
942 if m.m_visibility <> Public || is_static
943 then Errors.toString_visibility (fst m.m_name);
944 match m.m_ret with
945 | Some (_, Hprim Tstring) -> ()
946 | Some (p, _) -> Errors.toString_returns_string p
947 | None -> ()
950 and add_constraint pos tenv (ty1, ck, ty2) =
951 Typing_subtype.add_constraint pos tenv ck ty1 ty2
953 and add_constraints pos tenv (cstrs: locl where_constraint list) =
954 List.fold_left cstrs ~init:tenv ~f:(add_constraint pos)
956 and check_static_memoized_function m =
957 if Attributes.mem SN.UserAttributes.uaMemoize m.m_user_attributes ||
958 Attributes.mem SN.UserAttributes.uaMemoizeLSB m.m_user_attributes then
959 Errors.static_memoized_function (fst m.m_name);
962 and method_ (env, is_static) m =
963 let env =
964 { env with is_reactive = fun_is_reactive m.m_user_attributes } in
965 let named_body = assert_named_body m.m_body in
966 check__toString m is_static;
967 let env = { env with function_name = Some (snd m.m_name) } in
968 let p, name = m.m_name in
969 let is_coroutine = m.m_fun_kind = Ast.FCoroutine in
970 check_coroutines_enabled is_coroutine env p;
971 check_coroutine_constructor name is_coroutine p;
972 (* Add method type parameters to environment and localize the bounds *)
973 let tenv, constraints =
974 Phase.localize_generic_parameters_with_bounds env.tenv m.m_tparams
975 ~ety_env:(Phase.env_with_self env.tenv) in
976 let tenv = add_constraints (fst m.m_name) tenv constraints in
977 Typing_instantiability.check_tparams_instantiable tenv m.m_tparams;
978 Typing_instantiability.check_params_instantiable tenv m.m_params;
979 let env = { env with tenv = tenv } in
981 (* If this is a destructor make sure it is allowed *)
982 if name = SN.Members.__destruct
983 && not (Attributes.mem SN.UserAttributes.uaOptionalDestruct m.m_user_attributes)
984 then Errors.illegal_destructor p;
986 let is_mutable, is_mutable_pos =
987 Attributes.find SN.UserAttributes.uaMutable m.m_user_attributes
988 |> Option.value_map
989 ~default:(false, Pos.none)
990 ~f:(fun { ua_name = (p, _); _ } -> true, p) in
993 let is_maybe_mutable =
994 Attributes.mem SN.UserAttributes.uaMaybeMutable m.m_user_attributes in
996 (* Mutable methods must be reactive *)
997 if not env.is_reactive then begin
998 if is_mutable
999 then Errors.mutable_methods_must_be_reactive p name;
1000 if is_maybe_mutable
1001 then Errors.maybe_mutable_methods_must_be_reactive p name;
1002 end;
1003 if is_mutable
1004 then begin
1005 if is_maybe_mutable
1006 then Errors.conflicting_mutable_and_maybe_mutable_attributes p;
1007 if is_static && name <> SN.Members.__construct
1008 then Errors.mutable_on_static is_mutable_pos;
1009 end;
1011 (*Methods annotated with MutableReturn attribute must be reactive *)
1012 if Attributes.mem SN.UserAttributes.uaMutableReturn m.m_user_attributes
1013 && not env.is_reactive then
1014 Errors.mutable_return_annotated_decls_must_be_reactive "method" p name;
1016 check_conditionally_reactive_annotations env.is_reactive p name m.m_user_attributes;
1017 error_if_has_atmost_rx_as_rxfunc_attribute env m.m_user_attributes;
1018 check_maybe_rx_attributes_on_params env m.m_user_attributes m.m_params;
1020 let byref = List.find m.m_params ~f:(fun x -> x.param_is_reference) in
1021 List.iter m.m_params (fun_param env m.m_name m.m_fun_kind byref);
1022 if m.m_ret_by_ref &&
1023 TypecheckerOptions.disallow_return_by_ref (Env.get_options env.tenv)
1024 then Errors.illegal_return_by_ref p;
1025 let inout = List.find m.m_params ~f:(
1026 fun x -> x.param_callconv = Some Ast.Pinout) in
1027 (match inout with
1028 | Some param ->
1029 if Attributes.mem SN.UserAttributes.uaMemoize m.m_user_attributes ||
1030 Attributes.mem SN.UserAttributes.uaMemoizeLSB m.m_user_attributes
1031 then Errors.inout_params_memoize p param.param_pos;
1032 if m.m_ret_by_ref then Errors.inout_params_ret_by_ref p param.param_pos;
1034 | _ -> ()
1036 (match m.m_variadic with
1037 | FVvariadicArg vparam ->
1038 Typing_instantiability.check_param_instantiable tenv vparam;
1039 if vparam.param_is_reference then
1040 Errors.variadic_byref_param vparam.param_pos
1041 | _ -> ()
1043 List.iter m.m_tparams (tparam env);
1044 block env named_body.fnb_nast;
1045 maybe hint env m.m_ret;
1046 CheckFunctionBody.start
1047 m.m_fun_kind
1049 named_body.fnb_nast;
1050 if m.m_abstract && named_body.fnb_nast <> []
1051 then Errors.abstract_with_body m.m_name;
1052 if not (Env.is_decl env.tenv) && not m.m_abstract && named_body.fnb_nast = []
1053 then Errors.not_abstract_without_body m.m_name;
1054 (match env.class_name with
1055 | Some cname ->
1056 let p, mname = m.m_name in
1057 if String.lowercase (strip_ns cname) = String.lowercase mname
1058 && env.class_kind <> Some Ast.Ctrait
1059 then Errors.dangerous_method_name p
1060 else ()
1061 | None -> assert false)
1063 and check_maybe_rx_attributes_on_params env parent_attrs params =
1064 let parent_only_rx_if_args =
1065 Attributes.find2 SN.UserAttributes.uaOnlyRxIfArgs_do_not_use
1066 SN.UserAttributes.uaAtMostRxAsArgs parent_attrs in
1067 let check_param seen_atmost_rx_as_rxfunc p =
1068 let only_rx_if_rxfunc_attr =
1069 Attributes.find2 SN.UserAttributes.uaOnlyRxIfRxFunc_do_not_use
1070 SN.UserAttributes.uaAtMostRxAsFunc p.param_user_attributes in
1071 let only_rx_if_impl_attr =
1072 Attributes.find SN.UserAttributes.uaOnlyRxIfImpl p.param_user_attributes in
1073 match only_rx_if_rxfunc_attr, only_rx_if_impl_attr with
1074 | Some { ua_name = (p, _); _ }, _ ->
1075 if parent_only_rx_if_args = None || not env.is_reactive
1076 then Errors.atmost_rx_as_rxfunc_invalid_location p;
1077 true
1078 | _, Some { ua_name = (p, _); ua_params; _ } ->
1079 if parent_only_rx_if_args = None || not env.is_reactive
1080 then Errors.atmost_rx_as_rxfunc_invalid_location p
1081 else check_conditionally_reactive_annotation_params ~is_method:false p ua_params;
1082 true
1083 | _ -> seen_atmost_rx_as_rxfunc in
1084 let has_param_with_atmost_rx_as_rxfunc =
1085 List.fold_left params ~init:false ~f:check_param in
1086 match parent_only_rx_if_args, has_param_with_atmost_rx_as_rxfunc with
1087 | Some { ua_name = (p, _); _ }, false ->
1088 Errors.no_atmost_rx_as_rxfunc_for_rx_if_args p
1089 | _ -> ()
1091 and param_is_mutable p =
1092 Attributes.mem SN.UserAttributes.uaMutable p.param_user_attributes
1094 and param_is_maybe_mutable p =
1095 Attributes.mem SN.UserAttributes.uaMaybeMutable p.param_user_attributes
1097 and fun_param env (pos, name) f_type byref param =
1098 maybe hint env param.param_hint;
1099 maybe expr env param.param_expr;
1100 let is_mutable = param_is_mutable param in
1101 let is_maybe_mutable = param_is_maybe_mutable param in
1102 if not env.is_reactive then begin
1103 if is_mutable
1104 then Errors.mutable_methods_must_be_reactive param.param_pos name;
1105 if is_maybe_mutable
1106 then Errors.maybe_mutable_methods_must_be_reactive param.param_pos name;
1107 end;
1109 if env.is_reactive
1110 && param.param_is_reference
1111 && not (TypecheckerOptions.unsafe_rx (Env.get_options env.tenv))
1112 then Errors.reference_in_rx pos;
1114 match param.param_callconv with
1115 | None -> ()
1116 | Some Ast.Pinout ->
1117 let pos = param.param_pos in
1118 if f_type <> Ast.FSync then Errors.inout_params_outside_of_sync pos;
1119 if SSet.mem name SN.Members.as_set then Errors.inout_params_special pos;
1120 Option.iter byref ~f:(fun param ->
1121 Errors.inout_params_mix_byref pos param.param_pos);
1124 and stmt env = function
1125 | Return (p, _) when env.t_is_finally ->
1126 Errors.return_in_finally p; ()
1127 | Return (_, None)
1128 | GotoLabel _
1129 | Goto _
1130 | Noop
1131 | Unsafe_block _
1132 | Fallthrough -> ()
1133 | Break p -> begin
1134 match env.imm_ctrl_ctx with
1135 | Toplevel -> Errors.toplevel_break p
1136 | _ -> ()
1138 | Continue p -> begin
1139 match env.imm_ctrl_ctx with
1140 | Toplevel -> Errors.toplevel_continue p
1141 | SwitchContext -> Errors.continue_in_switch p
1142 | LoopContext -> ()
1144 | Return (_, Some e)
1145 | Expr e | Throw (_, e) ->
1146 expr env e
1147 | Static_var _ ->
1149 | Global_var _ ->
1151 | If (e, b1, b2) ->
1152 expr env e;
1153 block env b1;
1154 block env b2;
1156 | Do (b, e) ->
1157 block { env with imm_ctrl_ctx = LoopContext } b;
1158 expr env e;
1160 | While (e, b) ->
1161 expr env e;
1162 block { env with imm_ctrl_ctx = LoopContext } b;
1164 | Using (_has_await, e, b) ->
1165 expr env e;
1166 block env b;
1168 | For (e1, e2, e3, b) ->
1169 expr env e1;
1170 expr env e2;
1171 expr env e3;
1172 block { env with imm_ctrl_ctx = LoopContext } b;
1174 | Switch (e, cl) ->
1175 expr env e;
1176 List.iter cl (case { env with imm_ctrl_ctx = SwitchContext });
1178 | Foreach (e1, ae, b) ->
1179 expr env e1;
1180 as_expr env ae;
1181 block { env with imm_ctrl_ctx = LoopContext } b;
1183 | Try (b, cl, fb) ->
1184 block env b;
1185 List.iter cl (catch env);
1186 block { env with t_is_finally = true } fb;
1188 | Let ((p, x), _, e) ->
1189 (* We treat let statement as assignment expresssions *)
1190 let fake_expr = (p, Binop (Ast.Eq None, (p, Lvar (p, x)), e)) in
1191 expr env fake_expr;
1194 and as_expr env = function
1195 | As_v e
1196 | Await_as_v (_, e) -> expr env e
1197 | As_kv (e1, e2)
1198 | Await_as_kv (_, e1, e2) ->
1199 expr env e1;
1200 expr env e2;
1203 and afield env = function
1204 | AFvalue e -> expr env e
1205 | AFkvalue (e1, e2) -> expr env e1; expr env e2;
1207 and block env stl =
1208 List.iter stl (stmt env)
1210 and expr env (p, e) =
1211 expr_ env p e
1213 and expr_ env p = function
1214 | Any
1215 | Fun_id _
1216 | Method_id _
1217 | Smethod_id _
1218 | Method_caller _
1219 | This
1220 | Typename _
1221 | Lvar _
1222 | ImmutableVar _
1223 | Lplaceholder _
1224 | Dollardollar _
1225 | Unsafe_expr _ -> ()
1226 | Class_const (cid, ((_, m_name) as mid)) ->
1227 let func_name = env.function_name in
1228 if is_magic mid &&
1229 (not(is_parent cid) || func_name <> Some m_name)
1230 then Errors.magic mid;
1232 | Dollar e ->
1233 let env' = {env with is_array_append_allowed = false} in
1234 expr env' e
1235 | Pipe (_, e1, e2) ->
1236 expr env e1;
1237 expr env e2
1238 | Class_get _ ->
1240 (* Check that __CLASS__ and __TRAIT__ are used appropriately *)
1241 | Id (pos, const) ->
1242 if SN.PseudoConsts.is_pseudo_const const then
1243 if const = SN.PseudoConsts.g__CLASS__ then
1244 (match env.class_kind with
1245 | Some _ -> ()
1246 | _ -> Errors.illegal_CLASS pos)
1247 else if const = SN.PseudoConsts.g__TRAIT__ then
1248 (match env.class_kind with
1249 | Some Ast.Ctrait -> ()
1250 | _ -> Errors.illegal_TRAIT pos);
1252 | Array afl ->
1253 List.iter afl (afield env);
1255 | Darray fdl ->
1256 List.iter fdl (field env);
1258 | Varray el ->
1259 List.iter el (expr env);
1261 | ValCollection (_, el) ->
1262 List.iter el (expr env);
1264 | KeyValCollection (_, fdl) ->
1265 List.iter fdl (field env);
1267 | Clone e -> expr env e; ()
1268 | Obj_get (e, (_, Id s), _) ->
1269 if is_magic s
1270 then Errors.magic s;
1271 let env' = {env with is_array_append_allowed = false} in
1272 expr env' e;
1274 | Obj_get (e1, e2, _) ->
1275 let env' = {env with is_array_append_allowed = false} in
1276 expr env' e1;
1277 expr env' e2;
1279 | Array_get ((p, _), None) when not env.is_array_append_allowed ->
1280 Errors.reading_from_append p;
1282 | Array_get (e, eopt) ->
1283 let env' = {env with is_array_append_allowed = false} in
1284 expr env' e;
1285 maybe expr env' eopt;
1287 | Call (_, e, _, el, uel) ->
1288 expr env e;
1289 List.iter el (expr env);
1290 List.iter uel (expr env);
1292 | True | False | Int _
1293 | Float _ | Null | String _ | PrefixedString _ -> ()
1294 | String2 el ->
1295 List.iter el (expr env);
1297 | Unop (Ast.Uref, e) ->
1298 expr env e;
1299 begin match snd e with
1300 | This ->
1301 Errors.illegal_by_ref_expr p SN.SpecialIdents.this
1302 | Dollardollar (_, id)
1303 when Local_id.to_string id = SN.SpecialIdents.dollardollar ->
1304 Errors.illegal_by_ref_expr p SN.SpecialIdents.dollardollar
1305 | _ -> ()
1307 | Unop (_, e) -> expr env e;
1308 | Yield_break -> ()
1309 | Special_func func ->
1310 (match func with
1311 | Gena e
1312 | Gen_array_rec e ->
1313 expr env e
1314 | Genva el ->
1315 List.iter el (expr env));
1317 | Yield e ->
1318 afield env e;
1320 | Yield_from e ->
1321 expr env e;
1323 | Await e ->
1324 expr env e;
1326 | Suspend e ->
1327 expr env e;
1329 | List el ->
1330 List.iter el (expr env);
1332 | Pair (e1, e2) ->
1333 expr env e1;
1334 expr env e2;
1336 | Expr_list el ->
1337 List.iter el (expr env);
1339 | Cast (h, e) ->
1340 hint env h;
1341 expr env e;
1343 | Binop (op, e1, e2) ->
1344 let lvalue_env = match op with
1345 | Ast.Eq _ -> { env with is_array_append_allowed = true }
1346 | _ -> env in
1347 expr lvalue_env e1;
1348 expr env e2;
1350 | Eif (e1, None, e3) ->
1351 expr env e1;
1352 expr env e3;
1354 | Eif (e1, Some e2, e3) ->
1355 expr env e1;
1356 expr env e2;
1357 expr env e3;
1359 | Assert (AE_assert e) ->
1360 expr env e;
1362 | InstanceOf (e, e2) ->
1363 (match e2 with
1364 | _, CIexpr (_, Class_const ((_, CI ((_, classname), _)), (p, "class"))) ->
1365 Errors.classname_const_instanceof (Utils.strip_ns classname) p;
1366 | _ -> ());
1367 expr env e;
1369 | Is (e, h)
1370 | As (e, h, _)->
1371 expr env e;
1372 hint env h;
1374 | New (_, el, uel) ->
1375 List.iter el (expr env);
1376 List.iter uel (expr env);
1378 | Efun (f, _) ->
1379 check_coroutines_enabled (f.f_fun_kind = Ast.FCoroutine) env p;
1380 let env = { env with imm_ctrl_ctx = Toplevel } in
1381 let body = Nast.assert_named_body f.f_body in
1382 func env f body; ()
1383 | Xml (_, attrl, el) ->
1384 List.iter attrl (fun attr -> expr env (get_xhp_attr_expr attr));
1385 List.iter el (expr env);
1387 | Callconv (_, e) ->
1388 expr env e;
1389 let rec aux = function
1390 | _, Lvar _ -> true
1391 | _, Array_get (e1, Some _) -> aux e1
1392 | _ -> false
1394 if not (aux e) then Errors.inout_argument_bad_expr (fst e);
1396 | Execution_operator _ -> ()
1397 | Shape fdm ->
1398 ShapeMap.iter (fun _ v -> expr env v) fdm
1400 and case env = function
1401 | Default b -> block env b
1402 | Case (e, b) ->
1403 expr env e;
1404 block env b;
1407 and catch env (_, _, b) = block env b
1408 and field env (e1, e2) =
1409 expr env e1;
1410 expr env e2;
1413 let typedef tenv t =
1414 let env = { t_is_finally = false;
1415 is_array_append_allowed = false;
1416 class_name = None; class_kind = None;
1417 imm_ctrl_ctx = Toplevel;
1418 function_name = None;
1419 (* Since typedefs cannot have constraints we shouldn't check
1420 * if its type params satisfy the constraints of any tapply it
1421 * references.
1423 typedef_tparams = t.t_tparams;
1424 tenv = tenv;
1425 is_reactive = false
1426 } in
1427 maybe hint env t.t_constraint;
1428 hint env t.t_kind