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