2 * Copyright (c) 2014, Facebook, Inc.
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.
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.
30 module Env
= Typing_env
32 module CheckGenerator
= struct
34 let rec stmt = function
36 Errors.return_in_gen p
59 | Foreach
(_
, _
, b
) ->
72 | Default b
-> block b
77 and catch
(_
, _
, b
) = block b
81 module CheckFunctionType
= struct
82 let rec stmt f_type
= function
83 | Return
(_
, None
) -> ()
91 | Break
| Continue
-> ()
103 | For
(_
, _
, _
, b
) ->
107 liter case f_type cl
;
109 | Foreach
(_
, _
, b
) ->
114 liter catch f_type cl
;
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
127 and catch f_type
(_
, _
, b
) = block f_type b
129 and expr f_type
(p
, e
) =
132 and expr2 f_type
(e1
, e2
) =
137 and expr_ p f_type exp
= match f_type
, exp
with
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
)) ->
159 | _
, Obj_get
(e1
, e2
) ->
160 expr2 f_type
(e1
, e2
);
162 | _
, Array_get
(e
, eopt
) ->
164 maybe expr f_type eopt
;
166 | _
, Call
(_
, e
, el
) ->
168 liter expr f_type el
;
170 | _
, True
| _
, False
| _
, Int _
171 | _
, Float _
| _
, Null
| _
, String _
-> ()
172 | _
, String2
(el
, _
) ->
173 liter expr f_type el
;
176 liter expr f_type el
;
178 | _
, Pair
(e1
, e2
) ->
179 expr2 f_type
(e1
, e2
);
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
];
195 liter expr f_type el
;
197 | _
, InstanceOf
(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
->
209 | Gen_array_rec e
-> expr f_type e
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
->
218 | Gen_array_rec e
-> expr f_type e
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
) ->
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
);
238 ShapeMap.iter
(fun _ v
-> expr f_type v
) fdm
;
245 t_is_finally
: bool ref;
246 class_name
: string option;
247 class_kind
: Ast.class_kind
option;
252 let h = Hashtbl.create
23 in
253 let a x
= Hashtbl.add
h x
true in
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
;
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
;
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) =
285 and hint_
env p
= function
286 | Hany
| Hmixed
| Habstr _
| Hprim _
->
288 | Harray
(ty1
, ty2
) ->
291 | Htuple hl
-> List.iter
(hint
env) hl
295 List.iter
(hint
env) hl
;
298 | Happly
((_
, x
), hl
) when Typing_env.is_typedef
env.tenv x
->
299 let tdef = Typing_env.Typedefs.find_unsafe x
in
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
311 check_params
env p x class_
.tc_tparams hl
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
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;
334 class_kind
= Some c
.c_kind
;
336 let env = { env with tenv = Env.set_mode
env.tenv c
.c_mode
} in
337 if c
.c_kind
= Ast.Cinterface
then begin
341 maybe method_
(env, true) c
.c_constructor
;
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
;
357 (** Make sure that the given hint points to an interface *)
358 and check_is_interface
(env, error_verb
) (x
: hint
) =
361 let _, class_
= Env.get_class
env.tenv (snd id
) in
364 (* in partial mode, we can fail to find the class if it's
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
) =
379 let _, class_
= Env.get_class
env.tenv (snd id
) in
382 (* in partial mode, we can fail to find the class if it's
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 *)
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 *)
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
->
426 then Errors.abstract_body
(fst m
.m_name
)
428 if m
.m_visibility
<> Public
429 then Errors.not_public_interface
(fst m
.m_name
)
431 end env (c
.c_static_methods
@ c
.c_methods
);
432 (* make sure that interfaces don't have any member variables *)
435 let pos = fst
(hd
.cv_id
) in
436 Errors.interface_with_member_variable
pos
438 (* make sure that interfaces don't have static variables *)
439 match c
.c_static_vars
with
441 let pos = fst
(hd
.cv_id
) in
442 Errors.interface_with_static_member_variable
pos
445 and class_const
env (h, _, 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"
457 if m
.m_visibility
<> Public
|| is_static
458 then Errors.toString_visibility
(fst m
.m_name
);
460 | Some
(_, Hprim Tstring
) -> ()
461 | Some
(p
, _) -> Errors.toString_returns_string p
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
;
470 maybe hint
env m
.m_ret
;
471 CheckFunctionType.block m
.m_type m
.m_body
;
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
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
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
) =
499 and stmt env = function
500 | Return
(p, _) when !(env.t_is_finally
) ->
501 Errors.return_in_finally
p; ()
505 | Break
| Continue
-> ()
507 | Expr e
| Throw
(_, e
) ->
524 | For
(e1
, e2
, e3
, b
) ->
534 | Foreach
(e1
, ae
, b
) ->
542 let is_fin_copy = !(env.t_is_finally
) in
543 env.t_is_finally
:= true;
545 env.t_is_finally
:= is_fin_copy;
548 and as_expr
env = function
549 | As_id e
-> expr
env e
558 and expr
env (_, e
) =
561 and expr_
env = function
573 | ValCollection
(_, el
) ->
576 | KeyValCollection
(_, fdl
) ->
579 | Clone e
-> expr
env e
; ()
580 | Obj_get
(e
, (_, Id s
)) ->
581 if is_magic s
&& Env.is_strict
env.tenv
585 | Obj_get
(e1
, e2
) ->
589 | Array_get
(e
, eopt
) ->
597 | True
| False
| Int
_
598 | Float
_ | Null
| String
_ -> ()
602 | Unop
(_, e
) -> expr
env e
604 | Special_func func
->
610 | Gen_array_va_rec el
->
614 env.t_is_gen
:= true;
634 | Assert
(AE_invariant
(e1
, e2
, el
)) ->
638 | Binop
(_, e1
, e2
) ->
642 | Eif
(e1
, None
, e3
) ->
646 | Eif
(e1
, Some e2
, e3
) ->
651 | Assert
(AE_invariant_violation
(e
, el
)) ->
654 | Assert
(AE_assert e
)
655 | InstanceOf
(e
, _) ->
661 let is_gen_copy = !(env.t_is_gen
) in
662 env.t_is_gen
:= false;
664 env.t_is_gen
:= is_gen_copy;
666 | Xml
(_, attrl
, el
) ->
667 liter attribute
env attrl
;
671 ShapeMap.iter
(fun _ v
-> expr
env v
) fdm
673 and case
env = function
674 | Default b
-> block
env b
680 and catch
env (_, _, b
) = block
env b
681 and field
env (e1
, e2
) =
686 and attribute
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
;
696 let tenv, ty
= Typing_hint.hint
tenv h in
697 Typing_tdef.check_typedef name
tenv ty