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