Ban references in strict mode again
[hiphop-php.git] / hphp / hack / src / naming / naming.ml
blobbda6d529ab784770cc34d2187bf320e3878e89c3
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 *)
10 (** Module "naming" a program.
12 * The naming phase consists in several things
13 * 1- get all the global names
14 * 2- transform all the local names into a unique identifier
16 open Ast
17 open Hh_core
18 open Utils
19 open String_utils
21 module N = Nast
22 module ShapeMap = N.ShapeMap
23 module SN = Naming_special_names
24 module NS = Namespaces
26 module GEnv = NamingGlobal.GEnv
28 (*****************************************************************************)
29 (* The types *)
30 (*****************************************************************************)
32 (* We want to keep the positions of names that have been
33 * replaced by identifiers.
35 type positioned_ident = (Pos.t * Local_id.t)
37 (* <T as A>, A is a type constraint *)
38 type type_constraint = (Ast.constraint_kind * Ast.hint) list
40 type genv = {
42 (* strict? decl? partial? *)
43 in_mode: FileInfo.mode;
45 (* various options that control the strictness of the typechecker *)
46 tcopt: TypecheckerOptions.t;
48 (* are we in the body of a try statement? *)
49 in_try: bool;
51 (* are we in the body of a finally statement? *)
52 in_finally: bool;
54 (* In function foo<T1, ..., Tn> or class<T1, ..., Tn>, the field
55 * type_params knows T1 .. Tn. It is able to find out about the
56 * constraint on these parameters. *)
57 type_params: type_constraint SMap.t;
59 (* The current class, None if we are in a function *)
60 current_cls: (Ast.id * Ast.class_kind) option;
62 class_consts: (string, Pos.t) Hashtbl.t;
64 class_props: (string, Pos.t) Hashtbl.t;
66 (* Normally we don't need to add dependencies at this stage, but there
67 * are edge cases when we do. *)
68 droot: Typing_deps.Dep.variant;
70 (* Namespace environment, e.g., what namespace we're in and what use
71 * declarations are in play. *)
72 namespace: Namespace_env.env;
75 (* How to behave when we see an unbound name. Either we raise an
76 * error, or we call a function first and continue if it can resolve
77 * the name. This is used to nest environments when processing
78 * closures. *)
79 type unbound_mode =
80 | UBMErr
81 | UBMFunc of ((Pos.t * string) -> positioned_ident)
83 (* The primitives to manipulate the naming environment *)
84 module Env : sig
86 type all_locals
87 type lenv
89 val empty_local : unbound_mode -> lenv
90 val make_class_genv :
91 TypecheckerOptions.t ->
92 type_constraint SMap.t ->
93 FileInfo.mode ->
94 Ast.id * Ast.class_kind -> Namespace_env.env -> genv
95 val make_class_env :
96 TypecheckerOptions.t ->
97 type_constraint SMap.t -> Ast.class_ -> genv * lenv
98 val make_typedef_env :
99 TypecheckerOptions.t ->
100 type_constraint SMap.t -> Ast.typedef -> genv * lenv
101 val make_fun_genv :
102 TypecheckerOptions.t ->
103 type_constraint SMap.t ->
104 FileInfo.mode -> string -> Namespace_env.env -> genv
105 val make_fun_decl_genv :
106 TypecheckerOptions.t ->
107 type_constraint SMap.t -> Ast.fun_ -> genv
108 val make_const_env : TypecheckerOptions.t -> Ast.gconst -> genv * lenv
110 val has_unsafe : genv * lenv -> bool
111 val set_unsafe : genv * lenv -> bool -> unit
113 val add_lvar : genv * lenv -> Ast.id -> positioned_ident -> unit
114 val add_param : genv * lenv -> N.fun_param -> genv * lenv
115 val new_lvar : genv * lenv -> Ast.id -> positioned_ident
116 val new_let_local : genv * lenv -> Ast.id -> positioned_ident
117 val found_dollardollar : genv * lenv -> Pos.t -> positioned_ident
118 val has_dollardollar : all_locals -> Pos.t option
119 val inside_pipe : genv * lenv -> bool
120 val new_pending_lvar : genv * lenv -> Ast.id -> unit
121 val promote_pending_lvar : genv * lenv -> string -> unit
122 val lvar : genv * lenv -> Ast.id -> positioned_ident
123 val let_local : genv * lenv -> Ast.id -> positioned_ident option
124 val global_const : genv * lenv -> Ast.id -> Ast.id
125 val type_name : genv * lenv -> Ast.id -> allow_typedef:bool -> Ast.id
126 val fun_id : genv * lenv -> Ast.id -> Ast.id
127 val bind_class_const : genv * lenv -> Ast.id -> unit
128 val bind_prop : genv * lenv -> Ast.id -> unit
129 val goto_label : genv * lenv -> string -> Pos.t option
130 val new_goto_label : genv * lenv -> pstring -> unit
131 val new_goto_target : genv * lenv -> pstring -> unit
132 val check_goto_references : genv * lenv -> unit
133 val copy_let_locals : genv * lenv -> genv * lenv -> unit
135 val scope : genv * lenv -> (genv * lenv -> 'a) -> 'a
136 val scope_all : genv * lenv -> (genv * lenv -> 'a) -> all_locals * 'a
137 val scope_lexical : genv * lenv -> (genv * lenv -> 'a) -> 'a
138 val extend_all_locals : genv * lenv -> all_locals -> unit
139 val remove_locals : genv * lenv -> Ast.id list -> unit
140 val pipe_scope : genv * lenv -> (genv * lenv -> N.expr) -> Local_id.t * N.expr
142 end = struct
144 type map = positioned_ident SMap.t
145 type all_locals = Pos.t SMap.t
147 (* The local environment *)
148 type lenv = {
150 (* The set of locals *)
151 locals: map ref;
153 (* We keep all the locals, even if we are in a different scope
154 * to provide better error messages.
155 * if you write:
156 * if(...) {
157 * $x = ...;
159 * Technically, passed this point, $x is unbound.
160 * But it is much better to keep it somewhere, so that you can
161 * say it is bound, but in a different scope.
163 all_locals: all_locals ref;
165 (* Some statements can define new variables afterwards, e.g.,
166 * if (...) {
167 * $x = ...;
168 * } else {
169 * $x = ...;
171 * We need to give $x the same name in both branches, but we don't want
172 * $x to actually be a local until after the if block. So we stash it here,
173 * to indicate a name has been pre-allocated, but that the variable isn't
174 * actually defined yet.
176 pending_locals: map ref;
178 (* The set of lexically-scoped local `let` variables *)
179 (* TODO: Currently these locals live in a separate namespace, it is
180 * worthwhile considering unified namespace for all local variables T28712009 *)
181 let_locals: map ref;
183 (* Tag controlling what we do when we encounter an unbound name.
184 * This is used when processing a lambda expression body that has
185 * an automatic use list.
187 * See expr_lambda for details.
189 unbound_mode: unbound_mode;
191 (* The presence of an "UNSAFE" in the function body changes the
192 * verifiability of the function's return type, since the unsafe
193 * block could return. For the sanity of the typechecker, we flatten
194 * this out, but need to track if we've seen an "UNSAFE" in order to
195 * do so. *)
196 has_unsafe: bool ref;
198 (** Allows us to ban $$ appearances outside of pipe expressions and
199 * equals expressions within pipes. *)
200 inside_pipe: bool ref;
203 * A map from goto label strings to named labels.
205 goto_labels: Pos.t SMap.t ref;
208 * A map from goto label used in a goto statement to the position of that
209 * goto label usage.
211 goto_targets: Pos.t SMap.t ref;
214 let empty_local unbound_mode = {
215 locals = ref SMap.empty;
216 all_locals = ref SMap.empty;
217 pending_locals = ref SMap.empty;
218 let_locals = ref SMap.empty;
219 unbound_mode;
220 has_unsafe = ref false;
221 inside_pipe = ref false;
222 goto_labels = ref SMap.empty;
223 goto_targets = ref SMap.empty;
226 let make_class_genv tcopt tparams mode (cid, ckind) namespace = {
227 in_mode =
228 (if !Autocomplete.auto_complete then FileInfo.Mpartial else mode);
229 tcopt;
230 in_try = false;
231 in_finally = false;
232 type_params = tparams;
233 current_cls = Some (cid, ckind);
234 class_consts = Hashtbl.create 0;
235 class_props = Hashtbl.create 0;
236 droot = Typing_deps.Dep.Class (snd cid);
237 namespace;
240 let unbound_name_error genv pos name kind =
241 (* Naming pretends to be local and not dependent on other files, so it
242 * doesn't bother with adding dependencies (even though it does look up
243 * things in global state). This is mostly brushed aside because "they
244 * will be added during typing". Unfortunately, there are multiple scenarios
245 * when typechecker will name an expression, but gives up on typechecking
246 * it. We are then left with a unrecorded dependency. This should be fixed
247 * on some more basic level, but so far the only incorrectness that anyone
248 * has observed due to this is that we fail to remove "unbound name" errors
249 * sometimes. I add this dependency here for now to fix the annoyance it
250 * causes developers. *)
251 begin match kind with
252 | `func -> Typing_deps.Dep.Fun name
253 | `cls -> Typing_deps.Dep.Class name
254 | `const -> Typing_deps.Dep.GConst name
255 end |> Typing_deps.add_idep genv.droot;
256 Errors.unbound_name pos name kind
258 let make_class_env tcopt tparams c =
259 let genv = make_class_genv tcopt tparams c.c_mode
260 (c.c_name, c.c_kind) c.c_namespace in
261 let lenv = empty_local UBMErr in
262 let env = genv, lenv in
265 let make_typedef_genv tcopt cstrs tdef = {
266 in_mode = FileInfo.(if !Ide.is_ide_mode then Mpartial else Mstrict);
267 tcopt;
268 in_try = false;
269 in_finally = false;
270 type_params = cstrs;
271 current_cls = None;
272 class_consts = Hashtbl.create 0;
273 class_props = Hashtbl.create 0;
274 droot = Typing_deps.Dep.Class (snd tdef.t_id);
275 namespace = tdef.t_namespace;
278 let make_typedef_env genv cstrs tdef =
279 let genv = make_typedef_genv genv cstrs tdef in
280 let lenv = empty_local UBMErr in
281 let env = genv, lenv in
284 let make_fun_genv tcopt params f_mode f_name f_namespace = {
285 in_mode = f_mode;
286 tcopt;
287 in_try = false;
288 in_finally = false;
289 type_params = params;
290 current_cls = None;
291 class_consts = Hashtbl.create 0;
292 class_props = Hashtbl.create 0;
293 droot = Typing_deps.Dep.Fun f_name;
294 namespace = f_namespace;
297 let make_fun_decl_genv nenv params f =
298 make_fun_genv nenv params f.f_mode (snd f.f_name) f.f_namespace
300 let make_const_genv tcopt cst = {
301 in_mode = cst.cst_mode;
302 tcopt;
303 in_try = false;
304 in_finally = false;
305 type_params = SMap.empty;
306 current_cls = None;
307 class_consts = Hashtbl.create 0;
308 class_props = Hashtbl.create 0;
309 droot = Typing_deps.Dep.GConst (snd cst.cst_name);
310 namespace = cst.cst_namespace;
313 let make_const_env nenv cst =
314 let genv = make_const_genv nenv cst in
315 let lenv = empty_local UBMErr in
316 let env = genv, lenv in
319 let has_unsafe (_genv, lenv) = !(lenv.has_unsafe)
320 let set_unsafe (_genv, lenv) x =
321 lenv.has_unsafe := x
323 let lookup genv (env : string -> FileInfo.pos option) (p, x) =
324 let v = env x in
325 match v with
326 | None ->
327 (match genv.in_mode with
328 | FileInfo.Mstrict -> unbound_name_error genv p x `const
329 | FileInfo.Mpartial | FileInfo.Mdecl when not
330 (TypecheckerOptions.assume_php genv.tcopt) ->
331 unbound_name_error genv p x `const
332 | FileInfo.Mphp | FileInfo.Mdecl | FileInfo.Mpartial -> ()
334 | _ -> ()
336 (* Check and see if the user might have been trying to use one of the
337 * generics in scope as a runtime value *)
338 let check_no_runtime_generic genv (p, name) =
339 let tparaml = SMap.keys genv.type_params in
340 if List.mem tparaml name then Errors.generic_at_runtime p;
343 let handle_unbound_name genv get_full_pos get_canon (p, name) kind =
344 match get_canon name with
345 | Some canonical ->
346 canonical
347 |> get_full_pos
348 |> Option.iter ~f:(fun p_canon ->
349 Errors.did_you_mean_naming p name p_canon canonical);
350 (* Recovering from the capitalization error means
351 * returning the name in its canonical form *)
352 p, canonical
353 | None ->
354 (match genv.in_mode with
355 | FileInfo.Mpartial | FileInfo.Mdecl
356 when TypecheckerOptions.assume_php genv.tcopt
357 || name = SN.Classes.cUnknown -> ()
358 | FileInfo.Mphp -> ()
359 | FileInfo.Mstrict -> unbound_name_error genv p name kind
360 | FileInfo.Mpartial | FileInfo.Mdecl ->
361 unbound_name_error genv p name kind
363 p, name
365 let canonicalize genv get_pos get_full_pos get_canon (p, name) kind =
366 (* Get the canonical name to check if the name exists in the heap *)
367 match get_pos name with
368 | Some _ -> p, name
369 | None -> handle_unbound_name genv get_full_pos get_canon (p, name) kind
371 let check_variable_scoping env (p, x) =
372 match SMap.get x !(env.all_locals) with
373 | Some p' -> Errors.different_scope p x p'
374 | None -> ()
376 (* Adds a local variable, without any check *)
377 let add_lvar (_, lenv) (_, name) (p, x) =
378 lenv.locals := SMap.add name (p, x) !(lenv.locals);
381 let add_param env param =
382 let p_name = param.N.param_name in
383 let id = Local_id.get p_name in
384 let p_pos = param.N.param_pos in
385 let () = add_lvar env (p_pos, p_name) (p_pos, id) in
388 (* Defines a new local variable.
389 Side effects:
390 1) if the local is not in the local environment then it is added.
391 Return value: the given position and deduced/created identifier. *)
392 let new_lvar (_, lenv) (p, x) =
393 let lcl = SMap.get x !(lenv.locals) in
394 let ident =
395 match lcl with
396 | Some lcl -> snd lcl
397 | None ->
398 let ident = match SMap.get x !(lenv.pending_locals) with
399 | Some (_, ident) -> ident
400 | None -> Local_id.make x in
401 lenv.all_locals := SMap.add x p !(lenv.all_locals);
402 lenv.locals := SMap.add x (p, ident) !(lenv.locals);
403 ident
405 p, ident
407 (* Defines a new scoped local variable
408 * Side effects:
409 * Always add a new variable in the local environment.
410 * If the variable has been defined already, shadow the previously-defined
411 * variable *)
412 (* TODO: Emit warning if names are getting shadowed T28436131 *)
413 let new_let_local (_, lenv) (p, x) =
414 let ident = Local_id.make x in
415 lenv.all_locals := SMap.add x p !(lenv.all_locals);
416 lenv.let_locals := SMap.add x (p, ident) !(lenv.let_locals);
417 p, ident
419 (* Defines a new local variable for this dollardollar (or reuses
420 * the exiting identifier). *)
421 let found_dollardollar (genv, lenv) p =
422 if not !(lenv.inside_pipe) then
423 Errors.undefined p SN.SpecialIdents.dollardollar;
424 new_lvar (genv, lenv) (p, SN.SpecialIdents.dollardollar)
426 (* Check if dollardollar is defined in the current environment *)
427 let has_dollardollar locals =
428 SMap.get SN.SpecialIdents.dollardollar locals
430 let inside_pipe (_, lenv) =
431 !(lenv.inside_pipe)
433 let new_pending_lvar (_, lenv) (p, x) =
434 match SMap.get x !(lenv.locals), SMap.get x !(lenv.pending_locals) with
435 | None, None ->
436 let y = p, Local_id.make x in
437 lenv.pending_locals := SMap.add x y !(lenv.pending_locals)
438 | _ -> ()
440 let promote_pending_lvar (_, lenv) x =
441 match SMap.get x !(lenv.pending_locals) with
442 | Some (p, ident) ->
443 lenv.locals := SMap.add x (p, ident) !(lenv.locals);
444 lenv.pending_locals := SMap.remove x !(lenv.pending_locals)
445 | None -> ()
447 let handle_undefined_variable (_genv, env) (p, x) =
448 match env.unbound_mode with
449 | UBMErr -> Errors.undefined p x; p, Local_id.make x
450 | UBMFunc f -> f (p, x)
452 (* Function used to name a local variable *)
453 let lvar (genv, env) (p, x) =
454 let p, ident =
455 if SN.Superglobals.is_superglobal x && genv.in_mode = FileInfo.Mpartial
456 then p, Local_id.make x
457 else
458 let lcl = SMap.get x !(env.locals) in
459 match lcl with
460 | Some lcl -> p, snd lcl
461 | None when not !Autocomplete.auto_complete ->
462 check_variable_scoping env (p, x);
463 handle_undefined_variable (genv, env) (p, x)
464 | None -> p, Local_id.tmp()
466 p, ident
468 let let_local (_genv, env) (p, x) =
469 let lcl = SMap.get x !(env.let_locals) in
470 match lcl with
471 | Some lcl -> Some (p, snd lcl)
472 | None -> None
474 let get_name genv get_pos x =
475 lookup genv get_pos x; x
477 (* For dealing with namespace fallback on constants *)
478 let elaborate_and_get_name_with_fallback
479 mk_dep
480 genv
481 (get_pos : string -> FileInfo.pos option) x =
482 let get_name x = get_name genv get_pos x in
483 let fq_x = NS.elaborate_id genv.namespace NS.ElaborateConst x in
484 let need_fallback =
485 genv.namespace.Namespace_env.ns_name <> None &&
486 not (String.contains (snd x) '\\') in
487 let use_fallback =
488 need_fallback &&
489 (* __FILE__, __LINE__ etc *)
490 (string_starts_with (snd x) "__") && (string_ends_with (snd x) "__") in
491 if use_fallback then begin
492 let global_x = (fst x, "\\" ^ (snd x)) in
493 (* Explicitly add dependencies on both of the consts we could be
494 * referring to here. Normally naming doesn't have to deal with
495 * deps at all -- they are added during typechecking just by the
496 * nature of looking up a class or function name. However, we're
497 * flattening namespaces here, and the fallback behavior of
498 * consts means that we might suddenly be referring to a
499 * different const without any change to the callsite at
500 * all. Adding both dependencies explicitly captures this
501 * action-at-a-distance. *)
502 Typing_deps.add_idep genv.droot (mk_dep (snd fq_x));
503 Typing_deps.add_idep genv.droot (mk_dep (snd global_x));
504 let mem (_, s) = get_pos s in
505 match mem fq_x, mem global_x with
506 (* Found in the current namespace *)
507 | Some _, _ -> get_name fq_x
508 (* Found in the global namespace *)
509 | _, Some _ -> get_name global_x
510 (* Not found. Pick the more specific one to error on. *)
511 | None, None -> get_name fq_x
512 end else
513 get_name fq_x
515 (* For dealing with namespace resolution on functions *)
516 let elaborate_and_get_name_with_canonicalized_fallback
517 genv
518 (get_pos : string -> FileInfo.pos option)
519 (get_full_pos : string -> Pos.t option)
520 get_canon x =
521 let get_name x = get_name genv get_pos x in
522 let canonicalize = canonicalize genv get_pos get_full_pos get_canon in
523 let fq_x = NS.elaborate_id genv.namespace NS.ElaborateFun x in
524 let fq_x = canonicalize fq_x `func in
525 get_name fq_x
527 let global_const (genv, _env) x =
528 elaborate_and_get_name_with_fallback
529 (* Same idea as Dep.FunName, see below. *)
530 (fun x -> Typing_deps.Dep.GConstName x)
531 genv
532 (Naming_heap.ConstPosHeap.get)
535 let type_name (genv, _) x ~allow_typedef =
536 (* Generic names are not allowed to shadow class names *)
537 check_no_runtime_generic genv x;
538 let (pos, name) as x = NS.elaborate_id genv.namespace NS.ElaborateClass x in
539 match Naming_heap.TypeIdHeap.get name with
540 | Some (_def_pos, `Class) ->
541 (* Don't let people use strictly internal classes
542 * (except when they are being declared in .hhi files) *)
543 if name = SN.Classes.cHH_BuiltinEnum &&
544 not (string_ends_with (Relative_path.suffix (Pos.filename pos)) ".hhi")
545 then Errors.using_internal_class pos (strip_ns name);
546 pos, name
547 | Some (def_pos, `Typedef) when not allow_typedef ->
548 let full_pos, _ = GEnv.get_full_pos genv.tcopt (def_pos, name) in
549 Errors.unexpected_typedef pos full_pos;
550 pos, name
551 | Some (_def_pos, `Typedef) -> pos, name
552 | None ->
553 handle_unbound_name genv
554 (GEnv.type_pos genv.tcopt)
555 GEnv.type_canon_name x `cls
557 let fun_id (genv, _) x =
558 elaborate_and_get_name_with_canonicalized_fallback
559 genv
560 (Naming_heap.FunPosHeap.get)
561 (GEnv.fun_pos genv.tcopt)
562 GEnv.fun_canon_name
565 let bind_class_member tbl (p, x) =
567 let p' = Hashtbl.find tbl x in
568 Errors.error_name_already_bound x x p p'
569 with Not_found ->
570 Hashtbl.replace tbl x p
572 let bind_class_const (genv, _env) (p, x) =
573 if String.lowercase_ascii x = "class" then Errors.illegal_member_variable_class p;
574 bind_class_member genv.class_consts (p, x)
576 let bind_prop (genv, _env) x =
577 bind_class_member genv.class_props x
580 * Returns the position of the goto label declaration, if it exists.
582 let goto_label (_, { goto_labels; _ }) label =
583 SMap.get label !goto_labels
586 * Adds a goto label and the position of its declaration to the known labels.
588 let new_goto_label (_, { goto_labels; _ }) (pos, label) =
589 goto_labels := SMap.add label pos !goto_labels
592 * Adds a goto target and its reference position to the known targets.
594 let new_goto_target (_, { goto_targets; _ }) (pos, label) =
595 goto_targets := SMap.add label pos !goto_targets
598 * Ensures that goto statements do not reference goto labels that are not
599 * known within the current lenv.
601 let check_goto_references (_, { goto_labels; goto_targets; _ }) =
602 let check_label referenced_label referenced_label_pos =
603 if not (SMap.mem referenced_label !goto_labels) then
604 Errors.goto_label_undefined referenced_label_pos referenced_label in
605 SMap.iter check_label !goto_targets
607 (* Scope, keep the locals, go and name the body, and leave the
608 * local environment intact
610 let scope env f =
611 let _genv, lenv = env in
612 let lenv_copy = !(lenv.locals) in
613 let lenv_pending_copy = !(lenv.pending_locals) in
614 let lenv_scoped_copy = !(lenv.let_locals) in
615 let res = f env in
616 lenv.locals := lenv_copy;
617 lenv.pending_locals := lenv_pending_copy;
618 lenv.let_locals := lenv_scoped_copy;
621 let remove_locals env vars =
622 let _genv, lenv = env in
623 lenv.locals :=
624 List.fold_left vars ~f:(fun l id -> SMap.remove (snd id) l) ~init:!(lenv.locals)
626 let scope_all env f =
627 let _genv, lenv = env in
628 let lenv_all_locals_copy = !(lenv.all_locals) in
629 let res = scope env f in
630 let lenv_all_locals = !(lenv.all_locals) in
631 lenv.all_locals := lenv_all_locals_copy;
632 lenv_all_locals, res
634 (* Add a new lexical scope for block-scoped `let` variables.
635 No other changes in the local environment *)
636 let scope_lexical env f =
637 let _genv, lenv = env in
638 let lenv_scoped_copy = !(lenv.let_locals) in
639 let res = f env in
640 lenv.let_locals := lenv_scoped_copy;
643 (* Copy the let locals from lenv1 to lenv2 *)
644 let copy_let_locals (_genv1, lenv1) (_genv2, lenv2) =
645 let let_locals_1 = !(lenv1.let_locals) in
646 lenv2.let_locals := let_locals_1
648 let extend_all_locals (_genv, lenv) more_locals =
649 lenv.all_locals := SMap.union more_locals !(lenv.all_locals)
651 (** Sets up the environment so that naming can be done on the RHS of a
652 * pipe expression. It returns the identity of the $$ in the RHS and the
653 * named RHS. The steps are as follows:
654 * - Removes the $$ from the local env
655 * - Name the RHS scope
656 * - Restore the binding of $$ in the local env (if it was bound).
658 * This will append an error if $$ was not used in the RHS.
660 * The inside_pipe flag is also set before the naming and restored afterwards.
661 * *)
662 let pipe_scope env name_e2 =
663 let _, lenv = env in
664 let outer_pipe_var_opt =
665 SMap.get SN.SpecialIdents.dollardollar !(lenv.locals) in
666 let inner_locals = SMap.remove SN.SpecialIdents.dollardollar
667 !(lenv.locals) in
668 lenv.locals := inner_locals;
669 lenv.inside_pipe := true;
670 (** Name the RHS of the pipe expression. During this naming, if the $$ from
671 * this pipe is used, it will be added to the locals. *)
672 let e2 = name_e2 env in
673 let pipe_var_ident =
674 match SMap.get SN.SpecialIdents.dollardollar !(lenv.locals) with
675 | None ->
676 Errors.dollardollar_unused (fst e2);
677 (** The $$ lvar should be named when it is encountered inside e2,
678 * but we've now discovered it wasn't used at all.
679 * Create an ID here so we can keep going. *)
680 Local_id.make SN.SpecialIdents.dollardollar
681 | Some (_, x) -> x
683 let restored_locals = SMap.remove SN.SpecialIdents.dollardollar
684 !(lenv.locals) in
685 (match outer_pipe_var_opt with
686 | None -> begin
687 lenv.locals := restored_locals;
688 lenv.inside_pipe := false;
690 | Some outer_pipe_var -> begin
691 let restored_locals = SMap.add SN.SpecialIdents.dollardollar
692 outer_pipe_var restored_locals in
693 lenv.locals := restored_locals;
694 end);
695 pipe_var_ident, e2
699 (*****************************************************************************)
700 (* Helpers *)
701 (*****************************************************************************)
703 (* Alok is constantly complaining that in partial mode,
704 * he forgets to bind a type parameter, for example T,
705 * and because partial assumes T is just a class that lives
706 * in PHP land there is no error message.
707 * So to help him, I am adding a rule that if
708 * the class name starts with a T and is only 2 characters
709 * it is considered a type variable. You will not be able to
710 * define a class T in php land in this scheme ... But it is a bad
711 * name for a class anyway.
713 let is_alok_type_name (_, x) = String.length x <= 2 && x.[0] = 'T'
715 let check_constraint (_, (pos, name), _) =
716 (* TODO refactor this in a separate module for errors *)
717 if String.lowercase_ascii name = "this"
718 then Errors.this_reserved pos
719 else if name.[0] <> 'T' then Errors.start_with_T pos
721 let check_repetition s param =
722 let x = snd param.param_id in
723 if SSet.mem x s
724 then Errors.already_bound (fst param.param_id) x;
725 if x <> SN.SpecialIdents.placeholder then SSet.add x s else s
727 let convert_shape_name env = function
728 | SFlit (pos, s) -> (pos, SFlit (pos, s))
729 | SFclass_const (x, (pos, y)) ->
730 let class_name =
731 if (snd x) = SN.Classes.cSelf then
732 match (fst env).current_cls with
733 | Some (cid, _) -> cid
734 | None -> Errors.self_outside_class pos; (pos, SN.Classes.cUnknown)
735 else Env.type_name env x ~allow_typedef:false in
736 (pos, SFclass_const (class_name, (pos, y)))
738 let arg_unpack_unexpected = function
739 | [] -> ()
740 | (pos, _) :: _ -> Errors.naming_too_few_arguments pos; ()
742 module type GetLocals = sig
743 val stmt : TypecheckerOptions.t -> Namespace_env.env * Pos.t SMap.t ->
744 Ast.stmt -> Namespace_env.env * Pos.t SMap.t
745 val lvalue : TypecheckerOptions.t -> Namespace_env.env * Pos.t SMap.t ->
746 Ast.expr -> Namespace_env.env * Pos.t SMap.t
749 (* This was made a functor due to the awkward nature of how our naming
750 * code is structured.
752 * Naming is called both in the decl phase and type-check phase. In the
753 * decl phase it's mostly used to construct things that do not belong in
754 * function bodies; examples include classes, their member fields, and
755 * global constants. This part of naming is entirely self-contained; it
756 * only uses the data from the AST in the current file, and does not need
757 * to cross-reference decl type data from other files.
759 * In the type-check phase, Naming is invoked again, this time to name the
760 * bodies of functions. Now it requires decl type data in order to know
761 * which function calls are marked as `noreturn`, because this affects
762 * which local variables are considered to be defined at the end of a
763 * statement.
765 * So decl depends on naming, but naming also depends on decl, creating
766 * a circular dependency. The obvious solution would be to split it into
767 * two, but this is nontrivial because decl-phase naming also does some
768 * naming of expressions -- for example, constant initializers and default
769 * parameter values have them. Of course, none of these expressions can
770 * actually contain local variables, but our code is not written in a way
771 * that the OCaml type system can understand that. So as a hacky solution,
772 * I'm parameterizing GetLocals so that it is a no-op in the decl phase
773 * but can be properly instantiated with Typing_get_locals in the typing
774 * phase.
776 module Make (GetLocals : GetLocals) = struct
777 let hacksperimental (genv, _) =
778 let tcopt = genv.tcopt in
779 TypecheckerOptions.experimental_feature_enabled tcopt
780 GlobalOptions.tco_hacksperimental
782 (************************************************************************)
783 (* Naming of type hints *)
784 (************************************************************************)
785 let rec hint
786 ?(is_static_var=false)
787 ?(forbid_this=false)
788 ?(allow_retonly=false)
789 ?(allow_typedef=true)
790 ?(allow_wildcard=false)
791 ?(in_where_clause=false)
792 ?(tp_depth=0)
793 env (p, h) =
794 p, hint_
795 ~forbid_this
796 ~allow_retonly
797 ~allow_typedef
798 ~allow_wildcard
799 ~in_where_clause
800 ~tp_depth
801 is_static_var env h
803 and shape_field_to_shape_field_info env { sf_optional; sf_name=_; sf_hint } =
805 N.sfi_optional = sf_optional;
806 sfi_hint = hint env sf_hint;
809 and ast_shape_info_to_nast_shape_info
811 { si_allows_unknown_fields; si_shape_field_list } =
812 let f fdm shape_field =
813 let pos, name = convert_shape_name env shape_field.sf_name in
814 if ShapeMap.mem name fdm
815 then Errors.fd_name_already_bound pos;
816 ShapeMap.add
817 name (shape_field_to_shape_field_info env shape_field) fdm in
818 let nsi_field_map =
819 List.fold_left si_shape_field_list ~init:ShapeMap.empty ~f in
821 nsi_allows_unknown_fields=si_allows_unknown_fields;
822 nsi_field_map
825 and hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
826 ~in_where_clause ?(tp_depth=0)
827 is_static_var env x =
828 let hint =
829 hint ~is_static_var ~forbid_this ~allow_typedef ~allow_wildcard in
830 match x with
831 | Htuple hl ->
832 N.Htuple (List.map hl (hint ~allow_retonly env))
833 | Hoption h ->
834 (* void/noreturn are permitted for Typing.option_return_only_typehint *)
835 N.Hoption (hint ~allow_retonly env h)
836 | Hsoft h ->
837 let h = hint ~allow_retonly env h
838 in snd h
839 | Hfun (is_coroutine, hl, kl, variadic_hint, h) ->
840 let is_reactive = false in
841 let variadic_hint = match variadic_hint with
842 | Hvariadic Some (h) -> N.Hvariadic (Some (hint env h))
843 | Hvariadic None -> N.Hvariadic (None)
844 | Hnon_variadic -> N.Hnon_variadic in
845 N.Hfun (is_reactive, is_coroutine, List.map hl (hint env), kl, variadic_hint,
846 hint ~allow_retonly:true env h)
847 (* Special case for Rx<function> *)
848 | Happly ((_, "Rx"), [(_, Hfun (is_coroutine, hl, kl, variadic_hint, h))]) ->
849 let is_reactive = true in
850 let variadic_hint = match variadic_hint with
851 | Hvariadic Some (h) -> N.Hvariadic (Some (hint env h))
852 | Hvariadic None -> N.Hvariadic (None)
853 | Hnon_variadic -> N.Hnon_variadic in
854 N.Hfun (is_reactive, is_coroutine, List.map hl (hint env), kl, variadic_hint,
855 hint ~allow_retonly:true env h)
856 | Happly ((p, _x) as id, hl) ->
857 let hint_id =
858 hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
859 env is_static_var id
860 hl in
861 (match hint_id with
862 | N.Hprim _ | N.Hmixed | N.Hnonnull ->
863 if hl <> [] then Errors.unexpected_type_arguments p
864 | _ -> ()
866 hint_id
867 | Haccess ((pos, root_id) as root, id, ids) ->
868 let root_ty =
869 match root_id with
870 | x when x = SN.Classes.cSelf ->
871 (match (fst env).current_cls with
872 | None ->
873 Errors.self_outside_class pos;
874 N.Hany
875 | Some (cid, _) ->
876 N.Happly (cid, [])
878 | x when x = SN.Classes.cStatic || x = SN.Classes.cParent ->
879 Errors.invalid_type_access_root root; N.Hany
880 | _ ->
881 let tconst_on_generics_enabled =
882 TypecheckerOptions.experimental_feature_enabled
883 (fst env).tcopt
884 TypecheckerOptions.experimental_tconst_on_generics in
885 let h =
886 hint_id ~forbid_this ~allow_retonly
887 ~allow_typedef ~allow_wildcard:false ~tp_depth env is_static_var root [] in
888 (match h with
889 | N.Hthis | N.Happly _ as h -> h
890 | N.Habstr _ when in_where_clause && tconst_on_generics_enabled ->
892 | _ -> Errors.invalid_type_access_root root; N.Hany
895 N.Haccess ((pos, root_ty), id :: ids)
896 | Hshape ast_shape_info ->
897 N.Hshape (ast_shape_info_to_nast_shape_info env ast_shape_info)
899 and hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
900 env is_static_var (p, x as id) hl =
901 let params = (fst env).type_params in
902 if is_alok_type_name id && not (SMap.mem x params)
903 then Errors.typeparam_alok id;
904 if is_static_var && SMap.mem x params
905 then Errors.generic_class_var (fst id);
906 (* some common Xhp screw ups *)
907 if (x = "Xhp") || (x = ":Xhp") || (x = "XHP")
908 then Errors.disallowed_xhp_type p x;
909 match try_castable_hint ~forbid_this ~allow_wildcard ~tp_depth env p x hl with
910 | Some h -> h
911 | None -> begin
912 match x with
913 | x when x = SN.Typehints.wildcard && allow_wildcard && tp_depth = 1 ->
914 if hl <> [] then
915 (Errors.tparam_with_tparam p x;
916 N.Hany)
917 else
918 N.Happly(id, [])
919 | x when x = SN.Typehints.wildcard ->
920 Errors.wildcard_disallowed p;
921 N.Hany
922 | x when x.[0] = '\\' &&
923 ( x = ("\\"^SN.Typehints.void)
924 || x = ("\\"^SN.Typehints.noreturn)
925 || x = ("\\"^SN.Typehints.int)
926 || x = ("\\"^SN.Typehints.bool)
927 || x = ("\\"^SN.Typehints.float)
928 || x = ("\\"^SN.Typehints.num)
929 || x = ("\\"^SN.Typehints.string)
930 || x = ("\\"^SN.Typehints.resource)
931 || x = ("\\"^SN.Typehints.mixed)
932 || x = ("\\"^SN.Typehints.nonnull)
933 || x = ("\\"^SN.Typehints.array)
934 || x = ("\\"^SN.Typehints.arraykey)
935 || x = ("\\"^SN.Typehints.integer)
936 || x = ("\\"^SN.Typehints.boolean)
937 || x = ("\\"^SN.Typehints.double)
938 || x = ("\\"^SN.Typehints.real)
939 ) ->
940 Errors.primitive_toplevel p;
941 N.Hany
942 | x when x = SN.Typehints.void && allow_retonly -> N.Hprim N.Tvoid
943 | x when x = SN.Typehints.void ->
944 if TypecheckerOptions.experimental_feature_enabled
945 (fst env).tcopt
946 TypecheckerOptions.experimental_void_is_type_of_null
947 then N.Hprim N.Tvoid
948 else (Errors.return_only_typehint p `void; N.Hany)
949 | x when x = SN.Typehints.noreturn && allow_retonly -> N.Hprim N.Tnoreturn
950 | x when x = SN.Typehints.noreturn ->
951 Errors.return_only_typehint p `noreturn;
952 N.Hany
953 | x when x = SN.Typehints.num -> N.Hprim N.Tnum
954 | x when x = SN.Typehints.resource -> N.Hprim N.Tresource
955 | x when x = SN.Typehints.arraykey -> N.Hprim N.Tarraykey
956 | x when x = SN.Typehints.mixed -> N.Hmixed
957 | x when x = SN.Typehints.nonnull -> N.Hnonnull
958 | x when x = SN.Typehints.dynamic -> N.Hdynamic
959 | x when x = SN.Typehints.this && not forbid_this ->
960 if hl != []
961 then Errors.this_no_argument p;
962 (match (fst env).current_cls with
963 | None ->
964 Errors.this_hint_outside_class p;
965 N.Hany
966 | Some _c ->
967 N.Hthis
969 | x when x = SN.Typehints.this ->
970 (match (fst env).current_cls with
971 | None ->
972 Errors.this_hint_outside_class p
973 | Some _ ->
974 Errors.this_type_forbidden p
976 N.Hany
977 | x when x = SN.Classes.cClassname && (List.length hl) <> 1 ->
978 Errors.classname_param p;
979 N.Hprim N.Tstring
980 | _ when String.lowercase_ascii x = SN.Typehints.this ->
981 Errors.lowercase_this p x;
982 N.Hany
983 | _ when SMap.mem x params ->
984 if hl <> [] then
985 Errors.tparam_with_tparam p x;
986 N.Habstr x
987 | _ ->
988 let name = Env.type_name env id ~allow_typedef in
989 (* Note that we are intentionally setting allow_typedef to `true` here.
990 * In general, generics arguments can be typedefs -- there is no
991 * runtime restriction. *)
992 N.Happly (name, hintl ~allow_wildcard ~forbid_this ~allow_typedef:true
993 ~allow_retonly:true ~tp_depth:(tp_depth+1) env hl)
996 (* Hints that are valid both as casts and type annotations. Neither
997 * casts nor annotations are a strict subset of the other: For
998 * instance, 'object' is not a valid annotation. Thus callers will
999 * have to handle the remaining cases. *)
1000 and try_castable_hint ?(forbid_this=false) ?(allow_wildcard=false) ~tp_depth env p x hl =
1001 let hint = hint ~forbid_this ~tp_depth:(tp_depth+1) ~allow_wildcard ~allow_retonly:false in
1002 let canon = String.lowercase_ascii x in
1003 let opt_hint = match canon with
1004 | nm when nm = SN.Typehints.int -> Some (N.Hprim N.Tint)
1005 | nm when nm = SN.Typehints.bool -> Some (N.Hprim N.Tbool)
1006 | nm when nm = SN.Typehints.float -> Some (N.Hprim N.Tfloat)
1007 | nm when nm = SN.Typehints.string -> Some (N.Hprim N.Tstring)
1008 | nm when nm = SN.Typehints.array ->
1009 let tcopt = (fst env).tcopt in
1010 let array_typehints_disallowed =
1011 TypecheckerOptions.disallow_array_typehint tcopt in
1012 if array_typehints_disallowed
1013 then Errors.array_typehints_disallowed p;
1014 Some (match hl with
1015 | [] -> N.Harray (None, None)
1016 | [val_] -> N.Harray (Some (hint env val_), None)
1017 | [key_; val_] ->
1018 N.Harray (Some (hint env key_), Some (hint env val_))
1019 | _ -> Errors.too_many_type_arguments p; N.Hany
1021 | nm when nm = SN.Typehints.darray ->
1022 Some (match hl with
1023 | [] ->
1024 if (fst env).in_mode = FileInfo.Mstrict then
1025 Errors.too_few_type_arguments p;
1026 N.Hdarray ((p, N.Hany), (p, N.Hany))
1027 | [_] -> Errors.too_few_type_arguments p; N.Hany
1028 | [key_; val_] -> N.Hdarray (hint env key_, hint env val_)
1029 | _ -> Errors.too_many_type_arguments p; N.Hany)
1030 | nm when nm = SN.Typehints.varray ->
1031 Some (match hl with
1032 | [] ->
1033 if (fst env).in_mode = FileInfo.Mstrict then
1034 Errors.too_few_type_arguments p;
1035 N.Hvarray (p, N.Hany)
1036 | [val_] -> N.Hvarray (hint env val_)
1037 | _ -> Errors.too_many_type_arguments p; N.Hany)
1038 | nm when nm = SN.Typehints.varray_or_darray ->
1039 Some (match hl with
1040 | [] ->
1041 if (fst env).in_mode = FileInfo.Mstrict then
1042 Errors.too_few_type_arguments p;
1043 N.Hvarray_or_darray (p, N.Hany)
1044 | [val_] -> N.Hvarray_or_darray (hint env val_)
1045 | _ -> Errors.too_many_type_arguments p; N.Hany)
1046 | nm when nm = SN.Typehints.integer ->
1047 Errors.primitive_invalid_alias p nm SN.Typehints.int;
1048 Some (N.Hprim N.Tint)
1049 | nm when nm = SN.Typehints.boolean ->
1050 Errors.primitive_invalid_alias p nm SN.Typehints.bool;
1051 Some (N.Hprim N.Tbool)
1052 | nm when nm = SN.Typehints.double || nm = SN.Typehints.real ->
1053 Errors.primitive_invalid_alias p nm SN.Typehints.float;
1054 Some (N.Hprim N.Tfloat)
1055 | _ -> None
1057 let () = match opt_hint with
1058 | Some _ when canon <> x -> Errors.primitive_invalid_alias p x canon
1059 | _ -> ()
1060 in opt_hint
1062 and constraint_ ?(forbid_this=false) env (ck, h) = ck, hint ~forbid_this env h
1064 and hintl ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth env l =
1065 List.map l
1066 (hint ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth env)
1067 and hintl_funcall env l =
1068 hintl
1069 ~allow_wildcard:true
1070 ~forbid_this:false
1071 ~allow_typedef:true
1072 ~allow_retonly:true
1073 ~tp_depth:1
1077 (**************************************************************************)
1078 (* All the methods and static methods of an interface are "implicitly"
1079 * declared as abstract
1081 (**************************************************************************)
1083 let add_abstract m = {m with N.m_abstract = true}
1085 let add_abstractl methods = List.map methods add_abstract
1087 let interface c constructor methods smethods =
1088 if c.c_kind <> Cinterface then constructor, methods, smethods else
1089 let constructor = Option.map constructor add_abstract in
1090 let methods = add_abstractl methods in
1091 let smethods = add_abstractl smethods in
1092 constructor, methods, smethods
1094 (**************************************************************************)
1095 (* Checking for collision on method names *)
1096 (**************************************************************************)
1098 let check_method acc { N.m_name = (p, x); _ } =
1099 if SSet.mem x acc
1100 then Errors.method_name_already_bound p x;
1101 SSet.add x acc
1103 let check_name_collision methods =
1104 ignore (List.fold_left methods ~init:SSet.empty ~f:check_method)
1106 (**************************************************************************)
1107 (* Checking for shadowing of method type parameters *)
1108 (**************************************************************************)
1110 let check_method_tparams class_tparam_names { N.m_tparams = tparams; _ } =
1111 List.iter tparams begin fun (_, (p,x),_) ->
1112 List.iter class_tparam_names
1113 (fun (pc,xc) -> if (x = xc) then Errors.shadowed_type_param p pc x)
1116 let check_tparams_constructor class_tparam_names constructor =
1117 match constructor with
1118 | None -> ()
1119 | Some constr -> check_method_tparams class_tparam_names constr
1121 let check_tparams_shadow class_tparam_names methods =
1122 List.iter methods (check_method_tparams class_tparam_names)
1124 let check_break_continue_level p level_opt =
1125 if Option.is_some level_opt
1126 then Errors.break_continue_n_not_supported p
1128 let ensure_name_not_dynamic env e err =
1129 match e with
1130 | (_, (Id _ | Lvar _)) -> ()
1131 | (p, _) ->
1132 if (fst env).in_mode = FileInfo.Mstrict
1133 then err p
1135 (* Naming of a class *)
1136 let rec class_ nenv c =
1137 let constraints = make_constraints c.c_tparams in
1138 let env = Env.make_class_env nenv constraints c in
1139 (* Checking for a code smell *)
1140 List.iter c.c_tparams check_constraint;
1141 let name = Env.type_name env c.c_name ~allow_typedef:false in
1142 let smethods =
1143 List.fold_right c.c_body ~init:[] ~f:(class_static_method env) in
1144 let sprops = List.fold_right c.c_body ~init:[] ~f:(class_prop_static env) in
1145 let attrs = user_attributes env c.c_user_attributes in
1146 let const = (Attributes.find SN.UserAttributes.uaConst attrs) in
1147 let props = List.fold_right c.c_body ~init:[] ~f:(class_prop ~const env) in
1148 let parents =
1149 List.map c.c_extends
1150 (hint ~allow_retonly:false ~allow_typedef:false env) in
1151 let parents = match c.c_kind with
1152 (* Make enums implicitly extend the BuiltinEnum class in order to provide
1153 * utility methods. *)
1154 | Cenum ->
1155 let pos = fst name in
1156 let enum_type = pos, N.Happly (name, []) in
1157 let parent =
1158 pos, N.Happly ((pos, Naming_special_names.Classes.cHH_BuiltinEnum),
1159 [enum_type]) in
1160 parent::parents
1161 | _ -> parents in
1162 let methods = List.fold_right c.c_body ~init:[] ~f:(class_method env) in
1163 let uses = List.fold_right c.c_body ~init:[] ~f:(class_use env) in
1164 let xhp_attr_uses =
1165 List.fold_right c.c_body ~init:[] ~f:(xhp_attr_use env) in
1166 let xhp_category =
1167 Option.value ~default:[] @@
1168 List.fold_right c.c_body ~init:None ~f:(xhp_category env) in
1169 let req_implements, req_extends = List.fold_right c.c_body
1170 ~init:([], []) ~f:(class_require env c.c_kind) in
1171 (* Setting a class type parameters constraint to the 'this' type is weird
1172 * so lets forbid it for now.
1174 let tparam_l = type_paraml ~forbid_this:true env c.c_tparams in
1175 let consts = List.fold_right ~f:(class_const env) c.c_body ~init:[] in
1176 let typeconsts =
1177 List.fold_right ~f:(class_typeconst env) c.c_body ~init:[] in
1178 let implements = List.map c.c_implements
1179 (hint ~allow_retonly:false ~allow_typedef:false env) in
1180 let constructor = List.fold_left ~f:(constructor env) ~init:None c.c_body in
1181 let constructor, methods, smethods =
1182 interface c constructor methods smethods in
1183 let class_tparam_names = List.map c.c_tparams (fun (_, x,_) -> x) in
1184 let enum = Option.map c.c_enum (enum_ env) in
1185 check_tparams_constructor class_tparam_names constructor;
1186 check_name_collision methods;
1187 check_tparams_shadow class_tparam_names methods;
1188 check_name_collision smethods;
1189 check_tparams_shadow class_tparam_names smethods;
1190 let named_class =
1191 { N.c_annotation = ();
1192 N.c_mode = c.c_mode;
1193 N.c_final = c.c_final;
1194 N.c_is_xhp = c.c_is_xhp;
1195 N.c_kind = c.c_kind;
1196 N.c_name = name;
1197 N.c_tparams = (tparam_l, constraints);
1198 N.c_extends = parents;
1199 N.c_uses = uses;
1200 N.c_xhp_attr_uses = xhp_attr_uses;
1201 N.c_xhp_category = xhp_category;
1202 N.c_req_extends = req_extends;
1203 N.c_req_implements = req_implements;
1204 N.c_implements = implements;
1205 N.c_consts = consts;
1206 N.c_typeconsts = typeconsts;
1207 N.c_static_vars = sprops;
1208 N.c_vars = props;
1209 N.c_constructor = constructor;
1210 N.c_static_methods = smethods;
1211 N.c_methods = methods;
1212 N.c_user_attributes = attrs;
1213 N.c_enum = enum
1216 named_class
1218 and user_attributes env attrl =
1219 let seen = Hashtbl.create 0 in
1220 let validate_seen = begin fun ua_name ->
1221 let pos, name = ua_name in
1222 let existing_attr_pos =
1223 try Some (Hashtbl.find seen name)
1224 with Not_found -> None
1225 in (match existing_attr_pos with
1226 | Some p -> Errors.duplicate_user_attribute ua_name p; false
1227 | None -> Hashtbl.add seen name pos; true
1229 end in
1230 List.fold_left attrl ~init:[] ~f:begin fun acc {ua_name; ua_params} ->
1231 if not (validate_seen ua_name) then acc
1232 else let attr = {
1233 N.ua_name = ua_name;
1234 N.ua_params = List.map ua_params (expr env)
1235 } in
1236 attr :: acc
1239 and xhp_attribute_decl env h cv is_required maybe_enum =
1240 let p, (_, id), default = cv in
1241 if is_required && Option.is_some default then
1242 Errors.xhp_required_with_default p id;
1243 let h = (match maybe_enum with
1244 | Some (pos, _optional, items) ->
1245 let contains_int = List.exists items begin function
1246 | _, Int _ -> true
1247 | _ -> false
1248 end in
1249 let contains_str = List.exists items begin function
1250 | _, String _ | _, String2 _ -> true
1251 | _ -> false
1252 end in
1253 if contains_int && not contains_str then
1254 Some (pos, Happly ((pos, "int"), []))
1255 else if not contains_int && contains_str then
1256 Some (pos, Happly ((pos, "string"), []))
1257 else
1258 (* If the list was empty, or if there was a mix of
1259 ints and strings, then fallback to mixed *)
1260 Some (pos, Happly ((pos, "mixed"), []))
1261 | _ -> h) in
1262 let h = (match h with
1263 | Some (p, ((Hoption _) as x)) -> begin
1264 (* `null` has special meaning in XHP and a required attribute cannot
1265 * actually be nullable *)
1266 if is_required then Errors.xhp_optional_required_attr p id;
1267 Some (p, x)
1269 | Some (p, ((Happly ((_, "mixed"), [])) as x)) -> Some (p, x)
1270 | Some (p, h) ->
1271 (* If a non-nullable attribute is not marked as "@required"
1272 AND it does not have a non-null default value, make the
1273 typehint nullable for now *)
1274 if (is_required ||
1275 (match default with
1276 | None -> false
1277 | Some (_, Null) -> false
1278 | Some _ -> true))
1279 then Some (p, h)
1280 else Some (p, Hoption (p, h))
1281 | None -> None) in
1282 let h = Option.map h (hint env) in
1283 let cv = class_prop_ env cv in
1284 fill_prop [] h cv
1286 and enum_ env e =
1287 { N.e_base = hint env e.e_base;
1288 N.e_constraint = Option.map e.e_constraint (hint env);
1291 and type_paraml ?(forbid_this = false) env tparams =
1292 let _, ret = List.fold_left tparams ~init:(SMap.empty, [])
1293 ~f:(fun (seen, tparaml) ((_, (p, name), _) as tparam) ->
1294 match SMap.get name seen with
1295 | None ->
1296 SMap.add name p seen, (type_param ~forbid_this env tparam)::tparaml
1297 | Some pos ->
1298 Errors.shadowed_type_param p pos name;
1299 seen, tparaml
1302 List.rev ret
1304 and type_param ~forbid_this env (variance, param_name, cstr_list) =
1305 variance,
1306 param_name,
1307 List.map cstr_list (constraint_ ~forbid_this env)
1309 and type_where_constraints env locl_cstrl =
1310 List.map locl_cstrl (fun (h1, ck, h2) ->
1311 let ty1 = hint ~in_where_clause:true env h1 in
1312 let ty2 = hint ~in_where_clause:true env h2 in
1313 (ty1, ck, ty2))
1315 and class_use env x acc =
1316 match x with
1317 | Attributes _ -> acc
1318 | Const _ -> acc
1319 | AbsConst _ -> acc
1320 | ClassUse h ->
1321 hint ~allow_typedef:false env h :: acc
1322 | ClassUseAlias _ -> acc
1323 | ClassUsePrecedence _ -> acc
1324 | XhpAttrUse _ -> acc
1325 | ClassTraitRequire _ -> acc
1326 | ClassVars _ -> acc
1327 | XhpAttr _ -> acc
1328 | XhpCategory _ -> acc
1329 | XhpChild _ -> acc
1330 | Method _ -> acc
1331 | TypeConst _ -> acc
1333 and xhp_attr_use env x acc =
1334 match x with
1335 | Attributes _ -> acc
1336 | Const _ -> acc
1337 | AbsConst _ -> acc
1338 | ClassUse _ -> acc
1339 | ClassUseAlias _ -> acc
1340 | ClassUsePrecedence _ -> acc
1341 | XhpAttrUse h ->
1342 hint ~allow_typedef:false env h :: acc
1343 | ClassTraitRequire _ -> acc
1344 | ClassVars _ -> acc
1345 | XhpAttr _ -> acc
1346 | XhpCategory _ -> acc
1347 | XhpChild _ -> acc
1348 | Method _ -> acc
1349 | TypeConst _ -> acc
1351 and xhp_category _env x acc =
1352 match x with
1353 | Attributes _ -> acc
1354 | Const _ -> acc
1355 | AbsConst _ -> acc
1356 | ClassUse _ -> acc
1357 | ClassUseAlias _ -> acc
1358 | ClassUsePrecedence _ -> acc
1359 | XhpAttrUse _ -> acc
1360 | ClassTraitRequire _ -> acc
1361 | ClassVars _ -> acc
1362 | XhpAttr _ -> acc
1363 | XhpCategory (_, cs) ->
1364 (match acc with
1365 | Some _ -> Errors.multiple_xhp_category (fst (List.hd_exn cs)); acc
1366 | None -> Some cs)
1367 | XhpChild _ -> acc
1368 | Method _ -> acc
1369 | TypeConst _ -> acc
1371 and class_require env c_kind x acc =
1372 match x with
1373 | Attributes _ -> acc
1374 | Const _ -> acc
1375 | AbsConst _ -> acc
1376 | ClassUse _ -> acc
1377 | ClassUseAlias _ -> acc
1378 | ClassUsePrecedence _ -> acc
1379 | XhpAttrUse _ -> acc
1380 | ClassTraitRequire (MustExtend, h)
1381 when c_kind <> Ast.Ctrait && c_kind <> Ast.Cinterface ->
1382 let () = Errors.invalid_req_extends (fst h) in
1384 | ClassTraitRequire (MustExtend, h) ->
1385 let acc_impls, acc_exts = acc in
1386 (acc_impls, hint ~allow_typedef:false env h :: acc_exts)
1387 | ClassTraitRequire (MustImplement, h) when c_kind <> Ast.Ctrait ->
1388 let () = Errors.invalid_req_implements (fst h) in
1390 | ClassTraitRequire (MustImplement, h) ->
1391 let acc_impls, acc_exts = acc in
1392 (hint ~allow_typedef:false env h :: acc_impls, acc_exts)
1393 | ClassVars _ -> acc
1394 | XhpAttr _ -> acc
1395 | XhpCategory _ -> acc
1396 | XhpChild _ -> acc
1397 | Method _ -> acc
1398 | TypeConst _ -> acc
1400 and constructor env acc = function
1401 | Attributes _ -> acc
1402 | Const _ -> acc
1403 | AbsConst _ -> acc
1404 | ClassUse _ -> acc
1405 | ClassUseAlias _ -> acc
1406 | ClassUsePrecedence _ -> acc
1407 | XhpAttrUse _ -> acc
1408 | ClassTraitRequire _ -> acc
1409 | ClassVars _ -> acc
1410 | XhpAttr _ -> acc
1411 | XhpCategory _ -> acc
1412 | XhpChild _ -> acc
1413 | Method ({ m_name = (p, name); _ } as m)
1414 when name = SN.Members.__construct ->
1415 (match acc with
1416 | None -> Some (method_ (fst env) m)
1417 | Some _ -> Errors.method_name_already_bound p name; acc)
1418 | Method _ -> acc
1419 | TypeConst _ -> acc
1421 and class_const env x acc =
1422 match x with
1423 | Attributes _ -> acc
1424 | Const (h, l) -> const_defl h env l @ acc
1425 | AbsConst (h, x) -> abs_const_def env h x :: acc
1426 | ClassUse _ -> acc
1427 | ClassUseAlias _ -> acc
1428 | ClassUsePrecedence _ -> acc
1429 | XhpAttrUse _ -> acc
1430 | ClassTraitRequire _ -> acc
1431 | ClassVars _ -> acc
1432 | XhpAttr _ -> acc
1433 | XhpCategory _ -> acc
1434 | XhpChild _ -> acc
1435 | Method _ -> acc
1436 | TypeConst _ -> acc
1438 and class_prop_static env x acc =
1439 match x with
1440 | Attributes _ -> acc
1441 | ClassUse _ -> acc
1442 | ClassUseAlias _ -> acc
1443 | ClassUsePrecedence _ -> acc
1444 | XhpAttrUse _ -> acc
1445 | ClassTraitRequire _ -> acc
1446 | Const _ -> acc
1447 | AbsConst _ -> acc
1448 | ClassVars { cv_kinds = kl; cv_hint = h; cv_names = cvl; _ }
1449 when List.mem kl Static ->
1450 (* Static variables are shared for all classes in the hierarchy.
1451 * This makes the 'this' type completely unsafe as a type for a
1452 * static variable. See test/typecheck/this_tparam_static.php as
1453 * an example of what can occur.
1455 let h = Option.map h (hint ~forbid_this:true ~is_static_var:true env) in
1456 let cvl = List.map cvl (class_prop_ env) in
1457 let cvl = List.map cvl (fill_prop kl h) in
1458 cvl @ acc
1459 | ClassVars _ -> acc
1460 | XhpAttr _ -> acc
1461 | XhpCategory _ -> acc
1462 | XhpChild _ -> acc
1463 | Method _ -> acc
1464 | TypeConst _ -> acc
1466 and class_prop env ?(const = None) x acc =
1467 match x with
1468 | Attributes _ -> acc
1469 | ClassUse _ -> acc
1470 | ClassUseAlias _ -> acc
1471 | ClassUsePrecedence _ -> acc
1472 | XhpAttrUse _ -> acc
1473 | ClassTraitRequire _ -> acc
1474 | Const _ -> acc
1475 | AbsConst _ -> acc
1476 | ClassVars { cv_kinds; cv_hint; cv_names; cv_user_attributes; _ }
1477 when not (List.mem cv_kinds Static) ->
1478 let h = Option.map cv_hint (hint env) in
1479 let cvl = List.map cv_names (class_prop_ env) in
1480 let cvl = List.map cvl (fill_prop cv_kinds h) in
1481 let attrs = user_attributes env cv_user_attributes in
1482 (* if class is __Const, make all member fields __Const *)
1483 let attrs = match const with
1484 | Some c -> if not (Attributes.mem SN.UserAttributes.uaConst attrs)
1485 then c :: attrs else attrs
1486 | None -> attrs in
1487 let cvl = List.map cvl (fun cv -> { cv with N.cv_user_attributes = attrs}) in
1488 cvl @ acc
1489 | ClassVars _ -> acc
1490 | XhpAttr (h, cv, is_required, maybe_enum) ->
1491 (xhp_attribute_decl env h cv is_required maybe_enum) :: acc
1492 | XhpCategory _ -> acc
1493 | XhpChild _ -> acc
1494 | Method _ -> acc
1495 | TypeConst _ -> acc
1497 and class_static_method env x acc =
1498 match x with
1499 | Attributes _ -> acc
1500 | ClassUse _ -> acc
1501 | ClassUseAlias _ -> acc
1502 | ClassUsePrecedence _ -> acc
1503 | XhpAttrUse _ -> acc
1504 | ClassTraitRequire _ -> acc
1505 | Const _ -> acc
1506 | AbsConst _ -> acc
1507 | ClassVars _ -> acc
1508 | XhpAttr _ -> acc
1509 | XhpCategory _ -> acc
1510 | XhpChild _ -> acc
1511 | Method m when snd m.m_name = SN.Members.__construct -> acc
1512 | Method m when List.mem m.m_kind Static -> method_ (fst env) m :: acc
1513 | Method _ -> acc
1514 | TypeConst _ -> acc
1516 and class_method env x acc =
1517 match x with
1518 | Attributes _ -> acc
1519 | ClassUse _ -> acc
1520 | ClassUseAlias _ -> acc
1521 | ClassUsePrecedence _ -> acc
1522 | XhpAttrUse _ -> acc
1523 | ClassTraitRequire _ -> acc
1524 | Const _ -> acc
1525 | AbsConst _ -> acc
1526 | ClassVars _ -> acc
1527 | XhpAttr _ -> acc
1528 | XhpCategory _ -> acc
1529 | XhpChild _ -> acc
1530 | Method m when snd m.m_name = SN.Members.__construct -> acc
1531 | Method m when not (List.mem m.m_kind Static) ->(
1532 match (m.m_name, m.m_params) with
1533 | ( (m_pos, m_name), _::_) when m_name = SN.Members.__clone ->
1534 Errors.clone_too_many_arguments m_pos; acc
1535 | _ -> let genv = fst env in method_ genv m :: acc
1537 | Method _ -> acc
1538 | TypeConst _ -> acc
1540 and class_typeconst env x acc =
1541 match x with
1542 | Attributes _ -> acc
1543 | Const _ -> acc
1544 | AbsConst _ -> acc
1545 | ClassUse _ -> acc
1546 | ClassUseAlias _ -> acc
1547 | ClassUsePrecedence _ -> acc
1548 | XhpAttrUse _ -> acc
1549 | ClassTraitRequire _ -> acc
1550 | ClassVars _ -> acc
1551 | XhpAttr _ -> acc
1552 | XhpCategory _ -> acc
1553 | XhpChild _ -> acc
1554 | Method _ -> acc
1555 | TypeConst t -> typeconst env t :: acc
1557 and check_constant_expr env (pos, e) =
1558 match e with
1559 | Unsafeexpr _ | Id _ | Null | True | False | Int _
1560 | Float _ | String _ -> ()
1561 | Class_const ((_, cls), _)
1562 when (match cls with Id (_, "static") -> false | _ -> true) -> ()
1563 | Unop ((Uplus | Uminus | Utild | Unot), e) -> check_constant_expr env e
1564 | Binop (op, e1, e2) ->
1565 (* Only assignment is invalid *)
1566 (match op with
1567 | Eq _ -> Errors.illegal_constant pos
1568 | _ ->
1569 check_constant_expr env e1;
1570 check_constant_expr env e2)
1571 | Eif (e1, e2, e3) ->
1572 check_constant_expr env e1;
1573 Option.iter e2 (check_constant_expr env);
1574 check_constant_expr env e3
1575 | Array l -> List.iter l (check_afield_constant_expr env)
1576 | Darray l -> List.iter l (fun (e1, e2) ->
1577 check_constant_expr env e1;
1578 check_constant_expr env e2)
1579 | Varray l -> List.iter l (check_constant_expr env)
1580 | Shape fdl ->
1581 (* Only check the values because shape field names are always legal *)
1582 List.iter fdl (fun (_, e) -> check_constant_expr env e)
1583 | Call ((_, Id (_, cn)), _, el, uel) when cn = SN.SpecialFunctions.tuple ->
1584 (* Tuples are not really function calls, they are just parsed that way*)
1585 arg_unpack_unexpected uel;
1586 List.iter el (check_constant_expr env)
1587 | Collection (id, l) ->
1588 let p, cn = NS.elaborate_id ((fst env).namespace) NS.ElaborateClass id in
1589 (* Only vec/keyset/dict are allowed because they are value types *)
1590 (match cn with
1591 | _ when
1592 cn = SN.Collections.cVec
1593 || cn = SN.Collections.cKeyset
1594 || cn = SN.Collections.cDict ->
1595 List.iter l (check_afield_constant_expr env)
1596 | _ -> Errors.illegal_constant p)
1597 | _ -> Errors.illegal_constant pos
1599 and check_afield_constant_expr env = function
1600 | AFvalue e -> check_constant_expr env e
1601 | AFkvalue (e1, e2) ->
1602 check_constant_expr env e1;
1603 check_constant_expr env e2
1605 and constant_expr env e =
1606 let valid_constant_expression = Errors.try_with_error begin fun () ->
1607 check_constant_expr env e;
1608 true
1609 end (fun () -> false) in
1610 if valid_constant_expression then expr env e else fst e, N.Any
1612 and const_defl h env l = List.map l (const_def h env)
1613 and const_def h env (x, e) =
1614 Env.bind_class_const env x;
1615 let h = Option.map h (hint env) in
1616 h, x, Some (constant_expr env e)
1618 and abs_const_def env h x =
1619 Env.bind_class_const env x;
1620 let h = Option.map h (hint env) in
1621 h, x, None
1623 and class_prop_ env (_, x, e) =
1624 Env.bind_prop env x;
1625 let e = Option.map e (expr env) in
1626 (* If the user has not provided a value, we initialize the member variable
1627 * ourselves to a value of type Tany. Classes might inherit from our decl
1628 * mode class that are themselves not in decl, and there's no way to figure
1629 * out what variables are initialized in a decl class without typechecking
1630 * its initalizers and constructor, which we don't want to do, so just
1631 * assume we're covered. *)
1632 let e =
1633 if (fst env).in_mode = FileInfo.Mdecl && e = None
1634 then Some (fst x, N.Any)
1635 else e
1637 let is_xhp = try ((String.sub (snd x) 0 1) = ":") with Invalid_argument _ -> false
1639 { N.cv_final = false;
1640 N.cv_is_xhp = is_xhp;
1641 N.cv_visibility = N.Public;
1642 N.cv_type = None;
1643 N.cv_id = x;
1644 N.cv_expr = e;
1645 N.cv_user_attributes = [];
1648 and fill_prop kl ty x =
1649 let x = { x with N.cv_type = ty } in
1650 List.fold_left kl ~init:x ~f:begin fun x k ->
1651 (* There is no field Static, they are dissociated earlier.
1652 An abstract class variable doesn't make sense.
1654 match k with
1655 | Final -> { x with N.cv_final = true }
1656 | Static -> x
1657 | Abstract -> x
1658 | Private -> { x with N.cv_visibility = N.Private }
1659 | Public -> { x with N.cv_visibility = N.Public }
1660 | Protected -> { x with N.cv_visibility = N.Protected }
1663 and typeconst env t =
1664 (* We use the same namespace as constants within the class so we cannot have
1665 * a const and type const with the same name
1667 Env.bind_class_const env t.tconst_name;
1668 let constr = Option.map t.tconst_constraint (hint env) in
1669 let hint_ =
1670 match t.tconst_type with
1671 | None when not t.tconst_abstract ->
1672 Errors.not_abstract_without_typeconst t.tconst_name;
1673 t.tconst_constraint
1674 | Some _h when t.tconst_abstract ->
1675 Errors.abstract_with_typeconst t.tconst_name;
1676 None
1677 | h -> h
1679 let type_ = Option.map hint_ (hint env) in
1680 N.({ c_tconst_name = t.tconst_name;
1681 c_tconst_constraint = constr;
1682 c_tconst_type = type_;
1685 and func_body_had_unsafe env = Env.has_unsafe env
1687 and method_ genv m =
1688 let genv = extend_params genv m.m_tparams in
1689 let env = genv, Env.empty_local UBMErr in
1690 (* Cannot use 'this' if it is a public instance method *)
1691 let variadicity, paraml = fun_paraml env m.m_params in
1692 let contains_visibility = List.exists m.m_kind ~f:(
1693 function
1694 | Private
1695 | Public
1696 | Protected -> true
1697 | _ -> false
1698 ) in
1699 if not contains_visibility then
1700 Errors.method_needs_visibility (fst m.m_name);
1701 let acc = false, false, N.Public in
1702 let final, abs, vis = List.fold_left ~f:kind ~init:acc m.m_kind in
1703 List.iter m.m_tparams check_constraint;
1704 let tparam_l = type_paraml env m.m_tparams in
1705 let where_constraints = type_where_constraints env m.m_constrs in
1706 let ret = Option.map m.m_ret (hint ~allow_retonly:true env) in
1707 let f_kind = m.m_fun_kind in
1708 let body = (match genv.in_mode with
1709 | FileInfo.Mdecl | FileInfo.Mphp ->
1710 N.NamedBody {
1711 N.fnb_nast = [];
1712 fnb_unsafe = true;
1714 | FileInfo.Mstrict | FileInfo.Mpartial ->
1715 N.UnnamedBody {
1716 N.fub_ast = m.m_body;
1717 fub_tparams = m.m_tparams;
1718 fub_namespace = genv.namespace;
1720 ) in
1721 let attrs = user_attributes env m.m_user_attributes in
1722 { N.m_annotation = () ;
1723 N.m_final = final ;
1724 N.m_visibility = vis ;
1725 N.m_abstract = abs ;
1726 N.m_name = m.Ast.m_name;
1727 N.m_tparams = tparam_l ;
1728 N.m_where_constraints = where_constraints ;
1729 N.m_params = paraml ;
1730 N.m_body = body ;
1731 N.m_fun_kind = f_kind ;
1732 N.m_ret = ret ;
1733 N.m_variadic = variadicity ;
1734 N.m_user_attributes = attrs;
1735 N.m_ret_by_ref = m.m_ret_by_ref;
1738 and kind (final, abs, vis) = function
1739 | Final -> true, abs, vis
1740 | Static -> final, abs, vis
1741 | Abstract -> final, true, vis
1742 | Private -> final, abs, N.Private
1743 | Public -> final, abs, N.Public
1744 | Protected -> final, abs, N.Protected
1746 and fun_paraml env l =
1747 let _names = List.fold_left ~f:check_repetition ~init:SSet.empty l in
1748 let variadicity, l = determine_variadicity env l in
1749 variadicity, List.map l (fun_param env)
1751 and determine_variadicity env l =
1752 match l with
1753 | [] -> N.FVnonVariadic, []
1754 | [x] -> (
1755 match x.param_is_variadic, x.param_id with
1756 | false, _ -> N.FVnonVariadic, [x]
1757 (* NOTE: variadic params are removed from the list *)
1758 | true, (p, "...") -> N.FVellipsis p, []
1759 | true, _ -> N.FVvariadicArg (fun_param env x), []
1761 | x :: rl ->
1762 let variadicity, rl = determine_variadicity env rl in
1763 variadicity, x :: rl
1765 and fun_param env param =
1766 let p, name = param.param_id in
1767 let ident = Local_id.get name in
1768 Env.add_lvar env param.param_id (p, ident);
1769 let ty = Option.map param.param_hint (hint env) in
1770 let eopt = Option.map param.param_expr (expr env) in
1771 let _ = match (fst env).in_mode with
1772 | FileInfo.Mstrict when param.param_is_reference ->
1773 Errors.reference_in_strict_mode p;
1774 | _ -> () in
1775 { N.param_annotation = p;
1776 param_hint = ty;
1777 param_is_reference = param.param_is_reference;
1778 param_is_variadic = param.param_is_variadic;
1779 param_pos = p;
1780 param_name = name;
1781 param_expr = eopt;
1782 param_callconv = param.param_callconv;
1783 param_user_attributes = user_attributes env param.param_user_attributes;
1786 and make_constraints paraml =
1787 List.fold_right paraml ~init:SMap.empty
1788 ~f:begin fun (_, (_, x), cstr_list) acc ->
1789 SMap.add x cstr_list acc
1792 and extend_params genv paraml =
1793 let params = List.fold_right paraml ~init:genv.type_params
1794 ~f:begin fun (_, (_, x), cstr_list) acc ->
1795 SMap.add x cstr_list acc
1796 end in
1797 { genv with type_params = params }
1799 and fun_ nenv f =
1800 let tparams = make_constraints f.f_tparams in
1801 let genv = Env.make_fun_decl_genv nenv tparams f in
1802 let lenv = Env.empty_local UBMErr in
1803 let env = genv, lenv in
1804 let where_constraints = type_where_constraints env f.f_constrs in
1805 let h = Option.map f.f_ret (hint ~allow_retonly:true env) in
1806 let variadicity, paraml = fun_paraml env f.f_params in
1807 let x = Env.fun_id env f.f_name in
1808 List.iter f.f_tparams check_constraint;
1809 let f_tparams = type_paraml env f.f_tparams in
1810 let f_kind = f.f_fun_kind in
1811 let body = match genv.in_mode with
1812 | FileInfo.Mdecl | FileInfo.Mphp ->
1813 N.NamedBody {
1814 N.fnb_nast = [];
1815 fnb_unsafe = true;
1817 | FileInfo.Mstrict | FileInfo.Mpartial ->
1818 N.UnnamedBody {
1819 N.fub_ast = f.f_body;
1820 fub_tparams = f.f_tparams;
1821 fub_namespace = f.f_namespace;
1824 let named_fun = {
1825 N.f_annotation = ();
1826 f_mode = f.f_mode;
1827 f_ret = h;
1828 f_name = x;
1829 f_tparams = f_tparams;
1830 f_where_constraints = where_constraints;
1831 f_params = paraml;
1832 f_body = body;
1833 f_fun_kind = f_kind;
1834 f_variadic = variadicity;
1835 f_user_attributes = user_attributes env f.f_user_attributes;
1836 f_ret_by_ref = f.f_ret_by_ref;
1837 } in
1838 named_fun
1840 and cut_unsafe ?(replacement=Noop) env = function
1841 | [] -> []
1842 | (p, Unsafe) :: _ -> Env.set_unsafe env true; [p, replacement]
1843 | (p, Block b) :: rest ->
1844 (p, Block (cut_unsafe ~replacement env b)) ::
1845 (cut_unsafe ~replacement env rest)
1846 | x :: rest -> x :: (cut_unsafe ~replacement env rest)
1848 and get_using_vars e =
1849 match snd e with
1850 | Expr_list using_clauses ->
1851 List.concat_map using_clauses get_using_vars
1852 (* Simple assignment to local of form `$lvar = e` *)
1853 | Binop (Ast.Eq None, (_, Lvar lvar), _) ->
1854 [lvar]
1855 (* Arbitrary expression. This will be assigned to a temporary *)
1856 | _ ->
1859 and stmt env (p, st_ as st) =
1860 match st_ with
1861 | Let (x, h, e) -> let_stmt env x h e
1862 | Block _ -> assert false
1863 | Unsafe -> assert false
1864 | Fallthrough -> N.Fallthrough
1865 | Noop -> N.Noop
1866 | Markup (_, None) -> N.Noop (* ignore markup *)
1867 | Markup (_, Some e) -> N.Expr (expr env e)
1868 | Break level_opt ->
1869 check_break_continue_level p level_opt;
1870 N.Break p
1871 | Continue level_opt ->
1872 check_break_continue_level p level_opt;
1873 N.Continue p
1874 | Throw e -> let terminal = not (fst env).in_try in
1875 N.Throw (terminal, expr env e)
1876 | Return e -> N.Return (p, oexpr env e)
1877 | GotoLabel label -> name_goto_label env label
1878 | Goto label -> name_goto env label
1879 | Static_var el -> N.Static_var (static_varl env el)
1880 | Global_var el -> N.Global_var (global_varl env el)
1881 | If (e, b1, b2) -> if_stmt env st e b1 b2
1882 | Do (b, e) -> do_stmt env b e
1883 | While (e, b) -> while_stmt env e b
1884 | Declare (is_block, e, b) -> declare_stmt env is_block e b
1885 | Using s -> using_stmt env s.us_has_await s.us_expr s.us_block
1886 | For (st1, e, st2, b) -> for_stmt env st1 e st2 b
1887 | Switch (e, cl) -> switch_stmt env st e cl
1888 | Foreach (e, aw, ae, b)-> foreach_stmt env e aw ae b
1889 | Try (b, cl, fb) -> try_stmt env st b cl fb
1890 | Def_inline _ ->
1891 Errors.experimental_feature p "inlined definitions"; N.Expr (p, N.Any)
1892 | Expr (cp, Call ((p, Id (fp, fn)), hl, el, uel))
1893 when fn = SN.SpecialFunctions.invariant ->
1894 (* invariant is subject to a source-code transform in the HHVM
1895 * runtime: the arguments to invariant are lazily evaluated only in
1896 * the case in which the invariant condition does not hold. So:
1898 * invariant_violation(<condition>, <format>, <format_args...>)
1900 * ... is rewritten as:
1902 * if (!<condition>) {
1903 * invariant_violation(<format>, <format_args...>);
1906 (match el with
1907 | [] | [_] ->
1908 Errors.naming_too_few_arguments p;
1909 N.Expr (cp, N.Any)
1910 | (cond_p, cond) :: el ->
1911 let violation = (cp, Call
1912 ((p, Id (fp, "\\"^SN.SpecialFunctions.invariant_violation)), hl, el,
1913 uel)) in
1914 if cond <> False then
1915 let b1, b2 = [p, Expr violation], [p, Noop] in
1916 let cond = cond_p, Unop (Unot, (cond_p, cond)) in
1917 if_stmt env st cond b1 b2
1918 else (* a false <condition> means unconditional invariant_violation *)
1919 N.Expr (expr env violation)
1921 | Expr e -> N.Expr (expr env e)
1923 and let_stmt env x h e =
1924 let e = expr env e in
1925 let h = Option.map h (hint env) in
1926 let x = Env.new_let_local env x in
1927 N.Let (x, h, e)
1929 and if_stmt env st e b1 b2 =
1930 let e = expr env e in
1931 let nsenv = (fst env).namespace in
1932 let _, vars = GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) st in
1933 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1934 let result = Env.scope env (
1935 fun env ->
1936 let all1, b1 = branch env b1 in
1937 let all2, b2 = branch env b2 in
1938 Env.extend_all_locals env all2;
1939 Env.extend_all_locals env all1;
1940 N.If (e, b1, b2)
1941 ) in
1942 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) vars;
1943 result
1945 and do_stmt env b e =
1946 (* lexical block of `do` is extended to the expr of loop termination *)
1947 Env.scope_lexical env (fun env ->
1948 let b = block ~new_scope:false env b in
1949 let e = expr env e in
1950 N.Do (b, e)
1953 and while_stmt env e b =
1954 let e = expr env e in
1955 N.While (e, block env b)
1957 and declare_stmt _env _is_block e _b =
1958 N.Expr (fst e, N.Any)
1960 (* Scoping is essentially that of do: block is always executed *)
1961 and using_stmt env has_await e b =
1962 let vars = get_using_vars e in
1963 let e = expr env e in
1964 let b = block ~new_scope:false env b in
1965 Env.remove_locals env vars;
1966 N.Using (has_await, e, b)
1968 and for_stmt env e1 e2 e3 b =
1969 (* The initialization and condition expression should be in the outer scope,
1970 * as they are always executed. *)
1971 let e1 = expr env e1 in
1972 let e2 = expr env e2 in
1973 Env.scope env (
1974 fun env ->
1975 (* The third expression (iteration step) should have the same scope as the
1976 * block, as it is not always executed. *)
1977 let b = block ~new_scope:false env b in
1978 let e3 = expr env e3 in
1979 N.For (e1, e2, e3, b)
1982 and switch_stmt env st e cl =
1983 let e = expr env e in
1984 let nsenv = (fst env).namespace in
1985 let _, vars = GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) st in
1986 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1987 let result = Env.scope env begin fun env ->
1988 let all_locals_l, cl = casel env cl in
1989 List.iter all_locals_l (Env.extend_all_locals env);
1990 N.Switch (e, cl)
1991 end in
1992 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) vars;
1993 result
1995 and foreach_stmt env e aw ae b =
1996 let e = expr env e in
1997 Env.scope env begin fun env ->
1998 let ae = as_expr env aw ae in
1999 let b = block env b in
2000 N.Foreach (e, ae, b)
2003 and as_expr env aw =
2004 let handle_v ev = match ev with
2005 | p, Id x when hacksperimental env ->
2006 let x = Env.new_let_local env x in
2007 let ev = (p, N.ImmutableVar x) in
2009 | p, Id _ ->
2010 Errors.expected_variable p;
2011 p, N.Lvar (Env.new_lvar env (p, "__internal_placeholder"))
2012 | ev ->
2013 let nsenv = (fst env).namespace in
2014 let _, vars =
2015 GetLocals.lvalue (fst env).tcopt (nsenv, SMap.empty) ev in
2016 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
2017 let ev = expr env ev in
2020 let handle_k ek = match ek with
2021 | p, Lvar x ->
2022 p, N.Lvar (Env.new_lvar env x)
2023 | p, Id x when hacksperimental env ->
2024 p, N.ImmutableVar (Env.new_let_local env x)
2025 | p, _ ->
2026 Errors.expected_variable p;
2027 p, N.Lvar (Env.new_lvar env (p, "__internal_placeholder"))
2029 begin function
2030 | As_v ev ->
2031 let ev = handle_v ev in
2032 begin match aw with
2033 | None -> N.As_v ev
2034 | Some p -> N.Await_as_v (p, ev)
2035 end (* match *)
2036 | As_kv (k, ev) ->
2037 let k = handle_k k in
2038 let ev = handle_v ev in
2039 begin match aw with
2040 | None -> N.As_kv (k, ev)
2041 | Some p -> N.Await_as_kv (p, k, ev)
2042 end (* match *)
2043 end (* function *)
2045 and try_stmt env st b cl fb =
2046 let nsenv = (fst env).namespace in
2047 let _, vars =
2048 GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) st in
2049 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
2050 let result = Env.scope env (
2051 fun env ->
2052 let genv, lenv = env in
2053 (* isolate finally from the rest of the try-catch: if the first
2054 * statement of the try is an uncaught exception, finally will
2055 * still be executed *)
2056 let _all_finally, fb = branch ({genv with in_finally = true}, lenv) fb in
2057 let all_locals_b, b = branch ({genv with in_try = true}, lenv) b in
2058 let all_locals_cl, cl = catchl env cl in
2059 List.iter all_locals_cl (Env.extend_all_locals env);
2060 Env.extend_all_locals env all_locals_b;
2061 N.Try (b, cl, fb)
2062 ) in
2063 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) vars;
2064 result
2066 and stmt_with_block env = fun astmt -> match astmt with
2067 | (_, Block b) -> always_block env b
2068 | astmt -> [stmt env astmt]
2070 and always_block env stl =
2071 (* Add lexical scope for block scoped let variables *)
2072 Env.scope_lexical env (fun env -> List.concat_map stl (stmt_with_block env))
2074 and block ?(new_scope=true) env stl =
2075 let stl = cut_unsafe env stl in
2076 if new_scope
2077 then
2078 Env.scope env (
2079 fun env -> List.concat_map stl (stmt_with_block env)
2081 else List.concat_map stl (stmt_with_block env)
2083 and branch env stmt_l =
2084 let stmt_l = cut_unsafe env stmt_l in
2085 Env.scope_all env begin fun env ->
2086 List.concat_map stmt_l (stmt_with_block env)
2090 * Names a goto label.
2092 * The goto label is added to the local labels if it is not already there.
2093 * Otherwise, an error is produced.
2095 * An error is produced if this is called within a finally block.
2097 and name_goto_label
2098 ({ in_finally; _ }, _ as env) (label_pos, label_name as label) =
2099 (match Env.goto_label env label_name with
2100 | Some original_declaration_pos ->
2101 Errors.goto_label_already_defined
2102 label_name
2103 label_pos
2104 original_declaration_pos
2105 | None -> Env.new_goto_label env label);
2106 if in_finally then
2107 Errors.goto_label_defined_in_finally label_pos;
2108 N.GotoLabel label
2111 * Names a goto target.
2113 * The goto statement's target label is added to the local goto targets.
2115 * An error is produced if this is called within a finally block.
2117 and name_goto
2118 ({ in_finally; _ }, _ as env) (label_pos, _ as label) =
2119 Env.new_goto_target env label;
2120 if in_finally then Errors.goto_invoked_in_finally label_pos;
2121 N.Goto label
2123 and static_varl env l = List.map l (static_var env)
2124 and static_var env = function
2125 | p, Lvar _ as lv -> expr env (p, Binop(Eq None, lv, (p, Null)))
2126 | e -> expr env e
2128 and global_varl env l = List.map l (global_var env)
2129 and global_var env = function
2130 | p, Lvar _ as lv -> expr env (p, Binop(Eq None, lv, (p, Null)))
2131 | e -> expr env e
2133 and expr_obj_get_name env = function
2134 | p, Id x -> p, N.Id x
2135 | p, e ->
2136 expr env (p, e)
2138 and exprl env l = List.map l (expr env)
2139 and oexpr env e = Option.map e (expr env)
2140 and expr env (p, e) = p, expr_ env p e
2141 and expr_ env p = function
2142 | Array l ->
2143 let tcopt = (fst env).tcopt in
2144 let array_literals_disallowed =
2145 TypecheckerOptions.disallow_array_literal tcopt in
2146 if array_literals_disallowed
2147 then Errors.array_literals_disallowed p;
2148 N.Array (List.map l (afield env))
2149 | ParenthesizedExpr (p, e) -> expr_ env p e
2150 | Darray l ->
2151 N.Darray (List.map l (fun (e1, e2) -> expr env e1, expr env e2))
2152 | Varray l -> N.Varray (List.map l (expr env))
2153 | Collection (id, l) -> begin
2154 let p, cn = NS.elaborate_id ((fst env).namespace) NS.ElaborateClass id in
2155 match cn with
2156 | x when N.is_vc_kind x ->
2157 N.ValCollection ((N.get_vc_kind cn),
2158 (List.map l (afield_value env cn)))
2159 | x when N.is_kvc_kind x ->
2160 N.KeyValCollection ((N.get_kvc_kind cn),
2161 (List.map l (afield_kvalue env cn)))
2162 | x when x = SN.Collections.cPair ->
2163 (match l with
2164 | [] ->
2165 Errors.naming_too_few_arguments p;
2166 N.Any
2167 | e1::e2::[] ->
2168 let pn = SN.Collections.cPair in
2169 N.Pair (afield_value env pn e1, afield_value env pn e2)
2170 | _ ->
2171 Errors.naming_too_many_arguments p;
2172 N.Any
2174 | _ ->
2175 Errors.expected_collection p cn;
2176 N.Any
2178 | Clone e -> N.Clone (expr env e)
2179 | Null -> N.Null
2180 | True -> N.True
2181 | False -> N.False
2182 | Int s -> N.Int s
2183 | Float s -> N.Float s
2184 | String s -> N.String s
2185 | String2 idl
2186 (* treat execution operator similar to interpolated strings *)
2187 | Execution_operator idl -> N.String2 (string2 env idl)
2188 | Id x ->
2189 (** TODO: Emit proper error messages T28473207. Currently the error message
2190 * emitted has reason Naming[2049] unbound name for global constant *)
2191 begin match Env.let_local env x with
2192 | Some x -> N.ImmutableVar x
2193 | None -> N.Id (Env.global_const env x)
2194 end (* match *)
2195 | Id_type_arguments (_x, _hl) ->
2196 (* Shouldn't happen: parser only allows this in New *)
2197 failwith "Unexpected Id with type arguments"
2198 | Lvar (_, x) when x = SN.SpecialIdents.this -> N.This
2199 | Lvar (_, x) when x = SN.SpecialIdents.dollardollar ->
2200 N.Dollardollar (Env.found_dollardollar env p)
2201 | Lvar (pos, x) when x = SN.SpecialIdents.placeholder ->
2202 N.Lplaceholder pos
2203 | Lvar x ->
2204 N.Lvar (Env.lvar env x)
2205 | Obj_get (e1, e2, nullsafe) ->
2206 (* If we encounter Obj_get(_,_,true) by itself, then it means "?->"
2207 is being used for instance property access; see the case below for
2208 handling nullsafe instance method calls to see how this works *)
2209 let nullsafe = match nullsafe with
2210 | OG_nullsafe -> N.OG_nullsafe
2211 | OG_nullthrows -> N.OG_nullthrows
2213 N.Obj_get (expr env e1, expr_obj_get_name env e2, nullsafe)
2214 | Array_get ((p, Lvar x), None) ->
2215 let id = p, N.Lvar (Env.lvar env x) in
2216 N.Array_get (id, None)
2217 | Array_get (e1, e2) -> N.Array_get (expr env e1, oexpr env e2)
2218 | Class_get ((_, (Id x1 | Lvar x1)), (_, (Id x2 | Lvar x2))) ->
2219 N.Class_get (make_class_id env x1 [], x2)
2220 | Class_get (x1, x2) ->
2221 ensure_name_not_dynamic env x1
2222 Errors.dynamic_class_name_in_strict_mode;
2223 ensure_name_not_dynamic env x2
2224 Errors.dynamic_class_property_name_in_strict_mode;
2225 N.Any
2226 | Class_const ((_, Id x1), x2)
2227 | Class_const ((_, Lvar x1), x2) ->
2228 let (genv, _) = env in
2229 let (_, name) = NS.elaborate_id genv.namespace NS.ElaborateClass x1 in
2230 begin match Naming_heap.TypeIdHeap.get name with
2231 | Some (_, `Typedef) when (snd x2) = "class" ->
2232 N.Typename (Env.type_name env x1 ~allow_typedef:true)
2233 | _ ->
2234 N.Class_const (make_class_id env x1 [], x2)
2236 | Class_const _ ->
2237 (* TODO: report error in strict mode *)
2238 N.Any
2239 | Call ((_, Id (p, pseudo_func)), hl, el, uel)
2240 when pseudo_func = SN.SpecialFunctions.echo ->
2241 arg_unpack_unexpected uel ;
2242 N.Call (N.Cnormal, (p, N.Id (p, pseudo_func)), hintl_funcall env hl, exprl env el, [])
2243 | Call ((p, Id (_, cn)), hl, el, uel)
2244 when cn = SN.SpecialFunctions.call_user_func ->
2245 arg_unpack_unexpected uel ;
2246 (match el with
2247 | [] -> Errors.naming_too_few_arguments p; N.Any
2248 | f :: el -> N.Call (N.Cuser_func, expr env f, hintl_funcall env hl, exprl env el, [])
2250 | Call ((p, Id (_, cn)), _, el, uel) when cn = SN.SpecialFunctions.fun_ ->
2251 arg_unpack_unexpected uel ;
2252 (match el with
2253 | [] -> Errors.naming_too_few_arguments p; N.Any
2254 | [_, String s] when String.contains s ':' ->
2255 Errors.illegal_meth_fun p; N.Any
2256 | [p, String x] -> N.Fun_id (Env.fun_id env (p, x))
2257 | [p, _] ->
2258 Errors.illegal_fun p;
2259 N.Any
2260 | _ -> Errors.naming_too_many_arguments p; N.Any
2262 | Call ((p, Id (_, cn)), _, el, uel)
2263 when cn = SN.SpecialFunctions.inst_meth ->
2264 arg_unpack_unexpected uel ;
2265 (match el with
2266 | [] -> Errors.naming_too_few_arguments p; N.Any
2267 | [_] -> Errors.naming_too_few_arguments p; N.Any
2268 | instance::(p, String meth)::[] ->
2269 N.Method_id (expr env instance, (p, meth))
2270 | (p, _)::(_)::[] ->
2271 Errors.illegal_inst_meth p;
2272 N.Any
2273 | _ -> Errors.naming_too_many_arguments p; N.Any
2275 | Call ((p, Id (_, cn)), _, el, uel)
2276 when cn = SN.SpecialFunctions.meth_caller ->
2277 arg_unpack_unexpected uel ;
2278 (match el with
2279 | [] -> Errors.naming_too_few_arguments p; N.Any
2280 | [_] -> Errors.naming_too_few_arguments p; N.Any
2281 | e1::e2::[] ->
2282 (match (expr env e1), (expr env e2) with
2283 | (pc, N.String cl), (pm, N.String meth) ->
2284 N.Method_caller (Env.type_name env (pc, cl) ~allow_typedef:false, (pm, meth))
2285 | (_, N.Class_const (((), N.CI (cl, _)), (_, mem))), (pm, N.String meth)
2286 when mem = SN.Members.mClass ->
2287 N.Method_caller (Env.type_name env cl ~allow_typedef:false, (pm, meth))
2288 | (p, _), (_) ->
2289 Errors.illegal_meth_caller p;
2290 N.Any
2292 | _ -> Errors.naming_too_many_arguments p; N.Any
2294 | Call ((p, Id (_, cn)), _, el, uel)
2295 when cn = SN.SpecialFunctions.class_meth ->
2296 arg_unpack_unexpected uel ;
2297 (match el with
2298 | [] -> Errors.naming_too_few_arguments p; N.Any
2299 | [_] -> Errors.naming_too_few_arguments p; N.Any
2300 | e1::e2::[] ->
2301 (match (expr env e1), (expr env e2) with
2302 | (pc, N.String cl), (pm, N.String meth) ->
2303 N.Smethod_id (Env.type_name env (pc, cl) ~allow_typedef:false, (pm, meth))
2304 | (_, N.Id (_, const)), (pm, N.String meth)
2305 when const = SN.PseudoConsts.g__CLASS__ ->
2306 (* All of these that use current_cls aren't quite correct
2307 * inside a trait, as the class should be the using class.
2308 * It's sufficient for typechecking purposes (we require
2309 * subclass to be compatible with the trait member/method
2310 * declarations).
2312 (match (fst env).current_cls with
2313 | Some (cid, _) -> N.Smethod_id (cid, (pm, meth))
2314 | None -> Errors.illegal_class_meth p; N.Any)
2315 | (_, N.Class_const (((), N.CI (cl, _)), (_, mem))), (pm, N.String meth)
2316 when mem = SN.Members.mClass ->
2317 N.Smethod_id (Env.type_name env cl ~allow_typedef:false, (pm, meth))
2318 | (p, N.Class_const (((), (N.CIself|N.CIstatic)), (_, mem))),
2319 (pm, N.String meth) when mem = SN.Members.mClass ->
2320 (match (fst env).current_cls with
2321 | Some (cid, _) -> N.Smethod_id (cid, (pm, meth))
2322 | None -> Errors.illegal_class_meth p; N.Any)
2323 | (p, _), (_) -> Errors.illegal_class_meth p; N.Any
2325 | _ -> Errors.naming_too_many_arguments p; N.Any
2327 | Call ((p, Id (_, cn)), _, el, uel) when cn = SN.SpecialFunctions.assert_ ->
2328 arg_unpack_unexpected uel ;
2329 if List.length el <> 1
2330 then Errors.assert_arity p;
2331 N.Assert (N.AE_assert (
2332 Option.value_map (List.hd el) ~default:(p, N.Any) ~f:(expr env)
2334 | Call ((p, Id (_, cn)), _, el, uel) when cn = SN.SpecialFunctions.tuple ->
2335 arg_unpack_unexpected uel ;
2336 (match el with
2337 | [] -> Errors.naming_too_few_arguments p; N.Any
2338 | el -> N.List (exprl env el)
2340 | Call ((p, Id f), hl, el, uel) ->
2341 begin match Env.let_local env f with
2342 | Some x ->
2343 (* Translate into local id *)
2344 let f = (p, N.ImmutableVar x) in
2345 N.Call (N.Cnormal, f, hintl_funcall env hl, exprl env el, exprl env uel)
2346 | None ->
2347 (* The name is not a local `let` binding *)
2348 let qualified = Env.fun_id env f in
2349 let cn = snd qualified in
2350 (* The above special cases (fun, inst_meth, meth_caller, class_meth,
2351 * and friends) are magical language constructs, which we should
2352 * check before calling fun_id and looking up the function and doing
2353 * namespace normalization. However, gena, genva, etc are actual
2354 * functions that actually exist, we just need to handle them
2355 * specially here, during naming. Note that most of the function
2356 * special cases, such as idx, are actually handled in typing, and
2357 * don't require naming magic. *)
2358 if cn = SN.FB.fgena then begin
2359 arg_unpack_unexpected uel ;
2360 (match el with
2361 | [e] -> N.Special_func (N.Gena (expr env e))
2362 | _ -> Errors.gena_arity p; N.Any
2364 end else if (cn = SN.FB.fgenva)
2365 || (cn = SN.HH.asio_va)
2366 || (cn = SN.HH.lib_tuple_gen)
2367 || (cn = SN.HH.lib_tuple_from_async) then begin
2368 arg_unpack_unexpected uel ;
2369 if List.length el < 1
2370 then (Errors.genva_arity p; N.Any)
2371 else N.Special_func (N.Genva (exprl env el))
2372 end else if cn = SN.FB.fgen_array_rec then begin
2373 arg_unpack_unexpected uel ;
2374 (match el with
2375 | [e] -> N.Special_func (N.Gen_array_rec (expr env e))
2376 | _ -> Errors.gen_array_rec_arity p; N.Any
2378 end else
2379 N.Call (N.Cnormal, (p, N.Id qualified), hintl_funcall env hl,
2380 exprl env el, exprl env uel)
2381 end (* match *)
2382 (* Handle nullsafe instance method calls here. Because Obj_get is used
2383 for both instance property access and instance method calls, we need
2384 to match the entire "Call(Obj_get(..), ..)" pattern here so that we
2385 only match instance method calls *)
2386 | Call ((p, Obj_get (e1, e2, OG_nullsafe)), hl, el, uel) ->
2387 N.Call
2388 (N.Cnormal,
2389 (p, N.Obj_get (expr env e1,
2390 expr_obj_get_name env e2, N.OG_nullsafe)),
2391 hintl_funcall env hl,
2392 exprl env el, exprl env uel)
2393 (* Handle all kinds of calls that weren't handled by any of
2394 the cases above *)
2395 | Call (e, hl, el, uel) ->
2396 N.Call (N.Cnormal, expr env e,
2397 hintl_funcall env hl, exprl env el, exprl env uel)
2398 | Yield_break -> N.Yield_break
2399 | Yield e -> N.Yield (afield env e)
2400 | Await e -> N.Await (expr env e)
2401 | Suspend e -> N.Suspend (expr env e)
2402 | List el -> N.List (exprl env el)
2403 | Expr_list el -> N.Expr_list (exprl env el)
2404 | Cast (ty, e2) ->
2405 let (p, x), hl = match ty with
2406 | _, Happly (id, hl) -> (id, hl)
2407 | _ -> assert false in
2408 let ty = match try_castable_hint ~tp_depth:1 env p x hl with
2409 | Some ty -> p, ty
2410 | None -> begin
2411 match x with
2412 | x when x = SN.Typehints.object_cast ->
2413 (* (object) is a valid cast but not a valid type annotation *)
2414 (* FIXME we are not modeling the correct runtime behavior here --
2415 * the runtime result type is an stdClass if the original type is
2416 * primitive. But we should probably just disallow object casts
2417 * altogether. *)
2418 p, N.Hany
2419 | x when x = SN.Typehints.void ->
2420 Errors.void_cast p;
2421 p, N.Hany
2422 | x when x = SN.Typehints.unset_cast ->
2423 Errors.unset_cast p;
2424 p, N.Hany
2425 | _ ->
2426 (* Let's just assume that any other invalid cases are attempts to
2427 * cast to specific objects *)
2428 let h = hint ~allow_typedef:false env ty in
2429 Errors.object_cast p x;
2431 end in
2432 N.Cast (ty, expr env e2)
2433 | Unop (uop, e) -> N.Unop (uop, expr env e)
2434 | Binop (Eq None as op, lv, e2) ->
2435 if Env.inside_pipe env then
2436 Errors.unimplemented_feature p "Assignment within pipe expressions";
2437 let e2 = expr env e2 in
2438 let nsenv = (fst env).namespace in
2439 let _, vars =
2440 GetLocals.lvalue (fst env).tcopt (nsenv, SMap.empty) lv in
2441 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
2442 N.Binop (op, expr env lv, e2)
2443 | Binop (Eq _ as bop, e1, e2) ->
2444 if Env.inside_pipe env then
2445 Errors.unimplemented_feature p "Assignment within pipe expressions";
2446 let e1 = expr env e1 in
2447 N.Binop (bop, e1, expr env e2)
2448 | Binop (bop, e1, e2) ->
2449 let e1 = expr env e1 in
2450 N.Binop (bop, e1, expr env e2)
2451 | Pipe (e1, e2) ->
2452 let e1 = expr env e1 in
2453 let ident, e2 = Env.pipe_scope env
2454 begin fun env ->
2455 expr env e2
2458 N.Pipe ((p, ident), e1, e2)
2459 | Eif (e1, e2opt, e3) ->
2460 (* The order matters here, of course -- e1 can define vars that need to
2461 * be available in e2 and e3. *)
2462 let e1 = expr env e1 in
2463 let nsenv = (fst env).namespace in
2464 let get_lvalues = function e ->
2465 snd @@ GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) (p, Expr e) in
2466 let e2_lvalues =
2467 Option.value (Option.map e2opt get_lvalues) ~default:SMap.empty
2469 let e3_lvalues = get_lvalues e3 in
2470 let lvalues = smap_inter e2_lvalues e3_lvalues in
2471 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) lvalues;
2472 let e2opt, e3, dollar_dollar = Env.scope env (fun env ->
2473 let all2, e2opt = Env.scope_all env (fun env -> oexpr env e2opt) in
2474 let dollardollar_e2 = Env.has_dollardollar all2 in
2475 let all3, e3 = Env.scope_all env (fun env -> expr env e3) in
2476 let dollardollar_e3 = Env.has_dollardollar all3 in
2477 Env.extend_all_locals env all2;
2478 Env.extend_all_locals env all3;
2479 e2opt, e3, Option.first_some dollardollar_e2 dollardollar_e3
2480 ) in
2481 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) lvalues;
2482 if Env.inside_pipe env then
2483 ignore (Option.map dollar_dollar (Env.found_dollardollar env));
2484 N.Eif (e1, e2opt, e3)
2485 | InstanceOf (e, (p, Id x)) ->
2486 let id = match x with
2487 | px, n when n = SN.Classes.cParent ->
2488 if (fst env).current_cls = None then
2489 let () = Errors.parent_outside_class p in
2490 N.CI ((px, SN.Classes.cUnknown), [])
2491 else N.CIparent
2492 | px, n when n = SN.Classes.cSelf ->
2493 if (fst env).current_cls = None then
2494 let () = Errors.self_outside_class p in
2495 N.CI ((px, SN.Classes.cUnknown), [])
2496 else N.CIself
2497 | px, n when n = SN.Classes.cStatic ->
2498 if (fst env).current_cls = None then
2499 let () = Errors.static_outside_class p in
2500 N.CI ((px, SN.Classes.cUnknown), [])
2501 else N.CIstatic
2502 | _ ->
2503 N.CI (Env.type_name env x ~allow_typedef:false, [])
2505 N.InstanceOf (expr env e, ((), id))
2506 | InstanceOf (e1, (_,
2507 (Lvar _ | Obj_get _ | Class_get _ | Class_const _
2508 | Array_get _ | Call _) as e2)) ->
2509 N.InstanceOf (expr env e1, ((), N.CIexpr (expr env e2)))
2510 | InstanceOf (_e1, (p, _)) ->
2511 Errors.invalid_instanceof p;
2512 N.Any
2513 | Is (e, h) ->
2514 let e1 = expr env e in
2515 let h1 = hint ~allow_wildcard:true env h in
2516 N.Is (e1, h1)
2517 | As (e, h, b) ->
2518 let e1 = expr env e in
2519 let h1 = hint ~allow_wildcard:true env h in
2520 N.As (e1, h1, b)
2521 | New ((_, Id_type_arguments (x, hl)), el, uel) ->
2522 N.New (make_class_id env x hl,
2523 exprl env el,
2524 exprl env uel)
2525 | New ((_, Id x), el, uel)
2526 | New ((_, Lvar x), el, uel) ->
2527 N.New (make_class_id env x [],
2528 exprl env el,
2529 exprl env uel)
2530 | New ((p, _e), el, uel) ->
2531 if (fst env).in_mode = FileInfo.Mstrict
2532 then Errors.dynamic_new_in_strict_mode p;
2533 N.New (make_class_id env (p, SN.Classes.cUnknown) [],
2534 exprl env el,
2535 exprl env uel)
2536 | NewAnonClass _ ->
2537 N.Null
2538 | Efun (f, idl) ->
2539 let _ = match (fst env).in_mode with
2540 | FileInfo.Mstrict -> List.iter idl ~f:(function
2541 | (p, _), is_ref when is_ref -> Errors.reference_in_strict_mode p;
2542 | _ -> ())
2543 | _ -> () in
2544 let idl =
2545 List.fold_right idl
2546 ~init:[]
2547 ~f:(fun ((p, x) as id, _) acc ->
2548 if x = SN.SpecialIdents.this
2549 then (Errors.this_as_lexical_variable p; acc)
2550 else id :: acc)
2552 let idl' = List.map idl (Env.lvar env) in
2553 let env = (fst env, Env.empty_local UBMErr) in
2554 List.iter2_exn idl idl' (Env.add_lvar env);
2555 let f = expr_lambda env f in
2556 N.Efun (f, idl')
2557 | Lfun f ->
2558 (* We have to build the capture list while we're finding names in
2559 the closure body---accumulate it in to_capture. *)
2560 (* semantic duplication: The logic here is also used in `uselist_lambda`.
2561 The differences are enough that it does not make sense to refactor
2562 this out for now. *)
2563 let to_capture = ref [] in
2564 let handle_unbound (p, x) =
2565 let cap = Env.lvar env (p, x) in
2566 to_capture := cap :: !to_capture;
2569 let lenv = Env.empty_local @@ UBMFunc handle_unbound in
2570 (* Extend the current let binding into the scope of lambda *)
2571 Env.copy_let_locals env (fst env, lenv);
2572 let env = (fst env, lenv) in
2573 let f = expr_lambda env f in
2574 (* TODO T28711692: Compute the correct capture list for let variables,
2575 * it does not seem to affect typechecking... *)
2576 N.Efun (f, !to_capture)
2577 | Xml (x, al, el) ->
2578 N.Xml (Env.type_name env x ~allow_typedef:false, attrl env al,
2579 exprl env el)
2580 | Shape fdl ->
2581 N.Shape begin List.fold_left fdl ~init:ShapeMap.empty
2582 ~f:begin fun fdm (pname, value) ->
2583 let pos, name = convert_shape_name env pname in
2584 if ShapeMap.mem name fdm
2585 then Errors.fd_name_already_bound pos;
2586 ShapeMap.add name (expr env value) fdm
2589 | Unsafeexpr _ ->
2590 N.Any
2591 | BracedExpr _ ->
2592 N.Any
2593 | Dollar _ ->
2594 Errors.variable_variables_disallowed p;
2595 N.Any
2596 | Yield_from e ->
2597 N.Yield_from (expr env e)
2598 | Import _ ->
2599 N.Any
2600 | Omitted ->
2601 N.Any
2602 | Callconv (kind, e) ->
2603 N.Callconv (kind, expr env e)
2605 and expr_lambda env f =
2606 let h = Option.map f.f_ret (hint ~allow_retonly:true env) in
2607 let previous_unsafe = Env.has_unsafe env in
2608 (* save unsafe and yield state *)
2609 Env.set_unsafe env false;
2610 let variadicity, paraml = fun_paraml env f.f_params in
2611 let f_kind = f.f_fun_kind in
2612 (* The bodies of lambdas go through naming in the containing local
2613 * environment *)
2614 let body_nast = block env f.f_body in
2615 let unsafe = func_body_had_unsafe env in
2616 (* restore unsafe state *)
2617 Env.set_unsafe env previous_unsafe;
2618 let body = N.NamedBody {
2619 N.fnb_unsafe = unsafe;
2620 fnb_nast = body_nast;
2621 } in {
2622 N.f_annotation = ();
2623 f_mode = (fst env).in_mode;
2624 f_ret = h;
2625 f_name = f.f_name;
2626 f_params = paraml;
2627 f_tparams = [];
2628 f_where_constraints = [];
2629 f_body = body;
2630 f_fun_kind = f_kind;
2631 f_variadic = variadicity;
2632 f_user_attributes = user_attributes env f.f_user_attributes;
2633 f_ret_by_ref = f.f_ret_by_ref;
2636 and make_class_id env (p, x as cid) hl =
2638 match x with
2639 | x when x = SN.Classes.cParent ->
2640 if (fst env).current_cls = None then
2641 let () = Errors.parent_outside_class p in
2642 N.CI ((p, SN.Classes.cUnknown), [])
2643 else N.CIparent
2644 | x when x = SN.Classes.cSelf ->
2645 if (fst env).current_cls = None then
2646 let () = Errors.self_outside_class p in
2647 N.CI ((p, SN.Classes.cUnknown), [])
2648 else N.CIself
2649 | x when x = SN.Classes.cStatic -> if (fst env).current_cls = None then
2650 let () = Errors.static_outside_class p in
2651 N.CI ((p, SN.Classes.cUnknown), [])
2652 else N.CIstatic
2653 | x when x = SN.SpecialIdents.this -> N.CIexpr (p, N.This)
2654 | x when x = SN.SpecialIdents.dollardollar ->
2655 (* We won't reach here for "new $$" because the parser creates a
2656 * proper Ast.Dollardollar node, so make_class_id won't be called with
2657 * that node. In fact, the parser creates an Ast.Dollardollar for all
2658 * "$$" except in positions where a classname is expected, like in
2659 * static member access. So, we only reach here for things
2660 * like "$$::someMethod()". *)
2661 N.CIexpr(p, N.Lvar (Env.found_dollardollar env p))
2662 | x when x.[0] = '$' -> N.CIexpr (p, N.Lvar (Env.lvar env cid))
2663 | _ -> N.CI (Env.type_name env cid ~allow_typedef:false,
2664 hintl ~allow_wildcard:true ~forbid_this:false
2665 ~allow_typedef:true ~allow_retonly:true ~tp_depth:1 env hl
2668 and casel env l =
2669 List.map_env [] l (case env)
2671 and case env acc = function
2672 | Default b ->
2673 let b = cut_unsafe ~replacement:Fallthrough env b in
2674 let all_locals, b = branch env b in
2675 all_locals :: acc, N.Default b
2676 | Case (e, b) ->
2677 let e = expr env e in
2678 let b = cut_unsafe ~replacement:Fallthrough env b in
2679 let all_locals, b = branch env b in
2680 all_locals :: acc, N.Case (e, b)
2682 and catchl env l = List.map_env [] l (catch env)
2683 and catch env acc (x1, x2, b) =
2684 Env.scope env (
2685 fun env ->
2686 (* If the variable does not begin with $, it is an immutable binding *)
2687 let x2 = if (snd x2).[0] = '$' (* This is always true if hacksperimental is off *)
2688 then Env.new_lvar env x2
2689 else Env.new_let_local env x2
2691 let all_locals, b = branch env b in
2692 all_locals :: acc, (Env.type_name env x1 ~allow_typedef:true, x2, b)
2695 and afield env = function
2696 | AFvalue e -> N.AFvalue (expr env e)
2697 | AFkvalue (e1, e2) -> N.AFkvalue (expr env e1, expr env e2)
2699 and afield_value env cname = function
2700 | AFvalue e -> expr env e
2701 | AFkvalue (e1, _e2) ->
2702 Errors.unexpected_arrow (fst e1) cname;
2703 expr env e1
2705 and afield_kvalue env cname = function
2706 | AFvalue e ->
2707 Errors.missing_arrow (fst e) cname;
2708 expr env e, expr env (fst e, Lvar (fst e, "__internal_placeholder"))
2709 | AFkvalue (e1, e2) -> expr env e1, expr env e2
2711 and attrl env l = List.map l (attr env)
2712 and attr env = function
2713 | Xhp_simple (x, e) -> N.Xhp_simple (x, expr env e)
2714 | Xhp_spread e -> N.Xhp_spread (expr env e)
2716 and string2 env idl =
2717 List.map idl (expr env)
2719 (**************************************************************************)
2720 (* Function/Method Body Naming: *)
2721 (* Ensure that, given a function / class, any UnnamedBody within is
2722 * transformed into a a named body *)
2723 (**************************************************************************)
2725 let func_body nenv f =
2726 match f.N.f_body with
2727 | N.NamedBody b -> b
2728 | N.UnnamedBody { N.fub_ast; N.fub_tparams; N.fub_namespace; _ } ->
2729 let genv = Env.make_fun_genv nenv
2730 SMap.empty f.N.f_mode (snd f.N.f_name) fub_namespace in
2731 let genv = extend_params genv fub_tparams in
2732 let lenv = Env.empty_local UBMErr in
2733 let env = genv, lenv in
2734 let env =
2735 List.fold_left ~f:Env.add_param f.N.f_params ~init:env in
2736 let env = match f.N.f_variadic with
2737 | N.FVellipsis _ | N.FVnonVariadic -> env
2738 | N.FVvariadicArg param -> Env.add_param env param
2740 let body = block env fub_ast in
2741 let unsafe = func_body_had_unsafe env in
2742 Env.check_goto_references env;
2744 N.fnb_nast = body;
2745 fnb_unsafe = unsafe;
2748 let meth_body genv m =
2749 let named_body = (match m.N.m_body with
2750 | N.NamedBody _ as b -> b
2751 | N.UnnamedBody {N.fub_ast; N.fub_tparams; N.fub_namespace; _} ->
2752 let genv = {genv with namespace = fub_namespace} in
2753 let genv = extend_params genv fub_tparams in
2754 let env = genv, Env.empty_local UBMErr in
2755 let env =
2756 List.fold_left ~f:Env.add_param m.N.m_params ~init:env in
2757 let env = match m.N.m_variadic with
2758 | N.FVellipsis _ | N.FVnonVariadic -> env
2759 | N.FVvariadicArg param -> Env.add_param env param
2761 let body = block env fub_ast in
2762 let unsafe = func_body_had_unsafe env in
2763 Env.check_goto_references env;
2764 N.NamedBody {
2765 N.fnb_nast = body;
2766 fnb_unsafe = unsafe;
2768 ) in
2769 {m with N.m_body = named_body}
2771 let class_meth_bodies nenv nc =
2772 let _n_tparams, cstrs = nc.N.c_tparams in
2773 let genv = Env.make_class_genv nenv cstrs
2774 nc.N.c_mode (nc.N.c_name, nc.N.c_kind)
2775 Namespace_env.empty_with_default_popt
2777 let inst_meths = List.map nc.N.c_methods (meth_body genv) in
2778 let opt_constructor = match nc.N.c_constructor with
2779 | None -> None
2780 | Some c -> Some (meth_body genv c) in
2781 let static_meths = List.map nc.N.c_static_methods (meth_body genv) in
2782 { nc with
2783 N.c_methods = inst_meths;
2784 N.c_static_methods = static_meths ;
2785 N.c_constructor = opt_constructor ;
2788 (**************************************************************************)
2789 (* Typedefs *)
2790 (**************************************************************************)
2792 let typedef genv tdef =
2793 let ty = match tdef.t_kind with Alias t | NewType t -> t in
2794 let cstrs = make_constraints tdef.t_tparams in
2795 let env = Env.make_typedef_env genv cstrs tdef in
2796 let tconstraint = Option.map tdef.t_constraint (hint env) in
2797 List.iter tdef.t_tparams check_constraint;
2798 let tparaml = type_paraml env tdef.t_tparams in
2799 List.iter tparaml begin fun (_, _, constrs) ->
2800 List.iter constrs (fun (_, (pos, _)) -> Errors.typedef_constraint pos)
2801 end;
2802 let t_vis = match tdef.t_kind with
2803 | Ast.Alias _ -> N.Transparent
2804 | Ast.NewType _ -> N.Opaque
2806 let attrs = user_attributes env tdef.t_user_attributes in
2808 N.t_annotation = ();
2809 t_name = tdef.t_id;
2810 t_tparams = tparaml;
2811 t_constraint = tconstraint;
2812 t_kind = hint env ty;
2813 t_user_attributes = attrs;
2814 t_mode = tdef.t_mode;
2815 t_vis;
2818 (**************************************************************************)
2819 (* Global constants *)
2820 (**************************************************************************)
2822 let check_constant_hint cst =
2823 match cst.cst_type with
2824 | None when cst.cst_mode = FileInfo.Mstrict ->
2825 Errors.add_a_typehint (fst cst.cst_name)
2826 | None
2827 | Some _ -> ()
2829 let global_const genv cst =
2830 let env = Env.make_const_env genv cst in
2831 let hint = Option.map cst.cst_type (hint env) in
2832 let e = match cst.cst_kind with
2833 | Ast.Cst_const ->
2834 check_constant_hint cst;
2835 Some (constant_expr env cst.cst_value)
2836 (* Define allows any expression, so don't call check_constant.
2837 * Furthermore it often appears at toplevel, which we don't track at
2838 * all, so don't type or even name that expression, it may refer to
2839 * "undefined" variables that actually exist, just untracked since
2840 * they're toplevel. *)
2841 | Ast.Cst_define -> None in
2842 { N.cst_annotation = ();
2843 cst_mode = cst.cst_mode;
2844 cst_name = cst.cst_name;
2845 cst_type = hint;
2846 cst_value = e;
2847 cst_is_define = (cst.cst_kind = Ast.Cst_define);
2850 (* Uses a default empty environment to extract the use list
2851 of a lambda expression. This exists only for the sake of
2852 the dehackificator and is not meant for general use. *)
2853 let uselist_lambda f =
2854 (* semantic duplication: This is copied from the implementation of the
2855 `Lfun` variant of `expr_` defined earlier in this file. *)
2856 let to_capture = ref [] in
2857 let handle_unbound (p, x) =
2858 to_capture := x :: !to_capture;
2859 p, Local_id.tmp()
2861 let tcopt = TypecheckerOptions.make_permissive TypecheckerOptions.default in
2862 let genv = Env.make_fun_decl_genv tcopt SMap.empty f in
2863 let lenv = Env.empty_local @@ UBMFunc handle_unbound in
2864 let env = genv, lenv in
2865 ignore (expr_lambda env f);
2866 List.dedup !to_capture
2868 (**************************************************************************)
2869 (* The entry point to CHECK the program, and transform the program *)
2870 (**************************************************************************)
2872 let program tcopt ast =
2873 let rec program ast =
2874 List.concat @@ List.map ast begin function
2875 | Ast.Fun f -> [N.Fun (fun_ tcopt f)]
2876 | Ast.Class c -> [N.Class (class_ tcopt c)]
2877 | Ast.Typedef t -> [N.Typedef (typedef tcopt t)]
2878 | Ast.Constant cst -> [N.Constant (global_const tcopt cst)]
2879 | Ast.Stmt _ -> []
2880 | Ast.Namespace (_ns, ast) -> program ast
2881 | Ast.NamespaceUse _ -> []
2882 | Ast.SetNamespaceEnv _ -> []
2883 end in program ast
2886 include Make(struct
2887 let stmt _ acc _ = acc
2888 let lvalue _ acc _ = acc
2889 end)