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