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