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