2 * Copyright (c) 2016, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
10 module SyntaxError
= Full_fidelity_syntax_error
11 module SN
= Naming_special_names
15 (* What we are lowering to *)
17 module Partial
= Partial_provider
19 (* Don't allow expressions to nest deeper than this to avoid stack overflow *)
20 let recursion_limit = 30000
24 (* unused ppx_deriving show function in OCaml ast trips Werror *)
26 type lifted_await_kind
=
28 | LiftedFromConcurrent
33 type lifted_awaits
= {
34 mutable awaits
: (id
option * expr
) list
;
35 lift_kind
: lifted_await_kind
;
39 (* Context of the file being parsed, as (hopefully some day read-only) state. *)
43 php5_compat_mode
: bool;
44 elaborate_namespaces
: bool;
45 include_line_comments
: 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;
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;
92 ?
(php5_compat_mode
= false)
93 ?
(elaborate_namespaces
= true)
94 ?
(include_line_comments
= false)
98 ?
(show_all_errors
= false)
99 ?
(lower_coroutines
= 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
111 elaborate_namespaces
;
112 include_line_comments
;
129 top_level_statements
= true;
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
;
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
146 ((not env
.quick_mode
) || env
.show_all_errors
) && env
.keep_errors
149 fi_mode
: FileInfo.mode
;
153 file
: Relative_path.t
;
154 comments
: (Pos.t
* comment
) list
;
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
) =
164 (* What we're lowering from *)
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
179 | InDoubleQuotedString
183 | RightOfAssignmentInUsingStatement
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
196 String.sub str cnt
(len - cnt
) )
199 if not env
.top_level_statements
then
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"
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
225 let make_tmp_var_name env
=
227 SN.SpecialIdents.tmp_var_prefix ^ string_of_int env
.tmp_var_counter
229 env
.tmp_var_counter
<- env
.tmp_var_counter
+ 1;
232 let lift_await ((pos
, _
) as expr
) env location
=
233 match (env
.lifted_awaits
, location
) with
234 | (_
, UsingStatement
)
235 | (_
, RightOfAssignmentInUsingStatement
)
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
;
244 awaits
.awaits
<- (None
, expr
) :: awaits
.awaits
;
248 let process_lifted_awaits lifted_awaits
=
249 List.iter lifted_awaits
.awaits ~f
:(fun (_
, (pos
, _
)) ->
250 assert (pos
<> Pos.none
));
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;
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)
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
; _
}
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;
290 Utils.try_finally ~f ~finally
:(fun () ->
291 env
.lifted_awaits <- saved_lifted_awaits)
294 if List.is_empty
lifted_awaits.awaits
then
299 (lifted_awaits, result)
301 match lifted_awaits with
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
313 | ListItem
{ list_item
; list_separator
} ->
314 let acc = list_item
:: acc in
316 if include_separators
then
317 list_separator
:: acc
322 | _
-> aux (h
:: acc) t
325 match syntax node
with
327 | SyntaxList s
-> List.rev
(aux [] s
)
328 | ListItem
{ list_item
; list_separator
} ->
329 if include_separators
then
330 [list_item
; list_separator
]
335 let syntax_to_list_no_separators = syntax_to_list false
337 let pPos : Pos.t parser
=
339 if env
.ignore_pos
then
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
347 match node_or_pos
with
349 | `Node node
-> pPos node env
351 env
.lowpri_errors
:= (p, msg
) :: !(env
.lowpri_errors
)
352 else if env
.codegen
then
354 match node_or_pos
with
356 | `Node node
-> Option.value (position env
.file node
) ~default
:Pos.none
358 env
.lowpri_errors
:= (p, msg
) :: !(env
.lowpri_errors
)
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
; _
}
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
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
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
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
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
) <> [])
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
;
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
->
455 with API_Missing_syntax
(s
, env
, n
) ->
456 let pos = Pos.string (Pos.to_absolute
(pPos n env
)) in
467 (SyntaxKind.to_string
(kind n
))
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
)]
478 let mpStripNoop pThing node env
=
479 match pThing node env
with
483 let mpOptional : ('a
, 'a
option) metaparser
=
485 match syntax node
with
487 | _
-> Some
(p node env
)
489 let mpYielding : ('a
, 'a
* bool) metaparser
=
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
498 let in_string l
= l
= InDoubleQuotedString
|| l
= InBacktickedString
500 let pos_qualified_name node env
=
503 | ListItem li
-> text li
.list_item ^
text li
.list_separator
506 let p = pPos node env
in
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
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
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;
531 let couldMap : 'a
. f
:'a parser
-> 'a list parser
=
533 let rec synmap : 'a
. 'a parser
-> 'a list parser
=
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
]
539 and go
: 'a
. f
:'a parser
-> 'a list parser
=
541 | node
when is_missing node
-> (fun _env
-> [])
542 | node
-> synmap f node
546 let as_list : node
-> node list
=
547 let strip_list_item = function
548 | { syntax
= ListItem
{ list_item
= i
; _
}; _
} -> i
552 | { syntax
= SyntaxList
({ syntax
= ListItem _
; _
} :: _
as synl
); _
} ->
553 List.map ~f
:strip_list_item synl
554 | { syntax
= SyntaxList synl
; _
} -> synl
555 | { syntax
= Missing
; _
} -> []
558 let token_kind : node
-> TK.t
option = function
559 | { syntax
= Token t
; _
} -> Some
(Token.kind t
)
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
=
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
=
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
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
=
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
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
688 | x
:: xs
-> x
:: unwind xs
690 raise_parsing_error env
(`Node node
) "Malformed String2 SyntaxList";
693 (* Trim the starting b and double quote *)
695 if (Token.text t
).[0] = 'b'
then
700 let s = make_token
(trimLeft ~n
:left_trim t
) in
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
717 | x
:: xs
-> x
:: unwind xs
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
733 let mkStr env node
: (string -> string) -> string -> string =
734 fun unescaper
content ->
736 if String.length content > 0 && content.[0] = 'b'
then
737 String.sub
content 1 (String.length content - 1)
741 let len = String.length content in
743 Php_escaping.extract_unquoted_string ~
start:0 ~
len content
745 try unescaper
no_quotes
746 with Php_escaping.Invalid_string _
->
750 (Printf.sprintf
"Malformed string literal <<%s>>" no_quotes);
753 let unempty_str = function
759 let unesc_dbl s = unempty_str @@ Php_escaping.unescape_double
s
761 let get_quoted_content s =
763 if string_match
quoted s 0 then
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
=
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
786 "Coroutine functions may not be async";
789 let mk_suspension_kind node env is_async is_coroutine
=
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
;
813 f_name
= (p, ";anonymous");
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
;
821 f_doc_comment
= None
;
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
=
833 param_is_reference
= false;
834 param_is_variadic
= false;
835 param_id
= pos_name node env
;
837 param_modifier
= None
;
838 param_callconv
= None
;
839 param_user_attributes
= [];
842 let pShapeFieldName : shape_field_name parser
=
844 let is_valid_shape_literal t
=
846 Token.kind t
= TK.SingleQuotedStringLiteral
847 || Token.kind t
= TK.DoubleQuotedStringLiteral
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
; _
} ->
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
866 match int_of_string_opt
str with
871 SyntaxError.shape_field_int_like_string
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
884 { field_initializer_name
= name; field_initializer_value
= ty
; _
} ->
885 let name = pShapeFieldName name env
in
886 let ty = hintParser
ty env
in
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 }
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
} ->
909 mpOptional pParamKind closure_parameter_call_convention env
911 let cp_hint = hintParser closure_parameter_type env
in
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
919 id
= SN.Classes.cSelf
920 && (not
@@ SSet.is_empty !(env
.cls_reified_generics
))
921 || (id
= SN.Classes.cParent
&& !(env
.parent_maybe_reified
))
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
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
941 let reified_hint_visitor =
943 inherit [_
] iter
as super
945 method! on_hint env hint
=
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
960 fh_suspension_kind
: suspension_kind
;
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
;
971 fh_suspension_kind
= SKSync
;
972 fh_name
= (Pos.none
, "<ANONYMOUS>");
974 fh_type_parameters
= [];
976 fh_return_type
= None
;
977 fh_param_modifiers
= [];
980 let check_intrinsic_type_arg_varity env node
ty =
982 | [tk
; tv
] -> Some
(CollectionTKV
(tk
, tv
))
983 | [tv
] -> Some
(CollectionTV tv
)
989 SyntaxError.collection_intrinsic_many_typeargs
;
992 let rec pHint : hint parser
=
994 let rec pHint_ : hint_ parser
=
996 match syntax node
with
997 (* Dirty hack; CastExpression can have type represented by 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
=
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 *)
1022 (List.last
(syntax_to_list true shape_type_fields
))
1024 if is_missing last
&& si_allows_unknown_fields then
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;
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
;
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
1071 match syntax generic_argument_list
with
1072 | TypeArguments
{ type_arguments_types
; _
} ->
1073 couldMap ~
f:pHint type_arguments_types env
1075 missing_syntax "generic type arguments" generic_argument_list env
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)
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
) =
1097 | VariadicParameter
{ variadic_parameter_type
= vtype
; _
} ->
1098 if is_missing vtype
then
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
1111 "%d variadic parameters found. There should be no more than one."
1114 invariant_failure node
msg env
);
1115 match List.hd hints
with
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
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
;
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")
1137 raise_parsing_error env
(`Node node
) SyntaxError.only_soft_allowed
;
1138 let (_
, hint_
) = soften_hint
attrs hint in
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;
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
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
1181 and pFunParam
: fun_param parser
=
1183 match syntax node
with
1184 | ParameterDeclaration
1186 parameter_attribute
;
1187 parameter_visibility
;
1188 parameter_call_convention
;
1191 parameter_default_value
;
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 *)
1201 match syntax decorated_expression_expression
with
1202 | DecoratedExpression
1204 decorated_expression_decorator
= nested_decorator
;
1205 decorated_expression_expression
= nested_expression
;
1207 let decorator = text decorated_expression_decorator
in
1208 let nested_decorator = text nested_decorator in
1209 ( decorator = "&" || nested_decorator = "&",
1210 decorator = "..." || nested_decorator = "...",
1213 let decorator = text decorated_expression_decorator
in
1216 decorated_expression_expression
)
1218 | _
-> (false, false, parameter_name
)
1220 let param_user_attributes = pUserAttributes env parameter_attribute
in
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
1229 SyntaxError.no_attributes_on_variadic_parameter
;
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
;
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.
1244 (let rec go = function
1246 | x
:: _
when List.mem
[Private
; Public
; Protected
] x ~equal
:( = )
1251 go (pKinds parameter_visibility env
));
1253 | VariadicParameter _
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
1267 && List.length (as_list constructor_call_argument_list
) > 0
1269 raise_parsing_error env
(`Node node
) SyntaxError.soft_no_arguments
;
1271 couldMap constructor_call_argument_list env ~
f:(fun p ->
1274 | ScopeResolutionExpression
1275 { scope_resolution_name
= { syntax
= Token t
; _
}; _
}
1276 when Token.kind t
= TK.Name
->
1280 SyntaxError.constants_as_attribute_arguments
1281 | Token t
when Token.kind t
= TK.Name
->
1285 SyntaxError.constants_as_attribute_arguments
1290 { ua_name; ua_params }
1292 and pUserAttribute
: user_attribute list parser
=
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
1303 { constructor_call_argument_list
; constructor_call_type
; _
};
1306 process_attribute_constructor_call
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
1318 attribute_attribute_name
=
1323 constructor_call_argument_list
;
1324 constructor_call_type
;
1333 process_attribute_constructor_call
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) =
1345 List.exists
attrs ~
f:(fun { ua_name = (_
, s); _
} -> s = "__Soft")
1347 if should_soften then
1352 and pAField
: afield parser
=
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}". *)
1363 | [] -> List.rev
acc
1364 | { syntax
= Token token
; _
}
1365 :: ({ syntax
= EmbeddedBracedExpression _
; _
} as expr_with_braces
)
1367 when Token.kind token
= TK.Dollar
->
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
=
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
=
1390 let split_args_varargs arg_list
=
1391 match List.rev
(as_list arg_list
) with
1396 decorated_expression_decorator
= { syntax
= Token token
; _
};
1397 decorated_expression_expression
= e
;
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
1407 let args = couldMap ~
f:pExpr arg_list env
in
1410 let rec pExpr_ : expr_ parser
=
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
1417 match syntax node
with
1424 lambda_attribute_spec
;
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
) =
1442 ( if not
(is_compound_statement lambda_body
) then
1447 let f_external = is_external lambda_body
in
1450 (fun_template yield node
suspension_kind env
) with
1454 f_user_attributes
= pUserAttributes env lambda_attribute_spec
;
1457 | BracedExpression
{ braced_expression_expression
= expr
; _
}
1458 | EmbeddedBracedExpression
1459 { embedded_braced_expression_expression
= expr
; _
}
1460 | ParenthesizedExpression
1461 { parenthesized_expression_expression
= expr
; _
} ->
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
;
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
;
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
} ->
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;
1514 let hints = expand_type_args env
ty (fun () -> []) in
1515 let hints = check_intrinsic_type_arg_varity env node
hints in
1518 | Some
(CollectionTV
ty) -> Some
ty
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;
1530 let hints = expand_type_args env
ty (fun () -> []) in
1531 let hints = check_intrinsic_type_arg_varity env node
hints in
1534 | Some
(CollectionTKV
(tk
, tv
)) ->
1535 Darray
(Some
(tk
, tv
), couldMap ~
f:pMember members env
)
1536 | None
-> Darray
(None
, couldMap ~
f:pMember members env
)
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; _
}
1554 { isset_keyword
= recv
; isset_argument_list
= args; _
}
1557 tuple_expression_keyword
= recv
;
1558 tuple_expression_items
= args;
1561 let pos_if_has_parens =
1562 match syntax recv
with
1563 | ParenthesizedExpression _
-> Some
(pPos recv env
)
1566 let recv = pExpr
recv env
in
1568 match (snd
recv, pos_if_has_parens) with
1569 | ((Obj_get _
| Class_get _
), Some
p) -> (p, ParenthesizedExpr
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
=
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
1609 |> Php_escaping.extract_unquoted_string
1610 ~
start:(start_offset 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;
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
; _
}, _
) ->
1628 match syntax generic_argument_list
with
1629 | TypeArguments
{ type_arguments_types
; _
} ->
1630 couldMap ~
f:pHint type_arguments_types env
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
)
1644 let recv = pExpr
recv env
in
1646 match (snd
recv, pos_if_has_parens) with
1647 | ((Obj_get _
| Class_get _
), Some
p) -> (p, ParenthesizedExpr
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
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
} ->
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;
1678 if is_object_creation_expression
recv && not env
.codegen
then
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
;
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
1709 let postfix = kind node
= SyntaxKind.PostfixUnaryExpression
in
1710 let kind = token_kind operator
in
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)
1722 if ParserOptions.disallow_silence env
.parser_options then
1723 raise_parsing_error env
(`Node operator
) SyntaxError.no_silence
;
1725 Unop
(Usilence
, 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], [])
1738 if not env
.codegen
then
1742 SyntaxError.invalid_variable_name
;
1748 SyntaxError.invalid_variable_variable
;
1750 | _
-> missing_syntax "unary operator" node env
)
1752 { binary_left_operand
; binary_operator
; binary_right_operand
} ->
1755 if token_kind binary_operator
= Some
TK.Equal
then
1757 | AsStatement
-> RightOfAssignment
1758 | UsingStatement
-> RightOfAssignmentInUsingStatement
1766 (pExpr binary_left_operand env
)
1767 (pExpr binary_right_operand ~location
:rlocation env
)
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
)
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
))
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
1803 raise_parsing_error env
(`Node node
) SyntaxError.invalid_yield
;
1804 if text yield_operand
= "break" then
1806 else if is_missing yield_operand
then
1807 Yield
(AFvalue
(pos, Null
))
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
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
; _
} ->
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
),
1827 | ScopeResolutionExpression
1828 { scope_resolution_qualifier
; scope_resolution_name
; _
} ->
1830 match pExpr scope_resolution_qualifier env
with
1831 | (p, Lvar v
) when not env
.codegen
-> (p, Id v
)
1836 | (_
, Id x
) -> fail_if_invalid_reified_generic env node x
1840 match syntax scope_resolution_name
with
1841 | Token token
when Token.kind token
= TK.Variable
->
1843 ( pPos scope_resolution_name env
,
1844 Lvar
(pos_name scope_resolution_name env
) )
1846 Class_get
(qual, name)
1848 let name = pExpr scope_resolution_name env
in
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
1862 conditional_consequence
;
1863 conditional_alternative
;
1867 ( pExpr conditional_test env
,
1868 mpOptional pExpr conditional_consequence env
,
1869 pExpr conditional_alternative env
)
1870 | SubscriptExpression
{ subscript_receiver
; subscript_index
; _
} ->
1872 (pExpr subscript_receiver env
, mpOptional pExpr subscript_index env
)
1873 | EmbeddedSubscriptExpression
1874 { embedded_subscript_receiver
; embedded_subscript_index
; _
} ->
1876 ( pExpr embedded_subscript_receiver env
,
1877 mpOptional (pExpr ~location
) embedded_subscript_index env
)
1878 | ShapeExpression
{ shape_expression_fields
; _
} ->
1881 ~
f:(mpShapeExpressionField pExpr
)
1882 shape_expression_fields
1884 | ObjectCreationExpression
{ object_creation_object
= obj
; _
} ->
1887 { constructor_call_argument_list
; constructor_call_type
; _
} ->
1888 let (args, varargs
) =
1889 split_args_varargs constructor_call_argument_list
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
1897 match syntax generic_argument_list
with
1898 | TypeArguments
{ type_arguments_types
; _
} ->
1899 couldMap ~
f:pHint type_arguments_types env
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
, [])
1914 fail_if_invalid_reified_generic env node
name;
1915 fail_if_invalid_class_creation env node
name
1917 New
(e
, hl
, args, varargs
)
1918 | GenericTypeSpecifier
{ generic_class_type
; generic_argument_list
} ->
1919 if not
(is_missing generic_argument_list
) then
1922 (`Node generic_argument_list
)
1923 SyntaxError.targs_not_allowed
;
1924 let name = pos_name generic_class_type env
in
1926 | RecordCreationExpression
1928 record_creation_type
= rec_type
;
1929 record_creation_members
= members
;
1930 record_creation_array_token
= array_token
;
1934 match syntax rec_type
with
1935 | SimpleTypeSpecifier _
->
1936 let name = pos_name rec_type env
in
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
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'
)
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;
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
1991 | _
-> missing_syntax ("boolean (not: " ^
s ^
")") expr env
)
1992 | _
-> missing_syntax "literal" expr env
)
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
,
2015 anonymous_attribute_spec
= attribute_spec
;
2016 anonymous_static_keyword
;
2017 anonymous_async_keyword
;
2018 anonymous_coroutine_keyword
;
2019 anonymous_parameters
;
2026 ParserOptions.disable_static_closures env
.parser_options
2027 && Some
TK.Static
= token_kind anonymous_static_keyword
2032 SyntaxError.static_closures_are_disabled
;
2034 match syntax node
with
2035 | Token _
-> pos_name node env
2036 | _
-> missing_syntax "use variable" node env
2039 match syntax node
with
2040 | AnonymousFunctionUseClause
{ anonymous_use_variables
; _
} ->
2041 couldMap ~
f:pArg anonymous_use_variables
2042 | _
-> (fun _env
-> [])
2044 let suspension_kind =
2048 anonymous_async_keyword
2049 anonymous_coroutine_keyword
2051 let (f_body
, yield
) =
2052 mpYielding pFunctionBody anonymous_body
(non_tls env
)
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
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
;
2067 f_static
= not
(is_missing anonymous_static_keyword
);
2068 f_doc_comment
= doc_comment;
2069 f_user_attributes
= user_attributes;
2072 (try pUse anonymous_use env
with _
-> []) )
2073 | AwaitableCreationExpression
2076 awaitable_coroutine
;
2077 awaitable_compound_statement
;
2078 awaitable_attribute_spec
;
2080 let suspension_kind =
2081 mk_suspension_kind node env awaitable_async awaitable_coroutine
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
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;
2096 Call
((pPos node env
, Lfun
body), [], [], [])
2101 syntax
= XHPOpen
{ xhp_open_name
; xhp_open_attributes
; _
};
2107 env
.ignore_pos
<- false;
2109 let (pos, name) = pos_name xhp_open_name env
in
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 *)
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
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
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 *)
2138 Html_entities.decode
@@ get_quoted_content (full_text node
)
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
2147 let p = pPos node env
in
2148 (p, String
(escaper
(full_text node
)))
2150 (match pExpr node env
with
2151 | (_
, BracedExpr
e) -> e
2154 let pAttr node env
=
2155 match syntax node
with
2156 | XHPSimpleAttribute
2158 xhp_simple_attribute_name
;
2159 xhp_simple_attribute_expression
;
2162 let name = pos_name xhp_simple_attribute_name env
in
2165 is_braced_expression xhp_simple_attribute_expression
2166 && env
.fi_mode
= FileInfo.Mdecl
2171 pEmbedded unesc_xhp_attr xhp_simple_attribute_expression env
2173 Xhp_simple
(name, expr)
2174 | XHPSpreadAttribute
{ xhp_spread_attribute_expression
; _
} ->
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
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
;
2197 match pExpr pocket_identifier_qualifier env
with
2198 | (p, Lvar v
) when not env
.codegen
-> (p, Id v
)
2202 let field = pExpr pocket_identifier_field env
in
2205 | (_
, Id
(p, id
)) ->
2207 | _
-> missing_syntax "PocketIdentifierExpression field" node env
2210 let name = pExpr pocket_identifier_name env
in
2213 | (_
, Id
(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);
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 *)
2239 | (_
, (Lvar _
| String _
| Int _
| Float _
)) -> inner
2240 | (p, _
) -> (p, BracedExpr
inner)
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;
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
=
2268 match pStmt node env
with
2269 | (_
, Block
[(_
, Noop
)]) when remove_noop
-> []
2270 | (_
, Block block
) -> block
2273 and pFunctionBody
: block parser
=
2275 with_new_nonconcurrent_scope env
(fun () ->
2276 match syntax node
with
2280 compound_statements
= { syntax
= Missing
; _
};
2281 compound_right_brace
= { syntax
= Token _
; _
};
2286 { compound_statements
= { syntax
= SyntaxList
[t
]; _
}; _
}
2287 when Syntax.is_specific_token
TK.Yield t
->
2288 env
.saw_yield
<- true;
2290 | CompoundStatement _
->
2291 let block = pBlock node env
in
2293 (not env
.top_level_statements
)
2294 && ( (env
.fi_mode
= FileInfo.Mdecl
&& not env
.codegen
)
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
=
2310 clear_statement_scope env
(fun () ->
2311 extract_and_push_docblock node
;
2312 let pos = pPos node env
in
2314 match syntax node
with
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
=
2328 match syntax node
with
2331 switch_section_labels
;
2332 switch_section_statements
;
2333 switch_section_fallthrough
;
2335 let rec null_out cont
= function
2337 | x
:: xs
-> x
[] :: null_out cont xs
2342 "Malformed block result";
2346 couldMap ~
f:pStmt switch_section_statements env
2349 if is_missing switch_section_fallthrough
then
2352 blk @ [(Pos.none
, Fallthrough
)]
2356 (couldMap ~
f:pSwitchLabel switch_section_labels env
)
2357 | _
-> missing_syntax "switch section" node env
2362 List.concat
@@ couldMap ~
f:pSwitchSection sections env
)
2366 if_condition
= cond
;
2367 if_statement
= stmt
;
2368 if_elseif_clauses
= elseif_clause
;
2369 if_else_clause
= else_clause
;
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
=
2381 match syntax node
with
2384 elseif_condition
= ei_cond
;
2385 elseif_statement
= ei_stmt
;
2389 let elseif_condition = pExpr ei_cond env
in
2390 let elseif_statement =
2391 pBlock ~remove_noop
:true ei_stmt env
2395 If
(elseif_condition, elseif_statement, next_clause
)
2398 | _
-> missing_syntax "elseif clause" node env
2402 (couldMap ~
f:pElseIf elseif_clause env
)
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; _
} ->
2413 if is_missing
e then
2416 (pos, Expr
(pExpr ~location
:AsStatement
e env
))
2419 is_simple_assignment_await_expression
e
2420 || is_simple_await_expression
e
2424 lift_awaits_in_statement env
pos f
2425 | CompoundStatement
{ compound_statements
; compound_right_brace
; _
}
2428 match leading_token compound_right_brace
with
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
; _
} ->
2441 ( pExpr while_condition env
,
2442 pBlock ~remove_noop
:true while_body env
) )
2443 | UsingStatementBlockScoped
2445 using_block_await_keyword
;
2446 using_block_expressions
;
2450 lift_awaits_in_statement env
pos (fun () ->
2454 us_is_block_scoped
= true;
2455 us_has_await
= not
(is_missing using_block_await_keyword
);
2458 ~location
:UsingStatement
2459 using_block_expressions
2461 us_block
= pBlock using_block_body env
;
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 () ->
2473 us_is_block_scoped
= false;
2475 not
(is_missing using_function_await_keyword
);
2478 ~location
:UsingStatement
2479 using_function_expression
2481 us_block
= [(Pos.none
, Noop
)];
2487 let_statement_initializer
;
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)))
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)))
2506 foreach_await_keyword
;
2512 lift_awaits_in_statement env
pos (fun () ->
2513 let col = pExpr foreach_collection env
in
2515 match syntax foreach_await_keyword
with
2516 | Token token
when Token.kind token
= TK.Await
->
2517 Some
(pPos foreach_await_keyword env
)
2521 let value = pExpr foreach_value env
in
2522 match syntax foreach_key
with
2523 | Missing
-> As_v
value
2525 let key = pExpr foreach_key env
in
2528 let blk = pBlock ~remove_noop
:true foreach_body env
in
2529 (pos, Foreach
(col, akw, akv, blk)))
2532 try_compound_statement
;
2539 ( pBlock try_compound_statement env
,
2540 couldMap try_catch_clauses env ~
f:(fun node env
->
2541 match syntax node
with
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
2552 | ReturnStatement
{ return_expression
; _
} ->
2555 match syntax return_expression
with
2558 Some
(pExpr ~location
:RightOfReturn return_expression env
)
2562 if is_simple_await_expression return_expression
then
2565 lift_awaits_in_statement env
pos f
2566 | Syntax.GotoLabel
{ goto_label_name
; _
} ->
2569 && not
(ParserOptions.allow_goto env
.parser_options)
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
; _
} ->
2578 && not
(ParserOptions.allow_goto env
.parser_options)
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 () ->
2588 ( (match syntax kw
with
2590 | SimpleTypeSpecifier _
2592 let name = pos_name kw env
in
2594 | _
-> missing_syntax "id" kw env
),
2596 couldMap ~
f:pExpr
exprs env
,
2598 | UnsetStatement
{ unset_keyword
= kw
; unset_variables
= exprs; _
}
2600 lift_awaits_in_statement env
pos
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 _
) ->
2610 SyntaxError.const_mutation
2613 List.iter ~
f:check_mutate_class_const exprl );
2618 ( (match syntax kw
with
2620 | SimpleTypeSpecifier _
2622 let name = pos_name kw env
in
2624 | _
-> missing_syntax "id" kw env
),
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
)
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 =
2646 ~
f:(fun lifted_await tmp_vars
->
2647 match lifted_await
with
2648 | (Some
(_
, tmp_var
), _
) -> tmp_var
:: tmp_vars
2649 | (None
, _
) -> tmp_vars
)
2652 (* Final assignment transformation *)
2653 let (body_stmts
, assign_stmts
, _
) =
2655 ~init
:([], [], tmp_vars_from_lifted_awaits)
2656 ~
f:(fun (body_stmts
, assign_stmts
, tmp_vars
) n ->
2658 | (p1
, Expr
(p2
, Binop
(Eq
op, e1
, ((p3
, _
) as e2
)))) ->
2659 (match tmp_vars
with
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
2670 match (tmp_n, e2
) with
2671 | ((_
, Lvar
(_
, name1
)), (_
, Lvar
(_
, name2
)))
2672 when name1
= name2
->
2673 (* Optimize away useless assignment *)
2677 (p1
, Expr
(p2
, Binop
(Eq None
, tmp_n, e2
)))
2682 (p1
, Expr
(p2
, Binop
(Eq
op, e1
, tmp_n)))
2685 assign_stmt :: assign_stmts
,
2687 | _
-> (n :: body_stmts, assign_stmts
, tmp_vars
))
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
2713 match pDef node env
with
2714 | [def
] -> (pos, Def_inline def
)
2717 "This should be impossible; inline definition was list."
2719 let () = env
.max_depth
<- outer_max_depth in
2723 ?fallback
:(Some
(Pos.none
, Noop
))
2731 and is_simple_await_expression
e =
2733 | PrefixUnaryExpression
{ prefix_unary_operator
= operator
; _
} ->
2734 token_kind operator
= Some
TK.Await
2737 and is_simple_assignment_await_expression
e =
2739 | BinaryExpression
{ binary_operator
; binary_right_operand
; _
} ->
2740 is_simple_await_expression binary_right_operand
2741 && token_kind binary_operator
= Some
TK.Equal
2744 and is_hashbang
text =
2745 match Syntax.extract_text
text with
2748 let count = List.length @@ String_utils.split_on_newlines
text in
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
2764 is_missing markup_prefix
2765 && width markup_text
> 0
2766 && not
(is_hashbang markup_text
)
2768 raise_parsing_error env
(`Node node
) SyntaxError.error1001
;
2770 match syntax markup_expression
with
2772 | ExpressionStatement
{ expression_statement_expression
= e; _
} ->
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
=
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
=
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
=
2801 let pTParam : tparam parser
=
2803 match syntax node
with
2806 type_attribute_spec
;
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
);
2818 match token_kind type_variance
with
2819 | Some
TK.Plus
-> Covariant
2820 | Some
TK.Minus
-> Contravariant
2823 if is_reified && variance <> Invariant
then
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
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
=
2845 match syntax node
with
2846 | FunctionDeclarationHeader
2850 function_where_clause
;
2851 function_type_parameter_list
;
2852 function_parameter_list
;
2857 String.lowercase
@@ text function_name
2858 = Naming_special_names.SpecialFunctions.autoload
2860 if is_missing function_name
then
2863 (`Node function_name
)
2864 SyntaxError.empty_method_name
;
2866 List.length (syntax_to_list_no_separators function_parameter_list
)
2868 if is_autoload && num_params > 1 then
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 =
2881 modifiers.has_coroutine
2883 let fh_name = pos_name function_name env
in
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
2900 | LambdaSignature
{ lambda_parameters
; lambda_type
; _
} ->
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
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
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 *)
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
=
2973 | [] -> List.rev
acc
2976 UsingStatementFunctionScoped
2978 using_function_await_keyword
= await_kw
;
2979 using_function_expression
= expression
;
2985 let body = conv [] rest
in
2987 lift_awaits_in_statement env
pos (fun () ->
2991 us_is_block_scoped
= false;
2992 us_has_await
= not
(is_missing await_kw
);
2993 us_expr
= pExprL ~location
:UsingStatement expression env
;
2997 List.rev
(using :: acc)
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
=
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
; _ } ->
3027 cc_hint
= mpOptional pHint const_type_specifier env
;
3028 cc_doc_comment
= doc_comment_opt;
3030 couldMap const_declarators env ~
f:(function
3035 constant_declarator_name
;
3036 constant_declarator_initializer
;
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
3046 constant_declarator_initializer
3050 | node
-> missing_syntax "constant declarator" node env
);
3053 | TypeConstDeclaration
3055 type_const_attribute_spec
;
3056 type_const_modifiers
;
3058 type_const_type_parameters
;
3059 type_const_type_constraint
;
3060 type_const_type_specifier
;
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
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
3076 tconst_user_attributes;
3079 tconst_name
= pos_name type_const_name env
;
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
;
3091 property_declarators
;
3094 let cv_user_attributes = pUserAttributes env property_attribute_spec
in
3096 mpOptional pHint property_type env
3097 |> Option.map ~
f:(soften_hint
cv_user_attributes)
3104 cv_kinds
= pKinds property_modifiers env
;
3105 cv_is_promoted_variadic
= false;
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
3113 ( if String.length n > 0 && n.[0] = '$'
then
3117 mpOptional pSimpleInitializer property_initializer env
3119 | _ -> missing_syntax "property declarator" node env
);
3121 ( if env
.quick_mode
then
3127 | MethodishDeclaration
3129 methodish_attribute
;
3130 methodish_function_decl_header
=
3131 { syntax
= FunctionDeclarationHeader
h; _ } as header
;
3132 methodish_function_body
;
3135 let classvar_init : fun_param
-> stmt * class_elt
=
3137 let ((p, _) as cvname
) = drop_pstr 1 param
.param_id
in
3140 match param
.param_expr
with
3141 | Some
(pos_end
, _) -> Pos.btw
p pos_end
3151 ( (p, Lvar
(p, "$this")),
3154 (p, Lvar param
.param_id
) ) ) ),
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)
3173 SyntaxError.no_generics_on_constructors
;
3174 let (member_init
, member_def
) =
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
3183 List.rev
member_init
3189 let kind = pKinds h.function_modifiers env
in
3190 env
.in_static_method
:=
3191 List.exists
kind ~
f:(function
3194 let (body, body_has_yield
) =
3195 mpYielding pBody methodish_function_body env
3197 env
.in_static_method
:= false;
3199 List.exists
kind ~
f:(function
3204 (not
is_abstract) && is_external methodish_function_body
3206 let user_attributes = pUserAttributes env methodish_attribute
in
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;
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
;
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
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
;
3259 | TraitUseConflictResolution
3261 trait_use_conflict_resolution_names
;
3262 trait_use_conflict_resolution_clauses
;
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
;
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
3282 couldMap ~
f:(fun n _e
-> pos_name n env
) removed_names env
3284 ClassUsePrecedence
(qualifier
, name, removed_names)
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
;
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
3310 List.is_empty modifiers
3311 || List.exists
modifiers ~
f:is_visibility
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
3326 ~
f:(fun n e -> ClassUse
(pHint n e))
3327 trait_use_conflict_resolution_names
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
; _ } ->
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
3349 xhp_attribute_decl_type
= ty;
3350 xhp_attribute_decl_name
= name;
3351 xhp_attribute_decl_initializer
= init
;
3352 xhp_attribute_decl_required
= req
;
3354 let (p, name) = pos_name name env
in
3356 match syntax
ty with
3357 | TypeConstant
_ when is_typechecker env
->
3361 SyntaxError.xhp_class_attribute_type_constant
3366 | XHPRequired
_ -> Some Required
3367 | XHPLateinit
_ -> Some LateInit
3371 if is_missing init
then
3374 Pos.btw
p (pPos init env
)
3376 (* we can either have a typehint or an xhp 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
)
3388 (pos, (p, ":" ^
name), mpOptional pSimpleInitializer init env
),
3391 | XHPSimpleClassAttribute
{ xhp_simple_class_attribute_type
= attr
}
3393 XhpAttrUse
(pPos attr env
, Happly
(pos_name attr env
, []))
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
;
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
=
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
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
3449 | _ -> missing_syntax "xhp children" node env
3451 and pPUField
: pufield parser
=
3453 match syntax node
with
3454 | PocketAtomMappingDeclaration
3456 pocket_atom_mapping_name
= expr;
3457 pocket_atom_mapping_mappings
= mappings
;
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;
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
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
;
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
3497 if is_missing clause_kind
then
3503 if is_missing
alias then
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
3519 if String.length n > 0 && n.[0] = '
\\'
then
3524 | _ -> missing_syntax "namespace use clause" node env
3526 and pWhereConstraint ~is_class parent
:
3527 (hint * constraint_kind
* hint) list parser
=
3529 match syntax node
with
3531 | WhereClause
{ where_clause_constraints
; _ } ->
3535 (ParserOptions.enable_class_level_where_clauses env
.parser_options)
3540 "Class-level where clauses are disabled";
3542 match syntax node
with
3543 | ListItem
{ list_item
; _ } -> f list_item
3546 where_constraint_left_type
;
3547 where_constraint_operator
;
3548 where_constraint_right_type
;
3550 let l = pHint where_constraint_left_type env
in
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
3558 "constraint operator"
3559 where_constraint_operator
3562 let r = pHint where_constraint_right_type env
in
3564 | _ -> missing_syntax "where constraint" node env
3566 List.map ~
f (syntax_node_to_list where_clause_constraints
)
3569 missing_syntax "classish declaration constraints" parent env
3571 missing_syntax "function header constraints" parent env
3573 and pDef
: def list parser
=
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
) =
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;
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
;
3616 { syntax
= ClassishBody
{ classish_body_elements
= elts
; _ }; _ };
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
3626 match token_kind name with
3627 | Some
(TK.XHPElementName
| TK.XHPClassName
) -> true
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
3638 let c_implements = couldMap ~
f:pHint impls
env in
3639 let c_where_constraints =
3640 pWhereConstraint ~is_class
:true node where_clause
env
3643 let rec aux acc ns
=
3647 let elt = pClassElt
n env in
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
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
3678 c_where_constraints;
3688 { const_type_specifier
= ty; const_declarators
= decls
; _ } ->
3689 let declarations = List.map ~
f:syntax
(as_list decls
) in
3691 | ConstantDeclarator
3693 constant_declarator_name
= name;
3694 constant_declarator_initializer
= init
;
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
3710 alias_attribute_spec
= attr
;
3713 alias_generic_parameter
= tparams
;
3714 alias_constraint
= constr
;
3718 let ast_tparams = pTParaml tparams
env in
3719 List.iter
ast_tparams ~
f:(function { tp_reified
; _ } ->
3721 raise_parsing_error env (`Node node
) SyntaxError.invalid_reified
);
3725 t_id
= pos_name name env;
3726 t_tparams
= ast_tparams;
3728 Option.map ~
f:snd
@@ mpOptional pTConstraint constr
env;
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
;
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);
3743 enum_attribute_spec
= attrs;
3747 enum_enumerators
= enums
;
3750 let pEnumerator node
=
3751 match syntax node
with
3752 | Enumerator
{ enumerator_name
= name; enumerator_value
= value; _ } ->
3757 cc_names
= [(pos_name name env, Some
(pExpr
value env))];
3758 cc_doc_comment
= None
;
3760 | _ -> missing_syntax "enumerator" node
3765 c_mode = mode_annotation env.fi_mode
;
3766 c_user_attributes = pUserAttributes
env attrs;
3767 c_file_attributes = [];
3771 c_name = pos_name name env;
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;
3782 e_base
= pHint base
env;
3783 e_constraint
= mpOptional pTConstraintTy constr
env;
3785 c_doc_comment = doc_comment_opt;
3790 record_attribute_spec
= attrs;
3791 record_modifier
= modifier
;
3793 record_extends_list
= exts
;
3794 record_fields
= fields
;
3798 match syntax node
with
3801 record_field_name
= name;
3802 record_field_type
= ftype
;
3803 record_field_init
= init
;
3807 let hint = pHint ftype
env in
3808 let name = pos_name name env in
3809 let init = mpOptional pSimpleInitializer
init env in
3811 | _ -> missing_syntax "record_field" node
env
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
)
3829 let expr = pExpr inclusion_expression
env in
3830 [Stmt
(pPos node
env, Expr
expr)]
3831 | NamespaceDeclaration
3833 namespace_name
= name;
3835 { syntax
= NamespaceBody
{ namespace_declarations
= decls
; _ }; _ };
3838 let env = non_tls env in
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
;
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
_ ->
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
=
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
=
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
)
3900 let not_namespace = function
3901 | Namespace
_ -> false
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
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 *)
3940 expression_statement_expression
=
3941 { syntax
= HaltCompilerExpression
_; _ };
3947 ( if ParserOptions.disable_halt_compiler
env.parser_options then
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 );
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
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
4000 (path
: Relative_path.t
)
4001 (source_text : SourceText.t
)
4002 ~
(collect_fixmes
: bool)
4003 ~
(include_line_comments
: bool)
4005 (env : env) : accumulator
=
4006 let pos_of_offset = SourceText.relative_pos path
source_text in
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
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
4033 let text = SourceText.text (Trivia.source_text t) in
4034 let start = Trivia.start_offset
t in
4038 if text.[start] = '#'
then
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
)
4049 | TriviaKind.IgnoreError
->
4050 if not collect_fixmes
then
4054 let txt = Trivia.text t in
4056 match GlobalOptions.ignored_fixme_regex
env.parser_options with
4057 | Some
s -> string_match
(Str.regexp
s) txt 0
4060 if ignore_fixme then
4063 let pos = pPos node
env in
4064 let line = Pos.line pos in
4066 (try IMap.find
line fm
with Caml.Not_found
-> IMap.empty
)
4069 (try IMap.find
line mu
with Caml.Not_found
-> IMap.empty
)
4072 ignore
(search_forward
ignore_error txt 0);
4074 pos_of_offset (Trivia.start_offset
t) (Trivia.end_offset
t)
4076 let code = int_of_string
(matched_group
2 txt) in
4081 (ParserOptions.disallowed_decl_fixmes
4084 let misuses = IMap.add
code p misuses in
4085 (cmts
, fm
, IMap.add
line misuses mu
)
4087 let ignores = IMap.add
code p ignores in
4088 (cmts
, IMap.add
line ignores fm
, mu
)
4092 Errors.fixme_format
pos;
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
4106 Token.has_trivia_kind
t TriviaKind.DelimitedComment
4107 || include_line_comments
4108 && Token.has_trivia_kind
t TriviaKind.SingleLineComment
4110 && ( Token.has_trivia_kind
t TriviaKind.FixMe
4111 || Token.has_trivia_kind
t TriviaKind.IgnoreError
)
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
4120 | _ -> recurse in_block
4122 aux false ([], IMap.empty
, IMap.empty
) tree
4124 (*****************************************************************************(
4126 )*****************************************************************************)
4128 let elaborate_halt_compiler ast
env source_text =
4129 match !(env.saw_compiler_halt_offset
) with
4131 let elaborate_halt_compiler_const defs
=
4134 inherit [_] endo
as super
4136 method! on_expr
env expr =
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
4145 SourceText.relative_pos
4152 | _ -> super#on_expr
env expr
4155 visitor#on_program
() defs
4157 elaborate_halt_compiler_const ast
4160 let lower env ~
source_text ~script comments
: result =
4161 let ast = runP pScript script
env in
4163 if env.elaborate_namespaces
then
4164 Namespaces.elaborate_toplevel_defs
env.parser_options ast
4168 let ast = elaborate_halt_compiler ast env source_text in
4173 SourceText.text source_text
4176 fi_mode
= env.fi_mode
;
4177 is_hh_file
= env.is_hh_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
4197 let rust_parse = Rust_parser_ffi.parse_positioned_with_decl_mode_sc
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
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
4222 | Some
FileInfo.Mdecl
4223 | Some
FileInfo.Mphp
->
4225 | _ -> env.quick_mode
4228 mode = Some
FileInfo.Mexperimental
4230 && not
env.hacksperimental
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
4247 (not
@@ ParserOptions.parser_errors_only
env.parser_options)
4248 && (env.codegen
|| env.keep_errors
)
4252 ParserOptions.rust_lowerer
env.parser_options
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)
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)
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
4278 PositionedSyntaxTree.make ~
env:env'
source_text
4282 let scour_comments_and_add_fixmes (env : env) source_text script
=
4283 let (comments
, fixmes
, misuses) =
4284 FromPositionedSyntax.scour_comments
4289 ~collect_fixmes
:env.keep_errors
4290 ~include_line_comments
:env.include_line_comments
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
4298 Fixme_provider.provide_hh_fixmes
env.file fixmes
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
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)
4316 type rust_lowerer_result
= { aast
: (Pos.t, unit, unit, unit) Aast.program
}
4318 external rust_lower_ffi
:
4322 (rust_lowerer_result
, string) Result.t = "lower"
4324 let rust_lower env ~
source_text ~
tree comments
=
4326 match PositionedSyntaxTree.rust_tree tree with
4328 | None
-> raise
(Failure
"missing rust tree ")
4331 match rust_lower_ffi
env source_text rust_tree with
4333 | Error
e -> raise
(Failure
e)
4336 fi_mode
= env.fi_mode
;
4337 is_hh_file
= env.is_hh_file
;
4340 ( if env.codegen
then
4343 SourceText.text source_text );
4353 (source_text : SourceText.t)
4354 (mode : FileInfo.mode option)
4355 (tree : PositionedSyntaxTree.t) : 'a result_
=
4360 env.lower_coroutines
4361 && PositionedSyntaxTree.sc_state
tree
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
4376 if env.lower_coroutines
then
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
4384 lower env ~
source_text ~
tree comments
4386 let ast_opt = ref None
in
4389 let ret = lower () in
4390 ast_opt := Some
ret.ast;
4393 check_syntax_error
env source_text tree !ast_opt;
4394 flush_parsing_errors env)
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
4403 | Some
ast -> Ast_check.check_program
ast
4408 ParserErrors.make_env
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 =
4419 ~
f:SyntaxError.((fun e -> error_type
e = RuntimeError
))
4421 match (errors, runtime_errors) with
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)
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 -> ()
4439 ParserErrors.make_env
4441 ~hhvm_compat_mode
:ParserErrors.HHVMCompat
4442 ~codegen
:env.codegen
4444 ~
parser_options:env.parser_options
4446 let errors = find_errors error_env in
4447 List.iter ~
f:report_error errors
4449 Rust_pointer.free_leaked_pointer ~warn
:false ();
4452 let lower env ~
source_text:st ~
tree comments =
4453 FromPositionedSyntax.lower
4456 ~
script:(PositionedSyntaxTree.root
tree)
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
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
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
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
4501 Tast.HasUnsafeBlocks
4502 Tast.dummy_saved_env
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
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
4535 )******************************************************************************)
4537 let defensive_program
4538 ?
(hacksperimental
= false)
4540 ?
(show_all_errors
= false)
4541 ?
(fail_open
= false)
4542 ?
(keep_errors
= false)
4543 ?
(elaborate_namespaces
= true)
4544 ?
(include_line_comments
= false)
4549 let source = Full_fidelity_source_text.make fn
content in
4550 (* If we fail open, we don't want errors. *)
4556 ~elaborate_namespaces
4557 ~keep_errors
:(keep_errors
|| not fail_open
)
4560 ~include_line_comments
4563 legacy @@ from_text env source
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*)
4571 let source = Full_fidelity_source_text.make fn
content in
4572 Full_fidelity_parser.parse_mode
source
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 =
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
4603 ?elaborate_namespaces
4611 ?elaborate_namespaces
4612 ParserOptions.default