Implement goto statement parsing
[hiphop-php.git] / hphp / hack / src / naming / naming.ml
blob772e2643b0fa937beac8429070e7b213374f6574
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
11 (** Module "naming" a program.
13 * The naming phase consists in several things
14 * 1- get all the global names
15 * 2- transform all the local names into a unique identifier
17 open Ast
18 open Core
19 open Utils
20 open String_utils
22 module N = Nast
23 module ShapeMap = N.ShapeMap
24 module SN = Naming_special_names
26 module GEnv = NamingGlobal.GEnv
28 (*****************************************************************************)
29 (* The types *)
30 (*****************************************************************************)
32 (* We want to keep the positions of names that have been
33 * replaced by identifiers.
35 type positioned_ident = (Pos.t * Local_id.t)
37 (* <T as A>, A is a type constraint *)
38 type type_constraint = (Ast.constraint_kind * Ast.hint) list
40 type genv = {
42 (* strict? decl? partial? *)
43 in_mode: FileInfo.mode;
45 (* various options that control the strictness of the typechecker *)
46 tcopt: TypecheckerOptions.t;
48 (* are we in the body of a try statement? *)
49 in_try: bool;
51 (* In function foo<T1, ..., Tn> or class<T1, ..., Tn>, the field
52 * type_params knows T1 .. Tn. It is able to find out about the
53 * constraint on these parameters. *)
54 type_params: type_constraint SMap.t;
56 (* The current class, None if we are in a function *)
57 current_cls: (Ast.id * Ast.class_kind) option;
59 class_consts: (string, Pos.t) Hashtbl.t;
61 class_props: (string, Pos.t) Hashtbl.t;
63 (* Normally we don't need to add dependencies at this stage, but there
64 * are edge cases when we do. *)
65 droot: Typing_deps.Dep.variant;
67 (* Namespace environment, e.g., what namespace we're in and what use
68 * declarations are in play. *)
69 namespace: Namespace_env.env;
72 (* How to behave when we see an unbound name. Either we raise an
73 * error, or we call a function first and continue if it can resolve
74 * the name. This is used to nest environments when processing
75 * closures. *)
76 type unbound_mode =
77 | UBMErr
78 | UBMFunc of ((Pos.t * string) -> positioned_ident)
80 (* The primitives to manipulate the naming environment *)
81 module Env : sig
83 type all_locals
84 type lenv
86 val empty_local : unbound_mode -> lenv
87 val make_class_genv :
88 TypecheckerOptions.t ->
89 type_constraint SMap.t ->
90 FileInfo.mode ->
91 Ast.id * Ast.class_kind -> Namespace_env.env -> genv
92 val make_class_env :
93 TypecheckerOptions.t ->
94 type_constraint SMap.t -> Ast.class_ -> genv * lenv
95 val make_typedef_env :
96 TypecheckerOptions.t ->
97 type_constraint SMap.t -> Ast.typedef -> genv * lenv
98 val make_fun_genv :
99 TypecheckerOptions.t ->
100 type_constraint SMap.t ->
101 FileInfo.mode -> string -> Namespace_env.env -> genv
102 val make_fun_decl_genv :
103 TypecheckerOptions.t ->
104 type_constraint SMap.t -> Ast.fun_ -> genv
105 val make_const_env : TypecheckerOptions.t -> Ast.gconst -> genv * lenv
107 val has_unsafe : genv * lenv -> bool
108 val set_unsafe : genv * lenv -> bool -> unit
110 val add_lvar : genv * lenv -> Ast.id -> positioned_ident -> unit
111 val add_param : genv * lenv -> N.fun_param -> genv * lenv
112 val new_lvar : genv * lenv -> Ast.id -> positioned_ident
113 val found_dollardollar : genv * lenv -> Pos.t -> positioned_ident
114 val inside_pipe : genv * lenv -> bool
115 val new_pending_lvar : genv * lenv -> Ast.id -> unit
116 val promote_pending : genv * lenv -> unit
117 val lvar : genv * lenv -> Ast.id -> positioned_ident
118 val global_const : genv * lenv -> Ast.id -> Ast.id
119 val type_name : genv * lenv -> Ast.id -> allow_typedef:bool -> Ast.id
120 val fun_id : genv * lenv -> Ast.id -> Ast.id
121 val bind_class_const : genv * lenv -> Ast.id -> unit
122 val bind_prop : genv * lenv -> Ast.id -> unit
124 val scope : genv * lenv -> (genv * lenv -> 'a) -> 'a
125 val scope_all : genv * lenv -> (genv * lenv -> 'a) -> all_locals * 'a
126 val extend_all_locals : genv * lenv -> all_locals -> unit
127 val pipe_scope : genv * lenv -> (genv * lenv -> N.expr) -> Local_id.t * N.expr
129 end = struct
131 type map = positioned_ident SMap.t
132 type all_locals = Pos.t SMap.t
134 (* The local environment *)
135 type lenv = {
137 (* The set of locals *)
138 locals: map ref;
140 (* We keep all the locals, even if we are in a different scope
141 * to provide better error messages.
142 * if you write:
143 * if(...) {
144 * $x = ...;
146 * Technically, passed this point, $x is unbound.
147 * But it is much better to keep it somewhere, so that you can
148 * say it is bound, but in a different scope.
150 all_locals: all_locals ref;
152 (* Some statements can define new variables afterwards, e.g.,
153 * if (...) {
154 * $x = ...;
155 * } else {
156 * $x = ...;
158 * We need to give $x the same name in both branches, but we don't want
159 * $x to actually be a local until after the if block. So we stash it here,
160 * to indicate a name has been pre-allocated, but that the variable isn't
161 * actually defined yet.
163 pending_locals: map ref;
165 (* Tag controlling what we do when we encounter an unbound name.
166 * This is used when processing a lambda expression body that has
167 * an automatic use list.
169 * See expr_lambda for details.
171 unbound_mode: unbound_mode;
173 (* The presence of an "UNSAFE" in the function body changes the
174 * verifiability of the function's return type, since the unsafe
175 * block could return. For the sanity of the typechecker, we flatten
176 * this out, but need to track if we've seen an "UNSAFE" in order to
177 * do so. *)
178 has_unsafe: bool ref;
180 (** Allows us to ban $$ appearances outside of pipe expressions and
181 * equals expressions within pipes. *)
182 inside_pipe: bool ref;
185 let empty_local unbound_mode = {
186 locals = ref SMap.empty;
187 all_locals = ref SMap.empty;
188 pending_locals = ref SMap.empty;
189 unbound_mode;
190 has_unsafe = ref false;
191 inside_pipe = ref false;
194 let make_class_genv tcopt tparams mode (cid, ckind) namespace = {
195 in_mode =
196 (if !Autocomplete.auto_complete then FileInfo.Mpartial else mode);
197 tcopt;
198 in_try = false;
199 type_params = tparams;
200 current_cls = Some (cid, ckind);
201 class_consts = Hashtbl.create 0;
202 class_props = Hashtbl.create 0;
203 droot = Typing_deps.Dep.Class (snd cid);
204 namespace;
207 let make_class_env tcopt tparams c =
208 let genv = make_class_genv tcopt tparams c.c_mode
209 (c.c_name, c.c_kind) c.c_namespace in
210 let lenv = empty_local UBMErr in
211 let env = genv, lenv in
214 let make_typedef_genv tcopt cstrs tdef = {
215 in_mode = FileInfo.(if !Ide.is_ide_mode then Mpartial else Mstrict);
216 tcopt;
217 in_try = false;
218 type_params = cstrs;
219 current_cls = None;
220 class_consts = Hashtbl.create 0;
221 class_props = Hashtbl.create 0;
222 droot = Typing_deps.Dep.Class (snd tdef.t_id);
223 namespace = tdef.t_namespace;
226 let make_typedef_env genv cstrs tdef =
227 let genv = make_typedef_genv genv cstrs tdef in
228 let lenv = empty_local UBMErr in
229 let env = genv, lenv in
232 let make_fun_genv tcopt params f_mode f_name f_namespace = {
233 in_mode = f_mode;
234 tcopt;
235 in_try = false;
236 type_params = params;
237 current_cls = None;
238 class_consts = Hashtbl.create 0;
239 class_props = Hashtbl.create 0;
240 droot = Typing_deps.Dep.Fun f_name;
241 namespace = f_namespace;
244 let make_fun_decl_genv nenv params f =
245 make_fun_genv nenv params f.f_mode (snd f.f_name) f.f_namespace
247 let make_const_genv tcopt cst = {
248 in_mode = cst.cst_mode;
249 tcopt;
250 in_try = false;
251 type_params = SMap.empty;
252 current_cls = None;
253 class_consts = Hashtbl.create 0;
254 class_props = Hashtbl.create 0;
255 droot = Typing_deps.Dep.GConst (snd cst.cst_name);
256 namespace = cst.cst_namespace;
259 let make_const_env nenv cst =
260 let genv = make_const_genv nenv cst in
261 let lenv = empty_local UBMErr in
262 let env = genv, lenv in
265 let has_unsafe (_genv, lenv) = !(lenv.has_unsafe)
266 let set_unsafe (_genv, lenv) x =
267 lenv.has_unsafe := x
269 let lookup genv env (p, x) =
270 let v = env x in
271 match v with
272 | None ->
273 (match genv.in_mode with
274 | FileInfo.Mstrict -> Errors.unbound_name p x `const
275 | FileInfo.Mpartial | FileInfo.Mdecl when not
276 (TypecheckerOptions.assume_php genv.tcopt) ->
277 Errors.unbound_name p x `const
278 | FileInfo.Mphp | FileInfo.Mdecl | FileInfo.Mpartial -> ()
280 | _ -> ()
282 (* Check and see if the user might have been trying to use one of the
283 * generics in scope as a runtime value *)
284 let check_no_runtime_generic genv (p, name) =
285 let tparaml = SMap.keys genv.type_params in
286 if List.mem tparaml name then Errors.generic_at_runtime p;
289 let handle_unbound_name genv get_pos get_canon (p, name) kind =
290 match get_canon name with
291 | Some canonical ->
292 canonical
293 |> get_pos
294 |> Option.iter ~f:(fun p_canon ->
295 Errors.did_you_mean_naming p name p_canon canonical);
296 (* Recovering from the capitalization error means
297 * returning the name in its canonical form *)
298 p, canonical
299 | None ->
300 (match genv.in_mode with
301 | FileInfo.Mpartial | FileInfo.Mdecl
302 when TypecheckerOptions.assume_php genv.tcopt
303 || name = SN.Classes.cUnknown -> ()
304 | FileInfo.Mphp -> ()
305 | FileInfo.Mstrict -> Errors.unbound_name p name kind
306 | FileInfo.Mpartial | FileInfo.Mdecl ->
307 Errors.unbound_name p name kind
309 p, name
311 let canonicalize genv get_pos get_canon (p, name) kind =
312 match get_pos name with
313 | Some _ -> p, name
314 | None -> handle_unbound_name genv get_pos get_canon (p, name) kind
316 let check_variable_scoping env (p, x) =
317 match SMap.get x !(env.all_locals) with
318 | Some p' -> Errors.different_scope p x p'
319 | None -> ()
321 (* Adds a local variable, without any check *)
322 let add_lvar (_, lenv) (_, name) (p, x) =
323 lenv.locals := SMap.add name (p, x) !(lenv.locals);
324 Naming_hooks.dispatch_lvar_hook x (p, name) !(lenv.locals);
327 let add_param env param =
328 let p_name = param.N.param_name in
329 let id = Local_id.get p_name in
330 let p_pos = param.N.param_pos in
331 let () = add_lvar env (p_pos, p_name) (p_pos, id) in
334 (* Defines a new local variable.
335 Side effects:
336 1) if the local is not in the local environment then it is added.
337 2) the naming hook callback is invoked.
338 Return value: the given position and deduced/created identifier. *)
339 let new_lvar (_, lenv) (p, x) =
340 let lcl = SMap.get x !(lenv.locals) in
341 let ident =
342 match lcl with
343 | Some lcl -> snd lcl
344 | None ->
345 let ident = match SMap.get x !(lenv.pending_locals) with
346 | Some (_, ident) -> ident
347 | None -> Local_id.make x in
348 lenv.all_locals := SMap.add x p !(lenv.all_locals);
349 lenv.locals := SMap.add x (p, ident) !(lenv.locals);
350 ident
352 Naming_hooks.dispatch_lvar_hook ident (p, x) !(lenv.locals);
353 p, ident
355 (* Defines a new local variable for this dollardollar (or reuses
356 * the exiting identifier). *)
357 let found_dollardollar (genv, lenv) p =
358 if not !(lenv.inside_pipe) then
359 Errors.undefined p SN.SpecialIdents.dollardollar;
360 new_lvar (genv, lenv) (p, SN.SpecialIdents.dollardollar)
362 let inside_pipe (_, lenv) =
363 !(lenv.inside_pipe)
365 let new_pending_lvar (_, lenv) (p, x) =
366 match SMap.get x !(lenv.locals), SMap.get x !(lenv.pending_locals) with
367 | None, None ->
368 let y = p, Local_id.make x in
369 lenv.pending_locals := SMap.add x y !(lenv.pending_locals)
370 | _ -> ()
372 let promote_pending (_, lenv as env) =
373 SMap.iter begin fun x (p, ident) ->
374 add_lvar env (p, x) (p, ident)
375 end !(lenv.pending_locals);
376 lenv.pending_locals := SMap.empty
378 let handle_undefined_variable (_genv, env) (p, x) =
379 match env.unbound_mode with
380 | UBMErr -> Errors.undefined p x; p, Local_id.make x
381 | UBMFunc f -> f (p, x)
383 (* Function used to name a local variable *)
384 let lvar (genv, env) (p, x) =
385 let p, ident =
386 if SN.Superglobals.is_superglobal x && genv.in_mode = FileInfo.Mpartial
387 then p, Local_id.make x
388 else
389 let lcl = SMap.get x !(env.locals) in
390 match lcl with
391 | Some lcl -> p, snd lcl
392 | None when not !Autocomplete.auto_complete ->
393 check_variable_scoping env (p, x);
394 handle_undefined_variable (genv, env) (p, x)
395 | None -> p, Local_id.tmp()
397 Naming_hooks.dispatch_lvar_hook ident (p, x) !(env.locals);
398 p, ident
400 let get_name genv namespace x =
401 lookup genv namespace x; x
403 (* For dealing with namespace fallback on constants *)
404 let elaborate_and_get_name_with_fallback mk_dep genv get_pos x =
405 let get_name x = get_name genv get_pos x in
406 let fq_x = Namespaces.elaborate_id genv.namespace NSConst x in
407 let need_fallback =
408 genv.namespace.Namespace_env.ns_name <> None &&
409 not (String.contains (snd x) '\\') in
410 if need_fallback then begin
411 let global_x = (fst x, "\\" ^ (snd x)) in
412 (* Explicitly add dependencies on both of the consts we could be
413 * referring to here. Normally naming doesn't have to deal with
414 * deps at all -- they are added during typechecking just by the
415 * nature of looking up a class or function name. However, we're
416 * flattening namespaces here, and the fallback behavior of
417 * consts means that we might suddenly be referring to a
418 * different const without any change to the callsite at
419 * all. Adding both dependencies explicitly captures this
420 * action-at-a-distance. *)
421 Typing_deps.add_idep genv.droot (mk_dep (snd fq_x));
422 Typing_deps.add_idep genv.droot (mk_dep (snd global_x));
423 let mem (_, s) = get_pos s in
424 match mem fq_x, mem global_x with
425 (* Found in the current namespace *)
426 | Some _, _ -> get_name fq_x
427 (* Found in the global namespace *)
428 | _, Some _ -> get_name global_x
429 (* Not found. Pick the more specific one to error on. *)
430 | None, None -> get_name fq_x
431 end else
432 get_name fq_x
434 (* For dealing with namespace fallback on functions *)
435 let elaborate_and_get_name_with_canonicalized_fallback
436 mk_dep genv get_pos get_canon x =
437 let get_name x = get_name genv get_pos x in
438 let canonicalize = canonicalize genv get_pos get_canon in
439 let fq_x = Namespaces.elaborate_id genv.namespace NSFun x in
440 let need_fallback =
441 genv.namespace.Namespace_env.ns_name <> None &&
442 not (String.contains (snd x) '\\') in
443 if need_fallback then begin
444 let global_x = (fst x, "\\" ^ (snd x)) in
445 (* Explicitly add dependencies on both of the functions we could be
446 * referring to here. Normally naming doesn't have to deal with deps at
447 * all -- they are added during typechecking just by the nature of
448 * looking up a class or function name. However, we're flattening
449 * namespaces here, and the fallback behavior of functions means that we
450 * might suddenly be referring to a different function without any
451 * change to the callsite at all. Adding both dependencies explicitly
452 * captures this action-at-a-distance. *)
453 Typing_deps.add_idep genv.droot (mk_dep (snd fq_x));
454 Typing_deps.add_idep genv.droot (mk_dep (snd global_x));
455 (* canonicalize the names being searched *)
456 let mem (_, nm) = get_canon nm in
457 match mem fq_x, mem global_x with
458 | Some _, _ -> (* Found in the current namespace *)
459 let fq_x = canonicalize fq_x `func in
460 get_name fq_x
461 | _, Some _ -> (* Found in the global namespace *)
462 let global_x = canonicalize global_x `func in
463 get_name global_x
464 | None, None ->
465 (* Not found. Pick the more specific one to error on. *)
466 get_name fq_x
467 end else
468 let fq_x = canonicalize fq_x `func in
469 get_name fq_x
471 let global_const (genv, _env) x =
472 elaborate_and_get_name_with_fallback
473 (* Same idea as Dep.FunName, see below. *)
474 (fun x -> Typing_deps.Dep.GConstName x)
475 genv
476 (GEnv.gconst_pos genv.tcopt)
479 let type_name (genv, _) x ~allow_typedef =
480 (* Generic names are not allowed to shadow class names *)
481 check_no_runtime_generic genv x;
482 let (pos, name) as x = Namespaces.elaborate_id genv.namespace NSClass x in
483 match GEnv.type_info genv.tcopt name with
484 | Some (_def_pos, `Class) ->
485 (* Don't let people use strictly internal classes
486 * (except when they are being declared in .hhi files) *)
487 if name = SN.Classes.cHH_BuiltinEnum &&
488 not (string_ends_with (Relative_path.suffix (Pos.filename pos)) ".hhi")
489 then Errors.using_internal_class pos (strip_ns name);
490 pos, name
491 | Some (def_pos, `Typedef) when not allow_typedef ->
492 Errors.unexpected_typedef pos def_pos;
493 pos, name
494 | Some (_def_pos, `Typedef) -> pos, name
495 | None ->
496 handle_unbound_name genv
497 (GEnv.type_pos genv.tcopt)
498 GEnv.type_canon_name x `cls
500 let fun_id (genv, _) x =
501 elaborate_and_get_name_with_canonicalized_fallback
502 (* Not just Dep.Fun, but Dep.FunName. This forces an incremental full
503 * redeclaration of this class if the name changes, not just a
504 * retypecheck -- the name that is referred to here actually changes as
505 * a result of what else is defined, which is stronger than just the need
506 * to retypecheck. *)
507 (fun x -> Typing_deps.Dep.FunName x)
508 genv
509 (GEnv.fun_pos genv.tcopt)
510 GEnv.fun_canon_name
513 let bind_class_member tbl (p, x) =
515 let p' = Hashtbl.find tbl x in
516 Errors.error_name_already_bound x x p p'
517 with Not_found ->
518 Hashtbl.replace tbl x p
520 let bind_class_const (genv, _env) (p, x) =
521 if String.lowercase x = "class" then Errors.illegal_member_variable_class p;
522 bind_class_member genv.class_consts (p, x)
524 let bind_prop (genv, _env) x =
525 bind_class_member genv.class_props x
527 (* Scope, keep the locals, go and name the body, and leave the
528 * local environment intact
530 let scope env f =
531 let _genv, lenv = env in
532 let lenv_copy = !(lenv.locals) in
533 let lenv_pending_copy = !(lenv.pending_locals) in
534 let res = f env in
535 lenv.locals := lenv_copy;
536 lenv.pending_locals := lenv_pending_copy;
539 let scope_all env f =
540 let _genv, lenv = env in
541 let lenv_all_locals_copy = !(lenv.all_locals) in
542 let res = scope env f in
543 let lenv_all_locals = !(lenv.all_locals) in
544 lenv.all_locals := lenv_all_locals_copy;
545 lenv_all_locals, res
547 let extend_all_locals (_genv, lenv) more_locals =
548 lenv.all_locals := SMap.union more_locals !(lenv.all_locals)
550 (** Sets up the environment so that naming can be done on the RHS of a
551 * pipe expression. It returns the identity of the $$ in the RHS and the
552 * named RHS. The steps are as follows:
553 * - Removes the $$ from the local env
554 * - Name the RHS scope
555 * - Restore the binding of $$ in the local env (if it was bound).
557 * This will append an error if $$ was not used in the RHS.
559 * The inside_pipe flag is also set before the naming and restored afterwards.
560 * *)
561 let pipe_scope env name_e2 =
562 let _, lenv = env in
563 let outer_pipe_var_opt =
564 SMap.get SN.SpecialIdents.dollardollar !(lenv.locals) in
565 let inner_locals = SMap.remove SN.SpecialIdents.dollardollar
566 !(lenv.locals) in
567 lenv.locals := inner_locals;
568 lenv.inside_pipe := true;
569 (** Name the RHS of the pipe expression. During this naming, if the $$ from
570 * this pipe is used, it will be added to the locals. *)
571 let e2 = name_e2 env in
572 let pipe_var_ident =
573 match SMap.get SN.SpecialIdents.dollardollar !(lenv.locals) with
574 | None ->
575 Errors.dollardollar_unused (fst e2);
576 (** The $$ lvar should be named when it is encountered inside e2,
577 * but we've now discovered it wasn't used at all.
578 * Create an ID here so we can keep going. *)
579 Local_id.make SN.SpecialIdents.dollardollar
580 | Some (_, x) -> x
582 let restored_locals = SMap.remove SN.SpecialIdents.dollardollar
583 !(lenv.locals) in
584 (match outer_pipe_var_opt with
585 | None -> begin
586 lenv.locals := restored_locals;
587 lenv.inside_pipe := false;
589 | Some outer_pipe_var -> begin
590 let restored_locals = SMap.add SN.SpecialIdents.dollardollar
591 outer_pipe_var restored_locals in
592 lenv.locals := restored_locals;
593 end);
594 pipe_var_ident, e2
598 (*****************************************************************************)
599 (* Helpers *)
600 (*****************************************************************************)
602 (* Alok is constantly complaining that in partial mode,
603 * he forgets to bind a type parameter, for example T,
604 * and because partial assumes T is just a class that lives
605 * in PHP land there is no error message.
606 * So to help him, I am adding a rule that if
607 * the class name starts with a T and is only 2 characters
608 * it is considered a type variable. You will not be able to
609 * define a class T in php land in this scheme ... But it is a bad
610 * name for a class anyway.
612 let is_alok_type_name (_, x) = String.length x <= 2 && x.[0] = 'T'
614 let check_constraint (_, (pos, name), _) =
615 (* TODO refactor this in a separate module for errors *)
616 if String.lowercase name = "this"
617 then Errors.this_reserved pos
618 else if name.[0] <> 'T' then Errors.start_with_T pos
620 let check_repetition s param =
621 let x = snd param.param_id in
622 if SSet.mem x s
623 then Errors.already_bound (fst param.param_id) x;
624 if x <> SN.SpecialIdents.placeholder then SSet.add x s else s
626 let convert_shape_name env = function
627 | SFlit (pos, s) -> (pos, SFlit (pos, s))
628 | SFclass_const (x, (pos, y)) ->
629 let class_name =
630 if (snd x) = SN.Classes.cSelf then
631 match (fst env).current_cls with
632 | Some (cid, _) -> cid
633 | None -> Errors.self_outside_class pos; (pos, SN.Classes.cUnknown)
634 else Env.type_name env x ~allow_typedef:false in
635 (pos, SFclass_const (class_name, (pos, y)))
637 let arg_unpack_unexpected = function
638 | [] -> ()
639 | (pos, _) :: _ -> Errors.naming_too_few_arguments pos; ()
641 module type GetLocals = sig
642 val stmt : TypecheckerOptions.t -> Namespace_env.env * Pos.t SMap.t ->
643 Ast.stmt -> Namespace_env.env * Pos.t SMap.t
644 val lvalue : TypecheckerOptions.t -> Namespace_env.env * Pos.t SMap.t ->
645 Ast.expr -> Namespace_env.env * Pos.t SMap.t
648 (* This was made a functor due to the awkward nature of how our naming
649 * code is structured.
651 * Naming is called both in the decl phase and type-check phase. In the
652 * decl phase it's mostly used to construct things that do not belong in
653 * function bodies; examples include classes, their member fields, and
654 * global constants. This part of naming is entirely self-contained; it
655 * only uses the data from the AST in the current file, and does not need
656 * to cross-reference decl type data from other files.
658 * In the type-check phase, Naming is invoked again, this time to name the
659 * bodies of functions. Now it requires decl type data in order to know
660 * which function calls are marked as `noreturn`, because this affects
661 * which local variables are considered to be defined at the end of a
662 * statement.
664 * So decl depends on naming, but naming also depends on decl, creating
665 * a circular dependency. The obvious solution would be to split it into
666 * two, but this is nontrivial because decl-phase naming also does some
667 * naming of expressions -- for example, constant initializers and default
668 * parameter values have them. Of course, none of these expressions can
669 * actually contain local variables, but our code is not written in a way
670 * that the OCaml type system can understand that. So as a hacky solution,
671 * I'm parameterizing GetLocals so that it is a no-op in the decl phase
672 * but can be properly instantiated with Typing_get_locals in the typing
673 * phase.
675 module Make (GetLocals : GetLocals) = struct
677 (************************************************************************)
678 (* Naming of type hints *)
679 (************************************************************************)
680 let rec hint
681 ?(is_static_var=false)
682 ?(forbid_this=false)
683 ?(allow_retonly=false)
684 ?(allow_typedef=true)
685 ?(allow_wildcard=false)
686 env (p, h) =
687 p, hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
688 is_static_var env h
690 and shape_field_to_shape_field_info env { sf_optional; sf_name=_; sf_hint } =
692 N.sfi_optional=sf_optional;
693 sfi_hint=hint env sf_hint;
696 and ast_shape_info_to_nast_shape_info
698 { si_allows_unknown_fields; si_shape_field_list } =
699 let f fdm shape_field =
700 let pos, name = convert_shape_name env shape_field.sf_name in
701 if ShapeMap.mem name fdm
702 then Errors.fd_name_already_bound pos;
703 ShapeMap.add
704 name (shape_field_to_shape_field_info env shape_field) fdm in
705 let nsi_field_map =
706 List.fold_left si_shape_field_list ~init:ShapeMap.empty ~f in
708 nsi_allows_unknown_fields=si_allows_unknown_fields;
709 nsi_field_map
712 and hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
713 is_static_var env x =
714 let hint =
715 hint ~is_static_var ~forbid_this ~allow_typedef ~allow_wildcard in
716 match x with
717 | Htuple hl ->
718 N.Htuple (List.map hl (hint ~allow_retonly env))
719 | Hoption h ->
720 (* void/noreturn are permitted for Typing.option_return_only_typehint *)
721 N.Hoption (hint ~allow_retonly env h)
722 | Hsoft h ->
723 let h = hint ~allow_retonly env h
724 in snd h
725 | Hfun (hl, opt, h) ->
726 N.Hfun (List.map hl (hint env), opt,
727 hint ~allow_retonly:true env h)
728 | Happly ((p, _x) as id, hl) ->
729 let hint_id =
730 hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
731 env is_static_var id
732 hl in
733 (match hint_id with
734 | N.Hprim _ | N.Hmixed ->
735 if hl <> [] then Errors.unexpected_type_arguments p
736 | _ -> ()
738 hint_id
739 | Haccess ((pos, root_id) as root, id, ids) ->
740 let root_ty =
741 match root_id with
742 | x when x = SN.Classes.cSelf ->
743 (match (fst env).current_cls with
744 | None ->
745 Errors.self_outside_class pos;
746 N.Hany
747 | Some (cid, _) ->
748 N.Happly (cid, [])
750 | x when x = SN.Classes.cStatic || x = SN.Classes.cParent ->
751 Errors.invalid_type_access_root root; N.Hany
752 | _ ->
753 let h =
754 hint_id ~forbid_this ~allow_retonly
755 ~allow_typedef ~allow_wildcard:false env is_static_var root [] in
756 (match h with
757 | N.Hthis | N.Happly _ as h -> h
758 | _ -> Errors.invalid_type_access_root root; N.Hany
761 N.Haccess ((pos, root_ty), id :: ids)
762 | Hshape ast_shape_info ->
763 N.Hshape (ast_shape_info_to_nast_shape_info env ast_shape_info)
765 and hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
766 env is_static_var (p, x as id) hl =
767 Naming_hooks.dispatch_hint_hook id;
768 let params = (fst env).type_params in
769 if is_alok_type_name id && not (SMap.mem x params)
770 then Errors.typeparam_alok id;
771 if is_static_var && SMap.mem x params
772 then Errors.generic_class_var (fst id);
773 (* some common Xhp screw ups *)
774 if (x = "Xhp") || (x = ":Xhp") || (x = "XHP")
775 then Errors.disallowed_xhp_type p x;
776 match try_castable_hint ~forbid_this env p x hl with
777 | Some h -> h
778 | None -> begin
779 match x with
780 | x when x = "_" && allow_wildcard ->
781 if hl <> [] then
782 (Errors.tparam_with_tparam p x;
783 N.Hany)
784 else
785 N.Happly(id, [])
786 | x when x.[0] = '\\' &&
787 ( x = ("\\"^SN.Typehints.void)
788 || x = ("\\"^SN.Typehints.noreturn)
789 || x = ("\\"^SN.Typehints.int)
790 || x = ("\\"^SN.Typehints.bool)
791 || x = ("\\"^SN.Typehints.float)
792 || x = ("\\"^SN.Typehints.num)
793 || x = ("\\"^SN.Typehints.string)
794 || x = ("\\"^SN.Typehints.resource)
795 || x = ("\\"^SN.Typehints.mixed)
796 || x = ("\\"^SN.Typehints.array)
797 || x = ("\\"^SN.Typehints.arraykey)
798 || x = ("\\"^SN.Typehints.integer)
799 || x = ("\\"^SN.Typehints.boolean)
800 || x = ("\\"^SN.Typehints.double)
801 || x = ("\\"^SN.Typehints.real)
802 ) ->
803 Errors.primitive_toplevel p;
804 N.Hany
805 | x when x = SN.Typehints.void && allow_retonly -> N.Hprim N.Tvoid
806 | x when x = SN.Typehints.void ->
807 Errors.return_only_typehint p `void;
808 N.Hany
809 | x when x = SN.Typehints.noreturn && allow_retonly -> N.Hprim N.Tnoreturn
810 | x when x = SN.Typehints.noreturn ->
811 Errors.return_only_typehint p `noreturn;
812 N.Hany
813 | x when x = SN.Typehints.num -> N.Hprim N.Tnum
814 | x when x = SN.Typehints.resource -> N.Hprim N.Tresource
815 | x when x = SN.Typehints.arraykey -> N.Hprim N.Tarraykey
816 | x when x = SN.Typehints.mixed -> N.Hmixed
817 | x when x = SN.Typehints.this && not forbid_this ->
818 if hl != []
819 then Errors.this_no_argument p;
820 (match (fst env).current_cls with
821 | None ->
822 Errors.this_hint_outside_class p;
823 N.Hany
824 | Some _c ->
825 N.Hthis
827 | x when x = SN.Typehints.this ->
828 (match (fst env).current_cls with
829 | None ->
830 Errors.this_hint_outside_class p
831 | Some _ ->
832 Errors.this_type_forbidden p
834 N.Hany
835 | x when x = SN.Classes.cClassname && (List.length hl) <> 1 ->
836 Errors.classname_param p;
837 N.Hprim N.Tstring
838 | _ when String.lowercase x = SN.Typehints.this ->
839 Errors.lowercase_this p x;
840 N.Hany
841 | _ when SMap.mem x params ->
842 if hl <> [] then
843 Errors.tparam_with_tparam p x;
844 N.Habstr x
845 | _ ->
846 let name = Env.type_name env id ~allow_typedef in
847 (* Note that we are intentionally setting allow_typedef to `true` here.
848 * In general, generics arguments can be typedefs -- there is no
849 * runtime restriction. *)
850 N.Happly (name, hintl ~allow_wildcard ~forbid_this ~allow_typedef:true
851 ~allow_retonly:true env hl)
854 (* Hints that are valid both as casts and type annotations. Neither
855 * casts nor annotations are a strict subset of the other: For
856 * instance, 'object' is not a valid annotation. Thus callers will
857 * have to handle the remaining cases. *)
858 and try_castable_hint ?(forbid_this=false) env p x hl =
859 let hint = hint ~forbid_this ~allow_retonly:false in
860 let canon = String.lowercase x in
861 let opt_hint = match canon with
862 | nm when nm = SN.Typehints.int -> Some (N.Hprim N.Tint)
863 | nm when nm = SN.Typehints.bool -> Some (N.Hprim N.Tbool)
864 | nm when nm = SN.Typehints.float -> Some (N.Hprim N.Tfloat)
865 | nm when nm = SN.Typehints.string -> Some (N.Hprim N.Tstring)
866 | nm when nm = SN.Typehints.array ->
867 Some (match hl with
868 | [] -> N.Harray (None, None)
869 | [val_] -> N.Harray (Some (hint env val_), None)
870 | [key_; val_] ->
871 N.Harray (Some (hint env key_), Some (hint env val_))
872 | _ -> Errors.too_many_type_arguments p; N.Hany
874 | nm when nm = SN.Typehints.darray ->
875 let darray_and_varray_allowed =
876 TypecheckerOptions.experimental_feature_enabled
877 (fst env).tcopt
878 TypecheckerOptions.experimental_darray_and_varray in
879 if not darray_and_varray_allowed then Errors.darray_not_supported p;
880 Some (match hl with
881 | []
882 | [_] -> Errors.too_few_type_arguments p; N.Hany
883 | [key_; val_] -> N.Hdarray (hint env key_, hint env val_)
884 | _ -> Errors.too_many_type_arguments p; N.Hany)
885 | nm when nm = SN.Typehints.varray ->
886 let darray_and_varray_allowed =
887 TypecheckerOptions.experimental_feature_enabled
888 (fst env).tcopt
889 TypecheckerOptions.experimental_darray_and_varray in
890 if not darray_and_varray_allowed then Errors.varray_not_supported p;
891 Some (match hl with
892 | [] -> Errors.too_few_type_arguments p; N.Hany
893 | [val_] -> N.Hvarray (hint env val_)
894 | _ -> Errors.too_many_type_arguments p; N.Hany)
895 | nm when nm = SN.Typehints.integer ->
896 Errors.primitive_invalid_alias p nm SN.Typehints.int;
897 Some (N.Hprim N.Tint)
898 | nm when nm = SN.Typehints.boolean ->
899 Errors.primitive_invalid_alias p nm SN.Typehints.bool;
900 Some (N.Hprim N.Tbool)
901 | nm when nm = SN.Typehints.double || nm = SN.Typehints.real ->
902 Errors.primitive_invalid_alias p nm SN.Typehints.float;
903 Some (N.Hprim N.Tfloat)
904 | _ -> None
906 let () = match opt_hint with
907 | Some _ when canon <> x -> Errors.primitive_invalid_alias p x canon
908 | _ -> ()
909 in opt_hint
911 and constraint_ ?(forbid_this=false) env (ck, h) = ck, hint ~forbid_this env h
913 and hintl ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard env l =
914 List.map l
915 (hint ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard env)
917 (**************************************************************************)
918 (* All the methods and static methods of an interface are "implicitly"
919 * declared as abstract
921 (**************************************************************************)
923 let add_abstract m = {m with N.m_abstract = true}
925 let add_abstractl methods = List.map methods add_abstract
927 let interface c constructor methods smethods =
928 if c.c_kind <> Cinterface then constructor, methods, smethods else
929 let constructor = Option.map constructor add_abstract in
930 let methods = add_abstractl methods in
931 let smethods = add_abstractl smethods in
932 constructor, methods, smethods
934 (**************************************************************************)
935 (* Checking for collision on method names *)
936 (**************************************************************************)
938 let check_method acc { N.m_name = (p, x); _ } =
939 if SSet.mem x acc
940 then Errors.method_name_already_bound p x;
941 SSet.add x acc
943 let check_name_collision methods =
944 ignore (List.fold_left methods ~init:SSet.empty ~f:check_method)
946 (**************************************************************************)
947 (* Checking for shadowing of method type parameters *)
948 (**************************************************************************)
950 let check_method_tparams class_tparam_names { N.m_tparams = tparams; _ } =
951 List.iter tparams begin fun (_, (p,x),_) ->
952 List.iter class_tparam_names
953 (fun (pc,xc) -> if (x = xc) then Errors.shadowed_type_param p pc x)
956 let check_tparams_shadow class_tparam_names methods =
957 List.iter methods (check_method_tparams class_tparam_names)
959 (* Naming of a class *)
960 let rec class_ nenv c =
961 let constraints = make_constraints c.c_tparams in
962 let env = Env.make_class_env nenv constraints c in
963 (* Checking for a code smell *)
964 List.iter c.c_tparams check_constraint;
965 let name = Env.type_name env c.c_name ~allow_typedef:false in
966 let smethods =
967 List.fold_right c.c_body ~init:[] ~f:(class_static_method env) in
968 let sprops = List.fold_right c.c_body ~init:[] ~f:(class_prop_static env) in
969 let props = List.fold_right c.c_body ~init:[] ~f:(class_prop env) in
970 let parents =
971 List.map c.c_extends
972 (hint ~allow_retonly:false ~allow_typedef:false env) in
973 let parents = match c.c_kind with
974 (* Make enums implicitly extend the BuiltinEnum class in order to provide
975 * utility methods. *)
976 | Cenum ->
977 let pos = fst name in
978 let enum_type = pos, N.Happly (name, []) in
979 let parent =
980 pos, N.Happly ((pos, Naming_special_names.Classes.cHH_BuiltinEnum),
981 [enum_type]) in
982 parent::parents
983 | _ -> parents in
984 let methods = List.fold_right c.c_body ~init:[] ~f:(class_method env) in
985 let uses = List.fold_right c.c_body ~init:[] ~f:(class_use env) in
986 let xhp_attr_uses =
987 List.fold_right c.c_body ~init:[] ~f:(xhp_attr_use env) in
988 let xhp_category =
989 Option.value ~default:[] @@
990 List.fold_right c.c_body ~init:None ~f:(xhp_category env) in
991 let req_implements, req_extends = List.fold_right c.c_body
992 ~init:([], []) ~f:(class_require env c.c_kind) in
993 (* Setting a class type parameters constraint to the 'this' type is weird
994 * so lets forbid it for now.
996 let tparam_l = type_paraml ~forbid_this:true env c.c_tparams in
997 let consts = List.fold_right ~f:(class_const env) c.c_body ~init:[] in
998 let typeconsts =
999 List.fold_right ~f:(class_typeconst env) c.c_body ~init:[] in
1000 let implements = List.map c.c_implements
1001 (hint ~allow_retonly:false ~allow_typedef:false env) in
1002 let constructor = List.fold_left ~f:(constructor env) ~init:None c.c_body in
1003 let constructor, methods, smethods =
1004 interface c constructor methods smethods in
1005 let class_tparam_names = List.map c.c_tparams (fun (_, x,_) -> x) in
1006 let enum = Option.map c.c_enum (enum_ env) in
1007 check_name_collision methods;
1008 check_tparams_shadow class_tparam_names methods;
1009 check_name_collision smethods;
1010 check_tparams_shadow class_tparam_names smethods;
1011 let named_class =
1012 { N.c_mode = c.c_mode;
1013 N.c_final = c.c_final;
1014 N.c_is_xhp = c.c_is_xhp;
1015 N.c_kind = c.c_kind;
1016 N.c_name = name;
1017 N.c_tparams = (tparam_l, constraints);
1018 N.c_extends = parents;
1019 N.c_uses = uses;
1020 N.c_xhp_attr_uses = xhp_attr_uses;
1021 N.c_xhp_category = xhp_category;
1022 N.c_req_extends = req_extends;
1023 N.c_req_implements = req_implements;
1024 N.c_implements = implements;
1025 N.c_consts = consts;
1026 N.c_typeconsts = typeconsts;
1027 N.c_static_vars = sprops;
1028 N.c_vars = props;
1029 N.c_constructor = constructor;
1030 N.c_static_methods = smethods;
1031 N.c_methods = methods;
1032 N.c_user_attributes = user_attributes env c.c_user_attributes;
1033 N.c_enum = enum
1036 Naming_hooks.dispatch_class_named_hook named_class;
1037 named_class
1039 and user_attributes env attrl =
1040 let seen = Hashtbl.create 0 in
1041 let tc_options = (fst env).tcopt in
1042 let validate_seen = begin fun ua_name ->
1043 let pos, name = ua_name in
1044 let existing_attr_pos =
1045 try Some (Hashtbl.find seen name)
1046 with Not_found -> None
1047 in (match existing_attr_pos with
1048 | Some p -> Errors.duplicate_user_attribute ua_name p; false
1049 | None -> Hashtbl.add seen name pos; true
1051 end in
1052 let validate_name = begin fun ua_name ->
1053 (validate_seen ua_name) && begin
1054 let pos, name = ua_name in
1055 let valid = if string_starts_with name "__"
1056 then SSet.mem name SN.UserAttributes.as_set
1057 else (TypecheckerOptions.allowed_attribute tc_options name)
1058 in if not valid then Errors.unbound_attribute_name pos name;
1059 valid
1061 end in
1062 List.fold_left attrl ~init:[] ~f:begin fun acc {ua_name; ua_params} ->
1063 if not (validate_name ua_name) then acc
1064 else let attr = {
1065 N.ua_name = ua_name;
1066 N.ua_params = List.map ua_params (expr env)
1067 } in
1068 attr :: acc
1071 and enum_ env e =
1072 { N.e_base = hint env e.e_base;
1073 N.e_constraint = Option.map e.e_constraint (hint env);
1076 and type_paraml ?(forbid_this = false) env tparams =
1077 let _, ret = List.fold_left tparams ~init:(SMap.empty, [])
1078 ~f:(fun (seen, tparaml) ((_, (p, name), _) as tparam) ->
1079 match SMap.get name seen with
1080 | None ->
1081 SMap.add name p seen, (type_param ~forbid_this env tparam)::tparaml
1082 | Some pos ->
1083 Errors.shadowed_type_param p pos name;
1084 seen, tparaml
1087 List.rev ret
1089 and type_param ~forbid_this env (variance, param_name, cstr_list) =
1090 variance,
1091 param_name,
1092 List.map cstr_list (constraint_ ~forbid_this env)
1094 and type_where_constraints env locl_cstrl =
1095 List.map locl_cstrl (fun (h1, ck, h2) ->
1096 let ty1 = hint env h1 in
1097 let ty2 = hint env h2 in
1098 (ty1, ck, ty2))
1100 and class_use env x acc =
1101 match x with
1102 | Attributes _ -> acc
1103 | Const _ -> acc
1104 | AbsConst _ -> acc
1105 | ClassUse h ->
1106 hint ~allow_typedef:false env h :: acc
1107 | XhpAttrUse _ -> acc
1108 | ClassTraitRequire _ -> acc
1109 | ClassVars _ -> acc
1110 | XhpAttr _ -> acc
1111 | XhpCategory _ -> acc
1112 | Method _ -> acc
1113 | TypeConst _ -> acc
1115 and xhp_attr_use env x acc =
1116 match x with
1117 | Attributes _ -> acc
1118 | Const _ -> acc
1119 | AbsConst _ -> acc
1120 | ClassUse _ -> acc
1121 | XhpAttrUse h ->
1122 hint ~allow_typedef:false env h :: acc
1123 | ClassTraitRequire _ -> acc
1124 | ClassVars _ -> acc
1125 | XhpAttr _ -> acc
1126 | XhpCategory _ -> acc
1127 | Method _ -> acc
1128 | TypeConst _ -> acc
1130 and xhp_category _env x acc =
1131 match x with
1132 | Attributes _ -> acc
1133 | Const _ -> acc
1134 | AbsConst _ -> acc
1135 | ClassUse _ -> acc
1136 | XhpAttrUse _ -> acc
1137 | ClassTraitRequire _ -> acc
1138 | ClassVars _ -> acc
1139 | XhpAttr _ -> acc
1140 | XhpCategory cs ->
1141 (match acc with
1142 | Some _ -> Errors.multiple_xhp_category (fst (List.hd_exn cs)); acc
1143 | None -> Some cs)
1144 | Method _ -> acc
1145 | TypeConst _ -> acc
1147 and class_require env c_kind x acc =
1148 match x with
1149 | Attributes _ -> acc
1150 | Const _ -> acc
1151 | AbsConst _ -> acc
1152 | ClassUse _ -> acc
1153 | XhpAttrUse _ -> acc
1154 | ClassTraitRequire (MustExtend, h)
1155 when c_kind <> Ast.Ctrait && c_kind <> Ast.Cinterface ->
1156 let () = Errors.invalid_req_extends (fst h) in
1158 | ClassTraitRequire (MustExtend, h) ->
1159 let acc_impls, acc_exts = acc in
1160 (acc_impls, hint ~allow_typedef:false env h :: acc_exts)
1161 | ClassTraitRequire (MustImplement, h) when c_kind <> Ast.Ctrait ->
1162 let () = Errors.invalid_req_implements (fst h) in
1164 | ClassTraitRequire (MustImplement, h) ->
1165 let acc_impls, acc_exts = acc in
1166 (hint ~allow_typedef:false env h :: acc_impls, acc_exts)
1167 | ClassVars _ -> acc
1168 | XhpAttr _ -> acc
1169 | XhpCategory _ -> acc
1170 | Method _ -> acc
1171 | TypeConst _ -> acc
1173 and constructor env acc = function
1174 | Attributes _ -> acc
1175 | Const _ -> acc
1176 | AbsConst _ -> acc
1177 | ClassUse _ -> acc
1178 | XhpAttrUse _ -> acc
1179 | ClassTraitRequire _ -> acc
1180 | ClassVars _ -> acc
1181 | XhpAttr _ -> acc
1182 | XhpCategory _ -> acc
1183 | Method ({ m_name = (p, name); _ } as m)
1184 when name = SN.Members.__construct ->
1185 (match acc with
1186 | None -> Some (method_ (fst env) m)
1187 | Some _ -> Errors.method_name_already_bound p name; acc)
1188 | Method _ -> acc
1189 | TypeConst _ -> acc
1191 and class_const env x acc =
1192 match x with
1193 | Attributes _ -> acc
1194 | Const (h, l) -> const_defl h env l @ acc
1195 | AbsConst (h, x) -> abs_const_def env h x :: acc
1196 | ClassUse _ -> acc
1197 | XhpAttrUse _ -> acc
1198 | ClassTraitRequire _ -> acc
1199 | ClassVars _ -> acc
1200 | XhpAttr _ -> acc
1201 | XhpCategory _ -> acc
1202 | Method _ -> acc
1203 | TypeConst _ -> acc
1205 and class_prop_static env x acc =
1206 match x with
1207 | Attributes _ -> acc
1208 | ClassUse _ -> acc
1209 | XhpAttrUse _ -> acc
1210 | ClassTraitRequire _ -> acc
1211 | Const _ -> acc
1212 | AbsConst _ -> acc
1213 | ClassVars (kl, h, cvl) when List.mem kl Static ->
1214 (* Static variables are shared for all classes in the hierarchy.
1215 * This makes the 'this' type completely unsafe as a type for a
1216 * static variable. See test/typecheck/this_tparam_static.php as
1217 * an example of what can occur.
1219 let h = Option.map h (hint ~forbid_this:true ~is_static_var:true env) in
1220 let cvl = List.map cvl (class_prop_ env) in
1221 let cvl = List.map cvl (fill_prop kl h) in
1222 cvl @ acc
1223 | ClassVars _ -> acc
1224 | XhpAttr _ -> acc
1225 | XhpCategory _ -> acc
1226 | Method _ -> acc
1227 | TypeConst _ -> acc
1229 and class_prop env x acc =
1230 match x with
1231 | Attributes _ -> acc
1232 | ClassUse _ -> acc
1233 | XhpAttrUse _ -> acc
1234 | ClassTraitRequire _ -> acc
1235 | Const _ -> acc
1236 | AbsConst _ -> acc
1237 | ClassVars (kl, h, cvl) when not (List.mem kl Static) ->
1238 let h = Option.map h (hint env) in
1239 let cvl = List.map cvl (class_prop_ env) in
1240 let cvl = List.map cvl (fill_prop kl h) in
1241 cvl @ acc
1242 | ClassVars _ -> acc
1243 | XhpAttr (h, cv, is_required, maybe_enum) ->
1244 let _, _, default = cv in
1245 let h = (match maybe_enum with
1246 | Some (pos, items) ->
1247 let contains_int = List.exists items begin function
1248 | _, Int _ -> true
1249 | _ -> false
1250 end in
1251 let contains_str = List.exists items begin function
1252 | _, String _ | _, String2 _ -> true
1253 | _ -> false
1254 end in
1255 if contains_int && not contains_str then
1256 Some (pos, Happly ((pos, "int"), []))
1257 else if not contains_int && contains_str then
1258 Some (pos, Happly ((pos, "string"), []))
1259 else
1260 (* If the list was empty, or if there was a mix of
1261 ints and strings, then fallback to mixed *)
1262 Some (pos, Happly ((pos, "mixed"), []))
1263 | _ -> h) in
1264 let h = (match h with
1265 | Some (p, ((Hoption _) as x)) -> Some (p, x)
1266 | Some (p, ((Happly ((_, "mixed"), [])) as x)) -> Some (p, x)
1267 | Some (p, h) ->
1268 (* If a non-nullable attribute is not marked as "@required"
1269 AND it does not have a non-null default value, make the
1270 typehint nullable for now *)
1271 if (is_required ||
1272 (match default with
1273 | None -> false
1274 | Some (_, Null) -> false
1275 | Some _ -> true))
1276 then Some (p, h)
1277 else Some (p, Hoption (p, h))
1278 | None -> None) in
1279 let h = Option.map h (hint env) in
1280 let cv = class_prop_ env cv in
1281 let cv = fill_prop [] h cv in
1282 cv :: acc
1283 | XhpCategory _ -> acc
1284 | Method _ -> acc
1285 | TypeConst _ -> acc
1287 and class_static_method env x acc =
1288 match x with
1289 | Attributes _ -> acc
1290 | ClassUse _ -> acc
1291 | XhpAttrUse _ -> acc
1292 | ClassTraitRequire _ -> acc
1293 | Const _ -> acc
1294 | AbsConst _ -> acc
1295 | ClassVars _ -> acc
1296 | XhpAttr _ -> acc
1297 | XhpCategory _ -> acc
1298 | Method m when snd m.m_name = SN.Members.__construct -> acc
1299 | Method m when List.mem m.m_kind Static -> method_ (fst env) m :: acc
1300 | Method _ -> acc
1301 | TypeConst _ -> acc
1303 and class_method env x acc =
1304 match x with
1305 | Attributes _ -> acc
1306 | ClassUse _ -> acc
1307 | XhpAttrUse _ -> acc
1308 | ClassTraitRequire _ -> acc
1309 | Const _ -> acc
1310 | AbsConst _ -> acc
1311 | ClassVars _ -> acc
1312 | XhpAttr _ -> acc
1313 | XhpCategory _ -> acc
1314 | Method m when snd m.m_name = SN.Members.__construct -> acc
1315 | Method m when not (List.mem m.m_kind Static) ->
1316 let genv = fst env in
1317 method_ genv m :: acc
1318 | Method _ -> acc
1319 | TypeConst _ -> acc
1321 and class_typeconst env x acc =
1322 match x with
1323 | Attributes _ -> acc
1324 | Const _ -> acc
1325 | AbsConst _ -> acc
1326 | ClassUse _ -> acc
1327 | XhpAttrUse _ -> acc
1328 | ClassTraitRequire _ -> acc
1329 | ClassVars _ -> acc
1330 | XhpAttr _ -> acc
1331 | XhpCategory _ -> acc
1332 | Method _ -> acc
1333 | TypeConst t -> typeconst env t :: acc
1335 and check_constant_expr (pos, e) =
1336 match e with
1337 | Unsafeexpr _ | Id _ | Null | True | False | Int _
1338 | Float _ | String _ -> ()
1339 | Class_const ((_, cls), _) when cls <> "static" -> ()
1341 | Unop ((Uplus | Uminus | Utild | Unot), e) -> check_constant_expr e
1342 | Binop (op, e1, e2) ->
1343 (* Only assignment is invalid *)
1344 (match op with
1345 | Eq _ -> Errors.illegal_constant pos
1346 | _ ->
1347 check_constant_expr e1;
1348 check_constant_expr e2)
1349 | Eif (e1, e2, e3) ->
1350 check_constant_expr e1;
1351 Option.iter e2 check_constant_expr;
1352 check_constant_expr e3
1353 | _ -> Errors.illegal_constant pos
1355 and constant_expr env e =
1356 Errors.try_with_error begin fun () ->
1357 check_constant_expr e;
1358 expr env e
1359 end (fun () -> fst e, N.Any)
1361 and const_defl h env l = List.map l (const_def h env)
1362 and const_def h env (x, e) =
1363 Env.bind_class_const env x;
1364 let h = Option.map h (hint env) in
1365 h, x, Some (constant_expr env e)
1367 and abs_const_def env h x =
1368 Env.bind_class_const env x;
1369 let h = Option.map h (hint env) in
1370 h, x, None
1372 and class_prop_ env (_, x, e) =
1373 Env.bind_prop env x;
1374 let e = Option.map e (expr env) in
1375 (* If the user has not provided a value, we initialize the member variable
1376 * ourselves to a value of type Tany. Classes might inherit from our decl
1377 * mode class that are themselves not in decl, and there's no way to figure
1378 * out what variables are initialized in a decl class without typechecking
1379 * its initalizers and constructor, which we don't want to do, so just
1380 * assume we're covered. *)
1381 let e =
1382 if (fst env).in_mode = FileInfo.Mdecl && e = None
1383 then Some (fst x, N.Any)
1384 else e
1386 N.({ cv_final = false;
1387 cv_is_xhp = ((String.sub (snd x) 0 1) = ":");
1388 cv_visibility = N.Public;
1389 cv_type = None;
1390 cv_id = x;
1391 cv_expr = e;
1394 and fill_prop kl ty x =
1395 let x = { x with N.cv_type = ty } in
1396 List.fold_left kl ~init:x ~f:begin fun x k ->
1397 (* There is no field Static, they are dissociated earlier.
1398 An abstract class variable doesn't make sense.
1400 match k with
1401 | Final -> { x with N.cv_final = true }
1402 | Static -> x
1403 | Abstract -> x
1404 | Private -> { x with N.cv_visibility = N.Private }
1405 | Public -> { x with N.cv_visibility = N.Public }
1406 | Protected -> { x with N.cv_visibility = N.Protected }
1409 and typeconst env t =
1410 (* We use the same namespace as constants within the class so we cannot have
1411 * a const and type const with the same name
1413 Env.bind_class_const env t.tconst_name;
1414 let constr = Option.map t.tconst_constraint (hint env) in
1415 let hint_ =
1416 match t.tconst_type with
1417 | None when not t.tconst_abstract ->
1418 Errors.not_abstract_without_typeconst t.tconst_name;
1419 t.tconst_constraint
1420 | Some _h when t.tconst_abstract ->
1421 Errors.abstract_with_typeconst t.tconst_name;
1422 None
1423 | h -> h
1425 let type_ = Option.map hint_ (hint env) in
1426 N.({ c_tconst_name = t.tconst_name;
1427 c_tconst_constraint = constr;
1428 c_tconst_type = type_;
1431 and func_body_had_unsafe env = Env.has_unsafe env
1433 and method_ genv m =
1434 let genv = extend_params genv m.m_tparams in
1435 let env = genv, Env.empty_local UBMErr in
1436 (* Cannot use 'this' if it is a public instance method *)
1437 let variadicity, paraml = fun_paraml env m.m_params in
1438 let acc = false, false, N.Public in
1439 let final, abs, vis = List.fold_left ~f:kind ~init:acc m.m_kind in
1440 List.iter m.m_tparams check_constraint;
1441 let tparam_l = type_paraml env m.m_tparams in
1442 let where_constraints = type_where_constraints env m.m_constrs in
1443 let ret = Option.map m.m_ret (hint ~allow_retonly:true env) in
1444 let f_kind = m.m_fun_kind in
1445 let body = (match genv.in_mode with
1446 | FileInfo.Mdecl | FileInfo.Mphp ->
1447 N.NamedBody {
1448 N.fnb_nast = [];
1449 fnb_unsafe = true;
1451 | FileInfo.Mstrict | FileInfo.Mpartial ->
1452 N.UnnamedBody {
1453 N.fub_ast = m.m_body;
1454 fub_tparams = m.m_tparams;
1455 fub_namespace = genv.namespace;
1457 ) in
1458 let attrs = user_attributes env m.m_user_attributes in
1459 { N.m_final = final ;
1460 N.m_visibility = vis ;
1461 N.m_abstract = abs ;
1462 N.m_name = m.Ast.m_name;
1463 N.m_tparams = tparam_l ;
1464 N.m_where_constraints = where_constraints ;
1465 N.m_params = paraml ;
1466 N.m_body = body ;
1467 N.m_fun_kind = f_kind ;
1468 N.m_ret = ret ;
1469 N.m_variadic = variadicity ;
1470 N.m_user_attributes = attrs;
1473 and kind (final, abs, vis) = function
1474 | Final -> true, abs, vis
1475 | Static -> final, abs, vis
1476 | Abstract -> final, true, vis
1477 | Private -> final, abs, N.Private
1478 | Public -> final, abs, N.Public
1479 | Protected -> final, abs, N.Protected
1481 and fun_paraml env l =
1482 let _names = List.fold_left ~f:check_repetition ~init:SSet.empty l in
1483 let variadicity, l = determine_variadicity env l in
1484 variadicity, List.map l (fun_param env)
1486 and determine_variadicity env l =
1487 match l with
1488 | [] -> N.FVnonVariadic, []
1489 | [x] -> (
1490 match x.param_is_variadic, x.param_id with
1491 | false, _ -> N.FVnonVariadic, [x]
1492 (* NOTE: variadic params are removed from the list *)
1493 | true, (_, "...") -> N.FVellipsis, []
1494 | true, _ -> N.FVvariadicArg (fun_param env x), []
1496 | x :: rl ->
1497 let variadicity, rl = determine_variadicity env rl in
1498 variadicity, x :: rl
1500 and fun_param env param =
1501 let p, x = param.param_id in
1502 let ident = Local_id.get x in
1503 Env.add_lvar env param.param_id (p, ident);
1504 let ty = Option.map param.param_hint (hint env) in
1505 let eopt = Option.map param.param_expr (expr env) in
1506 { N.param_hint = ty;
1507 param_is_reference = param.param_is_reference;
1508 param_is_variadic = param.param_is_variadic;
1509 param_pos = fst param.param_id;
1510 param_name = snd param.param_id;
1511 param_expr = eopt;
1514 and make_constraints paraml =
1515 List.fold_right paraml ~init:SMap.empty
1516 ~f:begin fun (_, (_, x), cstr_list) acc ->
1517 SMap.add x cstr_list acc
1520 and extend_params genv paraml =
1521 let params = List.fold_right paraml ~init:genv.type_params
1522 ~f:begin fun (_, (_, x), cstr_list) acc ->
1523 SMap.add x cstr_list acc
1524 end in
1525 { genv with type_params = params }
1527 and fun_ nenv f =
1528 let tparams = make_constraints f.f_tparams in
1529 let genv = Env.make_fun_decl_genv nenv tparams f in
1530 let lenv = Env.empty_local UBMErr in
1531 let env = genv, lenv in
1532 let h = Option.map f.f_ret (hint ~allow_retonly:true env) in
1533 let variadicity, paraml = fun_paraml env f.f_params in
1534 let x = Env.fun_id env f.f_name in
1535 List.iter f.f_tparams check_constraint;
1536 let f_tparams = type_paraml env f.f_tparams in
1537 let f_kind = f.f_fun_kind in
1538 let body = match genv.in_mode with
1539 | FileInfo.Mdecl | FileInfo.Mphp ->
1540 N.NamedBody {
1541 N.fnb_nast = [];
1542 fnb_unsafe = true;
1544 | FileInfo.Mstrict | FileInfo.Mpartial ->
1545 N.UnnamedBody {
1546 N.fub_ast = f.f_body;
1547 fub_tparams = f.f_tparams;
1548 fub_namespace = f.f_namespace;
1551 let named_fun = {
1552 N.f_mode = f.f_mode;
1553 f_ret = h;
1554 f_name = x;
1555 f_tparams = f_tparams;
1556 f_params = paraml;
1557 f_body = body;
1558 f_fun_kind = f_kind;
1559 f_variadic = variadicity;
1560 f_user_attributes = user_attributes env f.f_user_attributes;
1561 } in
1562 Naming_hooks.dispatch_fun_named_hook named_fun;
1563 named_fun
1565 and cut_and_flatten ?(replacement=Noop) env = function
1566 | [] -> []
1567 | Unsafe :: _ -> Env.set_unsafe env true; [replacement]
1568 | Block b :: rest ->
1569 (cut_and_flatten ~replacement env b) @
1570 (cut_and_flatten ~replacement env rest)
1571 | x :: rest -> x :: (cut_and_flatten ~replacement env rest)
1573 and stmt env st =
1574 match st with
1575 | Block _ -> assert false
1576 | Unsafe -> assert false
1577 | Fallthrough -> N.Fallthrough
1578 | Noop -> N.Noop
1579 | Break p -> N.Break p
1580 | Continue p -> N.Continue p
1581 | Throw e -> let terminal = not (fst env).in_try in
1582 N.Throw (terminal, expr env e)
1583 | Return (p, e) -> N.Return (p, oexpr env e)
1584 | GotoLabel _
1585 | Goto _ ->
1586 (* TODO(t17085086): Name the goto's label *)
1587 N.Noop
1588 | Static_var el -> N.Static_var (static_varl env el)
1589 | If (e, b1, b2) -> if_stmt env st e b1 b2
1590 | Do (b, e) -> do_stmt env b e
1591 | While (e, b) -> while_stmt env e b
1592 | For (st1, e, st2, b) -> for_stmt env st1 e st2 b
1593 | Switch (e, cl) -> switch_stmt env st e cl
1594 | Foreach (e, aw, ae, b)-> foreach_stmt env e aw ae b
1595 | Try (b, cl, fb) -> try_stmt env st b cl fb
1596 | Expr (cp, Call ((p, Id (fp, fn)), el, uel))
1597 when fn = SN.SpecialFunctions.invariant ->
1598 (* invariant is subject to a source-code transform in the HHVM
1599 * runtime: the arguments to invariant are lazily evaluated only in
1600 * the case in which the invariant condition does not hold. So:
1602 * invariant_violation(<condition>, <format>, <format_args...>)
1604 * ... is rewritten as:
1606 * if (!<condition>) {
1607 * invariant_violation(<format>, <format_args...>);
1610 (match el with
1611 | [] | [_] ->
1612 Errors.naming_too_few_arguments p;
1613 N.Expr (cp, N.Any)
1614 | (cond_p, cond) :: el ->
1615 let violation = (cp, Call
1616 ((p, Id (fp, "\\"^SN.SpecialFunctions.invariant_violation)), el,
1617 uel)) in
1618 if cond <> False then
1619 let b1, b2 = [Expr violation], [Noop] in
1620 let cond = cond_p, Unop (Unot, (cond_p, cond)) in
1621 if_stmt env st cond b1 b2
1622 else (* a false <condition> means unconditional invariant_violation *)
1623 N.Expr (expr env violation)
1625 | Expr e -> N.Expr (expr env e)
1627 and if_stmt env st e b1 b2 =
1628 let e = expr env e in
1629 let nsenv = (fst env).namespace in
1630 let _, vars = GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) st in
1631 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1632 let result = Env.scope env (
1633 fun env ->
1634 let all1, b1 = branch env b1 in
1635 let all2, b2 = branch env b2 in
1636 Env.extend_all_locals env all2;
1637 Env.extend_all_locals env all1;
1638 N.If (e, b1, b2)
1639 ) in
1640 Env.promote_pending env;
1641 result
1643 and do_stmt env b e =
1644 let new_scope = false in
1645 let b = block ~new_scope env b in
1646 N.Do (b, expr env e)
1648 and while_stmt env e b =
1649 let e = expr env e in
1650 N.While (e, block env b)
1652 and for_stmt env e1 e2 e3 b =
1653 let e1 = expr env e1 in
1654 let e2 = expr env e2 in
1655 let e3 = expr env e3 in
1656 Env.scope env (
1657 fun env ->
1658 N.For (e1, e2, e3, block env b)
1661 and switch_stmt env st e cl =
1662 let e = expr env e in
1663 let nsenv = (fst env).namespace in
1664 let _, vars = GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) st in
1665 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1666 let result = Env.scope env begin fun env ->
1667 let all_locals_l, cl = casel env cl in
1668 List.iter all_locals_l (Env.extend_all_locals env);
1669 N.Switch (e, cl)
1670 end in
1671 Env.promote_pending env;
1672 result
1674 and foreach_stmt env e aw ae b =
1675 let e = expr env e in
1676 Env.scope env begin fun env ->
1677 let ae = as_expr env aw ae in
1678 let b = block env b in
1679 N.Foreach (e, ae, b)
1682 and as_expr env aw = function
1683 | As_v ev ->
1684 let nsenv = (fst env).namespace in
1685 let _, vars =
1686 GetLocals.lvalue (fst env).tcopt (nsenv, SMap.empty) ev in
1687 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
1688 let ev = expr env ev in
1689 (match aw with
1690 | None -> N.As_v ev
1691 | Some p -> N.Await_as_v (p, ev))
1692 | As_kv ((p1, Lvar k), ev) ->
1693 let k = p1, N.Lvar (Env.new_lvar env k) in
1694 let nsenv = (fst env).namespace in
1695 let _, vars =
1696 GetLocals.lvalue (fst env).tcopt (nsenv, SMap.empty) ev in
1697 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
1698 let ev = expr env ev in
1699 (match aw with
1700 | None -> N.As_kv (k, ev)
1701 | Some p -> N.Await_as_kv (p, k, ev))
1702 | As_kv ((p, _), _) ->
1703 Errors.expected_variable p;
1704 let x1 = p, N.Lvar (Env.new_lvar env (p, "__internal_placeholder")) in
1705 let x2 = p, N.Lvar (Env.new_lvar env (p, "__internal_placeholder")) in
1706 (match aw with
1707 | None -> N.As_kv (x1, x2)
1708 | Some p -> N.Await_as_kv (p, x1, x2))
1710 and try_stmt env st b cl fb =
1711 let nsenv = (fst env).namespace in
1712 let _, vars =
1713 GetLocals.stmt (fst env).tcopt (nsenv, SMap.empty) st in
1714 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1715 let result = Env.scope env (
1716 fun env ->
1717 let genv, lenv = env in
1718 (* isolate finally from the rest of the try-catch: if the first
1719 * statement of the try is an uncaught exception, finally will
1720 * still be executed *)
1721 let _all_finally, fb = branch env fb in
1722 let all_locals_b, b = branch ({genv with in_try = true}, lenv) b in
1723 let all_locals_cl, cl = catchl env cl in
1724 List.iter all_locals_cl (Env.extend_all_locals env);
1725 Env.extend_all_locals env all_locals_b;
1726 N.Try (b, cl, fb)
1727 ) in
1728 Env.promote_pending env;
1729 result
1731 and block ?(new_scope=true) env stl =
1732 let stl = cut_and_flatten env stl in
1733 if new_scope
1734 then
1735 Env.scope env (
1736 fun env -> List.map stl (stmt env)
1738 else List.map stl (stmt env)
1740 and branch env stmt_l =
1741 let stmt_l = cut_and_flatten env stmt_l in
1742 Env.scope_all env begin fun env ->
1743 List.map stmt_l (stmt env)
1746 and static_varl env l = List.map l (static_var env)
1747 and static_var env = function
1748 | p, Lvar _ as lv -> expr env (p, Binop(Eq None, lv, (p, Null)))
1749 | e -> expr env e
1751 and expr_obj_get_name env = function
1752 | p, Id x -> p, N.Id x
1753 | p, e ->
1754 (match (fst env).in_mode with
1755 | FileInfo.Mstrict ->
1756 Errors.dynamic_method_call p
1757 | FileInfo.Mpartial | FileInfo.Mdecl | FileInfo.Mphp ->
1760 expr env (p, e)
1762 and exprl env l = List.map l (expr env)
1763 and oexpr env e = Option.map e (expr env)
1764 and expr env (p, e) = p, expr_ env p e
1765 and expr_ env p = function
1766 | Array l -> N.Array (List.map l (afield env))
1767 | Darray l ->
1768 N.Darray (List.map l (fun (e1, e2) -> expr env e1, expr env e2))
1769 | Varray l -> N.Varray (List.map l (expr env))
1770 | Collection (id, l) -> begin
1771 let p, cn = Namespaces.elaborate_id ((fst env).namespace) NSClass id in
1772 match cn with
1773 | x when N.is_vc_kind x ->
1774 N.ValCollection ((N.get_vc_kind cn),
1775 (List.map l (afield_value env cn)))
1776 | x when N.is_kvc_kind x ->
1777 N.KeyValCollection ((N.get_kvc_kind cn),
1778 (List.map l (afield_kvalue env cn)))
1779 | x when x = SN.Collections.cPair ->
1780 (match l with
1781 | [] ->
1782 Errors.naming_too_few_arguments p;
1783 N.Any
1784 | e1::e2::[] ->
1785 let pn = SN.Collections.cPair in
1786 N.Pair (afield_value env pn e1, afield_value env pn e2)
1787 | _ ->
1788 Errors.naming_too_many_arguments p;
1789 N.Any
1791 | _ ->
1792 Errors.expected_collection p cn;
1793 N.Any
1795 | Clone e -> N.Clone (expr env e)
1796 | Null -> N.Null
1797 | True -> N.True
1798 | False -> N.False
1799 | Int s -> N.Int s
1800 | Float s -> N.Float s
1801 | String s -> N.String s
1802 | String2 idl -> N.String2 (string2 env idl)
1803 | Id x -> N.Id (Env.global_const env x)
1804 | Id_type_arguments (_x, _hl) ->
1805 (* Shouldn't happen: parser only allows this in New *)
1806 failwith "Unexpected Id with type arguments"
1807 | Lvar (_, x) when x = SN.SpecialIdents.this -> N.This
1808 | Dollardollar ->
1809 N.Dollardollar (Env.found_dollardollar env p)
1810 | Lvar (pos, x) when x = SN.SpecialIdents.placeholder ->
1811 N.Lplaceholder pos
1812 | Lvar x ->
1813 N.Lvar (Env.lvar env x)
1814 | Lvarvar (n, x) ->
1815 N.Lvarvar (n, (Env.lvar env x))
1816 | Obj_get (e1, e2, nullsafe) ->
1817 (* If we encounter Obj_get(_,_,true) by itself, then it means "?->"
1818 is being used for instance property access; see the case below for
1819 handling nullsafe instance method calls to see how this works *)
1820 let nullsafe = match nullsafe with
1821 | OG_nullsafe -> N.OG_nullsafe
1822 | OG_nullthrows -> N.OG_nullthrows
1824 N.Obj_get (expr env e1, expr_obj_get_name env e2, nullsafe)
1825 | Array_get ((p, Lvar x), None) ->
1826 let id = p, N.Lvar (Env.lvar env x) in
1827 N.Array_get (id, None)
1828 | Array_get (e1, e2) -> N.Array_get (expr env e1, oexpr env e2)
1829 | Class_get (x1, x2) ->
1830 N.Class_get (make_class_id env x1 [], x2)
1831 | Class_const (x1, x2) ->
1832 let (genv, _) = env in
1833 let (_, name) = Namespaces.elaborate_id genv.namespace NSClass x1 in
1834 if GEnv.typedef_pos (genv.tcopt) name <> None && (snd x2) = "class" then
1835 N.Typename (Env.type_name env x1 ~allow_typedef:true)
1836 else
1837 N.Class_const (make_class_id env x1 [], x2)
1838 | Call ((_, Id (p, pseudo_func)), el, uel)
1839 when pseudo_func = SN.SpecialFunctions.echo ->
1840 arg_unpack_unexpected uel ;
1841 N.Call (N.Cnormal, (p, N.Id (p, pseudo_func)), exprl env el, [])
1842 | Call ((p, Id (_, cn)), el, uel)
1843 when cn = SN.SpecialFunctions.call_user_func ->
1844 arg_unpack_unexpected uel ;
1845 (match el with
1846 | [] -> Errors.naming_too_few_arguments p; N.Any
1847 | f :: el -> N.Call (N.Cuser_func, expr env f, exprl env el, [])
1849 | Call ((p, Id (_, cn)), el, uel) when cn = SN.SpecialFunctions.fun_ ->
1850 arg_unpack_unexpected uel ;
1851 (match el with
1852 | [] -> Errors.naming_too_few_arguments p; N.Any
1853 | [_, String (_p2, s)] when String.contains s ':' ->
1854 Errors.illegal_meth_fun p; N.Any
1855 | [_, String x] -> N.Fun_id (Env.fun_id env x)
1856 | [p, _] ->
1857 Errors.illegal_fun p;
1858 N.Any
1859 | _ -> Errors.naming_too_many_arguments p; N.Any
1861 | Call ((p, Id (_, cn)), el, uel)
1862 when cn = SN.SpecialFunctions.inst_meth ->
1863 arg_unpack_unexpected uel ;
1864 (match el with
1865 | [] -> Errors.naming_too_few_arguments p; N.Any
1866 | [_] -> Errors.naming_too_few_arguments p; N.Any
1867 | instance::(_, String meth)::[] ->
1868 N.Method_id (expr env instance, meth)
1869 | (p, _)::(_)::[] ->
1870 Errors.illegal_inst_meth p;
1871 N.Any
1872 | _ -> Errors.naming_too_many_arguments p; N.Any
1874 | Call ((p, Id (_, cn)), el, uel)
1875 when cn = SN.SpecialFunctions.meth_caller ->
1876 arg_unpack_unexpected uel ;
1877 (match el with
1878 | [] -> Errors.naming_too_few_arguments p; N.Any
1879 | [_] -> Errors.naming_too_few_arguments p; N.Any
1880 | e1::e2::[] ->
1881 (match (expr env e1), (expr env e2) with
1882 | (_, N.String cl), (_, N.String meth) ->
1883 N.Method_caller (Env.type_name env cl ~allow_typedef:false, meth)
1884 | (_, N.Class_const (N.CI (cl, _), (_, mem))), (_, N.String meth)
1885 when mem = SN.Members.mClass ->
1886 N.Method_caller (Env.type_name env cl ~allow_typedef:false, meth)
1887 | (p, _), (_) ->
1888 Errors.illegal_meth_caller p;
1889 N.Any
1891 | _ -> Errors.naming_too_many_arguments p; N.Any
1893 | Call ((p, Id (_, cn)), el, uel)
1894 when cn = SN.SpecialFunctions.class_meth ->
1895 arg_unpack_unexpected uel ;
1896 (match el with
1897 | [] -> Errors.naming_too_few_arguments p; N.Any
1898 | [_] -> Errors.naming_too_few_arguments p; N.Any
1899 | e1::e2::[] ->
1900 (match (expr env e1), (expr env e2) with
1901 | (_, N.String cl), (_, N.String meth) ->
1902 N.Smethod_id (Env.type_name env cl ~allow_typedef:false, meth)
1903 | (_, N.Id (_, const)), (_, N.String meth)
1904 when const = SN.PseudoConsts.g__CLASS__ ->
1905 (* All of these that use current_cls aren't quite correct
1906 * inside a trait, as the class should be the using class.
1907 * It's sufficient for typechecking purposes (we require
1908 * subclass to be compatible with the trait member/method
1909 * declarations).
1911 (match (fst env).current_cls with
1912 | Some (cid, _) -> N.Smethod_id (cid, meth)
1913 | None -> Errors.illegal_class_meth p; N.Any)
1914 | (_, N.Class_const (N.CI (cl, _), (_, mem))), (_, N.String meth)
1915 when mem = SN.Members.mClass ->
1916 N.Smethod_id (Env.type_name env cl ~allow_typedef:false, meth)
1917 | (p, N.Class_const ((N.CIself|N.CIstatic), (_, mem))),
1918 (_, N.String meth) when mem = SN.Members.mClass ->
1919 (match (fst env).current_cls with
1920 | Some (cid, _) -> N.Smethod_id (cid, meth)
1921 | None -> Errors.illegal_class_meth p; N.Any)
1922 | (p, _), (_) -> Errors.illegal_class_meth p; N.Any
1924 | _ -> Errors.naming_too_many_arguments p; N.Any
1926 | Call ((p, Id (_, cn)), el, uel) when cn = SN.SpecialFunctions.assert_ ->
1927 arg_unpack_unexpected uel ;
1928 if List.length el <> 1
1929 then Errors.assert_arity p;
1930 N.Assert (N.AE_assert (
1931 Option.value_map (List.hd el) ~default:(p, N.Any) ~f:(expr env)
1933 | Call ((p, Id (_, cn)), el, uel) when cn = SN.SpecialFunctions.tuple ->
1934 arg_unpack_unexpected uel ;
1935 (match el with
1936 | [] -> Errors.naming_too_few_arguments p; N.Any
1937 | el -> N.List (exprl env el)
1939 | Call ((p, Id f), el, uel) ->
1940 let qualified = Env.fun_id env f in
1941 let cn = snd qualified in
1942 (* The above special cases (fun, inst_meth, meth_caller, class_meth,
1943 * and friends) are magical language constructs, which we should
1944 * check before calling fun_id and looking up the function and doing
1945 * namespace normalization. However, gena, genva, etc are actual
1946 * functions that actually exist, we just need to handle them
1947 * specially here, during naming. Note that most of the function
1948 * special cases, such as idx, are actually handled in typing, and
1949 * don't require naming magic. *)
1950 if cn = SN.FB.fgena then begin
1951 arg_unpack_unexpected uel ;
1952 (match el with
1953 | [e] -> N.Special_func (N.Gena (expr env e))
1954 | _ -> Errors.gena_arity p; N.Any
1956 end else if cn = SN.FB.fgenva || cn = SN.HH.asio_va then begin
1957 arg_unpack_unexpected uel ;
1958 if List.length el < 1
1959 then (Errors.genva_arity p; N.Any)
1960 else N.Special_func (N.Genva (exprl env el))
1961 end else if cn = SN.FB.fgen_array_rec then begin
1962 arg_unpack_unexpected uel ;
1963 (match el with
1964 | [e] -> N.Special_func (N.Gen_array_rec (expr env e))
1965 | _ -> Errors.gen_array_rec_arity p; N.Any
1967 end else
1968 N.Call (N.Cnormal, (p, N.Id qualified),
1969 exprl env el, exprl env uel)
1970 (* Handle nullsafe instance method calls here. Because Obj_get is used
1971 for both instance property access and instance method calls, we need
1972 to match the entire "Call(Obj_get(..), ..)" pattern here so that we
1973 only match instance method calls *)
1974 | Call ((p, Obj_get (e1, e2, OG_nullsafe)), el, uel) ->
1975 N.Call
1976 (N.Cnormal,
1977 (p, N.Obj_get (expr env e1,
1978 expr_obj_get_name env e2, N.OG_nullsafe)),
1979 exprl env el, exprl env uel)
1980 (* Handle all kinds of calls that weren't handled by any of
1981 the cases above *)
1982 | Call (e, el, uel) ->
1983 N.Call (N.Cnormal, expr env e, exprl env el, exprl env uel)
1984 | Yield_break -> N.Yield_break
1985 | Yield e -> N.Yield (afield env e)
1986 | Await e -> N.Await (expr env e)
1987 | List el -> N.List (exprl env el)
1988 | Expr_list el -> N.Expr_list (exprl env el)
1989 | Cast (ty, e2) ->
1990 let (p, x), hl = match ty with
1991 | _, Happly (id, hl) -> (id, hl)
1992 | _ -> assert false in
1993 let ty = match try_castable_hint env p x hl with
1994 | Some ty -> p, ty
1995 | None -> begin
1996 match x with
1997 | x when x = SN.Typehints.object_cast ->
1998 (* (object) is a valid cast but not a valid type annotation *)
1999 (* FIXME we are not modeling the correct runtime behavior here --
2000 * the runtime result type is an stdClass if the original type is
2001 * primitive. But we should probably just disallow object casts
2002 * altogether. *)
2003 p, N.Hany
2004 | x when x = SN.Typehints.void ->
2005 Errors.void_cast p;
2006 p, N.Hany
2007 | x when x = SN.Typehints.unset_cast ->
2008 Errors.unset_cast p;
2009 p, N.Hany
2010 | _ ->
2011 (* Let's just assume that any other invalid cases are attempts to
2012 * cast to specific objects *)
2013 let h = hint ~allow_typedef:false env ty in
2014 Errors.object_cast p x;
2016 end in
2017 N.Cast (ty, expr env e2)
2018 | Unop (uop, e) -> N.Unop (uop, expr env e)
2019 | Binop (Eq None as op, lv, e2) ->
2020 if Env.inside_pipe env then
2021 Errors.unimplemented_feature p "Assignment within pipe expressions";
2022 let e2 = expr env e2 in
2023 let nsenv = (fst env).namespace in
2024 let _, vars =
2025 GetLocals.lvalue (fst env).tcopt (nsenv, SMap.empty) lv in
2026 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
2027 N.Binop (op, expr env lv, e2)
2028 | Binop (Eq _ as bop, e1, e2) ->
2029 if Env.inside_pipe env then
2030 Errors.unimplemented_feature p "Assignment within pipe expressions";
2031 let e1 = expr env e1 in
2032 N.Binop (bop, e1, expr env e2)
2033 | Binop (bop, e1, e2) ->
2034 let e1 = expr env e1 in
2035 N.Binop (bop, e1, expr env e2)
2036 | Pipe (e1, e2) ->
2037 let e1 = expr env e1 in
2038 let ident, e2 = Env.pipe_scope env
2039 begin fun env ->
2040 expr env e2
2043 N.Pipe ((p, ident), e1, e2)
2044 | Eif (e1, e2opt, e3) ->
2045 (* The order matters here, of course -- e1 can define vars that need to
2046 * be available in e2 and e3. *)
2047 let e1 = expr env e1 in
2048 let e2opt = oexpr env e2opt in
2049 let e3 = expr env e3 in
2050 N.Eif (e1, e2opt, e3)
2051 | NullCoalesce (e1, e2) ->
2052 let e1 = expr env e1 in
2053 let e2 = expr env e2 in
2054 N.NullCoalesce (e1, e2)
2055 | InstanceOf (e, (p, Id x)) ->
2056 let id = match x with
2057 | px, n when n = SN.Classes.cParent ->
2058 if (fst env).current_cls = None then
2059 let () = Errors.parent_outside_class p in
2060 N.CI ((px, SN.Classes.cUnknown), [])
2061 else N.CIparent
2062 | px, n when n = SN.Classes.cSelf ->
2063 if (fst env).current_cls = None then
2064 let () = Errors.self_outside_class p in
2065 N.CI ((px, SN.Classes.cUnknown), [])
2066 else N.CIself
2067 | px, n when n = SN.Classes.cStatic ->
2068 if (fst env).current_cls = None then
2069 let () = Errors.static_outside_class p in
2070 N.CI ((px, SN.Classes.cUnknown), [])
2071 else N.CIstatic
2072 | _ ->
2073 N.CI (Env.type_name env x ~allow_typedef:false, [])
2075 N.InstanceOf (expr env e, id)
2076 | InstanceOf (e1, (_,
2077 (Lvar _ | Obj_get _ | Class_get _ | Class_const _
2078 | Array_get _ | Call _) as e2)) ->
2079 N.InstanceOf (expr env e1, N.CIexpr (expr env e2))
2080 | InstanceOf (_e1, (p, _)) ->
2081 Errors.invalid_instanceof p;
2082 N.Any
2083 | New ((_, Id_type_arguments (x, hl)), el, uel) ->
2084 N.New (make_class_id env x hl,
2085 exprl env el,
2086 exprl env uel)
2087 | New ((_, Id x), el, uel)
2088 | New ((_, Lvar x), el, uel) ->
2089 N.New (make_class_id env x [],
2090 exprl env el,
2091 exprl env uel)
2092 | New ((p, _e), el, uel) ->
2093 if (fst env).in_mode = FileInfo.Mstrict
2094 then Errors.dynamic_new_in_strict_mode p;
2095 N.New (make_class_id env (p, SN.Classes.cUnknown) [],
2096 exprl env el,
2097 exprl env uel)
2098 | Efun (f, idl) ->
2099 let idl = List.map idl fst in
2100 let idl = List.filter idl
2101 (function (_, x) -> (x <> SN.SpecialIdents.this)) in
2102 let idl' = List.map idl (Env.lvar env) in
2103 let env = (fst env, Env.empty_local UBMErr) in
2104 List.iter2_exn idl idl' (Env.add_lvar env);
2105 let f = expr_lambda env f in
2106 N.Efun (f, idl')
2107 | Lfun f ->
2108 (* We have to build the capture list while we're finding names in
2109 the closure body---accumulate it in to_capture. *)
2110 (* semantic duplication: The logic here is also used in `uselist_lambda`.
2111 The differences are enough that it does not make sense to refactor
2112 this out for now. *)
2113 let to_capture = ref [] in
2114 let handle_unbound (p, x) =
2115 let cap = Env.lvar env (p, x) in
2116 to_capture := cap :: !to_capture;
2119 let lenv = Env.empty_local @@ UBMFunc handle_unbound in
2120 let env = (fst env, lenv) in
2121 let f = expr_lambda env f in
2122 N.Efun (f, !to_capture)
2123 | Xml (x, al, el) ->
2124 N.Xml (Env.type_name env x ~allow_typedef:false, attrl env al,
2125 exprl env el)
2126 | Shape fdl ->
2127 N.Shape begin List.fold_left fdl ~init:ShapeMap.empty
2128 ~f:begin fun fdm (pname, value) ->
2129 let pos, name = convert_shape_name env pname in
2130 if ShapeMap.mem name fdm
2131 then Errors.fd_name_already_bound pos;
2132 ShapeMap.add name (expr env value) fdm
2135 | Unsafeexpr _ ->
2136 N.Any
2137 | Import _ ->
2138 N.Any
2140 and expr_lambda env f =
2141 let h = Option.map f.f_ret (hint ~allow_retonly:true env) in
2142 let previous_unsafe = Env.has_unsafe env in
2143 (* save unsafe and yield state *)
2144 Env.set_unsafe env false;
2145 let variadicity, paraml = fun_paraml env f.f_params in
2146 let f_kind = f.f_fun_kind in
2147 (* The bodies of lambdas go through naming in the containing local
2148 * environment *)
2149 let body_nast = block env f.f_body in
2150 let unsafe = func_body_had_unsafe env in
2151 (* restore unsafe state *)
2152 Env.set_unsafe env previous_unsafe;
2153 let body = N.NamedBody {
2154 N.fnb_unsafe = unsafe;
2155 fnb_nast = body_nast;
2156 } in {
2157 N.f_mode = (fst env).in_mode;
2158 f_ret = h;
2159 f_name = f.f_name;
2160 f_params = paraml;
2161 f_tparams = [];
2162 f_body = body;
2163 f_fun_kind = f_kind;
2164 f_variadic = variadicity;
2165 f_user_attributes = user_attributes env f.f_user_attributes;
2168 and make_class_id env (p, x as cid) hl =
2169 match x with
2170 | x when x = SN.Classes.cParent ->
2171 if (fst env).current_cls = None then
2172 let () = Errors.parent_outside_class p in
2173 N.CI ((p, SN.Classes.cUnknown), [])
2174 else N.CIparent
2175 | x when x = SN.Classes.cSelf ->
2176 if (fst env).current_cls = None then
2177 let () = Errors.self_outside_class p in
2178 N.CI ((p, SN.Classes.cUnknown), [])
2179 else N.CIself
2180 | x when x = SN.Classes.cStatic -> if (fst env).current_cls = None then
2181 let () = Errors.static_outside_class p in
2182 N.CI ((p, SN.Classes.cUnknown), [])
2183 else N.CIstatic
2184 | x when x = SN.SpecialIdents.this -> N.CIexpr (p, N.This)
2185 | x when x = SN.SpecialIdents.dollardollar ->
2186 (* We won't reach here for "new $$" because the parser creates a
2187 * proper Ast.Dollardollar node, so make_class_id won't be called with
2188 * that node. In fact, the parser creates an Ast.Dollardollar for all
2189 * "$$" except in positions where a classname is expected, like in
2190 * static member access. So, we only reach here for things
2191 * like "$$::someMethod()". *)
2192 N.CIexpr(p, N.Lvar (Env.found_dollardollar env p))
2193 | x when x.[0] = '$' -> N.CIexpr (p, N.Lvar (Env.lvar env cid))
2194 | _ -> N.CI (Env.type_name env cid ~allow_typedef:false,
2195 hintl ~allow_wildcard:true ~forbid_this:false
2196 ~allow_typedef:true ~allow_retonly:true env hl
2199 and casel env l =
2200 List.map_env [] l (case env)
2202 and case env acc = function
2203 | Default b ->
2204 let b = cut_and_flatten ~replacement:Fallthrough env b in
2205 let all_locals, b = branch env b in
2206 all_locals :: acc, N.Default b
2207 | Case (e, b) ->
2208 let e = expr env e in
2209 let b = cut_and_flatten ~replacement:Fallthrough env b in
2210 let all_locals, b = branch env b in
2211 all_locals :: acc, N.Case (e, b)
2213 and catchl env l = List.map_env [] l (catch env)
2214 and catch env acc (x1, x2, b) =
2215 Env.scope env (
2216 fun env ->
2217 let x2 = Env.new_lvar env x2 in
2218 let all_locals, b = branch env b in
2219 all_locals :: acc, (Env.type_name env x1 ~allow_typedef:true, x2, b)
2222 and afield env = function
2223 | AFvalue e -> N.AFvalue (expr env e)
2224 | AFkvalue (e1, e2) -> N.AFkvalue (expr env e1, expr env e2)
2226 and afield_value env cname = function
2227 | AFvalue e -> expr env e
2228 | AFkvalue (e1, _e2) ->
2229 Errors.unexpected_arrow (fst e1) cname;
2230 expr env e1
2232 and afield_kvalue env cname = function
2233 | AFvalue e ->
2234 Errors.missing_arrow (fst e) cname;
2235 expr env e, expr env (fst e, Lvar (fst e, "__internal_placeholder"))
2236 | AFkvalue (e1, e2) -> expr env e1, expr env e2
2238 and attrl env l = List.map l (attr env)
2239 and attr env (x, e) = x, expr env e
2241 and string2 env idl =
2242 List.map idl (expr env)
2244 (**************************************************************************)
2245 (* Function/Method Body Naming: *)
2246 (* Ensure that, given a function / class, any UnnamedBody within is
2247 * transformed into a a named body *)
2248 (**************************************************************************)
2250 let func_body nenv f =
2251 match f.N.f_body with
2252 | N.NamedBody b -> b
2253 | N.UnnamedBody { N.fub_ast; N.fub_tparams; N.fub_namespace; _ } ->
2254 let genv = Env.make_fun_genv nenv
2255 SMap.empty f.N.f_mode (snd f.N.f_name) fub_namespace in
2256 let genv = extend_params genv fub_tparams in
2257 let lenv = Env.empty_local UBMErr in
2258 let env = genv, lenv in
2259 let env =
2260 List.fold_left ~f:Env.add_param f.N.f_params ~init:env in
2261 let env = match f.N.f_variadic with
2262 | N.FVellipsis | N.FVnonVariadic -> env
2263 | N.FVvariadicArg param -> Env.add_param env param
2265 let body = block env fub_ast in
2266 let unsafe = func_body_had_unsafe env in {
2267 N.fnb_nast = body;
2268 fnb_unsafe = unsafe;
2271 let meth_body genv m =
2272 let named_body = (match m.N.m_body with
2273 | N.NamedBody _ as b -> b
2274 | N.UnnamedBody {N.fub_ast; N.fub_tparams; N.fub_namespace; _} ->
2275 let genv = {genv with namespace = fub_namespace} in
2276 let genv = extend_params genv fub_tparams in
2277 let env = genv, Env.empty_local UBMErr in
2278 let env =
2279 List.fold_left ~f:Env.add_param m.N.m_params ~init:env in
2280 let env = match m.N.m_variadic with
2281 | N.FVellipsis | N.FVnonVariadic -> env
2282 | N.FVvariadicArg param -> Env.add_param env param
2284 let body = block env fub_ast in
2285 let unsafe = func_body_had_unsafe env in
2286 N.NamedBody {
2287 N.fnb_nast = body;
2288 fnb_unsafe = unsafe;
2290 ) in
2291 {m with N.m_body = named_body}
2293 let class_meth_bodies nenv nc =
2294 let _n_tparams, cstrs = nc.N.c_tparams in
2295 let genv = Env.make_class_genv nenv cstrs
2296 nc.N.c_mode (nc.N.c_name, nc.N.c_kind)
2297 Namespace_env.empty_with_default_popt
2299 let inst_meths = List.map nc.N.c_methods (meth_body genv) in
2300 let opt_constructor = match nc.N.c_constructor with
2301 | None -> None
2302 | Some c -> Some (meth_body genv c) in
2303 let static_meths = List.map nc.N.c_static_methods (meth_body genv) in
2304 { nc with
2305 N.c_methods = inst_meths;
2306 N.c_static_methods = static_meths ;
2307 N.c_constructor = opt_constructor ;
2310 (**************************************************************************)
2311 (* Typedefs *)
2312 (**************************************************************************)
2314 let typedef genv tdef =
2315 let ty = match tdef.t_kind with Alias t | NewType t -> t in
2316 let cstrs = make_constraints tdef.t_tparams in
2317 let env = Env.make_typedef_env genv cstrs tdef in
2318 let tconstraint = Option.map tdef.t_constraint (hint env) in
2319 List.iter tdef.t_tparams check_constraint;
2320 let tparaml = type_paraml env tdef.t_tparams in
2321 List.iter tparaml begin fun (_, _, constrs) ->
2322 List.iter constrs (fun (_, (pos, _)) -> Errors.typedef_constraint pos)
2323 end;
2324 let t_vis = match tdef.t_kind with
2325 | Ast.Alias _ -> N.Transparent
2326 | Ast.NewType _ -> N.Opaque
2328 let attrs = user_attributes env tdef.t_user_attributes in
2330 N.t_name = tdef.t_id;
2331 t_tparams = tparaml;
2332 t_constraint = tconstraint;
2333 t_kind = hint env ty;
2334 t_user_attributes = attrs;
2335 t_mode = tdef.t_mode;
2336 t_vis;
2339 (**************************************************************************)
2340 (* Global constants *)
2341 (**************************************************************************)
2343 let check_constant_hint cst =
2344 match cst.cst_type with
2345 | None when cst.cst_mode = FileInfo.Mstrict ->
2346 Errors.add_a_typehint (fst cst.cst_name)
2347 | None
2348 | Some _ -> ()
2350 let global_const genv cst =
2351 let env = Env.make_const_env genv cst in
2352 let hint = Option.map cst.cst_type (hint env) in
2353 let e = match cst.cst_kind with
2354 | Ast.Cst_const ->
2355 check_constant_hint cst;
2356 Some (constant_expr env cst.cst_value)
2357 (* Define allows any expression, so don't call check_constant.
2358 * Furthermore it often appears at toplevel, which we don't track at
2359 * all, so don't type or even name that expression, it may refer to
2360 * "undefined" variables that actually exist, just untracked since
2361 * they're toplevel. *)
2362 | Ast.Cst_define -> None in
2363 { N.cst_mode = cst.cst_mode;
2364 cst_name = cst.cst_name;
2365 cst_type = hint;
2366 cst_value = e;
2369 (* Uses a default empty environment to extract the use list
2370 of a lambda expression. This exists only for the sake of
2371 the dehackificator and is not meant for general use. *)
2372 let uselist_lambda f =
2373 (* semantic duplication: This is copied from the implementation of the
2374 `Lfun` variant of `expr_` defined earlier in this file. *)
2375 let to_capture = ref [] in
2376 let handle_unbound (p, x) =
2377 to_capture := x :: !to_capture;
2378 p, Local_id.tmp()
2380 let tcopt = TypecheckerOptions.make_permissive TypecheckerOptions.default in
2381 let genv = Env.make_fun_decl_genv tcopt SMap.empty f in
2382 let lenv = Env.empty_local @@ UBMFunc handle_unbound in
2383 let env = genv, lenv in
2384 ignore (expr_lambda env f);
2385 List.dedup !to_capture
2387 (**************************************************************************)
2388 (* The entry point to CHECK the program, and transform the program *)
2389 (**************************************************************************)
2391 let program tcopt ast = List.filter_map ast begin function
2392 | Ast.Fun f -> Some (N.Fun (fun_ tcopt f))
2393 | Ast.Class c -> Some (N.Class (class_ tcopt c))
2394 | Ast.Typedef t -> Some (N.Typedef (typedef tcopt t))
2395 | Ast.Constant cst -> Some (N.Constant (global_const tcopt cst))
2396 | Ast.Stmt _ -> None
2397 | Ast.Namespace _
2398 | Ast.NamespaceUse _ -> assert false
2403 include Make(struct
2404 let stmt _ acc _ = acc
2405 let lvalue _ acc _ = acc
2406 end)