Adding --pocket-universes flag to hh_parse and hh_single_parse
[hiphop-php.git] / hphp / hack / src / parser / full_fidelity_ast.ml
blob2e04f12f6dd43cfb5381f23b6669d5e7049f36cd
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 *)
10 module SyntaxError = Full_fidelity_syntax_error
11 module SN = Naming_special_names
12 open Core_kernel
13 open Prim_defs
14 (* What we are lowering to *)
15 open Ast
17 (* Don't allow expressions to nest deeper than this to avoid stack overflow *)
18 let recursion_limit = 30000
20 [@@@warning "-32"] (* unused ppx_deriving show function in OCaml ast trips Werror *)
21 type lifted_await_kind = LiftedFromStatement | LiftedFromConcurrent [@@deriving show]
22 [@@@warning "+32"]
24 type lifted_awaits = {
25 mutable awaits: (id option * expr) list;
26 mutable name_counter: int;
27 lift_kind: lifted_await_kind
28 }[@@deriving show]
30 let make_tmp_var_name c =
31 SN.SpecialIdents.tmp_var_prefix ^ (string_of_int c)
33 let lift_await expr awaits ~with_temp_local =
34 if (with_temp_local)
35 then
36 let name = make_tmp_var_name awaits.name_counter in
37 awaits.name_counter <- awaits.name_counter + 1;
38 awaits.awaits <- ((Some (Pos.none, name)), expr) :: awaits.awaits;
39 Lvar (Pos.none, name)
40 else
41 (awaits.awaits <- (None, expr) :: awaits.awaits;
42 Null)
44 (* Context of the file being parsed, as (hopefully some day read-only) state. *)
45 type env =
46 { is_hh_file : bool
47 ; codegen : bool
48 ; systemlib_compat_mode : bool
49 ; php5_compat_mode : bool
50 ; has_dot_hack_extension : bool
51 ; elaborate_namespaces : bool
52 ; include_line_comments : bool
53 ; keep_errors : bool
54 ; quick_mode : bool
55 ; lower_coroutines : bool
56 ; enable_hh_syntax : bool
57 ; enable_xhp : bool
58 ; fail_open : bool
59 ; parser_options : ParserOptions.t
60 ; fi_mode : FileInfo.mode
61 ; file : Relative_path.t
62 ; stats : Stats_container.t option
63 ; hacksperimental : bool
64 ; top_level_statements : bool (* Whether we are (still) considering TLSs*)
65 ; inside_declare : bool (* Whether we're inside a declare directive. *)
66 (* Changing parts; should disappear in future. `mutable` saves allocations. *)
67 ; mutable ignore_pos : bool
68 ; mutable max_depth : int (* Filthy hack around OCaml bug *)
69 ; mutable saw_yield : bool (* Information flowing back up *)
70 ; mutable unsafes : ISet.t (* Offsets of UNSAFE_EXPR in trivia *)
71 ; mutable saw_std_constant_redefinition: bool
72 ; mutable lifted_awaits : lifted_awaits option
73 (* Whether we've seen COMPILER_HALT_OFFSET. The value of COMPILER_HALT_OFFSET
74 defaults to 0 if HALT_COMPILER isn't called.
75 None -> COMPILER_HALT_OFFSET isn't in the source file
76 Some 0 -> COMPILER_HALT_OFFSET is in the source file, but HALT_COMPILER isn't
77 Some x -> COMPILER_HALT_OFFSET is in the source file,
78 HALT_COMPILER is at x bytes offset in the file.
80 ; saw_compiler_halt_offset : (int option) ref
81 ; recursion_depth : int ref
82 ; cls_reified_generics : SSet.t ref
83 ; in_static_method : bool ref
84 ; parent_is_reified : bool ref
85 }[@@deriving show]
87 let make_env
88 ?(codegen = false )
89 ?(systemlib_compat_mode = false )
90 ?(php5_compat_mode = false )
91 ?(elaborate_namespaces = true )
92 ?(include_line_comments = false )
93 ?(keep_errors = true )
94 ?(ignore_pos = false )
95 ?(quick_mode = false )
96 ?(lower_coroutines = true )
97 ?(enable_hh_syntax = false )
98 ?enable_xhp
99 ?(fail_open = true )
100 ?(parser_options = ParserOptions.default )
101 ?(fi_mode = FileInfo.Mpartial )
102 ?(is_hh_file = false )
103 ?stats
104 ?(hacksperimental = false )
105 ?(pocket_universes = false )
106 (file : Relative_path.t)
107 : env
109 let enable_xhp = match enable_xhp with
110 | Some b -> b
111 | None -> enable_hh_syntax in
112 let has_dot_hack_extension =
113 (String_utils.string_ends_with (Relative_path.suffix file) ".hack") in
114 let parser_options = ParserOptions.with_hh_syntax_for_hhvm parser_options
115 (codegen && (enable_hh_syntax || is_hh_file)) in
116 let parser_options = ParserOptions.setup_pocket_universes parser_options
117 pocket_universes in
118 { is_hh_file
119 ; codegen = codegen || systemlib_compat_mode
120 ; systemlib_compat_mode
121 ; php5_compat_mode
122 ; has_dot_hack_extension
123 ; elaborate_namespaces
124 ; include_line_comments
125 ; keep_errors
126 ; quick_mode =
127 not codegen
128 && (match fi_mode with
129 | FileInfo.Mdecl
130 | FileInfo.Mphp -> true
131 | _ -> quick_mode
133 ; lower_coroutines
134 ; enable_hh_syntax
135 ; enable_xhp
136 ; parser_options
137 ; fi_mode
138 ; fail_open
139 ; file
140 ; stats
141 ; hacksperimental
142 ; top_level_statements = true
143 ; inside_declare = false
144 ; ignore_pos
145 ; max_depth = 42
146 ; saw_yield = false
147 ; unsafes = ISet.empty
148 ; saw_std_constant_redefinition = false
149 ; saw_compiler_halt_offset = ref None
150 ; recursion_depth = ref 0
151 ; cls_reified_generics = ref SSet.empty
152 ; in_static_method = ref false
153 ; parent_is_reified = ref false
154 ; lifted_awaits = None
157 type result =
158 { fi_mode : FileInfo.mode
159 ; is_hh_file : bool
160 ; ast : Ast.program
161 ; content : string
162 ; file : Relative_path.t
163 ; comments : (Pos.t * comment) list
164 } [@@deriving show]
166 module WithPositionedSyntax(Syntax : Positioned_syntax_sig.PositionedSyntax_S) = struct
168 (* What we're lowering from *)
169 open Syntax
171 type node = Syntax.t
173 module Token = Syntax.Token
174 module Trivia = Token.Trivia
175 module TriviaKind = Trivia.TriviaKind
177 module SyntaxKind = Full_fidelity_syntax_kind
178 module TK = Full_fidelity_token_kind
179 module SourceText = Trivia.SourceText
181 module NS = Namespaces
183 let is_hack (env : env) = env.is_hh_file || env.enable_hh_syntax
184 let is_typechecker env =
185 is_hack env && (not env.codegen)
187 let drop_pstr : int -> pstring -> pstring = fun cnt (pos, str) ->
188 let len = String.length str in
189 pos, if cnt >= len then "" else String.sub str cnt (len - cnt)
191 let non_tls env = if not env.top_level_statements then env else
192 { env with top_level_statements = false }
194 type +'a parser = node -> env -> 'a
195 type ('a, 'b) metaparser = 'a parser -> 'b parser
197 let underscore = Str.regexp "_"
198 let quoted = Str.regexp "[ \t\n\r\012]*\"\\(\\(.\\|\n\\)*\\)\""
199 let whitespace = Str.regexp "[ \t\n\r\012]+"
200 let hashbang = Str.regexp "^#!.*\n"
201 let ignore_error = Str.regexp "HH_\\(FIXME\\|IGNORE_ERROR\\)[ \\t\\n]*\\[\\([0-9]+\\)\\]"
202 let namespace_use = Str.regexp "[^\\\\]*$"
204 let mode_annotation = function
205 | FileInfo.Mphp -> FileInfo.Mdecl
206 | m -> m
208 let with_new_nonconcurrent_scope env f =
209 let saved_lifted_awaits = env.lifted_awaits in
210 env.lifted_awaits <- None;
211 let result = f () in
212 env.lifted_awaits <- saved_lifted_awaits;
213 result
215 let with_new_concurrent_scope env f =
216 let saved_lifted_awaits = env.lifted_awaits in
217 let lifted_awaits =
218 { awaits = []; name_counter = 1; lift_kind = LiftedFromConcurrent } in
219 env.lifted_awaits <- Some lifted_awaits;
220 let result = f () in
221 env.lifted_awaits <- saved_lifted_awaits;
222 (lifted_awaits, result)
224 let with_new_statement_scope env f =
225 let saved_lifted_awaits = env.lifted_awaits in
226 match (ParserOptions.enable_await_as_an_expression env.parser_options), env.lifted_awaits with
227 | false, _
228 | true, Some { lift_kind = LiftedFromConcurrent; _ } ->
229 (None, f ())
230 | true, Some { lift_kind = LiftedFromStatement; _ }
231 | true, None ->
232 let lifted_awaits =
233 { awaits = []; name_counter = 1; lift_kind = LiftedFromStatement } in
234 env.lifted_awaits <- Some lifted_awaits;
235 let result = f () in
236 env.lifted_awaits <- saved_lifted_awaits;
237 let lifted_awaits =
238 if List.is_empty lifted_awaits.awaits then None else Some lifted_awaits in
239 (lifted_awaits, result)
241 let syntax_to_list include_separators node =
242 let rec aux acc syntax_list =
243 match syntax_list with
244 | [] -> acc
245 | h :: t ->
246 begin
247 match syntax h with
248 | ListItem { list_item; list_separator } ->
249 let acc = list_item :: acc in
250 let acc =
251 if include_separators then (list_separator :: acc ) else acc in
252 aux acc t
253 | _ -> aux (h :: acc) t
254 end in
255 match syntax node with
256 | Missing -> [ ]
257 | SyntaxList s -> List.rev (aux [] s)
258 | ListItem { list_item; list_separator } ->
259 if include_separators then [ list_item; list_separator ] else [ list_item ]
260 | _ -> [ node ]
262 let syntax_to_list_no_separators = syntax_to_list false
264 let pPos : Pos.t parser = fun node env ->
265 if env.ignore_pos
266 then Pos.none
267 else Option.value ~default:Pos.none (position_exclusive env.file node)
269 let raise_parsing_error env node_or_pos msg =
270 if not env.quick_mode && env.keep_errors then
271 let p = match node_or_pos with
272 | `Pos pos -> pos
273 | `Node node -> pPos node env
275 Errors.parsing_error (p, msg)
276 else if env.codegen && not env.lower_coroutines then
277 let p = match node_or_pos with
278 | `Pos pos -> pos
279 | `Node node -> (Option.value (position env.file node) ~default:Pos.none)
281 let (s, e) = Pos.info_raw p in
282 let e = SyntaxError.make ~error_type:SyntaxError.ParseError s e msg in
283 raise @@ SyntaxError.ParserFatal (e, p)
284 else ()
286 (* HHVM starts range of function declaration from the 'function' keyword *)
287 let pFunction node env =
288 let p = pPos node env in
289 match syntax node with
290 | FunctionDeclaration { function_declaration_header = h; _ }
291 | MethodishDeclaration { methodish_function_decl_header = h; _ }
292 when env.codegen ->
293 begin match syntax h with
294 | FunctionDeclarationHeader { function_keyword = f; _ }
295 when not (is_missing f) ->
296 (* For continuation compilation, we end up with spans across files :-( *)
297 Pos.btw_nocheck (pPos f env) p
298 | _ -> p
300 | _ -> p
302 exception Lowerer_invariant_failure of string * string
303 let invariant_failure node msg env =
304 let pos = Pos.string (Pos.to_absolute (pPos node env)) in
305 raise (Lowerer_invariant_failure (pos, msg))
307 let scuba_table = Scuba.Table.of_name "hh_missing_lowerer_cases"
309 let log_missing ?(caught = false) ~(env:env) ~expecting node : unit =
310 EventLogger.log_if_initialized @@ fun () ->
311 let source = source_text node in
312 let start = start_offset node in
313 let end_ = end_offset node in
314 let pos = SourceText.relative_pos env.file source start end_ in
315 let file = Relative_path.to_absolute env.file in
316 let contents =
317 let context_size = 5000 in
318 let start = max 0 (start - context_size) in
319 let length = min (2 * context_size) (SourceText.length source - start) in
320 SourceText.sub source start length
322 let kind = SyntaxKind.to_string (Syntax.kind node) in
323 let line = Pos.line pos in
324 let column = Pos.start_cnum pos in
325 let synthetic = is_synthetic node in
326 Scuba.new_sample (Some scuba_table)
327 |> Scuba.add_normal "filename" file
328 |> Scuba.add_normal "expecting" expecting
329 |> Scuba.add_normal "contents" contents
330 |> Scuba.add_normal "found_kind" kind
331 |> Scuba.add_int "line" line
332 |> Scuba.add_int "column" column
333 |> Scuba.add_int "is_synthetic" (if synthetic then 1 else 0)
334 |> Scuba.add_int "caught" (if caught then 1 else 0)
335 |> EventLogger.log
337 exception API_Missing_syntax of string * env * node
339 (* If we fail to lower something, raise an error in the typechecker
340 complaining that the code does not parse. Don't raise a parsing error
341 if there already is one, since that one will likely be better than this one. *)
342 let lowering_error env pos text syntax_kind =
343 if not (is_typechecker env) then () else
344 if not (Errors.currently_has_errors ()) then
345 raise_parsing_error env (`Pos pos)
346 (SyntaxError.lowering_parsing_error text syntax_kind)
348 let missing_syntax : ?fallback:'a -> string -> node -> env -> 'a =
349 fun ?fallback expecting node env ->
350 let pos = pPos node env in
351 let text = (text node) in
352 lowering_error env pos text expecting;
353 match fallback with
354 | Some x when env.fail_open ->
355 let () = log_missing ~env ~expecting node in
357 | _ -> raise (API_Missing_syntax (expecting, env, node))
359 let runP : 'a parser -> node -> env -> 'a = fun pThing thing env ->
360 try pThing thing env with
361 | API_Missing_syntax (s, env, n) ->
362 let pos = Pos.string (Pos.to_absolute (pPos n env)) in
363 let msg = Printf.sprintf
364 "missing case in %s.
365 - pos: %s
366 - unexpected: '%s'
367 - kind: %s
371 (text n)
372 (SyntaxKind.to_string (kind n))
374 raise (Failure msg)
376 (* TODO: Cleanup this hopeless Noop mess *)
377 let mk_noop pos : stmt list -> stmt list = function
378 | [] -> [pos, Noop]
379 | s -> s
380 let mpStripNoop pThing node env = match pThing node env with
381 | [_, Noop] -> []
382 | stmtl -> stmtl
384 let mpOptional : ('a, 'a option) metaparser = fun p -> fun node env ->
385 match syntax node with
386 | Missing -> None
387 | _ -> Some (p node env)
388 let mpYielding : ('a, ('a * bool)) metaparser = fun p node env ->
389 let outer_saw_yield = env.saw_yield in
390 let () = env.saw_yield <- false in
391 let result = p node env in
392 let result = result, env.saw_yield in
393 let () = env.saw_yield <- outer_saw_yield in
394 result
396 type expr_location =
397 | TopLevel
398 | MemberSelect
399 | InDoubleQuotedString
400 | InBacktickedString
401 | InGlobalVar
402 | AsStatement
403 | RightOfAssignment
404 | RightOfReturn
406 let in_string l =
407 l = InDoubleQuotedString || l = InBacktickedString
409 let pos_qualified_name node env =
410 let aux p =
411 match syntax p with
412 | ListItem li -> (text li.list_item) ^ (text li.list_separator)
413 | _ -> text p in
414 let p = pPos node env in
415 let name =
416 match syntax node with
417 | QualifiedName {
418 qualified_name_parts = { syntax = SyntaxList l; _ };
419 } ->
420 String.concat ~sep:"" @@ List.map ~f:aux l
421 | _ -> missing_syntax "qualified name" node env in
422 p, name
424 let rec pos_name node env =
425 match syntax node with
426 | QualifiedName _ -> pos_qualified_name node env
427 | SimpleTypeSpecifier { simple_type_specifier = s } -> pos_name s env
428 | _ ->
429 let name = text node in
430 let local_ignore_pos = env.ignore_pos in
431 (* Special case for __LINE__; never ignore position for that special name *)
432 if name = "__LINE__" then env.ignore_pos <- false;
433 if name = "__COMPILER_HALT_OFFSET__" then env.saw_compiler_halt_offset := Some 0;
434 let p = pPos node env in
435 env.ignore_pos <- local_ignore_pos;
436 p, name
438 let couldMap : 'a . f:'a parser -> 'a list parser = fun ~f -> fun node env ->
439 let rec synmap : 'a . 'a parser -> 'a list parser = fun f node env ->
440 match syntax node with
441 | SyntaxList l -> List.concat_map l ~f:(fun n -> go ~f n env)
442 | ListItem i -> [f i.list_item env]
443 | _ -> [f node env]
444 and go : 'a . f:'a parser -> 'a list parser = fun ~f -> function
445 | node when is_missing node -> fun _env -> []
446 | node -> synmap f node
448 go ~f node env
450 let as_list : node -> node list =
451 let strip_list_item = function
452 | { syntax = ListItem { list_item = i; _ }; _ } -> i
453 | x -> x
454 in function
455 | { syntax = SyntaxList ({syntax = ListItem _; _}::_ as synl); _ } ->
456 List.map ~f:strip_list_item synl
457 | { syntax = SyntaxList synl; _ } -> synl
458 | { syntax = Missing; _ } -> []
459 | syn -> [syn]
461 let token_kind : node -> TK.t option = function
462 | { syntax = Token t; _ } -> Some (Token.kind t)
463 | _ -> None
465 let pBop : (expr -> expr -> expr_) parser = fun node env lhs rhs ->
466 match token_kind node with
467 | Some TK.Equal -> Binop (Eq None, lhs, rhs)
468 | Some TK.Bar -> Binop (Bar, lhs, rhs)
469 | Some TK.Ampersand -> Binop (Amp, lhs, rhs)
470 | Some TK.Plus -> Binop (Plus, lhs, rhs)
471 | Some TK.Minus -> Binop (Minus, lhs, rhs)
472 | Some TK.Star -> Binop (Star, lhs, rhs)
473 | Some TK.Or ->
474 if not env.codegen then raise_parsing_error env (`Node node) SyntaxError.do_not_use_or;
475 Binop (Barbar, lhs, rhs)
476 | Some TK.And ->
477 if not env.codegen then raise_parsing_error env (`Node node) SyntaxError.do_not_use_and;
478 Binop (Ampamp, lhs, rhs)
479 | Some TK.Xor ->
480 if not env.codegen then raise_parsing_error env (`Node node) SyntaxError.do_not_use_xor;
481 Binop (LogXor, lhs, rhs)
482 | Some TK.Carat -> Binop (Xor, lhs, rhs)
483 | Some TK.Slash -> Binop (Slash, lhs, rhs)
484 | Some TK.Dot -> Binop (Dot, lhs, rhs)
485 | Some TK.Percent -> Binop (Percent, lhs, rhs)
486 | Some TK.LessThan -> Binop (Lt, lhs, rhs)
487 | Some TK.GreaterThan -> Binop (Gt, lhs, rhs)
488 | Some TK.EqualEqual -> Binop (Eqeq, lhs, rhs)
489 | Some TK.LessThanEqual -> Binop (Lte, lhs, rhs)
490 | Some TK.GreaterThanEqual -> Binop (Gte, lhs, rhs)
491 | Some TK.StarStar -> Binop (Starstar, lhs, rhs)
492 | Some TK.ExclamationEqual -> Binop (Diff, lhs, rhs)
493 | Some TK.BarEqual -> Binop (Eq (Some Bar), lhs, rhs)
494 | Some TK.PlusEqual -> Binop (Eq (Some Plus), lhs, rhs)
495 | Some TK.MinusEqual -> Binop (Eq (Some Minus), lhs, rhs)
496 | Some TK.StarEqual -> Binop (Eq (Some Star), lhs, rhs)
497 | Some TK.StarStarEqual -> Binop (Eq (Some Starstar),lhs, rhs)
498 | Some TK.SlashEqual -> Binop (Eq (Some Slash), lhs, rhs)
499 | Some TK.DotEqual -> Binop (Eq (Some Dot), lhs, rhs)
500 | Some TK.PercentEqual -> Binop (Eq (Some Percent), lhs, rhs)
501 | Some TK.CaratEqual -> Binop (Eq (Some Xor), lhs, rhs)
502 | Some TK.AmpersandEqual -> Binop (Eq (Some Amp), lhs, rhs)
503 | Some TK.BarBar -> Binop (Barbar, lhs, rhs)
504 | Some TK.AmpersandAmpersand -> Binop (Ampamp, lhs, rhs)
505 | Some TK.LessThanLessThan -> Binop (Ltlt, lhs, rhs)
506 | Some TK.GreaterThanGreaterThan -> Binop (Gtgt, lhs, rhs)
507 | Some TK.EqualEqualEqual -> Binop (Eqeqeq, lhs, rhs)
508 | Some TK.LessThanLessThanEqual -> Binop (Eq (Some Ltlt), lhs, rhs)
509 | Some TK.GreaterThanGreaterThanEqual -> Binop (Eq (Some Gtgt), lhs, rhs)
510 | Some TK.LessThanGreaterThan ->
511 if is_hack env
512 then raise_parsing_error env (`Node node) SyntaxError.do_not_use_ltgt;
513 Binop (Diff, lhs, rhs)
514 | Some TK.ExclamationEqualEqual -> Binop (Diff2, lhs, rhs)
515 | Some TK.LessThanEqualGreaterThan -> Binop (Cmp, lhs, rhs)
516 | Some TK.QuestionQuestion -> Binop (QuestionQuestion, lhs, rhs)
517 | Some TK.QuestionQuestionEqual -> Binop (Eq (Some QuestionQuestion), lhs, rhs)
518 (* The ugly duckling; In the FFP, `|>` is parsed as a
519 * `BinaryOperator`, whereas the typed AST has separate constructors for
520 * Pipe and Binop. This is why we don't just project onto a
521 * `bop`, but a `expr -> expr -> expr_`.
523 | Some TK.BarGreaterThan -> Pipe (lhs, rhs)
524 | Some TK.QuestionColon -> Eif (lhs, None, rhs)
525 (* TODO: Figure out why this fails silently when used in a pBlock; probably
526 just caught somewhere *)
527 | _ -> missing_syntax "binary operator" node env
529 let pImportFlavor : import_flavor parser = fun node env ->
530 match token_kind node with
531 | Some TK.Include -> Include
532 | Some TK.Require -> Require
533 | Some TK.Include_once -> IncludeOnce
534 | Some TK.Require_once -> RequireOnce
535 | _ -> missing_syntax "import flavor" node env
537 let pNullFlavor : og_null_flavor parser = fun node env ->
538 match token_kind node with
539 | Some TK.QuestionMinusGreaterThan -> OG_nullsafe
540 | Some TK.MinusGreaterThan -> OG_nullthrows
541 | _ -> missing_syntax "null flavor" node env
543 type modifiers = {
544 has_async: bool;
545 has_coroutine: bool;
546 kinds: kind list
549 let pModifiers check_modifier node env =
550 let f (has_async, has_coroutine, kinds) node =
551 let add_kind k =
552 check_modifier node;
553 k :: kinds
555 match token_kind node with
556 | Some TK.Final -> has_async, has_coroutine, add_kind Final
557 | Some TK.Static -> has_async, has_coroutine, add_kind Static
558 | Some TK.Abstract -> has_async, has_coroutine, add_kind Abstract
559 | Some TK.Private -> has_async, has_coroutine, add_kind Private
560 | Some TK.Public -> has_async, has_coroutine, add_kind Public
561 | Some TK.Protected -> has_async, has_coroutine, add_kind Protected
562 | Some TK.Var -> has_async, has_coroutine, add_kind Public
563 | Some TK.Async -> true, has_coroutine, kinds
564 | Some TK.Coroutine -> has_async, true, kinds
565 | _ -> missing_syntax "kind" node env in
566 let (has_async, has_coroutine, kinds) =
567 List.fold_left ~init:(false, false, []) ~f (as_list node) in
568 { has_async; has_coroutine; kinds = List.rev kinds }
570 let pKinds check_modifier node env =
571 (pModifiers check_modifier node env).kinds
573 let pParamKind : param_kind parser = fun node env ->
574 match token_kind node with
575 | Some TK.Inout -> Pinout
576 | _ -> missing_syntax "param kind" node env
578 (* TODO: Clean up string escaping *)
579 let prepString2 env : node list -> node list =
580 let is_double_quote_or_backtick ch = ch = '"' || ch = '`' in
581 let is_binary_string_header s =
582 (String.length s > 1) && (s.[0] = 'b') && (s.[1] = '"') in
583 let trimLeft = Token.trim_left in
584 let trimRight = Token.trim_right in
585 function
586 | ({ syntax = Token t; _ } as node::ss)
587 when (Token.width t) > 0 &&
588 ((is_double_quote_or_backtick (Token.text t).[0])
589 || is_binary_string_header (Token.text t)) ->
590 let rec unwind = function
591 | [{ syntax = Token t; _ }]
592 when (Token.width t) > 0 &&
593 is_double_quote_or_backtick ((Token.text t).[(Token.width t) - 1]) ->
594 let s = make_token (trimRight ~n:1 t) in
595 if width s > 0 then [s] else []
596 | x :: xs -> x :: unwind xs
597 | _ -> raise_parsing_error env (`Node node) "Malformed String2 SyntaxList"; []
599 (* Trim the starting b and double quote *)
600 let left_trim = if (Token.text t).[0] = 'b' then 2 else 1 in
601 let s = make_token (trimLeft ~n:left_trim t) in
602 if width s > 0 then s :: unwind ss else unwind ss
603 | ({ syntax = Token t; _ } as node ::ss)
604 when (Token.width t) > 3 && String.sub (Token.text t) 0 3 = "<<<" ->
605 let rec unwind = function
606 | [{ syntax = Token t; _ }] when (Token.width t) > 0 ->
607 let content = Token.text t in
608 let len = (Token.width t) in
609 let n = len - (String.rindex_from_exn content (len - 2) '\n') in
610 let s = make_token (trimRight ~n t) in
611 if width s > 0 then [s] else []
612 | x :: xs -> x :: unwind xs
613 | _ -> raise_parsing_error env (`Node node) "Malformed String2 SyntaxList"; []
615 let content = Token.text t in
616 let n = (String.index_exn content '\n') + 1 in
617 let s = make_token (trimLeft ~n t) in
618 if width s > 0 then s :: unwind ss else unwind ss
619 | x -> x (* unchanged *)
621 let extract_unquoted_string ~start ~len content =
622 try (* Using String.sub; Invalid_argument when str too short *)
623 if len >= 3 && String.sub content start 3 = "<<<" (* The heredoc case *)
624 then
625 (* These types of strings begin with an opening line containing <<<
626 * followed by a string to use as a terminator (which is optionally
627 * quoted) and end with a line containing only the terminator and a
628 * semicolon followed by a blank line. We need to drop the opening line
629 * as well as the blank line and preceding terminator line.
631 let start_ = String.index_from_exn content start '\n' + 1 in
632 let end_ = String.rindex_from_exn content (start + len - 2) '\n' in
633 (* An empty heredoc, this way, will have start >= end *)
634 if start_ >= end_ then "" else String.sub content start_ (end_ - start_)
635 else
636 match String.get content start, String.get content (start + len - 1) with
637 | '"', '"' | '\'', '\'' | '`', '`' ->
638 String.sub content (start + 1) (len - 2)
639 | _ ->
640 if start = 0 && len = String.length content then
641 content
642 else
643 String.sub content start len
644 with Invalid_argument _ | Not_found_s _ | Caml.Not_found -> content
646 let mkStr env node : (string -> string) -> string -> string = fun unescaper content ->
647 let content = if String.length content > 0 && content.[0] = 'b'
648 then String.sub content 1 (String.length content - 1) else content in
649 let len = String.length content in
650 let no_quotes = extract_unquoted_string ~start:0 ~len content in
651 try unescaper no_quotes with
652 | Php_escaping.Invalid_string _ ->
653 raise_parsing_error env
654 (`Node node) (Printf.sprintf "Malformed string literal <<%s>>" no_quotes);
657 let unempty_str = function
658 | "''" | "\"\"" -> ""
659 | s -> s
660 let unesc_dbl s = unempty_str @@ Php_escaping.unescape_double s
661 let get_quoted_content s =
662 let open Str in
663 if string_match (quoted) s 0
664 then matched_group 1 s
665 else s
666 let unesc_xhp s =
667 Str.global_replace whitespace " " s
668 let unesc_xhp_attr s =
669 unesc_dbl @@ get_quoted_content s
671 type suspension_kind =
672 | SKSync
673 | SKAsync
674 | SKCoroutine
676 let mk_suspension_kind_ node env has_async has_coroutine =
677 match has_async, has_coroutine with
678 | false, false -> SKSync
679 | true, false -> SKAsync
680 | false, true -> SKCoroutine
681 | true, true ->
682 raise_parsing_error env (`Node node) "Coroutine functions may not be async";
683 SKCoroutine
685 let mk_suspension_kind node env is_async is_coroutine =
686 mk_suspension_kind_ node env
687 (not (is_missing is_async))
688 (not (is_missing is_coroutine))
690 let mk_fun_kind suspension_kind yield =
691 match suspension_kind, yield with
692 | SKSync, true -> FGenerator
693 | SKAsync, true -> FAsyncGenerator
694 | SKSync, false -> FSync
695 | SKAsync, false -> FAsync
696 | SKCoroutine, _ -> FCoroutine
697 (* Yield in coroutine is not permitted, the error will be reported at NastCheck *)
699 let fun_template yielding node suspension_kind env =
700 let p = pFunction node env in
701 { f_mode = mode_annotation env.fi_mode
702 ; f_tparams = []
703 ; f_constrs = []
704 ; f_ret = None
705 ; f_name = p, ";anonymous"
706 ; f_params = []
707 ; f_body = []
708 ; f_user_attributes = []
709 ; f_file_attributes = []
710 ; f_fun_kind = mk_fun_kind suspension_kind yielding
711 ; f_namespace = Namespace_env.empty env.parser_options
712 ; f_span = p
713 ; f_doc_comment = None
714 ; f_static = false
715 ; f_external = false (* true if this declaration has no body
716 because it is an external function declaration
717 (e.g. from an HHI file)*)
720 let param_template node env =
721 { param_hint = None
722 ; param_is_reference = false
723 ; param_is_variadic = false
724 ; param_id = pos_name node env
725 ; param_expr = None
726 ; param_modifier = None
727 ; param_callconv = None
728 ; param_user_attributes = []
731 let pShapeFieldName : shape_field_name parser = fun name env ->
732 let is_valid_shape_literal t =
733 let is_str =
734 (Token.kind t = TK.SingleQuotedStringLiteral ||
735 Token.kind t = TK.DoubleQuotedStringLiteral) in
736 let is_empty = let text = Token.text t in
737 text = "\'\'" || text = "\"\"" in
738 is_str && (not is_empty) in
739 match syntax name with
740 | ScopeResolutionExpression
741 { scope_resolution_qualifier; scope_resolution_name; _ } ->
742 SFclass_const
743 ( pos_name scope_resolution_qualifier env
744 , pos_name scope_resolution_name env
746 | LiteralExpression {
747 literal_expression = { syntax = Token t; _ }
748 } when is_valid_shape_literal t ->
749 let p, n = pos_name name env in SFlit_str (p, mkStr env name unesc_dbl n)
750 | _ ->
751 raise_parsing_error env (`Node name) SyntaxError.invalid_shape_field_name;
752 let p, n = pos_name name env in SFlit_str (p, mkStr env name unesc_dbl n)
754 let mpShapeExpressionField : ('a, (shape_field_name * 'a)) metaparser =
755 fun hintParser node env ->
756 match syntax node with
757 | FieldInitializer
758 { field_initializer_name = name; field_initializer_value = ty; _ } ->
759 let name = pShapeFieldName name env in
760 let ty = hintParser ty env in
761 name, ty
762 | _ -> missing_syntax "shape field" node env
764 let mpShapeField : ('a, shape_field) metaparser =
765 fun hintParser node env ->
766 match syntax node with
767 | FieldSpecifier { field_question; field_name; field_type; _ } ->
768 let sf_optional = not (is_missing field_question) in
769 let sf_name = pShapeFieldName field_name env in
770 let sf_hint = hintParser field_type env in
771 { sf_optional; sf_name; sf_hint }
772 | _ ->
773 let sf_name, sf_hint = mpShapeExpressionField hintParser node env in
774 (* Shape expressions can never have optional fields. *)
775 { sf_optional = false; sf_name; sf_hint }
777 let mpClosureParameter : ('a, hint * param_kind option) metaparser =
778 fun hintParser node env ->
779 match syntax node with
780 | ClosureParameterTypeSpecifier
781 { closure_parameter_call_convention
782 ; closure_parameter_type
783 } ->
784 let cp_kind =
785 mpOptional pParamKind closure_parameter_call_convention env in
786 let cp_hint = hintParser closure_parameter_type env in
787 cp_hint, cp_kind
788 | _ -> missing_syntax "closure parameter" node env
790 (* In some cases, we need to unwrap an extra layer of Block due to lowering
791 * from CompoundStatement. This applies to `if`, `while` and other control flow
792 * statements which allow optional curly braces.
794 * In other words, we want these to be lowered into the same Ast
795 * `if ($b) { func(); }` and `if ($b) func();`
796 * rather than the left hand side one having an extra `Block` in the Ast
798 let unwrap_extra_block (stmt : block) : block =
799 let de_noop = function
800 | [_, Noop] -> []
801 | stmts -> stmts
803 match stmt with
804 | [pos, Unsafe; _, Block b] -> (pos, Unsafe) :: de_noop b
805 | [_, Block b] -> de_noop b
806 | blk -> blk
808 let fail_if_invalid_class_creation env node (_, id) =
809 if not !(env.in_static_method) then () else begin
810 if (id = SN.Classes.cSelf && not @@ SSet.is_empty !(env.cls_reified_generics)) ||
811 (id = SN.Classes.cParent && !(env.parent_is_reified)) then
812 raise_parsing_error env (`Node node) SyntaxError.static_method_reified_obj_creation;
815 let fail_if_invalid_reified_generic env node (_, id) =
816 if not !(env.in_static_method) then () else begin
817 if SSet.mem id !(env.cls_reified_generics) then
818 raise_parsing_error env (`Node node) SyntaxError.cls_reified_generic_in_static_method
821 let check_valid_reified_hint env node h =
822 if not !(env.in_static_method) then () else
823 let reified_hint_visitor = object(self) inherit [_] iter as super
824 method! on_hint env hint =
825 match snd hint with
826 | Happly (id, hl) ->
827 fail_if_invalid_reified_generic env node id;
828 List.iter hl ~f:(self#on_hint env)
829 | Haccess (id1, id2, ids) ->
830 fail_if_invalid_reified_generic env node id1;
831 fail_if_invalid_reified_generic env node id2;
832 List.iter ids ~f:(fail_if_invalid_reified_generic env node)
833 | _ -> super#on_hint env hint
834 end in
835 reified_hint_visitor#on_hint env h
837 let rec pHint : hint parser = fun node env ->
838 let rec pHint_ : hint_ parser = fun node env ->
839 match syntax node with
840 (* Dirty hack; CastExpression can have type represented by token *)
841 | Token _
842 | SimpleTypeSpecifier _
843 | QualifiedName _
844 -> Happly (pos_name node env, [])
845 | ShapeTypeSpecifier { shape_type_fields; shape_type_ellipsis; _ } ->
846 let si_allows_unknown_fields =
847 not (is_missing shape_type_ellipsis)
849 (* if last element lacks a separator and ellipsis is present, error *)
850 Option.iter (List.last (syntax_to_list true shape_type_fields)) (fun last ->
851 if is_missing last && si_allows_unknown_fields then
852 raise_parsing_error env (`Node node) SyntaxError.shape_type_ellipsis_without_trailing_comma
854 let si_shape_field_list =
855 couldMap ~f:(mpShapeField pHint) shape_type_fields env in
856 Hshape { si_allows_unknown_fields; si_shape_field_list }
857 | TupleTypeSpecifier { tuple_types; _ } ->
858 Htuple (couldMap ~f:pHint tuple_types env)
859 | KeysetTypeSpecifier { keyset_type_keyword = kw; keyset_type_type = ty; _ }
860 | VectorTypeSpecifier { vector_type_keyword = kw; vector_type_type = ty; _ }
861 | ClassnameTypeSpecifier {classname_keyword = kw; classname_type = ty; _ }
862 | TupleTypeExplicitSpecifier
863 { tuple_type_keyword = kw
864 ; tuple_type_types = ty
865 ; _ }
866 | VarrayTypeSpecifier
867 { varray_keyword = kw
868 ; varray_type = ty
869 ; _ }
870 | VectorArrayTypeSpecifier
871 { vector_array_keyword = kw
872 ; vector_array_type = ty
873 ; _ }
874 -> Happly (pos_name kw env, couldMap ~f:pHint ty env)
876 | DarrayTypeSpecifier
877 { darray_keyword = kw
878 ; darray_key = key
879 ; darray_value = value
880 ; _ }
881 | MapArrayTypeSpecifier
882 { map_array_keyword = kw
883 ; map_array_key = key
884 ; map_array_value = value
885 ; _ } ->
886 Happly
887 ( pos_name kw env
888 , pHint key env :: couldMap ~f:pHint value env
890 | DictionaryTypeSpecifier
891 { dictionary_type_keyword = kw
892 ; dictionary_type_members = members
893 ; _ } -> Happly (pos_name kw env, couldMap ~f:pHint members env)
894 | GenericTypeSpecifier { generic_class_type; generic_argument_list } ->
895 let name = pos_name generic_class_type env in
896 let type_args =
897 match syntax generic_argument_list with
898 | TypeArguments { type_arguments_types; _ }
899 -> couldMap ~f:pHint type_arguments_types env
900 | _ -> missing_syntax "generic type arguments" generic_argument_list env
902 if env.codegen
903 then match String.lowercase (snd name), type_args with
904 | ("rx" | "rxlocal" | "rxshallow"), [_, (Hfun _ as t)]
905 | ("mutable" | "maybemutable" | "ownedmutable"), [_, (Happly _ as t)] -> t
906 | _ -> Happly(name, type_args)
907 else Happly(name, type_args)
908 | NullableTypeSpecifier { nullable_type; _ } ->
909 Hoption (pHint nullable_type env)
910 | SoftTypeSpecifier { soft_type; _ } ->
911 Hsoft (pHint soft_type env)
912 | ClosureTypeSpecifier {
913 closure_parameter_list;
914 closure_return_type;
915 closure_coroutine; _} ->
916 let make_variadic_hint variadic_type =
917 if is_missing variadic_type
918 then Hvariadic (None)
919 else Hvariadic (Some (pHint variadic_type env))
921 let (param_list, variadic_hints) =
922 List.partition_map ~f:(fun x ->
923 match syntax x with
924 | VariadicParameter { variadic_parameter_type = vtype; _ } ->
925 `Snd (make_variadic_hint vtype)
926 | _ -> `Fst (mpClosureParameter pHint x env))
927 (as_list closure_parameter_list)
929 let hd_variadic_hint hints =
930 if List.length hints > 1 then begin
931 let msg = Printf.sprintf
932 "%d variadic parameters found. There should be no more than one."
933 (List.length hints)
935 invariant_failure node msg env
936 end;
937 match List.hd hints with
938 | Some h -> h
939 | None -> Hnon_variadic
941 let is_coroutine = not (is_missing closure_coroutine) in
942 let param_type_hints = List.map param_list fst in
943 let param_callconvs = List.map param_list snd in
944 Hfun
945 ( is_coroutine
946 , param_type_hints
947 , param_callconvs
948 , hd_variadic_hint variadic_hints
949 , pHint closure_return_type env
951 | TypeConstant { type_constant_left_type; type_constant_right_type; _ } ->
952 let child = pos_name type_constant_right_type env in
953 (match pHint_ type_constant_left_type env with
954 | Haccess (b, c, cs) -> Haccess (b, c, cs @ [child])
955 | Happly (b, []) -> Haccess (b, child, [])
956 | _ -> missing_syntax "type constant base" node env
958 | ReifiedTypeArgument {reified_type_argument_type = t; _} ->
959 Hreified (pHint t env)
960 | _ -> missing_syntax "type hint" node env
962 let hint = pPos node env, pHint_ node env in
963 check_valid_reified_hint env node hint;
964 hint
966 let pTarg node env =
967 match syntax node with
968 | ReifiedTypeArgument { reified_type_argument_type = t; _} ->
969 pHint t env, true
970 | _ -> pHint node env, false
972 type fun_hdr =
973 { fh_suspension_kind : suspension_kind
974 ; fh_name : pstring
975 ; fh_constrs : (hint * constraint_kind * hint) list
976 ; fh_type_parameters : tparam list
977 ; fh_parameters : fun_param list
978 ; fh_return_type : hint option
979 ; fh_param_modifiers : fun_param list
982 let empty_fun_hdr =
983 { fh_suspension_kind = SKSync
984 ; fh_name = Pos.none, "<ANONYMOUS>"
985 ; fh_constrs = []
986 ; fh_type_parameters = []
987 ; fh_parameters = []
988 ; fh_return_type = None
989 ; fh_param_modifiers = []
992 let prevent_intrinsic_generic env node ty =
993 if not (is_missing ty) && not env.codegen then
994 raise_parsing_error env (`Node node) SyntaxError.collection_intrinsic_generic
996 let rec pSimpleInitializer node env =
997 match syntax node with
998 | SimpleInitializer { simple_initializer_value; _ } ->
999 pExpr simple_initializer_value env
1000 | _ -> missing_syntax "simple initializer" node env
1002 and pFunParamDefaultValue node env =
1003 match syntax node with
1004 | SimpleInitializer { simple_initializer_value; _ } ->
1005 begin match syntax simple_initializer_value with
1006 | ListExpression _ ->
1007 raise_parsing_error env (`Node node) (SyntaxError.invalid_default_argument "A list destructuring")
1008 | YieldExpression _
1009 | YieldFromExpression _ ->
1010 raise_parsing_error env (`Node node) (SyntaxError.invalid_default_argument "A yield")
1011 | PrefixUnaryExpression {
1012 prefix_unary_operator = { syntax = Token t; _ }; _ } when Token.kind t = TK.Await ->
1013 raise_parsing_error env (`Node node) (SyntaxError.invalid_default_argument "An await")
1014 | _ -> () end;
1015 mpOptional pExpr simple_initializer_value env
1016 | _ -> None
1019 and pFunParam : fun_param parser = fun node env ->
1020 match syntax node with
1021 | ParameterDeclaration
1022 { parameter_attribute
1023 ; parameter_visibility
1024 ; parameter_call_convention
1025 ; parameter_type
1026 ; parameter_name
1027 ; parameter_default_value
1028 } ->
1029 let is_reference, is_variadic, name =
1030 match syntax parameter_name with
1031 | DecoratedExpression
1032 { decorated_expression_decorator; decorated_expression_expression } ->
1033 (* There is a chance that the expression might be nested with an
1034 additional decorator, check this *)
1035 begin match syntax decorated_expression_expression with
1036 | DecoratedExpression
1037 { decorated_expression_decorator = nested_decorator
1038 ; decorated_expression_expression = nested_expression } ->
1039 let decorator = text decorated_expression_decorator in
1040 let nested_decorator = text nested_decorator in
1041 decorator = "&" || nested_decorator = "&",
1042 decorator = "..." || nested_decorator = "...",
1043 nested_expression
1044 | _ ->
1045 let decorator = text decorated_expression_decorator in
1046 decorator = "&", decorator = "...", decorated_expression_expression
1048 | _ -> false, false, parameter_name
1050 { param_hint = mpOptional pHint parameter_type env
1051 ; param_is_reference = is_reference
1052 ; param_is_variadic = is_variadic
1053 ; param_id = pos_name name env
1054 ; param_expr = pFunParamDefaultValue parameter_default_value env
1055 ; param_user_attributes = pUserAttributes env parameter_attribute
1056 ; param_callconv =
1057 mpOptional pParamKind parameter_call_convention env
1058 (* implicit field via constructor parameter.
1059 * This is always None except for constructors and the modifier
1060 * can be only Public or Protected or Private.
1062 ; param_modifier =
1063 let rec go = function
1064 | [] -> None
1065 | x :: _ when List.mem [Private; Public; Protected] x ~equal:(=) -> Some x
1066 | _ :: xs -> go xs
1068 go (pKinds (fun _ -> ()) parameter_visibility env)
1070 | VariadicParameter _
1071 | Token _ when text node = "..."
1072 -> { (param_template node env) with param_is_variadic = true }
1073 | _ -> missing_syntax "function parameter" node env
1074 and pUserAttribute : user_attribute list parser = fun node env ->
1075 match syntax node with
1076 | FileAttributeSpecification { file_attribute_specification_attributes = attrs; _ }
1077 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
1078 couldMap attrs env ~f:begin function
1079 | { syntax = ConstructorCall { constructor_call_argument_list; constructor_call_type; _ }; _ } ->
1080 fun env ->
1081 { ua_name = pos_name constructor_call_type env
1082 ; ua_params = couldMap ~f:(fun p ->
1083 begin match syntax p with
1084 | ScopeResolutionExpression {
1085 scope_resolution_name = { syntax = Token t; _ }; _
1086 } when Token.kind t = TK.Name ->
1087 raise_parsing_error env (`Node p) SyntaxError.constants_as_attribute_arguments
1088 | Token t when Token.kind t = TK.Name ->
1089 raise_parsing_error env (`Node p) SyntaxError.constants_as_attribute_arguments
1090 | _ -> () end;
1091 pExpr p) constructor_call_argument_list env
1093 | node -> missing_syntax "attribute" node
1095 | _ -> missing_syntax "attribute specification" node env
1097 and pUserAttributes env attrs =
1098 List.concat @@ couldMap ~f:pUserAttribute attrs env
1099 and pAField : afield parser = fun node env ->
1100 match syntax node with
1101 | ElementInitializer { element_key; element_value; _ } ->
1102 AFkvalue (pExpr element_key env, pExpr element_value env)
1103 | _ -> AFvalue (pExpr node env)
1104 and pString2: expr_location -> node list -> env -> expr list =
1105 let rec convert_name_to_lvar location env n =
1106 match syntax n with
1107 | Token token when Token.kind token = TK.Name ->
1108 let pos, name = pos_name n env in
1109 let id = Lvar (pos, "$" ^ name) in
1110 Some (pos, id)
1111 | SubscriptExpression { subscript_receiver; subscript_index; _ } ->
1112 begin match convert_name_to_lvar location env subscript_receiver with
1113 | Some recv ->
1114 let index = mpOptional (pExpr ~location) subscript_index env in
1115 Some (pPos n env, Array_get (recv, index))
1116 | _ -> None
1118 | _ -> None in
1120 let rec aux loc l env acc =
1121 (* in PHP "${x}" in strings is treated as if it was written "$x",
1122 here we recognize pattern: Dollar; EmbeddedBracedExpression { QName (Token.Name) }
1123 produced by FFP and lower it into Lvar.
1125 match l with
1126 | [] -> List.rev acc
1127 | ({ syntax = Token token; _ })::
1128 ({ syntax = EmbeddedBracedExpression {
1129 embedded_braced_expression_expression = e; _ }; _
1130 } as expr_with_braces)::
1131 tl when Token.kind token = TK.Dollar ->
1132 let e =
1133 begin match convert_name_to_lvar loc env e with
1134 | Some e -> e
1135 | None ->
1136 raise_parsing_error env (`Node expr_with_braces)
1137 SyntaxError.invalid_variable_variable;
1138 pPos expr_with_braces env, Omitted
1139 end in
1140 aux loc tl env (e::acc)
1141 | x::xs -> aux loc xs env ((pExpr ~location:loc x env)::acc)
1143 fun loc l env -> aux loc l env []
1144 and pExprL node env =
1145 (pPos node env, Expr_list (couldMap ~f:pExpr node env))
1147 (* TODO: this function is a hotspot, deep recursion on huge files, attempt more optimization *)
1148 and pMember node env =
1149 match syntax node with
1150 | ElementInitializer { element_key; element_value; _ } ->
1151 (pExpr element_key env, pExpr element_value env)
1152 | _ -> missing_syntax "darray intrinsic expression element" node env
1154 and pExpr ?location:(location=TopLevel) : expr parser = fun node env ->
1155 let split_args_varargs arg_list =
1156 match List.rev (as_list arg_list) with
1157 | { syntax = DecoratedExpression
1158 { decorated_expression_decorator =
1159 { syntax = Token token; _ }
1160 ; decorated_expression_expression = e
1163 } :: xs when Token.kind token = TK.DotDotDot ->
1164 let args = List.rev_map xs (fun x -> pExpr x env) in
1165 let vararg = pExpr e env in
1166 args, [vararg]
1167 | _ ->
1168 let args = couldMap ~f:pExpr arg_list env in
1169 args, [] in
1170 let rec pExpr_ : expr_ parser = fun node env ->
1171 env.recursion_depth := !(env.recursion_depth) + 1;
1172 if !(env.recursion_depth) > recursion_limit then
1173 failwith "Expression recursion limit reached";
1174 let pos = pPos node env in
1175 let result = match syntax node with
1176 | LambdaExpression {
1177 lambda_async; lambda_coroutine; lambda_signature; lambda_body;
1178 lambda_attribute_spec; _ } ->
1179 let suspension_kind = mk_suspension_kind node env lambda_async lambda_coroutine in
1180 let f_params, f_ret =
1181 match syntax lambda_signature with
1182 | LambdaSignature { lambda_parameters; lambda_type; _ } ->
1183 ( couldMap ~f:pFunParam lambda_parameters env
1184 , mpOptional pHint lambda_type env
1186 | Token _ -> ([param_template lambda_signature env], None)
1187 | _ -> missing_syntax "lambda signature" lambda_signature env
1189 let f_body, yield =
1190 mpYielding pFunctionBody lambda_body
1191 (if not (is_compound_statement lambda_body) then env else non_tls env)
1193 let f_external = is_semicolon lambda_body in
1194 Lfun
1195 { (fun_template yield node suspension_kind env) with
1196 f_ret
1197 ; f_params
1198 ; f_body
1199 ; f_user_attributes = pUserAttributes env lambda_attribute_spec
1200 ; f_external
1203 | BracedExpression { braced_expression_expression = expr; _ }
1204 | EmbeddedBracedExpression
1205 { embedded_braced_expression_expression = expr; _ }
1206 | ParenthesizedExpression { parenthesized_expression_expression = expr; _ }
1207 -> pExpr_ expr env
1209 | DictionaryIntrinsicExpression
1210 { dictionary_intrinsic_keyword = kw
1211 ; dictionary_intrinsic_explicit_type = ty
1212 ; dictionary_intrinsic_members = members
1213 ; _ }
1214 | KeysetIntrinsicExpression
1215 { keyset_intrinsic_keyword = kw
1216 ; keyset_intrinsic_explicit_type = ty
1217 ; keyset_intrinsic_members = members
1218 ; _ }
1219 | VectorIntrinsicExpression
1220 { vector_intrinsic_keyword = kw
1221 ; vector_intrinsic_explicit_type = ty
1222 ; vector_intrinsic_members = members
1223 ; _ }
1225 prevent_intrinsic_generic env node ty;
1226 if env.is_hh_file || env.enable_hh_syntax then
1227 Collection (pos_name kw env, couldMap ~f:pAField members env)
1228 else
1229 (* If php, this is a subscript expression, not a collection. *)
1230 let subscript_receiver = pExpr kw env in
1231 let members = couldMap ~f:pExpr members env in
1232 let subscript_index = match members with
1233 | [] -> None
1234 | [x] -> Some x
1235 | _ ->
1236 let msg = "Hack keyword " ^ (text kw) ^ " used improperly in php." in
1237 invariant_failure node msg env in
1238 Array_get (subscript_receiver, subscript_index)
1239 | CollectionLiteralExpression
1240 { collection_literal_name = collection_name
1241 ; collection_literal_initializers = members
1242 ; _ } ->
1243 let collection_name =
1244 match syntax collection_name with
1245 | SimpleTypeSpecifier { simple_type_specifier = class_type } ->
1246 pos_name class_type env
1247 | GenericTypeSpecifier { generic_class_type = class_type; _ } ->
1248 prevent_intrinsic_generic env node class_type;
1249 pos_name class_type env
1250 | _ -> pos_name collection_name env in
1251 Collection (collection_name, couldMap ~f:pAField members env)
1253 | VarrayIntrinsicExpression
1254 { varray_intrinsic_members = members
1255 ; varray_intrinsic_explicit_type = ty; _ } ->
1256 prevent_intrinsic_generic env node ty;
1257 Varray (couldMap ~f:pExpr members env)
1258 | DarrayIntrinsicExpression
1259 { darray_intrinsic_members = members
1260 ; darray_intrinsic_explicit_type = ty; _ } ->
1261 prevent_intrinsic_generic env node ty;
1262 Darray (couldMap ~f:pMember members env)
1263 | ArrayIntrinsicExpression { array_intrinsic_members = members; _ }
1264 | ArrayCreationExpression { array_creation_members = members; _ }
1266 (* TODO: Or tie in with other intrinsics and post-process to Array *)
1267 Array (couldMap ~f:pAField members env)
1269 | ListExpression { list_members = members; _ } ->
1270 (* TODO: Or tie in with other intrinsics and post-process to List *)
1271 let pBinderOrIgnore node env = match syntax node with
1272 | Missing -> (Pos.none, Omitted)
1273 | _ -> pExpr node env
1275 List (couldMap ~f:pBinderOrIgnore members env)
1277 | EvalExpression { eval_keyword = recv; eval_argument = args; _ }
1278 | EmptyExpression { empty_keyword = recv; empty_argument = args; _ }
1279 | IssetExpression { isset_keyword = recv; isset_argument_list = args; _ }
1280 | TupleExpression
1281 { tuple_expression_keyword = recv
1282 ; tuple_expression_items = args
1283 ; _ }
1285 let pos_if_has_parens =
1286 match syntax recv with
1287 | ParenthesizedExpression _ -> Some (pPos recv env)
1288 | _ -> None in
1289 let recv = pExpr recv env in
1290 let recv =
1291 match snd recv, pos_if_has_parens with
1292 | (Obj_get _ | Class_get _), Some p -> p, ParenthesizedExpr recv
1293 | _ -> recv in
1294 let args, varargs = split_args_varargs args in
1295 Call (recv, [], args, varargs)
1296 | FunctionCallExpression
1297 { function_call_receiver = recv
1298 ; function_call_argument_list =
1299 { syntax = SyntaxList
1300 [ { syntax = ListItem
1301 { list_item =
1302 { syntax = LiteralExpression { literal_expression = expr }
1312 } when text recv = "__hhas_adata"
1313 && token_kind expr = Some TK.NowdocStringLiteral ->
1314 let literal_expression_pos = pPos expr env in
1315 let s =
1316 expr
1317 |> source_text
1318 |> SourceText.text
1319 |> extract_unquoted_string
1320 ~start:(start_offset expr)
1321 ~len:(width expr) in
1322 Call (
1323 pExpr recv env,
1325 [ literal_expression_pos, String s ],
1328 | FunctionCallExpression
1329 { function_call_receiver = recv
1330 ; function_call_argument_list = args
1331 ; _ }
1333 let hints =
1334 begin match (syntax recv) with
1335 | GenericTypeSpecifier { generic_argument_list; _ } ->
1336 begin match syntax generic_argument_list with
1337 | TypeArguments { type_arguments_types; _ }
1338 -> couldMap ~f:pTarg type_arguments_types env
1339 | _ -> []
1341 | _ -> []
1344 (* preserve parens on receiver of call expression
1345 to allow distinguishing between
1346 ($a->b)() // invoke on callable property
1347 $a->b() // method call *)
1348 let pos_if_has_parens =
1349 match syntax recv with
1350 | ParenthesizedExpression _ -> Some (pPos recv env)
1351 | _ -> None in
1352 let recv = pExpr recv env in
1353 let recv =
1354 match snd recv, pos_if_has_parens with
1355 | (Obj_get _ | Class_get _), Some p -> p, ParenthesizedExpr recv
1356 | _ -> recv in
1357 let args, varargs = split_args_varargs args in
1358 Call (recv, hints, args, varargs)
1359 | FunctionCallWithTypeArgumentsExpression
1360 { function_call_with_type_arguments_receiver = recv
1361 ; function_call_with_type_arguments_type_args = type_args
1362 ; function_call_with_type_arguments_argument_list = args
1363 ; _ }
1365 let hints =
1366 begin match (syntax type_args) with
1367 | TypeArguments { type_arguments_types; _ } ->
1368 couldMap ~f:pTarg type_arguments_types env
1369 | _ -> missing_syntax "type arguments" type_args env
1370 end in
1371 Call (pExpr recv env, hints, couldMap ~f:pExpr args env, [])
1372 | QualifiedName _ ->
1373 if in_string location
1374 then
1375 let _, n = pos_qualified_name node env in
1376 String n
1377 else Id (pos_qualified_name node env)
1379 | VariableExpression { variable_expression } ->
1380 Lvar (pos_name variable_expression env)
1382 | PipeVariableExpression _ ->
1383 Lvar (pos, "$$")
1385 | InclusionExpression { inclusion_require; inclusion_filename } ->
1386 Import
1387 ( pImportFlavor inclusion_require env
1388 , pExpr inclusion_filename env
1391 | MemberSelectionExpression
1392 { member_object = recv
1393 ; member_operator = op
1394 ; member_name = name
1396 | SafeMemberSelectionExpression
1397 { safe_member_object = recv
1398 ; safe_member_operator = op
1399 ; safe_member_name = name
1401 | EmbeddedMemberSelectionExpression
1402 { embedded_member_object = recv
1403 ; embedded_member_operator = op
1404 ; embedded_member_name = name
1407 if is_object_creation_expression recv && not env.codegen then
1408 raise_parsing_error env (`Node recv) SyntaxError.invalid_constructor_method_call;
1409 let recv = pExpr recv env in
1410 let name = pExpr ~location:MemberSelect name env in
1411 let op = pNullFlavor op env in
1412 Obj_get (recv, name, op)
1414 | PrefixUnaryExpression
1415 { prefix_unary_operator = operator
1416 ; prefix_unary_operand = operand
1418 | PostfixUnaryExpression
1419 { postfix_unary_operand = operand
1420 ; postfix_unary_operator = operator
1422 | DecoratedExpression
1423 { decorated_expression_expression = operand
1424 ; decorated_expression_decorator = operator
1427 let expr = pExpr operand env in
1429 * FFP does not destinguish between ++$i and $i++ on the level of token
1430 * kind annotation. Prevent duplication by switching on `postfix` for
1431 * the two operatores for which AST /does/ differentiate between
1432 * fixities.
1434 let postfix = kind node = SyntaxKind.PostfixUnaryExpression in
1435 let kind = token_kind operator in
1436 (match kind with
1437 | Some TK.PlusPlus when postfix -> Unop (Upincr, expr)
1438 | Some TK.MinusMinus when postfix -> Unop (Updecr, expr)
1439 | Some TK.PlusPlus -> Unop (Uincr, expr)
1440 | Some TK.MinusMinus -> Unop (Udecr, expr)
1441 | Some TK.Exclamation -> Unop (Unot, expr)
1442 | Some TK.Tilde -> Unop (Utild, expr)
1443 | Some TK.Plus -> Unop (Uplus, expr)
1444 | Some TK.Minus -> Unop (Uminus, expr)
1445 | Some TK.Ampersand -> Unop (Uref, expr)
1446 | Some TK.At when env.codegen -> Unop (Usilence, expr)
1447 | Some TK.At -> snd expr
1448 | Some TK.Inout -> Callconv (Pinout, expr)
1449 | Some TK.Await ->
1450 begin match env.lifted_awaits with
1451 | Some lifted_awaits ->
1452 let e = snd expr in
1453 let p = pPos node env in
1454 lift_await (p, e) lifted_awaits ~with_temp_local:(location <> AsStatement)
1455 | None -> Await expr
1457 | Some TK.Suspend -> Suspend expr
1458 | Some TK.Clone -> Clone expr
1459 | Some TK.Print ->
1460 Call ((pos, Id (pos, "echo")), [], [expr], [])
1461 | Some TK.Dollar ->
1462 (match expr with
1463 | p, String s
1464 | p, Int s
1465 | p, Float s when location <> InGlobalVar ->
1466 if not env.codegen
1467 then raise_parsing_error env (`Node operator) SyntaxError.invalid_variable_name;
1468 Lvar (p, "$" ^ s)
1469 | _ ->
1470 raise_parsing_error env (`Node operator) SyntaxError.invalid_variable_variable;
1471 Omitted
1474 | _ -> missing_syntax "unary operator" node env
1476 | BinaryExpression
1477 { binary_left_operand; binary_operator; binary_right_operand }
1479 let bop_ast_node =
1480 let rlocation =
1481 if location = AsStatement && token_kind binary_operator = Some TK.Equal
1482 then RightOfAssignment else TopLevel in
1483 pBop binary_operator env
1484 (pExpr binary_left_operand env)
1485 (pExpr binary_right_operand ~location:rlocation env)
1487 begin match env.inside_declare, bop_ast_node with
1488 | false, Binop (Eq _, lhs, _) ->
1489 Ast_check.check_lvalue (fun pos error -> raise_parsing_error env (`Pos pos) error) lhs
1490 | _ -> ()
1491 end;
1492 bop_ast_node
1494 | Token t ->
1495 (match location, Token.kind t with
1496 | MemberSelect, TK.Variable -> Lvar (pos_name node env)
1497 | InDoubleQuotedString, TK.HeredocStringLiteral
1498 | InDoubleQuotedString, TK.HeredocStringLiteralHead
1499 | InDoubleQuotedString, TK.HeredocStringLiteralTail ->
1500 String (Php_escaping.unescape_heredoc (text node))
1501 | InDoubleQuotedString, _ -> String (unesc_dbl (text node))
1502 | InBacktickedString, _ -> String (Php_escaping.unescape_backtick (text node))
1503 | MemberSelect, _
1504 | InGlobalVar, _
1505 | TopLevel, _
1506 | AsStatement, _
1507 | RightOfAssignment, _
1508 | RightOfReturn, _ -> Id (pos_name node env)
1511 | YieldExpression { yield_operand; _ } ->
1512 env.saw_yield <- true;
1513 if location <> AsStatement && location <> RightOfAssignment
1514 then raise_parsing_error env (`Node node) SyntaxError.invalid_yield;
1515 if text yield_operand = "break"
1516 then Yield_break
1517 else
1518 if is_missing yield_operand
1519 then Yield (AFvalue (pos, Null))
1520 else Yield (pAField yield_operand env)
1522 | YieldFromExpression { yield_from_operand; _ } ->
1523 env.saw_yield <- true;
1524 if location <> AsStatement && location <> RightOfAssignment && location <> RightOfReturn
1525 then raise_parsing_error env (`Node node) SyntaxError.invalid_yield_from;
1526 Yield_from (pExpr yield_from_operand env)
1528 | DefineExpression { define_keyword; define_argument_list; _ } -> Call
1529 ( (let name = pos_name define_keyword env in fst name, Id name)
1530 , []
1531 , List.map ~f:(fun x -> pExpr x env) (as_list define_argument_list)
1532 , []
1535 | ScopeResolutionExpression
1536 { scope_resolution_qualifier; scope_resolution_name; _ } ->
1537 let qual =
1538 match pExpr scope_resolution_qualifier env with
1539 | p, Lvar v when not env.codegen -> p, Id v
1540 | qual -> qual
1542 begin match syntax scope_resolution_name with
1543 | Token token when Token.kind token = TK.Variable ->
1544 let name =
1545 ( pPos scope_resolution_name env
1546 , Lvar (pos_name scope_resolution_name env)
1549 Class_get (qual, name)
1550 | _ ->
1551 let name = pExpr scope_resolution_name env in
1552 begin match name with
1553 | p, String id | _, Id (p, id) -> Class_const (qual, (p, id))
1554 | _ -> Class_get (qual, name)
1557 | CastExpression { cast_type; cast_operand; _ } ->
1558 Cast (pHint cast_type env, pExpr cast_operand env)
1559 | ConditionalExpression
1560 { conditional_test; conditional_consequence; conditional_alternative; _ }
1561 -> Eif
1562 ( pExpr conditional_test env
1563 , mpOptional pExpr conditional_consequence env
1564 , pExpr conditional_alternative env
1566 | SubscriptExpression { subscript_receiver; subscript_index; _ } ->
1567 Array_get
1568 ( pExpr subscript_receiver env
1569 , mpOptional pExpr subscript_index env
1571 | EmbeddedSubscriptExpression
1572 { embedded_subscript_receiver; embedded_subscript_index; _ } ->
1573 Array_get
1574 ( pExpr embedded_subscript_receiver env
1575 , mpOptional (pExpr ~location) embedded_subscript_index env
1577 | ShapeExpression { shape_expression_fields; _ } ->
1578 Shape (
1579 couldMap ~f:(mpShapeExpressionField pExpr) shape_expression_fields env
1581 | ObjectCreationExpression { object_creation_object = obj; _ } ->
1582 pExpr_ obj env
1583 | ConstructorCall
1584 { constructor_call_argument_list; constructor_call_type; _ } ->
1585 let args, varargs = split_args_varargs constructor_call_argument_list in
1586 let e, hl = match syntax constructor_call_type with
1587 | GenericTypeSpecifier { generic_class_type; generic_argument_list } ->
1588 let name = pos_name generic_class_type env in
1589 let hints =
1590 match syntax generic_argument_list with
1591 | TypeArguments { type_arguments_types; _ }
1592 -> couldMap ~f:pTarg type_arguments_types env
1593 | _ ->
1594 missing_syntax "generic type arguments" generic_argument_list env
1596 (fst name, Id name), hints
1597 | SimpleTypeSpecifier _ ->
1598 let name = pos_name constructor_call_type env in
1599 (fst name, Id name), []
1600 | _ -> pExpr constructor_call_type env, [] in
1601 (match snd e with
1602 | Id name ->
1603 fail_if_invalid_reified_generic env node name;
1604 fail_if_invalid_class_creation env node name
1605 | _ -> ());
1608 , hl
1609 , args
1610 , varargs
1612 | AnonymousClass
1613 { anonymous_class_argument_list = args
1614 ; anonymous_class_extends_list = extends
1615 ; anonymous_class_implements_list = implements
1616 ; anonymous_class_body =
1617 { syntax = ClassishBody { classish_body_elements = elts; _ }; _ }
1618 ; _ } ->
1619 let args, varargs = split_args_varargs args in
1620 let c_mode = mode_annotation env.fi_mode in
1621 let c_user_attributes = [] in
1622 let c_file_attributes = [] in
1623 let c_final = false in
1624 let c_is_xhp = false in
1625 let c_name = pos, "anonymous" in
1626 let c_tparams = [] in
1627 let c_extends = couldMap ~f:pHint extends env in
1628 let c_implements = couldMap ~f:pHint implements env in
1629 let c_body = List.concat (couldMap ~f:pClassElt elts env) in
1630 let c_namespace = Namespace_env.empty env.parser_options in
1631 let c_enum = None in
1632 let c_span = pPos node env in
1633 let c_kind = Cnormal in
1634 let c_doc_comment = None in
1635 let cls =
1636 { c_mode
1637 ; c_user_attributes
1638 ; c_file_attributes
1639 ; c_final
1640 ; c_is_xhp
1641 ; c_name
1642 ; c_tparams
1643 ; c_extends
1644 ; c_implements
1645 ; c_body
1646 ; c_namespace
1647 ; c_enum
1648 ; c_span
1649 ; c_kind
1650 ; c_doc_comment
1651 } in
1652 NewAnonClass (args, varargs, cls)
1653 | GenericTypeSpecifier
1654 { generic_class_type
1656 } ->
1657 let name = pos_name generic_class_type env in
1658 (* TODO: We are dropping generics here *)
1659 Id name
1660 | LiteralExpression { literal_expression = expr } ->
1661 let disallow_execution_operator =
1662 (is_hack env && ParserOptions.disallow_execution_operator env.parser_options) in
1663 (match syntax expr with
1664 | Token _ ->
1665 let s = text expr in
1666 (match location, token_kind expr with
1667 (* TODO(T21285960): Inside strings, int indices "should" be string indices *)
1668 | InDoubleQuotedString, _ when env.codegen -> String (mkStr env expr unesc_dbl s)
1669 | InBacktickedString, _ when env.codegen ->
1670 String (mkStr env expr Php_escaping.unescape_backtick s)
1671 | _, Some TK.OctalLiteral
1672 when is_typechecker env &&
1673 String_utils.fold_left ~f:(fun b c -> b || c = '8' || c = '9') ~acc:false s ->
1674 raise_parsing_error env (`Node node) SyntaxError.invalid_octal_integer;
1675 missing_syntax "octal int" expr env (* this should never get hit *)
1676 | _, Some TK.DecimalLiteral
1677 | _, Some TK.OctalLiteral
1678 | _, Some TK.HexadecimalLiteral
1679 (* We allow underscores while lexing the integer literals. This gets rid of them before
1680 * the literal is created. *)
1681 | _, Some TK.BinaryLiteral -> Int (Str.global_replace (underscore) "" s)
1682 | _, Some TK.FloatingLiteral -> Float s
1683 | _, Some TK.SingleQuotedStringLiteral ->
1684 String (mkStr env expr Php_escaping.unescape_single s)
1685 | _, Some TK.DoubleQuotedStringLiteral ->
1686 String (mkStr env expr Php_escaping.unescape_double s)
1687 | _, Some TK.HeredocStringLiteral ->
1688 String (mkStr env expr Php_escaping.unescape_heredoc s)
1689 | _, Some TK.NowdocStringLiteral ->
1690 String (mkStr env expr Php_escaping.unescape_nowdoc s)
1691 | _, Some TK.NullLiteral ->
1692 if not env.codegen && s <> String.lowercase s then
1693 Lint.lowercase_constant pos s;
1694 Null
1695 | _, Some TK.BooleanLiteral ->
1696 if not env.codegen && s <> String.lowercase s then
1697 Lint.lowercase_constant pos s;
1698 (match String.lowercase s with
1699 | "false" -> False
1700 | "true" -> True
1701 | _ -> missing_syntax ("boolean (not: " ^ s ^ ")") expr env
1703 | _, Some TK.ExecutionStringLiteral ->
1704 if (is_typechecker env || disallow_execution_operator) then
1705 raise_parsing_error env (`Node node) SyntaxError.execution_operator;
1706 Execution_operator [pos, String (mkStr env expr Php_escaping.unescape_backtick s)]
1707 | _ -> missing_syntax "literal" expr env
1709 | SyntaxList ({ syntax = Token token; _ } :: _ as ts)
1710 when Token.kind token = TK.ExecutionStringLiteralHead ->
1711 if (is_typechecker env || disallow_execution_operator) then
1712 raise_parsing_error env (`Node node) SyntaxError.execution_operator;
1713 Execution_operator (pString2 InBacktickedString (prepString2 env ts) env)
1714 | SyntaxList ts -> String2 (pString2 InDoubleQuotedString (prepString2 env ts) env)
1715 | _ -> missing_syntax "literal expression" expr env
1717 | PrefixedStringExpression
1718 { prefixed_string_name = name
1719 ; prefixed_string_str = str } ->
1720 (* Temporarily allow only`re`- prefixed strings *)
1721 let name_text = text name in
1722 if name_text <> "re"
1723 then raise_parsing_error env (`Node node) SyntaxError.non_re_prefix;
1724 PrefixedString (text name, pExpr str env)
1725 | InstanceofExpression
1726 { instanceof_left_operand; instanceof_right_operand; _ } ->
1727 let ty =
1728 match pExpr instanceof_right_operand env with
1729 | p, Class_const ((_, pid), (_, "")) -> p, pid
1730 | ty -> ty
1732 InstanceOf (pExpr instanceof_left_operand env, ty)
1733 (* TODO: Priority fix? *)
1734 (*match pExpr instanceof_left_operand env with
1735 | p, Unop (o,e) -> Unop (0, (p, InstanceOf (e, ty)))
1736 | e -> InstanceOf (e, ty)
1738 | IsExpression
1739 { is_left_operand; is_right_operand; _ } ->
1740 Is (pExpr is_left_operand env, pHint is_right_operand env)
1741 | AsExpression
1742 { as_left_operand; as_right_operand; _ } ->
1743 As (pExpr as_left_operand env, pHint as_right_operand env, false)
1744 | NullableAsExpression
1745 { nullable_as_left_operand; nullable_as_right_operand; _ } ->
1746 As (pExpr nullable_as_left_operand env,
1747 pHint nullable_as_right_operand env,
1748 true)
1749 | AnonymousFunction
1750 { anonymous_attribute_spec = attribute_spec
1751 ; anonymous_static_keyword
1752 ; anonymous_async_keyword
1753 ; anonymous_coroutine_keyword
1754 ; anonymous_parameters
1755 ; anonymous_type
1756 ; anonymous_use
1757 ; anonymous_body
1758 ; _ }
1759 | Php7AnonymousFunction
1760 { php7_anonymous_attribute_spec = attribute_spec
1761 ; php7_anonymous_static_keyword = anonymous_static_keyword
1762 ; php7_anonymous_async_keyword = anonymous_async_keyword
1763 ; php7_anonymous_coroutine_keyword = anonymous_coroutine_keyword
1764 ; php7_anonymous_parameters = anonymous_parameters
1765 ; php7_anonymous_type = anonymous_type
1766 ; php7_anonymous_use = anonymous_use
1767 ; php7_anonymous_body = anonymous_body
1768 ; _ } ->
1769 begin match syntax node with
1770 | Php7AnonymousFunction _ when is_typechecker env ->
1771 raise_parsing_error env (`Node node) SyntaxError.php7_anonymous_function
1772 | _ -> ()
1773 end;
1774 let pArg node env =
1775 match syntax node with
1776 | PrefixUnaryExpression
1777 { prefix_unary_operator = op; prefix_unary_operand = v } ->
1778 pos_name v env, token_kind op = Some TK.Ampersand
1779 | Token _ -> pos_name node env, false
1780 | _ -> missing_syntax "use variable" node env
1782 let pUse node =
1783 match syntax node with
1784 | AnonymousFunctionUseClause { anonymous_use_variables; _ } ->
1785 couldMap ~f:pArg anonymous_use_variables
1786 | _ -> fun _env -> []
1788 let suspension_kind =
1789 mk_suspension_kind
1790 node
1792 anonymous_async_keyword
1793 anonymous_coroutine_keyword in
1794 let f_body, yield =
1795 mpYielding pFunctionBody anonymous_body (non_tls env)
1797 let doc_comment = match extract_docblock node with
1798 | Some _ as doc_comment -> doc_comment
1799 | None -> top_docblock() in
1800 let user_attributes = pUserAttributes env attribute_spec in
1801 let f_external = is_semicolon anonymous_body in
1802 Efun
1803 ( { (fun_template yield node suspension_kind env) with
1804 f_ret = mpOptional pHint anonymous_type env
1805 ; f_params = couldMap ~f:pFunParam anonymous_parameters env
1806 ; f_body
1807 ; f_static = not (is_missing anonymous_static_keyword)
1808 ; f_doc_comment = doc_comment
1809 ; f_user_attributes = user_attributes
1810 ; f_external
1812 , try pUse anonymous_use env with _ -> []
1815 | AwaitableCreationExpression
1816 { awaitable_async; awaitable_coroutine; awaitable_compound_statement;
1817 awaitable_attribute_spec; } ->
1818 let suspension_kind =
1819 mk_suspension_kind node env awaitable_async awaitable_coroutine in
1820 let blk, yld = mpYielding pFunctionBody awaitable_compound_statement env in
1821 let user_attributes = pUserAttributes env awaitable_attribute_spec in
1822 let f_external = is_semicolon awaitable_compound_statement in
1823 let body =
1824 { (fun_template yld node suspension_kind env) with
1825 f_body = mk_noop (pPos awaitable_compound_statement env) blk;
1826 f_user_attributes = user_attributes;
1827 f_external }
1829 Call ((pPos node env, Lfun body), [], [], [])
1830 | XHPExpression
1831 { xhp_open =
1832 { syntax = XHPOpen { xhp_open_name; xhp_open_attributes; _ }; _ }
1833 ; xhp_body = body
1834 ; _ } ->
1835 env.ignore_pos <- false;
1836 let name =
1837 let pos, name = pos_name xhp_open_name env in
1838 (pos, ":" ^ name)
1840 let combine b e =
1841 make_token Token.(
1842 concatenate b e
1845 let aggregate_tokens node =
1846 let rec search = function (* scroll through non-token things *)
1847 | [] -> []
1848 | t :: xs when token_kind t = Some TK.XHPComment -> search xs
1849 | { syntax = Token b; _ } as t :: xs -> track t b None xs
1850 | x :: xs -> x :: search xs
1851 and track t b oe = function (* keep going through consecutive tokens *)
1852 | { syntax = Token e; _ } :: xs
1853 when Token.kind e <> TK.XHPComment -> track t b (Some e) xs
1854 | xs -> Option.value_map oe ~default:t ~f:(combine b) :: search xs
1856 search (as_list node)
1858 let pEmbedded escaper node env =
1859 match syntax node with
1860 | Token token
1861 when env.codegen && Token.kind token = TK.XHPStringLiteral ->
1862 let p = pPos node env in
1863 (* for XHP string literals (attribute values) just extract
1864 value from quotes and decode HTML entities *)
1865 let text =
1866 Html_entities.decode @@ get_quoted_content (full_text node) in
1867 p, String text
1868 | Token token
1869 when env.codegen && Token.kind token = TK.XHPBody ->
1870 let p = pPos node env in
1871 (* for XHP body - only decode HTML entities *)
1872 let text = Html_entities.decode @@ unesc_xhp (full_text node) in
1873 p, String text
1874 | Token _ ->
1875 let p = pPos node env in
1876 p, String (escaper (full_text node))
1877 | _ ->
1878 match pExpr node env with
1879 | _, BracedExpr e -> e
1880 | e -> e
1882 let pAttr = fun node env ->
1883 match syntax node with
1884 | XHPSimpleAttribute { xhp_simple_attribute_name; xhp_simple_attribute_expression; _ } ->
1885 let name = pos_name xhp_simple_attribute_name env in
1886 let expr =
1887 if is_braced_expression xhp_simple_attribute_expression
1888 && env.fi_mode = FileInfo.Mdecl && not env.codegen
1889 then Pos.none, Null
1890 else pEmbedded unesc_xhp_attr xhp_simple_attribute_expression env
1892 Xhp_simple (name, expr)
1893 | XHPSpreadAttribute { xhp_spread_attribute_expression; _ } ->
1894 Xhp_spread ( pEmbedded unesc_xhp_attr xhp_spread_attribute_expression env )
1895 | _ -> missing_syntax "XHP attribute" node env
1897 let attrs = couldMap ~f:pAttr xhp_open_attributes env in
1898 let exprs =
1899 List.map ~f:(fun x -> pEmbedded unesc_xhp x env) (aggregate_tokens body)
1901 Xml (name, attrs, exprs)
1902 (* FIXME; should this include Missing? ; "| Missing -> Null" *)
1903 | _ -> missing_syntax ?fallback:(Some Null) "expression" node env
1905 env.recursion_depth := !(env.recursion_depth) - 1;
1906 assert (!(env.recursion_depth) >= 0);
1907 result
1909 let outer_unsafes = env.unsafes in
1910 let is_safe =
1911 (* when in codegen ignore UNSAFE_EXPRs *)
1912 if env.codegen then true
1913 else
1914 match leading_token node with
1915 | None -> true
1916 | Some t ->
1917 let us =
1918 if Token.has_trivia_kind t TriviaKind.UnsafeExpression
1919 then Token.filter_leading_trivia_by_kind t TriviaKind.UnsafeExpression
1920 else []
1922 let f acc u = ISet.add (Trivia.start_offset u) acc in
1923 let us = List.fold ~f ~init:ISet.empty us in
1924 (* Have we already dealt with 'these' UNSAFE_EXPRs? *)
1925 let us = ISet.diff us outer_unsafes in
1926 let safe = ISet.is_empty us in
1927 if not safe then env.unsafes <- ISet.union us outer_unsafes;
1928 safe
1930 let result =
1931 match syntax node with
1932 | BracedExpression { braced_expression_expression = expr; _ }
1933 | ParenthesizedExpression { parenthesized_expression_expression = expr; _ }
1934 -> (**
1935 * Peeling off braced or parenthesised expresions. When there is XHP
1936 * inside, we want the XHP node to have this own positions, rather than
1937 * those of enclosing parenthesised/braced expressions.
1939 let inner = pExpr ~location expr env in
1940 if Syntax.is_braced_expression node
1941 then
1942 (* We elide the braces in {$x}, as it makes compilation easier *)
1943 begin match inner with
1944 | _, (Lvar _ | String _ | Int _ | Float _ ) -> inner
1945 | p, _ -> p, BracedExpr inner
1947 else inner
1948 | _ ->
1950 * Since we need positions in XHP, regardless of the ignore_pos flag, we
1951 * parenthesise the call to pExpr_ so that the XHP expression case can
1952 * flip the switch. The key part is that `pPos` happens before the old
1953 * setting is restored.
1955 let local_ignore_pos = env.ignore_pos in
1956 let expr_ = pExpr_ node env in
1957 let p = pPos node env in
1958 env.ignore_pos <- local_ignore_pos;
1959 p, expr_
1961 if is_safe then result else fst result, Unsafeexpr result
1962 and pBlock : block parser = fun node env ->
1963 let rec fix_last acc = function
1964 | x :: (_ :: _ as xs) -> fix_last (x::acc) xs
1965 | [_, Block block] -> List.rev acc @ block
1966 | stmt -> List.rev acc @ stmt
1968 let stmt = pStmtUnsafe node env in
1969 fix_last [] stmt
1970 and pFunctionBody : block parser = fun node env ->
1971 match syntax node with
1972 | Missing -> []
1973 | CompoundStatement
1974 { compound_statements = {syntax = Missing; _}
1975 ; compound_right_brace = { syntax = Token t; _ }
1976 ; _} ->
1977 [ Pos.none
1978 , if Token.has_trivia_kind t TriviaKind.Unsafe then Unsafe else Noop
1980 | CompoundStatement {compound_statements = {syntax = SyntaxList [t]; _}; _}
1981 when Syntax.is_specific_token TK.Yield t ->
1982 env.saw_yield <- true;
1983 [ Pos.none, Noop ]
1984 | CompoundStatement _ ->
1985 let block = with_new_nonconcurrent_scope env (fun () -> pBlock node env) in
1986 if not env.top_level_statements
1987 && ( env.fi_mode = FileInfo.Mdecl && not env.codegen
1988 || env.quick_mode)
1989 then [ Pos.none, Noop ]
1990 else block
1991 | _ ->
1992 let p, r = with_new_nonconcurrent_scope env (fun () -> pExpr node env) in
1993 [p, Return (Some (p, r))]
1994 and pStmtUnsafe : stmt list parser = fun node env ->
1995 let stmt = pStmt node env in
1996 match leading_token node with
1997 | Some t when Token.has_trivia_kind t TriviaKind.Unsafe -> [Pos.none, Unsafe; stmt]
1998 | _ -> [stmt]
1999 and pStmt : stmt parser = fun node env ->
2000 extract_and_push_docblock node;
2001 let pos = pPos node env in
2002 let result = match syntax node with
2003 | AlternateElseClause _ | AlternateIfStatement _ | AlternateElseifClause _
2004 | AlternateLoopStatement _ | AlternateSwitchStatement _ when is_typechecker env ->
2005 raise_parsing_error env (`Node node) SyntaxError.alternate_control_flow;
2006 missing_syntax "alternative control flow" node env (* this should never get hit *)
2007 | SwitchStatement { switch_expression=expr; switch_sections=sections; _ }
2008 | AlternateSwitchStatement { alternate_switch_expression=expr;
2009 alternate_switch_sections=sections; _ } ->
2010 lift_awaits_in_statement env pos (fun () ->
2011 let pSwitchLabel : (block -> case) parser = fun node env cont ->
2012 match syntax node with
2013 | CaseLabel { case_expression; _ } ->
2014 Case (pExpr case_expression env, cont)
2015 | DefaultLabel _ -> Default cont
2016 | _ -> missing_syntax "switch label" node env
2018 let pSwitchSection : case list parser = fun node env ->
2019 match syntax node with
2020 | SwitchSection
2021 { switch_section_labels
2022 ; switch_section_statements
2023 ; switch_section_fallthrough
2024 } ->
2025 let rec null_out cont = function
2026 | [x] -> [x cont]
2027 | (x::xs) -> x [] :: null_out cont xs
2028 | _ -> raise_parsing_error env (`Node node) "Malformed block result"; []
2030 let blk = List.concat @@
2031 couldMap ~f:pStmtUnsafe switch_section_statements env in
2032 let blk =
2033 if is_missing switch_section_fallthrough
2034 then blk
2035 else blk @ [Pos.none, Fallthrough]
2037 null_out blk (couldMap ~f:pSwitchLabel switch_section_labels env)
2038 | _ -> missing_syntax "switch section" node env
2040 pos,
2041 Switch
2042 ( pExpr expr env
2043 , List.concat @@ couldMap ~f:pSwitchSection sections env
2045 | IfStatement
2046 { if_condition=cond;
2047 if_statement=stmt;
2048 if_elseif_clauses=elseif_clause;
2049 if_else_clause=else_clause; _ }
2050 | AlternateIfStatement
2051 { alternate_if_condition=cond;
2052 alternate_if_statement=stmt;
2053 alternate_if_elseif_clauses=elseif_clause;
2054 alternate_if_else_clause=else_clause; _ } ->
2055 lift_awaits_in_statement env pos (fun () ->
2056 (* Because consistency is for the weak-willed, Parser_hack does *not*
2057 * produce `Noop`s for compound statements **in if statements**
2059 let if_condition = pExpr cond env in
2060 let if_statement = unwrap_extra_block @@ pStmtUnsafe stmt env in
2061 let if_elseif_statement =
2062 let pElseIf : (block -> block) parser = fun node env ->
2063 match syntax node with
2064 | ElseifClause { elseif_condition=ei_cond; elseif_statement=ei_stmt; _ }
2065 | AlternateElseifClause { alternate_elseif_condition=ei_cond;
2066 alternate_elseif_statement=ei_stmt; _ } ->
2067 fun next_clause ->
2068 let elseif_condition = pExpr ei_cond env in
2069 let elseif_statement = unwrap_extra_block @@ pStmtUnsafe ei_stmt env in
2070 [ pos, If (elseif_condition, elseif_statement, next_clause) ]
2071 | _ -> missing_syntax "elseif clause" node env
2073 List.fold_right ~f:(@@)
2074 (couldMap ~f:pElseIf elseif_clause env)
2075 ~init:( match syntax else_clause with
2076 | ElseClause { else_statement=e_stmt; _ }
2077 | AlternateElseClause { alternate_else_statement=e_stmt; _ } ->
2078 unwrap_extra_block @@ pStmtUnsafe e_stmt env
2079 | Missing -> [Pos.none, Noop]
2080 | _ -> missing_syntax "else clause" else_clause env
2083 pos, If (if_condition, if_statement, if_elseif_statement))
2084 | ExpressionStatement { expression_statement_expression; _ } ->
2085 lift_awaits_in_statement env pos (fun () ->
2086 if is_missing expression_statement_expression
2087 then pos, Noop
2088 else pos, Expr (pExpr ~location:AsStatement expression_statement_expression env))
2089 | CompoundStatement { compound_statements; compound_right_brace; _ } ->
2090 let tail =
2091 match leading_token compound_right_brace with
2092 | Some t when Token.has_trivia_kind t TriviaKind.Unsafe -> [pos, Unsafe]
2093 | _ -> []
2095 handle_loop_body pos compound_statements tail env
2096 | AlternateLoopStatement { alternate_loop_statements; _ } ->
2097 handle_loop_body pos alternate_loop_statements [] env
2098 | SyntaxList _ ->
2099 handle_loop_body pos node [] env
2100 | ThrowStatement { throw_expression; _ } ->
2101 lift_awaits_in_statement env pos (fun () ->
2102 pos, Throw (pExpr throw_expression env))
2103 | DoStatement { do_body; do_condition; _ } ->
2104 pos, Do (pBlock do_body env, pExpr do_condition env)
2105 | WhileStatement { while_condition; while_body; _ } ->
2106 pos, While (pExpr while_condition env, unwrap_extra_block @@ pStmtUnsafe while_body env)
2107 | DeclareDirectiveStatement { declare_directive_expression; _ } ->
2108 pos, Declare (false, pExpr declare_directive_expression { env with inside_declare = true }, [])
2109 | DeclareBlockStatement { declare_block_expression; declare_block_body; _ } ->
2110 let env = { env with inside_declare = true } in
2111 pos, Declare (true, pExpr declare_block_expression env,
2112 unwrap_extra_block @@ pStmtUnsafe declare_block_body env)
2113 | UsingStatementBlockScoped
2114 { using_block_await_keyword
2115 ; using_block_expressions
2116 ; using_block_body
2117 ; _ } ->
2118 pos, Using {
2119 us_is_block_scoped = true;
2120 us_has_await = not (is_missing using_block_await_keyword);
2121 us_expr = pExprL using_block_expressions env;
2122 us_block = pBlock using_block_body env;
2124 | UsingStatementFunctionScoped
2125 { using_function_await_keyword
2126 ; using_function_expression
2127 ; _ } ->
2128 (* All regular function scoped using statements should
2129 * be rewritten by this point
2130 * If this gets run, it means that this using statement is the only one
2131 * in the block, hence it is not in a compound statement *)
2132 pos, Using {
2133 us_is_block_scoped = false;
2134 us_has_await = not (is_missing using_function_await_keyword);
2135 us_expr = pExpr using_function_expression env;
2136 us_block = [Pos.none, Noop];
2138 | LetStatement
2139 { let_statement_name; let_statement_type; let_statement_initializer; _ } ->
2140 let id = pos_name let_statement_name env in
2141 let ty = mpOptional pHint let_statement_type env in
2142 let expr = pSimpleInitializer let_statement_initializer env in
2143 pos, Let (id, ty, expr)
2144 | ForStatement
2145 { for_initializer; for_control; for_end_of_loop; for_body; _ } ->
2146 lift_awaits_in_statement env pos (fun () ->
2147 let ini = pExprL for_initializer env in
2148 let ctr = pExprL for_control env in
2149 let eol = pExprL for_end_of_loop env in
2150 let blk = unwrap_extra_block @@ pStmtUnsafe for_body env in
2151 pos, For (ini, ctr, eol, blk))
2152 | ForeachStatement
2153 { foreach_collection
2154 ; foreach_await_keyword
2155 ; foreach_key
2156 ; foreach_value
2157 ; foreach_body
2158 ; _ } ->
2159 lift_awaits_in_statement env pos (fun () ->
2160 let col = pExpr foreach_collection env in
2161 let akw =
2162 match syntax foreach_await_keyword with
2163 | Token token when Token.kind token = TK.Await ->
2164 Some (pPos foreach_await_keyword env)
2165 | _ -> None
2167 let akv =
2168 let value = pExpr foreach_value env in
2169 match syntax foreach_key with
2170 | Missing -> As_v value
2171 | _ ->
2172 let key = pExpr foreach_key env in
2173 As_kv (key, value)
2175 let blk = unwrap_extra_block @@ pStmtUnsafe foreach_body env in
2176 pos, Foreach (col, akw, akv, blk)
2178 | TryStatement
2179 { try_compound_statement; try_catch_clauses; try_finally_clause; _ } ->
2180 pos, Try
2181 ( pBlock try_compound_statement env
2182 , couldMap try_catch_clauses env ~f:begin fun node env ->
2183 match syntax node with
2184 | CatchClause { catch_type; catch_variable; catch_body; _ } ->
2185 ( pos_name catch_type env
2186 , pos_name catch_variable env
2187 , mpStripNoop pBlock catch_body env
2189 | _ -> missing_syntax "catch clause" node env
2191 , match syntax try_finally_clause with
2192 | FinallyClause { finally_body; _ } ->
2193 pBlock finally_body env
2194 | _ -> []
2196 | FunctionStaticStatement { static_declarations; _ } ->
2197 let pStaticDeclarator node env =
2198 match syntax node with
2199 | StaticDeclarator { static_name; static_initializer } ->
2200 let lhs =
2201 match pExpr static_name env with
2202 | p, Id (p', s) -> p, Lvar (p', s)
2203 | x -> x
2205 (match syntax static_initializer with
2206 | SimpleInitializer { simple_initializer_value; _ } ->
2207 ( pPos static_initializer env
2208 , Binop (Eq None, lhs, pExpr simple_initializer_value env)
2210 | _ -> lhs
2212 | _ -> missing_syntax "static declarator" node env
2214 pos, Static_var (couldMap ~f:pStaticDeclarator static_declarations env)
2215 | ReturnStatement { return_expression; _ } ->
2216 lift_awaits_in_statement env pos (fun () ->
2217 let expr = match syntax return_expression with
2218 | Missing -> None
2219 | _ -> Some (pExpr ~location:RightOfReturn return_expression env)
2221 pos, Return (expr))
2222 | Syntax.GotoLabel { goto_label_name; _ } ->
2223 let pos_label = pPos goto_label_name env in
2224 let label_name = text goto_label_name in
2225 pos, Ast.GotoLabel (pos_label, label_name)
2226 | GotoStatement { goto_statement_label_name; _ } ->
2227 if is_typechecker env && not (ParserOptions.allow_goto env.parser_options)
2228 then raise_parsing_error env (`Node node) SyntaxError.goto;
2229 pos, Goto (pos_name goto_statement_label_name env)
2230 | EchoStatement { echo_keyword = kw; echo_expressions = exprs; _ }
2231 | UnsetStatement { unset_keyword = kw; unset_variables = exprs; _ }
2232 -> lift_awaits_in_statement env pos (fun () -> pos, Expr
2233 ( pPos node env
2234 , Call
2235 ( (match syntax kw with
2236 | QualifiedName _
2237 | SimpleTypeSpecifier _
2238 | Token _
2239 -> let name = pos_name kw env in fst name, Id name
2240 | _ -> missing_syntax "id" kw env
2242 , []
2243 , couldMap ~f:pExpr exprs env
2244 , []
2246 | BreakStatement { break_level=level; _ } ->
2247 pos, Break (pBreak_or_continue_level env level)
2248 | ContinueStatement { continue_level=level; _ } ->
2249 pos, Continue (pBreak_or_continue_level env level)
2250 | GlobalStatement { global_variables; _ } ->
2251 pos, Global_var (couldMap ~f:(pExpr ~location:InGlobalVar) global_variables env)
2252 | ConcurrentStatement { concurrent_statement=concurrent_stmt; _ } ->
2253 if not (ParserOptions.enable_concurrent env.parser_options) then
2254 raise_parsing_error env (`Node node) SyntaxError.concurrent_is_disabled;
2256 let (lifted_awaits, stmt) =
2257 with_new_concurrent_scope env (fun () -> pStmt concurrent_stmt env) in
2258 (* lifted awaits are accumulated in reverse *)
2259 let await_all = pos, Awaitall (List.rev lifted_awaits.awaits) in
2260 let stmt = match stmt with
2261 | pos, Block stmts ->
2262 let body_stmts, assign_stmts, _ =
2263 List.fold_left ~init:([], [], 1)
2264 ~f:(fun (body_stmts, assign_stmts, i) n ->
2265 match n with
2266 | (p1, Expr (p2, Binop ((Eq op), e1, e2))) ->
2267 let name = make_tmp_var_name i in
2268 let tmp_n = Pos.none, Lvar (Pos.none, name) in
2269 let body_stmts =
2270 match tmp_n, e2 with
2271 | (_, Lvar (_, name1)), (_, Lvar (_, name2))
2272 when name1 = name2 ->
2273 body_stmts
2274 | _ ->
2275 let new_n = (p1, Expr (Pos.none, Binop ((Eq None), tmp_n, e2))) in
2276 new_n :: body_stmts in
2278 let assign_stmt = (p1, Expr (p2, Binop ((Eq op), e1, tmp_n))) in
2279 (body_stmts, assign_stmt :: assign_stmts, i + 1)
2280 | _ -> (n :: body_stmts, assign_stmts, i)
2281 ) stmts in
2282 pos, Block (List.concat [List.rev body_stmts; List.rev assign_stmts])
2283 | _ -> failwith "Unexpected concurrent stmt structure" in
2285 pos, Block [await_all; stmt]
2286 | MarkupSection _ -> pMarkup node env
2287 | _ when env.max_depth > 0 && env.codegen ->
2288 (* OCaml optimisers; Forgive them, for they know not what they do!
2290 * The max_depth is only there to stop the *optimised* version from an
2291 * unbounded recursion. Sad times.
2293 * As for the code gen check, we only want to have a blanket assumption that
2294 * a statement we don't recognize is an inline definition when we're in
2295 * code generation mode, since typechecking runs with env.codegen set to
2296 * false, and typechecking needs to support ASTs with missing nodes to
2297 * support IDE features, such as autocomplete.
2299 let outer_max_depth = env.max_depth in
2300 let () = env.max_depth <- outer_max_depth - 1 in
2301 let result =
2302 match pDef node env with
2303 | [def] -> pos, Def_inline def
2304 | _ -> failwith "This should be impossible; inline definition was list."
2306 let () = env.max_depth <- outer_max_depth in
2307 result
2308 | _ -> missing_syntax ?fallback:(Some (Pos.none,Noop)) "statement" node env in
2309 pop_docblock ();
2310 result
2312 and lift_awaits_in_statement env pos f =
2313 let (lifted_awaits, result) =
2314 with_new_statement_scope env f in
2316 match lifted_awaits with
2317 | None -> result
2318 | Some lifted_awaits ->
2319 (* lifted awaits are accumulated in reverse *)
2320 let await_all = pos, Awaitall (List.rev lifted_awaits.awaits) in
2321 pos, Block [await_all; result]
2323 and is_hashbang text =
2324 match Syntax.extract_text text with
2325 | None -> false
2326 | Some text ->
2327 let count = List.length @@ String_utils.split_on_newlines text in
2328 count = 1 && Str.string_match hashbang text 0 && String.equal (Str.matched_string text) text
2330 and pMarkup node env =
2331 match syntax node with
2332 | MarkupSection { markup_prefix; markup_text; markup_expression; _ } ->
2333 let pos = pPos node env in
2334 if env.is_hh_file then
2335 if (is_missing markup_prefix) && not env.has_dot_hack_extension &&
2336 (width markup_text) > 0 && not (is_hashbang markup_text) then
2337 raise_parsing_error env (`Node node) SyntaxError.error1001
2338 else if env.has_dot_hack_extension && not (is_missing markup_prefix) then
2339 raise_parsing_error env (`Node node) SyntaxError.error1060
2340 else if (token_kind markup_prefix) = Some TK.QuestionGreaterThan then
2341 raise_parsing_error env (`Node node) SyntaxError.error2067;
2342 let expr =
2343 match syntax markup_expression with
2344 | Missing -> None
2345 | ExpressionStatement {
2346 expression_statement_expression = e
2347 ; _} -> Some (pExpr e env)
2348 | _ -> failwith "expression expected"
2350 pos, Markup ((pos, text markup_text), expr)
2351 | _ -> failwith "invalid node"
2353 and pBreak_or_continue_level env level =
2354 mpOptional pExpr level env
2356 and pTConstraintTy : hint parser = fun node ->
2357 match syntax node with
2358 | TypeConstraint { constraint_type; _ } -> pHint constraint_type
2359 | _ -> missing_syntax "type constraint" node
2361 and pTConstraint : (constraint_kind * hint) parser = fun node env ->
2362 match syntax node with
2363 | TypeConstraint { constraint_keyword; constraint_type } ->
2364 ( (match token_kind constraint_keyword with
2365 | Some TK.As -> Constraint_as
2366 | Some TK.Super -> Constraint_super
2367 | Some TK.Equal -> Constraint_eq
2368 | _ -> missing_syntax "constraint operator" constraint_keyword env
2370 , pHint constraint_type env
2372 | _ -> missing_syntax "type constraint" node env
2374 and pTParaml ?(is_class=false): tparam list parser = fun node env ->
2375 let pTParam : tparam parser = fun node env ->
2376 match syntax node with
2377 | TypeParameter
2378 { type_attribute_spec; type_reified; type_variance; type_name; type_constraints } ->
2379 let attributes = pUserAttributes env type_attribute_spec in
2380 let is_reified = not @@ is_missing type_reified in
2381 if is_class && is_reified then
2382 env.cls_reified_generics := SSet.add (text type_name) !(env.cls_reified_generics);
2383 let variance = match token_kind type_variance with
2384 | Some TK.Plus -> Covariant
2385 | Some TK.Minus -> Contravariant
2386 | _ -> Invariant in
2387 if is_reified && variance <> Invariant then
2388 raise_parsing_error env (`Node node) SyntaxError.non_invariant_reified_generic;
2389 { tp_variance = variance
2390 ; tp_name = pos_name type_name env
2391 ; tp_constraints = couldMap ~f:pTConstraint type_constraints env
2392 ; tp_reified = is_reified
2393 ; tp_user_attributes = attributes
2395 | _ -> missing_syntax "type parameter" node env
2397 match syntax node with
2398 | Missing -> []
2399 | TypeParameters { type_parameters_parameters; _ } ->
2400 couldMap ~f:pTParam type_parameters_parameters env
2401 | _ -> missing_syntax "type parameter list" node env
2403 and pFunHdr check_modifier : fun_hdr parser = fun node env ->
2404 match syntax node with
2405 | FunctionDeclarationHeader
2406 { function_modifiers
2407 ; function_name
2408 ; function_where_clause
2409 ; function_type_parameter_list
2410 ; function_parameter_list
2411 ; function_type
2412 ; _ } ->
2413 let is_autoload =
2414 String.lowercase @@ (text function_name)
2415 = Naming_special_names.SpecialFunctions.autoload in
2416 if is_missing function_name then
2417 raise_parsing_error env (`Node function_name) SyntaxError.empty_method_name;
2418 let num_params = List.length (syntax_to_list_no_separators function_parameter_list) in
2419 if is_autoload && num_params > 1 then
2420 raise_parsing_error env (`Node node) SyntaxError.autoload_takes_one_argument;
2421 let modifiers = pModifiers check_modifier function_modifiers env in
2422 let fh_parameters = couldMap ~f:pFunParam function_parameter_list env in
2423 let fh_return_type = mpOptional pHint function_type env in
2424 let fh_suspension_kind =
2425 mk_suspension_kind_ node env modifiers.has_async modifiers.has_coroutine in
2426 let fh_name = pos_name function_name env in
2427 let fh_constrs =
2428 match syntax function_where_clause with
2429 | Missing -> []
2430 | WhereClause { where_clause_constraints; _ } ->
2431 let rec f node =
2432 match syntax node with
2433 | ListItem { list_item; _ } -> f list_item
2434 | WhereConstraint
2435 { where_constraint_left_type
2436 ; where_constraint_operator
2437 ; where_constraint_right_type
2438 } ->
2439 let l = pHint where_constraint_left_type env in
2440 let o =
2441 match syntax where_constraint_operator with
2442 | Token token when Token.kind token = TK.Equal ->
2443 Constraint_eq
2444 | Token token when Token.kind token = TK.As -> Constraint_as
2445 | Token token when Token.kind token = TK.Super -> Constraint_super
2446 | _ -> missing_syntax "constraint operator" where_constraint_operator env
2448 let r = pHint where_constraint_right_type env in
2449 (l,o,r)
2450 | _ -> missing_syntax "where constraint" node env
2452 List.map ~f (syntax_node_to_list where_clause_constraints)
2453 | _ -> missing_syntax "function header constraints" node env
2455 let fh_type_parameters = pTParaml function_type_parameter_list env in
2456 let fh_param_modifiers =
2457 List.filter ~f:(fun p -> Option.is_some p.param_modifier) fh_parameters
2459 { fh_suspension_kind
2460 ; fh_name
2461 ; fh_constrs
2462 ; fh_type_parameters
2463 ; fh_parameters
2464 ; fh_return_type
2465 ; fh_param_modifiers
2467 | LambdaSignature { lambda_parameters; lambda_type; _ } ->
2468 { empty_fun_hdr with
2469 fh_parameters = couldMap ~f:pFunParam lambda_parameters env
2470 ; fh_return_type = mpOptional pHint lambda_type env
2472 | Token _ -> empty_fun_hdr
2473 | _ -> missing_syntax "function header" node env
2475 and docblock_stack = Caml.Stack.create ()
2477 and extract_docblock = fun node ->
2478 let source_text = leading_text node in
2479 let parse (str : string) : string option =
2480 let length = String.length str in
2481 let mk (start : int) (end_ : int) : string =
2482 String.sub source_text start (end_ - start + 1)
2484 let rec go start state idx : string option =
2485 if idx = length (* finished? *)
2486 then None
2487 else begin
2488 let next = idx + 1 in
2489 match state, str.[idx] with
2490 | `LineCmt, '\n' -> go next `Free next
2491 | `EndEmbedded, '/' -> go next `Free next
2492 | `EndDoc, '/' -> begin match go next `Free next with
2493 | Some doc -> Some doc
2494 | None -> Some (mk start idx)
2496 (* PHP has line comments delimited by a # *)
2497 | `Free, '#' -> go next `LineCmt next
2498 (* All other comment delimiters start with a / *)
2499 | `Free, '/' -> go idx `SawSlash next
2500 (* After a / in trivia, we must see either another / or a * *)
2501 | `SawSlash, '/' -> go next `LineCmt next
2502 | `SawSlash, '*' -> go start `MaybeDoc next
2503 | `MaybeDoc, '*' -> go start `MaybeDoc2 next
2504 | `MaybeDoc, _ -> go start `EmbeddedCmt next
2505 | `MaybeDoc2, '/' -> go next `Free next
2506 (* Doc comments have a space after the second star *)
2507 | `MaybeDoc2, (' ' | '\t' | '\n' | '\r') -> go start `DocComment idx
2508 | `MaybeDoc2, _ -> go start `EmbeddedCmt next
2509 | `DocComment, '*' -> go start `EndDoc next
2510 | `DocComment, _ -> go start `DocComment next
2511 | `EndDoc, _ -> go start `DocComment next
2512 (* A * without a / does not end an embedded comment *)
2513 | `EmbeddedCmt, '*' -> go start `EndEmbedded next
2514 | `EndEmbedded, '*' -> go start `EndEmbedded next
2515 | `EndEmbedded, _ -> go start `EmbeddedCmt next
2516 (* Whitespace skips everywhere else *)
2517 | _, (' ' | '\t' | '\n' | '\r') -> go start state next
2518 (* When scanning comments, anything else is accepted *)
2519 | `LineCmt, _ -> go start state next
2520 | `EmbeddedCmt, _ -> go start state next
2521 (* Anything else; bail *)
2522 | _ -> None
2525 go 0 `Free 0
2526 in (* Now that we have a parser *)
2527 parse (leading_text node)
2529 and extract_and_push_docblock node =
2530 let docblock = extract_docblock node in
2531 Caml.Stack.push docblock docblock_stack
2533 and handle_loop_body pos stmts tail env =
2534 let rec conv acc stmts =
2535 match stmts with
2536 | [] -> List.concat @@ List.rev acc
2537 | { syntax = UsingStatementFunctionScoped
2538 { using_function_await_keyword = await_kw
2539 ; using_function_expression = expression
2540 ; _ }
2541 ; _ } :: rest ->
2542 let body = conv [] rest in
2543 let using = Using {
2544 us_is_block_scoped = false;
2545 us_has_await = not (is_missing await_kw);
2546 us_expr = pExprL expression env;
2547 us_block = body; } in
2548 List.concat @@ List.rev ([pos, using] :: acc)
2549 | h :: rest ->
2550 let h = pStmtUnsafe h env in
2551 conv (h :: acc) rest
2553 let blk = conv [] (as_list stmts) in
2554 begin match List.filter ~f:(fun (_, x) -> x <> Noop) blk @ tail with
2555 | [] -> pos, Block [Pos.none, Noop]
2556 | blk -> pos, Block blk
2559 and pop_docblock () =
2561 let _ = Caml.Stack.pop docblock_stack in ()
2562 with
2563 | Caml.Stack.Empty -> ()
2565 and top_docblock () =
2567 Caml.Stack.top docblock_stack
2568 with
2569 | Caml.Stack.Empty -> None
2571 and pClassElt : class_elt list parser = fun node env ->
2572 let doc_comment_opt = extract_docblock node in
2573 let pClassElt_ = function
2574 | ConstDeclaration
2575 { const_abstract; const_type_specifier; const_declarators; _ } ->
2576 let ty = mpOptional pHint const_type_specifier env in
2577 let res =
2578 couldMap const_declarators env ~f:begin function
2579 | { syntax = ConstantDeclarator
2580 { constant_declarator_name; constant_declarator_initializer }
2581 ; _ } -> fun env ->
2582 ( pos_name constant_declarator_name env
2583 (* TODO: Parse error when const is abstract and has inits *)
2584 , if is_missing const_abstract
2585 then mpOptional pSimpleInitializer constant_declarator_initializer env
2586 else None
2588 | node -> missing_syntax "constant declarator" node env
2591 let rec aux absts concrs = function
2592 | (id, None ) :: xs -> aux (AbsConst (ty, id) :: absts) concrs xs
2593 | (id, Some x) :: xs -> aux absts ((id, x) :: concrs) xs
2594 | [] when concrs = [] -> List.rev absts
2595 | [] -> Const (ty, List.rev concrs) :: List.rev absts
2597 aux [] [] res
2598 | TypeConstDeclaration
2599 { type_const_attribute_spec
2600 ; type_const_abstract
2601 ; type_const_name
2602 ; type_const_type_parameters
2603 ; type_const_type_constraint
2604 ; type_const_type_specifier
2605 ; _ } ->
2606 if not @@ is_missing (type_const_type_parameters)
2607 then raise_parsing_error env (`Node node) SyntaxError.tparams_in_tconst;
2608 [ TypeConst
2609 { tconst_user_attributes = pUserAttributes env type_const_attribute_spec
2610 ; tconst_abstract = not (is_missing type_const_abstract)
2611 ; tconst_name = pos_name type_const_name env
2612 ; tconst_tparams = pTParaml type_const_type_parameters env
2613 ; tconst_constraint = mpOptional pTConstraintTy type_const_type_constraint env
2614 ; tconst_type = mpOptional pHint type_const_type_specifier env
2615 ; tconst_span = pPos node env
2618 | PropertyDeclaration
2619 { property_attribute_spec
2620 ; property_modifiers
2621 ; property_type
2622 ; property_declarators
2624 } ->
2625 (* TODO: doc comments do not have to be at the beginning, they can go in
2626 * the middle of the declaration, to be associated with individual
2627 * properties, right now we don't handle this *)
2628 let doc_comment_opt = extract_docblock node in
2629 let typecheck = is_typechecker env in (* so we don't capture the env in the closure *)
2630 let check_modifier node =
2631 if is_final node
2632 then raise_parsing_error env (`Node node) SyntaxError.final_property;
2633 if typecheck && is_var node
2634 then raise_parsing_error env (`Node node) SyntaxError.var_property in
2636 [ ClassVars
2637 { cv_kinds = pKinds check_modifier property_modifiers env
2638 ; cv_hint = mpOptional pHint property_type env
2639 ; cv_is_promoted_variadic = false
2640 ; cv_names = couldMap property_declarators env ~f:begin fun node env ->
2641 match syntax node with
2642 | PropertyDeclarator { property_name; property_initializer } ->
2643 ( let _, n as name = pos_name property_name env in
2644 ( pPos node env
2645 , ( if String.length n > 0 && n.[0] = '$'
2646 then drop_pstr 1 name
2647 else name
2649 , mpOptional pSimpleInitializer property_initializer env
2652 | _ -> missing_syntax "property declarator" node env
2654 ; cv_doc_comment = if env.quick_mode then None else doc_comment_opt
2655 ; cv_user_attributes = List.concat @@
2656 couldMap ~f:pUserAttribute property_attribute_spec env
2659 | MethodishDeclaration
2660 { methodish_attribute
2661 ; methodish_function_decl_header = {
2662 syntax = FunctionDeclarationHeader h; _
2663 } as header
2664 ; methodish_function_body
2665 ; _ } ->
2666 let classvar_init : fun_param -> stmt * class_elt = fun param ->
2667 let p, _ as cvname = drop_pstr 1 param.param_id in (* Drop the '$' *)
2668 let span =
2669 match param.param_expr with
2670 | Some (pos_end, _) -> Pos.btw p pos_end
2671 | None -> p
2673 ( (p, Expr (p, Binop (Eq None,
2674 (p, Obj_get((p, Lvar (p, "$this")), (p, Id cvname), OG_nullthrows)),
2675 (p, Lvar param.param_id))
2677 , ClassVars
2678 { cv_kinds = Option.to_list param.param_modifier
2679 ; cv_hint = param.param_hint
2680 ; cv_is_promoted_variadic = param.param_is_variadic
2681 ; cv_names = [span, cvname, None]
2682 ; cv_doc_comment = None
2683 ; cv_user_attributes = param.param_user_attributes
2687 let hdr = pFunHdr (fun _ -> ()) header env in
2688 if snd hdr.fh_name = "__construct" && not (List.is_empty hdr.fh_type_parameters) then
2689 raise_parsing_error env (`Node header) SyntaxError.no_generics_on_constructors;
2690 let member_init, member_def =
2691 List.unzip @@
2692 List.filter_map hdr.fh_parameters ~f:(fun p ->
2693 Option.map ~f: (fun _ -> classvar_init p) p.param_modifier
2696 let pBody = fun node env ->
2697 let body = pFunctionBody node env in
2698 let member_init =
2699 if env.codegen
2700 then List.rev member_init
2701 else member_init
2703 member_init @ body
2705 let kind = pKinds (fun _ -> ()) h.function_modifiers env in
2706 env.in_static_method := (List.exists kind ~f:(function Static -> true | _ -> false));
2707 let body, body_has_yield = mpYielding pBody methodish_function_body env in
2708 env.in_static_method := false;
2709 let is_external = is_semicolon methodish_function_body in
2710 member_def @ [Method
2711 { m_kind = kind
2712 ; m_tparams = hdr.fh_type_parameters
2713 ; m_constrs = hdr.fh_constrs
2714 ; m_name = hdr.fh_name
2715 ; m_params = hdr.fh_parameters
2716 ; m_body = body
2717 ; m_user_attributes = pUserAttributes env methodish_attribute
2718 ; m_ret = hdr.fh_return_type
2719 ; m_span = pFunction node env
2720 ; m_fun_kind = mk_fun_kind hdr.fh_suspension_kind body_has_yield
2721 ; m_doc_comment = doc_comment_opt
2722 ; m_external = is_external (* see f_external above for context *)
2724 | MethodishTraitResolution
2725 { methodish_trait_attribute
2726 ; methodish_trait_function_decl_header = {
2727 syntax = FunctionDeclarationHeader h; _
2728 } as header
2729 ; methodish_trait_name; _
2730 } ->
2731 let hdr = pFunHdr (fun _ -> ()) header env in
2732 let kind = pKinds (fun _ -> ()) h.function_modifiers env in
2733 let qualifier, name =
2734 match syntax methodish_trait_name with
2735 | ScopeResolutionExpression
2736 { scope_resolution_qualifier; scope_resolution_name; _ } ->
2737 pHint scope_resolution_qualifier env,
2738 pos_name scope_resolution_name env
2739 | _ -> missing_syntax "trait method redeclaration item" node env
2741 [MethodTraitResolution
2742 { mt_kind = kind
2743 ; mt_tparams = hdr.fh_type_parameters
2744 ; mt_constrs = hdr.fh_constrs
2745 ; mt_name = hdr.fh_name
2746 ; mt_params = hdr.fh_parameters
2747 ; mt_user_attributes = pUserAttributes env methodish_trait_attribute
2748 ; mt_ret = hdr.fh_return_type
2749 ; mt_fun_kind = mk_fun_kind hdr.fh_suspension_kind false
2750 ; mt_trait = qualifier
2751 ; mt_method = name
2753 | TraitUseConflictResolution
2754 { trait_use_conflict_resolution_names
2755 ; trait_use_conflict_resolution_clauses
2757 } ->
2758 let pTraitUseConflictResolutionItem node env =
2759 match syntax node with
2760 | TraitUsePrecedenceItem
2761 { trait_use_precedence_item_name = name
2762 ; trait_use_precedence_item_removed_names = removed_names
2764 } ->
2765 let qualifier, name =
2766 match syntax name with
2767 | ScopeResolutionExpression
2768 { scope_resolution_qualifier; scope_resolution_name; _ } ->
2769 pos_name scope_resolution_qualifier env,
2770 pos_name scope_resolution_name env
2771 | _ -> missing_syntax "trait use precedence item" node env
2773 let removed_names =
2774 couldMap ~f:(fun n _e -> pos_name n env) removed_names env
2776 ClassUsePrecedence (qualifier, name, removed_names)
2777 | TraitUseAliasItem
2778 { trait_use_alias_item_aliasing_name = aliasing_name
2779 ; trait_use_alias_item_modifiers = modifiers
2780 ; trait_use_alias_item_aliased_name = aliased_name
2782 } ->
2783 let qualifier, name =
2784 match syntax aliasing_name with
2785 | ScopeResolutionExpression
2786 { scope_resolution_qualifier; scope_resolution_name; _ } ->
2787 Some (pos_name scope_resolution_qualifier env),
2788 pos_name scope_resolution_name env
2789 | _ -> None, pos_name aliasing_name env
2791 let modifiers = pKinds (fun _ -> ()) modifiers env in
2792 List.iter modifiers ~f:(fun modifier ->
2793 match modifier with
2794 | Public | Private | Protected | Final -> ();
2795 | _ ->
2796 raise_parsing_error env (`Node node)
2797 SyntaxError.trait_alias_rule_allows_only_final_and_visibility_modifiers
2799 let is_visibility = function
2800 | Public | Private | Protected -> true
2801 | _ -> false in
2802 let modifiers =
2803 if List.is_empty modifiers || List.exists modifiers ~f:is_visibility
2804 then modifiers
2805 else Public :: modifiers in
2806 let aliased_name =
2807 Option.some_if (not (is_missing aliased_name)) (pos_name aliased_name env)
2809 ClassUseAlias (qualifier, name, aliased_name, modifiers)
2810 | _ -> missing_syntax "trait use conflict resolution item" node env
2812 (couldMap ~f:(fun n e ->
2813 ClassUse (pHint n e)) trait_use_conflict_resolution_names env)
2814 @ (couldMap ~f:pTraitUseConflictResolutionItem
2815 trait_use_conflict_resolution_clauses env)
2816 | TraitUse { trait_use_names; _ } ->
2817 couldMap ~f:(fun n e -> ClassUse (pHint n e)) trait_use_names env
2818 | RequireClause { require_kind; require_name; _ } ->
2819 [ ClassTraitRequire
2820 ( (match token_kind require_kind with
2821 | Some TK.Implements -> MustImplement
2822 | Some TK.Extends -> MustExtend
2823 | _ -> missing_syntax "trait require kind" require_kind env
2825 , pHint require_name env
2828 | XHPClassAttributeDeclaration { xhp_attribute_attributes; _ } ->
2829 let pXHPAttr node env =
2830 match syntax node with
2831 | XHPClassAttribute
2832 { xhp_attribute_decl_type = ty
2833 ; xhp_attribute_decl_name = name
2834 ; xhp_attribute_decl_initializer = init
2835 ; xhp_attribute_decl_required = req
2836 } ->
2837 let (p, name) = pos_name name env in
2838 begin match syntax ty with
2839 | TypeConstant _ when is_typechecker env ->
2840 raise_parsing_error env (`Node ty) (SyntaxError.xhp_class_attribute_type_constant)
2841 | _ -> ()
2842 end;
2843 let pos = if is_missing init then p else Pos.btw p (pPos init env) in
2844 (* we can either have a typehint or an xhp enum *)
2845 let hint, enum = match syntax ty with
2846 | XHPEnumType { xhp_enum_optional; xhp_enum_values; _ } ->
2847 let p = pPos ty env in
2848 let opt = not (is_missing xhp_enum_optional) in
2849 let vals = couldMap ~f:pExpr xhp_enum_values env in
2850 None, Some (p, opt, vals)
2851 | _ -> Some (pHint ty env), None
2853 XhpAttr
2854 ( hint
2855 , (pos, (p, ":" ^ name), mpOptional pSimpleInitializer init env)
2856 , not (is_missing req)
2857 , enum
2859 | XHPSimpleClassAttribute { xhp_simple_class_attribute_type = attr } ->
2860 XhpAttrUse (pPos attr env, Happly (pos_name attr env, []))
2861 | Token _ ->
2862 XhpAttrUse (pPos node env, Happly (pos_name node env, []))
2863 | _ -> missing_syntax "XHP attribute" node env
2865 couldMap ~f:pXHPAttr xhp_attribute_attributes env
2866 | XHPChildrenDeclaration { xhp_children_expression; _; } ->
2867 let p = pPos node env in
2868 [ XhpChild (p, pXhpChild xhp_children_expression env) ]
2869 | XHPCategoryDeclaration { xhp_category_categories = cats; _ } ->
2870 let p = pPos node env in
2871 let pNameSansPercent node _env = drop_pstr 1 (pos_name node env) in
2872 [ XhpCategory (p, couldMap ~f:pNameSansPercent cats env) ]
2873 | _ -> missing_syntax "class element" node env
2875 try pClassElt_ (syntax node) with
2876 | API_Missing_syntax (expecting, env, node) when env.fail_open ->
2877 let () = log_missing ~caught:true ~env ~expecting node in
2879 and pXhpChild : xhp_child parser = fun node env ->
2880 match syntax node with
2881 | Token _ -> ChildName (pos_name node env)
2882 | PostfixUnaryExpression { postfix_unary_operand; postfix_unary_operator;} ->
2883 let operand = pXhpChild postfix_unary_operand env in
2884 let operator =
2885 begin
2886 match token_kind postfix_unary_operator with
2887 | Some TK.Question -> ChildQuestion
2888 | Some TK.Plus -> ChildPlus
2889 | Some TK.Star -> ChildStar
2890 | _ -> missing_syntax "xhp children operator" node env
2891 end in
2892 ChildUnary(operand, operator)
2893 | BinaryExpression
2894 { binary_left_operand; binary_right_operand; _ } ->
2895 let left = pXhpChild binary_left_operand env in
2896 let right = pXhpChild binary_right_operand env in
2897 ChildBinary(left, right)
2898 | XHPChildrenParenthesizedList {xhp_children_list_xhp_children; _} ->
2899 let children = as_list xhp_children_list_xhp_children in
2900 let children = List.map ~f:(fun x -> pXhpChild x env) children in
2901 ChildList children
2902 | _ -> missing_syntax "xhp children" node env
2905 (*****************************************************************************(
2906 * Parsing definitions (AST's `def`)
2907 )*****************************************************************************)
2908 and pNamespaceUseClause ~prefix env kind node =
2909 match syntax node with
2910 | NamespaceUseClause
2911 { namespace_use_name = name
2912 ; namespace_use_alias = alias
2913 ; namespace_use_clause_kind = clause_kind
2914 ; _ } ->
2915 let p, n as name =
2916 match prefix, pos_name name env with
2917 | None, (p, n) -> (p, n)
2918 | Some prefix, (p, n) -> p, (snd @@ pos_name prefix env) ^ n
2920 let x = Str.search_forward (namespace_use) n 0 in
2921 let key = drop_pstr x name in
2922 let kind = if is_missing clause_kind then kind else clause_kind in
2923 let alias = if is_missing alias then key else pos_name alias env in
2924 begin match String.lowercase (snd alias) with
2925 | "null" | "true" | "false" -> env.saw_std_constant_redefinition <- true
2926 | _ -> ()
2927 end;
2928 let kind =
2929 match syntax kind with
2930 | Token token when Token.kind token = TK.Namespace -> NSNamespace
2931 | Token token when Token.kind token = TK.Type -> NSClass
2932 | Token token when Token.kind token = TK.Function -> NSFun
2933 | Token token when Token.kind token = TK.Const -> NSConst
2934 | Missing -> NSClassAndNamespace
2935 | _ -> missing_syntax "namespace use kind" kind env
2937 ( kind
2938 , (p, if String.length n > 0 && n.[0] = '\\' then n else "\\" ^ n)
2939 , alias
2941 | _ -> missing_syntax "namespace use clause" node env
2943 and pDef : def list parser = fun node env ->
2944 let doc_comment_opt = extract_docblock node in
2945 match syntax node with
2946 | FunctionDeclaration
2947 { function_attribute_spec; function_declaration_header; function_body } ->
2948 let env = non_tls env in
2949 let check_modifier node =
2950 raise_parsing_error env (`Node node) (SyntaxError.function_modifier (text node)) in
2951 let hdr = pFunHdr check_modifier function_declaration_header env in
2952 let is_external = is_semicolon function_body in
2953 let block, yield =
2954 if is_external then [], false else
2955 mpYielding pFunctionBody function_body env
2957 [ Fun
2958 { (fun_template yield node hdr.fh_suspension_kind env) with
2959 f_tparams = hdr.fh_type_parameters
2960 ; f_ret = hdr.fh_return_type
2961 ; f_constrs = hdr.fh_constrs
2962 ; f_name = hdr.fh_name
2963 ; f_params = hdr.fh_parameters
2964 ; f_body =
2965 begin
2966 let containsUNSAFE node =
2967 let tokens = all_tokens node in
2968 let has_unsafe t = Token.has_trivia_kind t TriviaKind.Unsafe in
2969 List.exists ~f:has_unsafe tokens
2971 match block with
2972 | [p, Noop] when containsUNSAFE function_body -> [p, Unsafe]
2973 | b -> b
2975 ; f_user_attributes = pUserAttributes env function_attribute_spec
2976 ; f_doc_comment = doc_comment_opt
2977 ; f_external = is_external
2979 | ClassishDeclaration
2980 { classish_attribute = attr
2981 ; classish_modifiers = mods
2982 ; classish_keyword = kw
2983 ; classish_name = name
2984 ; classish_type_parameters = tparaml
2985 ; classish_extends_list = exts
2986 ; classish_implements_list = impls
2987 ; classish_body =
2988 { syntax = ClassishBody { classish_body_elements = elts; _ }; _ }
2989 ; _ } ->
2990 let env = non_tls env in
2991 let c_mode = mode_annotation env.fi_mode in
2992 let c_user_attributes = pUserAttributes env attr in
2993 let c_file_attributes = [] in
2994 let kinds = pKinds (fun _ -> ()) mods env in
2995 let c_final = List.mem kinds Final ~equal:(=) in
2996 let c_is_xhp =
2997 match token_kind name with
2998 | Some (TK.XHPElementName | TK.XHPClassName) -> true
2999 | _ -> false
3001 let c_name = pos_name name env in
3002 env.cls_reified_generics := SSet.empty;
3003 let c_tparams = pTParaml ~is_class:true tparaml env in
3004 let c_extends = couldMap ~f:pHint exts env in
3005 env.parent_is_reified := (match c_extends with
3006 | (_, Happly (_, hl)) :: _ ->
3007 List.exists hl ~f:(function _, Hreified _ -> true | _ -> false)
3008 | _ -> false);
3009 let c_implements = couldMap ~f:pHint impls env in
3010 let c_body =
3011 let rec aux acc ns =
3012 match ns with
3013 | [] -> acc
3014 | n :: ns ->
3015 let elt = pClassElt n env in
3016 aux (elt :: acc) ns
3018 List.concat @@ List.rev (aux [] (as_list elts))
3020 let c_namespace = Namespace_env.empty env.parser_options in
3021 let c_enum = None in
3022 let c_span = pPos node env in
3023 let c_kind =
3024 let is_abs = List.mem kinds Abstract ~equal:(=) in
3025 match token_kind kw with
3026 | Some TK.Class when is_abs -> Cabstract
3027 | Some TK.Class -> Cnormal
3028 | Some TK.Interface -> Cinterface
3029 | Some TK.Trait -> Ctrait
3030 | Some TK.Enum -> Cenum
3031 | _ -> missing_syntax "class kind" kw env
3033 let c_doc_comment = doc_comment_opt in
3034 [ Class
3035 { c_mode
3036 ; c_user_attributes
3037 ; c_file_attributes
3038 ; c_final
3039 ; c_is_xhp
3040 ; c_name
3041 ; c_tparams
3042 ; c_extends
3043 ; c_implements
3044 ; c_body
3045 ; c_namespace
3046 ; c_enum
3047 ; c_span
3048 ; c_kind
3049 ; c_doc_comment
3051 | ConstDeclaration
3052 { const_type_specifier = ty
3053 ; const_declarators = decls
3054 ; _ } ->
3055 let declarations = List.map ~f:syntax (as_list decls) in
3056 let f = function
3057 | ConstantDeclarator
3058 { constant_declarator_name = name
3059 ; constant_declarator_initializer = init
3061 -> Constant
3062 { cst_mode = mode_annotation env.fi_mode
3063 ; cst_kind = Cst_const
3064 ; cst_name = pos_name name env
3065 ; cst_type = mpOptional pHint ty env
3066 ; cst_value = pSimpleInitializer init env
3067 ; cst_namespace = Namespace_env.empty env.parser_options
3068 ; cst_span = pPos node env
3070 | _ -> missing_syntax "constant declaration" decls env
3072 List.map ~f declarations
3073 | AliasDeclaration
3074 { alias_attribute_spec = attr
3075 ; alias_keyword = kw
3076 ; alias_name = name
3077 ; alias_generic_parameter = tparams
3078 ; alias_constraint = constr
3079 ; alias_type = hint
3080 ; _ } ->
3081 [ Typedef
3082 { t_id = pos_name name env
3083 ; t_tparams = pTParaml tparams env
3084 ; t_constraint = Option.map ~f:snd @@
3085 mpOptional pTConstraint constr env
3086 ; t_user_attributes = List.concat @@
3087 List.map ~f:(fun x -> pUserAttribute x env) (as_list attr)
3088 ; t_namespace = Namespace_env.empty env.parser_options
3089 ; t_mode = mode_annotation env.fi_mode
3090 ; t_kind =
3091 match token_kind kw with
3092 | Some TK.Newtype -> NewType (pHint hint env)
3093 | Some TK.Type -> Alias (pHint hint env)
3094 | _ -> missing_syntax "kind" kw env
3096 | EnumDeclaration
3097 { enum_attribute_spec = attrs
3098 ; enum_name = name
3099 ; enum_base = base
3100 ; enum_type = constr
3101 ; enum_enumerators = enums
3102 ; _ } ->
3103 let pEnumerator node =
3104 match syntax node with
3105 | Enumerator { enumerator_name = name; enumerator_value = value; _ } ->
3106 fun env -> Const (None, [pos_name name env, pExpr value env])
3107 | _ -> missing_syntax "enumerator" node
3109 [ Class
3110 { c_mode = mode_annotation env.fi_mode
3111 ; c_user_attributes = pUserAttributes env attrs
3112 ; c_file_attributes = []
3113 ; c_final = false
3114 ; c_kind = Cenum
3115 ; c_is_xhp = false
3116 ; c_name = pos_name name env
3117 ; c_tparams = []
3118 ; c_extends = []
3119 ; c_implements = []
3120 ; c_body = couldMap enums env ~f:pEnumerator
3121 ; c_namespace = Namespace_env.empty env.parser_options
3122 ; c_span = pPos node env
3123 ; c_enum = Some
3124 { e_base = pHint base env
3125 ; e_constraint = mpOptional pTConstraintTy constr env
3127 ; c_doc_comment = doc_comment_opt
3129 | InclusionDirective
3130 { inclusion_expression
3131 ; inclusion_semicolon = _
3132 } when env.fi_mode <> FileInfo.Mdecl && env.fi_mode <> FileInfo.Mphp
3133 || env.codegen ->
3134 let expr = pExpr inclusion_expression env in
3135 [ Stmt (pPos node env, Expr expr) ]
3136 | NamespaceDeclaration
3137 { namespace_name = name
3138 ; namespace_body =
3139 { syntax = NamespaceBody { namespace_declarations = decls; _ }; _ }
3140 ; _ } ->
3141 let env = non_tls env in
3142 [ Namespace
3143 ( pos_name name env
3144 , List.concat_map ~f:(fun x -> pDef x env) (as_list decls)
3146 | NamespaceDeclaration { namespace_name = name; _ } ->
3147 [ Namespace (pos_name name env, []) ]
3148 | NamespaceGroupUseDeclaration
3149 { namespace_group_use_kind = kind
3150 ; namespace_group_use_prefix = prefix
3151 ; namespace_group_use_clauses = clauses
3152 ; _ } ->
3153 let f = pNamespaceUseClause env kind ~prefix:(Some prefix) in
3154 [ NamespaceUse (List.map ~f (as_list clauses)) ]
3155 | NamespaceUseDeclaration
3156 { namespace_use_kind = kind
3157 ; namespace_use_clauses = clauses
3158 ; _ } ->
3159 let f = pNamespaceUseClause env kind ~prefix:None in
3160 [ NamespaceUse (List.map ~f (as_list clauses)) ]
3161 | FileAttributeSpecification _ ->
3162 [ FileAttributes
3163 { fa_user_attributes = pUserAttribute node env
3164 ; fa_namespace = Namespace_env.empty env.parser_options
3166 | _ when env.fi_mode = FileInfo.Mdecl || env.fi_mode = FileInfo.Mphp
3167 && not env.codegen -> []
3168 | _ -> [ Stmt (pStmt node env) ]
3169 let pProgram : program parser = fun node env ->
3170 let rec post_process program acc =
3171 let span (p : 'a -> bool) =
3172 let rec go yes = function
3173 | (x::xs) when p x -> go (x::yes) xs
3174 | xs -> (List.rev yes, xs)
3175 in go []
3177 let not_namespace = function
3178 | Namespace _ -> false
3179 | _ -> true
3181 match program with
3182 | [] -> List.rev acc
3183 | (Namespace (n, [])::el) ->
3184 let body, remainder = span not_namespace el in
3185 let body = post_process body [] in
3186 post_process remainder (Namespace (n, body) :: acc)
3187 | (Namespace (n, il)::el) ->
3188 let result = post_process il [] in
3189 post_process el (Namespace (n, result) :: acc)
3190 | (Stmt (_, Noop) :: el) -> post_process el acc
3191 | ((Stmt (_, Expr (pos, (Call
3192 ( (_, (Id (_, "define")))
3193 , []
3194 , [ (_, (String name))
3195 ; value
3197 , []
3199 )))) :: el) when not(ParserOptions.disable_define env.parser_options) ->
3200 let const = Constant
3201 { cst_mode = mode_annotation env.fi_mode
3202 ; cst_kind = Cst_define
3203 ; cst_name = pos, name
3204 ; cst_type = None
3205 ; cst_value = value
3206 ; cst_namespace = Namespace_env.empty env.parser_options
3207 ; cst_span = pos
3208 } in
3209 post_process el (const :: acc)
3210 | (Stmt (_, Markup _) as e)::el
3211 | (Stmt (_, Expr (_, Import _)) as e)::el ->
3212 post_process el (e :: acc)
3213 (* Toplevel statements not allowed in strict mode *)
3214 | (Stmt (p, _) as e)::el
3215 when (env.keep_errors) && (not env.codegen) && env.fi_mode = FileInfo.Mstrict ->
3216 (* We've already lowered at this point, so raise_parsing_error doesn't
3217 really fit. This is only a typechecker error anyway, so just add it
3218 directly *)
3219 Errors.parsing_error (p, SyntaxError.toplevel_statements);
3220 post_process el (e :: acc)
3221 | (e::el) -> post_process el (e :: acc)
3224 (* The list of top-level things in a file is somewhat special. *)
3225 let rec aux env acc = function
3226 | []
3227 (* EOF happens only as the last token in the list. *)
3228 | [{ syntax = EndOfFile _; _ }]
3229 -> List.concat (List.rev acc)
3230 (* HaltCompiler stops processing the list *)
3231 | { syntax = ExpressionStatement
3232 { expression_statement_expression =
3233 { syntax = HaltCompilerExpression _ ; _ } ; _ } ; _ } as cur_node :: nodel
3235 (* If we saw COMPILER_HALT_OFFSET, calculate the position of HALT_COMPILER *)
3236 if !(env.saw_compiler_halt_offset) <> None then
3237 begin
3238 let local_ignore_pos = env.ignore_pos in
3239 let () = env.ignore_pos <- false in
3240 let pos = pPos cur_node env in
3241 (* __COMPILER_HALT_OFFSET__ takes value equal to halt_compiler's end position *)
3242 let s = Pos.end_cnum pos in
3243 let () = env.saw_compiler_halt_offset := Some s in
3244 env.ignore_pos <- local_ignore_pos
3245 end;
3246 aux env acc nodel
3247 (* There's an incompatibility between the Full-Fidelity (FF) and the AST view
3248 * of the world; `define` is an *expression* in FF, but a *definition* in AST.
3249 * Luckily, `define` only happens at the level of definitions.
3251 | { syntax = ExpressionStatement
3252 { expression_statement_expression =
3253 { syntax = DefineExpression
3254 { define_keyword; define_argument_list = args; _ }
3255 ; _ }
3256 ; _ }
3257 ; _ } as cur_node :: nodel ->
3258 let def =
3259 match List.map ~f:(fun x -> pExpr x env) (as_list args) with
3260 | [ pos, String name; e ] -> Constant
3261 { cst_mode = mode_annotation env.fi_mode
3262 ; cst_kind = Cst_define
3263 ; cst_name = pos, name
3264 ; cst_type = None
3265 ; cst_value = e
3266 ; cst_namespace = Namespace_env.empty env.parser_options
3267 ; cst_span = pPos cur_node env
3269 | args ->
3270 let name = pos_name define_keyword env in
3271 Stmt (pPos cur_node env,
3272 Expr (fst name, Call ((fst name, Id name), [], args, [])))
3274 aux env ([def] :: acc) nodel
3275 | node :: nodel ->
3276 let acc =
3277 match pDef node env with
3278 | exception API_Missing_syntax (expecting, env, node)
3279 when env.fail_open ->
3280 let () = log_missing ~caught:true ~env ~expecting node in
3282 | def -> def :: acc
3284 aux env acc nodel
3286 let nodes = as_list node in
3287 let nodes = aux env [] nodes in
3288 post_process nodes []
3290 let pScript node env =
3291 match syntax node with
3292 | Script { script_declarations; _ } -> pProgram script_declarations env
3293 | _ -> missing_syntax "script" node env
3295 (* The full fidelity parser considers all comments "simply" trivia. Some
3296 * comments have meaning, though. This meaning can either be relevant for the
3297 * type checker (like UNSAFE, HH_FIXME, etc.), but also for other uses, like
3298 * Codex, where comments are used for documentation generation.
3300 * Inlining the scrape for comments in the lowering code would be prohibitively
3301 * complicated, but a separate pass is fine.
3304 type fixmes = Pos.t IMap.t IMap.t
3305 type scoured_comment = Pos.t * comment
3306 type scoured_comments = scoured_comment list
3307 type accumulator = scoured_comments * fixmes
3309 let scour_comments
3310 (path : Relative_path.t)
3311 (source_text : SourceText.t)
3312 ~(collect_fixmes: bool)
3313 ~(include_line_comments: bool)
3314 (tree : node)
3315 (env : env)
3316 : accumulator =
3317 let pos_of_offset = SourceText.relative_pos path source_text in
3318 let go (node : node) (cmts, fm as acc : accumulator) (t : Trivia.t)
3319 : accumulator =
3320 match Trivia.kind t with
3321 | TriviaKind.WhiteSpace
3322 | TriviaKind.EndOfLine
3323 | TriviaKind.Unsafe
3324 | TriviaKind.UnsafeExpression
3325 | TriviaKind.FallThrough
3326 | TriviaKind.ExtraTokenError
3327 | TriviaKind.AfterHaltCompiler
3328 -> acc
3329 | TriviaKind.DelimitedComment ->
3330 (* For legacy compliance, block comments should have the position of
3331 * their end
3333 let start = Trivia.start_offset t + 2 (* for the '/*' *) in
3334 let end_ = Trivia.end_offset t in
3335 let len = end_ - start - 1 in
3336 let p = pos_of_offset (end_ - 1) end_ in
3337 let t = String.sub (Trivia.text t) 2 len in
3338 (p, CmtBlock t) :: cmts, fm
3339 | TriviaKind.SingleLineComment ->
3340 if not include_line_comments then cmts, fm
3341 else
3342 let text = SourceText.text (Trivia.source_text t) in
3343 let start = Trivia.start_offset t in
3344 let start = start + if text.[start] = '#' then 1 else 2 in
3345 let end_ = Trivia.end_offset t in
3346 let len = end_ - start + 1 in
3347 let p = pos_of_offset start end_ in
3348 let t = String.sub text start len in
3349 (p, CmtLine (t ^ "\n")) :: cmts, fm
3350 | TriviaKind.FixMe
3351 | TriviaKind.IgnoreError
3352 -> if not collect_fixmes then cmts, fm
3353 else
3354 let open Str in
3355 let pos = pPos node env in
3356 let line = Pos.line pos in
3357 let ignores = try IMap.find line fm with Caml.Not_found -> IMap.empty in
3358 let txt = Trivia.text t in
3360 ignore (search_forward ignore_error txt 0);
3361 let p = pos_of_offset (Trivia.start_offset t) (Trivia.end_offset t) in
3362 let code = int_of_string (matched_group 2 txt) in
3363 let ignores = IMap.add code p ignores in
3364 cmts, IMap.add line ignores fm
3365 with
3366 | Not_found_s _
3367 | Caml.Not_found ->
3368 Errors.fixme_format pos;
3369 cmts, fm
3371 let rec aux (_cmts, _fm as acc : accumulator) (node : node) : accumulator =
3372 let recurse () = List.fold_left ~f:aux ~init:acc (children node) in
3373 match syntax node with
3374 | Token t ->
3375 if Token.has_trivia_kind t TriviaKind.DelimitedComment
3376 || (include_line_comments && Token.has_trivia_kind t TriviaKind.SingleLineComment)
3377 || (collect_fixmes && (
3378 Token.has_trivia_kind t TriviaKind.FixMe ||
3379 Token.has_trivia_kind t TriviaKind.IgnoreError))
3380 then
3381 let f = go node in
3382 let trivia = Token.leading t in
3383 let acc = List.fold_left ~f ~init:acc trivia in
3384 let trivia = Token.trailing t in
3385 List.fold_left ~f ~init:acc trivia
3386 else recurse ()
3387 | _ -> recurse ()
3389 aux ([], IMap.empty) tree
3391 (*****************************************************************************(
3392 * Front-end matter
3393 )*****************************************************************************)
3395 let elaborate_toplevel_and_std_constants ast (env: env) source_text =
3396 let autoimport =
3397 env.is_hh_file || ParserOptions.enable_hh_syntax_for_hhvm env.parser_options
3399 match env.elaborate_namespaces, env.saw_std_constant_redefinition with
3400 | true, true ->
3401 let elaborate_std_constants nsenv def =
3402 let visitor = object
3403 inherit [_] endo as super
3404 method! on_expr env expr =
3405 match expr with
3406 | p, True | p, False | p, Null when p <> Pos.none ->
3407 let s = Pos.start_cnum p in
3408 let e = Pos.end_cnum p in
3409 let text = SourceText.sub source_text s (e - s) in
3410 let was_renamed, _ =
3411 NS.elaborate_id_impl
3412 ~autoimport:true
3413 nsenv
3414 NS.ElaborateConst
3415 text in
3416 if was_renamed then p, Ast.Id (p, text)
3417 else expr
3418 | _ -> super#on_expr env expr
3419 end in
3420 visitor#on_def nsenv def in
3421 let parser_options = env.parser_options in
3422 NS.elaborate_map_toplevel_defs
3423 ~autoimport parser_options ast elaborate_std_constants
3424 | true, false ->
3425 NS.elaborate_toplevel_defs ~autoimport env.parser_options ast
3426 | _ -> ast
3428 let elaborate_halt_compiler ast env source_text =
3429 match !(env.saw_compiler_halt_offset) with
3430 | Some x ->
3431 let elaborate_halt_compiler_const defs =
3432 let visitor = object
3433 inherit [_] endo as super
3434 method! on_expr env expr =
3435 match expr with
3436 | p, Id (_, "__COMPILER_HALT_OFFSET__") ->
3437 let start_offset = Pos.start_cnum p in
3438 (* Construct a new position and id *)
3439 let id = string_of_int x in
3440 let end_offset = start_offset + (String.length id) in
3441 let pos_file = Pos.filename p in
3442 let pos = SourceText.relative_pos pos_file source_text start_offset end_offset in
3443 pos, Ast.Int id
3444 | _ -> super#on_expr env expr
3445 end in
3446 visitor#on_program () defs in
3447 elaborate_halt_compiler_const ast
3448 | None -> ast
3451 let lower env ~source_text ~script comments : result =
3452 let ast = runP pScript script env in
3453 let ast = elaborate_toplevel_and_std_constants ast env source_text in
3454 let ast = elaborate_halt_compiler ast env source_text in
3455 let content = if env.codegen then "" else SourceText.text source_text in
3456 { fi_mode = env.fi_mode
3457 ; is_hh_file = env.is_hh_file
3458 ; ast
3459 ; content
3460 ; comments
3461 ; file = env.file
3463 end (* WithPositionedSyntax *)
3465 (* TODO: Make these not default to positioned_syntax *)
3466 include Full_fidelity_ast_types
3467 module ParserErrors_ = Full_fidelity_parser_errors.WithSyntax(PositionedSyntax)
3468 module ParserErrors = ParserErrors_.WithSmartConstructors(CoroutineSC)
3470 module SourceText = Full_fidelity_source_text
3471 module DeclModeSC = DeclModeSmartConstructors.WithSyntax(PositionedSyntax)
3472 module DeclModeParser_ = Full_fidelity_parser.WithSyntax(PositionedSyntax)
3473 module DeclModeParser = DeclModeParser_.WithSmartConstructors(DeclModeSC)
3474 module FromPositionedSyntax = WithPositionedSyntax(PositionedSyntax)
3475 module FromEditablePositionedSyntax =
3476 WithPositionedSyntax(Full_fidelity_editable_positioned_syntax)
3478 let parse_text
3479 (env : env)
3480 (source_text : SourceText.t)
3481 : (FileInfo.mode option * PositionedSyntaxTree.t) =
3482 let default_mode = ParserOptions.default_mode env.parser_options in
3483 let mode = Full_fidelity_parser.parse_mode ~default:default_mode source_text in
3484 let quick_mode = not env.codegen && (
3485 match mode with
3486 | None
3487 | Some FileInfo.Mdecl
3488 | Some FileInfo.Mphp -> true
3489 | _ -> env.quick_mode
3490 ) in
3491 if mode = Some FileInfo.Mexperimental && env.codegen && (not env.hacksperimental) then begin
3492 let e = SyntaxError.make 0 0 SyntaxError.experimental_in_codegen_without_hacksperimental in
3493 let p =
3494 SourceText.relative_pos env.file source_text
3495 (Full_fidelity_syntax_error.start_offset e)
3496 (Full_fidelity_syntax_error.end_offset e) in
3497 raise @@ SyntaxError.ParserFatal (e, p)
3498 end;
3499 let tree =
3500 let env' =
3501 Full_fidelity_parser_env.make
3502 ~hhvm_compat_mode:env.codegen
3503 ~has_dot_hack_extension:env.has_dot_hack_extension
3504 ~codegen:env.codegen
3505 ~force_hh:env.enable_hh_syntax
3506 ~enable_xhp:env.enable_xhp
3507 ~php5_compat_mode:env.php5_compat_mode
3508 ~enable_stronger_await_binding:
3509 (GlobalOptions.po_enable_stronger_await_binding env.parser_options)
3510 ~disable_nontoplevel_declarations:
3511 (GlobalOptions.po_disable_nontoplevel_declarations env.parser_options)
3512 ?mode
3515 if quick_mode then
3516 let parser = DeclModeParser.make env' source_text in
3517 let (parser, root) = DeclModeParser.parse_script parser in
3518 let errors = DeclModeParser.errors parser in
3519 PositionedSyntaxTree.create source_text root errors mode false
3520 else
3521 PositionedSyntaxTree.make ~env:env' source_text
3523 (mode, tree)
3525 let scour_comments_and_add_fixmes (env : env) source_text script =
3526 let comments, fixmes =
3527 FromPositionedSyntax.scour_comments env.file source_text script env
3528 ~collect_fixmes:env.keep_errors
3529 ~include_line_comments:env.include_line_comments
3531 let () = if env.keep_errors then
3532 if env.quick_mode then
3533 Fixmes.DECL_HH_FIXMES.add env.file fixmes
3534 else
3535 Fixmes.HH_FIXMES.add env.file fixmes in
3536 comments
3538 let lower_tree
3539 (env : env)
3540 (source_text : SourceText.t)
3541 (mode : FileInfo.mode option)
3542 (tree : PositionedSyntaxTree.t)
3543 : result =
3544 let env =
3545 { env with lower_coroutines =
3546 env.lower_coroutines &&
3547 PositionedSyntaxTree.sc_state tree &&
3548 env.codegen
3549 } in
3550 let script = PositionedSyntaxTree.root tree in
3551 let comments = scour_comments_and_add_fixmes env source_text script in
3552 let () =
3553 if env.codegen && not env.lower_coroutines then
3554 let hhvm_compat_mode = if env.systemlib_compat_mode
3555 then ParserErrors.SystemLibCompat
3556 else ParserErrors.HHVMCompat in
3557 let error_env = ParserErrors.make_env tree
3558 ~hhvm_compat_mode
3559 ~enable_hh_syntax:env.enable_hh_syntax
3560 ~codegen:env.codegen
3561 ~parser_options:env.parser_options
3563 let errors = ParserErrors.parse_errors error_env in
3564 (* Prioritize runtime errors *)
3565 let runtime_errors =
3566 List.filter errors ~f:SyntaxError.(fun e -> error_type e = RuntimeError)
3568 match errors, runtime_errors with
3569 | [], [] -> ()
3570 | _, e :: _
3571 | e :: _, _
3573 let p =
3574 SourceText.relative_pos env.file source_text
3575 (Full_fidelity_syntax_error.start_offset e)
3576 (Full_fidelity_syntax_error.end_offset e) in
3577 raise @@ SyntaxError.ParserFatal (e, p)
3578 else if env.keep_errors then
3579 let pos_and_message_of error =
3580 let so = SyntaxError.start_offset error in
3581 let eo = SyntaxError.end_offset error in
3582 let p = SourceText.relative_pos env.file source_text so eo in
3583 p, SyntaxError.message error
3585 let is_hhi =
3586 String_utils.string_ends_with Relative_path.(suffix env.file) "hhi"
3588 match PositionedSyntaxTree.errors tree with
3589 | [] when env.quick_mode -> ()
3590 | [] ->
3591 let error_env = ParserErrors.make_env tree
3592 ~hhvm_compat_mode:ParserErrors.HHVMCompat
3593 ~enable_hh_syntax:env.enable_hh_syntax
3594 ~codegen:env.codegen
3595 ~hhi_mode:is_hhi
3596 ~parser_options:env.parser_options
3598 let errors = ParserErrors.parse_errors error_env in
3599 let f e = Errors.parsing_error (pos_and_message_of e) in
3600 List.iter ~f errors
3601 | e::_ ->
3602 Errors.parsing_error (pos_and_message_of e);
3604 let mode = Option.value mode ~default:(FileInfo.Mpartial) in
3605 let env = { env with fi_mode = mode; is_hh_file = mode <> FileInfo.Mphp } in
3606 let popt = env.parser_options in
3607 (* If we are generating code and this is an hh file or hh syntax is enabled,
3608 * then we want to inject auto import types into HH namespace during namespace
3609 * resolution.
3611 let popt = ParserOptions.with_hh_syntax_for_hhvm popt
3612 (env.codegen && (ParserOptions.enable_hh_syntax_for_hhvm popt || env.is_hh_file)) in
3613 let env = { env with parser_options = popt } in
3614 if env.lower_coroutines
3615 then
3616 let script =
3617 Full_fidelity_editable_positioned_syntax.from_positioned_syntax script
3618 |> Ppl_class_rewriter.rewrite_ppl_classes
3619 |> Coroutine_lowerer.lower_coroutines in
3620 FromEditablePositionedSyntax.lower
3622 ~source_text
3623 ~script
3624 comments
3625 else
3626 FromPositionedSyntax.lower
3628 ~source_text
3629 ~script
3630 comments
3632 let from_text (env : env) (source_text : SourceText.t) : result =
3633 let (mode, tree) = parse_text env source_text in
3634 lower_tree env source_text mode tree
3636 let from_file (env : env) : result =
3637 let source_text = SourceText.from_file env.file in
3638 from_text env source_text
3640 (*****************************************************************************(
3641 * Backward compatibility matter (should be short-lived)
3642 )*****************************************************************************)
3644 let legacy (x : result) : Parser_return.t =
3645 { Parser_return.file_mode = Option.some_if (x.fi_mode <> FileInfo.Mphp) x.fi_mode
3646 ; Parser_return.is_hh_file = x.is_hh_file
3647 ; Parser_return.comments = x.comments
3648 ; Parser_return.ast = x.ast
3649 ; Parser_return.content = x.content
3652 let from_text_with_legacy (env : env) (content : string)
3653 : Parser_return.t =
3654 let source_text = SourceText.make env.file content in
3655 legacy @@ from_text env source_text
3657 let from_file_with_legacy env = legacy (from_file env)
3660 (******************************************************************************(
3661 * For cut-over purposes only; this should be removed as soon as Parser_hack
3662 * is removed.
3663 )******************************************************************************)
3665 let defensive_program
3666 ?(hacksperimental=false)
3667 ?(quick=false)
3668 ?(fail_open=false)
3669 ?(keep_errors=false)
3670 ?(elaborate_namespaces=true)
3671 ?(include_line_comments=false)
3672 parser_options fn content =
3673 try begin
3674 let source = Full_fidelity_source_text.make fn content in
3675 (* If we fail open, we don't want errors. *)
3676 let env = make_env
3677 ~fail_open
3678 ~quick_mode:quick
3679 ~elaborate_namespaces
3680 ~keep_errors:(keep_errors || (not fail_open))
3681 ~parser_options
3682 ~hacksperimental
3683 ~include_line_comments
3686 legacy @@ from_text env source
3687 end with e ->
3688 (* If we fail to lower, try to just make a source text and get the file mode *)
3689 (* If even THAT fails, we just have to give up and return an empty php file*)
3690 let mode =
3692 let source = Full_fidelity_source_text.make fn content in
3693 let default_mode = ParserOptions.default_mode parser_options in
3694 Full_fidelity_parser.parse_mode ~default:default_mode source
3695 with _ -> None in
3696 let err = Exn.to_string e in
3697 let fn = Relative_path.suffix fn in
3698 (* If we've already found a parsing error, it's okay for lowering to fail *)
3699 if not (Errors.currently_has_errors ()) then
3700 Hh_logger.log "Warning, lowering failed for %s\n - error: %s\n" fn err;
3702 { Parser_return.file_mode = mode
3703 ; Parser_return.comments = []
3704 ; Parser_return.ast = []
3705 ; Parser_return.content = content
3706 ; Parser_return.is_hh_file = mode <> None
3709 let defensive_from_file ?quick popt fn =
3710 let content = try Sys_utils.cat (Relative_path.to_absolute fn) with _ -> "" in
3711 defensive_program ?quick popt fn content
3713 let defensive_from_file_with_default_popt ?quick fn =
3714 defensive_from_file ?quick ParserOptions.default fn
3716 let defensive_program_with_default_popt
3717 ?hacksperimental
3718 ?quick
3719 ?fail_open
3720 ?elaborate_namespaces
3721 fn content =
3722 defensive_program
3723 ?hacksperimental
3724 ?quick
3725 ?fail_open
3726 ?elaborate_namespaces
3727 (ParserOptions.default) fn content