Cleaning up and abstracting the (FFP to AST) lowerer
[hiphop-php.git] / hphp / hack / src / parser / full_fidelity_ast.ml
blob35697865db6841b43fef32bdb57a22bd2a4f0e76
1 (**
2 * Copyright (c) 2016, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
11 module SyntaxTree = Full_fidelity_syntax_tree
12 .WithSyntax(Full_fidelity_positioned_syntax)
13 module Syntax = Full_fidelity_editable_positioned_syntax
14 module SyntaxValue = Syntax.Value
15 module NS = Namespaces
17 module SyntaxKind = Full_fidelity_syntax_kind
18 module TK = Full_fidelity_token_kind
19 module Trivia = Full_fidelity_positioned_trivia
20 module TriviaKind = Full_fidelity_trivia_kind
21 module ParserErrors = Full_fidelity_parser_errors
22 .WithSyntax(Full_fidelity_positioned_syntax)
23 open Prim_defs
25 open Hh_core
27 (* What we're lowering from *)
28 open Syntax
29 type node = Syntax.t (* Let's be more explicit *)
30 (* What we're lowering to *)
31 open Ast
33 let drop_pstr : int -> pstring -> pstring = fun cnt (pos, str) ->
34 let len = String.length str in
35 pos, if cnt >= len then "" else String.sub str cnt (len - cnt)
37 (* Context of the file being parsed, as (hopefully some day read-only) state. *)
38 type env =
39 { hhvm_compat_mode : bool
40 ; is_hh_file : bool
41 ; codegen : bool
42 ; php5_compat_mode : bool
43 ; elaborate_namespaces : bool
44 ; include_line_comments : bool
45 ; keep_errors : bool
46 ; quick_mode : bool
47 ; lower_coroutines : bool
48 ; enable_hh_syntax : bool
49 ; top_level_statements : bool (* Whether we are (still) considering TLSs*)
50 ; parser_options : ParserOptions.t
51 ; fi_mode : FileInfo.mode
52 ; file : Relative_path.t
53 ; stats : Stats_container.t option
54 (* Changing parts; should disappear in future. `mutable` saves allocations. *)
55 ; mutable ignore_pos : bool
56 ; mutable max_depth : int (* Filthy hack around OCaml bug *)
57 ; mutable saw_yield : bool (* Information flowing back up *)
58 ; mutable unsafes : ISet.t (* Offsets of UNSAFE_EXPR in trivia *)
59 ; mutable saw_std_constant_redefinition: bool
62 let non_tls env = if not env.top_level_statements then env else
63 { env with top_level_statements = false }
65 type +'a parser = node -> env -> 'a
66 type ('a, 'b) metaparser = 'a parser -> 'b parser
68 let mode_annotation = function
69 | FileInfo.Mphp -> FileInfo.Mdecl
70 | m -> m
72 let pPos : Pos.t parser = fun node env ->
73 if env.ignore_pos || value node = Value.Synthetic
74 then Pos.none
75 else begin
76 let pos_file = env.file in
77 let text = source_text node in
78 let start_offset = start_offset node in
79 let end_offset = end_offset node in
80 SourceText.relative_pos pos_file text start_offset end_offset
81 end
83 (* HHVM starts range of function declaration from the 'function' keyword *)
84 let pFunction node env =
85 let p = pPos node env in
86 match syntax node with
87 | FunctionDeclaration { function_declaration_header = h; _ }
88 | MethodishDeclaration { methodish_function_decl_header = h; _ }
89 when env.hhvm_compat_mode ->
90 begin match syntax h with
91 | FunctionDeclarationHeader { function_keyword = f; _ }
92 when not (is_missing f) ->
93 let pos_start = Pos.pos_start @@ pPos f env in
94 let pos_end = Pos.pos_end p in
95 let pos_file = env.file in
96 Pos.make_from_file_pos ~pos_file ~pos_start ~pos_end
97 | _ -> p
98 end
99 | _ -> p
102 exception Lowerer_invariant_failure of string * string
103 let invariant_failure node msg env =
104 let pos = Pos.string (Pos.to_absolute (pPos node env)) in
105 raise (Lowerer_invariant_failure (pos, msg))
107 exception API_Missing_syntax of string * env * node
108 let missing_syntax : string -> node -> env -> 'a = fun s n env ->
109 raise (API_Missing_syntax (s, env, n))
110 let handle_missing_syntax : string -> node -> env -> 'a = fun s n e ->
111 let pos = Pos.string (Pos.to_absolute (pPos n e)) in
112 let msg = Printf.sprintf
113 "Missing case in %s.
114 - Pos: %s
115 - Unexpected: '%s'
116 - Kind: %s
120 (text n)
121 (SyntaxKind.to_string (kind n))
123 raise (Failure msg)
125 let runP : 'a parser -> node -> env -> 'a = fun pThing thing env ->
126 try pThing thing env with
127 | API_Missing_syntax (ex, env, n) -> handle_missing_syntax ex n env
128 | e -> raise e
131 (* TODO: Cleanup this hopeless Noop mess *)
132 let mk_noop pos : stmt list -> stmt list = function
133 | [] -> [pos, Noop]
134 | s -> s
135 let mpStripNoop pThing node env = match pThing node env with
136 | [_, Noop] -> []
137 | stmtl -> stmtl
139 let mpOptional : ('a, 'a option) metaparser = fun p -> fun node env ->
140 Option.try_with (fun () -> p node env)
141 let mpYielding : ('a, ('a * bool)) metaparser = fun p node env ->
142 let outer_saw_yield = env.saw_yield in
143 let () = env.saw_yield <- false in
144 let result = p node env in
145 let result = result, env.saw_yield in
146 let () = env.saw_yield <- outer_saw_yield in
147 result
149 type expr_location =
150 | TopLevel
151 | MemberSelect
152 | InDoubleQuotedString
153 | InBacktickedString
154 | InGlobalVar
156 let in_string l =
157 l = InDoubleQuotedString || l = InBacktickedString
159 let pos_qualified_name node env =
160 let aux p =
161 match syntax p with
162 | ListItem li -> (text li.list_item) ^ (text li.list_separator)
163 | _ -> text p in
164 let p = pPos node env in
165 let name =
166 match syntax node with
167 | QualifiedName {
168 qualified_name_parts = { syntax = SyntaxList l; _ };
169 } ->
170 String.concat "" @@ List.map ~f:aux l
171 | _ -> missing_syntax "qualified name" node env in
172 p, name
174 let rec pos_name node env =
175 match syntax node with
176 | QualifiedName _ -> pos_qualified_name node env
177 | SimpleTypeSpecifier { simple_type_specifier = s } -> pos_name s env
178 | _ ->
179 let name = text node in
180 let local_ignore_pos = env.ignore_pos in
181 (* Special case for __LINE__; never ignore position for that special name *)
182 if name = "__LINE__" then env.ignore_pos <- false;
183 let p = pPos node env in
184 env.ignore_pos <- local_ignore_pos;
185 p, name
187 let is_ret_by_ref node = not @@ is_missing node
189 let couldMap : 'a . f:'a parser -> 'a list parser = fun ~f -> fun node env ->
190 let rec synmap : 'a . 'a parser -> 'a list parser = fun f node env ->
191 match syntax node with
192 | SyntaxList l -> List.concat_map l ~f:(fun n -> go ~f n env)
193 | ListItem i -> [f i.list_item env]
194 | _ -> [f node env]
195 and go : 'a . f:'a parser -> 'a list parser = fun ~f -> function
196 | node when is_missing node -> fun _env -> []
197 | node -> synmap f node
199 go ~f node env
201 let as_list : node -> node list =
202 let strip_list_item = function
203 | { syntax = ListItem { list_item = i; _ }; _ } -> i
204 | x -> x
205 in function
206 | { syntax = SyntaxList ({syntax = ListItem _; _}::_ as synl); _ } ->
207 List.map ~f:strip_list_item synl
208 | { syntax = SyntaxList synl; _ } -> synl
209 | { syntax = Missing; _ } -> []
210 | syn -> [syn]
212 let token_kind : node -> TK.t option = function
213 | { syntax = Token t; _ } -> Some (Token.kind t)
214 | _ -> None
216 let pBop : (expr -> expr -> expr_) parser = fun node env lhs rhs ->
217 match token_kind node with
218 | Some TK.Equal -> Binop (Eq None, lhs, rhs)
219 | Some TK.Bar -> Binop (Bar, lhs, rhs)
220 | Some TK.Ampersand -> Binop (Amp, lhs, rhs)
221 | Some TK.Plus -> Binop (Plus, lhs, rhs)
222 | Some TK.Minus -> Binop (Minus, lhs, rhs)
223 | Some TK.Star -> Binop (Star, lhs, rhs)
224 | Some TK.Or -> Binop (BArbar, lhs, rhs)
225 | Some TK.And -> Binop (AMpamp, lhs, rhs)
226 | Some TK.Xor -> Binop (LogXor, lhs, rhs)
227 | Some TK.Carat -> Binop (Xor, lhs, rhs)
228 | Some TK.Slash -> Binop (Slash, lhs, rhs)
229 | Some TK.Dot -> Binop (Dot, lhs, rhs)
230 | Some TK.Percent -> Binop (Percent, lhs, rhs)
231 | Some TK.LessThan -> Binop (Lt, lhs, rhs)
232 | Some TK.GreaterThan -> Binop (Gt, lhs, rhs)
233 | Some TK.EqualEqual -> Binop (Eqeq, lhs, rhs)
234 | Some TK.LessThanEqual -> Binop (Lte, lhs, rhs)
235 | Some TK.GreaterThanEqual -> Binop (Gte, lhs, rhs)
236 | Some TK.StarStar -> Binop (Starstar, lhs, rhs)
237 | Some TK.ExclamationEqual -> Binop (Diff, lhs, rhs)
238 | Some TK.BarEqual -> Binop (Eq (Some Bar), lhs, rhs)
239 | Some TK.PlusEqual -> Binop (Eq (Some Plus), lhs, rhs)
240 | Some TK.MinusEqual -> Binop (Eq (Some Minus), lhs, rhs)
241 | Some TK.StarEqual -> Binop (Eq (Some Star), lhs, rhs)
242 | Some TK.StarStarEqual -> Binop (Eq (Some Starstar),lhs, rhs)
243 | Some TK.SlashEqual -> Binop (Eq (Some Slash), lhs, rhs)
244 | Some TK.DotEqual -> Binop (Eq (Some Dot), lhs, rhs)
245 | Some TK.PercentEqual -> Binop (Eq (Some Percent), lhs, rhs)
246 | Some TK.CaratEqual -> Binop (Eq (Some Xor), lhs, rhs)
247 | Some TK.AmpersandEqual -> Binop (Eq (Some Amp), lhs, rhs)
248 | Some TK.BarBar -> Binop (BArbar, lhs, rhs)
249 | Some TK.AmpersandAmpersand -> Binop (AMpamp, lhs, rhs)
250 | Some TK.LessThanLessThan -> Binop (Ltlt, lhs, rhs)
251 | Some TK.GreaterThanGreaterThan -> Binop (Gtgt, lhs, rhs)
252 | Some TK.EqualEqualEqual -> Binop (EQeqeq, lhs, rhs)
253 | Some TK.LessThanLessThanEqual -> Binop (Eq (Some Ltlt), lhs, rhs)
254 | Some TK.GreaterThanGreaterThanEqual -> Binop (Eq (Some Gtgt), lhs, rhs)
255 | Some TK.LessThanGreaterThan -> Binop (Diff, lhs, rhs)
256 | Some TK.ExclamationEqualEqual -> Binop (Diff2, lhs, rhs)
257 | Some TK.LessThanEqualGreaterThan -> Binop (Cmp, lhs, rhs)
258 (* The ugly ducklings; In the FFP, `|>` and '??' are parsed as
259 * `BinaryOperator`s, whereas the typed AST has separate constructors for
260 * NullCoalesce, Pipe and Binop. This is why we don't just project onto a
261 * `bop`, but a `expr -> expr -> expr_`.
263 | Some TK.BarGreaterThan -> Pipe (lhs, rhs)
264 | Some TK.QuestionQuestion -> NullCoalesce (lhs, rhs)
265 | Some TK.QuestionColon -> Eif (lhs, None, rhs)
266 (* TODO: Figure out why this fails silently when used in a pBlock; probably
267 just caught somewhere *)
268 | _ -> missing_syntax "binary operator" node env
270 let pImportFlavor : import_flavor parser = fun node env ->
271 match token_kind node with
272 | Some TK.Include -> Include
273 | Some TK.Require -> Require
274 | Some TK.Include_once -> IncludeOnce
275 | Some TK.Require_once -> RequireOnce
276 | _ -> missing_syntax "import flavor" node env
278 let pNullFlavor : og_null_flavor parser = fun node env ->
279 match token_kind node with
280 | Some TK.QuestionMinusGreaterThan -> OG_nullsafe
281 | Some TK.MinusGreaterThan -> OG_nullthrows
282 | _ -> missing_syntax "null flavor" node env
284 type modifiers = {
285 has_async: bool;
286 has_coroutine: bool;
287 kinds: kind list
290 let pModifiers node env =
291 let f (has_async, has_coroutine, kinds) node =
292 match token_kind node with
293 | Some TK.Final -> has_async, has_coroutine, (Final :: kinds)
294 | Some TK.Static -> has_async, has_coroutine, (Static :: kinds)
295 | Some TK.Abstract -> has_async, has_coroutine, (Abstract :: kinds)
296 | Some TK.Private -> has_async, has_coroutine, (Private :: kinds)
297 | Some TK.Public -> has_async, has_coroutine, (Public :: kinds)
298 | Some TK.Protected -> has_async, has_coroutine, (Protected :: kinds)
299 | Some TK.Var -> has_async, has_coroutine, (Public :: kinds)
300 | Some TK.Async -> true, has_coroutine, kinds
301 | Some TK.Coroutine -> has_async, true, kinds
302 | _ -> missing_syntax "kind" node env in
303 let (has_async, has_coroutine, kinds) =
304 Core_list.fold_left ~init:(false, false, []) ~f (as_list node) in
305 { has_async; has_coroutine; kinds = List.rev kinds }
307 let pKinds node env = (pModifiers node env).kinds
309 let pParamKind : param_kind parser = fun node env ->
310 match token_kind node with
311 | Some TK.Inout -> Pinout
312 | _ -> missing_syntax "param kind" node env
314 let syntax_of_token : Token.t -> node = fun t ->
315 let value =
316 SyntaxValue.from_token t in
317 make (Token t) value
319 (* TODO: Clean up string escaping *)
320 let prepString2 : node list -> node list =
321 let is_double_quote_or_backtick ch = ch = '"' || ch = '`' in
322 let trimLeft ~n t =
323 Token.(
324 with_updated_original_source_data
326 SourceData.(fun t ->
327 { t with
328 leading_width = leading_width t + n;
329 width = width t - n;
334 let trimRight ~n t =
335 Token.(
336 with_updated_original_source_data
338 SourceData.(fun t ->
339 { t with
340 trailing_width = trailing_width t + n;
341 width = width t - n;
346 function
347 | ({ syntax = Token t; _ }::ss)
348 when (Token.width t) > 0 && is_double_quote_or_backtick (Token.text t).[0] ->
349 let rec unwind = function
350 | [{ syntax = Token t; _ }]
351 when (Token.width t) > 0 &&
352 is_double_quote_or_backtick ((Token.text t).[(Token.width t) - 1]) ->
353 let s = syntax_of_token (trimRight ~n:1 t) in
354 if width s > 0 then [s] else []
355 | x :: xs -> x :: unwind xs
356 | _ -> raise (Invalid_argument "Malformed String2 SyntaxList")
358 let s = syntax_of_token (trimLeft ~n:1 t) in
359 if width s > 0 then s :: unwind ss else unwind ss
360 | ({ syntax = Token t; _ }::ss)
361 when (Token.width t) > 3 && String.sub (Token.text t) 0 3 = "<<<" ->
362 let rec unwind = function
363 | [{ syntax = Token t; _ }] when (Token.width t) > 0 ->
364 let content = Token.text t in
365 let len = (Token.width t) in
366 let n = len - (String.rindex_from content (len - 2) '\n') in
367 let s = syntax_of_token (trimRight ~n t) in
368 if width s > 0 then [s] else []
369 | x :: xs -> x :: unwind xs
370 | _ -> raise (Invalid_argument "Malformed String2 SyntaxList")
372 let content = Token.text t in
373 let n = (String.index content '\n') + 1 in
374 let s = syntax_of_token (trimLeft ~n t) in
375 if width s > 0 then s :: unwind ss else unwind ss
376 | x -> x (* unchanged *)
378 let mkStr : (string -> string) -> string -> string = fun unescaper content ->
379 let len = String.length content in
380 let no_quotes = try
381 if len >= 3 && String.sub content 0 3 = "<<<" (* The heredoc case *)
382 then
383 (* These types of strings begin with an opening line containing <<<
384 * followed by a string to use as a terminator (which is optionally
385 * quoted) and end with a line containing only the terminator and a
386 * semicolon followed by a blank line. We need to drop the opening
387 * line as well as the blank line and preceding terminator line. *)
388 let start = (String.index content '\n') + 1 in
389 let end_ = (String.rindex_from content (len - 2) '\n') in
390 (* in case of empty heredoc expected start position will be
391 located after the end *)
392 if start >= end_ then ""
393 else String.sub content start (end_ - start)
394 else
395 let transform_binary_string_to_string_if_needed str =
396 if String.get str 0 = 'b'
397 then String.sub str 1 (String.length str - 1)
398 else str in
399 let content = transform_binary_string_to_string_if_needed content in
400 let has_quotes str =
401 let len = String.length str in
402 if len >= 2 then
403 let first_char = String.get content 0 in
404 let last_char = String.get content (len - 1) in
405 let first_and_last_are ch = (first_char == ch && last_char == ch) in
406 List.exists ['"'; '\''; '`'] ~f:first_and_last_are
407 else false in
408 if has_quotes content
409 then String.sub content 1 (String.length content - 2)
410 else content
411 with
412 | Invalid_argument _ -> content
414 try unescaper no_quotes with
415 | Php_escaping.Invalid_string _ -> raise @@
416 Failure (Printf.sprintf "Malformed string literal <<%s>>" no_quotes)
417 let unempty_str = function
418 | "''" | "\"\"" -> ""
419 | s -> s
420 let unesc_dbl s = unempty_str @@ Php_escaping.unescape_double s
421 let unesc_xhp s =
422 let whitespace = Str.regexp "[ \t\n\r\012]+" in
423 Str.global_replace whitespace " " s
424 let unesc_xhp_attr s =
425 let open Str in
426 unesc_dbl @@
427 if string_match (regexp "[ \t\n\r\012]*\"\\(\\(.\\|\n\\)*\\)\"") s 0
428 then matched_group 1 s
429 else s
431 type suspension_kind =
432 | SKSync
433 | SKAsync
434 | SKCoroutine
436 let mk_suspension_kind_ has_async has_coroutine =
437 match has_async, has_coroutine with
438 | false, false -> SKSync
439 | true, false -> SKAsync
440 | false, true-> SKCoroutine
441 | true, true -> raise (Failure "Couroutine functions may not be async")
443 let mk_suspension_kind is_async is_coroutine =
444 mk_suspension_kind_
445 (not (is_missing is_async))
446 (not (is_missing is_coroutine))
448 let mk_fun_kind suspension_kind yield =
449 match suspension_kind, yield with
450 | SKSync, true -> FGenerator
451 | SKAsync, true -> FAsyncGenerator
452 | SKSync, false -> FSync
453 | SKAsync, false -> FAsync
454 (* TODO(t17335630): Implement an FCoroutine fun_kind *)
455 | SKCoroutine, false -> assert false
456 | SKCoroutine, true -> raise (Failure "Couroutine functions may not yield")
458 let fun_template yielding node suspension_kind env =
459 let p = pFunction node env in
460 { f_mode = mode_annotation env.fi_mode
461 ; f_tparams = []
462 ; f_constrs = []
463 ; f_ret = None
464 ; f_ret_by_ref = false
465 ; f_name = p, ";anonymous"
466 ; f_params = []
467 ; f_body = []
468 ; f_user_attributes = []
469 ; f_fun_kind = mk_fun_kind suspension_kind yielding
470 ; f_namespace = Namespace_env.empty env.parser_options
471 ; f_span = p
472 ; f_doc_comment = None
473 ; f_static = false
476 let param_template node env =
477 { param_hint = None
478 ; param_is_reference = false
479 ; param_is_variadic = false
480 ; param_id = pos_name node env
481 ; param_expr = None
482 ; param_modifier = None
483 ; param_callconv = None
484 ; param_user_attributes = []
487 let pShapeFieldName : shape_field_name parser = fun name env ->
488 match syntax name with
489 | ScopeResolutionExpression
490 { scope_resolution_qualifier; scope_resolution_name; _ } ->
491 SFclass_const
492 ( pos_name scope_resolution_qualifier env
493 , pos_name scope_resolution_name env
495 | _ -> let p, n = pos_name name env in SFlit (p, mkStr unesc_dbl n)
497 let mpShapeExpressionField : ('a, (shape_field_name * 'a)) metaparser =
498 fun hintParser node env ->
499 match syntax node with
500 | FieldInitializer
501 { field_initializer_name = name; field_initializer_value = ty; _ } ->
502 let name = pShapeFieldName name env in
503 let ty = hintParser ty env in
504 name, ty
505 | _ -> missing_syntax "shape field" node env
507 let mpShapeField : ('a, shape_field) metaparser =
508 fun hintParser node env ->
509 match syntax node with
510 | FieldSpecifier { field_question; field_name; field_type; _ } ->
511 let sf_optional = not (is_missing field_question) in
512 let sf_name = pShapeFieldName field_name env in
513 let sf_hint = hintParser field_type env in
514 { sf_optional; sf_name; sf_hint }
515 | _ ->
516 let sf_name, sf_hint = mpShapeExpressionField hintParser node env in
517 (* Shape expressions can never have optional fields. *)
518 { sf_optional = false; sf_name; sf_hint }
520 let mpClosureParameter : ('a, hint * param_kind option) metaparser =
521 fun hintParser node env ->
522 match syntax node with
523 | ClosureParameterTypeSpecifier
524 { closure_parameter_call_convention
525 ; closure_parameter_type
526 } ->
527 let cp_kind =
528 mpOptional pParamKind closure_parameter_call_convention env in
529 let cp_hint = hintParser closure_parameter_type env in
530 cp_hint, cp_kind
531 | _ -> missing_syntax "closure parameter" node env
533 let rec pHint : hint parser = fun node env ->
534 let rec pHint_ : hint_ parser = fun node env ->
535 match syntax node with
536 (* Dirty hack; CastExpression can have type represented by token *)
537 | Token _
538 | SimpleTypeSpecifier _
539 | QualifiedName _
540 -> Happly (pos_name node env, [])
541 | ShapeTypeSpecifier { shape_type_fields; shape_type_ellipsis; _ } ->
542 let si_allows_unknown_fields =
543 not (is_missing shape_type_ellipsis)
545 let si_shape_field_list =
546 couldMap ~f:(mpShapeField pHint) shape_type_fields env in
547 Hshape { si_allows_unknown_fields; si_shape_field_list }
548 | TupleTypeSpecifier { tuple_types; _ } ->
549 Htuple (couldMap ~f:pHint tuple_types env)
550 | KeysetTypeSpecifier { keyset_type_keyword = kw; keyset_type_type = ty; _ }
551 | VectorTypeSpecifier { vector_type_keyword = kw; vector_type_type = ty; _ }
552 | ClassnameTypeSpecifier {classname_keyword = kw; classname_type = ty; _ }
553 | TupleTypeExplicitSpecifier
554 { tuple_type_keyword = kw
555 ; tuple_type_types = ty
556 ; _ }
557 | VarrayTypeSpecifier
558 { varray_keyword = kw
559 ; varray_type = ty
560 ; _ }
561 | VectorArrayTypeSpecifier
562 { vector_array_keyword = kw
563 ; vector_array_type = ty
564 ; _ }
565 -> Happly (pos_name kw env, couldMap ~f:pHint ty env)
567 | DarrayTypeSpecifier
568 { darray_keyword = kw
569 ; darray_key = key
570 ; darray_value = value
571 ; _ }
572 | MapArrayTypeSpecifier
573 { map_array_keyword = kw
574 ; map_array_key = key
575 ; map_array_value = value
576 ; _ } ->
577 Happly
578 ( pos_name kw env
579 , pHint key env :: couldMap ~f:pHint value env
581 | DictionaryTypeSpecifier
582 { dictionary_type_keyword = kw
583 ; dictionary_type_members = members
584 ; _ } -> Happly (pos_name kw env, couldMap ~f:pHint members env)
585 | GenericTypeSpecifier { generic_class_type; generic_argument_list } ->
586 Happly
587 ( pos_name generic_class_type env
588 , match syntax generic_argument_list with
589 | TypeArguments { type_arguments_types; _ }
590 -> couldMap ~f:pHint type_arguments_types env
591 | _ -> missing_syntax "generic type arguments" generic_argument_list env
593 | NullableTypeSpecifier { nullable_type; _ } ->
594 Hoption (pHint nullable_type env)
595 | SoftTypeSpecifier { soft_type; _ } ->
596 Hsoft (pHint soft_type env)
597 | ClosureTypeSpecifier {
598 closure_parameter_list;
599 closure_return_type;
600 closure_coroutine; _} ->
601 let make_variadic_hint variadic_type =
602 if is_missing variadic_type
603 then Hvariadic (None)
604 else Hvariadic (Some (pHint variadic_type env))
606 let (param_list, variadic_hints) =
607 List.partition_map ~f:(fun x ->
608 match syntax x with
609 | VariadicParameter { variadic_parameter_type = vtype; _ } ->
610 `Snd (make_variadic_hint vtype)
611 | _ -> `Fst (mpClosureParameter pHint x env))
612 (as_list closure_parameter_list)
614 let hd_variadic_hint hints =
615 if List.length hints > 1 then begin
616 let msg = Printf.sprintf
617 "%d variadic parameters found. There should be no more than one."
618 (List.length hints)
620 invariant_failure node msg env
621 end;
622 match List.hd hints with
623 | Some h -> h
624 | None -> Hnon_variadic
626 let is_coroutine = not (is_missing closure_coroutine) in
627 let param_type_hints = List.map param_list fst in
628 let param_callconvs = List.map param_list snd in
629 Hfun
630 ( is_coroutine
631 , param_type_hints
632 , param_callconvs
633 , hd_variadic_hint variadic_hints
634 , pHint closure_return_type env
636 | TypeConstant { type_constant_left_type; type_constant_right_type; _ } ->
637 let child = pos_name type_constant_right_type env in
638 (match pHint_ type_constant_left_type env with
639 | Haccess (b, c, cs) -> Haccess (b, c, cs @ [child])
640 | Happly (b, []) -> Haccess (b, child, [])
641 | _ -> missing_syntax "type constant base" node env
643 | _ -> missing_syntax "type hint" node env
645 pPos node env, pHint_ node env
647 type fun_hdr =
648 { fh_suspension_kind : suspension_kind
649 ; fh_name : pstring
650 ; fh_constrs : (hint * constraint_kind * hint) list
651 ; fh_type_parameters : tparam list
652 ; fh_parameters : fun_param list
653 ; fh_return_type : hint option
654 ; fh_param_modifiers : fun_param list
655 ; fh_ret_by_ref : bool
658 let empty_fun_hdr =
659 { fh_suspension_kind = SKSync
660 ; fh_name = Pos.none, "<ANONYMOUS>"
661 ; fh_constrs = []
662 ; fh_type_parameters = []
663 ; fh_parameters = []
664 ; fh_return_type = None
665 ; fh_param_modifiers = []
666 ; fh_ret_by_ref = false
669 let rec pSimpleInitializer node env =
670 match syntax node with
671 | SimpleInitializer { simple_initializer_value; simple_initializer_equal } ->
672 pExpr simple_initializer_value env
673 | _ -> missing_syntax "simple initializer" node env
675 and pFunParam : fun_param parser = fun node env ->
676 match syntax node with
677 | ParameterDeclaration
678 { parameter_attribute
679 ; parameter_visibility
680 ; parameter_call_convention
681 ; parameter_type
682 ; parameter_name
683 ; parameter_default_value
684 } ->
685 let is_reference, is_variadic, name =
686 match syntax parameter_name with
687 | DecoratedExpression
688 { decorated_expression_decorator; decorated_expression_expression } ->
689 (* There is a chance that the expression might be nested with an
690 additional decorator, check this *)
691 begin match syntax decorated_expression_expression with
692 | DecoratedExpression
693 { decorated_expression_decorator = nested_decorator
694 ; decorated_expression_expression = nested_expression } ->
695 let decorator = text decorated_expression_decorator in
696 let nested_decorator = text nested_decorator in
697 decorator = "&" || nested_decorator = "&",
698 decorator = "..." || nested_decorator = "...",
699 nested_expression
700 | _ ->
701 let decorator = text decorated_expression_decorator in
702 decorator = "&", decorator = "...", decorated_expression_expression
704 | _ -> false, false, parameter_name
706 { param_hint = mpOptional pHint parameter_type env
707 ; param_is_reference = is_reference
708 ; param_is_variadic = is_variadic
709 ; param_id = pos_name name env
710 ; param_expr =
711 mpOptional pSimpleInitializer parameter_default_value env
712 ; param_user_attributes = List.concat @@
713 couldMap ~f:pUserAttribute parameter_attribute env
714 ; param_callconv =
715 mpOptional pParamKind parameter_call_convention env
716 (* implicit field via constructor parameter.
717 * This is always None except for constructors and the modifier
718 * can be only Public or Protected or Private.
720 ; param_modifier =
721 let rec go = function
722 | [] -> None
723 | x :: _ when List.mem [Private; Public; Protected] x -> Some x
724 | _ :: xs -> go xs
726 go (pKinds parameter_visibility env)
728 | VariadicParameter _
729 | Token _ when text node = "..."
730 -> { (param_template node env) with param_is_variadic = true }
731 | _ -> missing_syntax "function parameter" node env
732 and pUserAttribute : user_attribute list parser = fun node env ->
733 match syntax node with
734 | AttributeSpecification { attribute_specification_attributes; _ } ->
735 couldMap attribute_specification_attributes env ~f:begin function
736 | { syntax = Attribute { attribute_name; attribute_values; _}; _ } ->
737 fun env ->
738 { ua_name = pos_name attribute_name env
739 ; ua_params = couldMap ~f:pExpr attribute_values env
741 | node -> missing_syntax "attribute" node
743 | _ -> missing_syntax "attribute specification" node env
744 and pAField : afield parser = fun node env ->
745 match syntax node with
746 | ElementInitializer { element_key; element_value; _ } ->
747 AFkvalue (pExpr element_key env, pExpr element_value env)
748 | _ -> AFvalue (pExpr node env)
749 and pString2: expr_location -> node list -> env -> expr list =
750 let rec convert_name_to_lvar location env n =
751 match syntax n with
752 | Token { Token.kind = TK.Name; _ } ->
753 let pos, name = pos_name n env in
754 let id = Lvar (pos, "$" ^ name) in
755 Some (pos, id)
756 | SubscriptExpression { subscript_receiver; subscript_index; _ } ->
757 begin match convert_name_to_lvar location env subscript_receiver with
758 | Some recv ->
759 let index = mpOptional (pExpr ~location) subscript_index env in
760 Some (pPos n env, Array_get (recv, index))
761 | _ -> None
763 | _ -> None in
765 let rec aux loc l env acc =
766 (* in PHP "${x}" in strings is treated as if it was written "$x",
767 here we recognize pattern: Dollar; EmbeddedBracedExpression { QName (Token.Name) }
768 produced by FFP and lower it into Lvar.
770 match l with
771 | [] -> List.rev acc
772 | ({ syntax = Token { Token.kind = TK.Dollar; _ }; _ })::
773 ({ syntax = EmbeddedBracedExpression {
774 embedded_braced_expression_expression = e; _ }; _
775 } as expr_with_braces)::
776 tl ->
777 let e =
778 begin match convert_name_to_lvar loc env e with
779 | Some e -> e
780 | None ->
781 let e = pExpr ~location:loc expr_with_braces env in
782 fst e, Dollar (fst e, BracedExpr e)
783 end in
784 aux loc tl env (e::acc)
785 | x::xs -> aux loc xs env ((pExpr ~location:loc x env)::acc)
787 fun loc l env -> aux loc l env []
788 and pExprL node env =
789 (pPos node env, Expr_list (couldMap ~f:pExpr node env))
790 and pExpr ?location:(location=TopLevel) : expr parser = fun node env ->
791 let split_args_varargs arg_list =
792 match List.rev (as_list arg_list) with
793 | { syntax = DecoratedExpression
794 { decorated_expression_decorator =
795 { syntax = Token { Token.kind = Token.TokenKind.DotDotDot; _ }; _ }
796 ; decorated_expression_expression = e
799 } :: xs ->
800 let args = List.rev_map xs (fun x -> pExpr x env) in
801 let vararg = pExpr e env in
802 args, [vararg]
803 | _ ->
804 let args = couldMap ~f:pExpr arg_list env in
805 args, [] in
806 let rec pExpr_ : expr_ parser = fun node env ->
807 let pos = pPos node env in
808 match syntax node with
809 | LambdaExpression {
810 lambda_async; lambda_coroutine; lambda_signature; lambda_body; _ } ->
811 let suspension_kind =
812 mk_suspension_kind lambda_async lambda_coroutine in
813 let f_params, f_ret =
814 match syntax lambda_signature with
815 | LambdaSignature { lambda_parameters; lambda_type; _ } ->
816 ( couldMap ~f:pFunParam lambda_parameters env
817 , mpOptional pHint lambda_type env
819 | Token _ -> ([param_template lambda_signature env], None)
820 | _ -> missing_syntax "lambda signature" lambda_signature env
822 let f_body, yield =
823 mpYielding pFunctionBody lambda_body
824 (if not (is_compound_statement lambda_body) then env else non_tls env)
826 Lfun
827 { (fun_template yield node suspension_kind env) with
828 f_ret
829 ; f_params
830 ; f_body
833 | BracedExpression { braced_expression_expression = expr; _ }
834 | EmbeddedBracedExpression
835 { embedded_braced_expression_expression = expr; _ }
836 | ParenthesizedExpression { parenthesized_expression_expression = expr; _ }
837 -> pExpr_ expr env
839 | DictionaryIntrinsicExpression
840 { dictionary_intrinsic_keyword = kw
841 ; dictionary_intrinsic_members = members
842 ; _ }
843 | KeysetIntrinsicExpression
844 { keyset_intrinsic_keyword = kw
845 ; keyset_intrinsic_members = members
846 ; _ }
847 | VectorIntrinsicExpression
848 { vector_intrinsic_keyword = kw
849 ; vector_intrinsic_members = members
850 ; _ }
852 let members_opt =
853 try Some (couldMap ~f:pExpr members env) with
854 | API_Missing_syntax (_,_,n)
855 when kind n = SyntaxKind.ElementInitializer -> None
857 if env.is_hh_file || env.enable_hh_syntax || members_opt = None
858 then Collection (pos_name kw env, couldMap ~f:pAField members env)
859 else
860 (* If php, this is a subscript expression, not a collection. *)
861 let subscript_receiver = pExpr kw env in
862 let members = couldMap ~f:pExpr members env in
863 let subscript_index = match members with
864 | [] -> None
865 | [x] -> Some x
866 | _ ->
867 let msg = "Hack keyword " ^ (text kw) ^ " used improperly in php." in
868 invariant_failure node msg env in
869 Array_get (subscript_receiver, subscript_index)
870 | CollectionLiteralExpression
871 { collection_literal_name = collection_name
872 ; collection_literal_initializers = members
873 ; _ } ->
874 let collection_name =
875 match syntax collection_name with
876 | SimpleTypeSpecifier { simple_type_specifier = class_type }
877 (* TODO: currently type arguments are dropped on the floor,
878 though they should be properly propagated to the typechecker *)
879 | GenericTypeSpecifier { generic_class_type = class_type; _ } ->
880 pos_name class_type env
881 | _ -> pos_name collection_name env in
882 Collection (collection_name, couldMap ~f:pAField members env)
884 | VarrayIntrinsicExpression { varray_intrinsic_members = members; _ } ->
885 Varray (couldMap ~f:pExpr members env)
886 | DarrayIntrinsicExpression { darray_intrinsic_members = members; _ } ->
887 let pMember node env =
888 match syntax node with
889 | ElementInitializer { element_key; element_value; _ } ->
890 (pExpr element_key env, pExpr element_value env)
891 | _ -> missing_syntax "darray intrinsic expression element" node env
893 Darray (couldMap ~f:pMember members env)
894 | ArrayIntrinsicExpression { array_intrinsic_members = members; _ }
895 | ArrayCreationExpression { array_creation_members = members; _ }
897 (* TODO: Or tie in with other intrinsics and post-process to Array *)
898 Array (couldMap ~f:pAField members env)
900 | ListExpression { list_members = members; _ } ->
901 (* TODO: Or tie in with other intrinsics and post-process to List *)
902 let pBinderOrIgnore node env =
903 Option.value ~default:(Pos.none, Omitted) @@ mpOptional pExpr node env
905 List (couldMap ~f:pBinderOrIgnore members env)
907 | EvalExpression { eval_keyword = recv; eval_argument = args; _ }
908 | EmptyExpression { empty_keyword = recv; empty_argument = args; _ }
909 | IssetExpression { isset_keyword = recv; isset_argument_list = args; _ }
910 | TupleExpression
911 { tuple_expression_keyword = recv
912 ; tuple_expression_items = args
913 ; _ }
914 -> Call (pExpr recv env, [], couldMap ~f:pExpr args env, [])
915 | FunctionCallExpression
916 { function_call_receiver = recv
917 ; function_call_argument_list = args
918 ; _ }
920 let hints =
921 begin match (syntax recv) with
922 | GenericTypeSpecifier {generic_class_type; generic_argument_list} ->
923 begin match syntax generic_argument_list with
924 | TypeArguments { type_arguments_types; _ }
925 -> couldMap ~f:pHint type_arguments_types env
926 | _ -> []
928 | _ -> []
931 (* preserve parens on receiver of call expression
932 to allow distinguishing between
933 ($a->b)() // invoke on callable property
934 $a->b() // method call *)
935 let pos_if_has_parens =
936 match syntax recv with
937 | ParenthesizedExpression _ -> Some (pPos recv env)
938 | _ -> None in
939 let recv = pExpr recv env in
940 let recv =
941 match snd recv, pos_if_has_parens with
942 | (Obj_get _ | Class_get _), Some p -> p, ParenthesizedExpr recv
943 | _ -> recv in
944 let args, varargs = split_args_varargs args in
945 Call (recv, hints, args, varargs)
946 | FunctionCallWithTypeArgumentsExpression
947 { function_call_with_type_arguments_receiver = recv
948 ; function_call_with_type_arguments_type_args = type_args
949 ; function_call_with_type_arguments_argument_list = args
950 ; _ }
952 let hints =
953 begin match (syntax type_args) with
954 | TypeArguments { type_arguments_types; _ } ->
955 couldMap ~f:pHint type_arguments_types env
956 | _ -> missing_syntax "no type arguments for annotated function call" type_args env
957 end in
958 Call (pExpr recv env, hints, couldMap ~f:pExpr args env, [])
959 | QualifiedName _ ->
960 if in_string location
961 then String (pos_qualified_name node env)
962 else Id (pos_qualified_name node env)
964 | VariableExpression { variable_expression } ->
965 Lvar (pos_name variable_expression env)
967 | PipeVariableExpression _ ->
968 Lvar (pos, "$$")
970 | InclusionExpression { inclusion_require; inclusion_filename } ->
971 Import
972 ( pImportFlavor inclusion_require env
973 , pExpr inclusion_filename env
976 | MemberSelectionExpression
977 { member_object = recv
978 ; member_operator = op
979 ; member_name = name
981 | SafeMemberSelectionExpression
982 { safe_member_object = recv
983 ; safe_member_operator = op
984 ; safe_member_name = name
986 | EmbeddedMemberSelectionExpression
987 { embedded_member_object = recv
988 ; embedded_member_operator = op
989 ; embedded_member_name = name
992 let recv = pExpr recv env in
993 let name = pExpr ~location:MemberSelect name env in
994 let op = pNullFlavor op env in
995 Obj_get (recv, name, op)
997 | PrefixUnaryExpression
998 { prefix_unary_operator = operator
999 ; prefix_unary_operand = operand
1001 | PostfixUnaryExpression
1002 { postfix_unary_operand = operand
1003 ; postfix_unary_operator = operator
1005 | DecoratedExpression
1006 { decorated_expression_expression = operand
1007 ; decorated_expression_decorator = operator
1010 let expr = pExpr operand env in
1012 * FFP does not destinguish between ++$i and $i++ on the level of token
1013 * kind annotation. Prevent duplication by switching on `postfix` for
1014 * the two operatores for which AST /does/ differentiate between
1015 * fixities.
1017 let postfix = kind node = SyntaxKind.PostfixUnaryExpression in
1018 (match token_kind operator with
1019 | Some TK.PlusPlus when postfix -> Unop (Upincr, expr)
1020 | Some TK.MinusMinus when postfix -> Unop (Updecr, expr)
1021 | Some TK.PlusPlus -> Unop (Uincr, expr)
1022 | Some TK.MinusMinus -> Unop (Udecr, expr)
1023 | Some TK.Exclamation -> Unop (Unot, expr)
1024 | Some TK.Tilde -> Unop (Utild, expr)
1025 | Some TK.Plus -> Unop (Uplus, expr)
1026 | Some TK.Minus -> Unop (Uminus, expr)
1027 | Some TK.Ampersand -> Unop (Uref, expr)
1028 | Some TK.At when env.codegen -> Unop (Usilence, expr)
1029 | Some TK.At -> snd expr
1030 | Some TK.Inout -> Callconv (Pinout, expr)
1031 | Some TK.Await -> Await expr
1032 | Some TK.Suspend -> Suspend expr
1033 | Some TK.Clone -> Clone expr
1034 | Some TK.Print ->
1035 Call ((pos, Id (pos, "echo")), [], [expr], [])
1036 | Some TK.Dollar ->
1037 (match snd expr with
1038 | String (p, s)
1039 | Int (p, s)
1040 | Float (p, s) when location <> InGlobalVar -> Lvar (p, "$" ^ s)
1041 | _ -> Dollar expr
1043 | _ -> missing_syntax "unary operator" node env
1045 | BinaryExpression
1046 { binary_left_operand; binary_operator; binary_right_operand }
1048 pBop binary_operator env
1049 (pExpr binary_left_operand env)
1050 (pExpr binary_right_operand env)
1052 | Token t ->
1053 (match location with
1054 | MemberSelect when Token.kind t = TK.Variable -> Lvar (pos_name node env)
1055 | InDoubleQuotedString -> String (pos, unesc_dbl (text node))
1056 | InBacktickedString -> String (pos, Php_escaping.unescape_backtick (text node))
1057 | MemberSelect
1058 | InGlobalVar
1059 | TopLevel -> Id (pos_name node env)
1062 | YieldExpression { yield_operand; _ } when text yield_operand = "break" ->
1063 env.saw_yield <- true;
1064 Yield_break
1065 | YieldExpression { yield_operand; _ } when is_missing yield_operand ->
1066 env.saw_yield <- true;
1067 Yield (AFvalue (pos, Null))
1068 | YieldExpression { yield_operand; _ } ->
1069 env.saw_yield <- true;
1070 Yield (pAField yield_operand env)
1071 | YieldFromExpression { yield_from_operand; _ } ->
1072 env.saw_yield <- true;
1073 Yield_from (pExpr yield_from_operand env)
1075 | DefineExpression { define_keyword; define_argument_list; _ } -> Call
1076 ( (let name = pos_name define_keyword env in fst name, Id name)
1077 , []
1078 , List.map ~f:(fun x -> pExpr x env) (as_list define_argument_list)
1079 , []
1082 | ScopeResolutionExpression
1083 { scope_resolution_qualifier; scope_resolution_name; _ } ->
1084 let qual =
1085 match pExpr scope_resolution_qualifier env with
1086 | p, Lvar v when not env.codegen -> p, Id v
1087 | qual -> qual
1089 begin match syntax scope_resolution_name with
1090 | Token { Token.kind = TK.Variable; _ } ->
1091 let name =
1092 ( pPos scope_resolution_name env
1093 , Lvar (pos_name scope_resolution_name env)
1096 Class_get (qual, name)
1097 | _ ->
1098 let name = pExpr scope_resolution_name env in
1099 begin match snd name with
1100 | String id | Id id -> Class_const (qual, id)
1101 | _ -> Class_get (qual, name)
1104 | CastExpression { cast_type; cast_operand; _ } ->
1105 Cast (pHint cast_type env, pExpr cast_operand env)
1106 | ConditionalExpression
1107 { conditional_test; conditional_consequence; conditional_alternative; _ }
1108 -> Eif
1109 ( pExpr conditional_test env
1110 , mpOptional pExpr conditional_consequence env
1111 , pExpr conditional_alternative env
1113 | SubscriptExpression { subscript_receiver; subscript_index; _ } ->
1114 Array_get
1115 ( pExpr subscript_receiver env
1116 , mpOptional pExpr subscript_index env
1118 | EmbeddedSubscriptExpression
1119 { embedded_subscript_receiver; embedded_subscript_index; _ } ->
1120 Array_get
1121 ( pExpr embedded_subscript_receiver env
1122 , mpOptional (pExpr ~location) embedded_subscript_index env
1124 | ShapeExpression { shape_expression_fields; _ } ->
1125 Shape (
1126 couldMap ~f:(mpShapeExpressionField pExpr) shape_expression_fields env
1128 | ObjectCreationExpression { object_creation_object = obj; _ } ->
1129 pExpr_ obj env
1130 | ConstructorCall
1131 { constructor_call_argument_list; constructor_call_type; _ } ->
1132 let args, varargs = split_args_varargs constructor_call_argument_list in
1134 ( (match syntax constructor_call_type with
1135 | GenericTypeSpecifier { generic_class_type; generic_argument_list } ->
1136 let name = pos_name generic_class_type env in
1137 let hints =
1138 match syntax generic_argument_list with
1139 | TypeArguments { type_arguments_types; _ }
1140 -> couldMap ~f:pHint type_arguments_types env
1141 | _ ->
1142 missing_syntax "generic type arguments" generic_argument_list env
1144 fst name, Id_type_arguments (name, hints)
1145 | SimpleTypeSpecifier _ ->
1146 let name = pos_name constructor_call_type env in fst name, Id name
1147 | _ -> pExpr constructor_call_type env
1149 , args
1150 , varargs
1152 | AnonymousClass
1153 { anonymous_class_argument_list = args
1154 ; anonymous_class_extends_list = extends
1155 ; anonymous_class_implements_list = implements
1156 ; anonymous_class_body =
1157 { syntax = ClassishBody { classish_body_elements = elts; _ }; _ }
1158 ; _ } ->
1159 let args, varargs = split_args_varargs args in
1160 let c_mode = mode_annotation env.fi_mode in
1161 let c_user_attributes = [] in
1162 let c_final = false in
1163 let c_is_xhp = false in
1164 let c_name = pos, "anonymous" in
1165 let c_tparams = [] in
1166 let c_extends = couldMap ~f:pHint extends env in
1167 let c_implements = couldMap ~f:pHint implements env in
1168 let c_body = List.concat (couldMap ~f:pClassElt elts env) in
1169 let c_namespace = Namespace_env.empty env.parser_options in
1170 let c_enum = None in
1171 let c_span = pPos node env in
1172 let c_kind = Cnormal in
1173 let c_doc_comment = None in
1174 let cls =
1175 { c_mode
1176 ; c_user_attributes
1177 ; c_final
1178 ; c_is_xhp
1179 ; c_name
1180 ; c_tparams
1181 ; c_extends
1182 ; c_implements
1183 ; c_body
1184 ; c_namespace
1185 ; c_enum
1186 ; c_span
1187 ; c_kind
1188 ; c_doc_comment
1189 } in
1190 NewAnonClass (args, varargs, cls)
1191 | GenericTypeSpecifier
1192 { generic_class_type
1193 ; generic_argument_list
1194 } ->
1195 let name = pos_name generic_class_type env in
1196 let hints =
1197 match syntax generic_argument_list with
1198 | TypeArguments { type_arguments_types; _ }
1199 -> couldMap ~f:pHint type_arguments_types env
1200 | _ ->
1201 missing_syntax "generic type arguments" generic_argument_list env
1203 Id_type_arguments (name, hints)
1204 | LiteralExpression { literal_expression = expr } ->
1205 (match syntax expr with
1206 | Token _ ->
1207 let s = text expr in
1208 (* We allow underscores while lexing the integer literals. This function gets
1209 * rid of them before the literal is created. *)
1210 let eliminate_underscores s = s
1211 |> Str.split (Str.regexp "_")
1212 |> String.concat "" in
1213 (* TODO(17796330): Get rid of linter functionality in the lowerer *)
1214 if s <> String.lowercase_ascii s then Lint.lowercase_constant pos s;
1215 (match location, token_kind expr with
1216 (* TODO(T21285960): Inside strings, int indices "should" be string indices *)
1217 | InDoubleQuotedString, _ when env.hhvm_compat_mode -> String (pos, mkStr unesc_dbl s)
1218 | InBacktickedString, _ when env.hhvm_compat_mode -> String (pos, mkStr Php_escaping.unescape_backtick s)
1219 | _, Some TK.DecimalLiteral
1220 | _, Some TK.OctalLiteral
1221 | _, Some TK.HexadecimalLiteral
1222 | _, Some TK.BinaryLiteral -> Int (pos, eliminate_underscores s)
1223 | _, Some TK.FloatingLiteral -> Float (pos, s)
1224 | _, Some TK.SingleQuotedStringLiteral -> String (pos, mkStr Php_escaping.unescape_single s)
1225 | _, Some TK.DoubleQuotedStringLiteral -> String (pos, mkStr Php_escaping.unescape_double s)
1226 | _, Some TK.HeredocStringLiteral -> String (pos, mkStr Php_escaping.unescape_heredoc s)
1227 | _, Some TK.NowdocStringLiteral -> String (pos, mkStr Php_escaping.unescape_nowdoc s)
1228 | _, Some TK.NullLiteral -> Null
1229 | _, Some TK.BooleanLiteral ->
1230 (match String.lowercase_ascii s with
1231 | "false" -> False
1232 | "true" -> True
1233 | _ -> missing_syntax ("boolean (not: " ^ s ^ ")") expr env
1235 | _, Some TK.ExecutionStringLiteral ->
1236 Execution_operator [pos, String (pos, mkStr Php_escaping.unescape_backtick s)]
1237 | _ -> missing_syntax "literal" expr env
1239 | SyntaxList (
1240 { syntax = Token { Token.kind = TK.ExecutionStringLiteralHead; _ }; _ }
1241 :: _ as ts) ->
1242 Execution_operator (pString2 InBacktickedString (prepString2 ts) env)
1243 | SyntaxList ts -> String2 (pString2 InDoubleQuotedString (prepString2 ts) env)
1244 | _ -> missing_syntax "literal expression" expr env
1247 | InstanceofExpression
1248 { instanceof_left_operand; instanceof_right_operand; _ } ->
1249 let ty =
1250 match pExpr instanceof_right_operand env with
1251 | p, Class_const ((_, pid), (_, "")) -> p, pid
1252 | ty -> ty
1254 InstanceOf (pExpr instanceof_left_operand env, ty)
1255 (* TODO: Priority fix? *)
1256 (*match pExpr instanceof_left_operand env with
1257 | p, Unop (o,e) -> Unop (0, (p, InstanceOf (e, ty)))
1258 | e -> InstanceOf (e, ty)
1260 | IsExpression
1261 { is_left_operand; is_right_operand; _ } ->
1262 Is (pExpr is_left_operand env, pHint is_right_operand env)
1263 | AnonymousFunction
1264 { anonymous_static_keyword
1265 ; anonymous_async_keyword
1266 ; anonymous_coroutine_keyword
1267 ; anonymous_parameters
1268 ; anonymous_type
1269 ; anonymous_use
1270 ; anonymous_body
1271 ; _ }
1272 | Php7AnonymousFunction
1273 { php7_anonymous_static_keyword = anonymous_static_keyword
1274 ; php7_anonymous_async_keyword = anonymous_async_keyword
1275 ; php7_anonymous_coroutine_keyword = anonymous_coroutine_keyword
1276 ; php7_anonymous_parameters = anonymous_parameters
1277 ; php7_anonymous_type = anonymous_type
1278 ; php7_anonymous_use = anonymous_use
1279 ; php7_anonymous_body = anonymous_body
1280 ; _ } ->
1281 let pArg node env =
1282 match syntax node with
1283 | PrefixUnaryExpression
1284 { prefix_unary_operator = op; prefix_unary_operand = v } ->
1285 pos_name v env, token_kind op = Some TK.Ampersand
1286 | Token _ -> pos_name node env, false
1287 | _ -> missing_syntax "use variable" node env
1289 let pUse node =
1290 match syntax node with
1291 | AnonymousFunctionUseClause { anonymous_use_variables; _ } ->
1292 couldMap ~f:pArg anonymous_use_variables
1293 | _ -> fun _env -> []
1295 let suspension_kind =
1296 mk_suspension_kind
1297 anonymous_async_keyword
1298 anonymous_coroutine_keyword in
1299 let f_body, yield =
1300 mpYielding pFunctionBody anonymous_body (non_tls env)
1302 let doc_comment = match extract_docblock node with
1303 | Some _ as doc_comment -> doc_comment
1304 | None -> top_docblock() in
1305 Efun
1306 ( { (fun_template yield node suspension_kind env) with
1307 f_ret = mpOptional pHint anonymous_type env
1308 ; f_params = couldMap ~f:pFunParam anonymous_parameters env
1309 ; f_body
1310 ; f_static = not (is_missing anonymous_static_keyword)
1311 ; f_doc_comment = doc_comment
1313 , try pUse anonymous_use env with _ -> []
1316 | AwaitableCreationExpression
1317 { awaitable_async; awaitable_coroutine; awaitable_compound_statement } ->
1318 let suspension_kind =
1319 mk_suspension_kind awaitable_async awaitable_coroutine in
1320 let blk, yld = mpYielding pFunctionBody awaitable_compound_statement env in
1321 let body =
1322 { (fun_template yld node suspension_kind env) with
1323 f_body = mk_noop (pPos awaitable_compound_statement env) blk }
1325 Call ((pPos node env, Lfun body), [], [], [])
1326 | XHPExpression
1327 { xhp_open =
1328 { syntax = XHPOpen { xhp_open_name; xhp_open_attributes; _ }; _ }
1329 ; xhp_body = body
1330 ; _ } ->
1331 env.ignore_pos <- false;
1332 let name =
1333 let pos, name = pos_name xhp_open_name env in
1334 (pos, ":" ^ name)
1336 let combine b e =
1337 syntax_of_token Token.(
1338 concatenate b e
1341 let aggregate_tokens node =
1342 let rec search = function (* scroll through non-token things *)
1343 | [] -> []
1344 | t :: xs when token_kind t = Some TK.XHPComment -> search xs
1345 | { syntax = Token b; _ } as t :: xs -> track t b None xs
1346 | x :: xs -> x :: search xs
1347 and track t b oe = function (* keep going through consecutive tokens *)
1348 | { syntax = Token e; _ } :: xs -> track t b (Some e) xs
1349 | xs -> Option.value_map oe ~default:t ~f:(combine b) :: search xs
1351 search (as_list node)
1353 let pEmbedded escaper node env =
1354 match syntax node with
1355 | Token _ ->
1356 let p = pPos node env in
1357 p, String (p, escaper (full_text node))
1358 | _ ->
1359 match pExpr node env with
1360 | _, BracedExpr e -> e
1361 | e -> e
1363 let pAttr = fun node env ->
1364 match syntax node with
1365 | XHPSimpleAttribute { xhp_simple_attribute_name; xhp_simple_attribute_expression; _ } ->
1366 let name = pos_name xhp_simple_attribute_name env in
1367 let expr =
1368 if is_braced_expression xhp_simple_attribute_expression
1369 && env.fi_mode = FileInfo.Mdecl && not env.hhvm_compat_mode
1370 then Pos.none, Null
1371 else pEmbedded unesc_xhp_attr xhp_simple_attribute_expression env
1373 Xhp_simple (name, expr)
1374 | XHPSpreadAttribute { xhp_spread_attribute_expression; _ } ->
1375 Xhp_spread ( pEmbedded unesc_xhp_attr xhp_spread_attribute_expression env )
1376 | _ -> missing_syntax "XHP attribute" node env
1378 let attrs = couldMap ~f:pAttr xhp_open_attributes env in
1379 let exprs =
1380 List.map ~f:(fun x -> pEmbedded unesc_xhp x env) (aggregate_tokens body)
1382 Xml (name, attrs, exprs)
1383 (* FIXME; should this include Missing? ; "| Missing -> Null" *)
1384 | _ -> missing_syntax "expression" node env
1386 let outer_unsafes = env.unsafes in
1387 let is_safe =
1388 match leading_token node with
1389 | None -> true
1390 | Some t ->
1391 let us =
1392 Token.filter_leading_trivia_by_kind t TriviaKind.UnsafeExpression
1394 let f acc u = ISet.add (Trivia.start_offset u) acc in
1395 let us = List.fold ~f ~init:ISet.empty us in
1396 (* Have we already dealt with 'these' UNSAFE_EXPRs? *)
1397 let us = ISet.diff us outer_unsafes in
1398 let safe = ISet.is_empty us in
1399 if not safe then env.unsafes <- ISet.union us outer_unsafes;
1400 safe
1402 let result =
1403 match syntax node with
1404 | BracedExpression { braced_expression_expression = expr; _ }
1405 | ParenthesizedExpression { parenthesized_expression_expression = expr; _ }
1406 -> (**
1407 * Peeling off braced or parenthesised expresions. When there is XHP
1408 * inside, we want the XHP node to have this own positions, rather than
1409 * those of enclosing parenthesised/braced expressions.
1411 let inner = pExpr ~location expr env in
1412 if Syntax.is_braced_expression node
1413 then
1414 (* We elide the braces in {$x}, as it makes compilation easier *)
1415 begin match inner with
1416 | _, (Lvar _ | String _ | Int _ | Float _ ) -> inner
1417 | p, _ -> p, BracedExpr inner
1419 else inner
1420 | _ ->
1422 * Since we need positions in XHP, regardless of the ignore_pos flag, we
1423 * parenthesise the call to pExpr_ so that the XHP expression case can
1424 * flip the switch. The key part is that `pPos` happens before the old
1425 * setting is restored.
1427 let local_ignore_pos = env.ignore_pos in
1428 let expr_ = pExpr_ node env in
1429 let p = pPos node env in
1430 env.ignore_pos <- local_ignore_pos;
1431 p, expr_
1433 if is_safe then result else fst result, Unsafeexpr result
1434 and pBlock : block parser = fun node env ->
1435 let rec fix_last acc = function
1436 | x :: (_ :: _ as xs) -> fix_last (x::acc) xs
1437 | [_, Block block] -> List.rev acc @ block
1438 | stmt -> List.rev acc @ stmt
1440 let stmt = pStmtUnsafe node env in
1441 fix_last [] stmt
1442 and pFunctionBody : block parser = fun node env ->
1443 match syntax node with
1444 | Missing -> []
1445 | CompoundStatement _ ->
1446 let block = pBlock node env in
1447 if not env.top_level_statements
1448 && ( env.fi_mode = FileInfo.Mdecl && not env.hhvm_compat_mode
1449 || env.quick_mode)
1450 then [ Pos.none, Noop ]
1451 else block
1452 | _ ->
1453 let p, r = pExpr node env in
1454 [p, Return (Some (p, r))]
1455 and pStmtUnsafe : stmt list parser = fun node env ->
1456 let stmt = pStmt node env in
1457 match leading_token node with
1458 | Some t when Token.has_trivia_kind t TriviaKind.Unsafe -> [Pos.none, Unsafe; stmt]
1459 | _ -> [stmt]
1460 and pStmt : stmt parser = fun node env ->
1461 extract_and_push_docblock node;
1462 let pos = pPos node env in
1463 let result = match syntax node with
1464 | SwitchStatement { switch_expression; switch_sections; _ } ->
1465 let pSwitchLabel : (block -> case) parser = fun node env cont ->
1466 match syntax node with
1467 | CaseLabel { case_expression; _ } ->
1468 Case (pExpr case_expression env, cont)
1469 | DefaultLabel _ -> Default cont
1470 | _ -> missing_syntax "pSwitchLabel" node env
1472 let pSwitchSection : case list parser = fun node env ->
1473 match syntax node with
1474 | SwitchSection
1475 { switch_section_labels
1476 ; switch_section_statements
1477 ; switch_section_fallthrough
1478 } ->
1479 let rec null_out cont = function
1480 | [x] -> [x cont]
1481 | (x::xs) -> x [] :: null_out cont xs
1482 | _ -> raise (Failure "Malformed block result")
1484 let blk = List.concat @@
1485 couldMap ~f:pStmtUnsafe switch_section_statements env in
1486 let blk =
1487 if is_missing switch_section_fallthrough
1488 then blk
1489 else blk @ [Pos.none, Fallthrough]
1491 null_out blk (couldMap ~f:pSwitchLabel switch_section_labels env)
1492 | _ -> missing_syntax "switch section" node env
1494 pos,
1495 Switch
1496 ( pExpr switch_expression env
1497 , List.concat @@ couldMap ~f:pSwitchSection switch_sections env
1499 | IfStatement
1500 { if_condition; if_statement; if_elseif_clauses; if_else_clause; _ } ->
1501 (* Because consistency is for the weak-willed, Parser_hack does *not*
1502 * produce `Noop`s for compound statements **in if statements**
1504 let de_noop = function
1505 | [p, Block [_, Noop]] -> [p, Block []]
1506 | stmts -> stmts
1508 let if_condition = pExpr if_condition env in
1509 let if_statement = de_noop (pStmtUnsafe if_statement env) in
1510 let if_elseif_statement =
1511 let pElseIf : (block -> block) parser = fun node env ->
1512 match syntax node with
1513 | ElseifClause { elseif_condition; elseif_statement; _ } ->
1514 fun next_clause ->
1515 let elseif_condition = pExpr elseif_condition env in
1516 let elseif_statement = de_noop (pStmtUnsafe elseif_statement env) in
1517 [ pos, If (elseif_condition, elseif_statement, next_clause) ]
1518 | _ -> missing_syntax "elseif clause" node env
1520 List.fold_right ~f:(@@)
1521 (couldMap ~f:pElseIf if_elseif_clauses env)
1522 ~init:( match syntax if_else_clause with
1523 | ElseClause { else_statement; _ } ->
1524 de_noop (pStmtUnsafe else_statement env)
1525 | Missing -> [Pos.none, Noop]
1526 | _ -> missing_syntax "else clause" if_else_clause env
1529 pos, If (if_condition, if_statement, if_elseif_statement)
1530 | ExpressionStatement { expression_statement_expression; _ } ->
1531 if is_missing expression_statement_expression
1532 then pos, Noop
1533 else pos, Expr (pExpr expression_statement_expression env)
1534 | CompoundStatement { compound_statements; compound_right_brace; _ } ->
1535 let tail =
1536 match leading_token compound_right_brace with
1537 | Some t when Token.has_trivia_kind t TriviaKind.Unsafe -> [pos, Unsafe]
1538 | _ -> []
1540 handle_loop_body pos compound_statements tail env
1541 | AlternateLoopStatement { alternate_loop_statements; _ } ->
1542 handle_loop_body pos alternate_loop_statements [] env
1543 | ThrowStatement { throw_expression; _ } ->
1544 pos, Throw (pExpr throw_expression env)
1545 | DoStatement { do_body; do_condition; _ } ->
1546 pos, Do ([pos, Block (pBlock do_body env)], pExpr do_condition env)
1547 | WhileStatement { while_condition; while_body; _ } ->
1548 pos, While (pExpr while_condition env, pStmtUnsafe while_body env)
1549 | DeclareDirectiveStatement { declare_directive_expression; _ } ->
1550 pos, Declare (false, pExpr declare_directive_expression env, [])
1551 | DeclareBlockStatement { declare_block_expression; declare_block_body; _ } ->
1552 pos, Declare (true, pExpr declare_block_expression env,
1553 pStmtUnsafe declare_block_body env)
1554 | UsingStatementBlockScoped
1555 { using_block_await_keyword
1556 ; using_block_expressions
1557 ; using_block_body
1558 ; _ } ->
1559 pos, Using {
1560 us_is_block_scoped = true;
1561 us_has_await = not (is_missing using_block_await_keyword);
1562 us_expr = pExprL using_block_expressions env;
1563 us_block = [pos, Block (pBlock using_block_body env)];
1565 | UsingStatementFunctionScoped
1566 { using_function_await_keyword
1567 ; using_function_expression
1568 ; _ } ->
1569 (* All regular function scoped using statements should
1570 * be rewritten by this point
1571 * If this gets run, it means that this using statement is the only one
1572 * in the block, hence it is not in a compound statement *)
1573 pos, Using {
1574 us_is_block_scoped = false;
1575 us_has_await = not (is_missing using_function_await_keyword);
1576 us_expr = pExpr using_function_expression env;
1577 us_block = [Pos.none, Block [Pos.none, Noop]];
1579 | ForStatement
1580 { for_initializer; for_control; for_end_of_loop; for_body; _ } ->
1581 let ini = pExprL for_initializer env in
1582 let ctr = pExprL for_control env in
1583 let eol = pExprL for_end_of_loop env in
1584 let blk = pStmtUnsafe for_body env in
1585 pos, For (ini, ctr, eol, blk)
1586 | ForeachStatement
1587 { foreach_collection
1588 ; foreach_await_keyword
1589 ; foreach_key
1590 ; foreach_value
1591 ; foreach_body
1592 ; _ } ->
1593 let col = pExpr foreach_collection env in
1594 let akw =
1595 if is_specific_token TK.Await foreach_await_keyword
1596 then Some (pPos foreach_await_keyword env)
1597 else None
1599 let akv =
1600 let value = pExpr foreach_value env in
1601 Option.value_map (mpOptional pExpr foreach_key env)
1602 ~default:(As_v value)
1603 ~f:(fun key -> As_kv (key, value))
1605 let blk =
1606 match pStmtUnsafe foreach_body env with
1607 | [p, Block [_, Noop]] -> [p, Block []]
1608 | blk -> blk
1610 pos, Foreach (col, akw, akv, blk)
1611 | TryStatement
1612 { try_compound_statement; try_catch_clauses; try_finally_clause; _ } ->
1613 pos, Try
1614 ( [ pPos try_compound_statement env, Block (pBlock try_compound_statement env) ]
1615 , couldMap try_catch_clauses env ~f:begin fun node env ->
1616 match syntax node with
1617 | CatchClause { catch_type; catch_variable; catch_body; _ } ->
1618 ( pos_name catch_type env
1619 , pos_name catch_variable env
1620 , [ pPos catch_body env, Block (mpStripNoop pBlock catch_body env) ]
1622 | _ -> missing_syntax "catch clause" node env
1624 , match syntax try_finally_clause with
1625 | FinallyClause { finally_body; _ } ->
1626 [ pPos finally_body env, Block (pBlock finally_body env) ]
1627 | _ -> []
1629 | FunctionStaticStatement { static_declarations; _ } ->
1630 let pStaticDeclarator node env =
1631 match syntax node with
1632 | StaticDeclarator { static_name; static_initializer } ->
1633 let lhs =
1634 match pExpr static_name env with
1635 | p, Id (p', s) -> p, Lvar (p', s)
1636 | x -> x
1638 (match syntax static_initializer with
1639 | SimpleInitializer { simple_initializer_value; _ } ->
1640 ( pPos static_initializer env
1641 , Binop (Eq None, lhs, pExpr simple_initializer_value env)
1643 | _ -> lhs
1645 | _ -> missing_syntax "static declarator" node env
1647 pos, Static_var (couldMap ~f:pStaticDeclarator static_declarations env)
1648 | ReturnStatement { return_expression; return_keyword; _} ->
1649 let expr = mpOptional pExpr return_expression env in
1650 pos, Return (expr)
1651 | Syntax.GotoLabel { goto_label_name; _ } ->
1652 let pos_label = pPos goto_label_name env in
1653 let label_name = text goto_label_name in
1654 pos, Ast.GotoLabel (pos_label, label_name)
1655 | GotoStatement { goto_statement_label_name; _ } ->
1656 pos, Goto (pos_name goto_statement_label_name env)
1657 | EchoStatement { echo_keyword = kw; echo_expressions = exprs; _ }
1658 | UnsetStatement { unset_keyword = kw; unset_variables = exprs; _ }
1659 -> pos, Expr
1660 ( pPos node env
1661 , Call
1662 ( (match syntax kw with
1663 | QualifiedName _
1664 | SimpleTypeSpecifier _
1665 | Token _
1666 -> let name = pos_name kw env in fst name, Id name
1667 | _ -> missing_syntax "id" kw env
1669 , []
1670 , couldMap ~f:pExpr exprs env
1671 , []
1673 | BreakStatement { break_level=level; _ } ->
1674 pos, Break (pBreak_or_continue_level env level)
1675 | ContinueStatement { continue_level=level; _ } ->
1676 pos, Continue (pBreak_or_continue_level env level)
1677 | GlobalStatement { global_variables; _ } ->
1678 pos, Global_var (couldMap ~f:(pExpr ~location:InGlobalVar) global_variables env)
1679 | MarkupSection _ -> pMarkup node env
1680 | _ when env.max_depth > 0 ->
1681 (* OCaml optimisers; Forgive them, for they know not what they do!
1683 * The max_depth is only there to stop the *optimised* version from an
1684 * unbounded recursion. Sad times.
1686 let outer_max_depth = env.max_depth in
1687 let () = env.max_depth <- outer_max_depth - 1 in
1688 let result =
1689 match pDef node env with
1690 | [def] -> pos, Def_inline def
1691 | _ -> failwith "This should be impossible; inline definition was list."
1693 let () = env.max_depth <- outer_max_depth in
1694 result
1695 | _ -> missing_syntax "statement" node env in
1696 pop_docblock ();
1697 result
1699 and pMarkup node env =
1700 match syntax node with
1701 | MarkupSection { markup_text; markup_expression; _ } ->
1702 let expr =
1703 match syntax markup_expression with
1704 | Missing -> None
1705 | ExpressionStatement {
1706 expression_statement_expression = e
1707 ; _} -> Some (pExpr e env)
1708 | _ -> failwith "expression expected"
1710 pPos node env, Markup ((pPos node env, text markup_text), expr)
1711 | _ -> failwith "invalid node"
1713 and pBreak_or_continue_level env level =
1714 mpOptional pExpr level env
1716 and pTConstraintTy : hint parser = fun node ->
1717 match syntax node with
1718 | TypeConstraint { constraint_type; _ } -> pHint constraint_type
1719 | _ -> missing_syntax "type constraint" node
1721 and pTConstraint : (constraint_kind * hint) parser = fun node env ->
1722 match syntax node with
1723 | TypeConstraint { constraint_keyword; constraint_type } ->
1724 ( (match token_kind constraint_keyword with
1725 | Some TK.As -> Constraint_as
1726 | Some TK.Super -> Constraint_super
1727 | Some TK.Equal -> Constraint_eq
1728 | _ -> missing_syntax "constraint operator" constraint_keyword env
1730 , pHint constraint_type env
1732 | _ -> missing_syntax "type constraint" node env
1734 and pTParaml : tparam list parser = fun node env ->
1735 let pTParam : tparam parser = fun node env ->
1736 match syntax node with
1737 | TypeParameter { type_variance; type_name; type_constraints } ->
1738 ( (match token_kind type_variance with
1739 | Some TK.Plus -> Covariant
1740 | Some TK.Minus -> Contravariant
1741 | _ -> Invariant
1743 , pos_name type_name env
1744 , couldMap ~f:pTConstraint type_constraints env
1746 | _ -> missing_syntax "type parameter" node env
1748 match syntax node with
1749 | Missing -> []
1750 | TypeParameters { type_parameters_parameters; _ } ->
1751 couldMap ~f:pTParam type_parameters_parameters env
1752 | _ -> missing_syntax "type parameter list" node env
1754 and pFunHdr : fun_hdr parser = fun node env ->
1755 match syntax node with
1756 | FunctionDeclarationHeader
1757 { function_modifiers
1758 ; function_ampersand
1759 ; function_name
1760 ; function_where_clause
1761 ; function_type_parameter_list
1762 ; function_parameter_list
1763 ; function_type
1764 ; _ } ->
1765 let modifiers = pModifiers function_modifiers env in
1766 let fh_parameters = couldMap ~f:pFunParam function_parameter_list env in
1767 let fh_return_type = mpOptional pHint function_type env in
1768 let fh_suspension_kind =
1769 mk_suspension_kind_ modifiers.has_async modifiers.has_coroutine in
1770 let fh_name = pos_name function_name env in
1771 let fh_constrs =
1772 match syntax function_where_clause with
1773 | Missing -> []
1774 | WhereClause { where_clause_constraints; _ } ->
1775 let rec f node =
1776 match syntax node with
1777 | ListItem { list_item; _ } -> f list_item
1778 | WhereConstraint
1779 { where_constraint_left_type
1780 ; where_constraint_operator
1781 ; where_constraint_right_type
1782 } ->
1783 let l = pHint where_constraint_left_type env in
1784 let o =
1785 match syntax where_constraint_operator with
1786 | Token { Token.kind = TK.Equal; _ } -> Constraint_eq
1787 | Token { Token.kind = TK.As; _ } -> Constraint_as
1788 | Token { Token.kind = TK.Super; _ } -> Constraint_super
1789 | _ -> missing_syntax "constraint operator" where_constraint_operator env
1791 let r = pHint where_constraint_right_type env in
1792 (l,o,r)
1793 | _ -> missing_syntax "where constraint" node env
1795 List.map ~f (syntax_node_to_list where_clause_constraints)
1796 | _ -> missing_syntax "function header constraints" node env
1798 let fh_type_parameters = pTParaml function_type_parameter_list env in
1799 let fh_param_modifiers =
1800 List.filter ~f:(fun p -> Option.is_some p.param_modifier) fh_parameters
1802 let fh_ret_by_ref = is_ret_by_ref function_ampersand in
1803 { fh_suspension_kind
1804 ; fh_name
1805 ; fh_constrs
1806 ; fh_type_parameters
1807 ; fh_parameters
1808 ; fh_return_type
1809 ; fh_param_modifiers
1810 ; fh_ret_by_ref
1812 | LambdaSignature { lambda_parameters; lambda_type; _ } ->
1813 { empty_fun_hdr with
1814 fh_parameters = couldMap ~f:pFunParam lambda_parameters env
1815 ; fh_return_type = mpOptional pHint lambda_type env
1817 | Token _ -> empty_fun_hdr
1818 | _ -> missing_syntax "function header" node env
1820 and docblock_stack = Stack.create ()
1822 and extract_docblock = fun node ->
1823 let source_text = leading_text node in
1824 let parse (str : string) : string option =
1825 let length = String.length str in
1826 let mk (start : int) (end_ : int) : string =
1827 String.sub source_text start (end_ - start + 1)
1829 let rec go start state idx : string option =
1830 if idx = length (* finished? *)
1831 then None
1832 else begin
1833 let next = idx + 1 in
1834 match state, str.[idx] with
1835 | `LineCmt, '\n' -> go next `Free next
1836 | `EndEmbedded, '/' -> go next `Free next
1837 | `EndDoc, '/' -> begin match go next `Free next with
1838 | Some doc -> Some doc
1839 | None -> Some (mk start idx)
1841 (* PHP has line comments delimited by a # *)
1842 | `Free, '#' -> go next `LineCmt next
1843 (* All other comment delimiters start with a / *)
1844 | `Free, '/' -> go idx `SawSlash next
1845 (* After a / in trivia, we must see either another / or a * *)
1846 | `SawSlash, '/' -> go next `LineCmt next
1847 | `SawSlash, '*' -> go start `MaybeDoc next
1848 | `MaybeDoc, '*' -> go start `MaybeDoc2 next
1849 | `MaybeDoc, _ -> go start `EmbeddedCmt next
1850 | `MaybeDoc2, '/' -> go next `Free next
1851 | `MaybeDoc2, _ -> go start `DocComment next
1852 | `DocComment, '*' -> go start `EndDoc next
1853 | `DocComment, _ -> go start `DocComment next
1854 | `EndDoc, _ -> go start `DocComment next
1855 (* A * without a / does not end an embedded comment *)
1856 | `EmbeddedCmt, '*' -> go start `EndEmbedded next
1857 | `EndEmbedded, '*' -> go start `EndEmbedded next
1858 | `EndEmbedded, _ -> go start `EmbeddedCmt next
1859 (* Whitespace skips everywhere else *)
1860 | _, (' ' | '\t' | '\n') -> go start state next
1861 (* When scanning comments, anything else is accepted *)
1862 | `LineCmt, _ -> go start state next
1863 | `EmbeddedCmt, _ -> go start state next
1864 (* Anything else; bail *)
1865 | _ -> None
1868 go 0 `Free 0
1869 in (* Now that we have a parser *)
1870 parse (leading_text node)
1872 and extract_and_push_docblock node =
1873 let docblock = extract_docblock node in
1874 Stack.push docblock docblock_stack
1876 and handle_loop_body pos stmts tail env =
1877 let rec conv acc stmts =
1878 match stmts with
1879 | [] -> List.concat @@ List.rev acc
1880 | { syntax = UsingStatementFunctionScoped
1881 { using_function_await_keyword = await_kw
1882 ; using_function_expression = expression
1883 ; _ }
1884 ; _ } :: rest ->
1885 let body = conv [] rest in
1886 let using = Using {
1887 us_is_block_scoped = false;
1888 us_has_await = not (is_missing await_kw);
1889 us_expr = pExprL expression env;
1890 us_block = [pos, Block body]; } in
1891 List.concat @@ List.rev ([pos, using] :: acc)
1892 | h :: rest ->
1893 let h = pStmtUnsafe h env in
1894 conv (h :: acc) rest
1896 let blk = conv [] (as_list stmts) in
1897 begin match List.filter ~f:(fun (_, x) -> x <> Noop) blk @ tail with
1898 | [] -> pos, Block [Pos.none, Noop]
1899 | blk -> pos, Block blk
1902 and pop_docblock () =
1904 let _ = Stack.pop docblock_stack in ()
1905 with
1906 | Stack.Empty -> ()
1908 and top_docblock () =
1910 Stack.top docblock_stack
1911 with
1912 | Stack.Empty -> None
1914 and pClassElt : class_elt list parser = fun node env ->
1915 let doc_comment_opt = extract_docblock node in
1916 match syntax node with
1917 | ConstDeclaration
1918 { const_abstract; const_type_specifier; const_declarators; _ } ->
1919 let ty = mpOptional pHint const_type_specifier env in
1920 let res =
1921 couldMap const_declarators env ~f:begin function
1922 | { syntax = ConstantDeclarator
1923 { constant_declarator_name; constant_declarator_initializer }
1924 ; _ } -> fun env ->
1925 ( pos_name constant_declarator_name env
1926 (* TODO: Parse error when const is abstract and has inits *)
1927 , if is_missing const_abstract
1928 then mpOptional pSimpleInitializer constant_declarator_initializer env
1929 else None
1931 | node -> missing_syntax "constant declarator" node env
1934 let rec aux absts concrs = function
1935 | (id, None ) :: xs -> aux (AbsConst (ty, id) :: absts) concrs xs
1936 | (id, Some x) :: xs -> aux absts ((id, x) :: concrs) xs
1937 | [] when concrs = [] -> List.rev absts
1938 | [] -> Const (ty, List.rev concrs) :: List.rev absts
1940 aux [] [] res
1941 | TypeConstDeclaration
1942 { type_const_abstract
1943 ; type_const_name
1944 ; type_const_type_parameters
1945 ; type_const_type_constraint
1946 ; type_const_type_specifier
1947 ; _ } ->
1948 [ TypeConst
1949 { tconst_abstract = not (is_missing type_const_abstract)
1950 ; tconst_name = pos_name type_const_name env
1951 ; tconst_tparams = pTParaml type_const_type_parameters env
1952 ; tconst_constraint = mpOptional pTConstraintTy type_const_type_constraint env
1953 ; tconst_type = mpOptional pHint type_const_type_specifier env
1954 ; tconst_span = pPos node env
1957 | PropertyDeclaration
1958 { property_modifiers; property_type; property_declarators; _ } ->
1959 (* TODO: doc comments do not have to be at the beginning, they can go in
1960 * the middle of the declaration, to be associated with individual
1961 * properties, right now we don't handle this *)
1962 let doc_comment_opt = extract_docblock node in
1963 [ ClassVars
1964 { cv_kinds = pKinds property_modifiers env
1965 ; cv_hint = mpOptional pHint property_type env
1966 ; cv_is_promoted_variadic = false
1967 ; cv_names = couldMap property_declarators env ~f:begin fun node env ->
1968 match syntax node with
1969 | PropertyDeclarator { property_name; property_initializer } ->
1970 ( let _, n as name = pos_name property_name env in
1971 ( pPos node env
1972 , ( if n.[0] = '$'
1973 then drop_pstr 1 name
1974 else name
1976 , mpOptional pSimpleInitializer property_initializer env
1979 | _ -> missing_syntax "property declarator" node env
1981 ; cv_doc_comment = if env.quick_mode then None else doc_comment_opt
1982 ; cv_user_attributes = [] (* TODO figure out how to inject attributes *)
1985 | MethodishDeclaration
1986 { methodish_attribute
1987 ; methodish_function_decl_header = {
1988 syntax = FunctionDeclarationHeader h; _
1989 } as header
1990 ; methodish_function_body
1991 ; _ } ->
1992 let classvar_init : fun_param -> stmt * class_elt = fun param ->
1993 let p, _ as cvname = drop_pstr 1 param.param_id in (* Drop the '$' *)
1994 let span =
1995 match param.param_expr with
1996 | Some (pos_end, _) -> Pos.btw p pos_end
1997 | None -> p
1999 ( (p, Expr (p, Binop (Eq None,
2000 (p, Obj_get((p, Lvar (p, "$this")), (p, Id cvname), OG_nullthrows)),
2001 (p, Lvar param.param_id))
2003 , ClassVars
2004 { cv_kinds = Option.to_list param.param_modifier
2005 ; cv_hint = param.param_hint
2006 ; cv_is_promoted_variadic = param.param_is_variadic
2007 ; cv_names = [span, cvname, None]
2008 ; cv_doc_comment = None
2009 ; cv_user_attributes = param.param_user_attributes
2013 let hdr = pFunHdr header env in
2014 let member_init, member_def =
2015 List.unzip @@
2016 List.filter_map hdr.fh_parameters ~f:(fun p ->
2017 Option.map ~f: (fun _ -> classvar_init p) p.param_modifier
2020 let pBody = fun node env ->
2021 let body = pFunctionBody node env in
2022 let member_init =
2023 if env.hhvm_compat_mode || env.codegen
2024 then List.rev member_init
2025 else member_init
2027 member_init @ body
2029 let body, body_has_yield = mpYielding pBody methodish_function_body env in
2030 let kind = pKinds h.function_modifiers env in
2031 member_def @ [Method
2032 { m_kind = kind
2033 ; m_tparams = hdr.fh_type_parameters
2034 ; m_constrs = hdr.fh_constrs
2035 ; m_name = hdr.fh_name
2036 ; m_params = hdr.fh_parameters
2037 ; m_body = body
2038 ; m_user_attributes = List.concat @@
2039 couldMap ~f:pUserAttribute methodish_attribute env
2040 ; m_ret = hdr.fh_return_type
2041 ; m_ret_by_ref = hdr.fh_ret_by_ref
2042 ; m_span = pFunction node env
2043 ; m_fun_kind = mk_fun_kind hdr.fh_suspension_kind body_has_yield
2044 ; m_doc_comment = doc_comment_opt
2046 | TraitUseConflictResolution
2047 { trait_use_conflict_resolution_names
2048 ; trait_use_conflict_resolution_clauses
2050 } ->
2051 let pTraitUseConflictResolutionItem node env =
2052 match syntax node with
2053 | TraitUsePrecedenceItem
2054 { trait_use_precedence_item_name = name
2055 ; trait_use_precedence_item_removed_names = removed_names
2057 } ->
2058 let qualifier, name =
2059 match syntax name with
2060 | ScopeResolutionExpression
2061 { scope_resolution_qualifier; scope_resolution_name; _ } ->
2062 pos_name scope_resolution_qualifier env,
2063 pos_name scope_resolution_name env
2064 | _ -> missing_syntax "trait use precedence item" node env
2066 let removed_names =
2067 couldMap ~f:(fun n _e -> pos_name n env) removed_names env
2069 ClassUsePrecedence (qualifier, name, removed_names)
2070 | TraitUseAliasItem
2071 { trait_use_alias_item_aliasing_name = aliasing_name
2072 ; trait_use_alias_item_modifiers = modifiers
2073 ; trait_use_alias_item_aliased_name = aliased_name
2075 } ->
2076 let qualifier, name =
2077 match syntax aliasing_name with
2078 | ScopeResolutionExpression
2079 { scope_resolution_qualifier; scope_resolution_name; _ } ->
2080 Some (pos_name scope_resolution_qualifier env),
2081 pos_name scope_resolution_name env
2082 | _ -> None, pos_name aliasing_name env
2084 let modifiers = pKinds modifiers env in
2085 let is_visibility = function
2086 | Public | Private | Protected -> true
2087 | _ -> false in
2088 let modifiers =
2089 if List.is_empty modifiers || List.exists modifiers ~f:is_visibility
2090 then modifiers
2091 else Public :: modifiers in
2092 let aliased_name =
2093 Option.some_if (not (is_missing aliased_name)) (pos_name aliased_name env)
2095 ClassUseAlias (qualifier, name, aliased_name, modifiers)
2096 | _ -> missing_syntax "trait use conflict resolution item" node env
2098 (couldMap ~f:(fun n e ->
2099 ClassUse (pHint n e)) trait_use_conflict_resolution_names env)
2100 @ (couldMap ~f:pTraitUseConflictResolutionItem
2101 trait_use_conflict_resolution_clauses env)
2102 | TraitUse { trait_use_names; _ } ->
2103 couldMap ~f:(fun n e -> ClassUse (pHint n e)) trait_use_names env
2104 | RequireClause { require_kind; require_name; _ } ->
2105 [ ClassTraitRequire
2106 ( (match token_kind require_kind with
2107 | Some TK.Implements -> MustImplement
2108 | Some TK.Extends -> MustExtend
2109 | _ -> missing_syntax "trait require kind" require_kind env
2111 , pHint require_name env
2114 | XHPClassAttributeDeclaration { xhp_attribute_attributes; _ } ->
2115 let pXHPAttr node env =
2116 match syntax node with
2117 | XHPClassAttribute
2118 { xhp_attribute_decl_type = ty
2119 ; xhp_attribute_decl_name = name
2120 ; xhp_attribute_decl_initializer = init
2121 ; xhp_attribute_decl_required = req
2122 } ->
2123 let (p, name) = pos_name name env in
2124 XhpAttr
2125 ( mpOptional pHint ty env
2126 , (Pos.none, (p, ":" ^ name), mpOptional pSimpleInitializer init env)
2127 , not (is_missing req)
2128 , match syntax ty with
2129 | XHPEnumType { xhp_enum_optional; xhp_enum_values; _ } ->
2130 let p = pPos ty env in
2131 let opt = not (is_missing xhp_enum_optional) in
2132 let vals = couldMap ~f:pExpr xhp_enum_values env in
2133 Some (p, opt, vals)
2134 | _ -> None
2136 | XHPSimpleClassAttribute { xhp_simple_class_attribute_type = attr } ->
2137 XhpAttrUse (pPos attr env, Happly (pos_name attr env, []))
2138 | Token _ ->
2139 XhpAttrUse (pPos node env, Happly (pos_name node env, []))
2140 | _ -> missing_syntax "XHP attribute" node env
2142 couldMap ~f:pXHPAttr xhp_attribute_attributes env
2143 | XHPChildrenDeclaration { xhp_children_expression; _; } ->
2144 [ XhpChild (pXhpChild xhp_children_expression env) ]
2145 | XHPCategoryDeclaration { xhp_category_categories = cats; _ } ->
2146 let pNameSansPercent node _env = drop_pstr 1 (pos_name node env) in
2147 [ XhpCategory (couldMap ~f:pNameSansPercent cats env) ]
2148 | _ -> missing_syntax "expression" node env
2150 and pXhpChild : xhp_child parser = fun node env ->
2151 match syntax node with
2152 | Token t -> ChildName (pos_name node env)
2153 | PostfixUnaryExpression { postfix_unary_operand; postfix_unary_operator;} ->
2154 let operand = pXhpChild postfix_unary_operand env in
2155 let operator =
2156 begin
2157 match token_kind postfix_unary_operator with
2158 | Some TK.Question -> ChildQuestion
2159 | Some TK.Plus -> ChildPlus
2160 | Some TK.Star -> ChildStar
2161 | _ -> missing_syntax "xhp children operator" node env
2162 end in
2163 ChildUnary(operand, operator)
2164 | BinaryExpression
2165 { binary_left_operand; binary_right_operand; _ } ->
2166 let left = pXhpChild binary_left_operand env in
2167 let right = pXhpChild binary_right_operand env in
2168 ChildBinary(left, right)
2169 | XHPChildrenParenthesizedList {xhp_children_list_xhp_children; _} ->
2170 let children = as_list xhp_children_list_xhp_children in
2171 let children = List.map ~f:(fun x -> pXhpChild x env) children in
2172 ChildList children
2173 | _ -> missing_syntax "xhp children" node env
2176 (*****************************************************************************(
2177 * Parsing definitions (AST's `def`)
2178 )*****************************************************************************)
2179 and pNamespaceUseClause ~prefix env kind node =
2180 match syntax node with
2181 | NamespaceUseClause
2182 { namespace_use_name = name
2183 ; namespace_use_alias = alias
2184 ; namespace_use_clause_kind = clause_kind
2185 ; _ } ->
2186 let p, n as name =
2187 match prefix, pos_name name env with
2188 | None, (p, n) -> (p, n)
2189 | Some prefix, (p, n) -> p, (snd @@ pos_name prefix env) ^ n
2191 let x = Str.search_forward (Str.regexp "[^\\\\]*$") n 0 in
2192 let key = drop_pstr x name in
2193 let kind = if is_missing clause_kind then kind else clause_kind in
2194 let alias = if is_missing alias then key else pos_name alias env in
2195 begin match String.lowercase_ascii (snd alias) with
2196 | "null" | "true" | "false" -> env.saw_std_constant_redefinition <- true
2197 | _ -> ()
2198 end;
2199 let kind =
2200 match syntax kind with
2201 | Token { Token.kind = TK.Namespace; _ } -> NSNamespace
2202 | Token { Token.kind = TK.Type ; _ } -> NSClass
2203 | Token { Token.kind = TK.Function ; _ } -> NSFun
2204 | Token { Token.kind = TK.Const ; _ } -> NSConst
2205 | Missing -> NSClassAndNamespace
2206 | _ -> missing_syntax "namespace use kind" kind env
2208 ( kind
2209 , (p, if n.[0] = '\\' then n else "\\" ^ n)
2210 , alias
2212 | _ -> missing_syntax "namespace use clause" node env
2214 and pDef : def list parser = fun node env ->
2215 let doc_comment_opt = extract_docblock node in
2216 match syntax node with
2217 | FunctionDeclaration
2218 { function_attribute_spec; function_declaration_header; function_body } ->
2219 let env = non_tls env in
2220 let hdr = pFunHdr function_declaration_header env in
2221 let block, yield =
2222 if is_semicolon function_body then [], false else
2223 mpYielding pFunctionBody function_body env
2225 [ Fun
2226 { (fun_template yield node hdr.fh_suspension_kind env) with
2227 f_tparams = hdr.fh_type_parameters
2228 ; f_ret = hdr.fh_return_type
2229 ; f_constrs = hdr.fh_constrs
2230 ; f_name = hdr.fh_name
2231 ; f_params = hdr.fh_parameters
2232 ; f_ret_by_ref = hdr.fh_ret_by_ref
2233 ; f_body =
2234 begin
2235 let containsUNSAFE node =
2236 let tokens = all_tokens node in
2237 let has_unsafe t = Token.has_trivia_kind t TriviaKind.Unsafe in
2238 List.exists ~f:has_unsafe tokens
2240 match block with
2241 | [p, Noop] when containsUNSAFE function_body -> [p, Unsafe]
2242 | b -> b
2244 ; f_user_attributes =
2245 List.concat @@ couldMap ~f:pUserAttribute function_attribute_spec env
2246 ; f_doc_comment = doc_comment_opt
2248 | ClassishDeclaration
2249 { classish_attribute = attr
2250 ; classish_modifiers = mods
2251 ; classish_keyword = kw
2252 ; classish_name = name
2253 ; classish_type_parameters = tparaml
2254 ; classish_extends_list = exts
2255 ; classish_implements_list = impls
2256 ; classish_body =
2257 { syntax = ClassishBody { classish_body_elements = elts; _ }; _ }
2258 ; _ } ->
2259 let env = non_tls env in
2260 let c_mode = mode_annotation env.fi_mode in
2261 let c_user_attributes = List.concat @@ couldMap ~f:pUserAttribute attr env in
2262 let c_final = List.mem (pKinds mods env) Final in
2263 let c_is_xhp =
2264 match token_kind name with
2265 | Some (TK.XHPElementName | TK.XHPClassName) -> true
2266 | _ -> false
2268 let c_name = pos_name name env in
2269 let c_tparams = pTParaml tparaml env in
2270 let c_extends = couldMap ~f:pHint exts env in
2271 let c_implements = couldMap ~f:pHint impls env in
2272 let c_body =
2273 let rec aux acc ns =
2274 match ns with
2275 | [] -> acc
2276 | { syntax = PropertyDeclaration { property_modifiers; _ }; _ } :: _
2277 when not env.codegen && not env.hhvm_compat_mode &&
2278 List.exists ~f:Syntax.is_var (as_list property_modifiers) ->
2279 (* Break off remaining class body parse; legacy compliance *)
2281 | n :: ns ->
2282 let elt = pClassElt n env in
2283 aux (elt :: acc) ns
2285 List.concat @@ List.rev (aux [] (as_list elts))
2287 let c_namespace = Namespace_env.empty env.parser_options in
2288 let c_enum = None in
2289 let c_span = pPos node env in
2290 let c_kind =
2291 let is_abs = Str.(string_match (regexp ".*abstract.*") (text mods) 0) in
2292 match token_kind kw with
2293 | Some TK.Class when is_abs -> Cabstract
2294 | Some TK.Class -> Cnormal
2295 | Some TK.Interface -> Cinterface
2296 | Some TK.Trait -> Ctrait
2297 | Some TK.Enum -> Cenum
2298 | _ -> missing_syntax "class kind" kw env
2300 let c_doc_comment = doc_comment_opt in
2301 [ Class
2302 { c_mode
2303 ; c_user_attributes
2304 ; c_final
2305 ; c_is_xhp
2306 ; c_name
2307 ; c_tparams
2308 ; c_extends
2309 ; c_implements
2310 ; c_body
2311 ; c_namespace
2312 ; c_enum
2313 ; c_span
2314 ; c_kind
2315 ; c_doc_comment
2317 | ConstDeclaration { const_keyword = kw; _ }
2318 when env.fi_mode = FileInfo.Mdecl
2319 && "const" <>
2320 Option.value_map ~default:"" ~f:Token.text (Syntax.get_token kw)
2321 -> [] (* Legacy parity; case-sensitive global const declarations *)
2322 | ConstDeclaration
2323 { const_type_specifier = ty
2324 ; const_declarators = decls
2325 ; _ } ->
2326 let declarations = List.map ~f:syntax (as_list decls) in
2327 let f = function
2328 | ConstantDeclarator
2329 { constant_declarator_name = name
2330 ; constant_declarator_initializer = init
2332 -> Constant
2333 { cst_mode = mode_annotation env.fi_mode
2334 ; cst_kind = Cst_const
2335 ; cst_name = pos_name name env
2336 ; cst_type = mpOptional pHint ty env
2337 ; cst_value = pSimpleInitializer init env
2338 ; cst_namespace = Namespace_env.empty env.parser_options
2339 ; cst_span = pPos node env
2341 | _ -> missing_syntax "constant declaration" decls env
2343 List.map ~f declarations
2344 | AliasDeclaration
2345 { alias_attribute_spec = attr
2346 ; alias_keyword = kw
2347 ; alias_name = name
2348 ; alias_generic_parameter = tparams
2349 ; alias_constraint = constr
2350 ; alias_type = hint
2351 ; _ } ->
2352 [ Typedef
2353 { t_id = pos_name name env
2354 ; t_tparams = pTParaml tparams env
2355 ; t_constraint = Option.map ~f:snd @@
2356 mpOptional pTConstraint constr env
2357 ; t_user_attributes = List.concat @@
2358 List.map ~f:(fun x -> pUserAttribute x env) (as_list attr)
2359 ; t_namespace = Namespace_env.empty env.parser_options
2360 ; t_mode = mode_annotation env.fi_mode
2361 ; t_kind =
2362 match token_kind kw with
2363 | Some TK.Newtype -> NewType (pHint hint env)
2364 | Some TK.Type -> Alias (pHint hint env)
2365 | _ -> missing_syntax "kind" kw env
2367 | EnumDeclaration
2368 { enum_attribute_spec = attrs
2369 ; enum_name = name
2370 ; enum_base = base
2371 ; enum_type = constr
2372 ; enum_enumerators = enums
2373 ; _ } ->
2374 let pEnumerator node =
2375 match syntax node with
2376 | Enumerator { enumerator_name = name; enumerator_value = value; _ } ->
2377 fun env -> Const (None, [pos_name name env, pExpr value env])
2378 | _ -> missing_syntax "enumerator" node
2380 [ Class
2381 { c_mode = mode_annotation env.fi_mode
2382 ; c_user_attributes = List.concat @@ couldMap ~f:pUserAttribute attrs env
2383 ; c_final = false
2384 ; c_kind = Cenum
2385 ; c_is_xhp = false
2386 ; c_name = pos_name name env
2387 ; c_tparams = []
2388 ; c_extends = []
2389 ; c_implements = []
2390 ; c_body = couldMap enums env ~f:pEnumerator
2391 ; c_namespace = Namespace_env.empty env.parser_options
2392 ; c_span = pPos node env
2393 ; c_enum = Some
2394 { e_base = pHint base env
2395 ; e_constraint = mpOptional pTConstraintTy constr env
2397 ; c_doc_comment = doc_comment_opt
2399 | InclusionDirective
2400 { inclusion_expression
2401 ; inclusion_semicolon = _
2402 } when env.fi_mode <> FileInfo.Mdecl && env.fi_mode <> FileInfo.Mphp
2403 || env.hhvm_compat_mode || env.codegen ->
2404 let expr = pExpr inclusion_expression env in
2405 [ Stmt (pPos node env, Expr expr) ]
2406 | NamespaceDeclaration
2407 { namespace_name = name
2408 ; namespace_body =
2409 { syntax = NamespaceBody { namespace_declarations = decls; _ }; _ }
2410 ; _ } ->
2411 let env = non_tls env in
2412 [ Namespace
2413 ( pos_name name env
2414 , List.concat_map ~f:(fun x -> pDef x env) (as_list decls)
2416 | NamespaceDeclaration { namespace_name = name; _ } ->
2417 [ Namespace (pos_name name env, []) ]
2418 | NamespaceGroupUseDeclaration
2419 { namespace_group_use_kind = kind
2420 ; namespace_group_use_prefix = prefix
2421 ; namespace_group_use_clauses = clauses
2422 ; _ } ->
2423 let f = pNamespaceUseClause env kind ~prefix:(Some prefix) in
2424 [ NamespaceUse (List.map ~f (as_list clauses)) ]
2425 | NamespaceUseDeclaration
2426 { namespace_use_kind = kind
2427 ; namespace_use_clauses = clauses
2428 ; _ } ->
2429 let f = pNamespaceUseClause env kind ~prefix:None in
2430 [ NamespaceUse (List.map ~f (as_list clauses)) ]
2431 | _ when env.fi_mode = FileInfo.Mdecl || env.fi_mode = FileInfo.Mphp
2432 && not (env.hhvm_compat_mode || env.codegen)-> []
2433 | _ -> [ Stmt (pStmt node env) ]
2434 let pProgram : program parser = fun node env ->
2435 let rec post_process program =
2436 let span (p : 'a -> bool) =
2437 let rec go yes = function
2438 | (x::xs) when p x -> go (x::yes) xs
2439 | xs -> (List.rev yes, xs)
2440 in go []
2442 let not_namespace = function
2443 | Namespace _ -> false
2444 | _ -> true
2446 match program with
2447 | [] -> []
2448 | (Namespace (n, [])::el) ->
2449 let body, remainder = span not_namespace el in
2450 Namespace (n, body) :: post_process remainder
2451 | (Namespace (n, il)::el) ->
2452 Namespace (n, post_process il) :: post_process el
2453 | (Stmt (_, Noop) :: el) -> post_process el
2454 | ((Stmt (_, Expr (pos, (Call
2455 ( (_, (Id (_, "define")))
2456 , []
2457 , [ (_, (String name))
2458 ; value
2460 , []
2462 )))) :: el) -> Constant
2463 { cst_mode = mode_annotation env.fi_mode
2464 ; cst_kind = Cst_define
2465 ; cst_name = name
2466 ; cst_type = None
2467 ; cst_value = value
2468 ; cst_namespace = Namespace_env.empty env.parser_options
2469 ; cst_span = pos
2470 } :: post_process el
2471 | (e::el) -> e :: post_process el
2474 (* The list of top-level things in a file is somewhat special. *)
2475 let rec aux env acc = function
2476 | []
2477 (* EOF happens only as the last token in the list. *)
2478 | [{ syntax = EndOfFile _; _ }]
2479 -> List.concat (List.rev acc)
2480 (* HaltCompiler stops processing the list *)
2481 | [{ syntax = ExpressionStatement
2482 { expression_statement_expression =
2483 { syntax = HaltCompilerExpression _ ; _ } ; _ } ; _ }]
2484 -> List.concat (List.rev acc)
2485 (* There's an incompatibility between the Full-Fidelity (FF) and the AST view
2486 * of the world; `define` is an *expression* in FF, but a *definition* in AST.
2487 * Luckily, `define` only happens at the level of definitions.
2489 | { syntax = ExpressionStatement
2490 { expression_statement_expression =
2491 { syntax = DefineExpression
2492 { define_keyword; define_argument_list = args; _ }
2493 ; _ }
2494 ; _ }
2495 ; _ } as cur_node :: nodel when not env.quick_mode ->
2496 let def =
2497 match List.map ~f:(fun x -> pExpr x env) (as_list args) with
2498 | [ _, String name; e ] -> Constant
2499 { cst_mode = mode_annotation env.fi_mode
2500 ; cst_kind = Cst_define
2501 ; cst_name = name
2502 ; cst_type = None
2503 ; cst_value = e
2504 ; cst_namespace = Namespace_env.empty env.parser_options
2505 ; cst_span = pPos cur_node env
2507 | args ->
2508 let name = pos_name define_keyword env in
2509 Stmt (pPos cur_node env,
2510 Expr (fst name, Call ((fst name, Id name), [], args, [])))
2512 aux env ([def] :: acc) nodel
2513 | node :: nodel ->
2514 let def = pDef node env in
2515 aux env (def :: acc) nodel
2517 let nodes = as_list node in
2518 let nodes = aux env [] nodes in
2519 post_process nodes
2521 let pScript node env =
2522 match syntax node with
2523 | Script { script_declarations; _ } -> pProgram script_declarations env
2524 | _ -> missing_syntax "script" node env
2526 (* The full fidelity parser considers all comments "simply" trivia. Some
2527 * comments have meaning, though. This meaning can either be relevant for the
2528 * type checker (like UNSAFE, HH_FIXME, etc.), but also for other uses, like
2529 * Codex, where comments are used for documentation generation.
2531 * Inlining the scrape for comments in the lowering code would be prohibitively
2532 * complicated, but a separate pass is fine.
2535 type fixmes = Pos.t IMap.t IMap.t
2536 type scoured_comment = Pos.t * comment
2537 type scoured_comments = scoured_comment list
2538 type accumulator = scoured_comments * fixmes
2540 let scour_comments
2541 (path : Relative_path.t)
2542 (source_text : SourceText.t)
2543 (tree : node)
2544 (env : env)
2545 : accumulator =
2546 let pos_of_offset = SourceText.relative_pos path source_text in
2547 let go (node : node) (cmts, fm as acc : accumulator) (t : Trivia.t)
2548 : accumulator =
2549 match Trivia.kind t with
2550 | TriviaKind.WhiteSpace
2551 | TriviaKind.EndOfLine
2552 | TriviaKind.Unsafe
2553 | TriviaKind.UnsafeExpression
2554 | TriviaKind.FallThrough
2555 | TriviaKind.ExtraTokenError
2556 | TriviaKind.AfterHaltCompiler
2557 -> acc
2558 | TriviaKind.DelimitedComment ->
2559 let start = Trivia.start_offset t + 2 (* for the '/*' *) in
2560 let end_ = Trivia.end_offset t - 2 (* for the '*/' *) in
2561 let len = end_ - start + 1 in
2562 let p = pos_of_offset start end_ in
2563 let t = String.sub (Trivia.text t) 2 len in
2564 (p, CmtBlock t) :: cmts, fm
2565 | TriviaKind.SingleLineComment ->
2566 let text = SourceText.text (Trivia.source_text t) in
2567 let start = Trivia.start_offset t in
2568 let start = start + if text.[start] = '#' then 1 else 2 in
2569 let end_ = Trivia.end_offset t in
2570 let len = end_ - start + 1 in
2571 let p = pos_of_offset start end_ in
2572 let t = String.sub text start len in
2573 (p, CmtLine t) :: cmts, fm
2574 | TriviaKind.FixMe
2575 | TriviaKind.IgnoreError
2576 -> let open Str in
2577 let pos = pPos node env in
2578 let line = Pos.line pos in
2579 let ignores = try IMap.find line fm with Not_found -> IMap.empty in
2580 let reg = regexp "HH_\\(FIXME\\|IGNORE_ERROR\\)[ \\t\\n]*\\[?\\([0-9]+\\)\\]?" in
2581 let txt = Trivia.text t in
2582 (try ignore (search_forward reg txt 0) with
2583 | Not_found ->
2584 let msg = Printf.sprintf
2585 "Inconsistent trivia classification: Received %s, but failed to \
2586 match regexp for FIXME or IGNORE_ERROR. Either the lexer has a \
2587 bug in its trivia classification, or the lowerer is more \
2588 restrictive than the lexer. Source: %s"
2589 (TriviaKind.to_string (Trivia.kind t))
2590 (Pos.string (Pos.to_absolute pos))
2592 failwith msg;
2594 let p = pos_of_offset (Trivia.start_offset t) (Pos.start_cnum pos) in
2595 let code = int_of_string (matched_group 2 txt) in
2596 let ignores = IMap.add code p ignores in
2597 cmts, IMap.add line ignores fm
2599 let rec aux (cmts, fm as acc : accumulator) (node : node) : accumulator =
2600 match syntax node with
2601 | Token t ->
2602 let f = go node in
2603 let trivia = Token.leading t in
2604 let acc = List.fold_left ~f ~init:acc trivia in
2605 let trivia = Token.trailing t in
2606 List.fold_left ~f ~init:acc trivia
2607 | _ -> List.fold_left ~f:aux ~init:acc (children node)
2609 aux ([], IMap.empty) tree
2611 (*****************************************************************************(
2612 * Front-end matter
2613 )*****************************************************************************)
2615 type result =
2616 { fi_mode : FileInfo.mode
2617 ; is_hh_file : bool
2618 ; ast : Ast.program
2619 ; content : string
2620 ; file : Relative_path.t
2621 ; comments : (Pos.t * comment) list
2624 let make_env
2625 ?(hhvm_compat_mode = false )
2626 ?(codegen = false )
2627 ?(php5_compat_mode = false )
2628 ?(elaborate_namespaces = true )
2629 ?(include_line_comments = false )
2630 ?(keep_errors = true )
2631 ?(ignore_pos = false )
2632 ?(quick_mode = false )
2633 ?(lower_coroutines = true )
2634 ?(enable_hh_syntax = false )
2635 ?(parser_options = ParserOptions.default )
2636 ?(fi_mode = FileInfo.Mpartial )
2637 ?(is_hh_file = false )
2638 ?stats
2639 (file : Relative_path.t)
2640 : env
2641 = { hhvm_compat_mode
2642 ; is_hh_file
2643 ; codegen = codegen || hhvm_compat_mode
2644 ; php5_compat_mode
2645 ; elaborate_namespaces
2646 ; include_line_comments
2647 ; keep_errors
2648 ; quick_mode = not hhvm_compat_mode && (match fi_mode with
2649 | FileInfo.Mdecl
2650 | FileInfo.Mphp
2651 -> true
2652 | _ -> quick_mode
2654 ; lower_coroutines
2655 ; enable_hh_syntax
2656 ; parser_options =
2657 ParserOptions.with_hh_syntax_for_hhvm parser_options
2658 (codegen && (enable_hh_syntax || is_hh_file))
2659 ; file
2660 ; fi_mode
2661 ; stats
2662 ; ignore_pos
2663 ; top_level_statements = true
2664 ; saw_yield = false
2665 ; max_depth = 42
2666 ; unsafes = ISet.empty
2667 ; saw_std_constant_redefinition = false
2670 let elaborate_toplevel_and_std_constants ast env source_text =
2671 match env.elaborate_namespaces, env.saw_std_constant_redefinition with
2672 | true, true ->
2673 let elaborate_std_constants nsenv def =
2674 let visitor = object(self)
2675 inherit [_] endo as super
2676 method! on_expr env expr =
2677 match expr with
2678 | p, True | p, False | p, Null when p <> Pos.none ->
2679 let s = File_pos.offset @@ Pos.pos_start p in
2680 let e = File_pos.offset @@ Pos.pos_end p in
2681 let text = SourceText.sub source_text s (e - s) in
2682 let was_renamed, _ =
2683 NS.elaborate_id_impl
2684 ~autoimport:true
2685 nsenv
2686 NS.ElaborateConst
2687 (p, text) in
2688 if was_renamed then p, Ast.Id (p, text)
2689 else expr
2690 | _ -> super#on_expr env expr
2691 end in
2692 visitor#on_def nsenv def in
2693 let parser_options = env.parser_options in
2694 NS.elaborate_map_toplevel_defs parser_options ast elaborate_std_constants
2695 | true, false ->
2696 NS.elaborate_toplevel_defs env.parser_options ast
2697 | _ -> ast
2699 let lower env ~source_text ~script : result =
2700 let ast = runP pScript script env in
2701 let ast = elaborate_toplevel_and_std_constants ast env source_text in
2702 let comments, fixmes = scour_comments env.file source_text script env in
2703 let comments = if env.include_line_comments then comments else
2704 List.filter ~f:(fun (_,c) -> not (Prim_defs.is_line_comment c)) comments
2706 let () = if env.keep_errors then Fixmes.HH_FIXMES.add env.file fixmes in
2707 { fi_mode = env.fi_mode
2708 ; is_hh_file = env.is_hh_file
2709 ; ast
2710 ; content = SourceText.text source_text
2711 ; comments
2712 ; file = env.file
2715 let from_text (env : env) (source_text : SourceText.t) : result =
2716 let tree =
2717 let env =
2718 Full_fidelity_parser_env.make
2719 ~hhvm_compat_mode:env.hhvm_compat_mode
2720 ~php5_compat_mode:env.php5_compat_mode
2721 () in
2722 SyntaxTree.make ~env source_text in
2723 let () = if env.hhvm_compat_mode then
2724 let errors =
2725 ParserErrors.parse_errors
2726 ~enable_hh_syntax:env.enable_hh_syntax
2727 ~level:ParserErrors.HHVMCompatibility
2728 tree
2730 (* Prioritize runtime errors *)
2731 let runtime_errors =
2732 List.filter errors
2733 ~f:(fun e -> Full_fidelity_syntax_error.error_type e =
2734 Full_fidelity_syntax_error.RuntimeError) in
2735 match errors, runtime_errors with
2736 | [], [] -> ()
2737 | _, e :: _
2738 | e :: _, _ ->
2739 raise @@ Full_fidelity_syntax_error.ParserFatal e
2741 let script = SyntaxTree.root tree in
2742 let script = from_positioned_syntax script in
2743 let script =
2744 if env.lower_coroutines then
2745 Coroutine_lowerer.lower_coroutines script
2746 else
2747 script in
2748 let fi_mode = if SyntaxTree.is_php tree then FileInfo.Mphp else
2749 let mode_string = String.trim (SyntaxTree.mode tree) in
2750 let mode_word =
2751 try List.hd (Str.split (Str.regexp " +") mode_string) with
2752 | _ -> None
2754 Option.value_map mode_word ~default:FileInfo.Mpartial ~f:(function
2755 | "decl" when env.hhvm_compat_mode -> FileInfo.Mphp
2756 | "decl" -> FileInfo.Mdecl
2757 | "strict" -> FileInfo.Mstrict
2758 | ("partial" | "") -> FileInfo.Mpartial
2759 (* TODO: Come up with better mode detection *)
2760 | _ -> FileInfo.Mpartial
2763 let env = if env.fi_mode = fi_mode then env else { env with fi_mode } in
2764 let env = { env with is_hh_file = SyntaxTree.is_hack tree } in
2765 (* If we are generating code and this is an hh file or hh syntax is enabled,
2766 * then we want to inject auto import types into HH namespace during namespace
2767 * resolution.
2769 let env = { env with parser_options =
2770 ParserOptions.with_hh_syntax_for_hhvm env.parser_options
2771 (env.codegen && (ParserOptions.enable_hh_syntax_for_hhvm env.parser_options || env.is_hh_file)) } in
2772 lower
2774 ~source_text
2775 ~script
2777 let from_file (env : env) : result =
2778 let source_text = SourceText.from_file env.file in
2779 from_text env source_text
2781 (*****************************************************************************(
2782 * Backward compatibility matter (should be short-lived)
2783 )*****************************************************************************)
2785 let legacy (x : result) : Parser_hack.parser_return =
2786 { Parser_hack.file_mode = Option.some_if (x.fi_mode <> FileInfo.Mphp) x.fi_mode
2787 ; Parser_hack.is_hh_file = x.is_hh_file
2788 ; Parser_hack.comments = x.comments
2789 ; Parser_hack.ast = x.ast
2790 ; Parser_hack.content = x.content
2793 let from_text_with_legacy (env : env) (content : string)
2794 : Parser_hack.parser_return =
2795 let source_text = SourceText.make env.file content in
2796 legacy @@ from_text env source_text
2798 let from_file_with_legacy env = legacy (from_file env)