AST changes to support optional type annotations for collection expressions
[hiphop-php.git] / hphp / hack / src / naming / naming.ml
blobd0e484d7d7c8b8b299263b3f1e166ffcc9d3712b
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 Utils
19 open String_utils
21 (* Alias for Nast used by functions on potentially unnamed Aast values *)
22 module Aast = Ast_to_nast.Aast
23 module N = Nast
24 module ShapeMap = N.ShapeMap
25 module SN = Naming_special_names
26 module NS = Namespaces
28 module GEnv = NamingGlobal.GEnv
30 (*****************************************************************************)
31 (* The types *)
32 (*****************************************************************************)
34 (* We want to keep the positions of names that have been
35 * replaced by identifiers.
37 type positioned_ident = (Pos.t * Local_id.t)
39 (* <T as A>, A is a type constraint *)
40 type type_constraint = bool * (Ast.constraint_kind * Ast.hint) list
41 type aast_type_constraint = bool * (Ast.constraint_kind * Aast.hint) list
43 let convert_type_constraints_to_aast =
44 Tuple.T2.map_snd ~f:(List.map ~f:(Tuple.T2.map_snd ~f:Ast_to_nast.on_hint))
46 type genv = {
48 (* strict? decl? partial? *)
49 in_mode: FileInfo.mode;
51 (* various options that control the strictness of the typechecker *)
52 tcopt: TypecheckerOptions.t;
54 (* are we in the body of a try statement? *)
55 in_try: bool;
57 (* are we in the body of a finally statement? *)
58 in_finally: bool;
60 (* are we in a __PPL attributed class *)
61 in_ppl: bool;
63 (* In function foo<T1, ..., Tn> or class<T1, ..., Tn>, the field
64 * type_params knows T1 .. Tn. It is able to find out about the
65 * constraint on these parameters. *)
66 type_params: aast_type_constraint SMap.t;
68 (* The current class, None if we are in a function *)
69 current_cls: (Ast.id * Ast.class_kind) option;
71 class_consts: (string, Pos.t) Caml.Hashtbl.t;
73 class_props: (string, Pos.t) Caml.Hashtbl.t;
75 (* Normally we don't need to add dependencies at this stage, but there
76 * are edge cases when we do. *)
77 droot: Typing_deps.Dep.variant;
79 (* Namespace environment, e.g., what namespace we're in and what use
80 * declarations are in play. *)
81 namespace: Namespace_env.env;
84 (* Handler called when we see an unbound name. *)
85 type unbound_handler = (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_handler option -> lenv
94 val make_class_genv :
95 aast_type_constraint SMap.t ->
96 FileInfo.mode ->
97 Ast.id * Ast.class_kind -> Namespace_env.env -> bool -> genv
98 val aast_make_class_env :
99 aast_type_constraint SMap.t -> Aast.class_ -> genv * lenv
100 val aast_make_typedef_env :
101 aast_type_constraint SMap.t -> Aast.typedef -> genv * lenv
102 val make_top_level_env :
103 unit -> genv * lenv
104 val make_fun_genv :
105 type_constraint SMap.t ->
106 FileInfo.mode -> string -> Namespace_env.env -> genv
107 val aast_make_fun_decl_genv :
108 aast_type_constraint SMap.t -> Aast.fun_ -> genv
109 val make_file_attributes_env :
110 FileInfo.mode -> Aast.nsenv -> genv * lenv
111 val aast_make_const_env :
112 Aast.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 aast_add_lvar : genv * lenv -> Aast.lid -> positioned_ident -> unit
122 val add_param : genv * lenv -> N.fun_param -> genv * lenv
123 val new_lvar : genv * lenv -> Ast.id -> positioned_ident
124 val new_let_local : genv * lenv -> Ast.id -> positioned_ident
125 val found_dollardollar : genv * lenv -> Pos.t -> Local_id.t
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 aast_lvar : genv * lenv -> Aast.lid -> positioned_ident
131 val let_local : genv * lenv -> Ast.id -> positioned_ident option
132 val global_const : genv * lenv -> Ast.id -> Ast.id
133 val type_name : genv * lenv -> Ast.id -> allow_typedef:bool -> allow_generics:bool -> Ast.id
134 val fun_id : genv * lenv -> Ast.id -> Ast.id
135 val bind_class_const : genv * lenv -> Ast.id -> unit
136 val goto_label : genv * lenv -> string -> Pos.t option
137 val new_goto_label : genv * lenv -> Aast.pstring -> unit
138 val new_goto_target : genv * lenv -> Aast.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 type pipe_scope = {
154 (* The identifier for the special pipe variable $$ for this pipe scope. *)
155 dollardollar : Local_id.t;
156 (* Whether the current pipe scope's $$ has been used. Used to raise error
157 * if not. *)
158 used_dollardollar : bool;
161 (* The local environment *)
162 type lenv = {
164 (* The set of locals *)
165 locals: map ref;
167 (* We keep all the locals, even if we are in a different scope
168 * to provide better error messages.
169 * if you write:
170 * if(...) {
171 * $x = ...;
173 * Technically, passed this point, $x is unbound.
174 * But it is much better to keep it somewhere, so that you can
175 * say it is bound, but in a different scope.
177 all_locals: all_locals ref;
179 (* Some statements can define new variables afterwards, e.g.,
180 * if (...) {
181 * $x = ...;
182 * } else {
183 * $x = ...;
185 * We need to give $x the same name in both branches, but we don't want
186 * $x to actually be a local until after the if block. So we stash it here,
187 * to indicate a name has been pre-allocated, but that the variable isn't
188 * actually defined yet.
190 pending_locals: map ref;
192 (* The set of lexically-scoped local `let` variables *)
193 (* TODO: Currently these locals live in a separate namespace, it is
194 * worthwhile considering unified namespace for all local variables T28712009 *)
195 let_locals: map ref;
197 (* stack of pipe scopes.
198 * We use a stack (represented by a list) because pipe operators
199 * can be nested. *)
200 pipe_locals: pipe_scope list ref;
202 (* Handler called when we see an unbound name.
203 * This is used to compute an approximation of the list of captured
204 * variables for closures: when we see an undefined variable, we add it
205 * to the list of captured variables.
207 * See expr_lambda for details.
209 unbound_handler: unbound_handler option;
211 (* The presence of an "UNSAFE" in the function body changes the
212 * verifiability of the function's return type, since the unsafe
213 * block could return. For the sanity of the typechecker, we flatten
214 * this out, but need to track if we've seen an "UNSAFE" in order to
215 * do so. *)
216 has_unsafe: bool ref;
219 * A map from goto label strings to named labels.
221 goto_labels: Pos.t SMap.t ref;
224 * A map from goto label used in a goto statement to the position of that
225 * goto label usage.
227 goto_targets: Pos.t SMap.t ref;
230 let empty_local unbound_handler = {
231 locals = ref SMap.empty;
232 all_locals = ref SMap.empty;
233 pending_locals = ref SMap.empty;
234 let_locals = ref SMap.empty;
235 pipe_locals = ref [];
236 unbound_handler;
237 has_unsafe = ref false;
238 goto_labels = ref SMap.empty;
239 goto_targets = ref SMap.empty;
242 let make_class_genv tparams mode (cid, ckind) namespace is_ppl =
243 { in_mode = mode;
244 tcopt = GlobalNamingOptions.get ();
245 in_try = false;
246 in_finally = false;
247 in_ppl = is_ppl;
248 type_params = tparams;
249 current_cls = Some (cid, ckind);
250 class_consts = Caml.Hashtbl.create 0;
251 class_props = Caml.Hashtbl.create 0;
252 droot = Typing_deps.Dep.Class (snd cid);
253 namespace;
256 let unbound_name_error genv pos name kind =
257 (* Naming pretends to be local and not dependent on other files, so it
258 * doesn't bother with adding dependencies (even though it does look up
259 * things in global state). This is mostly brushed aside because "they
260 * will be added during typing". Unfortunately, there are multiple scenarios
261 * when typechecker will name an expression, but gives up on typechecking
262 * it. We are then left with a unrecorded dependency. This should be fixed
263 * on some more basic level, but so far the only incorrectness that anyone
264 * has observed due to this is that we fail to remove "unbound name" errors
265 * sometimes. I add this dependency here for now to fix the annoyance it
266 * causes developers. *)
267 begin match kind with
268 | `func -> Typing_deps.Dep.Fun name
269 | `cls -> Typing_deps.Dep.Class name
270 | `const -> Typing_deps.Dep.GConst name
271 end |> Typing_deps.add_idep genv.droot;
272 Errors.unbound_name pos name kind
274 let aast_make_class_env tparams c =
275 let is_ppl = List.exists
276 c.Aast.c_user_attributes
277 (fun { Aast.ua_name; _ } -> snd ua_name = SN.UserAttributes.uaProbabilisticModel) in
278 let genv = make_class_genv tparams c.Aast.c_mode
279 (c.Aast.c_name, c.Aast.c_kind) c.Aast.c_namespace is_ppl in
280 let lenv = empty_local None in
281 genv, lenv
283 let make_typedef_genv cstrs tdef_name tdef_namespace = {
284 in_mode = FileInfo.Mstrict;
285 tcopt = GlobalNamingOptions.get ();
286 in_try = false;
287 in_finally = false;
288 in_ppl = false;
289 type_params = cstrs;
290 current_cls = None;
291 class_consts = Caml.Hashtbl.create 0;
292 class_props = Caml.Hashtbl.create 0;
293 droot = Typing_deps.Dep.Class tdef_name;
294 namespace = tdef_namespace;
297 let aast_make_typedef_env cstrs tdef =
298 let genv = make_typedef_genv cstrs (snd tdef.Aast.t_name) tdef.Aast.t_namespace in
299 let lenv = empty_local None in
300 genv, lenv
302 let make_fun_genv params f_mode f_name f_namespace = {
303 in_mode = f_mode;
304 tcopt = GlobalNamingOptions.get ();
305 in_try = false;
306 in_finally = false;
307 in_ppl = false;
308 type_params = SMap.map convert_type_constraints_to_aast params;
309 current_cls = None;
310 class_consts = Caml.Hashtbl.create 0;
311 class_props = Caml.Hashtbl.create 0;
312 droot = Typing_deps.Dep.Fun f_name;
313 namespace = f_namespace;
316 let aast_make_fun_genv params f_mode f_name f_namespace =
317 { in_mode = f_mode
318 ; tcopt = GlobalNamingOptions.get ()
319 ; in_try = false
320 ; in_finally = false
321 ; in_ppl = false
322 ; type_params = params
323 ; current_cls = None
324 ; class_consts = Caml.Hashtbl.create 0
325 ; class_props = Caml.Hashtbl.create 0
326 ; droot = Typing_deps.Dep.Fun f_name
327 ; namespace = f_namespace
330 let aast_make_fun_decl_genv params f =
331 aast_make_fun_genv params f.Aast.f_mode (snd f.Aast.f_name) f.Aast.f_namespace
333 let aast_make_const_genv cst = {
334 in_mode = cst.Aast.cst_mode;
335 tcopt = GlobalNamingOptions.get ();
336 in_try = false;
337 in_finally = false;
338 in_ppl = false;
339 type_params = SMap.empty;
340 current_cls = None;
341 class_consts = Caml.Hashtbl.create 0;
342 class_props = Caml.Hashtbl.create 0;
343 droot = Typing_deps.Dep.GConst (snd cst.Aast.cst_name);
344 namespace = cst.Aast.cst_namespace;
347 let make_top_level_genv () = {
348 in_mode = FileInfo.Mpartial;
349 tcopt = GlobalNamingOptions.get ();
350 in_try = false;
351 in_finally = false;
352 in_ppl = false;
353 type_params = SMap.empty;
354 current_cls = None;
355 class_consts = Caml.Hashtbl.create 0;
356 class_props = Caml.Hashtbl.create 0;
357 droot = Typing_deps.Dep.Fun "";
358 namespace = Namespace_env.empty_with_default_popt;
361 let make_top_level_env () =
362 let genv = make_top_level_genv () in
363 let lenv = empty_local None in
364 let env = genv, lenv in
367 let make_file_attributes_genv mode namespace =
369 in_mode = mode;
370 tcopt = GlobalNamingOptions.get ();
371 in_try = false;
372 in_finally = false;
373 in_ppl = false;
374 type_params = SMap.empty;
375 current_cls = None;
376 class_consts = Caml.Hashtbl.create 0;
377 class_props = Caml.Hashtbl.create 0;
378 droot = Typing_deps.Dep.Fun "";
379 namespace = namespace;
382 let make_file_attributes_env mode namespace =
383 let genv = make_file_attributes_genv mode namespace in
384 let lenv = empty_local None in
385 let env = genv, lenv in
388 let aast_make_const_env cst =
389 let genv = aast_make_const_genv cst in
390 let lenv = empty_local None in
391 let env = genv, lenv in
394 let has_unsafe (_genv, lenv) = !(lenv.has_unsafe)
395 let set_unsafe (_genv, lenv) x =
396 lenv.has_unsafe := x
398 let in_ppl (genv, _lenv) = genv.in_ppl
400 let set_ppl (genv, lenv) in_ppl =
401 let genv = { genv with in_ppl } in
402 (genv, lenv)
404 let lookup genv (env : string -> FileInfo.pos option) (p, x) =
405 let v = env x in
406 match v with
407 | None ->
408 (match genv.in_mode with
409 | FileInfo.Mstrict | FileInfo.Mexperimental -> unbound_name_error genv p x `const
410 | FileInfo.Mpartial | FileInfo.Mdecl when not
411 (TypecheckerOptions.assume_php genv.tcopt) ->
412 unbound_name_error genv p x `const
413 | FileInfo.Mphp | FileInfo.Mdecl | FileInfo.Mpartial -> ()
415 | _ -> ()
417 let handle_unbound_name genv get_full_pos get_canon (p, name) kind =
418 match get_canon name with
419 | Some canonical ->
420 canonical
421 |> get_full_pos
422 |> Option.iter ~f:(fun p_canon ->
423 Errors.did_you_mean_naming p name p_canon canonical);
424 (* Recovering from the capitalization error means
425 * returning the name in its canonical form *)
426 p, canonical
427 | None ->
428 (match genv.in_mode with
429 | FileInfo.Mpartial | FileInfo.Mdecl
430 when TypecheckerOptions.assume_php genv.tcopt
431 || name = SN.Classes.cUnknown -> ()
432 | FileInfo.Mphp -> ()
433 | FileInfo.Mstrict | FileInfo.Mexperimental -> unbound_name_error genv p name kind
434 | FileInfo.Mpartial | FileInfo.Mdecl ->
435 unbound_name_error genv p name kind
437 p, name
439 let canonicalize genv get_pos get_full_pos get_canon (p, name) kind =
440 (* Get the canonical name to check if the name exists in the heap *)
441 match get_pos name with
442 | Some _ -> p, name
443 | None -> handle_unbound_name genv get_full_pos get_canon (p, name) kind
445 let check_variable_scoping env (_p, x) =
446 match SMap.get x !(env.all_locals) with
447 | Some _p' -> () (*Errors.different_scope p x p'*)
448 | None -> ()
450 (* Adds a local variable, without any check *)
451 let add_lvar (_, lenv) (_, name) (p, x) =
452 lenv.locals := SMap.add name (p, x) !(lenv.locals);
455 let aast_add_lvar (_, lenv) (_, lid) (p, x) =
456 lenv.locals := SMap.add (Local_id.to_string lid) (p, x) !(lenv.locals);
459 let add_param env param =
460 let p_name = param.N.param_name in
461 let id = Local_id.get p_name in
462 let p_pos = param.N.param_pos in
463 let () = add_lvar env (p_pos, p_name) (p_pos, id) in
466 (* Defines a new local variable.
467 Side effects:
468 1) if the local is not in the local environment then it is added.
469 Return value: the given position and deduced/created identifier. *)
470 let new_lvar (_, lenv) (p, x) =
471 let lcl = SMap.get x !(lenv.locals) in
472 let ident =
473 match lcl with
474 | Some lcl -> snd lcl
475 | None ->
476 let ident = (match SMap.get x !(lenv.pending_locals) with
477 | Some (_, ident) -> ident
478 | None -> Local_id.make_unscoped x) in
479 lenv.all_locals := SMap.add x p !(lenv.all_locals);
480 lenv.locals := SMap.add x (p, ident) !(lenv.locals);
481 ident
483 p, ident
485 (* Defines a new scoped local variable
486 * Side effects:
487 * Always add a new variable in the local environment.
488 * If the variable has been defined already, shadow the previously-defined
489 * variable *)
490 (* TODO: Emit warning if names are getting shadowed T28436131 *)
491 let new_let_local (_, lenv) (p, x) =
492 let ident = Local_id.make_scoped x in
493 lenv.all_locals := SMap.add x p !(lenv.all_locals);
494 lenv.let_locals := SMap.add x (p, ident) !(lenv.let_locals);
495 p, ident
497 (* Check $$ is defined (i.e. we are indeed in a pipe) and if yes, mark it
498 * as used and get the identifier for it. *)
499 let found_dollardollar (_, lenv) p =
500 match !(lenv.pipe_locals) with
501 | [] ->
502 Errors.undefined ~in_rx_scope:false p SN.SpecialIdents.dollardollar; (* TODO better error *)
503 Local_id.make_scoped SN.SpecialIdents.dollardollar
504 | pipe_scope :: scopes ->
505 let pipe_scope = { pipe_scope with used_dollardollar = true } in
506 lenv.pipe_locals := pipe_scope :: scopes;
507 pipe_scope.dollardollar
509 let inside_pipe (_, lenv) =
510 match !(lenv.pipe_locals) with
511 | [] -> false
512 | _ -> true
514 let new_pending_lvar (_, lenv) (p, x) =
515 match SMap.get x !(lenv.locals), SMap.get x !(lenv.pending_locals) with
516 | None, None ->
517 let y = p, Local_id.make_unscoped x in
518 lenv.pending_locals := SMap.add x y !(lenv.pending_locals)
519 | _ -> ()
521 let promote_pending_lvar (_, lenv) x =
522 match SMap.get x !(lenv.pending_locals) with
523 | Some (p, ident) ->
524 lenv.locals := SMap.add x (p, ident) !(lenv.locals);
525 lenv.pending_locals := SMap.remove x !(lenv.pending_locals)
526 | None -> ()
528 let handle_undefined_variable (_genv, env) (p, x) =
529 match env.unbound_handler with
530 | None -> p, Local_id.make_unscoped x
531 | Some f -> f (p, x)
533 (* Function used to name a local variable *)
534 let lvar (genv, env) (p, x) =
535 let p, ident =
536 if SN.Superglobals.is_superglobal x && genv.in_mode = FileInfo.Mpartial
537 then p, Local_id.make_unscoped x
538 else
539 let lcl = SMap.get x !(env.locals) in
540 match lcl with
541 | Some lcl -> p, snd lcl
542 | None ->
543 check_variable_scoping env (p, x);
544 handle_undefined_variable (genv, env) (p, x)
546 p, ident
548 let aast_lvar (genv, env) (p, x) =
549 lvar (genv, env) (p, Local_id.to_string x)
551 let let_local (_genv, env) (p, x) =
552 let lcl = SMap.get x !(env.let_locals) in
553 match lcl with
554 | Some lcl -> Some (p, snd lcl)
555 | None -> None
557 let get_name genv get_pos x =
558 lookup genv get_pos x; x
560 (* For dealing with namespace resolution on functions *)
561 let elaborate_and_get_name_with_canonicalized_fallback
562 genv
563 (get_pos : string -> FileInfo.pos option)
564 (get_full_pos : string -> Pos.t option)
565 get_canon x =
566 let get_name x = get_name genv get_pos x in
567 let canonicalize = canonicalize genv get_pos get_full_pos get_canon in
568 let fq_x = NS.elaborate_id genv.namespace NS.ElaborateFun x in
569 let fq_x = canonicalize fq_x `func in
570 get_name fq_x
572 let global_const (genv, _env) x =
573 let fq_x = NS.elaborate_id genv.namespace NS.ElaborateConst x in
574 get_name genv (Naming_heap.ConstPosHeap.get) fq_x
576 let type_name (genv, _) x ~allow_typedef ~allow_generics =
577 let (p, name) = x in
578 match SMap.find_opt name genv.type_params with
579 | Some (reified, _) ->
580 if not allow_generics then Errors.generics_not_allowed p;
581 if not reified then Errors.generic_at_runtime p;
583 | None ->
584 let (pos, name) as x = NS.elaborate_id genv.namespace NS.ElaborateClass x in
585 match Naming_heap.TypeIdHeap.get name with
586 | Some (_def_pos, `Class) ->
587 (* Don't let people use strictly internal classes
588 * (except when they are being declared in .hhi files) *)
589 if name = SN.Classes.cHH_BuiltinEnum &&
590 not (string_ends_with (Relative_path.suffix (Pos.filename pos)) ".hhi")
591 then Errors.using_internal_class pos (strip_ns name);
592 pos, name
593 | Some (def_pos, `Typedef) when not allow_typedef ->
594 let full_pos, _ = GEnv.get_full_pos (def_pos, name) in
595 Errors.unexpected_typedef pos full_pos;
596 pos, name
597 | Some (_def_pos, `Typedef) -> pos, name
598 | None ->
599 handle_unbound_name genv
600 GEnv.type_pos
601 GEnv.type_canon_name x `cls
603 let fun_id (genv, _) x =
604 elaborate_and_get_name_with_canonicalized_fallback
605 genv
606 (Naming_heap.FunPosHeap.get)
607 GEnv.fun_pos
608 GEnv.fun_canon_name
611 let bind_class_member tbl (p, x) =
613 let p' = Caml.Hashtbl.find tbl x in
614 Errors.error_name_already_bound x x p p'
615 with Caml.Not_found ->
616 Caml.Hashtbl.replace tbl x p
618 let bind_class_const (genv, _env) (p, x) =
619 if String.lowercase x = "class" then Errors.illegal_member_variable_class p;
620 bind_class_member genv.class_consts (p, x)
623 * Returns the position of the goto label declaration, if it exists.
625 let goto_label (_, { goto_labels; _ }) label =
626 SMap.get label !goto_labels
629 * Adds a goto label and the position of its declaration to the known labels.
631 let new_goto_label (_, { goto_labels; _ }) (pos, label) =
632 goto_labels := SMap.add label pos !goto_labels
635 * Adds a goto target and its reference position to the known targets.
637 let new_goto_target (_, { goto_targets; _ }) (pos, label) =
638 goto_targets := SMap.add label pos !goto_targets
641 * Ensures that goto statements do not reference goto labels that are not
642 * known within the current lenv.
644 let check_goto_references (_, { goto_labels; goto_targets; _ }) =
645 let check_label referenced_label referenced_label_pos =
646 if not (SMap.mem referenced_label !goto_labels) then
647 Errors.goto_label_undefined referenced_label_pos referenced_label in
648 SMap.iter check_label !goto_targets
650 (* Scope, keep the locals, go and name the body, and leave the
651 * local environment intact
653 let scope env f =
654 let _genv, lenv = env in
655 let lenv_copy = !(lenv.locals) in
656 let lenv_pending_copy = !(lenv.pending_locals) in
657 let lenv_scoped_copy = !(lenv.let_locals) in
658 let res = f env in
659 lenv.locals := lenv_copy;
660 lenv.pending_locals := lenv_pending_copy;
661 lenv.let_locals := lenv_scoped_copy;
664 let remove_locals env vars =
665 let _genv, lenv = env in
666 lenv.locals :=
667 List.fold_left vars ~f:(fun l id -> SMap.remove (snd id) l) ~init:!(lenv.locals)
669 let scope_all env f =
670 let _genv, lenv = env in
671 let lenv_all_locals_copy = !(lenv.all_locals) in
672 let res = scope env f in
673 let lenv_all_locals = !(lenv.all_locals) in
674 lenv.all_locals := lenv_all_locals_copy;
675 lenv_all_locals, res
677 (* Add a new lexical scope for block-scoped `let` variables.
678 No other changes in the local environment *)
679 let scope_lexical env f =
680 let _genv, lenv = env in
681 let lenv_scoped_copy = !(lenv.let_locals) in
682 let res = f env in
683 lenv.let_locals := lenv_scoped_copy;
686 (* Copy the let locals from lenv1 to lenv2 *)
687 let copy_let_locals (_genv1, lenv1) (_genv2, lenv2) =
688 let let_locals_1 = !(lenv1.let_locals) in
689 lenv2.let_locals := let_locals_1
691 let extend_all_locals (_genv, lenv) more_locals =
692 lenv.all_locals := SMap.union more_locals !(lenv.all_locals)
694 (** Push a new pipe scope on the stack of pipe scopes in the environment
695 * and create an identifier for the $$ variable associated to this pipe,
696 * then perform the naming function [name_e2],
697 * then finally pops the added pipe scope.
698 * Append an error if $$ was not used in the RHS.
700 * Return the identifier for the $$ of this pipe and the names RHS.
701 * *)
702 let pipe_scope env name_e2 =
703 let _, lenv = env in
704 let pipe_var_ident = Local_id.make_scoped SN.SpecialIdents.dollardollar in
705 let pipe_scope = {
706 dollardollar = pipe_var_ident;
707 used_dollardollar = false;
708 } in
709 lenv.pipe_locals := pipe_scope :: !(lenv.pipe_locals);
710 (** Name the RHS of the pipe expression. *)
711 let e2 = name_e2 env in
712 let (pipe_scope, pipe_scopes) = match !(lenv.pipe_locals) with
713 | [] -> assert false
714 | pipe_scope :: pipe_scopes -> (pipe_scope, pipe_scopes) in
715 if not pipe_scope.used_dollardollar then
716 Errors.dollardollar_unused (fst e2);
717 lenv.pipe_locals := pipe_scopes;
718 pipe_var_ident, e2
721 (*****************************************************************************)
722 (* Helpers *)
723 (*****************************************************************************)
725 let aast_check_constraint { Aast.tp_name = (pos, name); _ } =
726 if String.lowercase name = "this"
727 then Errors.this_reserved pos
728 else if name.[0] <> 'T' then Errors.start_with_T pos
730 let aast_check_repetition s param =
731 let name = param.Aast.param_name in
732 if SSet.mem name s
733 then Errors.already_bound param.Aast.param_pos name;
734 if name <> SN.SpecialIdents.placeholder
735 then SSet.add name s
736 else s
738 let convert_shape_name env = function
739 | Ast.SFlit_int (pos, s) -> (pos, Ast.SFlit_int (pos, s))
740 | Ast.SFlit_str (pos, s) -> (pos, Ast.SFlit_str (pos, s))
741 | Ast.SFclass_const (x, (pos, y)) ->
742 let class_name =
743 if (snd x) = SN.Classes.cSelf then
744 match (fst env).current_cls with
745 | Some (cid, _) -> cid
746 | None -> Errors.self_outside_class pos; (pos, SN.Classes.cUnknown)
747 else Env.type_name env x ~allow_typedef:false ~allow_generics:false in
748 (pos, Ast.SFclass_const (class_name, (pos, y)))
750 let arg_unpack_unexpected = function
751 | [] -> ()
752 | (pos, _) :: _ -> Errors.naming_too_few_arguments pos; ()
754 module type GetLocals = sig
755 val stmt : Namespace_env.env * Pos.t SMap.t ->
756 Ast.stmt -> Namespace_env.env * Pos.t SMap.t
757 val lvalue : Namespace_env.env * Pos.t SMap.t ->
758 Ast.expr -> Namespace_env.env * Pos.t SMap.t
760 val aast_lvalue : Namespace_env.env * Pos.t SMap.t ->
761 Aast.expr -> Namespace_env.env * Pos.t SMap.t
762 val aast_stmt : Namespace_env.env * Pos.t SMap.t ->
763 Aast.stmt -> Namespace_env.env * Pos.t SMap.t
766 (* This was made a functor due to the awkward nature of how our naming
767 * code is structured.
769 * Naming is called both in the decl phase and type-check phase. In the
770 * decl phase it's mostly used to construct things that do not belong in
771 * function bodies; examples include classes, their member fields, and
772 * global constants. This part of naming is entirely self-contained; it
773 * only uses the data from the AST in the current file, and does not need
774 * to cross-reference decl type data from other files.
776 * In the type-check phase, Naming is invoked again, this time to name the
777 * bodies of functions. Now it requires decl type data in order to know
778 * which function calls are marked as `noreturn`, because this affects
779 * which local variables are considered to be defined at the end of a
780 * statement.
782 * So decl depends on naming, but naming also depends on decl, creating
783 * a circular dependency. The obvious solution would be to split it into
784 * two, but this is nontrivial because decl-phase naming also does some
785 * naming of expressions -- for example, constant initializers and default
786 * parameter values have them. Of course, none of these expressions can
787 * actually contain local variables, but our code is not written in a way
788 * that the OCaml type system can understand that. So as a hacky solution,
789 * I'm parameterizing GetLocals so that it is a no-op in the decl phase
790 * but can be properly instantiated with Typing_get_locals in the typing
791 * phase.
793 module Make (GetLocals : GetLocals) = struct
794 (************************************************************************)
795 (* Naming of type hints *)
796 (************************************************************************)
798 * The existing hint function goes from Ast.hint -> Nast.hint
799 * This hint function goes from Aast.hint -> Nast.hint
800 * Used with with Ast_to_nast to go from Ast.hint -> Nast.hint
802 let rec aast_hint
803 ?(forbid_this=false)
804 ?(allow_retonly=false)
805 ?(allow_typedef=true)
806 ?(allow_wildcard=false)
807 ?(in_where_clause=false)
808 ?(tp_depth=0)
809 env (hh : Aast.hint) =
810 let mut, (p, h) = aast_unwrap_mutability hh in
811 if Option.is_some mut
812 then Errors.misplaced_mutability_hint p;
813 p, aast_hint_
814 ~forbid_this
815 ~allow_retonly
816 ~allow_typedef
817 ~allow_wildcard
818 ~in_where_clause
819 ~tp_depth
820 env h
822 and aast_unwrap_mutability p =
823 match p with
824 | _, Aast.Happly ((_, "Mutable"), [t]) -> Some N.PMutable, t
825 | _, Aast.Happly ((_, "MaybeMutable"), [t]) -> Some N.PMaybeMutable, t
826 | _, Aast.Happly ((_, "OwnedMutable"), [t]) -> Some N.POwnedMutable, t
827 | t -> None, t
829 and aast_hfun env reactivity is_coroutine hl kl variadic_hint h =
830 let variadic_hint =
831 match variadic_hint with
832 | Aast.Hnon_variadic
833 | Aast.Hvariadic None -> variadic_hint
834 | Aast.Hvariadic (Some h) -> N.Hvariadic (Some (aast_hint env h)) in
835 let muts, hl =
836 List.map ~f:(fun h ->
837 let mut, h1 = aast_unwrap_mutability h in
838 if Option.is_some mut && reactivity = N.FNonreactive
839 then Errors.mutability_hint_in_non_rx_function (fst h);
840 mut, aast_hint env h1)
842 |> List.unzip in
843 let ret_mut, rh = aast_unwrap_mutability h in
844 let ret_mut =
845 match ret_mut with
846 | None -> false
847 | Some N.POwnedMutable -> true
848 | Some _ ->
849 Errors.invalid_mutability_in_return_type_hint (fst h);
850 true in
851 N.Hfun (reactivity, is_coroutine, hl, kl, muts,
852 variadic_hint, aast_hint ~allow_retonly:true env rh, ret_mut)
854 and aast_hint_ ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard
855 ~in_where_clause ?(tp_depth=0)
856 env x =
857 let aast_hint =
858 aast_hint ~forbid_this ~allow_typedef ~allow_wildcard in
859 match x with
860 | Aast.Htuple hl ->
861 N.Htuple (List.map hl ~f:(aast_hint ~allow_retonly env))
862 | Aast.Hoption h ->
863 (* void/noreturn are permitted for Typing.option_return_only_typehint *)
864 N.Hoption (aast_hint ~allow_retonly env h)
865 | Aast.Hsoft h ->
866 let h = aast_hint ~allow_retonly env h
867 in snd h
868 | Aast.Hfun (reactivity, coroutine, hl, kl, _, variadic_hint, h, _) ->
869 aast_hfun env reactivity coroutine hl kl variadic_hint h
870 (* Special case for Rx<function> *)
871 | Aast.Happly
872 ((_, "Rx"), [(_, Aast.Hfun (_, is_coroutine, hl, kl, _, variadic_hint, h, _))]) ->
873 aast_hfun env N.FReactive is_coroutine hl kl variadic_hint h
874 (* Special case for RxShallow<function> *)
875 | Aast.Happly
876 ((_, "RxShallow"),
877 [(_, Aast.Hfun (_, is_coroutine, hl, kl, _, variadic_hint, h, _))]) ->
878 aast_hfun env N.FShallow is_coroutine hl kl variadic_hint h
879 (* Special case for RxLocal<function> *)
880 | Aast.Happly
881 ((_, "RxLocal"),
882 [(_, Aast.Hfun (_, is_coroutine, hl, kl, _, variadic_hint, h, _))]) ->
883 aast_hfun env N.FLocal is_coroutine hl kl variadic_hint h
884 | Aast.Happly ((p, _x) as id, hl) ->
885 let hint_id =
886 aast_hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
887 env id
888 hl in
889 (match hint_id with
890 | N.Hprim _ | N.Hmixed | N.Hnonnull | N.Hnothing ->
891 if hl <> [] then Errors.unexpected_type_arguments p
892 | _ -> ()
894 hint_id
895 | Aast.Haccess ((pos, root_id), ids) ->
896 let root_ty =
897 match root_id with
898 | Aast.Happly ((pos, x), _) when x = SN.Classes.cSelf ->
899 begin
900 match (fst env).current_cls with
901 | None -> Errors.self_outside_class pos; N.Hany
902 | Some (cid, _) -> N.Happly (cid, [])
904 | Aast.Happly ((pos, x), _) when x = SN.Classes.cStatic || x = SN.Classes.cParent ->
905 Errors.invalid_type_access_root (pos, x); N.Hany
906 | Aast.Happly (root, _) ->
907 let h = aast_hint_id ~forbid_this ~allow_retonly ~allow_typedef
908 ~allow_wildcard:false ~tp_depth env root [] in
909 begin
910 match h with
911 | N.Hthis
912 | N.Happly _ -> h
913 | N.Habstr _ when in_where_clause -> h
914 | _ -> Errors.invalid_type_access_root root; N.Hany
916 | _ ->
917 Errors.internal_error
919 "Malformed hint: expected Haccess (Happly ...) from ast_to_nast";
920 N.Hany
922 N.Haccess ((pos, root_ty), ids)
923 | Aast.Hshape { Aast.nsi_allows_unknown_fields; nsi_field_map } ->
924 let nsi_field_map =
925 ShapeMap.fold
926 (fun key { Aast.sfi_optional; sfi_hint } acc ->
927 let _pos, new_key = convert_shape_name env key in
928 let new_field = { N.sfi_optional; sfi_hint = aast_hint ~allow_retonly env sfi_hint } in
929 ShapeMap.add new_key new_field acc)
930 nsi_field_map
931 ShapeMap.empty in
932 N.Hshape { N.nsi_allows_unknown_fields; nsi_field_map }
933 | Aast.Hany
934 | Aast.Hmixed
935 | Aast.Hnonnull
936 | Aast.Habstr _
937 | Aast.Harray _
938 | Aast.Hdarray _
939 | Aast.Hvarray _
940 | Aast.Hvarray_or_darray _
941 | Aast.Hprim _
942 | Aast.Hthis
943 | Aast.Hdynamic
944 | Aast.Hnothing ->
945 Errors.internal_error Pos.none "Unexpected hint not present on legacy AST";
946 N.Hany
948 and aast_hint_id ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth
949 env (p, x as id) hl =
950 let params = (fst env).type_params in
951 let allow_null = TypecheckerOptions.experimental_feature_enabled
952 (fst env).tcopt
953 TypecheckerOptions.experimental_null_type
955 (* some common Xhp screw ups *)
956 if (x = "Xhp") || (x = ":Xhp") || (x = "XHP")
957 then Errors.disallowed_xhp_type p x;
958 match aast_try_castable_hint ~forbid_this ~allow_wildcard ~tp_depth env p x hl with
959 | Some h -> h
960 | None -> begin
961 match x with
962 | x when x = SN.Typehints.wildcard && allow_wildcard && tp_depth = 1 ->
963 if hl <> [] then
964 (Errors.tparam_with_tparam p x;
965 N.Hany)
966 else
967 N.Happly(id, [])
968 | x when x = SN.Typehints.wildcard ->
969 Errors.wildcard_disallowed p;
970 N.Hany
971 | x when
972 ( x = ("\\"^SN.Typehints.void)
973 || x = ("\\"^SN.Typehints.null)
974 || x = ("\\"^SN.Typehints.noreturn)
975 || x = ("\\"^SN.Typehints.int)
976 || x = ("\\"^SN.Typehints.bool)
977 || x = ("\\"^SN.Typehints.float)
978 || x = ("\\"^SN.Typehints.num)
979 || x = ("\\"^SN.Typehints.string)
980 || x = ("\\"^SN.Typehints.resource)
981 || x = ("\\"^SN.Typehints.mixed)
982 || x = ("\\"^SN.Typehints.nonnull)
983 || x = ("\\"^SN.Typehints.array)
984 || x = ("\\"^SN.Typehints.arraykey)
985 || x = ("\\"^SN.Typehints.integer)
986 || x = ("\\"^SN.Typehints.boolean)
987 || x = ("\\"^SN.Typehints.double)
988 || x = ("\\"^SN.Typehints.real)
989 ) ->
990 Errors.primitive_toplevel p;
991 N.Hany
992 | x when x = "\\"^SN.Typehints.nothing &&
993 TypecheckerOptions.new_inference (fst env).tcopt ->
994 Errors.primitive_toplevel p;
995 N.Hany
996 | x when x = SN.Typehints.void && allow_retonly -> N.Hprim N.Tvoid
997 | x when x = SN.Typehints.void ->
998 Errors.return_only_typehint p `void;
999 N.Hany
1000 | x when x = SN.Typehints.noreturn && allow_retonly -> N.Hprim N.Tnoreturn
1001 | x when x = SN.Typehints.noreturn ->
1002 Errors.return_only_typehint p `noreturn;
1003 N.Hany
1004 | x when x = SN.Typehints.null && allow_null -> N.Hprim N.Tnull
1005 | x when x = SN.Typehints.num -> N.Hprim N.Tnum
1006 | x when x = SN.Typehints.resource -> N.Hprim N.Tresource
1007 | x when x = SN.Typehints.arraykey -> N.Hprim N.Tarraykey
1008 | x when x = SN.Typehints.mixed -> N.Hmixed
1009 | x when x = SN.Typehints.nonnull -> N.Hnonnull
1010 | x when x = SN.Typehints.dynamic -> N.Hdynamic
1011 | x when x = SN.Typehints.nothing &&
1012 TypecheckerOptions.new_inference (fst env).tcopt ->
1013 N.Hnothing
1014 | x when x = SN.Typehints.this && not forbid_this ->
1015 if not (phys_equal hl [])
1016 then Errors.this_no_argument p;
1017 (match (fst env).current_cls with
1018 | None ->
1019 Errors.this_hint_outside_class p;
1020 N.Hany
1021 | Some _c ->
1022 N.Hthis
1024 | x when x = SN.Typehints.this ->
1025 (match (fst env).current_cls with
1026 | None ->
1027 Errors.this_hint_outside_class p
1028 | Some _ ->
1029 Errors.this_type_forbidden p
1031 N.Hany
1032 | x when x = SN.Classes.cClassname && (List.length hl) <> 1 ->
1033 Errors.classname_param p;
1034 N.Hprim N.Tstring
1035 | _ when String.lowercase x = SN.Typehints.this ->
1036 Errors.lowercase_this p x;
1037 N.Hany
1038 | _ when SMap.mem x params ->
1039 if hl <> [] then
1040 Errors.tparam_with_tparam p x;
1041 N.Habstr x
1042 | _ ->
1043 let name = Env.type_name env id ~allow_typedef ~allow_generics:false in
1044 (* Note that we are intentionally setting allow_typedef to `true` here.
1045 * In general, generics arguments can be typedefs -- there is no
1046 * runtime restriction. *)
1047 N.Happly (name, aast_hintl ~allow_wildcard ~forbid_this ~allow_typedef:true
1048 ~allow_retonly:true ~tp_depth:(tp_depth+1) env hl)
1051 (* Hints that are valid both as casts and type annotations. Neither
1052 * casts nor annotations are a strict subset of the other: For
1053 * instance, 'object' is not a valid annotation. Thus callers will
1054 * have to handle the remaining cases. *)
1055 and aast_try_castable_hint ?(forbid_this=false) ?(allow_wildcard=false) ~tp_depth env p x hl =
1056 let aast_hint =
1057 aast_hint ~forbid_this ~tp_depth:(tp_depth+1) ~allow_wildcard ~allow_retonly:false in
1058 let canon = String.lowercase x in
1059 let opt_hint = match canon with
1060 | nm when nm = SN.Typehints.int -> Some (N.Hprim N.Tint)
1061 | nm when nm = SN.Typehints.bool -> Some (N.Hprim N.Tbool)
1062 | nm when nm = SN.Typehints.float -> Some (N.Hprim N.Tfloat)
1063 | nm when nm = SN.Typehints.string -> Some (N.Hprim N.Tstring)
1064 | nm when nm = SN.Typehints.array ->
1065 let tcopt = (fst env).tcopt in
1066 let array_typehints_disallowed =
1067 TypecheckerOptions.disallow_array_typehint tcopt in
1068 if array_typehints_disallowed
1069 then Errors.array_typehints_disallowed p;
1070 Some (match hl with
1071 | [] -> N.Harray (None, None)
1072 | [val_] -> N.Harray (Some (aast_hint env val_), None)
1073 | [key_; val_] ->
1074 N.Harray (Some (aast_hint env key_), Some (aast_hint env val_))
1075 | _ -> Errors.too_many_type_arguments p; N.Hany
1077 | nm when nm = SN.Typehints.darray ->
1078 Some (match hl with
1079 | [] ->
1080 if (fst env).in_mode = FileInfo.Mstrict then
1081 Errors.too_few_type_arguments p;
1082 N.Hdarray ((p, N.Hany), (p, N.Hany))
1083 | [_] -> Errors.too_few_type_arguments p; N.Hany
1084 | [key_; val_] -> N.Hdarray (aast_hint env key_, aast_hint env val_)
1085 | _ -> Errors.too_many_type_arguments p; N.Hany)
1086 | nm when nm = SN.Typehints.varray ->
1087 Some (match hl with
1088 | [] ->
1089 if (fst env).in_mode = FileInfo.Mstrict then
1090 Errors.too_few_type_arguments p;
1091 N.Hvarray (p, N.Hany)
1092 | [val_] -> N.Hvarray (aast_hint env val_)
1093 | _ -> Errors.too_many_type_arguments p; N.Hany)
1094 | nm when nm = SN.Typehints.varray_or_darray ->
1095 Some (match hl with
1096 | [] ->
1097 if (fst env).in_mode = FileInfo.Mstrict then
1098 Errors.too_few_type_arguments p;
1099 N.Hvarray_or_darray (p, N.Hany)
1100 | [val_] -> N.Hvarray_or_darray (aast_hint env val_)
1101 | _ -> Errors.too_many_type_arguments p; N.Hany)
1102 | nm when nm = SN.Typehints.integer ->
1103 Errors.primitive_invalid_alias p nm SN.Typehints.int;
1104 Some (N.Hprim N.Tint)
1105 | nm when nm = SN.Typehints.boolean ->
1106 Errors.primitive_invalid_alias p nm SN.Typehints.bool;
1107 Some (N.Hprim N.Tbool)
1108 | nm when nm = SN.Typehints.double || nm = SN.Typehints.real ->
1109 Errors.primitive_invalid_alias p nm SN.Typehints.float;
1110 Some (N.Hprim N.Tfloat)
1111 | _ -> None
1113 let () = match opt_hint with
1114 | Some _ when canon <> x -> Errors.primitive_invalid_alias p x canon
1115 | _ -> ()
1116 in opt_hint
1118 and aast_hintl ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth env l =
1119 List.map
1120 ~f:(aast_hint ~forbid_this ~allow_retonly ~allow_typedef ~allow_wildcard ~tp_depth env)
1123 let aast_constraint_ ?(forbid_this=false) env (ck, h) = ck, aast_hint ~forbid_this env h
1125 let aast_targ env t =
1126 aast_hint
1127 ~allow_wildcard:true
1128 ~forbid_this:false
1129 ~allow_typedef:true
1130 ~allow_retonly:true
1131 ~tp_depth:1
1132 env t
1133 let aast_targl env _ tal =
1134 List.map tal ~f:(aast_targ env)
1137 (**************************************************************************)
1138 (* All the methods and static methods of an interface are "implicitly"
1139 * declared as abstract
1141 (**************************************************************************)
1143 let add_abstract m = {m with N.m_abstract = true}
1145 let add_abstractl methods = List.map methods add_abstract
1147 let aast_interface c constructor methods smethods =
1148 if c.Aast.c_kind <> Ast.Cinterface
1149 then constructor, methods, smethods
1150 else
1151 let constructor = Option.map constructor add_abstract in
1152 let methods = add_abstractl methods in
1153 let smethods = add_abstractl smethods in
1154 constructor, methods, smethods
1156 (**************************************************************************)
1157 (* Checking for collision on method names *)
1158 (**************************************************************************)
1160 let check_method acc { N.m_name = (p, x); _ } =
1161 if SSet.mem x acc
1162 then Errors.method_name_already_bound p x;
1163 SSet.add x acc
1165 let check_name_collision methods =
1166 ignore (List.fold_left methods ~init:SSet.empty ~f:check_method)
1168 (**************************************************************************)
1169 (* Checking for shadowing of method type parameters *)
1170 (**************************************************************************)
1172 let check_method_tparams class_tparam_names { N.m_tparams = tparams; _ } =
1173 List.iter tparams begin fun { N.tp_name = (p,x); _ } ->
1174 List.iter class_tparam_names
1175 (fun (pc,xc) -> if (x = xc) then Errors.shadowed_type_param p pc x)
1178 let check_tparams_constructor class_tparam_names constructor =
1179 match constructor with
1180 | None -> ()
1181 | Some constr -> check_method_tparams class_tparam_names constr
1183 let check_tparams_shadow class_tparam_names methods =
1184 List.iter methods (check_method_tparams class_tparam_names)
1186 let aast_ensure_name_not_dynamic env e err =
1187 match e with
1188 | (_, (Aast.Id _ | Aast.Lvar _)) -> ()
1189 | (p, _) ->
1190 if (fst env).in_mode = FileInfo.Mstrict
1191 then err p
1193 (* Naming of a class *)
1194 let rec class_ c =
1195 let c = Ast_to_nast.on_class c in
1196 aast_class_ c
1198 (* Naming of a class *)
1199 and aast_class_ c =
1200 let constraints = aast_make_constraints c.Aast.c_tparams.Aast.c_tparam_list in
1201 let env = Env.aast_make_class_env constraints c in
1202 (* Checking for a code smell *)
1203 List.iter c.Aast.c_tparams.Aast.c_tparam_list aast_check_constraint;
1204 let name = Env.type_name env c.Aast.c_name ~allow_typedef:false ~allow_generics:false in
1205 let smethods = List.map ~f:(aast_method_ (fst env)) c.Aast.c_static_methods in
1206 let sprops = List.map ~f:(aast_class_prop_static env) c.Aast.c_static_vars in
1207 let attrs = aast_user_attributes env c.Aast.c_user_attributes in
1208 let const = (Attributes.find SN.UserAttributes.uaConst attrs) in
1209 let props = List.map ~f:(aast_class_prop_non_static ~const env) c.Aast.c_vars in
1210 let xhp_attrs = List.map ~f:(aast_xhp_attribute_decl env) c.Aast.c_xhp_attrs in
1211 (* These would be out of order with the old attributes, but that shouldn't matter? *)
1212 let props = props @ xhp_attrs in
1213 let parents =
1214 List.map c.Aast.c_extends
1215 (aast_hint ~allow_retonly:false ~allow_typedef:false env) in
1216 let parents =
1217 match c.Aast.c_kind with
1218 (* Make enums implicitly extend the BuiltinEnum class in order to provide
1219 * utility methods. *)
1220 | Ast.Cenum ->
1221 let pos = fst name in
1222 let enum_type = pos, N.Happly (name, []) in
1223 let parent =
1224 pos, N.Happly ((pos, Naming_special_names.Classes.cHH_BuiltinEnum),
1225 [enum_type]) in
1226 parent::parents
1227 | _ -> parents in
1228 let methods = List.map ~f:(aast_class_method env) c.Aast.c_methods in
1229 let uses = List.map ~f:(aast_hint ~allow_typedef:false env) c.Aast.c_uses in
1230 let redeclarations =
1231 List.map ~f:(aast_method_redeclaration env) c.Aast.c_method_redeclarations in
1232 let xhp_attr_uses =
1233 List.map ~f:(aast_hint ~allow_typedef:false env) c.Aast.c_xhp_attr_uses in
1234 if c.Aast.c_req_implements <> [] && c.Aast.c_kind <> Ast.Ctrait
1235 then Errors.invalid_req_implements (fst (List.hd_exn c.Aast.c_req_implements));
1236 let req_implements =
1237 List.map ~f:(aast_hint ~allow_typedef:false env) c.Aast.c_req_implements in
1238 if c.Aast.c_req_extends <> [] &&
1239 c.Aast.c_kind <> Ast.Ctrait &&
1240 c.Aast.c_kind <> Ast.Cinterface
1241 then Errors.invalid_req_extends (fst (List.hd_exn c.Aast.c_req_extends));
1242 let req_extends =
1243 List.map ~f:(aast_hint ~allow_typedef:false env) c.Aast.c_req_extends in
1244 (* Setting a class type parameters constraint to the 'this' type is weird
1245 * so lets forbid it for now.
1247 let tparam_l = aast_type_paraml ~forbid_this:true env c.Aast.c_tparams.Aast.c_tparam_list in
1248 let consts = List.map ~f:(aast_class_const env) c.Aast.c_consts in
1249 let typeconsts = List.map ~f:(aast_typeconst env) c.Aast.c_typeconsts in
1250 let implements =
1251 List.map
1252 ~f:(aast_hint ~allow_retonly:false ~allow_typedef:false env)
1253 c.Aast.c_implements in
1254 let constructor = Option.map c.Aast.c_constructor (aast_method_ (fst env)) in
1255 let constructor, methods, smethods =
1256 aast_interface c constructor methods smethods in
1257 let class_tparam_names =
1258 List.map ~f:(fun tp -> tp.Aast.tp_name) c.Aast.c_tparams.Aast.c_tparam_list in
1259 let enum = Option.map c.Aast.c_enum (aast_enum_ env) in
1260 let file_attributes =
1261 aast_file_attributes c.Aast.c_mode c.Aast.c_file_attributes in
1262 let c_tparams =
1263 { N.c_tparam_list = tparam_l
1264 ; N.c_tparam_constraints = constraints
1265 } in
1266 check_tparams_constructor class_tparam_names constructor;
1267 check_name_collision methods;
1268 check_tparams_shadow class_tparam_names methods;
1269 check_name_collision smethods;
1270 check_tparams_shadow class_tparam_names smethods;
1271 { N.c_annotation = ();
1272 N.c_span = c.Aast.c_span;
1273 N.c_mode = c.Aast.c_mode;
1274 N.c_final = c.Aast.c_final;
1275 N.c_is_xhp = c.Aast.c_is_xhp;
1276 N.c_kind = c.Aast.c_kind;
1277 N.c_name = name;
1278 N.c_tparams = c_tparams;
1279 N.c_extends = parents;
1280 N.c_uses = uses;
1281 N.c_method_redeclarations = redeclarations;
1282 N.c_xhp_attr_uses = xhp_attr_uses;
1283 N.c_xhp_category = c.Aast.c_xhp_category;
1284 N.c_req_extends = req_extends;
1285 N.c_req_implements = req_implements;
1286 N.c_implements = implements;
1287 N.c_consts = consts;
1288 N.c_typeconsts = typeconsts;
1289 N.c_static_vars = sprops;
1290 N.c_vars = props;
1291 N.c_constructor = constructor;
1292 N.c_static_methods = smethods;
1293 N.c_methods = methods;
1294 N.c_user_attributes = attrs;
1295 N.c_file_attributes = file_attributes;
1296 N.c_namespace = c.Aast.c_namespace;
1297 N.c_enum = enum;
1298 N.c_doc_comment = c.Aast.c_doc_comment;
1299 (* Naming and typechecking shouldn't use these fields *)
1300 N.c_attributes = [];
1301 N.c_xhp_children = [];
1302 N.c_xhp_attrs = [];
1305 and aast_user_attributes env attrl =
1306 let seen = Caml.Hashtbl.create 0 in
1307 let validate_seen ua_name =
1308 let pos, name = ua_name in
1309 let existing_attr_pos =
1310 try Some (Caml.Hashtbl.find seen name)
1311 with Caml.Not_found -> None in
1312 match existing_attr_pos with
1313 | Some p -> Errors.duplicate_user_attribute ua_name p; false
1314 | None -> Caml.Hashtbl.add seen name pos; true in
1315 let on_attr acc { Aast.ua_name; ua_params } =
1316 let name = snd ua_name in
1317 let ua_name =
1318 if String.is_prefix name ~prefix:"__"
1319 then ua_name
1320 else Env.type_name env ua_name ~allow_typedef:false ~allow_generics:false in
1321 if not (validate_seen ua_name)
1322 then acc
1323 else
1324 let attr =
1325 { N.ua_name = ua_name
1326 ; N.ua_params = List.map ~f:(aast_expr env) ua_params
1327 } in
1328 attr :: acc in
1329 List.fold_left ~init:[] ~f:on_attr attrl
1331 and aast_file_attributes mode fal =
1332 List.map ~f:(aast_file_attribute mode) fal
1333 and aast_file_attribute mode fa =
1334 let env = Env.make_file_attributes_env mode fa.Aast.fa_namespace in
1335 let ua = aast_user_attributes env fa.Aast.fa_user_attributes in
1337 { fa_user_attributes = ua
1338 ; fa_namespace = fa.Aast.fa_namespace
1341 (* h cv is_required maybe_enum *)
1342 and aast_xhp_attribute_decl env (h, cv, is_required, maybe_enum) =
1343 let p, id = cv.Aast.cv_id in
1344 let default = cv.Aast.cv_expr in
1345 if is_required && Option.is_some default
1346 then Errors.xhp_required_with_default p id;
1347 let hint =
1348 match maybe_enum with
1349 | Some (pos, _optional, items) ->
1350 let is_int item =
1351 match item with
1352 | _, Aast.Int _ -> true
1353 | _ -> false in
1354 let contains_int = List.exists ~f:is_int items in
1355 let is_string item =
1356 match item with
1357 | _, Aast.String _
1358 | _, Aast.String2 _ -> true
1359 | _ -> false in
1360 let contains_str = List.exists ~f:is_string items in
1361 if contains_int && not contains_str
1362 then Some (pos, Aast.Happly ((pos, "int"), []))
1363 else if not contains_int && contains_str
1364 then Some (pos, Aast.Happly ((pos, "string"), []))
1365 else Some (pos, Aast.Happly ((pos, "mixed"), []))
1366 | _ -> h in
1367 let hint =
1368 match hint with
1369 | Some (p, Aast.Hoption _) ->
1370 if is_required
1371 then Errors.xhp_optional_required_attr p id;
1372 hint
1373 | Some (_, (Aast.Happly ((_, "mixed"), []))) -> hint
1374 | Some (p, h) ->
1375 let has_default =
1376 match default with
1377 | None
1378 | Some (_, Aast.Null) -> false
1379 | _ -> true in
1380 if is_required || has_default
1381 then hint
1382 else Some (p, Aast.Hoption (p, h))
1383 | None -> None in
1384 let hint = Option.map hint (aast_hint env) in
1385 let expr, is_xhp = aast_class_prop_expr_is_xhp env cv in
1386 { N.cv_final = cv.Aast.cv_final
1387 ; N.cv_is_xhp = is_xhp
1388 ; N.cv_visibility = cv.Aast.cv_visibility
1389 ; N.cv_type = hint
1390 ; N.cv_id = cv.Aast.cv_id
1391 ; N.cv_expr = expr
1392 ; N.cv_user_attributes = []
1395 and aast_enum_ env e =
1396 { N.e_base = aast_hint env e.Aast.e_base
1397 ; N.e_constraint = Option.map e.Aast.e_constraint (aast_hint env)
1400 and aast_type_paraml ?(forbid_this = false) env tparams =
1401 let _, ret = List.fold_left tparams ~init:(SMap.empty, [])
1402 ~f:(fun (seen, tparaml) tparam ->
1403 let (p, name) = tparam.Aast.tp_name in
1404 match SMap.get name seen with
1405 | None ->
1406 SMap.add name p seen, (aast_type_param ~forbid_this env tparam) :: tparaml
1407 | Some pos ->
1408 Errors.shadowed_type_param p pos name;
1409 seen, tparaml
1412 List.rev ret
1414 and aast_type_param ~forbid_this env t =
1415 let (genv, _) = env in
1416 if t.Aast.tp_reified && not (TypecheckerOptions.experimental_feature_enabled
1417 genv.tcopt
1418 TypecheckerOptions.experimental_reified_generics)
1419 then
1420 Errors.experimental_feature (fst t.Aast.tp_name) "reified generics";
1422 begin
1423 if (TypecheckerOptions.experimental_feature_enabled
1424 (fst env).tcopt
1425 TypecheckerOptions.experimental_type_param_shadowing)
1426 then
1427 (* Treat type params as inline class declarations that don't go into the naming heap *)
1428 let (pos, name) = NS.elaborate_id genv.namespace NS.ElaborateClass t.Aast.tp_name in
1429 match Naming_heap.TypeIdHeap.get name with
1430 | Some (def_pos, _) ->
1431 let def_pos, _ = GEnv.get_full_pos (def_pos, name) in
1432 Errors.error_name_already_bound name name pos def_pos
1433 | None ->
1434 match GEnv.type_canon_name name with
1435 | Some canonical ->
1436 let def_pos = Option.value ~default:Pos.none (GEnv.type_pos canonical) in
1437 Errors.error_name_already_bound name canonical pos def_pos
1438 | None -> ()
1439 end;
1442 N.tp_variance = t.Aast.tp_variance;
1443 tp_name = t.Aast.tp_name;
1444 tp_constraints = List.map t.Aast.tp_constraints (aast_constraint_ ~forbid_this env);
1445 tp_reified = t.Aast.tp_reified;
1446 tp_user_attributes = aast_user_attributes env t.Aast.tp_user_attributes;
1449 and aast_type_where_constraints env locl_cstrl =
1450 List.map
1451 ~f:(fun (h1, ck, h2) ->
1452 let ty1 = aast_hint ~in_where_clause:true env h1 in
1453 let ty2 = aast_hint ~in_where_clause:true env h2 in
1454 (ty1, ck, ty2))
1455 locl_cstrl
1457 and aast_class_prop_expr_is_xhp env cv =
1458 let expr = Option.map cv.Aast.cv_expr (aast_expr env) in
1459 let expr =
1460 if (fst env).in_mode = FileInfo.Mdecl && expr = None
1461 then Some (fst cv.Aast.cv_id, N.Any)
1462 else expr in
1463 let is_xhp =
1464 try ((String.sub (snd cv.Aast.cv_id) 0 1) = ":")
1465 with Invalid_argument _ -> false in
1466 expr, is_xhp
1468 and aast_class_prop_static env cv =
1469 let attrs = aast_user_attributes env cv.Aast.cv_user_attributes in
1470 let lsb = Attributes.mem SN.UserAttributes.uaLSB attrs in
1471 let forbid_this = not lsb in
1472 let h = Option.map cv.Aast.cv_type (aast_hint ~forbid_this env) in
1473 let expr, is_xhp = aast_class_prop_expr_is_xhp env cv in
1474 { N.cv_final = cv.Aast.cv_final
1475 ; N.cv_is_xhp = is_xhp
1476 ; N.cv_visibility = cv.Aast.cv_visibility
1477 ; N.cv_type = h
1478 ; N.cv_id = cv.Aast.cv_id
1479 ; N.cv_expr = expr
1480 ; N.cv_user_attributes = attrs
1483 and aast_class_prop_non_static env ?(const = None) cv =
1484 let h = Option.map cv.Aast.cv_type (aast_hint env) in
1485 let attrs = aast_user_attributes env cv.Aast.cv_user_attributes in
1486 let lsb_pos = Attributes.mem_pos SN.UserAttributes.uaLSB attrs in
1487 (* Non-static properties cannot have attribute __LSB *)
1488 let _ =
1489 match lsb_pos with
1490 | Some p -> Errors.nonstatic_property_with_lsb p
1491 | None -> () in
1492 (* if class is __Const, make all member fields __Const *)
1493 let attrs =
1494 match const with
1495 | Some c ->
1496 if not (Attributes.mem SN.UserAttributes.uaConst attrs)
1497 then c :: attrs
1498 else attrs
1499 | None -> attrs in
1500 let expr, is_xhp = aast_class_prop_expr_is_xhp env cv in
1501 { N.cv_final = cv.Aast.cv_final
1502 ; N.cv_is_xhp = is_xhp
1503 ; N.cv_visibility = cv.Aast.cv_visibility
1504 ; N.cv_type = h
1505 ; N.cv_id = cv.Aast.cv_id
1506 ; N.cv_expr = expr
1507 ; N.cv_user_attributes = attrs
1510 and aast_class_method env c_meth =
1511 match c_meth.Aast.m_name, c_meth.Aast.m_params with
1512 | ((m_pos, m_name), _ :: _) when m_name = SN.Members.__clone ->
1513 Errors.clone_too_many_arguments m_pos; c_meth
1514 | _ -> aast_method_ (fst env) c_meth
1516 and aast_check_constant_expr env (pos, e) =
1517 match e with
1518 | Aast.Unsafe_expr _
1519 | Aast.Id _
1520 | Aast.Null
1521 | Aast.True
1522 | Aast.False
1523 | Aast.Int _
1524 | Aast.Float _
1525 | Aast.String _ -> ()
1526 | Aast.Class_const ((_, Aast.CIexpr (_, cls)), _)
1527 when (match cls with Aast.Id (_, "static") -> false | _ -> true) -> ()
1528 | Aast.Unop ((Ast.Uplus| Ast.Uminus | Ast.Utild | Ast.Unot), e) ->
1529 aast_check_constant_expr env e
1530 | Aast.Binop (op, e1, e2) ->
1531 (* Only assignment is invalid *)
1532 begin
1533 match op with
1534 | Ast.Eq _ -> Errors.illegal_constant pos
1535 | _ ->
1536 aast_check_constant_expr env e1;
1537 aast_check_constant_expr env e2
1539 | Aast.Eif (e1, e2, e3) ->
1540 aast_check_constant_expr env e1;
1541 Option.iter e2 (aast_check_constant_expr env);
1542 aast_check_constant_expr env e3
1543 | Aast.Array l -> List.iter l ~f:(aast_check_afield_constant_expr env)
1544 | Aast.Darray (_, l) ->
1545 List.iter l ~f:(fun (e1, e2) ->
1546 aast_check_constant_expr env e1;
1547 aast_check_constant_expr env e2)
1548 | Aast.Varray (_, l) -> List.iter l ~f:(aast_check_constant_expr env)
1549 | Aast.Shape fdl ->
1550 (* Only check the values because shape field names are always legal *)
1551 List.iter fdl ~f:(fun (_, e) -> aast_check_constant_expr env e)
1552 | Aast.Call (_, (_, Aast.Id (_, cn)), _, el, uel)
1553 when cn = SN.SpecialFunctions.tuple ->
1554 (* Tuples are not really function calls, they are just parsed that way*)
1555 arg_unpack_unexpected uel;
1556 List.iter el ~f:(aast_check_constant_expr env)
1557 | Aast.Collection (id, _, l) ->
1558 let p, cn = NS.elaborate_id ((fst env).namespace) NS.ElaborateClass id in
1559 (* Only vec/keyset/dict are allowed because they are value types *)
1560 if cn = SN.Collections.cVec
1561 || cn = SN.Collections.cKeyset
1562 || cn = SN.Collections.cDict
1563 then List.iter l ~f:(aast_check_afield_constant_expr env)
1564 else Errors.illegal_constant p
1565 | _ -> Errors.illegal_constant pos
1567 and aast_check_afield_constant_expr env afield =
1568 match afield with
1569 | Aast.AFvalue e -> aast_check_constant_expr env e
1570 | Aast.AFkvalue (e1, e2) ->
1571 aast_check_constant_expr env e1;
1572 aast_check_constant_expr env e2
1574 and aast_constant_expr env e =
1575 let valid_constant_expression =
1576 Errors.try_with_error
1577 (fun () -> aast_check_constant_expr env e; true)
1578 (fun () -> false) in
1579 if valid_constant_expression
1580 then aast_expr env e
1581 else fst e, N.Any
1583 and aast_class_const env (h, x, eo) =
1584 Env.bind_class_const env x;
1585 let h = Option.map h (aast_hint env) in
1586 let eo = Option.map eo (aast_constant_expr env) in
1587 h, x, eo
1589 and aast_typeconst env t =
1590 (* We use the same namespace as constants within the class so we cannot have
1591 * a const and type const with the same name
1593 Env.bind_class_const env t.Aast.c_tconst_name;
1594 let constr = Option.map t.Aast.c_tconst_constraint (aast_hint env) in
1595 let type_ = Option.map t.Aast.c_tconst_type (aast_hint env) in
1596 let attrs = aast_user_attributes env t.Aast.c_tconst_user_attributes in
1597 if not (TypecheckerOptions.experimental_feature_enabled (fst env).tcopt
1598 TypecheckerOptions.experimental_type_const_attributes || List.is_empty attrs)
1599 then Errors.experimental_feature (fst t.Aast.c_tconst_name) "type constant attributes";
1601 { c_tconst_name = t.Aast.c_tconst_name
1602 ; c_tconst_constraint = constr
1603 ; c_tconst_type = type_
1604 ; c_tconst_user_attributes = attrs
1607 and func_body_had_unsafe env = Env.has_unsafe env
1609 and aast_method_ genv m =
1610 let genv = aast_extend_params genv m.Aast.m_tparams in
1611 let env = genv, Env.empty_local None in
1612 (* Cannot use 'this' if it is a public instance method *)
1613 let variadicity, paraml = aast_fun_paraml env m.Aast.m_params in
1614 let tparam_l = aast_type_paraml env m.Aast.m_tparams in
1615 List.iter tparam_l aast_check_constraint;
1616 let where_constraints = aast_type_where_constraints env m.Aast.m_where_constraints in
1617 let ret = Option.map m.Aast.m_ret (aast_hint ~allow_retonly:true env) in
1618 let body =
1619 match genv.in_mode with
1620 | FileInfo.Mdecl | FileInfo.Mphp ->
1621 { N.fb_ast = [];
1622 fb_annotation = N.BodyNamingAnnotation.NamedWithUnsafeBlocks;
1624 | FileInfo.Mstrict | FileInfo.Mpartial | FileInfo.Mexperimental ->
1625 if Aast.is_body_named m.Aast.m_body
1626 then
1627 { N.fb_ast = m.Aast.m_body.Aast.fb_ast;
1628 fb_annotation = N.BodyNamingAnnotation.Unnamed genv.namespace;
1630 else failwith "ast_to_nast error unnamedbody in method_"
1632 let attrs = aast_user_attributes env m.Aast.m_user_attributes in
1633 { N.m_annotation = ();
1634 N.m_span = m.Aast.m_span;
1635 N.m_final = m.Aast.m_final;
1636 N.m_visibility = m.Aast.m_visibility;
1637 N.m_abstract = m.Aast.m_abstract;
1638 N.m_static = m.Aast.m_static;
1639 N.m_name = m.Aast.m_name;
1640 N.m_tparams = tparam_l;
1641 N.m_where_constraints = where_constraints;
1642 N.m_params = paraml;
1643 N.m_body = body;
1644 N.m_fun_kind = m.Aast.m_fun_kind;
1645 N.m_ret = ret;
1646 N.m_variadic = variadicity;
1647 N.m_user_attributes = attrs;
1648 N.m_external = m.Aast.m_external;
1649 N.m_doc_comment = m.Aast.m_doc_comment;
1652 and aast_method_redeclaration env mt =
1653 if not
1654 (TypecheckerOptions.experimental_feature_enabled
1655 (fst env).tcopt
1656 TypecheckerOptions.experimental_trait_method_redeclarations)
1657 then Errors.experimental_feature (fst mt.Aast.mt_name) "trait method redeclarations";
1658 let genv = aast_extend_params (fst env) mt.Aast.mt_tparams in
1659 let env = genv, Env.empty_local None in
1660 let variadicity, paraml = aast_fun_paraml env mt.Aast.mt_params in
1661 let tparam_l = aast_type_paraml env mt.Aast.mt_tparams in
1662 let where_constraints = aast_type_where_constraints env mt.Aast.mt_where_constraints in
1663 let ret = Option.map mt.Aast.mt_ret (aast_hint ~allow_retonly:true env) in
1664 { N.mt_final = mt.Aast.mt_final
1665 ; N.mt_visibility = mt.Aast.mt_visibility
1666 ; N.mt_abstract = mt.Aast.mt_abstract
1667 ; N.mt_static = mt.Aast.mt_static
1668 ; N.mt_name = mt.Aast.mt_name
1669 ; N.mt_tparams = tparam_l
1670 ; N.mt_where_constraints = where_constraints
1671 ; N.mt_params = paraml
1672 ; N.mt_fun_kind = mt.Aast.mt_fun_kind
1673 ; N.mt_ret = ret
1674 ; N.mt_variadic = variadicity
1675 ; N.mt_trait = aast_hint ~allow_typedef:false env mt.Aast.mt_trait
1676 ; N.mt_method = mt.Aast.mt_method
1677 ; N.mt_user_attributes = []
1680 and aast_fun_paraml env paraml =
1681 let _ = List.fold_left ~f:aast_check_repetition ~init:SSet.empty paraml in
1682 let variadicity, paraml = aast_determine_variadicity env paraml in
1683 variadicity, List.map ~f:(aast_fun_param env) paraml
1685 (* Variadic params are removed from the list *)
1686 and aast_determine_variadicity env paraml =
1687 match paraml with
1688 | [] -> N.FVnonVariadic, []
1689 | [x] ->
1690 begin
1691 match x.Aast.param_is_variadic, x.Aast.param_name with
1692 | false, _ -> N.FVnonVariadic, paraml
1693 | true, "..." -> N.FVellipsis x.Aast.param_pos, []
1694 | true, _ -> N.FVvariadicArg (aast_fun_param env x), []
1696 | x :: rl ->
1697 let variadicity, rl = aast_determine_variadicity env rl in
1698 variadicity, x :: rl
1700 and aast_fun_param env (param : Aast.fun_param) =
1701 let p = param.Aast.param_pos in
1702 let name = param.Aast.param_name in
1703 let ident = Local_id.get name in
1704 Env.add_lvar env (p, name) (p, ident);
1705 let ty = Option.map param.Aast.param_hint (aast_hint env) in
1706 let eopt = Option.map param.Aast.param_expr (aast_expr env) in
1707 if param.Aast.param_is_reference && (fst env).in_mode = FileInfo.Mstrict
1708 then Errors.reference_in_strict_mode p;
1709 { N.param_annotation = p;
1710 param_hint = ty;
1711 param_is_reference = param.Aast.param_is_reference;
1712 param_is_variadic = param.Aast.param_is_variadic;
1713 param_pos = p;
1714 param_name = name;
1715 param_expr = eopt;
1716 param_callconv = param.Aast.param_callconv;
1717 param_user_attributes = aast_user_attributes env param.Aast.param_user_attributes;
1720 and aast_make_constraints paraml =
1721 List.fold_right
1722 ~init:SMap.empty
1723 ~f:(fun { Aast.tp_name = (_, x); tp_constraints; tp_reified; _ } acc ->
1724 SMap.add x (tp_reified, tp_constraints) acc)
1725 paraml
1727 and aast_extend_params genv paraml =
1728 let params = List.fold_right paraml ~init:genv.type_params
1729 ~f:begin fun { Aast.tp_name = (_, x); tp_constraints = cstr_list; tp_reified = r; _ } acc ->
1730 SMap.add x (r, cstr_list) acc
1731 end in
1732 { genv with type_params = params }
1734 and fun_ f =
1735 let f = Ast_to_nast.on_fun f in
1736 aast_fun_ f
1738 and aast_fun_ f =
1739 let tparams = aast_make_constraints f.Aast.f_tparams in
1740 let genv = Env.aast_make_fun_decl_genv tparams f in
1741 let lenv = Env.empty_local None in
1742 let env = genv, lenv in
1743 let where_constraints = aast_type_where_constraints env f.Aast.f_where_constraints in
1744 let h = Option.map f.Aast.f_ret (aast_hint ~allow_retonly:true env) in
1745 let variadicity, paraml = aast_fun_paraml env f.Aast.f_params in
1746 let x = Env.fun_id env f.Aast.f_name in
1747 List.iter f.Aast.f_tparams aast_check_constraint;
1748 let f_tparams = aast_type_paraml env f.Aast.f_tparams in
1749 let f_kind = f.Aast.f_fun_kind in
1750 let body =
1751 match genv.in_mode with
1752 | FileInfo.Mdecl | FileInfo.Mphp ->
1753 { N.fb_ast = [];
1754 fb_annotation = N.BodyNamingAnnotation.NamedWithUnsafeBlocks;
1756 | FileInfo.Mstrict | FileInfo.Mpartial | FileInfo.Mexperimental ->
1757 if Aast.is_body_named f.Aast.f_body
1758 then
1759 { N.fb_ast = f.Aast.f_body.Aast.fb_ast;
1760 fb_annotation = N.BodyNamingAnnotation.Unnamed genv.namespace;
1762 else failwith "ast_to_nast error unnamedbody in fun_"
1764 let named_fun = {
1765 N.f_annotation = ();
1766 f_span = f.Aast.f_span;
1767 f_mode = f.Aast.f_mode;
1768 f_ret = h;
1769 f_name = x;
1770 f_tparams = f_tparams;
1771 f_where_constraints = where_constraints;
1772 f_params = paraml;
1773 f_body = body;
1774 f_fun_kind = f_kind;
1775 f_variadic = variadicity;
1776 f_user_attributes = aast_user_attributes env f.Aast.f_user_attributes;
1777 (* Fix file attributes if they are important *)
1778 f_file_attributes = [];
1779 f_external = f.Aast.f_external;
1780 f_namespace = f.Aast.f_namespace;
1781 f_doc_comment = f.Aast.f_doc_comment;
1782 f_static = f.Aast.f_static;
1783 } in
1784 named_fun
1786 and aast_get_using_vars (_, e) =
1787 match e with
1788 | Aast.Expr_list using_clauses ->
1789 List.concat_map using_clauses aast_get_using_vars
1790 (* Simple assignment to local of form `$lvar = e` *)
1791 | Aast.Binop (Ast.Eq None, (_, Aast.Lvar (p, lid)), _) ->
1792 [ (p, Local_id.get_name lid) ]
1793 (* Arbitrary expression. This will be assigned to a temporary *)
1794 | _ -> []
1796 and aast_stmt env st =
1797 match st with
1798 | Aast.Let (x, h, e) -> aast_let_stmt env x h e
1799 | Aast.Block _ -> failwith "aast_stmt block error"
1800 | Aast.Unsafe_block _ -> failwith "aast_stmt unsafe_block error"
1801 | Aast.Fallthrough -> N.Fallthrough
1802 | Aast.Noop -> N.Noop
1803 | Aast.Markup (_, None) -> N.Noop
1804 | Aast.Markup (_m, Some e) -> N.Expr (aast_expr env e)
1805 | Aast.Break p -> Aast.Break p
1806 | Aast.Continue p -> Aast.Continue p
1807 | Aast.Throw (_, e) ->
1808 let terminal = not (fst env).in_try in
1809 N.Throw (terminal, aast_expr env e)
1810 | Aast.Return (p, e) -> N.Return (p, Option.map e (aast_expr env))
1811 | Aast.GotoLabel label -> name_goto_label env label
1812 | Aast.Goto label -> name_goto env label
1813 | Aast.Static_var el -> N.Static_var (aast_static_varl env el)
1814 | Aast.Global_var el -> N.Global_var (aast_global_varl env el)
1815 | Aast.Awaitall (p, el) -> aast_awaitall_stmt env p el
1816 | Aast.If (e, b1, b2) -> aast_if_stmt env st e b1 b2
1817 | Aast.Do (b, e) -> aast_do_stmt env b e
1818 | Aast.While (e, b) ->
1819 N.While (aast_expr env e, aast_block env b)
1820 | Aast.Declare (_, (p, _), _) ->
1821 Errors.declare_statement_in_hack p;
1822 N.Expr (p, N.Any)
1823 | Aast.Using s -> aast_using_stmt env s.Aast.us_has_await s.Aast.us_expr s.Aast.us_block
1824 | Aast.For (st1, e, st2, b) -> aast_for_stmt env st1 e st2 b
1825 | Aast.Switch (e, cl) -> aast_switch_stmt env st e cl
1826 | Aast.Foreach (e, ae, b) -> aast_foreach_stmt env e ae b
1827 | Aast.Try (b, cl, fb) -> aast_try_stmt env st b cl fb
1828 | Aast.Def_inline _ -> (* No convenient pos information on Aast *)
1829 Errors.experimental_feature Pos.none "inlined definitions"; N.Expr (Pos.none, N.Any)
1830 | Aast.Expr (cp, Aast.Call (_, (p, Aast.Id (fp, fn)), hl, el, uel))
1831 when fn = SN.SpecialFunctions.invariant ->
1832 (* invariant is subject to a source-code transform in the HHVM
1833 * runtime: the arguments to invariant are lazily evaluated only in
1834 * the case in which the invariant condition does not hold. So:
1836 * invariant_violation(<condition>, <format>, <format_args...>)
1838 * ... is rewritten as:
1840 * if (!<condition>) {
1841 * invariant_violation(<format>, <format_args...>);
1844 begin
1845 match el with
1846 | [] | [_] ->
1847 Errors.naming_too_few_arguments p;
1848 N.Expr (cp, N.Any)
1849 | (cond_p, cond) :: el ->
1850 let violation = (cp, Aast.Call (Aast.Cnormal, (p, Aast.Id
1851 (fp, "\\"^SN.SpecialFunctions.invariant_violation)), hl, el, uel)) in
1852 if cond <> Aast.False
1853 then
1854 let b1, b2 = [Aast.Expr violation], [Aast.Noop] in
1855 let cond = (cond_p, Aast.Unop (Ast.Unot, (cond_p, cond))) in
1856 aast_if_stmt env st cond b1 b2
1857 else (* a false <condition> means unconditional invariant_violation *)
1858 N.Expr (aast_expr env violation)
1860 | Aast.Expr e -> N.Expr (aast_expr env e)
1862 and aast_let_stmt env (p, lid) h e =
1863 let name = Local_id.get_name lid in
1864 let e = aast_expr env e in
1865 let h = Option.map h (aast_hint env) in
1866 let x = Env.new_let_local env (p, name) in
1867 N.Let (x, h, e)
1869 and aast_if_stmt env st e b1 b2 =
1870 let e = aast_expr env e in
1871 let nsenv = (fst env).namespace in
1872 let _, vars = GetLocals.aast_stmt (nsenv, SMap.empty) st in
1873 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1874 let result = Env.scope env
1875 (fun env ->
1876 let all1, b1 = aast_branch env b1 in
1877 let all2, b2 = aast_branch env b2 in
1878 Env.extend_all_locals env all2;
1879 Env.extend_all_locals env all1;
1880 N.If (e, b1, b2)
1881 ) in
1882 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) vars;
1883 result
1885 and aast_do_stmt env b e =
1886 (* lexical block of `do` is extended to the expr of loop termination *)
1887 Env.scope_lexical
1889 (fun env ->
1890 let b = aast_block ~new_scope:false env b in
1891 let e = aast_expr env e in
1892 N.Do (b, e))
1894 (* Scoping is essentially that of do: block is always executed *)
1895 and aast_using_stmt env has_await e b =
1896 let vars = aast_get_using_vars e in
1897 let e = aast_expr env e in
1898 let b = aast_block ~new_scope:false env b in
1899 Env.remove_locals env vars;
1900 N.Using N.{
1901 us_is_block_scoped = false; (* This isn't used for naming so provide a default *)
1902 us_has_await = has_await;
1903 us_expr = e;
1904 us_block = b;
1907 and aast_for_stmt env e1 e2 e3 b =
1908 (* The initialization and condition expression should be in the outer scope,
1909 * as they are always executed. *)
1910 let e1 = aast_expr env e1 in
1911 let e2 = aast_expr env e2 in
1912 Env.scope
1914 (fun env ->
1915 (* The third expression (iteration step) should have the same scope as the
1916 * block, as it is not always executed. *)
1917 let b = aast_block ~new_scope:false env b in
1918 let e3 = aast_expr env e3 in
1919 N.For (e1, e2, e3, b))
1921 and aast_switch_stmt env st e cl =
1922 let e = aast_expr env e in
1923 let nsenv = (fst env).namespace in
1924 let _, vars = GetLocals.aast_stmt (nsenv, SMap.empty) st in
1925 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1926 let result = Env.scope env begin fun env ->
1927 let all_locals_l, cl = aast_casel env cl in
1928 List.iter all_locals_l (Env.extend_all_locals env);
1929 N.Switch (e, cl)
1930 end in
1931 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) vars;
1932 result
1934 and aast_foreach_stmt env e ae b =
1935 let e = aast_expr env e in
1936 Env.scope
1938 (fun env ->
1939 let ae = aast_as_expr env ae in
1940 let b = aast_block env b in
1941 N.Foreach (e, ae, b))
1943 and aast_as_expr env ae =
1944 let handle_v ev =
1945 match ev with
1946 | p, Aast.Id x when (fst env).in_mode = FileInfo.Mexperimental ->
1947 let x = Env.new_let_local env x in
1948 let ev = (p, N.ImmutableVar x) in
1950 | p, Aast.Id _ ->
1951 Errors.expected_variable p;
1952 p, N.Lvar (Env.new_lvar env (p, "__internal_placeholder"))
1953 | ev ->
1954 let nsenv = (fst env).namespace in
1955 let _, vars =
1956 GetLocals.aast_lvalue (nsenv, SMap.empty) ev in
1957 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
1958 aast_expr env ev in
1959 let handle_k ek =
1960 match ek with
1961 | _, Aast.Lvar (p, lid) ->
1962 let x = (p, Local_id.get_name lid) in
1963 p, N.Lvar (Env.new_lvar env x)
1964 | p, Aast.Id x when (fst env).in_mode = FileInfo.Mexperimental ->
1965 p, N.ImmutableVar (Env.new_let_local env x)
1966 | p, _ ->
1967 Errors.expected_variable p;
1968 p, N.Lvar (Env.new_lvar env (p, "__internal_placeholder")) in
1969 match ae with
1970 | Aast.As_v ev ->
1971 let ev = handle_v ev in
1972 N.As_v ev
1973 | Aast.As_kv (k, ev) ->
1974 let k = handle_k k in
1975 let ev = handle_v ev in
1976 N.As_kv (k, ev)
1977 | N.Await_as_v (p, ev) ->
1978 let ev = handle_v ev in
1979 N.Await_as_v (p, ev)
1980 | N.Await_as_kv (p, k, ev) ->
1981 let k = handle_k k in
1982 let ev = handle_v ev in
1983 N.Await_as_kv (p, k, ev)
1985 and aast_try_stmt env st b cl fb =
1986 let nsenv = (fst env).namespace in
1987 let _, vars =
1988 GetLocals.aast_stmt (nsenv, SMap.empty) st in
1989 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) vars;
1990 let result = Env.scope
1992 (fun env ->
1993 let genv, lenv = env in
1994 (* isolate finally from the rest of the try-catch: if the first
1995 * statement of the try is an uncaught exception, finally will
1996 * still be executed *)
1997 let all_finally, fb = aast_branch ({genv with in_finally = true}, lenv) fb in
1998 Env.extend_all_locals env all_finally;
1999 let all_locals_b, b = aast_branch ({genv with in_try = true}, lenv) b in
2000 let all_locals_cl, cl = aast_catchl env cl in
2001 List.iter all_locals_cl (Env.extend_all_locals env);
2002 Env.extend_all_locals env all_locals_b;
2003 N.Try (b, cl, fb)) in
2004 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) vars;
2005 result
2007 and aast_stmt_list ?after_unsafe stl env =
2008 let aast_stmt_list = aast_stmt_list ?after_unsafe in
2009 match stl with
2010 | [] -> []
2011 | Aast.Unsafe_block b :: _ ->
2012 Env.set_unsafe env true;
2013 let st = Errors.ignore_ (fun () -> N.Unsafe_block (aast_stmt_list b env)) in
2014 st :: Option.to_list after_unsafe
2015 | Aast.Block b :: rest ->
2016 (* Add lexical scope for block scoped let variables *)
2017 let b = Env.scope_lexical env (aast_stmt_list b) in
2018 let rest = aast_stmt_list rest env in
2019 b @ rest
2020 | x :: rest ->
2021 let x = aast_stmt env x in
2022 let rest = aast_stmt_list rest env in
2023 x :: rest
2025 and aast_block ?(new_scope=true) env stl =
2026 if new_scope
2027 then Env.scope env (aast_stmt_list stl)
2028 else aast_stmt_list stl env
2030 and aast_branch ?after_unsafe env stmt_l =
2031 Env.scope_all env (aast_stmt_list ?after_unsafe stmt_l)
2034 * Names a goto label.
2036 * The goto label is added to the local labels if it is not already there.
2037 * Otherwise, an error is produced.
2039 * An error is produced if this is called within a finally block.
2041 and name_goto_label
2042 ({ in_finally; _ }, _ as env) (label_pos, label_name as label) =
2043 (match Env.goto_label env label_name with
2044 | Some original_declaration_pos ->
2045 Errors.goto_label_already_defined
2046 label_name
2047 label_pos
2048 original_declaration_pos
2049 | None -> Env.new_goto_label env label);
2050 if in_finally then
2051 Errors.goto_label_defined_in_finally label_pos;
2052 N.GotoLabel label
2055 * Names a goto target.
2057 * The goto statement's target label is added to the local goto targets.
2059 * An error is produced if this is called within a finally block.
2061 and name_goto
2062 ({ in_finally; _ }, _ as env) (label_pos, _ as label) =
2063 Env.new_goto_target env label;
2064 if in_finally then Errors.goto_invoked_in_finally label_pos;
2065 N.Goto label
2067 and aast_static_varl env l = List.map l (aast_static_var env)
2068 and aast_static_var env = function
2069 | p, Aast.Lvar _ as lv ->
2070 aast_expr env (p, Aast.Binop (Ast.Eq None, lv, (p, Aast.Null)))
2071 | e -> aast_expr env e
2073 and aast_global_varl env l = aast_static_varl env l
2075 and aast_awaitall_stmt env pos el =
2076 let el =
2077 List.map
2078 ~f:(fun (e1, e2) ->
2079 let e2 = aast_expr env e2 in
2080 let e1 =
2081 match e1 with
2082 | Some (_, Aast.Lvar e) ->
2083 let e = Pos.none, Aast.Lvar e in
2084 let nsenv = (fst env).namespace in
2085 let _, vars =
2086 GetLocals.aast_lvalue (nsenv, SMap.empty) e in
2087 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
2088 Some (aast_expr env e)
2089 | Some _ -> failwith "ast_to_nast error in awaitall lvar"
2090 | None -> None in
2091 (e1, e2))
2092 el in
2093 N.Awaitall (pos, el)
2095 and aast_expr_obj_get_name env expr =
2096 match expr with
2097 | p, Aast.Id x -> p, N.Id x
2098 | p, e -> aast_expr env (p, e)
2100 and aast_exprl env l = List.map ~f:(aast_expr env) l
2102 and aast_oexpr env e = Option.map e (aast_expr env)
2104 and aast_expr env (p, e) = p, aast_expr_ env p e
2106 and aast_expr_ env p (e : Aast.expr_) =
2107 match e with
2108 | Aast.ParenthesizedExpr (p, e) -> aast_expr_ env p e
2109 | Aast.Array l ->
2110 let tcopt = (fst env).tcopt in
2111 if TypecheckerOptions.disallow_array_literal tcopt
2112 then Errors.array_literals_disallowed p;
2113 N.Array (List.map l (aast_afield env))
2114 | Aast.Varray (ta, l) ->
2115 N.Varray (Option.map ~f:(aast_targ env) ta, List.map l (aast_expr env))
2116 | Aast.Darray (tap, l) ->
2117 let nargs = Option.map ~f:(fun (t1, t2) -> aast_targ env t1, aast_targ env t2) tap in
2118 N.Darray (
2119 nargs,
2120 List.map l (fun (e1, e2) -> aast_expr env e1, aast_expr env e2))
2121 | Aast.Collection (id, tal, l) ->
2122 let p, cn = NS.elaborate_id ((fst env).namespace) NS.ElaborateClass id in
2123 begin
2124 match cn with
2125 | x when N.is_vc_kind x ->
2126 let ta = begin match tal with
2127 | Some Aast.CollectionTV tv -> Some (aast_targ env tv)
2128 | Some Aast.CollectionTKV _ -> Errors.naming_too_many_arguments p; None
2129 | None -> None
2130 end in
2131 N.ValCollection ((N.get_vc_kind cn), ta, (List.map l (aast_afield_value env cn)))
2132 | x when N.is_kvc_kind x ->
2133 let ta = begin match tal with
2134 | Some Aast.CollectionTV _ -> Errors.naming_too_few_arguments p; None
2135 | Some Aast.CollectionTKV (tk, tv) -> Some (aast_targ env tk, aast_targ env tv)
2136 | None -> None
2137 end in
2138 N.KeyValCollection ((N.get_kvc_kind cn), ta,
2139 (List.map l (aast_afield_kvalue env cn)))
2140 | x when x = SN.Collections.cPair ->
2141 begin
2142 match l with
2143 | [] ->
2144 Errors.naming_too_few_arguments p;
2145 N.Any
2146 | e1::e2::[] ->
2147 let pn = SN.Collections.cPair in
2148 N.Pair (aast_afield_value env pn e1, aast_afield_value env pn e2)
2149 | _ ->
2150 Errors.naming_too_many_arguments p;
2151 N.Any
2153 | _ ->
2154 Errors.expected_collection p cn;
2155 N.Any
2157 | Aast.Clone e -> N.Clone (aast_expr env e)
2158 | Aast.Null -> N.Null
2159 | Aast.True -> N.True
2160 | Aast.False -> N.False
2161 | Aast.Int s -> N.Int s
2162 | Aast.Float s -> N.Float s
2163 | Aast.String s -> N.String s
2164 | Aast.String2 idl
2165 (* treat execution operator similar to interpolated strings *)
2166 | Aast.Execution_operator idl -> N.String2 (aast_string2 env idl)
2167 | Aast.PrefixedString (n, e) -> N.PrefixedString (n, (aast_expr env e))
2168 | Aast.Id x ->
2169 (** TODO: Emit proper error messages T28473207. Currently the error message
2170 * emitted has reason Naming[2049] unbound name for global constant *)
2171 begin
2172 match Env.let_local env x with
2173 | Some x -> N.ImmutableVar x
2174 | None -> N.Id (Env.global_const env x)
2175 end (* match *)
2176 | Aast.Lvar (_, x) when Local_id.to_string x = SN.SpecialIdents.this -> N.This
2177 | Aast.Lvar (p, x) when Local_id.to_string x = SN.SpecialIdents.dollardollar ->
2178 N.Dollardollar (p, Env.found_dollardollar env p)
2179 | Aast.Lvar (p, x) when Local_id.to_string x = SN.SpecialIdents.placeholder ->
2180 N.Lplaceholder p
2181 | Aast.Lvar x ->
2182 N.Lvar (Env.aast_lvar env x)
2183 | Aast.Obj_get (e1, e2, nullsafe) ->
2184 (* If we encounter Obj_get(_,_,true) by itself, then it means "?->"
2185 is being used for instance property access; see the case below for
2186 handling nullsafe instance method calls to see how this works *)
2187 N.Obj_get (aast_expr env e1, aast_expr_obj_get_name env e2, nullsafe)
2188 | Aast.Array_get ((p, Aast.Lvar x), None) ->
2189 let id = p, N.Lvar (Env.aast_lvar env x) in
2190 N.Array_get (id, None)
2191 | Aast.Array_get (e1, e2) -> N.Array_get (aast_expr env e1, aast_oexpr env e2)
2192 | Aast.Class_get ((_, Aast.CIexpr (_, Aast.Id x1)), Aast.CGstring x2) ->
2193 N.Class_get (make_class_id env x1, N.CGstring x2)
2194 | Aast.Class_get ((_, Aast.CIexpr (_, Aast.Lvar (p, lid))), Aast.CGstring x2) ->
2195 let x1 = (p, Local_id.to_string lid) in
2196 N.Class_get (make_class_id env x1, N.CGstring x2)
2197 | Aast.Class_get ((_, Aast.CIexpr x1), Aast.CGexpr x2) ->
2198 aast_ensure_name_not_dynamic env x1
2199 Errors.dynamic_class_name_in_strict_mode;
2200 aast_ensure_name_not_dynamic env x2
2201 Errors.dynamic_class_name_in_strict_mode;
2202 N.Any
2203 | Aast.Class_get _ -> failwith "Error in Ast_to_nast module for Class_get"
2204 | Aast.Class_const ((_, Aast.CIexpr (_, Aast.Id x1)), x2) ->
2205 let (genv, _) = env in
2206 let (_, name) = NS.elaborate_id genv.namespace NS.ElaborateClass x1 in
2207 begin
2208 match Naming_heap.TypeIdHeap.get name with
2209 | Some (_, `Typedef) when (snd x2) = "class" ->
2210 N.Typename (Env.type_name env x1 ~allow_typedef:true ~allow_generics:false)
2211 | _ ->
2212 N.Class_const (make_class_id env x1, x2)
2214 | Aast.Class_const ((_, Aast.CIexpr (_, Aast.Lvar (p, lid))), x2) ->
2215 let x1 = (p, Local_id.to_string lid) in
2216 let (genv, _) = env in
2217 let (_, name) = NS.elaborate_id genv.namespace NS.ElaborateClass x1 in
2218 begin
2219 match Naming_heap.TypeIdHeap.get name with
2220 | Some (_, `Typedef) when (snd x2) = "class" ->
2221 N.Typename (Env.type_name env x1 ~allow_typedef:true ~allow_generics:false)
2222 | _ ->
2223 N.Class_const (make_class_id env x1, x2)
2225 | Aast.Class_const _ -> (* TODO: report error in strict mode *) N.Any
2226 | Aast.Call (_, (_, Aast.Id (p, pseudo_func)), tal, el, uel)
2227 when pseudo_func = SN.SpecialFunctions.echo ->
2228 arg_unpack_unexpected uel;
2229 N.Call (N.Cnormal, (p, N.Id (p, pseudo_func)), aast_targl env p tal, aast_exprl env el, [])
2230 | Aast.Call (_, (p, (Aast.Id (_, cn))), tal, el, uel)
2231 when cn = SN.SpecialFunctions.call_user_func ->
2232 arg_unpack_unexpected uel;
2233 begin
2234 match el with
2235 | [] -> Errors.naming_too_few_arguments p; N.Any
2236 | f :: el ->
2237 N.Call
2238 (N.Cuser_func, aast_expr env f, aast_targl env p tal, aast_exprl env el, [])
2240 | Aast.Call (_, (p, Aast.Id (_, cn)), _, el, uel)
2241 when cn = SN.SpecialFunctions.fun_ ->
2242 arg_unpack_unexpected uel;
2243 let (genv, _) = env in
2244 begin
2245 match el with
2246 | [] -> Errors.naming_too_few_arguments p; N.Any
2247 | [_, Aast.String s] when String.contains s ':' ->
2248 Errors.illegal_meth_fun p; N.Any
2249 | [_, Aast.String s] when genv.in_ppl && SN.PPLFunctions.is_reserved s ->
2250 Errors.ppl_meth_pointer p ("fun("^s^")"); N.Any
2251 | [p, Aast.String x] ->
2252 (* Functions referenced by fun() are always fully-qualified *)
2253 let x = if x <> "" && x.[0] <> '\\' then "\\" ^ x else x in
2254 N.Fun_id (Env.fun_id env (p, x))
2255 | [p, _] -> Errors.illegal_fun p; N.Any
2256 | _ -> Errors.naming_too_many_arguments p; N.Any
2258 | Aast.Call (_, (p, Aast.Id (_, cn)), _, el, uel)
2259 when cn = SN.SpecialFunctions.inst_meth ->
2260 arg_unpack_unexpected uel;
2261 begin
2262 match el with
2263 | []
2264 | [_] -> Errors.naming_too_few_arguments p; N.Any
2265 | instance :: (p, Aast.String meth) :: [] ->
2266 N.Method_id (aast_expr env instance, (p, meth))
2267 | (p, _) :: _ :: [] -> Errors.illegal_inst_meth p; N.Any
2268 | _ -> Errors.naming_too_many_arguments p; N.Any
2270 | Aast.Call (_, (p, Aast.Id (_, cn)), _, el, uel)
2271 when cn = SN.SpecialFunctions.meth_caller ->
2272 arg_unpack_unexpected uel;
2273 begin
2274 match el with
2275 | []
2276 | [_] -> Errors.naming_too_few_arguments p; N.Any
2277 | e1 :: e2 :: [] ->
2278 begin
2279 match (aast_expr env e1), (aast_expr env e2) with
2280 | (pc, N.String cl), (pm, N.String meth) ->
2281 N.Method_caller (Env.type_name env (pc, cl) ~allow_typedef:false ~allow_generics:false, (pm, meth))
2282 | (_, N.Class_const ((_, N.CI cl), (_, mem))), (pm, N.String meth)
2283 when mem = SN.Members.mClass ->
2284 N.Method_caller (Env.type_name env cl ~allow_typedef:false ~allow_generics:false, (pm, meth))
2285 | (p, _), _ -> Errors.illegal_meth_caller p; N.Any
2287 | _ -> Errors.naming_too_many_arguments p; N.Any
2289 | Aast.Call (_, (p, Aast.Id (_, cn)), _, el, uel)
2290 when cn = SN.SpecialFunctions.class_meth ->
2291 arg_unpack_unexpected uel;
2292 begin
2293 match el with
2294 | []
2295 | [_] -> Errors.naming_too_few_arguments p; N.Any
2296 | e1 :: e2 :: [] ->
2297 begin
2298 match (aast_expr env e1), (aast_expr env e2) with
2299 | (pc, N.String cl), (pm, N.String meth) ->
2300 N.Smethod_id (Env.type_name env (pc, cl) ~allow_typedef:false ~allow_generics:false, (pm, meth))
2301 | (_, N.Id (_, const)), (pm, N.String meth)
2302 when const = SN.PseudoConsts.g__CLASS__ ->
2303 (* All of these that use current_cls aren't quite correct
2304 * inside a trait, as the class should be the using class.
2305 * It's sufficient for typechecking purposes (we require
2306 * subclass to be compatible with the trait member/method
2307 * declarations).
2309 (match (fst env).current_cls with
2310 | Some (cid, _) -> N.Smethod_id (cid, (pm, meth))
2311 | None -> Errors.illegal_class_meth p; N.Any)
2312 | (_, N.Class_const ((_, N.CI cl), (_, mem))), (pm, N.String meth)
2313 when mem = SN.Members.mClass ->
2314 N.Smethod_id (Env.type_name env cl ~allow_typedef:false ~allow_generics:false, (pm, meth))
2315 | (p, N.Class_const ((_, (N.CIself | N.CIstatic)), (_, mem))),
2316 (pm, N.String meth) when mem = SN.Members.mClass ->
2317 (match (fst env).current_cls with
2318 | Some (cid, _) -> N.Smethod_id (cid, (pm, meth))
2319 | None -> Errors.illegal_class_meth p; N.Any)
2320 | (p, _), _ -> Errors.illegal_class_meth p; N.Any
2322 | _ -> Errors.naming_too_many_arguments p; N.Any
2324 | Aast.Call (_, (p, Aast.Id (_, cn)), _, el, uel)
2325 when cn = SN.SpecialFunctions.assert_ ->
2326 arg_unpack_unexpected uel;
2327 if List.length el <> 1
2328 then Errors.assert_arity p;
2329 N.Assert (N.AE_assert (
2330 Option.value_map (List.hd el) ~default:(p, N.Any) ~f:(aast_expr env)
2332 | Aast.Call (_, (p, Aast.Id (_, cn)), _, el, uel)
2333 when cn = SN.SpecialFunctions.tuple ->
2334 arg_unpack_unexpected uel;
2335 (match el with
2336 | [] -> Errors.naming_too_few_arguments p; N.Any
2337 | el -> N.List (aast_exprl env el)
2339 (* sample, factor, observe, condition *)
2340 | Aast.Call (_, (p1, Aast.Id (p2, cn)), tal, el, uel)
2341 when Env.in_ppl env && SN.PPLFunctions.is_reserved cn ->
2342 let n_expr = N.Id (p2, cn) in
2343 N.Call (N.Cnormal, (p1, n_expr),
2344 aast_targl env p tal, aast_exprl env el, aast_exprl env uel)
2345 | Aast.Call (_, (p, Aast.Id f), tal, el, uel) ->
2346 begin
2347 match Env.let_local env f with
2348 | Some x ->
2349 (* Translate into local id *)
2350 let f = (p, N.ImmutableVar x) in
2351 N.Call (N.Cnormal, f, aast_targl env p tal, aast_exprl env el, aast_exprl env uel)
2352 | None ->
2353 (* The name is not a local `let` binding *)
2354 let qualified = Env.fun_id env f in
2355 let cn = snd qualified in
2356 (* The above special cases (fun, inst_meth, meth_caller, class_meth,
2357 * and friends) are magical language constructs, which we should
2358 * check before calling fun_id and looking up the function and doing
2359 * namespace normalization. However, gena, genva, etc are actual
2360 * functions that actually exist, we just need to handle them
2361 * specially here, during naming. Note that most of the function
2362 * special cases, such as idx, are actually handled in typing, and
2363 * don't require naming magic. *)
2364 if cn = SN.FB.fgena
2365 then
2366 begin
2367 arg_unpack_unexpected uel;
2368 match el with
2369 | [e] -> N.Special_func (N.Gena (aast_expr env e))
2370 | _ -> Errors.gena_arity p; N.Any
2372 else if (cn = SN.FB.fgenva)
2373 || (cn = SN.HH.asio_va)
2374 || (cn = SN.HH.lib_tuple_gen)
2375 || (cn = SN.HH.lib_tuple_from_async)
2376 then
2377 begin
2378 arg_unpack_unexpected uel;
2379 if List.length el < 1
2380 then (Errors.genva_arity p; N.Any)
2381 else N.Special_func (N.Genva (aast_exprl env el))
2383 else if cn = SN.FB.fgen_array_rec
2384 then
2385 begin
2386 arg_unpack_unexpected uel;
2387 match el with
2388 | [e] -> N.Special_func (N.Gen_array_rec (aast_expr env e))
2389 | _ -> Errors.gen_array_rec_arity p; N.Any
2391 else
2392 N.Call (N.Cnormal, (p, N.Id qualified), aast_targl env p tal,
2393 aast_exprl env el, aast_exprl env uel)
2394 end (* match *)
2395 (* Handle nullsafe instance method calls here. Because Obj_get is used
2396 for both instance property access and instance method calls, we need
2397 to match the entire "Call(Obj_get(..), ..)" pattern here so that we
2398 only match instance method calls *)
2399 | Aast.Call (_, (p, Aast.Obj_get (e1, e2, Aast.OG_nullsafe)), tal, el, uel) ->
2400 N.Call
2401 (N.Cnormal,
2402 (p, N.Obj_get (aast_expr env e1,
2403 aast_expr_obj_get_name env e2, N.OG_nullsafe)),
2404 aast_targl env p tal,
2405 aast_exprl env el, aast_exprl env uel)
2406 (* Handle all kinds of calls that weren't handled by any of the cases above *)
2407 | Aast.Call (_, e, tal, el, uel) ->
2408 N.Call (N.Cnormal, aast_expr env e,
2409 aast_targl env p tal, aast_exprl env el, aast_exprl env uel)
2410 | Aast.Yield_break -> N.Yield_break
2411 | Aast.Yield e -> N.Yield (aast_afield env e)
2412 | Aast.Await e -> N.Await (aast_expr env e)
2413 | Aast.Suspend e -> N.Suspend (aast_expr env e)
2414 | Aast.List el -> N.List (aast_exprl env el)
2415 | Aast.Expr_list el -> N.Expr_list (aast_exprl env el)
2416 | Aast.Cast (ty, e2) ->
2417 let (p, x), hl =
2418 match ty with
2419 | _, Aast.Happly (id, hl) -> (id, hl)
2420 | _ -> assert false in
2421 let ty =
2422 match aast_try_castable_hint ~tp_depth:1 env p x hl with
2423 | Some ty -> p, ty
2424 | None ->
2425 begin
2426 match x with
2427 | x when x = SN.Typehints.object_cast ->
2428 (* (object) is a valid cast but not a valid type annotation *)
2429 if (fst env).in_mode = FileInfo.Mstrict then Errors.object_cast p None;
2430 p, N.Hany
2431 | x when x = SN.Typehints.void ->
2432 Errors.void_cast p;
2433 p, N.Hany
2434 | x when x = SN.Typehints.unset_cast ->
2435 Errors.unset_cast p;
2436 p, N.Hany
2437 | _ ->
2438 (* Let's just assume that any other invalid cases are attempts to
2439 * cast to specific objects *)
2440 let h = aast_hint ~allow_typedef:false env ty in
2441 Errors.object_cast p (Some x);
2443 end in
2444 N.Cast (ty, aast_expr env e2)
2445 | Aast.Unop (uop, e) -> N.Unop (uop, aast_expr env e)
2446 | Aast.Binop (Ast.Eq None as op, lv, e2) ->
2447 if Env.inside_pipe env then
2448 Errors.unimplemented_feature p "Assignment within pipe expressions";
2449 let e2 = aast_expr env e2 in
2450 let nsenv = (fst env).namespace in
2451 let _, vars =
2452 GetLocals.aast_lvalue (nsenv, SMap.empty) lv in
2453 SMap.iter (fun x p -> ignore (Env.new_lvar env (p, x))) vars;
2454 N.Binop (op, aast_expr env lv, e2)
2455 | Aast.Binop (Ast.Eq _ as bop, e1, e2) ->
2456 if Env.inside_pipe env
2457 then Errors.unimplemented_feature p "Assignment within pipe expressions";
2458 N.Binop (bop, aast_expr env e1, aast_expr env e2)
2459 | Aast.Binop (bop, e1, e2) -> N.Binop (bop, aast_expr env e1, aast_expr env e2)
2460 | Aast.Pipe (_dollardollar, e1, e2) ->
2461 let e1 = aast_expr env e1 in
2462 let ident, e2 = Env.pipe_scope env (fun env -> aast_expr env e2) in
2463 N.Pipe ((p, ident), e1, e2)
2464 | Aast.Eif (e1, e2opt, e3) ->
2465 (* The order matters here, of course -- e1 can define vars that need to
2466 * be available in e2 and e3. *)
2467 let e1 = aast_expr env e1 in
2468 let nsenv = (fst env).namespace in
2469 let get_lvalues = function e ->
2470 snd @@ GetLocals.aast_stmt (nsenv, SMap.empty) (Aast.Expr e) in
2471 let e2_lvalues =
2472 Option.value (Option.map e2opt get_lvalues) ~default:SMap.empty
2474 let e3_lvalues = get_lvalues e3 in
2475 let lvalues = smap_inter e2_lvalues e3_lvalues in
2476 SMap.iter (fun x p -> Env.new_pending_lvar env (p, x)) lvalues;
2477 let e2opt, e3 = Env.scope env (fun env ->
2478 let all2, e2opt = Env.scope_all env (fun env -> aast_oexpr env e2opt) in
2479 let all3, e3 = Env.scope_all env (fun env -> aast_expr env e3) in
2480 Env.extend_all_locals env all2;
2481 Env.extend_all_locals env all3;
2482 e2opt, e3
2483 ) in
2484 SMap.iter (fun x _ -> Env.promote_pending_lvar env x) lvalues;
2485 N.Eif (e1, e2opt, e3)
2486 | Aast.InstanceOf (e, (_, Aast.CIexpr (p, Aast.Id x))) ->
2487 let id =
2488 match x with
2489 | px, n when n = SN.Classes.cParent ->
2490 if (fst env).current_cls = None then
2491 let () = Errors.parent_outside_class p in
2492 N.CI (px, SN.Classes.cUnknown)
2493 else N.CIparent
2494 | px, n when n = SN.Classes.cSelf ->
2495 if (fst env).current_cls = None then
2496 let () = Errors.self_outside_class p in
2497 N.CI (px, SN.Classes.cUnknown)
2498 else N.CIself
2499 | px, n when n = SN.Classes.cStatic ->
2500 if (fst env).current_cls = None then
2501 let () = Errors.static_outside_class p in
2502 N.CI (px, SN.Classes.cUnknown)
2503 else N.CIstatic
2504 | _ ->
2505 N.CI (Env.type_name env x ~allow_typedef:false ~allow_generics:false)
2507 N.InstanceOf (aast_expr env e, (p, id))
2508 | Aast.InstanceOf (e1, (_, Aast.CIexpr (_,
2509 (Aast.Lvar _ | Aast.Obj_get _ | Aast.Class_get _ | Aast.Class_const _
2510 | Aast.Array_get _ | Aast.Call _) as e2))) ->
2511 N.InstanceOf (aast_expr env e1, (fst e2, N.CIexpr (aast_expr env e2)))
2512 | Aast.InstanceOf (_e1, (p, _)) ->
2513 Errors.invalid_instanceof p;
2514 N.Any
2515 | Aast.Is (e, h) ->
2516 N.Is (aast_expr env e, aast_hint ~allow_wildcard:true env h)
2517 | Aast.As (e, h, b) ->
2518 N.As (aast_expr env e, aast_hint ~allow_wildcard:true env h, b)
2519 | Aast.New ((_, Aast.CIexpr (p, Aast.Id x)), tal, el, uel, _) ->
2520 N.New (make_class_id env x,
2521 aast_targl env p tal,
2522 aast_exprl env el,
2523 aast_exprl env uel,
2525 | Aast.New ((_, Aast.CIexpr (_, Aast.Lvar (pos, x))), tal, el, uel, p) ->
2526 N.New (make_class_id env (pos, Local_id.to_string x),
2527 aast_targl env p tal,
2528 aast_exprl env el,
2529 aast_exprl env uel,
2531 | Aast.New ((_, Aast.CIexpr(p, _e)), tal, el, uel, _) ->
2532 if (fst env).in_mode = FileInfo.Mstrict
2533 then Errors.dynamic_new_in_strict_mode p;
2534 N.New (make_class_id env (p, SN.Classes.cUnknown),
2535 aast_targl env p tal,
2536 aast_exprl env el,
2537 aast_exprl env uel,
2539 | Aast.New _ -> failwith "ast_to_nast aast.new"
2540 | Aast.NewAnonClass _ ->
2541 Errors.experimental_feature p "Anonymous classes";
2542 N.Null
2543 | Aast.Efun (f, idl) ->
2544 let idl =
2545 List.fold_right idl
2546 ~init:[]
2547 ~f:(fun ((p, x) as id) acc ->
2548 if Local_id.to_string x = SN.SpecialIdents.this
2549 then (Errors.this_as_lexical_variable p; acc)
2550 else id :: acc)
2552 let idl' = List.map idl (Env.aast_lvar env) in
2553 let env = (fst env, Env.empty_local None) in
2554 List.iter2_exn idl idl' (Env.aast_add_lvar env);
2555 let f = aast_expr_lambda env f in
2556 N.Efun (f, idl')
2557 | Aast.Lfun f ->
2558 (* We have to build the capture list while we're finding names in
2559 the closure body---accumulate it in to_capture. *)
2560 let to_capture = ref [] in
2561 let handle_unbound (p, x) =
2562 let cap = Env.lvar env (p, x) in
2563 to_capture := cap :: !to_capture;
2566 let lenv = Env.empty_local @@ Some handle_unbound in
2567 (* Extend the current let binding into the scope of lambda *)
2568 Env.copy_let_locals env (fst env, lenv);
2569 let env = (fst env, lenv) in
2570 let f = aast_expr_lambda env f in
2571 (* TODO T28711692: Compute the correct capture list for let variables,
2572 * it does not seem to affect typechecking... *)
2573 N.Efun (f, !to_capture)
2574 | Aast.Xml (x, al, el) ->
2575 N.Xml (Env.type_name env x ~allow_typedef:false ~allow_generics:false, aast_attrl env al,
2576 aast_exprl env el)
2577 | Aast.Shape fdl ->
2578 let (shp, _) = begin List.fold_left fdl ~init:([], Ast.ShapeSet.empty)
2579 ~f:begin fun (fdm, set) (pname, value) ->
2580 let pos, name = convert_shape_name env pname in
2581 if Ast.ShapeSet.mem name set
2582 then Errors.fd_name_already_bound pos;
2583 (name, (aast_expr env value)) :: fdm, Ast.ShapeSet.add name set
2585 end in
2586 N.Shape (List.rev shp)
2587 | Aast.Unsafe_expr e ->
2588 N.Unsafe_expr (Errors.ignore_ (fun () -> aast_expr env e))
2589 | Aast.BracedExpr _ ->
2590 N.Any
2591 | Aast.Yield_from e ->
2592 N.Yield_from (aast_expr env e)
2593 | Aast.Import _ ->
2594 N.Any
2595 | Aast.Omitted ->
2596 N.Any
2597 | Aast.Callconv (kind, e) ->
2598 N.Callconv (kind, aast_expr env e)
2599 (* The below were not found on the AST.ml so they are not implemented here *)
2600 | Aast.ValCollection _
2601 | Aast.KeyValCollection _
2602 | Aast.This
2603 | Aast.ImmutableVar _
2604 | Aast.Dollardollar _
2605 | Aast.Lplaceholder _
2606 | Aast.Fun_id _
2607 | Aast.Method_id _
2608 | Aast.Method_caller _
2609 | Aast.Smethod_id _
2610 | Aast.Special_func _
2611 | Aast.Pair _
2612 | Aast.Assert _
2613 | Aast.Typename _
2614 | Aast.Any ->
2615 Errors.internal_error p "Malformed expr: Expr not found on legacy AST: T39599317";
2616 Aast.Any
2618 and aast_expr_lambda env f =
2619 let env = Env.set_ppl env false in
2620 let h = Option.map f.Aast.f_ret (aast_hint ~allow_retonly:true env) in
2621 let previous_unsafe = Env.has_unsafe env in
2622 (* save unsafe and yield state *)
2623 Env.set_unsafe env false;
2624 let variadicity, paraml = aast_fun_paraml env f.Aast.f_params in
2625 (* The bodies of lambdas go through naming in the containing local
2626 * environment *)
2627 let body_nast = aast_f_body env f.Aast.f_body in
2628 let annotation =
2629 if func_body_had_unsafe env
2630 then N.BodyNamingAnnotation.NamedWithUnsafeBlocks
2631 else N.BodyNamingAnnotation.Named in
2632 (* restore unsafe state *)
2633 Env.set_unsafe env previous_unsafe;
2634 (* These could all be probably be replaced with a {... where ...} *)
2635 let body = {
2636 N.fb_ast = body_nast;
2637 fb_annotation = annotation;
2638 } in
2639 { N.f_annotation = ();
2640 f_span = f.Aast.f_span;
2641 f_mode = (fst env).in_mode;
2642 f_ret = h;
2643 f_name = f.Aast.f_name;
2644 f_params = paraml;
2645 f_tparams = [];
2646 f_where_constraints = [];
2647 f_body = body;
2648 f_fun_kind = f.Aast.f_fun_kind;
2649 f_variadic = variadicity;
2650 f_file_attributes = [];
2651 f_user_attributes = aast_user_attributes env f.Aast.f_user_attributes;
2652 f_external = f.Aast.f_external;
2653 f_namespace = f.Aast.f_namespace;
2654 f_doc_comment = f.Aast.f_doc_comment;
2655 f_static = f.Aast.f_static;
2658 and aast_f_body env f_body =
2659 if Aast.is_body_named f_body
2660 then aast_block env f_body.Aast.fb_ast
2661 else failwith "Malformed f_body: unexpected UnnamedBody from ast_to_nast"
2663 and make_class_id env (p, x as cid) =
2665 match x with
2666 | x when x = SN.Classes.cParent ->
2667 if (fst env).current_cls = None then
2668 let () = Errors.parent_outside_class p in
2669 N.CI (p, SN.Classes.cUnknown)
2670 else N.CIparent
2671 | x when x = SN.Classes.cSelf ->
2672 if (fst env).current_cls = None then
2673 let () = Errors.self_outside_class p in
2674 N.CI (p, SN.Classes.cUnknown)
2675 else N.CIself
2676 | x when x = SN.Classes.cStatic -> if (fst env).current_cls = None then
2677 let () = Errors.static_outside_class p in
2678 N.CI (p, SN.Classes.cUnknown)
2679 else N.CIstatic
2680 | x when x = SN.SpecialIdents.this -> N.CIexpr (p, N.This)
2681 | x when x = SN.SpecialIdents.dollardollar ->
2682 (* We won't reach here for "new $$" because the parser creates a
2683 * proper Ast.Dollardollar node, so make_class_id won't be called with
2684 * that node. In fact, the parser creates an Ast.Dollardollar for all
2685 * "$$" except in positions where a classname is expected, like in
2686 * static member access. So, we only reach here for things
2687 * like "$$::someMethod()". *)
2688 N.CIexpr(p, N.Lvar (p, Env.found_dollardollar env p))
2689 | x when x.[0] = '$' -> N.CIexpr (p, N.Lvar (Env.lvar env cid))
2690 | _ -> N.CI (Env.type_name env cid ~allow_typedef:false ~allow_generics:false)
2692 and aast_casel env l =
2693 List.map_env [] l (aast_case env)
2695 and aast_case env acc c =
2696 match c with
2697 | Aast.Default b ->
2698 let all_locals, b = aast_branch ~after_unsafe:N.Fallthrough env b in
2699 all_locals :: acc, N.Default b
2700 | Aast.Case (e, b) ->
2701 let e = aast_expr env e in
2702 let all_locals, b = aast_branch ~after_unsafe:N.Fallthrough env b in
2703 all_locals :: acc, N.Case (e, b)
2705 and aast_catchl env l = List.map_env [] l (aast_catch env)
2706 and aast_catch env acc ((p1, lid1), (p2, lid2), b) =
2707 Env.scope
2709 (fun env ->
2710 let name2 = Local_id.get_name lid2 in
2711 (* If the variable does not begin with $, it is an immutable binding *)
2712 let x2 =
2713 if name2 <> ""
2714 && name2.[0] = '$' (* This is always true if not in experimental mode *)
2715 then Env.new_lvar env (p2, name2)
2716 else Env.new_let_local env (p2, name2) in
2717 let all_locals, b = aast_branch env b in
2718 all_locals :: acc, (Env.type_name env (p1, lid1) ~allow_typedef:true ~allow_generics:false, x2, b))
2720 and aast_afield env field =
2721 match field with
2722 | Aast.AFvalue e -> N.AFvalue (aast_expr env e)
2723 | Aast.AFkvalue (e1, e2) -> N.AFkvalue (aast_expr env e1, aast_expr env e2)
2725 and aast_afield_value env cname field =
2726 match field with
2727 | Aast.AFvalue e -> aast_expr env e
2728 | Aast.AFkvalue (e1, _e2) ->
2729 Errors.unexpected_arrow (fst e1) cname;
2730 aast_expr env e1
2732 and aast_afield_kvalue env cname field =
2733 match field with
2734 | Aast.AFvalue e ->
2735 Errors.missing_arrow (fst e) cname;
2736 aast_expr env e,
2737 aast_expr env (fst e, Aast.Lvar (fst e, Local_id.make_unscoped "__internal_placeholder"))
2738 | Aast.AFkvalue (e1, e2) -> aast_expr env e1, aast_expr env e2
2740 and aast_attrl env l = List.map ~f:(aast_attr env) l
2741 and aast_attr env at =
2742 match at with
2743 | Aast.Xhp_simple (x, e) -> N.Xhp_simple (x, aast_expr env e)
2744 | Aast.Xhp_spread e -> N.Xhp_spread (aast_expr env e)
2746 and aast_string2 env idl =
2747 List.map idl (aast_expr env)
2749 (**************************************************************************)
2750 (* Function/Method Body Naming: *)
2751 (* Ensure that, given a function / class, any UnnamedBody within is
2752 * transformed into a a named body *)
2753 (**************************************************************************)
2755 let func_body f =
2756 match f.N.f_body.N.fb_annotation with
2757 | N.BodyNamingAnnotation.Named
2758 | N.BodyNamingAnnotation.NamedWithUnsafeBlocks -> f.N.f_body
2759 | N.BodyNamingAnnotation.Unnamed nsenv ->
2760 let genv = Env.make_fun_genv
2761 SMap.empty f.N.f_mode (snd f.N.f_name) nsenv in
2762 let genv = aast_extend_params genv f.N.f_tparams in
2763 let lenv = Env.empty_local None in
2764 let env = genv, lenv in
2765 let env =
2766 List.fold_left ~f:Env.add_param f.N.f_params ~init:env in
2767 let env = match f.N.f_variadic with
2768 | N.FVellipsis _ | N.FVnonVariadic -> env
2769 | N.FVvariadicArg param -> Env.add_param env param
2771 let fub_ast = aast_block env f.N.f_body.N.fb_ast in
2772 let annotation =
2773 if func_body_had_unsafe env
2774 then N.BodyNamingAnnotation.NamedWithUnsafeBlocks
2775 else N.BodyNamingAnnotation.Named in
2776 Env.check_goto_references env;
2778 N.fb_ast = fub_ast;
2779 fb_annotation = annotation;
2782 let meth_body genv m =
2783 let named_body =
2784 match m.N.m_body.N.fb_annotation with
2785 | N.BodyNamingAnnotation.Named
2786 | N.BodyNamingAnnotation.NamedWithUnsafeBlocks -> m.N.m_body
2787 | N.BodyNamingAnnotation.Unnamed nsenv ->
2788 let genv = { genv with namespace = nsenv } in
2789 let genv = aast_extend_params genv m.N.m_tparams in
2790 let env = genv, Env.empty_local None in
2791 let env =
2792 List.fold_left ~f:Env.add_param m.N.m_params ~init:env in
2793 let env = match m.N.m_variadic with
2794 | N.FVellipsis _ | N.FVnonVariadic -> env
2795 | N.FVvariadicArg param -> Env.add_param env param
2797 let fub_ast = aast_block env m.N.m_body.N.fb_ast in
2798 let annotation =
2799 if func_body_had_unsafe env
2800 then N.BodyNamingAnnotation.NamedWithUnsafeBlocks
2801 else N.BodyNamingAnnotation.Named in
2802 Env.check_goto_references env;
2803 { N.fb_ast = fub_ast;
2804 fb_annotation = annotation;
2805 } in
2806 {m with N.m_body = named_body}
2808 let class_meth_bodies nc =
2809 let { N.c_tparam_constraints = cstrs; _ } = nc.N.c_tparams in
2810 let genv = Env.make_class_genv cstrs
2811 nc.N.c_mode (nc.N.c_name, nc.N.c_kind)
2812 Namespace_env.empty_with_default_popt
2813 (Attributes.mem SN.UserAttributes.uaProbabilisticModel nc.N.c_user_attributes)
2815 let inst_meths = List.map nc.N.c_methods (meth_body genv) in
2816 let opt_constructor = match nc.N.c_constructor with
2817 | None -> None
2818 | Some c -> Some (meth_body genv c) in
2819 let static_meths = List.map nc.N.c_static_methods (meth_body genv) in
2820 { nc with
2821 N.c_methods = inst_meths;
2822 N.c_static_methods = static_meths ;
2823 N.c_constructor = opt_constructor ;
2826 (**************************************************************************)
2827 (* Typedefs *)
2828 (**************************************************************************)
2830 let aast_typedef tdef =
2831 let cstrs = aast_make_constraints tdef.Aast.t_tparams in
2832 let env = Env.aast_make_typedef_env cstrs tdef in
2833 let tconstraint = Option.map tdef.Aast.t_constraint (aast_hint env) in
2834 List.iter tdef.Aast.t_tparams aast_check_constraint;
2835 let tparaml = aast_type_paraml env tdef.Aast.t_tparams in
2836 let attrs = aast_user_attributes env tdef.Aast.t_user_attributes in
2838 N.t_annotation = ();
2839 t_name = tdef.Aast.t_name;
2840 t_tparams = tparaml;
2841 t_constraint = tconstraint;
2842 t_kind = aast_hint env tdef.Aast.t_kind;
2843 t_user_attributes = attrs;
2844 t_mode = tdef.Aast.t_mode;
2845 t_namespace = tdef.Aast.t_namespace;
2846 t_vis = tdef.Aast.t_vis;
2849 let typedef tdef =
2850 let tdef = Ast_to_nast.on_typedef tdef in
2851 aast_typedef tdef
2853 (**************************************************************************)
2854 (* Global constants *)
2855 (**************************************************************************)
2857 let check_constant_hint cst =
2858 match cst.Aast.cst_type with
2859 | None when cst.Aast.cst_mode = FileInfo.Mstrict ->
2860 Errors.add_a_typehint (fst cst.Aast.cst_name)
2861 | None
2862 | Some _ -> ()
2864 let check_constant_name genv cst =
2865 if genv.namespace.Namespace_env.ns_name <> None then
2866 let pos, name = cst.Aast.cst_name in
2867 let name = Utils.strip_all_ns name in
2868 if SN.PseudoConsts.is_pseudo_const (Utils.add_ns name) then
2869 Errors.name_is_reserved name pos
2871 let aast_global_const cst =
2872 let env = Env.aast_make_const_env cst in
2873 let hint = Option.map cst.Aast.cst_type (aast_hint env) in
2874 let e =
2875 (* Define allows any expression, so don't call check_constant.
2876 * Furthermore it often appears at toplevel, which we don't track at
2877 * all, so don't type or even name that expression, it may refer to
2878 * "undefined" variables that actually exist, just untracked since
2879 * they're toplevel. *)
2880 if cst.Aast.cst_is_define
2881 then None
2882 else
2883 let _ = check_constant_name (fst env) cst in
2884 let _ = check_constant_hint cst in
2885 Option.map cst.Aast.cst_value (aast_constant_expr env) in
2886 { N.cst_annotation = ();
2887 cst_mode = cst.Aast.cst_mode;
2888 cst_name = cst.Aast.cst_name;
2889 cst_type = hint;
2890 cst_value = e;
2891 cst_is_define = cst.Aast.cst_is_define;
2892 cst_namespace = cst.Aast.cst_namespace;
2895 let global_const cst =
2896 let cst = Ast_to_nast.on_constant cst in
2897 aast_global_const cst
2899 (**************************************************************************)
2900 (* The entry point to CHECK the program, and transform the program *)
2901 (**************************************************************************)
2903 let aast_program aast =
2904 let top_level_env = ref (Env.make_top_level_env ()) in
2905 let rec aux acc def =
2906 match def with
2907 | Aast.Fun f -> (N.Fun (aast_fun_ f)) :: acc
2908 | Aast.Class c -> (N.Class (aast_class_ c)) :: acc
2909 | Aast.Stmt Aast.Noop
2910 | Aast.Stmt (Aast.Markup _) -> acc
2911 | Aast.Stmt s -> (N.Stmt (aast_stmt !top_level_env s)) :: acc
2912 | Aast.Typedef t -> (N.Typedef (aast_typedef t)) :: acc
2913 | Aast.Constant cst -> (N.Constant (aast_global_const cst)) :: acc
2914 | Aast.Namespace (_ns, aast) -> List.fold_left ~f:aux ~init:[] aast @ acc
2915 | Aast.NamespaceUse _ -> acc
2916 | Aast.SetNamespaceEnv nsenv ->
2917 let (genv, lenv) = !top_level_env in
2918 let genv = { genv with namespace = nsenv } in
2919 top_level_env := (genv, lenv);
2920 acc in
2921 let on_program aast =
2922 let nast = List.fold_left ~f:aux ~init:[] aast in
2923 List.rev nast in
2924 on_program aast
2926 let program ast =
2927 let aast = Ast_to_nast.on_program ast in
2928 aast_program aast
2932 include Make(struct
2933 let stmt acc _ = acc
2934 let lvalue acc _ = acc
2936 let aast_stmt acc _ = acc
2937 let aast_lvalue acc _ = acc
2938 end)