Implement shapes with class constant fields.
[hiphop-php.git] / hphp / hack / src / typing / nastCheck.ml
blob5d6d6a44c77385c7e9fc3f1e8b98817085f0d5ea
1 (**
2 * Copyright (c) 2014, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
12 (* This module performs checks after the naming has been done.
13 Basically any check that doesn't fall in the typing category. *)
14 (* Check of type application arity *)
15 (* Check no `new AbstractClass` (or trait, or interface) *)
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 Utils
25 open Nast
26 open Typing_defs
27 open Typing_deps
28 open Autocomplete
30 module Env = Typing_env
32 module CheckGenerator = struct
34 let rec stmt = function
35 | Return (p, _) ->
36 Errors.return_in_gen p
37 | Throw (p, _) -> ()
38 | Noop
39 | Fallthrough
40 | Break | Continue
41 | Expr _
42 | Static_var _ -> ()
43 | If (_, b1, b2) ->
44 block b1;
45 block b2;
47 | Do (b, _) ->
48 block b;
50 | While (_, b) ->
51 block b;
53 | For (_, _, _, b) ->
54 block b;
56 | Switch (_, cl) ->
57 List.iter case cl;
59 | Foreach (_, _, b) ->
60 block b;
62 | Try (b, cl, fb) ->
63 block b;
64 List.iter catch cl;
65 block fb;
68 and block stl =
69 List.iter stmt stl
71 and case = function
72 | Default b -> block b
73 | Case (_, b) ->
74 block b;
77 and catch (_, _, b) = block b
79 end
81 module CheckFunctionType = struct
82 let rec stmt f_type = function
83 | Return (_, None) -> ()
84 | Return (_, Some e)
85 | Throw (_, e)
86 | Expr e ->
87 expr f_type e;
89 | Noop
90 | Fallthrough
91 | Break | Continue -> ()
92 | Static_var _ -> ()
93 | If (_, b1, b2) ->
94 block f_type b1;
95 block f_type b2;
97 | Do (b, _) ->
98 block f_type b;
100 | While (_, b) ->
101 block f_type b;
103 | For (_, _, _, b) ->
104 block f_type b;
106 | Switch (_, cl) ->
107 liter case f_type cl;
109 | Foreach (_, _, b) ->
110 block f_type b;
112 | Try (b, cl, fb) ->
113 block f_type b;
114 liter catch f_type cl;
115 block f_type fb;
118 and block f_type stl =
119 liter stmt f_type stl
121 and case f_type = function
122 | Default b -> block f_type b
123 | Case (_, b) ->
124 block f_type b;
127 and catch f_type (_, _, b) = block f_type b
129 and expr f_type (p, e) =
130 expr_ p f_type e
132 and expr2 f_type (e1, e2) =
133 expr f_type e1;
134 expr f_type e2;
137 and expr_ p f_type exp = match f_type, exp with
138 | _, Any -> ()
139 | _, Array _
140 | _, Fun_id _
141 | _, Method_id _
142 | _, Smethod_id _
143 | _, Method_caller _
144 | _, This
145 | _, Id _
146 | _, Class_get _
147 | _, Class_const _
148 | _, Lvar _ -> ()
149 | _, ValCollection (_, el) ->
150 liter expr f_type el;
152 | _, KeyValCollection (_, fdl) ->
153 liter expr2 f_type fdl;
155 | _, Clone e -> expr f_type e; ()
156 | _, Obj_get (e, (_, Id s)) ->
157 expr f_type e;
159 | _, Obj_get (e1, e2) ->
160 expr2 f_type (e1, e2);
162 | _, Array_get (e, eopt) ->
163 expr f_type e;
164 maybe expr f_type eopt;
166 | _, Call (_, e, el) ->
167 expr f_type e;
168 liter expr f_type el;
170 | _, True | _, False | _, Int _
171 | _, Float _ | _, Null | _, String _ -> ()
172 | _, String2 (el, _) ->
173 liter expr f_type el;
175 | _, List el ->
176 liter expr f_type el;
178 | _, Pair (e1, e2) ->
179 expr2 f_type (e1, e2);
181 | _, Expr_list el ->
182 liter expr f_type el;
184 | _, Unop (_, e) -> expr f_type e
185 | _, Binop (_, e1, e2) ->
186 expr2 f_type (e1, e2);
188 | _, Eif (e1, None, e3) ->
189 expr2 f_type (e1, e3);
191 | _, Eif (e1, Some e2, e3) ->
192 liter expr f_type [e1; e2; e3];
194 | _, New (_, el) ->
195 liter expr f_type el;
197 | _, InstanceOf (e, _) ->
198 expr f_type e;
200 | _, Cast (_, e) ->
201 expr f_type e;
203 | _, Efun (f, _) -> ()
204 | Ast.FAsync, Yield_break
205 | Ast.FAsync, Yield _ -> Errors.yield_in_async_function p
206 | Ast.FAsync, Special_func func ->
207 (match func with
208 | Gena e
209 | Gen_array_rec e -> expr f_type e
210 | Genva el
211 | Gen_array_va_rec el -> liter expr f_type el);
213 | Ast.FSync, Yield_break -> ()
214 | Ast.FSync, Yield e -> expr f_type e; ()
215 | Ast.FSync, Special_func func ->
216 (match func with
217 | Gena e
218 | Gen_array_rec e -> expr f_type e
219 | Genva el
220 | Gen_array_va_rec el -> liter expr f_type el);
222 | Ast.FSync, Await _ -> Errors.await_in_sync_function p
223 | Ast.FAsync, Await e -> expr f_type e; ()
224 | _, Xml (_, attrl, el) ->
225 List.iter (fun (_, e) -> expr f_type e) attrl;
226 liter expr f_type el;
228 | _, Assert (AE_assert e) ->
229 expr f_type e;
231 | _, Assert (AE_invariant_violation (e, el)) ->
232 liter expr f_type (e :: el);
234 | _, Assert (AE_invariant (e1, e2, el)) ->
235 liter expr f_type (e1 :: e2 :: el);
237 | _, Shape fdm ->
238 ShapeMap.iter (fun _ v -> expr f_type v) fdm;
243 type env = {
244 t_is_gen: bool ref;
245 t_is_finally: bool ref;
246 class_name: string option;
247 class_kind: Ast.class_kind option;
248 tenv: Env.env;
251 let is_magic =
252 let h = Hashtbl.create 23 in
253 let a x = Hashtbl.add h x true in
254 a "__set";
255 a "__isset";
256 a "__get";
257 a "__unset";
258 a "__call";
259 a "__callStatic";
260 fun (_, s) ->
261 Hashtbl.mem h s
263 let rec fun_ tenv f =
264 if f.f_mode = Ast.Mdecl || !auto_complete then () else begin
265 let tenv = Typing_env.set_root tenv (Dep.Fun (snd f.f_name)) in
266 let env = { t_is_gen = ref false; t_is_finally = ref false;
267 class_name = None; class_kind = None;
268 tenv = tenv } in
269 func env f
272 and func env f =
273 let old_gen = !(env.t_is_gen) in
274 let env = { env with tenv = Env.set_mode env.tenv f.f_mode } in
275 maybe hint env f.f_ret;
276 List.iter (fun_param env) f.f_params;
277 block env f.f_body;
278 CheckFunctionType.block f.f_type f.f_body;
279 if !(env.t_is_gen) then CheckGenerator.block f.f_body;
280 env.t_is_gen := old_gen
282 and hint env (p, h) =
283 hint_ env p h
285 and hint_ env p = function
286 | Hany | Hmixed | Habstr _ | Hprim _ ->
288 | Harray (ty1, ty2) ->
289 maybe hint env ty1;
290 maybe hint env ty2
291 | Htuple hl -> List.iter (hint env) hl
292 | Hoption h ->
293 hint env h; ()
294 | Hfun (hl,_, h) ->
295 List.iter (hint env) hl;
296 hint env h;
298 | Happly ((_, x), hl) when Typing_env.is_typedef env.tenv x ->
299 let tdef = Typing_env.Typedefs.find_unsafe x in
300 let params =
301 match tdef with
302 | Typing_env.Typedef.Error -> []
303 | Typing_env.Typedef.Ok (_, x, _, _, _) -> x
305 check_params env p x params hl
306 | Happly ((_, x), hl) ->
307 let _, class_ = Env.get_class env.tenv x in
308 (match class_ with
309 | None -> ()
310 | Some class_ ->
311 check_params env p x class_.tc_tparams hl
314 | Hshape fdl ->
315 ShapeMap.iter (fun _ v -> hint env v) fdl
317 and check_params env p x params hl =
318 let arity = List.length params in
319 check_arity env p x arity (List.length hl);
320 List.iter (hint env) hl;
322 and check_arity env p tname arity size =
323 if size = arity then () else
324 if size = 0 && not (Typing_env.is_strict env.tenv) then () else
325 let nargs = soi arity in
326 Errors.type_arity p tname nargs
328 and class_ tenv c =
329 if c.c_mode = Ast.Mdecl || !auto_complete then () else begin
330 let cname = Some (snd c.c_name) in
331 let tenv = Typing_env.set_root tenv (Dep.Class (snd c.c_name)) in
332 let env = { t_is_gen = ref false; t_is_finally = ref false;
333 class_name = cname;
334 class_kind = Some c.c_kind;
335 tenv = tenv } in
336 let env = { env with tenv = Env.set_mode env.tenv c.c_mode } in
337 if c.c_kind = Ast.Cinterface then begin
338 interface env c;
340 else begin
341 maybe method_ (env, true) c.c_constructor;
342 end;
343 liter hint env c.c_extends;
344 liter hint env c.c_implements;
345 liter class_const env c.c_consts;
346 liter class_var env c.c_static_vars;
347 liter class_var env c.c_vars;
348 liter method_ (env, true) c.c_static_methods;
349 liter method_ (env, false) c.c_methods;
350 liter check_is_interface (env, "implement") c.c_implements;
351 liter check_is_interface (env, "require implementation of") c.c_req_implements;
352 liter check_is_class env c.c_req_extends;
353 liter check_is_trait env c.c_uses;
354 end;
357 (** Make sure that the given hint points to an interface *)
358 and check_is_interface (env, error_verb) (x : hint) =
359 match (snd x) with
360 | Happly (id, _) ->
361 let _, class_ = Env.get_class env.tenv (snd id) in
362 (match class_ with
363 | None ->
364 (* in partial mode, we can fail to find the class if it's
365 defined in PHP. *)
366 (* in strict mode, we catch the unknown class error before
367 even reaching here. *)
369 | Some { tc_kind = Ast.Cinterface; _ } -> ()
370 | Some { tc_name; _ } ->
371 Errors.non_interface (fst x) tc_name error_verb
373 | _ -> failwith "assertion failure: interface isn't a Happly"
375 (** Make sure that the given hint points to a non-final class *)
376 and check_is_class env (x : hint) =
377 match (snd x) with
378 | Happly (id, _) ->
379 let _, class_ = Env.get_class env.tenv (snd id) in
380 (match class_ with
381 | None ->
382 (* in partial mode, we can fail to find the class if it's
383 defined in PHP. *)
384 (* in strict mode, we catch the unknown class error before
385 even reaching here. *)
387 | Some { tc_kind = Ast.Cabstract; _ } -> ()
388 | Some { tc_kind = Ast.Cnormal; _ } -> ()
389 | Some { tc_kind; tc_name; _ } ->
390 Errors.requires_non_class (fst x) tc_name (Ast.string_of_class_kind tc_kind)
392 | _ -> failwith "assertion failure: interface isn't a Happly"
395 * Make sure that all "use"s are with traits, and not
396 * classes, interfaces, etc.
398 and check_is_trait env (h : hint) =
399 (* Second part of a hint contains Happly information *)
400 (match (snd h) with
401 (* An Happly contains identifying info (sid) and hint list (which we *)
402 (* do not care about at this time *)
403 | Happly (pos_and_name, _) ->
404 (* Env.get_class will get the type info associated with the name *)
405 let _, type_info = Env.get_class env.tenv (snd pos_and_name) in
406 (match type_info with
407 (* in partial mode, it's possible to not find the trait, because the *)
408 (* trait may live in PHP land. In strict mode, we catch the unknown *)
409 (* trait error before even reaching here. so it's ok to just return *)
410 (* unit. *)
411 | None -> ()
412 (* tc_kind is part of the type_info. If we are a trait, all is good *)
413 | Some { tc_kind = Ast.Ctrait; _ } -> ()
414 (* Anything other than a trait we are going to throw an error *)
415 (* using the tc_kind and tc_name fields of our type_info *)
416 | Some { tc_kind; tc_name; _ } ->
417 Errors.uses_non_trait (fst h) tc_name (Ast.string_of_class_kind tc_kind)
419 | _ -> failwith "assertion failure: trait isn't an Happly"
422 and interface env c =
423 (* make sure that interfaces only have empty public methods *)
424 liter begin fun env m ->
425 if m.m_body <> []
426 then Errors.abstract_body (fst m.m_name)
427 else ();
428 if m.m_visibility <> Public
429 then Errors.not_public_interface (fst m.m_name)
430 else ()
431 end env (c.c_static_methods @ c.c_methods);
432 (* make sure that interfaces don't have any member variables *)
433 match c.c_vars with
434 | hd::_ ->
435 let pos = fst (hd.cv_id) in
436 Errors.interface_with_member_variable pos
437 | _ -> ();
438 (* make sure that interfaces don't have static variables *)
439 match c.c_static_vars with
440 | hd::_ ->
441 let pos = fst (hd.cv_id) in
442 Errors.interface_with_static_member_variable pos
443 | _ -> ()
445 and class_const env (h, _, e) =
446 maybe hint env h;
447 expr env e
449 and class_var env cv =
450 maybe hint env cv.cv_type;
451 maybe expr env cv.cv_expr;
454 and check__toString m is_static =
455 if snd m.m_name = "__toString"
456 then begin
457 if m.m_visibility <> Public || is_static
458 then Errors.toString_visibility (fst m.m_name);
459 match m.m_ret with
460 | Some (_, Hprim Tstring) -> ()
461 | Some (p, _) -> Errors.toString_returns_string p
462 | None -> ()
465 and method_ (env, is_static) m =
466 check__toString m is_static;
467 let old_gen = !(env.t_is_gen) in
468 liter fun_param env m.m_params;
469 block env m.m_body;
470 maybe hint env m.m_ret;
471 CheckFunctionType.block m.m_type m.m_body;
472 if !(env.t_is_gen)
473 then CheckGenerator.block m.m_body;
474 if m.m_body <> [] && m.m_abstract
475 then Errors.abstract_with_body m.m_name;
476 if m.m_body = [] && not m.m_abstract
477 then Errors.not_abstract_without_body m.m_name;
478 (match env.class_name with
479 | Some cname ->
480 let p, mname = m.m_name in
481 if String.lowercase (strip_ns cname) = String.lowercase mname
482 && env.class_kind <> Some Ast.Ctrait
483 then Errors.dangerous_method_name p
484 else ()
485 | None -> assert false);
486 env.t_is_gen := old_gen;
489 and fun_param env param =
490 maybe hint env param.param_hint;
491 maybe expr env param.param_expr;
494 and fun_param_opt env (h, _, e) =
495 maybe hint env h;
496 maybe expr env e;
499 and stmt env = function
500 | Return (p, _) when !(env.t_is_finally) ->
501 Errors.return_in_finally p; ()
502 | Return (_, None)
503 | Noop
504 | Fallthrough
505 | Break | Continue -> ()
506 | Return (_, Some e)
507 | Expr e | Throw (_, e) ->
508 expr env e
509 | Static_var el ->
510 liter expr env el
511 | If (e, b1, b2) ->
512 expr env e;
513 block env b1;
514 block env b2;
516 | Do (b, e) ->
517 block env b;
518 expr env e;
520 | While (e, b) ->
521 expr env e;
522 block env b;
524 | For (e1, e2, e3, b) ->
525 expr env e1;
526 expr env e2;
527 expr env e3;
528 block env b;
530 | Switch (e, cl) ->
531 expr env e;
532 liter case env cl;
534 | Foreach (e1, ae, b) ->
535 expr env e1;
536 as_expr env ae;
537 block env b;
539 | Try (b, cl, fb) ->
540 block env b;
541 liter catch env cl;
542 let is_fin_copy = !(env.t_is_finally) in
543 env.t_is_finally := true;
544 block env fb;
545 env.t_is_finally := is_fin_copy;
548 and as_expr env = function
549 | As_id e -> expr env e
550 | As_kv (e1, e2) ->
551 expr env e1;
552 expr env e2;
555 and block env stl =
556 liter stmt env stl
558 and expr env (_, e) =
559 expr_ env e
561 and expr_ env = function
562 | Any
563 | Array _
564 | Fun_id _
565 | Method_id _
566 | Smethod_id _
567 | Method_caller _
568 | This
569 | Id _
570 | Class_get _
571 | Class_const _
572 | Lvar _ -> ()
573 | ValCollection (_, el) ->
574 liter expr env el;
576 | KeyValCollection (_, fdl) ->
577 liter field env fdl;
579 | Clone e -> expr env e; ()
580 | Obj_get (e, (_, Id s)) ->
581 if is_magic s && Env.is_strict env.tenv
582 then Errors.magic s;
583 expr env e;
585 | Obj_get (e1, e2) ->
586 expr env e1;
587 expr env e2;
589 | Array_get (e, eopt) ->
590 expr env e;
591 maybe expr env eopt;
593 | Call (_, e, el) ->
594 expr env e;
595 liter expr env el;
597 | True | False | Int _
598 | Float _ | Null | String _ -> ()
599 | String2 (el, _) ->
600 liter expr env el;
602 | Unop (_, e) -> expr env e
603 | Yield_break -> ()
604 | Special_func func ->
605 (match func with
606 | Gena e
607 | Gen_array_rec e->
608 expr env e
609 | Genva el
610 | Gen_array_va_rec el ->
611 liter expr env el);
613 | Yield e ->
614 env.t_is_gen := true;
615 expr env e;
617 | Await e ->
618 expr env e;
620 | List el ->
621 liter expr env el;
623 | Pair (e1, e2) ->
624 expr env e1;
625 expr env e2;
627 | Expr_list el ->
628 liter expr env el;
630 | Cast (h, e) ->
631 hint env h;
632 expr env e;
634 | Assert (AE_invariant (e1, e2, el)) ->
635 expr env e1;
636 expr env e2;
637 liter expr env el
638 | Binop (_, e1, e2) ->
639 expr env e1;
640 expr env e2;
642 | Eif (e1, None, e3) ->
643 expr env e1;
644 expr env e3;
646 | Eif (e1, Some e2, e3) ->
647 expr env e1;
648 expr env e2;
649 expr env e3;
651 | Assert (AE_invariant_violation (e, el)) ->
652 expr env e;
653 liter expr env el
654 | Assert (AE_assert e)
655 | InstanceOf (e, _) ->
656 expr env e;
658 | New (_, el) ->
659 liter expr env el
660 | Efun (f, _) ->
661 let is_gen_copy = !(env.t_is_gen) in
662 env.t_is_gen := false;
663 func env f;
664 env.t_is_gen := is_gen_copy;
666 | Xml (_, attrl, el) ->
667 liter attribute env attrl;
668 liter expr env el;
670 | Shape fdm ->
671 ShapeMap.iter (fun _ v -> expr env v) fdm
673 and case env = function
674 | Default b -> block env b
675 | Case (e, b) ->
676 expr env e;
677 block env b;
680 and catch env (_, _, b) = block env b
681 and field env (e1, e2) =
682 expr env e1;
683 expr env e2;
686 and attribute env (_, e) =
687 expr env e;
690 let typedef tenv name (_, params, h) =
691 let env = { t_is_gen = ref false;
692 t_is_finally = ref false;
693 class_name = None; class_kind = None;
694 tenv = tenv } in
695 hint env h;
696 let tenv, ty = Typing_hint.hint tenv h in
697 Typing_tdef.check_typedef name tenv ty