Remove from_text from full_fidelity_ast
[hiphop-php.git] / hphp / hack / src / parser / full_fidelity_ast.ml
blobefbde08304a2aca89141d0a8661c1d316759614d
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
15 (* What we are lowering to *)
16 open Ast
17 module Partial = Partial_provider
19 (* Don't allow expressions to nest deeper than this to avoid stack overflow *)
20 let recursion_limit = 30000
22 [@@@warning "-32"]
24 (* unused ppx_deriving show function in OCaml ast trips Werror *)
26 type lifted_await_kind =
27 | LiftedFromStatement
28 | LiftedFromConcurrent
29 [@@deriving show]
31 [@@@warning "+32"]
33 type lifted_awaits = {
34 mutable awaits: (id option * expr) list;
35 lift_kind: lifted_await_kind;
37 [@@deriving show]
39 (* Context of the file being parsed, as (hopefully some day read-only) state. *)
40 type env = {
41 is_hh_file: bool;
42 codegen: bool;
43 php5_compat_mode: bool;
44 elaborate_namespaces: bool;
45 include_line_comments: bool;
46 keep_errors: bool;
47 quick_mode: bool;
48 (* Show errors even in quick mode. Does not override keep_errors. Hotfix
49 * until we can properly set up saved states to surface parse errors during
50 * typechecking properly. *)
51 show_all_errors: bool;
52 lower_coroutines: bool;
53 fail_open: bool;
54 parser_options: ParserOptions.t;
55 fi_mode: FileInfo.mode;
56 file: Relative_path.t;
57 hacksperimental: bool;
58 top_level_statements: bool;
59 (* Whether we are (still) considering TLSs*)
60 (* Changing parts; should disappear in future. `mutable` saves allocations. *)
61 mutable ignore_pos: bool;
62 mutable max_depth: int;
63 (* Filthy hack around OCaml bug *)
64 mutable saw_yield: bool;
65 (* Information flowing back up *)
66 mutable lifted_awaits: lifted_awaits option;
67 mutable tmp_var_counter: int;
68 (* Whether we've seen COMPILER_HALT_OFFSET. The value of COMPILER_HALT_OFFSET
69 defaults to 0 if HALT_COMPILER isn't called.
70 None -> COMPILER_HALT_OFFSET isn't in the source file
71 Some 0 -> COMPILER_HALT_OFFSET is in the source file, but HALT_COMPILER isn't
72 Some x -> COMPILER_HALT_OFFSET is in the source file,
73 HALT_COMPILER is at x bytes offset in the file.
75 saw_compiler_halt_offset: int option ref;
76 recursion_depth: int ref;
77 cls_reified_generics: SSet.t ref;
78 in_static_method: bool ref;
79 parent_maybe_reified: bool ref;
80 (* This provides a generic mechanism to delay raising parsing errors;
81 * since we're moving FFP errors away from CST to a stage after lowering
82 * _and_ want to prioritize errors before lowering, the lowering errors
83 * must be merely stored when the lowerer runs (until check for FFP runs (on AST)
84 * and raised _after_ FFP error checking (unless we run the lowerer twice,
85 * which would be expensive). *)
86 lowpri_errors: (Pos.t * string) list ref;
88 [@@deriving show]
90 let make_env
91 ?(codegen = false)
92 ?(php5_compat_mode = false)
93 ?(elaborate_namespaces = true)
94 ?(include_line_comments = false)
95 ?(keep_errors = true)
96 ?(ignore_pos = false)
97 ?(quick_mode = false)
98 ?(show_all_errors = false)
99 ?(lower_coroutines = true)
100 ?(fail_open = true)
101 ?(parser_options = ParserOptions.default)
102 ?(fi_mode = FileInfo.Mpartial)
103 ?(is_hh_file = false)
104 ?(hacksperimental = false)
105 (file : Relative_path.t) : env =
106 let parser_options = ParserOptions.with_codegen parser_options codegen in
108 is_hh_file;
109 codegen;
110 php5_compat_mode;
111 elaborate_namespaces;
112 include_line_comments;
113 keep_errors;
114 quick_mode =
115 ( (not codegen)
117 match fi_mode with
118 | FileInfo.Mdecl
119 | FileInfo.Mphp ->
120 true
121 | _ -> quick_mode );
122 show_all_errors;
123 lower_coroutines;
124 parser_options;
125 fi_mode;
126 fail_open;
127 file;
128 hacksperimental;
129 top_level_statements = true;
130 ignore_pos;
131 max_depth = 42;
132 saw_yield = false;
133 saw_compiler_halt_offset = ref None;
134 recursion_depth = ref 0;
135 cls_reified_generics = ref SSet.empty;
136 in_static_method = ref false;
137 parent_maybe_reified = ref false;
138 lifted_awaits = None;
139 tmp_var_counter = 1;
140 lowpri_errors = ref [];
143 let should_surface_errors env =
144 (* env.show_all_errors is a hotfix until we can retool how saved states handle
145 * parse errors. *)
146 ((not env.quick_mode) || env.show_all_errors) && env.keep_errors
148 type 'a result_ = {
149 fi_mode: FileInfo.mode;
150 is_hh_file: bool;
151 ast: 'a;
152 content: string;
153 file: Relative_path.t;
154 comments: (Pos.t * comment) list;
156 [@@deriving show]
158 type result = Ast.program result_
160 type rust_result = (pos, unit, unit, unit) Aast.program result_
162 module WithPositionedSyntax (Syntax : Positioned_syntax_sig.PositionedSyntax_S) =
163 struct
164 (* What we're lowering from *)
165 open Syntax
167 type node = Syntax.t
169 module Token = Syntax.Token
170 module Trivia = Token.Trivia
171 module TriviaKind = Trivia.TriviaKind
172 module SyntaxKind = Full_fidelity_syntax_kind
173 module TK = Full_fidelity_token_kind
174 module SourceText = Trivia.SourceText
176 type expr_location =
177 | TopLevel
178 | MemberSelect
179 | InDoubleQuotedString
180 | InBacktickedString
181 | AsStatement
182 | RightOfAssignment
183 | RightOfAssignmentInUsingStatement
184 | RightOfReturn
185 | UsingStatement
187 let is_typechecker env = not env.codegen
189 let drop_pstr : int -> pstring -> pstring =
190 fun cnt (pos, str) ->
191 let len = String.length str in
192 ( pos,
193 if cnt >= len then
195 else
196 String.sub str cnt (len - cnt) )
198 let non_tls env =
199 if not env.top_level_statements then
201 else
202 { env with top_level_statements = false }
204 type +'a parser = node -> env -> 'a
206 type ('a, 'b) metaparser = 'a parser -> 'b parser
208 let underscore = Str.regexp "_"
210 let quoted = Str.regexp "[ \t\n\r\012]*\"\\(\\(.\\|\n\\)*\\)\""
212 let whitespace = Str.regexp "[ \t\n\r\012]+"
214 let hashbang = Str.regexp "^#!.*\n"
216 let ignore_error =
217 Str.regexp "HH_\\(FIXME\\|IGNORE_ERROR\\)[ \\t\\n]*\\[\\([0-9]+\\)\\]"
219 let namespace_use = Str.regexp "[^\\\\]*$"
221 let mode_annotation = function
222 | FileInfo.Mphp -> FileInfo.Mdecl
223 | m -> m
225 let make_tmp_var_name env =
226 let name =
227 SN.SpecialIdents.tmp_var_prefix ^ string_of_int env.tmp_var_counter
229 env.tmp_var_counter <- env.tmp_var_counter + 1;
230 name
232 let lift_await ((pos, _) as expr) env location =
233 match (env.lifted_awaits, location) with
234 | (_, UsingStatement)
235 | (_, RightOfAssignmentInUsingStatement)
236 | (None, _) ->
237 Await expr
238 | (Some awaits, _) ->
239 if location <> AsStatement then (
240 let name = make_tmp_var_name env in
241 awaits.awaits <- (Some (pos, name), expr) :: awaits.awaits;
242 Lvar (pos, name)
243 ) else (
244 awaits.awaits <- (None, expr) :: awaits.awaits;
245 Null
248 let process_lifted_awaits lifted_awaits =
249 List.iter lifted_awaits.awaits ~f:(fun (_, (pos, _)) ->
250 assert (pos <> Pos.none));
251 List.sort
252 lifted_awaits.awaits
253 ~compare:(fun (_, (pos1, _)) (_, (pos2, _)) -> Pos.compare pos1 pos2)
255 let with_new_nonconcurrent_scope env f =
256 let saved_lifted_awaits = env.lifted_awaits in
257 env.lifted_awaits <- None;
258 Utils.try_finally ~f ~finally:(fun () ->
259 env.lifted_awaits <- saved_lifted_awaits)
261 let with_new_concurrent_scope env f =
262 let lifted_awaits = { awaits = []; lift_kind = LiftedFromConcurrent } in
263 let saved_lifted_awaits = env.lifted_awaits in
264 env.lifted_awaits <- Some lifted_awaits;
265 let result =
266 Utils.try_finally ~f ~finally:(fun () ->
267 env.lifted_awaits <- saved_lifted_awaits)
269 (process_lifted_awaits lifted_awaits, result)
271 let clear_statement_scope env f =
272 match env.lifted_awaits with
273 | Some { lift_kind = LiftedFromStatement; _ } ->
274 let saved_lifted_awaits = env.lifted_awaits in
275 env.lifted_awaits <- None;
276 Utils.try_finally ~f ~finally:(fun () ->
277 env.lifted_awaits <- saved_lifted_awaits)
278 | _ -> f ()
280 let lift_awaits_in_statement env pos f =
281 let (lifted_awaits, result) =
282 match env.lifted_awaits with
283 | Some { lift_kind = LiftedFromConcurrent; _ } -> (None, f ())
284 | Some { lift_kind = LiftedFromStatement; _ }
285 | None ->
286 let lifted_awaits = { awaits = []; lift_kind = LiftedFromStatement } in
287 let saved_lifted_awaits = env.lifted_awaits in
288 env.lifted_awaits <- Some lifted_awaits;
289 let result =
290 Utils.try_finally ~f ~finally:(fun () ->
291 env.lifted_awaits <- saved_lifted_awaits)
293 let lifted_awaits =
294 if List.is_empty lifted_awaits.awaits then
295 None
296 else
297 Some lifted_awaits
299 (lifted_awaits, result)
301 match lifted_awaits with
302 | None -> result
303 | Some lifted_awaits ->
304 (pos, Awaitall (process_lifted_awaits lifted_awaits, [result]))
306 let syntax_to_list include_separators node =
307 let rec aux acc syntax_list =
308 match syntax_list with
309 | [] -> acc
310 | h :: t ->
311 begin
312 match syntax h with
313 | ListItem { list_item; list_separator } ->
314 let acc = list_item :: acc in
315 let acc =
316 if include_separators then
317 list_separator :: acc
318 else
321 aux acc t
322 | _ -> aux (h :: acc) t
325 match syntax node with
326 | Missing -> []
327 | SyntaxList s -> List.rev (aux [] s)
328 | ListItem { list_item; list_separator } ->
329 if include_separators then
330 [list_item; list_separator]
331 else
332 [list_item]
333 | _ -> [node]
335 let syntax_to_list_no_separators = syntax_to_list false
337 let pPos : Pos.t parser =
338 fun node env ->
339 if env.ignore_pos then
340 Pos.none
341 else
342 Option.value ~default:Pos.none (position_exclusive env.file node)
344 let raise_parsing_error env node_or_pos msg =
345 if should_surface_errors env then
346 let p =
347 match node_or_pos with
348 | `Pos pos -> pos
349 | `Node node -> pPos node env
351 env.lowpri_errors := (p, msg) :: !(env.lowpri_errors)
352 else if env.codegen then
353 let p =
354 match node_or_pos with
355 | `Pos pos -> pos
356 | `Node node -> Option.value (position env.file node) ~default:Pos.none
358 env.lowpri_errors := (p, msg) :: !(env.lowpri_errors)
359 else
362 (* HHVM starts range of function declaration from the 'function' keyword *)
363 let pFunction node env =
364 let p = pPos node env in
365 match syntax node with
366 | FunctionDeclaration { function_declaration_header = h; _ }
367 | MethodishDeclaration { methodish_function_decl_header = h; _ }
368 when env.codegen ->
369 begin
370 match syntax h with
371 | FunctionDeclarationHeader { function_keyword = f; _ }
372 when not (is_missing f) ->
373 (* For continuation compilation, we end up with spans across files :-( *)
374 Pos.btw_nocheck (pPos f env) p
375 | _ -> p
377 | _ -> p
379 exception Lowerer_invariant_failure of string * string
381 let invariant_failure node msg env =
382 let pos = Pos.string (Pos.to_absolute (pPos node env)) in
383 raise (Lowerer_invariant_failure (pos, msg))
385 let scuba_table = Scuba.Table.of_name "hh_missing_lowerer_cases"
387 let log_missing ?(caught = false) ~(env : env) ~expecting node : unit =
388 EventLogger.log_if_initialized
389 @@ fun () ->
390 let source = source_text node in
391 let start = start_offset node in
392 let end_ = end_offset node in
393 let pos = SourceText.relative_pos env.file source start end_ in
394 let file = Relative_path.to_absolute env.file in
395 let contents =
396 let context_size = 5000 in
397 let start = max 0 (start - context_size) in
398 let length = min (2 * context_size) (SourceText.length source - start) in
399 SourceText.sub source start length
401 let kind = SyntaxKind.to_string (Syntax.kind node) in
402 let line = Pos.line pos in
403 let column = Pos.start_cnum pos in
404 let synthetic = is_synthetic node in
405 Scuba.new_sample (Some scuba_table)
406 |> Scuba.add_normal "filename" file
407 |> Scuba.add_normal "expecting" expecting
408 |> Scuba.add_normal "contents" contents
409 |> Scuba.add_normal "found_kind" kind
410 |> Scuba.add_int "line" line
411 |> Scuba.add_int "column" column
412 |> Scuba.add_int
413 "is_synthetic"
414 ( if synthetic then
416 else
418 |> Scuba.add_int
419 "caught"
420 ( if caught then
422 else
424 |> EventLogger.log
426 exception API_Missing_syntax of string * env * node
428 (* If we fail to lower something, raise an error in the typechecker
429 complaining that the code does not parse. Don't raise a parsing error
430 if there already is one, since that one will likely be better than this one. *)
431 let lowering_error env pos text syntax_kind =
432 if not (is_typechecker env) then
434 else if not (Errors.currently_has_errors () || !(env.lowpri_errors) <> [])
435 then
436 raise_parsing_error
438 (`Pos pos)
439 (SyntaxError.lowering_parsing_error text syntax_kind)
441 let missing_syntax : ?fallback:'a -> string -> node -> env -> 'a =
442 fun ?fallback expecting node env ->
443 let pos = pPos node env in
444 let text = text node in
445 lowering_error env pos text expecting;
446 match fallback with
447 | Some x when env.fail_open ->
448 let () = log_missing ~env ~expecting node in
450 | _ -> raise (API_Missing_syntax (expecting, env, node))
452 let runP : 'a parser -> node -> env -> 'a =
453 fun pThing thing env ->
454 try pThing thing env
455 with API_Missing_syntax (s, env, n) ->
456 let pos = Pos.string (Pos.to_absolute (pPos n env)) in
457 let msg =
458 Printf.sprintf
459 "missing case in %s.
460 - pos: %s
461 - unexpected: '%s'
462 - kind: %s
466 (text n)
467 (SyntaxKind.to_string (kind n))
469 raise (Failure msg)
471 let mk_empty_ns_env env = Namespace_env.empty_from_popt env.parser_options
473 (* TODO: Cleanup this hopeless Noop mess *)
474 let mk_noop pos : stmt list -> stmt list = function
475 | [] -> [(pos, Noop)]
476 | s -> s
478 let mpStripNoop pThing node env =
479 match pThing node env with
480 | [(_, Noop)] -> []
481 | stmtl -> stmtl
483 let mpOptional : ('a, 'a option) metaparser =
484 fun p node env ->
485 match syntax node with
486 | Missing -> None
487 | _ -> Some (p node env)
489 let mpYielding : ('a, 'a * bool) metaparser =
490 fun p node env ->
491 let outer_saw_yield = env.saw_yield in
492 let () = env.saw_yield <- false in
493 let result = p node env in
494 let result = (result, env.saw_yield) in
495 let () = env.saw_yield <- outer_saw_yield in
496 result
498 let in_string l = l = InDoubleQuotedString || l = InBacktickedString
500 let pos_qualified_name node env =
501 let aux p =
502 match syntax p with
503 | ListItem li -> text li.list_item ^ text li.list_separator
504 | _ -> text p
506 let p = pPos node env in
507 let name =
508 match syntax node with
509 | QualifiedName { qualified_name_parts = { syntax = SyntaxList l; _ } }
511 String.concat ~sep:"" @@ List.map ~f:aux l
512 | _ -> missing_syntax "qualified name" node env
514 (p, name)
516 let rec pos_name node env =
517 match syntax node with
518 | QualifiedName _ -> pos_qualified_name node env
519 | SimpleTypeSpecifier { simple_type_specifier = s } -> pos_name s env
520 | _ ->
521 let name = text node in
522 let local_ignore_pos = env.ignore_pos in
523 (* Special case for __LINE__; never ignore position for that special name *)
524 if name = "__LINE__" then env.ignore_pos <- false;
525 if name = "__COMPILER_HALT_OFFSET__" then
526 env.saw_compiler_halt_offset := Some 0;
527 let p = pPos node env in
528 env.ignore_pos <- local_ignore_pos;
529 (p, name)
531 let couldMap : 'a. f:'a parser -> 'a list parser =
532 fun ~f node env ->
533 let rec synmap : 'a. 'a parser -> 'a list parser =
534 fun f node env ->
535 match syntax node with
536 | SyntaxList l -> List.concat_map l ~f:(fun n -> go ~f n env)
537 | ListItem i -> [f i.list_item env]
538 | _ -> [f node env]
539 and go : 'a. f:'a parser -> 'a list parser =
540 fun ~f -> function
541 | node when is_missing node -> (fun _env -> [])
542 | node -> synmap f node
544 go ~f node env
546 let as_list : node -> node list =
547 let strip_list_item = function
548 | { syntax = ListItem { list_item = i; _ }; _ } -> i
549 | x -> x
551 function
552 | { syntax = SyntaxList ({ syntax = ListItem _; _ } :: _ as synl); _ } ->
553 List.map ~f:strip_list_item synl
554 | { syntax = SyntaxList synl; _ } -> synl
555 | { syntax = Missing; _ } -> []
556 | syn -> [syn]
558 let token_kind : node -> TK.t option = function
559 | { syntax = Token t; _ } -> Some (Token.kind t)
560 | _ -> None
562 let pBop : (expr -> expr -> expr_) parser =
563 fun node env lhs rhs ->
564 match token_kind node with
565 | Some TK.Equal -> Binop (Eq None, lhs, rhs)
566 | Some TK.Bar -> Binop (Bar, lhs, rhs)
567 | Some TK.Ampersand -> Binop (Amp, lhs, rhs)
568 | Some TK.Plus -> Binop (Plus, lhs, rhs)
569 | Some TK.Minus -> Binop (Minus, lhs, rhs)
570 | Some TK.Star -> Binop (Star, lhs, rhs)
571 | Some TK.Carat -> Binop (Xor, lhs, rhs)
572 | Some TK.Slash -> Binop (Slash, lhs, rhs)
573 | Some TK.Dot -> Binop (Dot, lhs, rhs)
574 | Some TK.Percent -> Binop (Percent, lhs, rhs)
575 | Some TK.LessThan -> Binop (Lt, lhs, rhs)
576 | Some TK.GreaterThan -> Binop (Gt, lhs, rhs)
577 | Some TK.EqualEqual -> Binop (Eqeq, lhs, rhs)
578 | Some TK.LessThanEqual -> Binop (Lte, lhs, rhs)
579 | Some TK.GreaterThanEqual -> Binop (Gte, lhs, rhs)
580 | Some TK.StarStar -> Binop (Starstar, lhs, rhs)
581 | Some TK.ExclamationEqual -> Binop (Diff, lhs, rhs)
582 | Some TK.BarEqual -> Binop (Eq (Some Bar), lhs, rhs)
583 | Some TK.PlusEqual -> Binop (Eq (Some Plus), lhs, rhs)
584 | Some TK.MinusEqual -> Binop (Eq (Some Minus), lhs, rhs)
585 | Some TK.StarEqual -> Binop (Eq (Some Star), lhs, rhs)
586 | Some TK.StarStarEqual -> Binop (Eq (Some Starstar), lhs, rhs)
587 | Some TK.SlashEqual -> Binop (Eq (Some Slash), lhs, rhs)
588 | Some TK.DotEqual -> Binop (Eq (Some Dot), lhs, rhs)
589 | Some TK.PercentEqual -> Binop (Eq (Some Percent), lhs, rhs)
590 | Some TK.CaratEqual -> Binop (Eq (Some Xor), lhs, rhs)
591 | Some TK.AmpersandEqual -> Binop (Eq (Some Amp), lhs, rhs)
592 | Some TK.BarBar -> Binop (Barbar, lhs, rhs)
593 | Some TK.AmpersandAmpersand -> Binop (Ampamp, lhs, rhs)
594 | Some TK.LessThanLessThan -> Binop (Ltlt, lhs, rhs)
595 | Some TK.GreaterThanGreaterThan -> Binop (Gtgt, lhs, rhs)
596 | Some TK.EqualEqualEqual -> Binop (Eqeqeq, lhs, rhs)
597 | Some TK.LessThanLessThanEqual -> Binop (Eq (Some Ltlt), lhs, rhs)
598 | Some TK.GreaterThanGreaterThanEqual -> Binop (Eq (Some Gtgt), lhs, rhs)
599 | Some TK.ExclamationEqualEqual -> Binop (Diff2, lhs, rhs)
600 | Some TK.LessThanEqualGreaterThan -> Binop (Cmp, lhs, rhs)
601 | Some TK.QuestionQuestion -> Binop (QuestionQuestion, lhs, rhs)
602 | Some TK.QuestionQuestionEqual ->
603 Binop (Eq (Some QuestionQuestion), lhs, rhs)
604 (* The ugly duckling; In the FFP, `|>` is parsed as a
605 * `BinaryOperator`, whereas the typed AST has separate constructors for
606 * Pipe and Binop. This is why we don't just project onto a
607 * `bop`, but a `expr -> expr -> expr_`.
609 | Some TK.BarGreaterThan -> Pipe (lhs, rhs)
610 | Some TK.QuestionColon -> Eif (lhs, None, rhs)
611 (* TODO: Figure out why this fails silently when used in a pBlock; probably
612 just caught somewhere *)
613 | _ -> missing_syntax "binary operator" node env
615 let pImportFlavor : import_flavor parser =
616 fun node env ->
617 match token_kind node with
618 | Some TK.Include -> Include
619 | Some TK.Require -> Require
620 | Some TK.Include_once -> IncludeOnce
621 | Some TK.Require_once -> RequireOnce
622 | _ -> missing_syntax "import flavor" node env
624 let pNullFlavor : og_null_flavor parser =
625 fun node env ->
626 match token_kind node with
627 | Some TK.QuestionMinusGreaterThan -> OG_nullsafe
628 | Some TK.MinusGreaterThan -> OG_nullthrows
629 | _ -> missing_syntax "null flavor" node env
631 type modifiers = {
632 has_async: bool;
633 has_coroutine: bool;
634 kinds: kind list;
637 let pModifiers node env =
638 let f (has_async, has_coroutine, kinds) node =
639 let add_kind k = k :: kinds in
640 match token_kind node with
641 | Some TK.Final -> (has_async, has_coroutine, add_kind Final)
642 | Some TK.Static -> (has_async, has_coroutine, add_kind Static)
643 | Some TK.Abstract -> (has_async, has_coroutine, add_kind Abstract)
644 | Some TK.Private -> (has_async, has_coroutine, add_kind Private)
645 | Some TK.Public -> (has_async, has_coroutine, add_kind Public)
646 | Some TK.Protected -> (has_async, has_coroutine, add_kind Protected)
647 | Some TK.Var -> (has_async, has_coroutine, add_kind Public)
648 | Some TK.Async -> (true, has_coroutine, kinds)
649 | Some TK.Coroutine -> (has_async, true, kinds)
650 | _ -> missing_syntax "kind" node env
652 let (has_async, has_coroutine, kinds) =
653 List.fold_left ~init:(false, false, []) ~f (as_list node)
655 { has_async; has_coroutine; kinds = List.rev kinds }
657 let pKinds node env = (pModifiers node env).kinds
659 let pParamKind : param_kind parser =
660 fun node env ->
661 match token_kind node with
662 | Some TK.Inout -> Pinout
663 | _ -> missing_syntax "param kind" node env
665 (* TODO: Clean up string escaping *)
666 let prepString2 env : node list -> node list =
667 let is_double_quote_or_backtick ch = ch = '"' || ch = '`' in
668 let is_binary_string_header s =
669 String.length s > 1 && s.[0] = 'b' && s.[1] = '"'
671 let trimLeft = Token.trim_left in
672 let trimRight = Token.trim_right in
673 function
674 | ({ syntax = Token t; _ } as node) :: ss
675 when Token.width t > 0
676 && ( is_double_quote_or_backtick (Token.text t).[0]
677 || is_binary_string_header (Token.text t) ) ->
678 let rec unwind = function
679 | [{ syntax = Token t; _ }]
680 when Token.width t > 0
681 && is_double_quote_or_backtick
682 (Token.text t).[Token.width t - 1] ->
683 let s = make_token (trimRight ~n:1 t) in
684 if width s > 0 then
686 else
688 | x :: xs -> x :: unwind xs
689 | _ ->
690 raise_parsing_error env (`Node node) "Malformed String2 SyntaxList";
693 (* Trim the starting b and double quote *)
694 let left_trim =
695 if (Token.text t).[0] = 'b' then
697 else
700 let s = make_token (trimLeft ~n:left_trim t) in
701 if width s > 0 then
702 s :: unwind ss
703 else
704 unwind ss
705 | ({ syntax = Token t; _ } as node) :: ss
706 when Token.width t > 3 && String.sub (Token.text t) 0 3 = "<<<" ->
707 let rec unwind = function
708 | [{ syntax = Token t; _ }] when Token.width t > 0 ->
709 let content = Token.text t in
710 let len = Token.width t in
711 let n = len - String.rindex_from_exn content (len - 2) '\n' in
712 let s = make_token (trimRight ~n t) in
713 if width s > 0 then
715 else
717 | x :: xs -> x :: unwind xs
718 | _ ->
719 raise_parsing_error env (`Node node) "Malformed String2 SyntaxList";
722 let content = Token.text t in
723 let n = String.index_exn content '\n' + 1 in
724 let s = make_token (trimLeft ~n t) in
725 if width s > 0 then
726 s :: unwind ss
727 else
728 unwind ss
729 | x -> x
731 (* unchanged *)
733 let mkStr env node : (string -> string) -> string -> string =
734 fun unescaper content ->
735 let content =
736 if String.length content > 0 && content.[0] = 'b' then
737 String.sub content 1 (String.length content - 1)
738 else
739 content
741 let len = String.length content in
742 let no_quotes =
743 Php_escaping.extract_unquoted_string ~start:0 ~len content
745 try unescaper no_quotes
746 with Php_escaping.Invalid_string _ ->
747 raise_parsing_error
749 (`Node node)
750 (Printf.sprintf "Malformed string literal <<%s>>" no_quotes);
753 let unempty_str = function
754 | "''"
755 | "\"\"" ->
757 | s -> s
759 let unesc_dbl s = unempty_str @@ Php_escaping.unescape_double s
761 let get_quoted_content s =
762 Str.(
763 if string_match quoted s 0 then
764 matched_group 1 s
765 else
768 let unesc_xhp s = Str.global_replace whitespace " " s
770 let unesc_xhp_attr s = unesc_dbl @@ get_quoted_content s
772 type suspension_kind =
773 | SKSync
774 | SKAsync
775 | SKCoroutine
777 let mk_suspension_kind_ node env has_async has_coroutine =
778 match (has_async, has_coroutine) with
779 | (false, false) -> SKSync
780 | (true, false) -> SKAsync
781 | (false, true) -> SKCoroutine
782 | (true, true) ->
783 raise_parsing_error
785 (`Node node)
786 "Coroutine functions may not be async";
787 SKCoroutine
789 let mk_suspension_kind node env is_async is_coroutine =
790 mk_suspension_kind_
791 node
793 (not (is_missing is_async))
794 (not (is_missing is_coroutine))
796 let mk_fun_kind suspension_kind yield =
797 match (suspension_kind, yield) with
798 | (SKSync, true) -> FGenerator
799 | (SKAsync, true) -> FAsyncGenerator
800 | (SKSync, false) -> FSync
801 | (SKAsync, false) -> FAsync
802 | (SKCoroutine, _) -> FCoroutine
804 (* Yield in coroutine is not permitted, the error will be reported at NastCheck *)
806 let fun_template yielding node suspension_kind env =
807 let p = pFunction node env in
809 f_mode = mode_annotation env.fi_mode;
810 f_tparams = [];
811 f_constrs = [];
812 f_ret = None;
813 f_name = (p, ";anonymous");
814 f_params = [];
815 f_body = [];
816 f_user_attributes = [];
817 f_file_attributes = [];
818 f_fun_kind = mk_fun_kind suspension_kind yielding;
819 f_namespace = mk_empty_ns_env env;
820 f_span = p;
821 f_doc_comment = None;
822 f_static = false;
823 f_external =
824 false
825 (* true if this declaration has no body
826 because it is an external function declaration
827 (e.g. from an HHI file)*);
830 let param_template node env =
832 param_hint = None;
833 param_is_reference = false;
834 param_is_variadic = false;
835 param_id = pos_name node env;
836 param_expr = None;
837 param_modifier = None;
838 param_callconv = None;
839 param_user_attributes = [];
842 let pShapeFieldName : shape_field_name parser =
843 fun name env ->
844 let is_valid_shape_literal t =
845 let is_str =
846 Token.kind t = TK.SingleQuotedStringLiteral
847 || Token.kind t = TK.DoubleQuotedStringLiteral
849 let is_empty =
850 let text = Token.text t in
851 text = "\'\'" || text = "\"\""
853 is_str && not is_empty
855 match syntax name with
856 | ScopeResolutionExpression
857 { scope_resolution_qualifier; scope_resolution_name; _ } ->
858 SFclass_const
859 ( pos_name scope_resolution_qualifier env,
860 pos_name scope_resolution_name env )
861 | LiteralExpression { literal_expression = { syntax = Token t; _ } }
862 when is_valid_shape_literal t ->
863 let (p, n) = pos_name name env in
864 let str = mkStr env name unesc_dbl n in
865 begin
866 match int_of_string_opt str with
867 | Some _ ->
868 raise_parsing_error
870 (`Node name)
871 SyntaxError.shape_field_int_like_string
872 | None -> ()
873 end;
874 SFlit_str (p, str)
875 | _ ->
876 raise_parsing_error env (`Node name) SyntaxError.invalid_shape_field_name;
877 let (p, n) = pos_name name env in
878 SFlit_str (p, mkStr env name unesc_dbl n)
880 let mpShapeExpressionField : ('a, shape_field_name * 'a) metaparser =
881 fun hintParser node env ->
882 match syntax node with
883 | FieldInitializer
884 { field_initializer_name = name; field_initializer_value = ty; _ } ->
885 let name = pShapeFieldName name env in
886 let ty = hintParser ty env in
887 (name, ty)
888 | _ -> missing_syntax "shape field" node env
890 let mpShapeField : ('a, shape_field) metaparser =
891 fun hintParser node env ->
892 match syntax node with
893 | FieldSpecifier { field_question; field_name; field_type; _ } ->
894 let sf_optional = not (is_missing field_question) in
895 let sf_name = pShapeFieldName field_name env in
896 let sf_hint = hintParser field_type env in
897 { sf_optional; sf_name; sf_hint }
898 | _ ->
899 let (sf_name, sf_hint) = mpShapeExpressionField hintParser node env in
900 (* Shape expressions can never have optional fields. *)
901 { sf_optional = false; sf_name; sf_hint }
903 let mpClosureParameter : ('a, hint * param_kind option) metaparser =
904 fun hintParser node env ->
905 match syntax node with
906 | ClosureParameterTypeSpecifier
907 { closure_parameter_call_convention; closure_parameter_type } ->
908 let cp_kind =
909 mpOptional pParamKind closure_parameter_call_convention env
911 let cp_hint = hintParser closure_parameter_type env in
912 (cp_hint, cp_kind)
913 | _ -> missing_syntax "closure parameter" node env
915 let fail_if_invalid_class_creation env node (_, id) =
916 if not !(env.in_static_method) then
918 else if
919 id = SN.Classes.cSelf
920 && (not @@ SSet.is_empty !(env.cls_reified_generics))
921 || (id = SN.Classes.cParent && !(env.parent_maybe_reified))
922 then
923 raise_parsing_error
925 (`Node node)
926 SyntaxError.static_method_reified_obj_creation
928 let fail_if_invalid_reified_generic env node (_, id) =
929 if not !(env.in_static_method) then
931 else if SSet.mem id !(env.cls_reified_generics) then
932 raise_parsing_error
934 (`Node node)
935 SyntaxError.cls_reified_generic_in_static_method
937 let check_valid_reified_hint env node h =
938 if not !(env.in_static_method) then
940 else
941 let reified_hint_visitor =
942 object (self)
943 inherit [_] iter as super
945 method! on_hint env hint =
946 match snd hint with
947 | Happly (id, hl) ->
948 fail_if_invalid_reified_generic env node id;
949 List.iter hl ~f:(self#on_hint env)
950 | Haccess (id1, id2, ids) ->
951 fail_if_invalid_reified_generic env node id1;
952 fail_if_invalid_reified_generic env node id2;
953 List.iter ids ~f:(fail_if_invalid_reified_generic env node)
954 | _ -> super#on_hint env hint
957 reified_hint_visitor#on_hint env h
959 type fun_hdr = {
960 fh_suspension_kind: suspension_kind;
961 fh_name: pstring;
962 fh_constrs: (hint * constraint_kind * hint) list;
963 fh_type_parameters: tparam list;
964 fh_parameters: fun_param list;
965 fh_return_type: hint option;
966 fh_param_modifiers: fun_param list;
969 let empty_fun_hdr =
971 fh_suspension_kind = SKSync;
972 fh_name = (Pos.none, "<ANONYMOUS>");
973 fh_constrs = [];
974 fh_type_parameters = [];
975 fh_parameters = [];
976 fh_return_type = None;
977 fh_param_modifiers = [];
980 let check_intrinsic_type_arg_varity env node ty =
981 match ty with
982 | [tk; tv] -> Some (CollectionTKV (tk, tv))
983 | [tv] -> Some (CollectionTV tv)
984 | [] -> None
985 | _ ->
986 raise_parsing_error
988 (`Node node)
989 SyntaxError.collection_intrinsic_many_typeargs;
990 None
992 let rec pHint : hint parser =
993 fun node env ->
994 let rec pHint_ : hint_ parser =
995 fun node env ->
996 match syntax node with
997 (* Dirty hack; CastExpression can have type represented by token *)
998 | Token _
999 | SimpleTypeSpecifier _
1000 | QualifiedName _ ->
1001 let (pos, name) = pos_name node env in
1002 let hint = String.lowercase name in
1003 let suggest canonical =
1004 raise_parsing_error
1006 (`Node node)
1007 (SyntaxError.invalid_typehint_alias hint canonical)
1009 if hint = SN.Typehints.integer then
1010 suggest SN.Typehints.int
1011 else if hint = SN.Typehints.boolean then
1012 suggest SN.Typehints.bool
1013 else if hint = SN.Typehints.double then
1014 suggest SN.Typehints.float
1015 else if hint = SN.Typehints.real then
1016 suggest SN.Typehints.float;
1017 Happly ((pos, name), [])
1018 | ShapeTypeSpecifier { shape_type_fields; shape_type_ellipsis; _ } ->
1019 let si_allows_unknown_fields = not (is_missing shape_type_ellipsis) in
1020 (* if last element lacks a separator and ellipsis is present, error *)
1021 Option.iter
1022 (List.last (syntax_to_list true shape_type_fields))
1023 (fun last ->
1024 if is_missing last && si_allows_unknown_fields then
1025 raise_parsing_error
1027 (`Node node)
1028 SyntaxError.shape_type_ellipsis_without_trailing_comma);
1029 let si_shape_field_list =
1030 couldMap ~f:(mpShapeField pHint) shape_type_fields env
1032 Hshape { si_allows_unknown_fields; si_shape_field_list }
1033 | TupleTypeSpecifier { tuple_types; _ } ->
1034 Htuple (couldMap ~f:pHint tuple_types env)
1035 | UnionTypeSpecifier { union_types; _ } ->
1036 Hunion (couldMap ~f:pHint union_types env)
1037 | IntersectionTypeSpecifier { intersection_types; _ } ->
1038 Hintersection (couldMap ~f:pHint intersection_types env)
1039 | KeysetTypeSpecifier
1040 { keyset_type_keyword = kw; keyset_type_type = ty; _ }
1041 | VectorTypeSpecifier
1042 { vector_type_keyword = kw; vector_type_type = ty; _ }
1043 | ClassnameTypeSpecifier
1044 { classname_keyword = kw; classname_type = ty; _ }
1045 | TupleTypeExplicitSpecifier
1046 { tuple_type_keyword = kw; tuple_type_types = ty; _ }
1047 | VarrayTypeSpecifier { varray_keyword = kw; varray_type = ty; _ }
1048 | VectorArrayTypeSpecifier
1049 { vector_array_keyword = kw; vector_array_type = ty; _ } ->
1050 Happly (pos_name kw env, couldMap ~f:pHint ty env)
1051 | DarrayTypeSpecifier
1052 { darray_keyword = kw; darray_key = key; darray_value = value; _ }
1053 | MapArrayTypeSpecifier
1055 map_array_keyword = kw;
1056 map_array_key = key;
1057 map_array_value = value;
1059 } ->
1060 Happly (pos_name kw env, pHint key env :: couldMap ~f:pHint value env)
1061 | DictionaryTypeSpecifier
1063 dictionary_type_keyword = kw;
1064 dictionary_type_members = members;
1066 } ->
1067 Happly (pos_name kw env, couldMap ~f:pHint members env)
1068 | GenericTypeSpecifier { generic_class_type; generic_argument_list } ->
1069 let name = pos_name generic_class_type env in
1070 let type_args =
1071 match syntax generic_argument_list with
1072 | TypeArguments { type_arguments_types; _ } ->
1073 couldMap ~f:pHint type_arguments_types env
1074 | _ ->
1075 missing_syntax "generic type arguments" generic_argument_list env
1077 if env.codegen then
1078 match (String.lowercase (snd name), type_args) with
1079 | (("rx" | "rxlocal" | "rxshallow"), [(_, (Hfun _ as t))])
1080 | ( ("mutable" | "maybemutable" | "ownedmutable"),
1081 [(_, (Happly _ as t))] ) ->
1083 | _ -> Happly (name, type_args)
1084 else
1085 Happly (name, type_args)
1086 | NullableTypeSpecifier { nullable_type; _ } ->
1087 Hoption (pHint nullable_type env)
1088 | LikeTypeSpecifier { like_type; _ } -> Hlike (pHint like_type env)
1089 | SoftTypeSpecifier { soft_type; _ } -> Hsoft (pHint soft_type env)
1090 | ClosureTypeSpecifier
1091 { closure_parameter_list; closure_return_type; closure_coroutine; _ }
1093 let (param_list, variadic_hints) =
1094 List.partition_map
1095 ~f:(fun x ->
1096 match syntax x with
1097 | VariadicParameter { variadic_parameter_type = vtype; _ } ->
1098 if is_missing vtype then
1099 raise_parsing_error
1101 (`Node x)
1102 "Cannot use ... without a typehint";
1103 `Snd (Some (pHint vtype env))
1104 | _ -> `Fst (mpClosureParameter pHint x env))
1105 (as_list closure_parameter_list)
1107 let hd_variadic_hint hints =
1108 ( if List.length hints > 1 then
1109 let msg =
1110 Printf.sprintf
1111 "%d variadic parameters found. There should be no more than one."
1112 (List.length hints)
1114 invariant_failure node msg env );
1115 match List.hd hints with
1116 | Some h -> h
1117 | None -> None
1119 let is_coroutine = not (is_missing closure_coroutine) in
1120 let param_type_hints = List.map param_list fst in
1121 let param_callconvs = List.map param_list snd in
1122 Hfun
1123 ( is_coroutine,
1124 param_type_hints,
1125 param_callconvs,
1126 hd_variadic_hint variadic_hints,
1127 pHint closure_return_type env )
1128 | AttributizedSpecifier
1130 attributized_specifier_attribute_spec = attr_spec;
1131 attributized_specifier_type = attr_type;
1132 } ->
1133 let attrs = pUserAttributes env attr_spec in
1134 let hint = pHint attr_type env in
1135 if List.exists attrs ~f:(fun { ua_name = (_, s); _ } -> s <> "__Soft")
1136 then
1137 raise_parsing_error env (`Node node) SyntaxError.only_soft_allowed;
1138 let (_, hint_) = soften_hint attrs hint in
1139 hint_
1140 | TypeConstant { type_constant_left_type; type_constant_right_type; _ }
1142 let child = pos_name type_constant_right_type env in
1143 (match pHint_ type_constant_left_type env with
1144 | Haccess (b, c, cs) -> Haccess (b, c, cs @ [child])
1145 | Happly (b, []) -> Haccess (b, child, [])
1146 | _ -> missing_syntax "type constant base" node env)
1147 | PUAccess { pu_access_left_type; pu_access_right_type; _ } ->
1148 let pos = pPos pu_access_left_type env in
1149 let child = pos_name pu_access_right_type env in
1150 (match pHint_ pu_access_left_type env with
1151 | Hpu_access (h, id) -> Hpu_access ((pos, Hpu_access (h, id)), child)
1152 | Happly (_, []) as head -> Hpu_access ((pos, head), child)
1153 | _ -> missing_syntax "pocket universe access base" node env)
1154 | ReifiedTypeArgument _ ->
1155 raise_parsing_error env (`Node node) SyntaxError.invalid_reified;
1156 missing_syntax "reified type" node env
1157 | _ -> missing_syntax "type hint" node env
1159 let hint = (pPos node env, pHint_ node env) in
1160 check_valid_reified_hint env node hint;
1161 hint
1163 and expand_type_args env ty or_else =
1164 match syntax ty with
1165 | TypeArguments { type_arguments_types; _ } ->
1166 couldMap ~f:pHint type_arguments_types env
1167 | _ -> or_else ()
1169 and pSimpleInitializer node env =
1170 match syntax node with
1171 | SimpleInitializer { simple_initializer_value; _ } ->
1172 pExpr simple_initializer_value env
1173 | _ -> missing_syntax "simple initializer" node env
1175 and pFunParamDefaultValue node env =
1176 match syntax node with
1177 | SimpleInitializer { simple_initializer_value; _ } ->
1178 mpOptional pExpr simple_initializer_value env
1179 | _ -> None
1181 and pFunParam : fun_param parser =
1182 fun node env ->
1183 match syntax node with
1184 | ParameterDeclaration
1186 parameter_attribute;
1187 parameter_visibility;
1188 parameter_call_convention;
1189 parameter_type;
1190 parameter_name;
1191 parameter_default_value;
1192 } ->
1193 let (is_reference, is_variadic, name) =
1194 match syntax parameter_name with
1195 | DecoratedExpression
1196 { decorated_expression_decorator; decorated_expression_expression }
1198 (* There is a chance that the expression might be nested with an
1199 additional decorator, check this *)
1200 begin
1201 match syntax decorated_expression_expression with
1202 | DecoratedExpression
1204 decorated_expression_decorator = nested_decorator;
1205 decorated_expression_expression = nested_expression;
1206 } ->
1207 let decorator = text decorated_expression_decorator in
1208 let nested_decorator = text nested_decorator in
1209 ( decorator = "&" || nested_decorator = "&",
1210 decorator = "..." || nested_decorator = "...",
1211 nested_expression )
1212 | _ ->
1213 let decorator = text decorated_expression_decorator in
1214 ( decorator = "&",
1215 decorator = "...",
1216 decorated_expression_expression )
1218 | _ -> (false, false, parameter_name)
1220 let param_user_attributes = pUserAttributes env parameter_attribute in
1221 let param_hint =
1222 mpOptional pHint parameter_type env
1223 |> Option.map ~f:(soften_hint param_user_attributes)
1225 if is_variadic && not (List.is_empty param_user_attributes) then
1226 raise_parsing_error
1228 (`Node node)
1229 SyntaxError.no_attributes_on_variadic_parameter;
1231 param_hint;
1232 param_user_attributes;
1233 param_is_reference = is_reference;
1234 param_is_variadic = is_variadic;
1235 param_id = pos_name name env;
1236 param_expr = pFunParamDefaultValue parameter_default_value env;
1237 param_callconv =
1238 mpOptional pParamKind parameter_call_convention env
1239 (* implicit field via constructor parameter.
1240 * This is always None except for constructors and the modifier
1241 * can be only Public or Protected or Private.
1243 param_modifier =
1244 (let rec go = function
1245 | [] -> None
1246 | x :: _ when List.mem [Private; Public; Protected] x ~equal:( = )
1248 Some x
1249 | _ :: xs -> go xs
1251 go (pKinds parameter_visibility env));
1253 | VariadicParameter _
1254 | Token _
1255 when text node = "..." ->
1256 { (param_template node env) with param_is_variadic = true }
1257 | _ -> missing_syntax "function parameter" node env
1259 and process_attribute_constructor_call
1260 node constructor_call_argument_list constructor_call_type env =
1261 let ua_name = pos_name constructor_call_type env in
1262 let name = String.lowercase (snd ua_name) in
1263 if name = "__reified" || name = "__hasreifiedparent" then
1264 raise_parsing_error env (`Node node) SyntaxError.reified_attribute
1265 else if
1266 name = "__soft"
1267 && List.length (as_list constructor_call_argument_list) > 0
1268 then
1269 raise_parsing_error env (`Node node) SyntaxError.soft_no_arguments;
1270 let ua_params =
1271 couldMap constructor_call_argument_list env ~f:(fun p ->
1272 begin
1273 match syntax p with
1274 | ScopeResolutionExpression
1275 { scope_resolution_name = { syntax = Token t; _ }; _ }
1276 when Token.kind t = TK.Name ->
1277 raise_parsing_error
1279 (`Node p)
1280 SyntaxError.constants_as_attribute_arguments
1281 | Token t when Token.kind t = TK.Name ->
1282 raise_parsing_error
1284 (`Node p)
1285 SyntaxError.constants_as_attribute_arguments
1286 | _ -> ()
1287 end;
1288 pExpr p)
1290 { ua_name; ua_params }
1292 and pUserAttribute : user_attribute list parser =
1293 fun node env ->
1294 match syntax node with
1295 | FileAttributeSpecification
1296 { file_attribute_specification_attributes = attrs; _ }
1297 | OldAttributeSpecification
1298 { old_attribute_specification_attributes = attrs; _ } ->
1299 couldMap attrs env ~f:(function
1301 syntax =
1302 ConstructorCall
1303 { constructor_call_argument_list; constructor_call_type; _ };
1305 } ->
1306 process_attribute_constructor_call
1307 node
1308 constructor_call_argument_list
1309 constructor_call_type
1310 | _ -> missing_syntax "attribute" node)
1311 | AttributeSpecification { attribute_specification_attributes = attrs; _ }
1313 couldMap attrs env ~f:(function
1315 syntax =
1316 Attribute
1318 attribute_attribute_name =
1320 syntax =
1321 ConstructorCall
1323 constructor_call_argument_list;
1324 constructor_call_type;
1332 } ->
1333 process_attribute_constructor_call
1334 node
1335 constructor_call_argument_list
1336 constructor_call_type
1337 | node -> missing_syntax "attribute" node)
1338 | _ -> missing_syntax "attribute specification" node env
1340 and pUserAttributes env attrs =
1341 List.concat @@ couldMap ~f:pUserAttribute attrs env
1343 and soften_hint attrs ((pos, _) as hint) =
1344 let should_soften =
1345 List.exists attrs ~f:(fun { ua_name = (_, s); _ } -> s = "__Soft")
1347 if should_soften then
1348 (pos, Hsoft hint)
1349 else
1350 hint
1352 and pAField : afield parser =
1353 fun node env ->
1354 match syntax node with
1355 | ElementInitializer { element_key; element_value; _ } ->
1356 AFkvalue (pExpr element_key env, pExpr element_value env)
1357 | _ -> AFvalue (pExpr node env)
1359 and pString2 : expr_location -> node list -> env -> expr list =
1360 let rec aux loc l env acc =
1361 (* "${x}" syntax is banned in Hack in favor of "{$x}". *)
1362 match l with
1363 | [] -> List.rev acc
1364 | { syntax = Token token; _ }
1365 :: ({ syntax = EmbeddedBracedExpression _; _ } as expr_with_braces)
1366 :: tl
1367 when Token.kind token = TK.Dollar ->
1368 raise_parsing_error
1370 (`Node expr_with_braces)
1371 SyntaxError.outside_dollar_str_interp;
1372 aux loc tl env (pExpr ~location:loc expr_with_braces env :: acc)
1373 | x :: xs -> aux loc xs env (pExpr ~location:loc x env :: acc)
1375 (fun loc l env -> aux loc l env [])
1377 and pExprL ?(location = TopLevel) : expr parser =
1378 fun node env ->
1379 (pPos node env, Expr_list (couldMap ~f:(pExpr ~location) node env))
1381 (* TODO: this function is a hotspot, deep recursion on huge files, attempt more optimization *)
1382 and pMember node env =
1383 match syntax node with
1384 | ElementInitializer { element_key; element_value; _ } ->
1385 (pExpr element_key env, pExpr element_value env)
1386 | _ -> missing_syntax "darray intrinsic expression element" node env
1388 and pExpr ?(location = TopLevel) : expr parser =
1389 fun node env ->
1390 let split_args_varargs arg_list =
1391 match List.rev (as_list arg_list) with
1393 syntax =
1394 DecoratedExpression
1396 decorated_expression_decorator = { syntax = Token token; _ };
1397 decorated_expression_expression = e;
1401 :: xs
1402 when Token.kind token = TK.DotDotDot ->
1403 let args = List.rev_map xs (fun x -> pExpr x env) in
1404 let vararg = pExpr e env in
1405 (args, [vararg])
1406 | _ ->
1407 let args = couldMap ~f:pExpr arg_list env in
1408 (args, [])
1410 let rec pExpr_ : expr_ parser =
1411 fun node env ->
1412 env.recursion_depth := !(env.recursion_depth) + 1;
1413 if !(env.recursion_depth) > recursion_limit then
1414 failwith "Expression recursion limit reached";
1415 let pos = pPos node env in
1416 let result =
1417 match syntax node with
1418 | LambdaExpression
1420 lambda_async;
1421 lambda_coroutine;
1422 lambda_signature;
1423 lambda_body;
1424 lambda_attribute_spec;
1426 } ->
1427 let suspension_kind =
1428 mk_suspension_kind node env lambda_async lambda_coroutine
1430 let (f_params, f_ret) =
1431 match syntax lambda_signature with
1432 | LambdaSignature { lambda_parameters; lambda_type; _ } ->
1433 ( couldMap ~f:pFunParam lambda_parameters env,
1434 mpOptional pHint lambda_type env )
1435 | Token _ -> ([param_template lambda_signature env], None)
1436 | _ -> missing_syntax "lambda signature" lambda_signature env
1438 let (f_body, yield) =
1439 mpYielding
1440 pFunctionBody
1441 lambda_body
1442 ( if not (is_compound_statement lambda_body) then
1444 else
1445 non_tls env )
1447 let f_external = is_external lambda_body in
1448 Lfun
1450 (fun_template yield node suspension_kind env) with
1451 f_ret;
1452 f_params;
1453 f_body;
1454 f_user_attributes = pUserAttributes env lambda_attribute_spec;
1455 f_external;
1457 | BracedExpression { braced_expression_expression = expr; _ }
1458 | EmbeddedBracedExpression
1459 { embedded_braced_expression_expression = expr; _ }
1460 | ParenthesizedExpression
1461 { parenthesized_expression_expression = expr; _ } ->
1462 pExpr_ expr env
1463 | DictionaryIntrinsicExpression
1465 dictionary_intrinsic_keyword = kw;
1466 dictionary_intrinsic_explicit_type = ty;
1467 dictionary_intrinsic_members = members;
1470 | KeysetIntrinsicExpression
1472 keyset_intrinsic_keyword = kw;
1473 keyset_intrinsic_explicit_type = ty;
1474 keyset_intrinsic_members = members;
1477 | VectorIntrinsicExpression
1479 vector_intrinsic_keyword = kw;
1480 vector_intrinsic_explicit_type = ty;
1481 vector_intrinsic_members = members;
1483 } ->
1484 let hints = expand_type_args env ty (fun () -> []) in
1485 let hints = check_intrinsic_type_arg_varity env node hints in
1486 Collection (pos_name kw env, hints, couldMap ~f:pAField members env)
1487 | CollectionLiteralExpression
1489 collection_literal_name = collection_name;
1490 collection_literal_initializers = members;
1492 } ->
1493 let hints = None in
1494 let (collection_name, hints) =
1495 match syntax collection_name with
1496 | SimpleTypeSpecifier { simple_type_specifier = class_type } ->
1497 (pos_name class_type env, hints)
1498 | GenericTypeSpecifier
1499 { generic_class_type = class_type; generic_argument_list } ->
1500 let hints =
1501 expand_type_args env generic_argument_list (fun () -> [])
1503 let hints = check_intrinsic_type_arg_varity env node hints in
1504 (pos_name class_type env, hints)
1505 | _ -> (pos_name collection_name env, hints)
1507 Collection (collection_name, hints, couldMap ~f:pAField members env)
1508 | VarrayIntrinsicExpression
1510 varray_intrinsic_members = members;
1511 varray_intrinsic_explicit_type = ty;
1513 } ->
1514 let hints = expand_type_args env ty (fun () -> []) in
1515 let hints = check_intrinsic_type_arg_varity env node hints in
1516 let targ =
1517 match hints with
1518 | Some (CollectionTV ty) -> Some ty
1519 | None -> None
1520 | _ ->
1521 missing_syntax "VarrayIntrinsicExpression type args" node env
1523 Varray (targ, couldMap ~f:pExpr members env)
1524 | DarrayIntrinsicExpression
1526 darray_intrinsic_members = members;
1527 darray_intrinsic_explicit_type = ty;
1529 } ->
1530 let hints = expand_type_args env ty (fun () -> []) in
1531 let hints = check_intrinsic_type_arg_varity env node hints in
1532 begin
1533 match hints with
1534 | Some (CollectionTKV (tk, tv)) ->
1535 Darray (Some (tk, tv), couldMap ~f:pMember members env)
1536 | None -> Darray (None, couldMap ~f:pMember members env)
1537 | _ ->
1538 missing_syntax "DarrayIntrinsicExpression type args" node env
1540 | ArrayIntrinsicExpression { array_intrinsic_members = members; _ }
1541 | ArrayCreationExpression { array_creation_members = members; _ } ->
1542 (* TODO: Or tie in with other intrinsics and post-process to Array *)
1543 Array (couldMap ~f:pAField members env)
1544 | ListExpression { list_members = members; _ } ->
1545 (* TODO: Or tie in with other intrinsics and post-process to List *)
1546 let pBinderOrIgnore node env =
1547 match syntax node with
1548 | Missing -> (Pos.none, Omitted)
1549 | _ -> pExpr node env
1551 List (couldMap ~f:pBinderOrIgnore members env)
1552 | EvalExpression { eval_keyword = recv; eval_argument = args; _ }
1553 | IssetExpression
1554 { isset_keyword = recv; isset_argument_list = args; _ }
1555 | TupleExpression
1557 tuple_expression_keyword = recv;
1558 tuple_expression_items = args;
1560 } ->
1561 let pos_if_has_parens =
1562 match syntax recv with
1563 | ParenthesizedExpression _ -> Some (pPos recv env)
1564 | _ -> None
1566 let recv = pExpr recv env in
1567 let recv =
1568 match (snd recv, pos_if_has_parens) with
1569 | ((Obj_get _ | Class_get _), Some p) -> (p, ParenthesizedExpr recv)
1570 | _ -> recv
1572 let (args, varargs) = split_args_varargs args in
1573 Call (recv, [], args, varargs)
1574 | FunctionCallExpression
1576 function_call_receiver = recv;
1577 function_call_argument_list =
1579 syntax =
1580 SyntaxList
1583 syntax =
1584 ListItem
1586 list_item =
1588 syntax =
1589 LiteralExpression
1590 { literal_expression = expr };
1602 when text recv = "__hhas_adata"
1603 && token_kind expr = Some TK.NowdocStringLiteral ->
1604 let literal_expression_pos = pPos expr env in
1605 let s =
1606 expr
1607 |> source_text
1608 |> SourceText.text
1609 |> Php_escaping.extract_unquoted_string
1610 ~start:(start_offset expr)
1611 ~len:(width expr)
1613 Call (pExpr recv env, [], [(literal_expression_pos, String s)], [])
1614 | FunctionCallExpression
1616 function_call_receiver = recv;
1617 function_call_type_args = type_args;
1618 function_call_argument_list = args;
1620 } ->
1621 let hints =
1622 match (syntax recv, syntax type_args) with
1623 | (_, TypeArguments { type_arguments_types; _ }) ->
1624 couldMap ~f:pHint type_arguments_types env
1625 (* TODO might not be needed *)
1626 | (GenericTypeSpecifier { generic_argument_list; _ }, _) ->
1627 begin
1628 match syntax generic_argument_list with
1629 | TypeArguments { type_arguments_types; _ } ->
1630 couldMap ~f:pHint type_arguments_types env
1631 | _ -> []
1633 | _ -> []
1635 (* preserve parens on receiver of call expression
1636 to allow distinguishing between
1637 ($a->b)() // invoke on callable property
1638 $a->b() // method call *)
1639 let pos_if_has_parens =
1640 match syntax recv with
1641 | ParenthesizedExpression _ -> Some (pPos recv env)
1642 | _ -> None
1644 let recv = pExpr recv env in
1645 let recv =
1646 match (snd recv, pos_if_has_parens) with
1647 | ((Obj_get _ | Class_get _), Some p) -> (p, ParenthesizedExpr recv)
1648 | _ -> recv
1650 let (args, varargs) = split_args_varargs args in
1651 Call (recv, hints, args, varargs)
1652 | QualifiedName _ ->
1653 if in_string location then
1654 let (_, n) = pos_qualified_name node env in
1655 String n
1656 else
1657 Id (pos_qualified_name node env)
1658 | VariableExpression { variable_expression } ->
1659 Lvar (pos_name variable_expression env)
1660 | PipeVariableExpression _ -> Lvar (pos, "$$")
1661 | InclusionExpression { inclusion_require; inclusion_filename } ->
1662 Import
1663 (pImportFlavor inclusion_require env, pExpr inclusion_filename env)
1664 | MemberSelectionExpression
1665 { member_object = recv; member_operator = op; member_name = name }
1666 | SafeMemberSelectionExpression
1668 safe_member_object = recv;
1669 safe_member_operator = op;
1670 safe_member_name = name;
1672 | EmbeddedMemberSelectionExpression
1674 embedded_member_object = recv;
1675 embedded_member_operator = op;
1676 embedded_member_name = name;
1677 } ->
1678 if is_object_creation_expression recv && not env.codegen then
1679 raise_parsing_error
1681 (`Node recv)
1682 SyntaxError.invalid_constructor_method_call;
1683 let recv = pExpr recv env in
1684 let name = pExpr ~location:MemberSelect name env in
1685 let op = pNullFlavor op env in
1686 Obj_get (recv, name, op)
1687 | PrefixUnaryExpression
1689 prefix_unary_operator = operator;
1690 prefix_unary_operand = operand;
1692 | PostfixUnaryExpression
1694 postfix_unary_operand = operand;
1695 postfix_unary_operator = operator;
1697 | DecoratedExpression
1699 decorated_expression_expression = operand;
1700 decorated_expression_decorator = operator;
1701 } ->
1702 let expr = pExpr operand env in
1704 * FFP does not destinguish between ++$i and $i++ on the level of token
1705 * kind annotation. Prevent duplication by switching on `postfix` for
1706 * the two operatores for which AST /does/ differentiate between
1707 * fixities.
1709 let postfix = kind node = SyntaxKind.PostfixUnaryExpression in
1710 let kind = token_kind operator in
1711 (match kind with
1712 | Some TK.PlusPlus when postfix -> Unop (Upincr, expr)
1713 | Some TK.MinusMinus when postfix -> Unop (Updecr, expr)
1714 | Some TK.PlusPlus -> Unop (Uincr, expr)
1715 | Some TK.MinusMinus -> Unop (Udecr, expr)
1716 | Some TK.Exclamation -> Unop (Unot, expr)
1717 | Some TK.Tilde -> Unop (Utild, expr)
1718 | Some TK.Plus -> Unop (Uplus, expr)
1719 | Some TK.Minus -> Unop (Uminus, expr)
1720 | Some TK.Ampersand -> Unop (Uref, expr)
1721 | Some TK.At ->
1722 if ParserOptions.disallow_silence env.parser_options then
1723 raise_parsing_error env (`Node operator) SyntaxError.no_silence;
1724 if env.codegen then
1725 Unop (Usilence, expr)
1726 else
1727 snd expr
1728 | Some TK.Inout -> Callconv (Pinout, expr)
1729 | Some TK.Await -> lift_await expr env location
1730 | Some TK.Suspend -> Suspend expr
1731 | Some TK.Clone -> Clone expr
1732 | Some TK.Print -> Call ((pos, Id (pos, "echo")), [], [expr], [])
1733 | Some TK.Dollar ->
1734 (match expr with
1735 | (p, String s)
1736 | (p, Int s)
1737 | (p, Float s) ->
1738 if not env.codegen then
1739 raise_parsing_error
1741 (`Node operator)
1742 SyntaxError.invalid_variable_name;
1743 Lvar (p, "$" ^ s)
1744 | _ ->
1745 raise_parsing_error
1747 (`Node operator)
1748 SyntaxError.invalid_variable_variable;
1749 Omitted)
1750 | _ -> missing_syntax "unary operator" node env)
1751 | BinaryExpression
1752 { binary_left_operand; binary_operator; binary_right_operand } ->
1753 let bop_ast_node =
1754 let rlocation =
1755 if token_kind binary_operator = Some TK.Equal then
1756 match location with
1757 | AsStatement -> RightOfAssignment
1758 | UsingStatement -> RightOfAssignmentInUsingStatement
1759 | _ -> TopLevel
1760 else
1761 TopLevel
1763 pBop
1764 binary_operator
1766 (pExpr binary_left_operand env)
1767 (pExpr binary_right_operand ~location:rlocation env)
1769 begin
1770 match bop_ast_node with
1771 | Binop (Eq _, lhs, _) ->
1772 Ast_check.check_lvalue
1773 (fun pos error -> raise_parsing_error env (`Pos pos) error)
1775 | _ -> ()
1776 end;
1777 bop_ast_node
1778 | Token t ->
1779 (match (location, Token.kind t) with
1780 | (MemberSelect, TK.Variable) -> Lvar (pos_name node env)
1781 | (InDoubleQuotedString, TK.HeredocStringLiteral)
1782 | (InDoubleQuotedString, TK.HeredocStringLiteralHead)
1783 | (InDoubleQuotedString, TK.HeredocStringLiteralTail) ->
1784 String (Php_escaping.unescape_heredoc (text node))
1785 | (InDoubleQuotedString, _) -> String (unesc_dbl (text node))
1786 | (InBacktickedString, _) ->
1787 String (Php_escaping.unescape_backtick (text node))
1788 | (MemberSelect, _)
1789 | (TopLevel, _)
1790 | (AsStatement, _)
1791 | (UsingStatement, _)
1792 | (RightOfAssignment, _)
1793 | (RightOfAssignmentInUsingStatement, _)
1794 | (RightOfReturn, _) ->
1795 Id (pos_name node env))
1796 | YieldExpression { yield_operand; _ } ->
1797 env.saw_yield <- true;
1799 location <> AsStatement
1800 && location <> RightOfAssignment
1801 && location <> RightOfAssignmentInUsingStatement
1802 then
1803 raise_parsing_error env (`Node node) SyntaxError.invalid_yield;
1804 if text yield_operand = "break" then
1805 Yield_break
1806 else if is_missing yield_operand then
1807 Yield (AFvalue (pos, Null))
1808 else
1809 Yield (pAField yield_operand env)
1810 | YieldFromExpression { yield_from_operand; _ } ->
1811 env.saw_yield <- true;
1813 location <> AsStatement
1814 && location <> RightOfAssignment
1815 && location <> RightOfAssignmentInUsingStatement
1816 && location <> RightOfReturn
1817 then
1818 raise_parsing_error env (`Node node) SyntaxError.invalid_yield_from;
1819 Yield_from (pExpr yield_from_operand env)
1820 | DefineExpression { define_keyword; define_argument_list; _ } ->
1821 Call
1822 ( (let name = pos_name define_keyword env in
1823 (fst name, Id name)),
1825 List.map ~f:(fun x -> pExpr x env) (as_list define_argument_list),
1826 [] )
1827 | ScopeResolutionExpression
1828 { scope_resolution_qualifier; scope_resolution_name; _ } ->
1829 let qual =
1830 match pExpr scope_resolution_qualifier env with
1831 | (p, Lvar v) when not env.codegen -> (p, Id v)
1832 | qual -> qual
1834 begin
1835 match qual with
1836 | (_, Id x) -> fail_if_invalid_reified_generic env node x
1837 | _ -> ()
1838 end;
1839 begin
1840 match syntax scope_resolution_name with
1841 | Token token when Token.kind token = TK.Variable ->
1842 let name =
1843 ( pPos scope_resolution_name env,
1844 Lvar (pos_name scope_resolution_name env) )
1846 Class_get (qual, name)
1847 | _ ->
1848 let name = pExpr scope_resolution_name env in
1849 begin
1850 match name with
1851 | (p, String id)
1852 | (_, Id (p, id)) ->
1853 Class_const (qual, (p, id))
1854 | _ -> Class_get (qual, name)
1857 | CastExpression { cast_type; cast_operand; _ } ->
1858 Cast (pHint cast_type env, pExpr cast_operand env)
1859 | ConditionalExpression
1861 conditional_test;
1862 conditional_consequence;
1863 conditional_alternative;
1865 } ->
1867 ( pExpr conditional_test env,
1868 mpOptional pExpr conditional_consequence env,
1869 pExpr conditional_alternative env )
1870 | SubscriptExpression { subscript_receiver; subscript_index; _ } ->
1871 Array_get
1872 (pExpr subscript_receiver env, mpOptional pExpr subscript_index env)
1873 | EmbeddedSubscriptExpression
1874 { embedded_subscript_receiver; embedded_subscript_index; _ } ->
1875 Array_get
1876 ( pExpr embedded_subscript_receiver env,
1877 mpOptional (pExpr ~location) embedded_subscript_index env )
1878 | ShapeExpression { shape_expression_fields; _ } ->
1879 Shape
1880 (couldMap
1881 ~f:(mpShapeExpressionField pExpr)
1882 shape_expression_fields
1883 env)
1884 | ObjectCreationExpression { object_creation_object = obj; _ } ->
1885 pExpr_ obj env
1886 | ConstructorCall
1887 { constructor_call_argument_list; constructor_call_type; _ } ->
1888 let (args, varargs) =
1889 split_args_varargs constructor_call_argument_list
1891 let (e, hl) =
1892 match syntax constructor_call_type with
1893 | GenericTypeSpecifier
1894 { generic_class_type; generic_argument_list } ->
1895 let name = pos_name generic_class_type env in
1896 let hints =
1897 match syntax generic_argument_list with
1898 | TypeArguments { type_arguments_types; _ } ->
1899 couldMap ~f:pHint type_arguments_types env
1900 | _ ->
1901 missing_syntax
1902 "generic type arguments"
1903 generic_argument_list
1906 ((fst name, Id name), hints)
1907 | SimpleTypeSpecifier _ ->
1908 let name = pos_name constructor_call_type env in
1909 ((fst name, Id name), [])
1910 | _ -> (pExpr constructor_call_type env, [])
1912 (match snd e with
1913 | Id name ->
1914 fail_if_invalid_reified_generic env node name;
1915 fail_if_invalid_class_creation env node name
1916 | _ -> ());
1917 New (e, hl, args, varargs)
1918 | GenericTypeSpecifier { generic_class_type; generic_argument_list } ->
1919 if not (is_missing generic_argument_list) then
1920 raise_parsing_error
1922 (`Node generic_argument_list)
1923 SyntaxError.targs_not_allowed;
1924 let name = pos_name generic_class_type env in
1925 Id name
1926 | RecordCreationExpression
1928 record_creation_type = rec_type;
1929 record_creation_members = members;
1930 record_creation_array_token = array_token;
1932 } ->
1933 let e =
1934 match syntax rec_type with
1935 | SimpleTypeSpecifier _ ->
1936 let name = pos_name rec_type env in
1937 (fst name, Id name)
1938 | _ -> pExpr rec_type env
1940 let is_record_array = token_kind array_token = Some TK.At in
1941 Record (e, is_record_array, couldMap ~f:pMember members env)
1942 | LiteralExpression { literal_expression = expr } ->
1943 (match syntax expr with
1944 | Token _ ->
1945 let s = text expr in
1946 (match (location, token_kind expr) with
1947 (* TODO(T21285960): Inside strings, int indices "should" be string indices *)
1948 | (InDoubleQuotedString, _) when env.codegen ->
1949 String (mkStr env expr unesc_dbl s)
1950 | (InBacktickedString, _) when env.codegen ->
1951 String (mkStr env expr Php_escaping.unescape_backtick s)
1952 | (_, Some TK.OctalLiteral)
1953 when is_typechecker env
1954 && String_utils.fold_left
1955 ~f:(fun b c -> b || c = '8' || c = '9')
1956 ~acc:false
1957 s ->
1958 raise_parsing_error
1960 (`Node node)
1961 SyntaxError.invalid_octal_integer;
1962 missing_syntax "octal int" expr env
1963 (* this should never get hit *)
1964 | (_, Some TK.DecimalLiteral)
1965 | (_, Some TK.OctalLiteral)
1966 | (_, Some TK.HexadecimalLiteral)
1967 (* We allow underscores while lexing the integer literals. This gets rid of them before
1968 * the literal is created. *)
1970 | (_, Some TK.BinaryLiteral) ->
1971 Int (Str.global_replace underscore "" s)
1972 | (_, Some TK.FloatingLiteral) -> Float s
1973 | (_, Some TK.SingleQuotedStringLiteral) ->
1974 String (mkStr env expr Php_escaping.unescape_single s)
1975 | (_, Some TK.DoubleQuotedStringLiteral) ->
1976 String (mkStr env expr Php_escaping.unescape_double s)
1977 | (_, Some TK.HeredocStringLiteral) ->
1978 String (mkStr env expr Php_escaping.unescape_heredoc s)
1979 | (_, Some TK.NowdocStringLiteral) ->
1980 String (mkStr env expr Php_escaping.unescape_nowdoc s)
1981 | (_, Some TK.NullLiteral) ->
1982 if (not env.codegen) && s <> String.lowercase s then
1983 Lint.lowercase_constant pos s;
1984 Null
1985 | (_, Some TK.BooleanLiteral) ->
1986 if (not env.codegen) && s <> String.lowercase s then
1987 Lint.lowercase_constant pos s;
1988 (match String.lowercase s with
1989 | "false" -> False
1990 | "true" -> True
1991 | _ -> missing_syntax ("boolean (not: " ^ s ^ ")") expr env)
1992 | _ -> missing_syntax "literal" expr env)
1993 | SyntaxList ts ->
1994 String2 (pString2 InDoubleQuotedString (prepString2 env ts) env)
1995 | _ -> missing_syntax "literal expression" expr env)
1996 | PrefixedStringExpression
1997 { prefixed_string_name = name; prefixed_string_str = str } ->
1998 (* Temporarily allow only`re`- prefixed strings *)
1999 let name_text = text name in
2000 if name_text <> "re" then
2001 raise_parsing_error env (`Node node) SyntaxError.non_re_prefix;
2002 PrefixedString (text name, pExpr str env)
2003 | IsExpression { is_left_operand; is_right_operand; _ } ->
2004 Is (pExpr is_left_operand env, pHint is_right_operand env)
2005 | AsExpression { as_left_operand; as_right_operand; _ } ->
2006 As (pExpr as_left_operand env, pHint as_right_operand env, false)
2007 | NullableAsExpression
2008 { nullable_as_left_operand; nullable_as_right_operand; _ } ->
2010 ( pExpr nullable_as_left_operand env,
2011 pHint nullable_as_right_operand env,
2012 true )
2013 | AnonymousFunction
2015 anonymous_attribute_spec = attribute_spec;
2016 anonymous_static_keyword;
2017 anonymous_async_keyword;
2018 anonymous_coroutine_keyword;
2019 anonymous_parameters;
2020 anonymous_type;
2021 anonymous_use;
2022 anonymous_body;
2024 } ->
2026 ParserOptions.disable_static_closures env.parser_options
2027 && Some TK.Static = token_kind anonymous_static_keyword
2028 then
2029 raise_parsing_error
2031 (`Node node)
2032 SyntaxError.static_closures_are_disabled;
2033 let pArg node env =
2034 match syntax node with
2035 | Token _ -> pos_name node env
2036 | _ -> missing_syntax "use variable" node env
2038 let pUse node =
2039 match syntax node with
2040 | AnonymousFunctionUseClause { anonymous_use_variables; _ } ->
2041 couldMap ~f:pArg anonymous_use_variables
2042 | _ -> (fun _env -> [])
2044 let suspension_kind =
2045 mk_suspension_kind
2046 node
2048 anonymous_async_keyword
2049 anonymous_coroutine_keyword
2051 let (f_body, yield) =
2052 mpYielding pFunctionBody anonymous_body (non_tls env)
2054 let doc_comment =
2055 match extract_docblock node with
2056 | Some _ as doc_comment -> doc_comment
2057 | None -> top_docblock ()
2059 let user_attributes = pUserAttributes env attribute_spec in
2060 let f_external = is_external anonymous_body in
2061 Efun
2063 (fun_template yield node suspension_kind env) with
2064 f_ret = mpOptional pHint anonymous_type env;
2065 f_params = couldMap ~f:pFunParam anonymous_parameters env;
2066 f_body;
2067 f_static = not (is_missing anonymous_static_keyword);
2068 f_doc_comment = doc_comment;
2069 f_user_attributes = user_attributes;
2070 f_external;
2072 (try pUse anonymous_use env with _ -> []) )
2073 | AwaitableCreationExpression
2075 awaitable_async;
2076 awaitable_coroutine;
2077 awaitable_compound_statement;
2078 awaitable_attribute_spec;
2079 } ->
2080 let suspension_kind =
2081 mk_suspension_kind node env awaitable_async awaitable_coroutine
2083 let (blk, yld) =
2084 mpYielding pFunctionBody awaitable_compound_statement env
2086 let user_attributes = pUserAttributes env awaitable_attribute_spec in
2087 let f_external = is_external awaitable_compound_statement in
2088 let body =
2090 (fun_template yld node suspension_kind env) with
2091 f_body = mk_noop (pPos awaitable_compound_statement env) blk;
2092 f_user_attributes = user_attributes;
2093 f_external;
2096 Call ((pPos node env, Lfun body), [], [], [])
2097 | XHPExpression
2099 xhp_open =
2101 syntax = XHPOpen { xhp_open_name; xhp_open_attributes; _ };
2104 xhp_body = body;
2106 } ->
2107 env.ignore_pos <- false;
2108 let name =
2109 let (pos, name) = pos_name xhp_open_name env in
2110 (pos, ":" ^ name)
2112 let combine b e = make_token Token.(concatenate b e) in
2113 let aggregate_tokens node =
2114 let rec search = function
2115 (* scroll through non-token things *)
2116 | [] -> []
2117 | t :: xs when token_kind t = Some TK.XHPComment -> search xs
2118 | ({ syntax = Token b; _ } as t) :: xs -> track t b None xs
2119 | x :: xs -> x :: search xs
2120 and track t b oe = function
2121 (* keep going through consecutive tokens *)
2122 | { syntax = Token e; _ } :: xs
2123 when Token.kind e <> TK.XHPComment ->
2124 track t b (Some e) xs
2125 | xs ->
2126 Option.value_map oe ~default:t ~f:(combine b) :: search xs
2128 search (as_list node)
2130 let pEmbedded escaper node env =
2131 match syntax node with
2132 | Token token
2133 when env.codegen && Token.kind token = TK.XHPStringLiteral ->
2134 let p = pPos node env in
2135 (* for XHP string literals (attribute values) just extract
2136 value from quotes and decode HTML entities *)
2137 let text =
2138 Html_entities.decode @@ get_quoted_content (full_text node)
2140 (p, String text)
2141 | Token token when env.codegen && Token.kind token = TK.XHPBody ->
2142 let p = pPos node env in
2143 (* for XHP body - only decode HTML entities *)
2144 let text = Html_entities.decode @@ unesc_xhp (full_text node) in
2145 (p, String text)
2146 | Token _ ->
2147 let p = pPos node env in
2148 (p, String (escaper (full_text node)))
2149 | _ ->
2150 (match pExpr node env with
2151 | (_, BracedExpr e) -> e
2152 | e -> e)
2154 let pAttr node env =
2155 match syntax node with
2156 | XHPSimpleAttribute
2158 xhp_simple_attribute_name;
2159 xhp_simple_attribute_expression;
2161 } ->
2162 let name = pos_name xhp_simple_attribute_name env in
2163 let expr =
2165 is_braced_expression xhp_simple_attribute_expression
2166 && env.fi_mode = FileInfo.Mdecl
2167 && not env.codegen
2168 then
2169 (Pos.none, Null)
2170 else
2171 pEmbedded unesc_xhp_attr xhp_simple_attribute_expression env
2173 Xhp_simple (name, expr)
2174 | XHPSpreadAttribute { xhp_spread_attribute_expression; _ } ->
2175 Xhp_spread
2176 (pEmbedded unesc_xhp_attr xhp_spread_attribute_expression env)
2177 | _ -> missing_syntax "XHP attribute" node env
2179 let attrs = couldMap ~f:pAttr xhp_open_attributes env in
2180 let exprs =
2181 List.map
2182 ~f:(fun x -> pEmbedded unesc_xhp x env)
2183 (aggregate_tokens body)
2185 Xml (name, attrs, exprs)
2186 (* Pocket Universes *)
2187 | PocketAtomExpression { pocket_atom_expression; _ } ->
2188 PU_atom (pos_name pocket_atom_expression env)
2189 | PocketIdentifierExpression
2191 pocket_identifier_qualifier;
2192 pocket_identifier_field;
2193 pocket_identifier_name;
2195 } ->
2196 let qual =
2197 match pExpr pocket_identifier_qualifier env with
2198 | (p, Lvar v) when not env.codegen -> (p, Id v)
2199 | qual -> qual
2201 let field =
2202 let field = pExpr pocket_identifier_field env in
2203 match field with
2204 | (p, String id)
2205 | (_, Id (p, id)) ->
2206 (p, id)
2207 | _ -> missing_syntax "PocketIdentifierExpression field" node env
2209 let name =
2210 let name = pExpr pocket_identifier_name env in
2211 match name with
2212 | (p, String id)
2213 | (_, Id (p, id)) ->
2214 (p, id)
2215 | _ -> missing_syntax "PocketIdentifierExpression name" node env
2217 PU_identifier (qual, field, name)
2218 (* FIXME; should this include Missing? ; "| Missing -> Null" *)
2219 | _ -> missing_syntax ?fallback:(Some Null) "expression" node env
2221 env.recursion_depth := !(env.recursion_depth) - 1;
2222 assert (!(env.recursion_depth) >= 0);
2223 result
2225 let result =
2226 match syntax node with
2227 | BracedExpression { braced_expression_expression = expr; _ }
2228 | ParenthesizedExpression
2229 { parenthesized_expression_expression = expr; _ } ->
2231 * Peeling off braced or parenthesised expresions. When there is XHP
2232 * inside, we want the XHP node to have this own positions, rather than
2233 * those of enclosing parenthesised/braced expressions.
2235 let inner = pExpr ~location expr env in
2236 if Syntax.is_braced_expression node then
2237 (* We elide the braces in {$x}, as it makes compilation easier *)
2238 match inner with
2239 | (_, (Lvar _ | String _ | Int _ | Float _)) -> inner
2240 | (p, _) -> (p, BracedExpr inner)
2241 else
2242 inner
2243 | _ ->
2245 * Since we need positions in XHP, regardless of the ignore_pos flag, we
2246 * parenthesise the call to pExpr_ so that the XHP expression case can
2247 * flip the switch. The key part is that `pPos` happens before the old
2248 * setting is restored.
2250 let local_ignore_pos = env.ignore_pos in
2251 let expr_ = pExpr_ node env in
2252 let p = pPos node env in
2253 env.ignore_pos <- local_ignore_pos;
2254 (p, expr_)
2256 result
2258 (* In some cases, we need to unwrap an extra layer of Block due to lowering
2259 * from CompoundStatement. This applies to `if`, `while` and other control flow
2260 * statements which allow optional curly braces.
2262 * In other words, we want these to be lowered into the same Ast
2263 * `if ($b) { func(); }` and `if ($b) func();`
2264 * rather than the left hand side one having an extra `Block` in the Ast
2266 and pBlock ?(remove_noop = false) : block parser =
2267 fun node env ->
2268 match pStmt node env with
2269 | (_, Block [(_, Noop)]) when remove_noop -> []
2270 | (_, Block block) -> block
2271 | blk -> [blk]
2273 and pFunctionBody : block parser =
2274 fun node env ->
2275 with_new_nonconcurrent_scope env (fun () ->
2276 match syntax node with
2277 | Missing -> []
2278 | CompoundStatement
2280 compound_statements = { syntax = Missing; _ };
2281 compound_right_brace = { syntax = Token _; _ };
2283 } ->
2284 [(Pos.none, Noop)]
2285 | CompoundStatement
2286 { compound_statements = { syntax = SyntaxList [t]; _ }; _ }
2287 when Syntax.is_specific_token TK.Yield t ->
2288 env.saw_yield <- true;
2289 [(Pos.none, Noop)]
2290 | CompoundStatement _ ->
2291 let block = pBlock node env in
2293 (not env.top_level_statements)
2294 && ( (env.fi_mode = FileInfo.Mdecl && not env.codegen)
2295 || env.quick_mode )
2296 then
2297 [(Pos.none, Noop)]
2298 else
2299 block
2300 | _ ->
2301 let pos = pPos node env in
2303 lift_awaits_in_statement env pos (fun () ->
2304 let (p, r) = pExpr node env in
2305 (p, Return (Some (p, r))));
2308 and pStmt : stmt parser =
2309 fun node env ->
2310 clear_statement_scope env (fun () ->
2311 extract_and_push_docblock node;
2312 let pos = pPos node env in
2313 let result =
2314 match syntax node with
2315 | SwitchStatement
2316 { switch_expression = expr; switch_sections = sections; _ } ->
2317 lift_awaits_in_statement env pos (fun () ->
2318 let pSwitchLabel : (block -> case) parser =
2319 fun node env cont ->
2320 match syntax node with
2321 | CaseLabel { case_expression; _ } ->
2322 Case (pExpr case_expression env, cont)
2323 | DefaultLabel _ -> Default (pPos node env, cont)
2324 | _ -> missing_syntax "switch label" node env
2326 let pSwitchSection : case list parser =
2327 fun node env ->
2328 match syntax node with
2329 | SwitchSection
2331 switch_section_labels;
2332 switch_section_statements;
2333 switch_section_fallthrough;
2334 } ->
2335 let rec null_out cont = function
2336 | [x] -> [x cont]
2337 | x :: xs -> x [] :: null_out cont xs
2338 | _ ->
2339 raise_parsing_error
2341 (`Node node)
2342 "Malformed block result";
2345 let blk =
2346 couldMap ~f:pStmt switch_section_statements env
2348 let blk =
2349 if is_missing switch_section_fallthrough then
2351 else
2352 blk @ [(Pos.none, Fallthrough)]
2354 null_out
2356 (couldMap ~f:pSwitchLabel switch_section_labels env)
2357 | _ -> missing_syntax "switch section" node env
2359 ( pos,
2360 Switch
2361 ( pExpr expr env,
2362 List.concat @@ couldMap ~f:pSwitchSection sections env )
2364 | IfStatement
2366 if_condition = cond;
2367 if_statement = stmt;
2368 if_elseif_clauses = elseif_clause;
2369 if_else_clause = else_clause;
2371 } ->
2372 lift_awaits_in_statement env pos (fun () ->
2373 (* Because consistency is for the weak-willed, Parser_hack does *not*
2374 * produce `Noop`s for compound statements **in if statements**
2376 let if_condition = pExpr cond env in
2377 let if_statement = pBlock ~remove_noop:true stmt env in
2378 let if_elseif_statement =
2379 let pElseIf : (block -> block) parser =
2380 fun node env ->
2381 match syntax node with
2382 | ElseifClause
2384 elseif_condition = ei_cond;
2385 elseif_statement = ei_stmt;
2387 } ->
2388 fun next_clause ->
2389 let elseif_condition = pExpr ei_cond env in
2390 let elseif_statement =
2391 pBlock ~remove_noop:true ei_stmt env
2394 ( pos,
2395 If (elseif_condition, elseif_statement, next_clause)
2398 | _ -> missing_syntax "elseif clause" node env
2400 List.fold_right
2401 ~f:( @@ )
2402 (couldMap ~f:pElseIf elseif_clause env)
2403 ~init:
2404 (match syntax else_clause with
2405 | ElseClause { else_statement = e_stmt; _ } ->
2406 pBlock ~remove_noop:true e_stmt env
2407 | Missing -> [(Pos.none, Noop)]
2408 | _ -> missing_syntax "else clause" else_clause env)
2410 (pos, If (if_condition, if_statement, if_elseif_statement)))
2411 | ExpressionStatement { expression_statement_expression = e; _ } ->
2412 let f () =
2413 if is_missing e then
2414 (pos, Noop)
2415 else
2416 (pos, Expr (pExpr ~location:AsStatement e env))
2419 is_simple_assignment_await_expression e
2420 || is_simple_await_expression e
2421 then
2422 f ()
2423 else
2424 lift_awaits_in_statement env pos f
2425 | CompoundStatement { compound_statements; compound_right_brace; _ }
2427 let tail =
2428 match leading_token compound_right_brace with
2429 | _ -> []
2431 handle_loop_body pos compound_statements tail env
2432 | SyntaxList _ -> handle_loop_body pos node [] env
2433 | ThrowStatement { throw_expression; _ } ->
2434 lift_awaits_in_statement env pos (fun () ->
2435 (pos, Throw (pExpr throw_expression env)))
2436 | DoStatement { do_body; do_condition; _ } ->
2437 (pos, Do (pBlock do_body env, pExpr do_condition env))
2438 | WhileStatement { while_condition; while_body; _ } ->
2439 ( pos,
2440 While
2441 ( pExpr while_condition env,
2442 pBlock ~remove_noop:true while_body env ) )
2443 | UsingStatementBlockScoped
2445 using_block_await_keyword;
2446 using_block_expressions;
2447 using_block_body;
2449 } ->
2450 lift_awaits_in_statement env pos (fun () ->
2451 ( pos,
2452 Using
2454 us_is_block_scoped = true;
2455 us_has_await = not (is_missing using_block_await_keyword);
2456 us_expr =
2457 pExprL
2458 ~location:UsingStatement
2459 using_block_expressions
2460 env;
2461 us_block = pBlock using_block_body env;
2462 } ))
2463 | UsingStatementFunctionScoped
2464 { using_function_await_keyword; using_function_expression; _ } ->
2465 (* All regular function scoped using statements should
2466 * be rewritten by this point
2467 * If this gets run, it means that this using statement is the only one
2468 * in the block, hence it is not in a compound statement *)
2469 lift_awaits_in_statement env pos (fun () ->
2470 ( pos,
2471 Using
2473 us_is_block_scoped = false;
2474 us_has_await =
2475 not (is_missing using_function_await_keyword);
2476 us_expr =
2477 pExpr
2478 ~location:UsingStatement
2479 using_function_expression
2480 env;
2481 us_block = [(Pos.none, Noop)];
2482 } ))
2483 | LetStatement
2485 let_statement_name;
2486 let_statement_type;
2487 let_statement_initializer;
2489 } ->
2490 lift_awaits_in_statement env pos (fun () ->
2491 let id = pos_name let_statement_name env in
2492 let ty = mpOptional pHint let_statement_type env in
2493 let expr = pSimpleInitializer let_statement_initializer env in
2494 (pos, Let (id, ty, expr)))
2495 | ForStatement
2496 { for_initializer; for_control; for_end_of_loop; for_body; _ } ->
2497 lift_awaits_in_statement env pos (fun () ->
2498 let ini = pExprL for_initializer env in
2499 let ctr = pExprL for_control env in
2500 let eol = pExprL for_end_of_loop env in
2501 let blk = pBlock ~remove_noop:true for_body env in
2502 (pos, For (ini, ctr, eol, blk)))
2503 | ForeachStatement
2505 foreach_collection;
2506 foreach_await_keyword;
2507 foreach_key;
2508 foreach_value;
2509 foreach_body;
2511 } ->
2512 lift_awaits_in_statement env pos (fun () ->
2513 let col = pExpr foreach_collection env in
2514 let akw =
2515 match syntax foreach_await_keyword with
2516 | Token token when Token.kind token = TK.Await ->
2517 Some (pPos foreach_await_keyword env)
2518 | _ -> None
2520 let akv =
2521 let value = pExpr foreach_value env in
2522 match syntax foreach_key with
2523 | Missing -> As_v value
2524 | _ ->
2525 let key = pExpr foreach_key env in
2526 As_kv (key, value)
2528 let blk = pBlock ~remove_noop:true foreach_body env in
2529 (pos, Foreach (col, akw, akv, blk)))
2530 | TryStatement
2532 try_compound_statement;
2533 try_catch_clauses;
2534 try_finally_clause;
2536 } ->
2537 ( pos,
2539 ( pBlock try_compound_statement env,
2540 couldMap try_catch_clauses env ~f:(fun node env ->
2541 match syntax node with
2542 | CatchClause
2543 { catch_type; catch_variable; catch_body; _ } ->
2544 ( pos_name catch_type env,
2545 pos_name catch_variable env,
2546 mpStripNoop pBlock catch_body env )
2547 | _ -> missing_syntax "catch clause" node env),
2548 match syntax try_finally_clause with
2549 | FinallyClause { finally_body; _ } ->
2550 pBlock finally_body env
2551 | _ -> [] ) )
2552 | ReturnStatement { return_expression; _ } ->
2553 let f () =
2554 let expr =
2555 match syntax return_expression with
2556 | Missing -> None
2557 | _ ->
2558 Some (pExpr ~location:RightOfReturn return_expression env)
2560 (pos, Return expr)
2562 if is_simple_await_expression return_expression then
2563 f ()
2564 else
2565 lift_awaits_in_statement env pos f
2566 | Syntax.GotoLabel { goto_label_name; _ } ->
2568 is_typechecker env
2569 && not (ParserOptions.allow_goto env.parser_options)
2570 then
2571 raise_parsing_error env (`Node node) SyntaxError.goto_label;
2572 let pos_label = pPos goto_label_name env in
2573 let label_name = text goto_label_name in
2574 (pos, Ast.GotoLabel (pos_label, label_name))
2575 | GotoStatement { goto_statement_label_name; _ } ->
2577 is_typechecker env
2578 && not (ParserOptions.allow_goto env.parser_options)
2579 then
2580 raise_parsing_error env (`Node node) SyntaxError.goto;
2581 (pos, Goto (pos_name goto_statement_label_name env))
2582 | EchoStatement { echo_keyword = kw; echo_expressions = exprs; _ } ->
2583 lift_awaits_in_statement env pos (fun () ->
2584 ( pos,
2585 Expr
2586 ( pPos node env,
2587 Call
2588 ( (match syntax kw with
2589 | QualifiedName _
2590 | SimpleTypeSpecifier _
2591 | Token _ ->
2592 let name = pos_name kw env in
2593 (fst name, Id name)
2594 | _ -> missing_syntax "id" kw env),
2596 couldMap ~f:pExpr exprs env,
2597 [] ) ) ))
2598 | UnsetStatement { unset_keyword = kw; unset_variables = exprs; _ }
2600 lift_awaits_in_statement env pos
2601 @@ fun () ->
2602 let exprl = couldMap ~f:pExpr exprs env in
2603 ( if ParserOptions.disable_unset_class_const env.parser_options then
2604 let rec check_mutate_class_const = function
2605 | (_, Array_get (e, Some _)) -> check_mutate_class_const e
2606 | (_, Class_const _) ->
2607 raise_parsing_error
2609 (`Node node)
2610 SyntaxError.const_mutation
2611 | _ -> ()
2613 List.iter ~f:check_mutate_class_const exprl );
2614 ( pos,
2615 Expr
2616 ( pPos node env,
2617 Call
2618 ( (match syntax kw with
2619 | QualifiedName _
2620 | SimpleTypeSpecifier _
2621 | Token _ ->
2622 let name = pos_name kw env in
2623 (fst name, Id name)
2624 | _ -> missing_syntax "id" kw env),
2626 exprl,
2627 [] ) ) )
2628 | BreakStatement { break_level = level; _ } ->
2629 (pos, Break (pBreak_or_continue_level env level))
2630 | ContinueStatement { continue_level = level; _ } ->
2631 (pos, Continue (pBreak_or_continue_level env level))
2632 | ConcurrentStatement { concurrent_statement = concurrent_stmt; _ }
2634 let (lifted_awaits, stmt) =
2635 with_new_concurrent_scope env (fun () ->
2636 pStmt concurrent_stmt env)
2638 let stmt =
2639 match stmt with
2640 | (pos, Block stmts) ->
2641 (* Reuse tmp vars from lifted_awaits, this is safe because there will
2642 * always be more awaits with tmp vars than statements with assignments *)
2643 let tmp_vars_from_lifted_awaits =
2644 List.fold_right
2645 ~init:[]
2646 ~f:(fun lifted_await tmp_vars ->
2647 match lifted_await with
2648 | (Some (_, tmp_var), _) -> tmp_var :: tmp_vars
2649 | (None, _) -> tmp_vars)
2650 lifted_awaits
2652 (* Final assignment transformation *)
2653 let (body_stmts, assign_stmts, _) =
2654 List.fold_left
2655 ~init:([], [], tmp_vars_from_lifted_awaits)
2656 ~f:(fun (body_stmts, assign_stmts, tmp_vars) n ->
2657 match n with
2658 | (p1, Expr (p2, Binop (Eq op, e1, ((p3, _) as e2)))) ->
2659 (match tmp_vars with
2660 | [] ->
2661 raise_parsing_error
2663 (`Pos pos)
2664 SyntaxError
2665 .statement_without_await_in_concurrent_block;
2666 (n :: body_stmts, assign_stmts, tmp_vars)
2667 | first_tmp_var :: rest_tmp_vars ->
2668 let tmp_n = (p3, Lvar (p3, first_tmp_var)) in
2669 let body_stmts =
2670 match (tmp_n, e2) with
2671 | ((_, Lvar (_, name1)), (_, Lvar (_, name2)))
2672 when name1 = name2 ->
2673 (* Optimize away useless assignment *)
2674 body_stmts
2675 | _ ->
2676 let new_n =
2677 (p1, Expr (p2, Binop (Eq None, tmp_n, e2)))
2679 new_n :: body_stmts
2681 let assign_stmt =
2682 (p1, Expr (p2, Binop (Eq op, e1, tmp_n)))
2684 ( body_stmts,
2685 assign_stmt :: assign_stmts,
2686 rest_tmp_vars ))
2687 | _ -> (n :: body_stmts, assign_stmts, tmp_vars))
2688 stmts
2690 ( pos,
2691 Block
2692 (List.concat [List.rev body_stmts; List.rev assign_stmts])
2694 | _ -> failwith "Unexpected concurrent stmt structure"
2696 (pos, Awaitall (lifted_awaits, [stmt]))
2697 | MarkupSection _ -> pMarkup node env
2698 | _ when env.max_depth > 0 && env.codegen ->
2699 (* OCaml optimisers; Forgive them, for they know not what they do!
2701 * The max_depth is only there to stop the *optimised* version from an
2702 * unbounded recursion. Sad times.
2704 * As for the code gen check, we only want to have a blanket assumption that
2705 * a statement we don't recognize is an inline definition when we're in
2706 * code generation mode, since typechecking runs with env.codegen set to
2707 * false, and typechecking needs to support ASTs with missing nodes to
2708 * support IDE features, such as autocomplete.
2710 let outer_max_depth = env.max_depth in
2711 let () = env.max_depth <- outer_max_depth - 1 in
2712 let result =
2713 match pDef node env with
2714 | [def] -> (pos, Def_inline def)
2715 | _ ->
2716 failwith
2717 "This should be impossible; inline definition was list."
2719 let () = env.max_depth <- outer_max_depth in
2720 result
2721 | _ ->
2722 missing_syntax
2723 ?fallback:(Some (Pos.none, Noop))
2724 "statement"
2725 node
2728 pop_docblock ();
2729 result)
2731 and is_simple_await_expression e =
2732 match syntax e with
2733 | PrefixUnaryExpression { prefix_unary_operator = operator; _ } ->
2734 token_kind operator = Some TK.Await
2735 | _ -> false
2737 and is_simple_assignment_await_expression e =
2738 match syntax e with
2739 | BinaryExpression { binary_operator; binary_right_operand; _ } ->
2740 is_simple_await_expression binary_right_operand
2741 && token_kind binary_operator = Some TK.Equal
2742 | _ -> false
2744 and is_hashbang text =
2745 match Syntax.extract_text text with
2746 | None -> false
2747 | Some text ->
2748 let count = List.length @@ String_utils.split_on_newlines text in
2749 count = 1
2750 && Str.string_match hashbang text 0
2751 && String.equal (Str.matched_string text) text
2753 and pMarkup node env =
2754 match syntax node with
2755 | MarkupSection { markup_prefix; markup_text; markup_expression; _ } ->
2756 let pos = pPos node env in
2757 let filename = Pos.filename pos in
2758 let has_dot_hack_extension =
2759 String_utils.string_ends_with (Relative_path.suffix filename) ".hack"
2761 if has_dot_hack_extension then
2762 raise_parsing_error env (`Node node) SyntaxError.error1060
2763 else if
2764 is_missing markup_prefix
2765 && width markup_text > 0
2766 && not (is_hashbang markup_text)
2767 then
2768 raise_parsing_error env (`Node node) SyntaxError.error1001;
2769 let expr =
2770 match syntax markup_expression with
2771 | Missing -> None
2772 | ExpressionStatement { expression_statement_expression = e; _ } ->
2773 Some (pExpr e env)
2774 | _ -> failwith "expression expected"
2776 (pos, Markup ((pos, text markup_text), expr))
2777 | _ -> failwith "invalid node"
2779 and pBreak_or_continue_level env level = mpOptional pExpr level env
2781 and pTConstraintTy : hint parser =
2782 fun node ->
2783 match syntax node with
2784 | TypeConstraint { constraint_type; _ } -> pHint constraint_type
2785 | _ -> missing_syntax "type constraint" node
2787 and pTConstraint : (constraint_kind * hint) parser =
2788 fun node env ->
2789 match syntax node with
2790 | TypeConstraint { constraint_keyword; constraint_type } ->
2791 ( (match token_kind constraint_keyword with
2792 | Some TK.As -> Constraint_as
2793 | Some TK.Super -> Constraint_super
2794 | Some TK.Equal -> Constraint_eq
2795 | _ -> missing_syntax "constraint operator" constraint_keyword env),
2796 pHint constraint_type env )
2797 | _ -> missing_syntax "type constraint" node env
2799 and pTParaml ?(is_class = false) : tparam list parser =
2800 fun node env ->
2801 let pTParam : tparam parser =
2802 fun node env ->
2803 match syntax node with
2804 | TypeParameter
2806 type_attribute_spec;
2807 type_reified;
2808 type_variance;
2809 type_name;
2810 type_constraints;
2811 } ->
2812 let attributes = pUserAttributes env type_attribute_spec in
2813 let is_reified = not @@ is_missing type_reified in
2814 if is_class && is_reified then
2815 env.cls_reified_generics :=
2816 SSet.add (text type_name) !(env.cls_reified_generics);
2817 let variance =
2818 match token_kind type_variance with
2819 | Some TK.Plus -> Covariant
2820 | Some TK.Minus -> Contravariant
2821 | _ -> Invariant
2823 if is_reified && variance <> Invariant then
2824 raise_parsing_error
2826 (`Node node)
2827 SyntaxError.non_invariant_reified_generic;
2829 tp_variance = variance;
2830 tp_name = pos_name type_name env;
2831 tp_constraints = couldMap ~f:pTConstraint type_constraints env;
2832 tp_reified = is_reified;
2833 tp_user_attributes = attributes;
2835 | _ -> missing_syntax "type parameter" node env
2837 match syntax node with
2838 | Missing -> []
2839 | TypeParameters { type_parameters_parameters; _ } ->
2840 couldMap ~f:pTParam type_parameters_parameters env
2841 | _ -> missing_syntax "type parameter list" node env
2843 and pFunHdr : fun_hdr parser =
2844 fun node env ->
2845 match syntax node with
2846 | FunctionDeclarationHeader
2848 function_modifiers;
2849 function_name;
2850 function_where_clause;
2851 function_type_parameter_list;
2852 function_parameter_list;
2853 function_type;
2855 } ->
2856 let is_autoload =
2857 String.lowercase @@ text function_name
2858 = Naming_special_names.SpecialFunctions.autoload
2860 if is_missing function_name then
2861 raise_parsing_error
2863 (`Node function_name)
2864 SyntaxError.empty_method_name;
2865 let num_params =
2866 List.length (syntax_to_list_no_separators function_parameter_list)
2868 if is_autoload && num_params > 1 then
2869 raise_parsing_error
2871 (`Node node)
2872 SyntaxError.autoload_takes_one_argument;
2873 let modifiers = pModifiers function_modifiers env in
2874 let fh_parameters = couldMap ~f:pFunParam function_parameter_list env in
2875 let fh_return_type = mpOptional pHint function_type env in
2876 let fh_suspension_kind =
2877 mk_suspension_kind_
2878 node
2880 modifiers.has_async
2881 modifiers.has_coroutine
2883 let fh_name = pos_name function_name env in
2884 let fh_constrs =
2885 pWhereConstraint ~is_class:false node function_where_clause env
2887 let fh_type_parameters = pTParaml function_type_parameter_list env in
2888 let fh_param_modifiers =
2889 List.filter ~f:(fun p -> Option.is_some p.param_modifier) fh_parameters
2892 fh_suspension_kind;
2893 fh_name;
2894 fh_constrs;
2895 fh_type_parameters;
2896 fh_parameters;
2897 fh_return_type;
2898 fh_param_modifiers;
2900 | LambdaSignature { lambda_parameters; lambda_type; _ } ->
2902 empty_fun_hdr with
2903 fh_parameters = couldMap ~f:pFunParam lambda_parameters env;
2904 fh_return_type = mpOptional pHint lambda_type env;
2906 | Token _ -> empty_fun_hdr
2907 | _ -> missing_syntax "function header" node env
2909 and docblock_stack = Caml.Stack.create ()
2911 and extract_docblock node =
2912 let source_text = leading_text node in
2913 let parse (str : string) : string option =
2914 let length = String.length str in
2915 let mk (start : int) (end_ : int) : string =
2916 String.sub source_text start (end_ - start + 1)
2918 let rec go start state idx : string option =
2919 if idx = length (* finished? *) then
2920 None
2921 else
2922 let next = idx + 1 in
2923 match (state, str.[idx]) with
2924 | (`LineCmt, '\n') -> go next `Free next
2925 | (`EndEmbedded, '/') -> go next `Free next
2926 | (`EndDoc, '/') ->
2927 begin
2928 match go next `Free next with
2929 | Some doc -> Some doc
2930 | None -> Some (mk start idx)
2932 (* PHP has line comments delimited by a # *)
2933 | (`Free, '#') -> go next `LineCmt next
2934 (* All other comment delimiters start with a / *)
2935 | (`Free, '/') -> go idx `SawSlash next
2936 (* After a / in trivia, we must see either another / or a * *)
2937 | (`SawSlash, '/') -> go next `LineCmt next
2938 | (`SawSlash, '*') -> go start `MaybeDoc next
2939 | (`MaybeDoc, '*') -> go start `MaybeDoc2 next
2940 | (`MaybeDoc, _) -> go start `EmbeddedCmt next
2941 | (`MaybeDoc2, '/') -> go next `Free next
2942 (* Doc comments have a space after the second star *)
2943 | (`MaybeDoc2, (' ' | '\t' | '\n' | '\r')) ->
2944 go start `DocComment idx
2945 | (`MaybeDoc2, _) -> go start `EmbeddedCmt next
2946 | (`DocComment, '*') -> go start `EndDoc next
2947 | (`DocComment, _) -> go start `DocComment next
2948 | (`EndDoc, _) -> go start `DocComment next
2949 (* A * without a / does not end an embedded comment *)
2950 | (`EmbeddedCmt, '*') -> go start `EndEmbedded next
2951 | (`EndEmbedded, '*') -> go start `EndEmbedded next
2952 | (`EndEmbedded, _) -> go start `EmbeddedCmt next
2953 (* Whitespace skips everywhere else *)
2954 | (_, (' ' | '\t' | '\n' | '\r')) -> go start state next
2955 (* When scanning comments, anything else is accepted *)
2956 | (`LineCmt, _) -> go start state next
2957 | (`EmbeddedCmt, _) -> go start state next
2958 (* Anything else; bail *)
2959 | _ -> None
2961 go 0 `Free 0
2963 (* Now that we have a parser *)
2964 parse (leading_text node)
2966 and extract_and_push_docblock node =
2967 let docblock = extract_docblock node in
2968 Caml.Stack.push docblock docblock_stack
2970 and handle_loop_body pos stmts tail env =
2971 let rec conv acc stmts =
2972 match stmts with
2973 | [] -> List.rev acc
2975 syntax =
2976 UsingStatementFunctionScoped
2978 using_function_await_keyword = await_kw;
2979 using_function_expression = expression;
2984 :: rest ->
2985 let body = conv [] rest in
2986 let using =
2987 lift_awaits_in_statement env pos (fun () ->
2988 ( pos,
2989 Using
2991 us_is_block_scoped = false;
2992 us_has_await = not (is_missing await_kw);
2993 us_expr = pExprL ~location:UsingStatement expression env;
2994 us_block = body;
2995 } ))
2997 List.rev (using :: acc)
2998 | h :: rest ->
2999 let h = pStmt h env in
3000 conv (h :: acc) rest
3002 let blk = conv [] (as_list stmts) in
3003 match List.filter ~f:(fun (_, x) -> x <> Noop) blk @ tail with
3004 | [] -> (pos, Block [(Pos.none, Noop)])
3005 | blk -> (pos, Block blk)
3007 and pop_docblock () =
3009 let _ = Caml.Stack.pop docblock_stack in
3011 with Caml.Stack.Empty -> ()
3013 and top_docblock () =
3014 (try Caml.Stack.top docblock_stack with Caml.Stack.Empty -> None)
3016 and pClassElt : class_elt list parser =
3017 fun node env ->
3018 (* TODO: doc comments do not have to be at the beginning, they can go in
3019 * the middle of the declaration, to be associated with individual
3020 * properties, right now we don't handle this *)
3021 let doc_comment_opt = extract_docblock node in
3022 let pClassElt_ = function
3023 | ConstDeclaration { const_type_specifier; const_declarators; _ } ->
3025 Const
3027 cc_hint = mpOptional pHint const_type_specifier env;
3028 cc_doc_comment = doc_comment_opt;
3029 cc_names =
3030 couldMap const_declarators env ~f:(function
3032 syntax =
3033 ConstantDeclarator
3035 constant_declarator_name;
3036 constant_declarator_initializer;
3039 } ->
3040 fun env ->
3041 ( pos_name constant_declarator_name env
3042 (* TODO: Parse error when const is abstract and has inits *),
3043 if not (is_abstract node) then
3044 mpOptional
3045 pSimpleInitializer
3046 constant_declarator_initializer
3048 else
3049 None )
3050 | node -> missing_syntax "constant declarator" node env);
3053 | TypeConstDeclaration
3055 type_const_attribute_spec;
3056 type_const_modifiers;
3057 type_const_name;
3058 type_const_type_parameters;
3059 type_const_type_constraint;
3060 type_const_type_specifier;
3062 } ->
3063 if not @@ is_missing type_const_type_parameters then
3064 raise_parsing_error env (`Node node) SyntaxError.tparams_in_tconst;
3065 let tconst_user_attributes =
3066 pUserAttributes env type_const_attribute_spec
3068 let tconst_type =
3069 mpOptional pHint type_const_type_specifier env
3070 |> Option.map ~f:(soften_hint tconst_user_attributes)
3072 let tconst_kinds = pKinds type_const_modifiers env in
3074 TypeConst
3076 tconst_user_attributes;
3077 tconst_kinds;
3078 tconst_type;
3079 tconst_name = pos_name type_const_name env;
3080 tconst_constraint =
3081 mpOptional pTConstraintTy type_const_type_constraint env;
3082 tconst_span = pPos node env;
3083 tconst_doc_comment = doc_comment_opt;
3086 | PropertyDeclaration
3088 property_attribute_spec;
3089 property_modifiers;
3090 property_type;
3091 property_declarators;
3093 } ->
3094 let cv_user_attributes = pUserAttributes env property_attribute_spec in
3095 let cv_hint =
3096 mpOptional pHint property_type env
3097 |> Option.map ~f:(soften_hint cv_user_attributes)
3100 ClassVars
3102 cv_user_attributes;
3103 cv_hint;
3104 cv_kinds = pKinds property_modifiers env;
3105 cv_is_promoted_variadic = false;
3106 cv_names =
3107 couldMap property_declarators env ~f:(fun node env ->
3108 match syntax node with
3109 | PropertyDeclarator
3110 { property_name; property_initializer } ->
3111 let ((_, n) as name) = pos_name property_name env in
3112 ( pPos node env,
3113 ( if String.length n > 0 && n.[0] = '$' then
3114 drop_pstr 1 name
3115 else
3116 name ),
3117 mpOptional pSimpleInitializer property_initializer env
3119 | _ -> missing_syntax "property declarator" node env);
3120 cv_doc_comment =
3121 ( if env.quick_mode then
3122 None
3123 else
3124 doc_comment_opt );
3127 | MethodishDeclaration
3129 methodish_attribute;
3130 methodish_function_decl_header =
3131 { syntax = FunctionDeclarationHeader h; _ } as header;
3132 methodish_function_body;
3134 } ->
3135 let classvar_init : fun_param -> stmt * class_elt =
3136 fun param ->
3137 let ((p, _) as cvname) = drop_pstr 1 param.param_id in
3138 (* Drop the '$' *)
3139 let span =
3140 match param.param_expr with
3141 | Some (pos_end, _) -> Pos.btw p pos_end
3142 | None -> p
3144 ( ( p,
3145 Expr
3146 ( p,
3147 Binop
3148 ( Eq None,
3149 ( p,
3150 Obj_get
3151 ( (p, Lvar (p, "$this")),
3152 (p, Id cvname),
3153 OG_nullthrows ) ),
3154 (p, Lvar param.param_id) ) ) ),
3155 ClassVars
3157 cv_kinds = Option.to_list param.param_modifier;
3158 cv_hint = param.param_hint;
3159 cv_is_promoted_variadic = param.param_is_variadic;
3160 cv_names = [(span, cvname, None)];
3161 cv_doc_comment = None;
3162 cv_user_attributes = param.param_user_attributes;
3165 let hdr = pFunHdr header env in
3167 snd hdr.fh_name = "__construct"
3168 && not (List.is_empty hdr.fh_type_parameters)
3169 then
3170 raise_parsing_error
3172 (`Node header)
3173 SyntaxError.no_generics_on_constructors;
3174 let (member_init, member_def) =
3175 List.unzip
3176 @@ List.filter_map hdr.fh_parameters ~f:(fun p ->
3177 Option.map ~f:(fun _ -> classvar_init p) p.param_modifier)
3179 let pBody node env =
3180 let body = pFunctionBody node env in
3181 let member_init =
3182 if env.codegen then
3183 List.rev member_init
3184 else
3185 member_init
3187 member_init @ body
3189 let kind = pKinds h.function_modifiers env in
3190 env.in_static_method :=
3191 List.exists kind ~f:(function
3192 | Static -> true
3193 | _ -> false);
3194 let (body, body_has_yield) =
3195 mpYielding pBody methodish_function_body env
3197 env.in_static_method := false;
3198 let is_abstract =
3199 List.exists kind ~f:(function
3200 | Abstract -> true
3201 | _ -> false)
3203 let is_external =
3204 (not is_abstract) && is_external methodish_function_body
3206 let user_attributes = pUserAttributes env methodish_attribute in
3207 member_def
3209 Method
3211 m_kind = kind;
3212 m_tparams = hdr.fh_type_parameters;
3213 m_constrs = hdr.fh_constrs;
3214 m_name = hdr.fh_name;
3215 m_params = hdr.fh_parameters;
3216 m_body = body;
3217 m_user_attributes = user_attributes;
3218 m_ret = hdr.fh_return_type;
3219 m_span = pFunction node env;
3220 m_fun_kind = mk_fun_kind hdr.fh_suspension_kind body_has_yield;
3221 m_doc_comment = doc_comment_opt;
3222 m_external = is_external (* see f_external above for context *);
3225 | MethodishTraitResolution
3227 methodish_trait_attribute;
3228 methodish_trait_function_decl_header =
3229 { syntax = FunctionDeclarationHeader h; _ } as header;
3230 methodish_trait_name;
3232 } ->
3233 let hdr = pFunHdr header env in
3234 let kind = pKinds h.function_modifiers env in
3235 let (qualifier, name) =
3236 match syntax methodish_trait_name with
3237 | ScopeResolutionExpression
3238 { scope_resolution_qualifier; scope_resolution_name; _ } ->
3239 ( pHint scope_resolution_qualifier env,
3240 pos_name scope_resolution_name env )
3241 | _ -> missing_syntax "trait method redeclaration item" node env
3244 MethodTraitResolution
3246 mt_kind = kind;
3247 mt_tparams = hdr.fh_type_parameters;
3248 mt_constrs = hdr.fh_constrs;
3249 mt_name = hdr.fh_name;
3250 mt_params = hdr.fh_parameters;
3251 mt_user_attributes =
3252 pUserAttributes env methodish_trait_attribute;
3253 mt_ret = hdr.fh_return_type;
3254 mt_fun_kind = mk_fun_kind hdr.fh_suspension_kind false;
3255 mt_trait = qualifier;
3256 mt_method = name;
3259 | TraitUseConflictResolution
3261 trait_use_conflict_resolution_names;
3262 trait_use_conflict_resolution_clauses;
3264 } ->
3265 let pTraitUseConflictResolutionItem node env =
3266 match syntax node with
3267 | TraitUsePrecedenceItem
3269 trait_use_precedence_item_name = name;
3270 trait_use_precedence_item_removed_names = removed_names;
3272 } ->
3273 let (qualifier, name) =
3274 match syntax name with
3275 | ScopeResolutionExpression
3276 { scope_resolution_qualifier; scope_resolution_name; _ } ->
3277 ( pos_name scope_resolution_qualifier env,
3278 pos_name scope_resolution_name env )
3279 | _ -> missing_syntax "trait use precedence item" node env
3281 let removed_names =
3282 couldMap ~f:(fun n _e -> pos_name n env) removed_names env
3284 ClassUsePrecedence (qualifier, name, removed_names)
3285 | TraitUseAliasItem
3287 trait_use_alias_item_aliasing_name = aliasing_name;
3288 trait_use_alias_item_modifiers = modifiers;
3289 trait_use_alias_item_aliased_name = aliased_name;
3291 } ->
3292 let (qualifier, name) =
3293 match syntax aliasing_name with
3294 | ScopeResolutionExpression
3295 { scope_resolution_qualifier; scope_resolution_name; _ } ->
3296 ( Some (pos_name scope_resolution_qualifier env),
3297 pos_name scope_resolution_name env )
3298 | _ -> (None, pos_name aliasing_name env)
3300 let modifiers = pKinds modifiers env in
3301 let is_visibility = function
3302 | Public
3303 | Private
3304 | Protected ->
3305 true
3306 | _ -> false
3308 let modifiers =
3310 List.is_empty modifiers
3311 || List.exists modifiers ~f:is_visibility
3312 then
3313 modifiers
3314 else
3315 Public :: modifiers
3317 let aliased_name =
3318 Option.some_if
3319 (not (is_missing aliased_name))
3320 (pos_name aliased_name env)
3322 ClassUseAlias (qualifier, name, aliased_name, modifiers)
3323 | _ -> missing_syntax "trait use conflict resolution item" node env
3325 couldMap
3326 ~f:(fun n e -> ClassUse (pHint n e))
3327 trait_use_conflict_resolution_names
3329 @ couldMap
3330 ~f:pTraitUseConflictResolutionItem
3331 trait_use_conflict_resolution_clauses
3333 | TraitUse { trait_use_names; _ } ->
3334 couldMap ~f:(fun n e -> ClassUse (pHint n e)) trait_use_names env
3335 | RequireClause { require_kind; require_name; _ } ->
3337 ClassTraitRequire
3338 ( (match token_kind require_kind with
3339 | Some TK.Implements -> MustImplement
3340 | Some TK.Extends -> MustExtend
3341 | _ -> missing_syntax "trait require kind" require_kind env),
3342 pHint require_name env );
3344 | XHPClassAttributeDeclaration { xhp_attribute_attributes; _ } ->
3345 let pXHPAttr node env =
3346 match syntax node with
3347 | XHPClassAttribute
3349 xhp_attribute_decl_type = ty;
3350 xhp_attribute_decl_name = name;
3351 xhp_attribute_decl_initializer = init;
3352 xhp_attribute_decl_required = req;
3353 } ->
3354 let (p, name) = pos_name name env in
3355 begin
3356 match syntax ty with
3357 | TypeConstant _ when is_typechecker env ->
3358 raise_parsing_error
3360 (`Node ty)
3361 SyntaxError.xhp_class_attribute_type_constant
3362 | _ -> ()
3363 end;
3364 let on_req r =
3365 match r.syntax with
3366 | XHPRequired _ -> Some Required
3367 | XHPLateinit _ -> Some LateInit
3368 | _ -> None
3370 let pos =
3371 if is_missing init then
3373 else
3374 Pos.btw p (pPos init env)
3376 (* we can either have a typehint or an xhp enum *)
3377 let (hint, enum) =
3378 match syntax ty with
3379 | XHPEnumType { xhp_enum_optional; xhp_enum_values; _ } ->
3380 let p = pPos ty env in
3381 let opt = not (is_missing xhp_enum_optional) in
3382 let vals = couldMap ~f:pExpr xhp_enum_values env in
3383 (None, Some (p, opt, vals))
3384 | _ -> (Some (pHint ty env), None)
3386 XhpAttr
3387 ( hint,
3388 (pos, (p, ":" ^ name), mpOptional pSimpleInitializer init env),
3389 on_req req,
3390 enum )
3391 | XHPSimpleClassAttribute { xhp_simple_class_attribute_type = attr }
3393 XhpAttrUse (pPos attr env, Happly (pos_name attr env, []))
3394 | Token _ ->
3395 XhpAttrUse (pPos node env, Happly (pos_name node env, []))
3396 | _ -> missing_syntax "XHP attribute" node env
3398 couldMap ~f:pXHPAttr xhp_attribute_attributes env
3399 | XHPChildrenDeclaration { xhp_children_expression; _ } ->
3400 let p = pPos node env in
3401 [XhpChild (p, pXhpChild xhp_children_expression env)]
3402 | XHPCategoryDeclaration { xhp_category_categories = cats; _ } ->
3403 let p = pPos node env in
3404 let pNameSansPercent node _env = drop_pstr 1 (pos_name node env) in
3405 [XhpCategory (p, couldMap ~f:pNameSansPercent cats env)]
3406 (* Pocket Universe *)
3407 | PocketEnumDeclaration
3409 pocket_enum_modifiers = mods;
3410 pocket_enum_name = name;
3411 pocket_enum_fields = fields;
3413 } ->
3414 let kinds = pKinds mods env in
3415 let final = List.mem kinds Final ~equal:( = ) in
3416 let id = pos_name name env in
3417 let flds = List.map ~f:(fun x -> pPUField x env) (as_list fields) in
3418 [ClassEnum (final, id, flds)]
3419 | _ -> missing_syntax "class element" node env
3421 try pClassElt_ (syntax node)
3422 with API_Missing_syntax (expecting, env, node) when env.fail_open ->
3423 let () = log_missing ~caught:true ~env ~expecting node in
3426 and pXhpChild : xhp_child parser =
3427 fun node env ->
3428 match syntax node with
3429 | Token _ -> ChildName (pos_name node env)
3430 | PostfixUnaryExpression { postfix_unary_operand; postfix_unary_operator }
3432 let operand = pXhpChild postfix_unary_operand env in
3433 let operator =
3434 match token_kind postfix_unary_operator with
3435 | Some TK.Question -> ChildQuestion
3436 | Some TK.Plus -> ChildPlus
3437 | Some TK.Star -> ChildStar
3438 | _ -> missing_syntax "xhp children operator" node env
3440 ChildUnary (operand, operator)
3441 | BinaryExpression { binary_left_operand; binary_right_operand; _ } ->
3442 let left = pXhpChild binary_left_operand env in
3443 let right = pXhpChild binary_right_operand env in
3444 ChildBinary (left, right)
3445 | XHPChildrenParenthesizedList { xhp_children_list_xhp_children; _ } ->
3446 let children = as_list xhp_children_list_xhp_children in
3447 let children = List.map ~f:(fun x -> pXhpChild x env) children in
3448 ChildList children
3449 | _ -> missing_syntax "xhp children" node env
3451 and pPUField : pufield parser =
3452 fun node env ->
3453 match syntax node with
3454 | PocketAtomMappingDeclaration
3456 pocket_atom_mapping_name = expr;
3457 pocket_atom_mapping_mappings = mappings;
3459 } ->
3460 let id = pos_name expr env in
3461 let maps = List.map ~f:(fun x -> pPUMapping x env) (as_list mappings) in
3462 PUAtomDecl (id, maps)
3463 | PocketFieldTypeExprDeclaration
3465 pocket_field_type_expr_type = ty;
3466 pocket_field_type_expr_name = name;
3468 } ->
3469 let typ = pHint ty env in
3470 let id = pos_name name env in
3471 PUCaseTypeExpr (typ, id)
3472 | PocketFieldTypeDeclaration { pocket_field_type_name = name; _ } ->
3473 let id = pos_name name env in
3474 PUCaseType id
3475 | _ -> missing_syntax "pufield" node env
3477 (*****************************************************************************(
3478 * Parsing definitions (AST's `def`)
3479 )*****************************************************************************)
3480 and pNamespaceUseClause ~prefix env kind node =
3481 match syntax node with
3482 | NamespaceUseClause
3484 namespace_use_name = name;
3485 namespace_use_alias = alias;
3486 namespace_use_clause_kind = clause_kind;
3488 } ->
3489 let ((p, n) as name) =
3490 match (prefix, pos_name name env) with
3491 | (None, (p, n)) -> (p, n)
3492 | (Some prefix, (p, n)) -> (p, (snd @@ pos_name prefix env) ^ n)
3494 let x = Str.search_forward namespace_use n 0 in
3495 let key = drop_pstr x name in
3496 let kind =
3497 if is_missing clause_kind then
3498 kind
3499 else
3500 clause_kind
3502 let alias =
3503 if is_missing alias then
3505 else
3506 pos_name alias env
3508 let kind =
3509 match syntax kind with
3510 | Token token when Token.kind token = TK.Namespace -> NSNamespace
3511 | Token token when Token.kind token = TK.Type -> NSClass
3512 | Token token when Token.kind token = TK.Function -> NSFun
3513 | Token token when Token.kind token = TK.Const -> NSConst
3514 | Missing -> NSClassAndNamespace
3515 | _ -> missing_syntax "namespace use kind" kind env
3517 ( kind,
3518 ( p,
3519 if String.length n > 0 && n.[0] = '\\' then
3521 else
3522 "\\" ^ n ),
3523 alias )
3524 | _ -> missing_syntax "namespace use clause" node env
3526 and pWhereConstraint ~is_class parent :
3527 (hint * constraint_kind * hint) list parser =
3528 fun node env ->
3529 match syntax node with
3530 | Missing -> []
3531 | WhereClause { where_clause_constraints; _ } ->
3533 is_class
3534 && not
3535 (ParserOptions.enable_class_level_where_clauses env.parser_options)
3536 then
3537 raise_parsing_error
3539 (`Node parent)
3540 "Class-level where clauses are disabled";
3541 let rec f node =
3542 match syntax node with
3543 | ListItem { list_item; _ } -> f list_item
3544 | WhereConstraint
3546 where_constraint_left_type;
3547 where_constraint_operator;
3548 where_constraint_right_type;
3549 } ->
3550 let l = pHint where_constraint_left_type env in
3551 let o =
3552 match syntax where_constraint_operator with
3553 | Token token when Token.kind token = TK.Equal -> Constraint_eq
3554 | Token token when Token.kind token = TK.As -> Constraint_as
3555 | Token token when Token.kind token = TK.Super -> Constraint_super
3556 | _ ->
3557 missing_syntax
3558 "constraint operator"
3559 where_constraint_operator
3562 let r = pHint where_constraint_right_type env in
3563 (l, o, r)
3564 | _ -> missing_syntax "where constraint" node env
3566 List.map ~f (syntax_node_to_list where_clause_constraints)
3567 | _ ->
3568 if is_class then
3569 missing_syntax "classish declaration constraints" parent env
3570 else
3571 missing_syntax "function header constraints" parent env
3573 and pDef : def list parser =
3574 fun node env ->
3575 let doc_comment_opt = extract_docblock node in
3576 match syntax node with
3577 | FunctionDeclaration
3578 { function_attribute_spec; function_declaration_header; function_body }
3580 let env = non_tls env in
3581 let hdr = pFunHdr function_declaration_header env in
3582 let is_external = is_external function_body in
3583 let (block, yield) =
3584 if is_external then
3585 ([], false)
3586 else
3587 mpYielding pFunctionBody function_body env
3589 let user_attributes = pUserAttributes env function_attribute_spec in
3593 (fun_template yield node hdr.fh_suspension_kind env) with
3594 f_tparams = hdr.fh_type_parameters;
3595 f_ret = hdr.fh_return_type;
3596 f_constrs = hdr.fh_constrs;
3597 f_name = hdr.fh_name;
3598 f_params = hdr.fh_parameters;
3599 f_body = block;
3600 f_user_attributes = user_attributes;
3601 f_doc_comment = doc_comment_opt;
3602 f_external = is_external;
3605 | ClassishDeclaration
3607 classish_attribute = attr;
3608 classish_modifiers = mods;
3609 classish_keyword = kw;
3610 classish_name = name;
3611 classish_type_parameters = tparaml;
3612 classish_extends_list = exts;
3613 classish_implements_list = impls;
3614 classish_where_clause = where_clause;
3615 classish_body =
3616 { syntax = ClassishBody { classish_body_elements = elts; _ }; _ };
3618 } ->
3619 let env = non_tls env in
3620 let c_mode = mode_annotation env.fi_mode in
3621 let c_user_attributes = pUserAttributes env attr in
3622 let c_file_attributes = [] in
3623 let kinds = pKinds mods env in
3624 let c_final = List.mem kinds Final ~equal:( = ) in
3625 let c_is_xhp =
3626 match token_kind name with
3627 | Some (TK.XHPElementName | TK.XHPClassName) -> true
3628 | _ -> false
3630 let c_name = pos_name name env in
3631 env.cls_reified_generics := SSet.empty;
3632 let c_tparams = pTParaml ~is_class:true tparaml env in
3633 let c_extends = couldMap ~f:pHint exts env in
3634 (env.parent_maybe_reified :=
3635 match c_extends with
3636 | (_, Happly (_, hl)) :: _ -> not @@ List.is_empty hl
3637 | _ -> false);
3638 let c_implements = couldMap ~f:pHint impls env in
3639 let c_where_constraints =
3640 pWhereConstraint ~is_class:true node where_clause env
3642 let c_body =
3643 let rec aux acc ns =
3644 match ns with
3645 | [] -> acc
3646 | n :: ns ->
3647 let elt = pClassElt n env in
3648 aux (elt :: acc) ns
3650 List.concat @@ List.rev (aux [] (as_list elts))
3652 let c_namespace = mk_empty_ns_env env in
3653 let c_enum = None in
3654 let c_span = pPos node env in
3655 let c_kind =
3656 let is_abs = List.mem kinds Abstract ~equal:( = ) in
3657 match token_kind kw with
3658 | Some TK.Class when is_abs -> Cabstract
3659 | Some TK.Class -> Cnormal
3660 | Some TK.Interface -> Cinterface
3661 | Some TK.Trait -> Ctrait
3662 | Some TK.Enum -> Cenum
3663 | _ -> missing_syntax "class kind" kw env
3665 let c_doc_comment = doc_comment_opt in
3667 Class
3669 c_mode;
3670 c_user_attributes;
3671 c_file_attributes;
3672 c_final;
3673 c_is_xhp;
3674 c_name;
3675 c_tparams;
3676 c_extends;
3677 c_implements;
3678 c_where_constraints;
3679 c_body;
3680 c_namespace;
3681 c_enum;
3682 c_span;
3683 c_kind;
3684 c_doc_comment;
3687 | ConstDeclaration
3688 { const_type_specifier = ty; const_declarators = decls; _ } ->
3689 let declarations = List.map ~f:syntax (as_list decls) in
3690 let f = function
3691 | ConstantDeclarator
3693 constant_declarator_name = name;
3694 constant_declarator_initializer = init;
3695 } ->
3696 Constant
3698 cst_mode = mode_annotation env.fi_mode;
3699 cst_name = pos_name name env;
3700 cst_type = mpOptional pHint ty env;
3701 cst_value = pSimpleInitializer init env;
3702 cst_namespace = mk_empty_ns_env env;
3703 cst_span = pPos node env;
3705 | _ -> missing_syntax "constant declaration" decls env
3707 List.map ~f declarations
3708 | AliasDeclaration
3710 alias_attribute_spec = attr;
3711 alias_keyword = kw;
3712 alias_name = name;
3713 alias_generic_parameter = tparams;
3714 alias_constraint = constr;
3715 alias_type = hint;
3717 } ->
3718 let ast_tparams = pTParaml tparams env in
3719 List.iter ast_tparams ~f:(function { tp_reified; _ } ->
3720 if tp_reified then
3721 raise_parsing_error env (`Node node) SyntaxError.invalid_reified);
3723 Typedef
3725 t_id = pos_name name env;
3726 t_tparams = ast_tparams;
3727 t_constraint =
3728 Option.map ~f:snd @@ mpOptional pTConstraint constr env;
3729 t_user_attributes =
3730 List.concat
3731 @@ List.map ~f:(fun x -> pUserAttribute x env) (as_list attr);
3732 t_namespace = mk_empty_ns_env env;
3733 t_mode = mode_annotation env.fi_mode;
3734 t_kind =
3735 (match token_kind kw with
3736 | Some TK.Newtype -> NewType (pHint hint env)
3737 | Some TK.Type -> Alias (pHint hint env)
3738 | _ -> missing_syntax "kind" kw env);
3741 | EnumDeclaration
3743 enum_attribute_spec = attrs;
3744 enum_name = name;
3745 enum_base = base;
3746 enum_type = constr;
3747 enum_enumerators = enums;
3749 } ->
3750 let pEnumerator node =
3751 match syntax node with
3752 | Enumerator { enumerator_name = name; enumerator_value = value; _ } ->
3753 fun env ->
3754 Const
3756 cc_hint = None;
3757 cc_names = [(pos_name name env, Some (pExpr value env))];
3758 cc_doc_comment = None;
3760 | _ -> missing_syntax "enumerator" node
3763 Class
3765 c_mode = mode_annotation env.fi_mode;
3766 c_user_attributes = pUserAttributes env attrs;
3767 c_file_attributes = [];
3768 c_final = false;
3769 c_kind = Cenum;
3770 c_is_xhp = false;
3771 c_name = pos_name name env;
3772 c_tparams = [];
3773 c_extends = [];
3774 c_implements = [];
3775 c_where_constraints = [];
3776 c_body = couldMap enums env ~f:pEnumerator;
3777 c_namespace = mk_empty_ns_env env;
3778 c_span = pPos node env;
3779 c_enum =
3780 Some
3782 e_base = pHint base env;
3783 e_constraint = mpOptional pTConstraintTy constr env;
3785 c_doc_comment = doc_comment_opt;
3788 | RecordDeclaration
3790 record_attribute_spec = attrs;
3791 record_modifier = modifier;
3792 record_name = name;
3793 record_extends_list = exts;
3794 record_fields = fields;
3796 } ->
3797 let pFields node =
3798 match syntax node with
3799 | RecordField
3801 record_field_name = name;
3802 record_field_type = ftype;
3803 record_field_init = init;
3805 } ->
3806 fun env ->
3807 let hint = pHint ftype env in
3808 let name = pos_name name env in
3809 let init = mpOptional pSimpleInitializer init env in
3810 (name, hint, init)
3811 | _ -> missing_syntax "record_field" node env
3814 RecordDef
3816 rd_name = pos_name name env;
3817 rd_extends = couldMap ~f:pHint exts env;
3818 rd_final = token_kind modifier = Some TK.Final;
3819 rd_fields = couldMap fields env ~f:pFields;
3820 rd_user_attributes = pUserAttributes env attrs;
3821 rd_namespace = Namespace_env.empty_from_popt env.parser_options;
3822 rd_span = pPos node env;
3823 rd_doc_comment = doc_comment_opt;
3826 | InclusionDirective { inclusion_expression; inclusion_semicolon = _ }
3827 when (env.fi_mode <> FileInfo.Mdecl && env.fi_mode <> FileInfo.Mphp)
3828 || env.codegen ->
3829 let expr = pExpr inclusion_expression env in
3830 [Stmt (pPos node env, Expr expr)]
3831 | NamespaceDeclaration
3833 namespace_name = name;
3834 namespace_body =
3835 { syntax = NamespaceBody { namespace_declarations = decls; _ }; _ };
3837 } ->
3838 let env = non_tls env in
3840 Namespace
3841 ( pos_name name env,
3842 List.concat_map ~f:(fun x -> pDef x env) (as_list decls) );
3844 | NamespaceDeclaration { namespace_name = name; _ } ->
3845 [Namespace (pos_name name env, [])]
3846 | NamespaceGroupUseDeclaration
3848 namespace_group_use_kind = kind;
3849 namespace_group_use_prefix = prefix;
3850 namespace_group_use_clauses = clauses;
3852 } ->
3853 let f = pNamespaceUseClause env kind ~prefix:(Some prefix) in
3854 [NamespaceUse (List.map ~f (as_list clauses))]
3855 | NamespaceUseDeclaration
3856 { namespace_use_kind = kind; namespace_use_clauses = clauses; _ } ->
3857 let f = pNamespaceUseClause env kind ~prefix:None in
3858 [NamespaceUse (List.map ~f (as_list clauses))]
3859 | FileAttributeSpecification _ ->
3861 FileAttributes
3863 fa_user_attributes = pUserAttribute node env;
3864 fa_namespace = mk_empty_ns_env env;
3868 when env.fi_mode = FileInfo.Mdecl
3869 || (env.fi_mode = FileInfo.Mphp && not env.codegen) ->
3871 | _ -> [Stmt (pStmt node env)]
3873 and pPUMapping : pumapping parser =
3874 fun node env ->
3875 match syntax node with
3876 | PocketMappingIdDeclaration
3877 { pocket_mapping_id_name = name; pocket_mapping_id_initializer = init }
3879 let id = pos_name name env in
3880 let init_val = pSimpleInitializer init env in
3881 PUMappingID (id, init_val)
3882 | PocketMappingTypeDeclaration
3883 { pocket_mapping_type_name = name; pocket_mapping_type_type = ty; _ }
3885 let id = pos_name name env in
3886 let hint = pHint ty env in
3887 PUMappingType (id, hint)
3888 | _ -> missing_syntax "pumapping" node env
3890 let pProgram : program parser =
3891 fun node env ->
3892 let rec post_process program acc =
3893 let span (p : 'a -> bool) =
3894 let rec go yes = function
3895 | x :: xs when p x -> go (x :: yes) xs
3896 | xs -> (List.rev yes, xs)
3898 go []
3900 let not_namespace = function
3901 | Namespace _ -> false
3902 | _ -> true
3904 match program with
3905 | [] -> List.rev acc
3906 | Namespace (n, []) :: el ->
3907 let (body, remainder) = span not_namespace el in
3908 let body = post_process body [] in
3909 post_process remainder (Namespace (n, body) :: acc)
3910 | Namespace (n, il) :: el ->
3911 let result = post_process il [] in
3912 post_process el (Namespace (n, result) :: acc)
3913 | Stmt (_, Noop) :: el -> post_process el acc
3914 | (Stmt (_, Markup _) as e) :: el -> post_process el (e :: acc)
3915 | (Stmt (_, Expr (_, Import _)) as e) :: el
3916 when not (ParserOptions.disallow_toplevel_requires env.parser_options)
3918 post_process el (e :: acc)
3919 (* Toplevel statements not allowed in strict mode *)
3920 | (Stmt (p, _) as e) :: el
3921 when env.keep_errors
3922 && is_typechecker env
3923 && Partial.should_check_error env.fi_mode 1002 ->
3924 raise_parsing_error env (`Pos p) SyntaxError.toplevel_statements;
3925 post_process el (e :: acc)
3926 | e :: el -> post_process el (e :: acc)
3928 (* The list of top-level things in a file is somewhat special. *)
3929 let rec aux env acc = function
3930 | []
3931 (* EOF happens only as the last token in the list. *)
3933 | [{ syntax = EndOfFile _; _ }] ->
3934 List.concat (List.rev acc)
3935 (* HaltCompiler stops processing the list in PHP but can be disabled in Hack *)
3936 | ( {
3937 syntax =
3938 ExpressionStatement
3940 expression_statement_expression =
3941 { syntax = HaltCompilerExpression _; _ };
3945 } as cur_node )
3946 :: nodel ->
3947 ( if ParserOptions.disable_halt_compiler env.parser_options then
3948 raise_parsing_error
3950 (`Node cur_node)
3951 SyntaxError.halt_compiler_is_disabled
3952 (* If we saw COMPILER_HALT_OFFSET, calculate the position of HALT_COMPILER *)
3953 else if !(env.saw_compiler_halt_offset) <> None then
3954 let local_ignore_pos = env.ignore_pos in
3955 let () = env.ignore_pos <- false in
3956 let pos = pPos cur_node env in
3957 (* __COMPILER_HALT_OFFSET__ takes value equal to halt_compiler's end position *)
3958 let s = Pos.end_cnum pos in
3959 let () = env.saw_compiler_halt_offset := Some s in
3960 env.ignore_pos <- local_ignore_pos );
3961 aux env acc nodel
3962 | node :: nodel ->
3963 let acc =
3964 match pDef node env with
3965 | exception API_Missing_syntax (expecting, env, node)
3966 when env.fail_open ->
3967 let () = log_missing ~caught:true ~env ~expecting node in
3969 | def -> def :: acc
3971 aux env acc nodel
3973 let nodes = as_list node in
3974 let nodes = aux env [] nodes in
3975 post_process nodes []
3977 let pScript node env =
3978 match syntax node with
3979 | Script { script_declarations; _ } -> pProgram script_declarations env
3980 | _ -> missing_syntax "script" node env
3982 (* The full fidelity parser considers all comments "simply" trivia. Some
3983 * comments have meaning, though. This meaning can either be relevant for the
3984 * type checker (like HH_FIXME, etc.), but also for other uses, like
3985 * Codex, where comments are used for documentation generation.
3987 * Inlining the scrape for comments in the lowering code would be prohibitively
3988 * complicated, but a separate pass is fine.
3991 type fixmes = Pos.t IMap.t IMap.t
3993 type scoured_comment = Pos.t * comment
3995 type scoured_comments = scoured_comment list
3997 type accumulator = scoured_comments * fixmes * fixmes
3999 let scour_comments
4000 (path : Relative_path.t)
4001 (source_text : SourceText.t)
4002 ~(collect_fixmes : bool)
4003 ~(include_line_comments : bool)
4004 (tree : node)
4005 (env : env) : accumulator =
4006 let pos_of_offset = SourceText.relative_pos path source_text in
4007 let go
4008 (node : node)
4009 (in_block : bool)
4010 ((cmts, fm, mu) as acc : accumulator)
4011 (t : Trivia.t) : accumulator =
4012 match Trivia.kind t with
4013 | TriviaKind.WhiteSpace
4014 | TriviaKind.EndOfLine
4015 | TriviaKind.FallThrough
4016 | TriviaKind.ExtraTokenError
4017 | TriviaKind.AfterHaltCompiler ->
4019 | TriviaKind.DelimitedComment ->
4020 (* For legacy compliance, block comments should have the position of
4021 * their end
4023 let start = Trivia.start_offset t + 2 (* for the '/*' *) in
4024 let end_ = Trivia.end_offset t in
4025 let len = end_ - start - 1 in
4026 let p = pos_of_offset (end_ - 1) end_ in
4027 let t = String.sub (Trivia.text t) 2 len in
4028 ((p, CmtBlock t) :: cmts, fm, mu)
4029 | TriviaKind.SingleLineComment ->
4030 if not include_line_comments then
4031 (cmts, fm, mu)
4032 else
4033 let text = SourceText.text (Trivia.source_text t) in
4034 let start = Trivia.start_offset t in
4035 let start =
4036 start
4038 if text.[start] = '#' then
4040 else
4043 let end_ = Trivia.end_offset t in
4044 let len = end_ - start + 1 in
4045 let p = pos_of_offset start end_ in
4046 let t = String.sub text start len in
4047 ((p, CmtLine (t ^ "\n")) :: cmts, fm, mu)
4048 | TriviaKind.FixMe
4049 | TriviaKind.IgnoreError ->
4050 if not collect_fixmes then
4051 (cmts, fm, mu)
4052 else
4053 Str.(
4054 let txt = Trivia.text t in
4055 let ignore_fixme =
4056 match GlobalOptions.ignored_fixme_regex env.parser_options with
4057 | Some s -> string_match (Str.regexp s) txt 0
4058 | None -> false
4060 if ignore_fixme then
4061 (cmts, fm, mu)
4062 else
4063 let pos = pPos node env in
4064 let line = Pos.line pos in
4065 let ignores =
4066 (try IMap.find line fm with Caml.Not_found -> IMap.empty)
4068 let misuses =
4069 (try IMap.find line mu with Caml.Not_found -> IMap.empty)
4071 (try
4072 ignore (search_forward ignore_error txt 0);
4073 let p =
4074 pos_of_offset (Trivia.start_offset t) (Trivia.end_offset t)
4076 let code = int_of_string (matched_group 2 txt) in
4078 (not in_block)
4079 && ISet.mem
4080 code
4081 (ParserOptions.disallowed_decl_fixmes
4082 env.parser_options)
4083 then
4084 let misuses = IMap.add code p misuses in
4085 (cmts, fm, IMap.add line misuses mu)
4086 else
4087 let ignores = IMap.add code p ignores in
4088 (cmts, IMap.add line ignores fm, mu)
4089 with
4090 | Not_found_s _
4091 | Caml.Not_found ->
4092 Errors.fixme_format pos;
4093 (cmts, fm, mu)))
4095 let rec aux
4096 (in_block : bool)
4097 ((_cmts, _fm, _mu) as acc : accumulator)
4098 (node : node) : accumulator =
4099 let recurse (in_block : bool) =
4100 List.fold_left ~f:(aux in_block) ~init:acc (children node)
4102 match syntax node with
4103 | CompoundStatement _ -> recurse true
4104 | Token t ->
4106 Token.has_trivia_kind t TriviaKind.DelimitedComment
4107 || include_line_comments
4108 && Token.has_trivia_kind t TriviaKind.SingleLineComment
4109 || collect_fixmes
4110 && ( Token.has_trivia_kind t TriviaKind.FixMe
4111 || Token.has_trivia_kind t TriviaKind.IgnoreError )
4112 then
4113 let f = go node in_block in
4114 let trivia = Token.leading t in
4115 let acc = List.fold_left ~f ~init:acc trivia in
4116 let trivia = Token.trailing t in
4117 List.fold_left ~f ~init:acc trivia
4118 else
4119 recurse in_block
4120 | _ -> recurse in_block
4122 aux false ([], IMap.empty, IMap.empty) tree
4124 (*****************************************************************************(
4125 * Front-end matter
4126 )*****************************************************************************)
4128 let elaborate_halt_compiler ast env source_text =
4129 match !(env.saw_compiler_halt_offset) with
4130 | Some x ->
4131 let elaborate_halt_compiler_const defs =
4132 let visitor =
4133 object
4134 inherit [_] endo as super
4136 method! on_expr env expr =
4137 match expr with
4138 | (p, Id (_, "__COMPILER_HALT_OFFSET__")) ->
4139 let start_offset = Pos.start_cnum p in
4140 (* Construct a new position and id *)
4141 let id = string_of_int x in
4142 let end_offset = start_offset + String.length id in
4143 let pos_file = Pos.filename p in
4144 let pos =
4145 SourceText.relative_pos
4146 pos_file
4147 source_text
4148 start_offset
4149 end_offset
4151 (pos, Ast.Int id)
4152 | _ -> super#on_expr env expr
4155 visitor#on_program () defs
4157 elaborate_halt_compiler_const ast
4158 | None -> ast
4160 let lower env ~source_text ~script comments : result =
4161 let ast = runP pScript script env in
4162 let ast =
4163 if env.elaborate_namespaces then
4164 Namespaces.elaborate_toplevel_defs env.parser_options ast
4165 else
4168 let ast = elaborate_halt_compiler ast env source_text in
4169 let content =
4170 if env.codegen then
4172 else
4173 SourceText.text source_text
4176 fi_mode = env.fi_mode;
4177 is_hh_file = env.is_hh_file;
4178 ast;
4179 content;
4180 comments;
4181 file = env.file;
4185 (* TODO: Make these not default to positioned_syntax *)
4186 include Full_fidelity_ast_types
4187 module ParserErrors_ = Full_fidelity_parser_errors.WithSyntax (PositionedSyntax)
4188 module ParserErrors = ParserErrors_.WithSmartConstructors (CoroutineSC)
4189 module SourceText = Full_fidelity_source_text
4190 module DeclModeSC_ = DeclModeSmartConstructors.WithSyntax (PositionedSyntax)
4192 module DeclModeSC = DeclModeSC_.WithRustParser (struct
4193 type r = PositionedSyntax.t
4195 type t = bool list
4197 let rust_parse = Rust_parser_ffi.parse_positioned_with_decl_mode_sc
4198 end)
4200 module DeclModeParser_ = Full_fidelity_parser.WithSyntax (PositionedSyntax)
4201 module DeclModeParser = DeclModeParser_.WithSmartConstructors (DeclModeSC)
4202 module FromPositionedSyntax = WithPositionedSyntax (PositionedSyntax)
4203 module FromEditablePositionedSyntax =
4204 WithPositionedSyntax (Full_fidelity_editable_positioned_syntax)
4206 (* Creates a relative position out of the error and the given path and source text. *)
4207 let pos_of_error path source_text error =
4208 SourceText.relative_pos
4209 path
4210 source_text
4211 (SyntaxError.start_offset error)
4212 (SyntaxError.end_offset error)
4214 let parse_text (env : env) (source_text : SourceText.t) :
4215 FileInfo.mode option * PositionedSyntaxTree.t =
4216 let mode = Full_fidelity_parser.parse_mode source_text in
4217 let quick_mode =
4218 (not env.codegen)
4220 match mode with
4221 | None
4222 | Some FileInfo.Mdecl
4223 | Some FileInfo.Mphp ->
4224 true
4225 | _ -> env.quick_mode
4227 ( if
4228 mode = Some FileInfo.Mexperimental
4229 && env.codegen
4230 && not env.hacksperimental
4231 then
4232 let e =
4233 SyntaxError.make
4236 SyntaxError.experimental_in_codegen_without_hacksperimental
4238 let p = pos_of_error env.file source_text e in
4239 raise @@ SyntaxError.ParserFatal (e, p) );
4240 let leak_rust_tree =
4241 (* DANGER: Needs to be kept in sync with other logic in this file, ensuring
4242 that the tree created here is later passed to ParserErrors. This can
4243 currently leak memory when an exception is thrown between parsing and
4244 error checking
4247 (not @@ ParserOptions.parser_errors_only env.parser_options)
4248 && (env.codegen || env.keep_errors)
4249 then
4250 true
4251 else
4252 ParserOptions.rust_lowerer env.parser_options
4254 let tree =
4255 let env' =
4256 Full_fidelity_parser_env.make
4257 ~hhvm_compat_mode:env.codegen
4258 ~codegen:env.codegen
4259 ~php5_compat_mode:env.php5_compat_mode
4260 ~disable_nontoplevel_declarations:
4261 (GlobalOptions.po_disable_nontoplevel_declarations env.parser_options)
4262 ~leak_rust_tree
4263 ~disable_legacy_soft_typehints:
4264 (GlobalOptions.po_disable_legacy_soft_typehints env.parser_options)
4265 ~allow_new_attribute_syntax:
4266 (GlobalOptions.po_allow_new_attribute_syntax env.parser_options)
4267 ~disable_legacy_attribute_syntax:
4268 (GlobalOptions.po_disable_legacy_attribute_syntax env.parser_options)
4269 ?mode
4272 if quick_mode then
4273 let parser = DeclModeParser.make env' source_text in
4274 let (parser, root, rust_tree) = DeclModeParser.parse_script parser in
4275 let errors = DeclModeParser.errors parser in
4276 PositionedSyntaxTree.create source_text root rust_tree errors mode false
4277 else
4278 PositionedSyntaxTree.make ~env:env' source_text
4280 (mode, tree)
4282 let scour_comments_and_add_fixmes (env : env) source_text script =
4283 let (comments, fixmes, misuses) =
4284 FromPositionedSyntax.scour_comments
4285 env.file
4286 source_text
4287 script
4289 ~collect_fixmes:env.keep_errors
4290 ~include_line_comments:env.include_line_comments
4292 let () =
4293 if env.keep_errors then (
4294 Fixme_provider.provide_disallowed_fixmes env.file misuses;
4295 if env.quick_mode then
4296 Fixme_provider.provide_decl_hh_fixmes env.file fixmes
4297 else
4298 Fixme_provider.provide_hh_fixmes env.file fixmes
4301 comments
4303 let flush_parsing_errors env =
4304 let lowpri_errors = List.rev !(env.lowpri_errors) in
4305 env.lowpri_errors := [];
4306 if should_surface_errors env then
4307 List.iter ~f:Errors.parsing_error lowpri_errors
4308 else if env.codegen then
4309 match lowpri_errors with
4310 | (p, msg) :: _ ->
4311 let (s, e) = Pos.info_raw p in
4312 let e = SyntaxError.make ~error_type:SyntaxError.ParseError s e msg in
4313 raise @@ SyntaxError.ParserFatal (e, p)
4314 | _ -> ()
4316 type rust_lowerer_result = { aast: (Pos.t, unit, unit, unit) Aast.program }
4318 external rust_lower_ffi :
4319 env ->
4320 SourceText.t ->
4321 Rust_pointer.t ->
4322 (rust_lowerer_result, string) Result.t = "lower"
4324 let rust_lower env ~source_text ~tree comments =
4325 let rust_tree =
4326 match PositionedSyntaxTree.rust_tree tree with
4327 | Some t -> t
4328 | None -> raise (Failure "missing rust tree ")
4330 let aast =
4331 match rust_lower_ffi env source_text rust_tree with
4332 | Ok r -> r.aast
4333 | Error e -> raise (Failure e)
4336 fi_mode = env.fi_mode;
4337 is_hh_file = env.is_hh_file;
4338 ast = aast;
4339 content =
4340 ( if env.codegen then
4342 else
4343 SourceText.text source_text );
4344 comments;
4345 file = env.file;
4348 let lower_tree_
4349 editable_lower
4350 lower
4351 check_syntax_error
4352 (env : env)
4353 (source_text : SourceText.t)
4354 (mode : FileInfo.mode option)
4355 (tree : PositionedSyntaxTree.t) : 'a result_ =
4356 let env =
4358 env with
4359 lower_coroutines =
4360 env.lower_coroutines
4361 && PositionedSyntaxTree.sc_state tree
4362 && env.codegen;
4365 let script = PositionedSyntaxTree.root tree in
4366 let comments = scour_comments_and_add_fixmes env source_text script in
4367 let mode = Option.value mode ~default:FileInfo.Mpartial in
4368 let env = { env with fi_mode = mode; is_hh_file = mode <> FileInfo.Mphp } in
4369 let popt = env.parser_options in
4370 (* If we are generating code, then we want to inject auto import types into
4371 * HH namespace during namespace resolution.
4373 let popt = ParserOptions.with_codegen popt env.codegen in
4374 let env = { env with parser_options = popt } in
4375 let lower () =
4376 if env.lower_coroutines then
4377 let script =
4378 Full_fidelity_editable_positioned_syntax.from_positioned_syntax script
4379 |> Ppl_class_rewriter.rewrite_ppl_classes
4380 |> Coroutine_lowerer.lower_coroutines
4382 editable_lower env ~source_text ~script comments
4383 else
4384 lower env ~source_text ~tree comments
4386 let ast_opt = ref None in
4387 Utils.try_finally
4388 ~f:(fun () ->
4389 let ret = lower () in
4390 ast_opt := Some ret.ast;
4391 ret)
4392 ~finally:(fun () ->
4393 check_syntax_error env source_text tree !ast_opt;
4394 flush_parsing_errors env)
4396 let lower_tree =
4397 let check_syntax_error (env : env) source_text tree ast_opt =
4398 let relative_pos = pos_of_error env.file source_text in
4399 let find_errors error_env =
4400 ParserErrors.parse_errors error_env
4402 match ast_opt with
4403 | Some ast -> Ast_check.check_program ast
4404 | _ -> []
4406 if env.codegen then
4407 let error_env =
4408 ParserErrors.make_env
4409 tree
4410 ~hhvm_compat_mode:ParserErrors.HHVMCompat
4411 ~codegen:env.codegen
4412 ~parser_options:env.parser_options
4414 let errors = find_errors error_env in
4415 (* Prioritize runtime errors *)
4416 let runtime_errors =
4417 List.filter
4418 errors
4419 ~f:SyntaxError.((fun e -> error_type e = RuntimeError))
4421 match (errors, runtime_errors) with
4422 | ([], []) -> ()
4423 | (_, e :: _)
4424 | (e :: _, _) ->
4425 raise @@ SyntaxError.ParserFatal (e, relative_pos e)
4426 else if env.keep_errors then
4427 let report_error e =
4428 Errors.parsing_error (relative_pos e, SyntaxError.message e)
4430 let is_hhi =
4431 String_utils.string_ends_with Relative_path.(suffix env.file) "hhi"
4433 match PositionedSyntaxTree.errors tree with
4434 | [] when env.quick_mode ->
4435 Rust_pointer.free_leaked_pointer ~warn:false ()
4436 | [] when ParserOptions.parser_errors_only env.parser_options -> ()
4437 | [] ->
4438 let error_env =
4439 ParserErrors.make_env
4440 tree
4441 ~hhvm_compat_mode:ParserErrors.HHVMCompat
4442 ~codegen:env.codegen
4443 ~hhi_mode:is_hhi
4444 ~parser_options:env.parser_options
4446 let errors = find_errors error_env in
4447 List.iter ~f:report_error errors
4448 | error :: _ ->
4449 Rust_pointer.free_leaked_pointer ~warn:false ();
4450 report_error error
4452 let lower env ~source_text:st ~tree comments =
4453 FromPositionedSyntax.lower
4455 ~source_text:st
4456 ~script:(PositionedSyntaxTree.root tree)
4457 comments
4459 lower_tree_ FromEditablePositionedSyntax.lower lower check_syntax_error
4461 let lower_tree_rust =
4462 let ni _ ~source_text:_ ~script:_ _ = raise (Failure "not implemented") in
4463 let empty_checker _ _ _ _ = () in
4464 lower_tree_ ni rust_lower empty_checker
4466 let from_text (env : env) (source_text : SourceText.t) : result =
4467 let (mode, tree) = parse_text env source_text in
4468 lower_tree env source_text mode tree
4470 let from_text_rust (env : env) (source_text : SourceText.t) : rust_result =
4471 let (mode, tree) = parse_text env source_text in
4472 lower_tree_rust env source_text mode tree
4474 let from_file (env : env) : result =
4475 let source_text = SourceText.from_file env.file in
4476 from_text env source_text
4479 * Converts a legacy ast (ast.ml) into a typed ast (tast.ml / aast.ml)
4480 * so that codegen and typing both operate on the same ast structure.
4482 * There are some errors that are not valid hack but are still expected
4483 * to produce valid bytecode. hh_single_compile is expected to catch
4484 * these errors.
4486 let from_text_to_custom_aast env source_text to_ex fb en hi =
4487 let legacy_ast_result = from_text env source_text in
4488 let tast =
4489 Ast_to_aast.convert_program to_ex fb en hi legacy_ast_result.ast
4491 let is_hh_file = legacy_ast_result.is_hh_file in
4492 (tast, is_hh_file)
4494 let from_text_to_empty_tast env source_text =
4495 let tany = (Typing_reason.Rnone, Typing_defs.make_tany ()) in
4496 let get_expr_annotation (p : Ast_defs.pos) = (p, tany) in
4497 from_text_to_custom_aast
4499 source_text
4500 get_expr_annotation
4501 Tast.HasUnsafeBlocks
4502 Tast.dummy_saved_env
4503 tany
4505 (*****************************************************************************(
4506 * Backward compatibility matter (should be short-lived)
4507 )*****************************************************************************)
4509 let legacy (x : result) : Parser_return.t =
4511 Parser_return.file_mode =
4512 Option.some_if (x.fi_mode <> FileInfo.Mphp) x.fi_mode;
4513 Parser_return.is_hh_file = x.is_hh_file;
4514 Parser_return.comments = x.comments;
4515 Parser_return.ast = Ast_to_nast.convert x.ast;
4516 Parser_return.content = x.content;
4519 let from_text_with_legacy (env : env) (content : string) : Parser_return.t =
4520 let source_text = SourceText.make env.file content in
4521 legacy @@ from_text env source_text
4523 let from_file_with_legacy env = legacy (from_file env)
4525 let lower_tree_with_legacy
4526 (env : env)
4527 (source_text : SourceText.t)
4528 (mode : FileInfo.mode option)
4529 (tree : PositionedSyntaxTree.t) =
4530 legacy @@ lower_tree env source_text mode tree
4532 (******************************************************************************(
4533 * For cut-over purposes only; this should be removed as soon as Parser_hack
4534 * is removed.
4535 )******************************************************************************)
4537 let defensive_program
4538 ?(hacksperimental = false)
4539 ?(quick = false)
4540 ?(show_all_errors = false)
4541 ?(fail_open = false)
4542 ?(keep_errors = false)
4543 ?(elaborate_namespaces = true)
4544 ?(include_line_comments = false)
4545 parser_options
4547 content =
4549 let source = Full_fidelity_source_text.make fn content in
4550 (* If we fail open, we don't want errors. *)
4551 let env =
4552 make_env
4553 ~fail_open
4554 ~quick_mode:quick
4555 ~show_all_errors
4556 ~elaborate_namespaces
4557 ~keep_errors:(keep_errors || not fail_open)
4558 ~parser_options
4559 ~hacksperimental
4560 ~include_line_comments
4563 legacy @@ from_text env source
4564 with e ->
4565 Rust_pointer.free_leaked_pointer ();
4567 (* If we fail to lower, try to just make a source text and get the file mode *)
4568 (* If even THAT fails, we just have to give up and return an empty php file*)
4569 let mode =
4571 let source = Full_fidelity_source_text.make fn content in
4572 Full_fidelity_parser.parse_mode source
4573 with _ -> None
4575 let err = Exn.to_string e in
4576 let fn = Relative_path.suffix fn in
4577 (* If we've already found a parsing error, it's okay for lowering to fail *)
4578 if not (Errors.currently_has_errors ()) then
4579 Hh_logger.log "Warning, lowering failed for %s\n - error: %s\n" fn err;
4582 Parser_return.file_mode = mode;
4583 Parser_return.comments = [];
4584 Parser_return.ast = [];
4585 Parser_return.content;
4586 Parser_return.is_hh_file = mode <> None;
4589 let defensive_from_file ?quick ?show_all_errors popt fn =
4590 let content =
4591 (try Sys_utils.cat (Relative_path.to_absolute fn) with _ -> "")
4593 defensive_program ?quick ?show_all_errors popt fn content
4595 let defensive_from_file_with_default_popt ?quick ?show_all_errors fn =
4596 defensive_from_file ?quick ?show_all_errors ParserOptions.default fn
4598 let defensive_program_with_default_popt
4599 ?hacksperimental
4600 ?quick
4601 ?show_all_errors
4602 ?fail_open
4603 ?elaborate_namespaces
4605 content =
4606 defensive_program
4607 ?hacksperimental
4608 ?quick
4609 ?show_all_errors
4610 ?fail_open
4611 ?elaborate_namespaces
4612 ParserOptions.default
4614 content