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.
9 module SyntaxError
= Full_fidelity_syntax_error
12 (* What we are lowering to *)
15 (* Context of the file being parsed, as (hopefully some day read-only) state. *)
19 ; systemlib_compat_mode
: bool
20 ; php5_compat_mode
: bool
21 ; elaborate_namespaces
: bool
22 ; include_line_comments
: bool
25 ; lower_coroutines
: bool
26 ; enable_hh_syntax
: bool
27 ; disallow_elvis_space
: bool
29 ; parser_options
: ParserOptions.t
30 ; fi_mode
: FileInfo.mode
31 ; file
: Relative_path.t
32 ; stats
: Stats_container.t
option
33 ; top_level_statements
: bool (* Whether we are (still) considering TLSs*)
34 (* Changing parts; should disappear in future. `mutable` saves allocations. *)
35 ; mutable ignore_pos
: bool
36 ; mutable max_depth
: int (* Filthy hack around OCaml bug *)
37 ; mutable saw_yield
: bool (* Information flowing back up *)
38 ; mutable unsafes
: ISet.t
(* Offsets of UNSAFE_EXPR in trivia *)
39 ; mutable saw_std_constant_redefinition
: bool
40 (* Whether we've seen COMPILER_HALT_OFFSET. The value of COMPILER_HALT_OFFSET
41 defaults to 0 if HALT_COMPILER isn't called.
42 None -> COMPILER_HALT_OFFSET isn't in the source file
43 Some 0 -> COMPILER_HALT_OFFSET is in the source file, but HALT_COMPILER isn't
44 Some x -> COMPILER_HALT_OFFSET is in the source file,
45 HALT_COMPILER is at x bytes offset in the file.
47 ; mutable saw_compiler_halt_offset
: int option
52 ?
(systemlib_compat_mode
= false )
53 ?
(php5_compat_mode
= false )
54 ?
(elaborate_namespaces
= true )
55 ?
(include_line_comments
= false )
56 ?
(keep_errors
= true )
57 ?
(ignore_pos
= false )
58 ?
(quick_mode
= false )
59 ?
(lower_coroutines
= false )
60 ?
(enable_hh_syntax
= false )
61 ?
(disallow_elvis_space
= false )
63 ?
(parser_options
= ParserOptions.default
)
64 ?
(fi_mode
= FileInfo.Mpartial
)
65 ?
(is_hh_file
= false )
67 (file
: Relative_path.t
)
69 = let parser_options = ParserOptions.with_hh_syntax_for_hhvm
parser_options
70 (codegen
&& (enable_hh_syntax
|| is_hh_file
)) in
71 let parser_options = ParserOptions.with_disallow_elvis_space
parser_options
72 disallow_elvis_space
in
74 ; codegen
= codegen
|| systemlib_compat_mode
75 ; systemlib_compat_mode
77 ; elaborate_namespaces
78 ; include_line_comments
82 && (match fi_mode
with
84 | FileInfo.Mphp
-> true
89 ; disallow_elvis_space
95 ; top_level_statements
= true
99 ; unsafes
= ISet.empty
100 ; saw_std_constant_redefinition
= false
101 ; saw_compiler_halt_offset
= None
105 { fi_mode
: FileInfo.mode
109 ; file
: Relative_path.t
110 ; comments
: (Pos.t
* comment
) list
113 module WithPositionedSyntax
(Syntax
: Positioned_syntax_sig.PositionedSyntax_S
) = struct
115 (* What we're lowering from *)
120 module Token
= Syntax.Token
121 module Trivia
= Token.Trivia
122 module TriviaKind
= Trivia.TriviaKind
124 module SyntaxKind
= Full_fidelity_syntax_kind
125 module TK
= Full_fidelity_token_kind
126 module SourceText
= Trivia.SourceText
128 module NS
= Namespaces
130 let drop_pstr : int -> pstring
-> pstring
= fun cnt
(pos
, str
) ->
131 let len = String.length str
in
132 pos
, if cnt
>= len then "" else String.sub str cnt
(len - cnt
)
134 let non_tls env
= if not env
.top_level_statements
then env
else
135 { env
with top_level_statements
= false }
137 type +'a parser
= node
-> env
-> 'a
138 type ('a
, 'b
) metaparser
= 'a parser
-> 'b parser
140 let mode_annotation = function
141 | FileInfo.Mphp
-> FileInfo.Mdecl
144 let pPos : Pos.t parser
= fun node env
->
147 else Option.value ~default
:Pos.none
(position_exclusive env
.file node
)
149 (* HHVM starts range of function declaration from the 'function' keyword *)
150 let pFunction node env
=
151 let p = pPos node env
in
152 match syntax node
with
153 | FunctionDeclaration
{ function_declaration_header
= h
; _
}
154 | MethodishDeclaration
{ methodish_function_decl_header
= h
; _
}
156 begin match syntax h
with
157 | FunctionDeclarationHeader
{ function_keyword
= f
; _
}
158 when not
(is_missing f
) ->
159 let pos_start = Pos.pos_start @@ pPos f env
in
160 let pos_end = Pos.pos_end p in
161 let pos_file = env
.file
in
162 Pos.make_from_file_pos ~
pos_file ~
pos_start ~
pos_end
167 exception Lowerer_invariant_failure
of string * string
168 let invariant_failure node msg env
=
169 let pos = Pos.string (Pos.to_absolute
(pPos node env
)) in
170 raise
(Lowerer_invariant_failure
(pos, msg
))
172 let scuba_table = Scuba.Table.of_name
"hh_missing_lowerer_cases"
174 let log_missing ?
(caught
= false) ~
(env
:env
) ~expecting node
: unit =
175 EventLogger.log_if_initialized
@@ fun () ->
176 let source = source_text node
in
177 let start = start_offset node
in
178 let end_ = end_offset node
in
179 let pos = SourceText.relative_pos env
.file
source start end_ in
180 let file = Relative_path.to_absolute env
.file in
182 let context_size = 5000 in
183 let start = max
0 (start - context_size) in
184 let length = min
(2 * context_size) (SourceText.length source - start) in
185 SourceText.sub
source start length
187 let kind = SyntaxKind.to_string
(Syntax.kind node
) in
188 let line = Pos.line pos in
189 let column = Pos.start_cnum
pos in
190 let synthetic = is_synthetic node
in
191 Scuba.new_sample
(Some
scuba_table)
192 |> Scuba.add_normal
"filename" file
193 |> Scuba.add_normal
"expecting" expecting
194 |> Scuba.add_normal
"contents" contents
195 |> Scuba.add_normal
"found_kind" kind
196 |> Scuba.add_int
"line" line
197 |> Scuba.add_int
"column" column
198 |> Scuba.add_int
"is_synthetic" (if synthetic then 1 else 0)
199 |> Scuba.add_int
"caught" (if caught
then 1 else 0)
202 exception API_Missing_syntax
of string * env
* node
203 let missing_syntax : ?fallback
:'a
-> string -> node
-> env
-> 'a
=
204 fun ?fallback expecting node env
->
206 | Some x
when env
.fail_open
->
207 let () = log_missing ~env ~expecting node
in
209 | _
-> raise
(API_Missing_syntax
(expecting
, env
, node
))
211 let runP : 'a parser
-> node
-> env
-> 'a
= fun pThing thing env
->
212 try pThing thing env
with
213 | API_Missing_syntax
(s
, env
, n
) ->
214 let pos = Pos.string (Pos.to_absolute
(pPos n env
)) in
215 let msg = Printf.sprintf
224 (SyntaxKind.to_string
(kind n
))
228 (* TODO: Cleanup this hopeless Noop mess *)
229 let mk_noop pos : stmt list
-> stmt list
= function
232 let mpStripNoop pThing node env
= match pThing node env
with
236 let mpOptional : ('a
, 'a
option) metaparser
= fun p -> fun node env
->
237 Option.try_with
(fun () -> p node env
)
238 let mpYielding : ('a
, ('a
* bool)) metaparser
= fun p node env
->
239 let outer_saw_yield = env
.saw_yield
in
240 let () = env
.saw_yield
<- false in
241 let result = p node env
in
242 let result = result, env
.saw_yield
in
243 let () = env
.saw_yield
<- outer_saw_yield in
249 | InDoubleQuotedString
254 l
= InDoubleQuotedString
|| l
= InBacktickedString
256 let pos_qualified_name node env
=
259 | ListItem li
-> (text li
.list_item
) ^
(text li
.list_separator
)
261 let p = pPos node env
in
263 match syntax node
with
265 qualified_name_parts
= { syntax
= SyntaxList l
; _
};
267 String.concat
"" @@ List.map ~f
:aux l
268 | _
-> missing_syntax "qualified name" node env
in
271 let rec pos_name node env
=
272 match syntax node
with
273 | QualifiedName _
-> pos_qualified_name node env
274 | SimpleTypeSpecifier
{ simple_type_specifier
= s
} -> pos_name s env
276 let name = text node
in
277 let local_ignore_pos = env
.ignore_pos
in
278 (* Special case for __LINE__; never ignore position for that special name *)
279 if name = "__LINE__" then env
.ignore_pos
<- false;
280 if name = "__COMPILER_HALT_OFFSET__" then env
.saw_compiler_halt_offset
<- Some
0;
281 let p = pPos node env
in
282 env
.ignore_pos
<- local_ignore_pos;
285 let is_ret_by_ref node
= not
@@ is_missing node
287 let couldMap : 'a
. f
:'a parser
-> 'a list parser
= fun ~f
-> fun node env
->
288 let rec synmap : 'a
. 'a parser
-> 'a list parser
= fun f node env
->
289 match syntax node
with
290 | SyntaxList l
-> List.concat_map l ~f
:(fun n
-> go ~f n env
)
291 | ListItem i
-> [f i
.list_item env
]
293 and go
: 'a
. f
:'a parser
-> 'a list parser
= fun ~f
-> function
294 | node
when is_missing node
-> fun _env
-> []
295 | node
-> synmap f node
299 let as_list : node
-> node list
=
300 let strip_list_item = function
301 | { syntax
= ListItem
{ list_item
= i
; _
}; _
} -> i
304 | { syntax
= SyntaxList
({syntax
= ListItem _
; _
}::_
as synl
); _
} ->
305 List.map ~f
:strip_list_item synl
306 | { syntax
= SyntaxList synl
; _
} -> synl
307 | { syntax
= Missing
; _
} -> []
310 let token_kind : node
-> TK.t
option = function
311 | { syntax
= Token t
; _
} -> Some
(Token.kind t
)
314 let pBop : (expr
-> expr
-> expr_
) parser
= fun node env lhs rhs
->
315 match token_kind node
with
316 | Some
TK.Equal
-> Binop
(Eq None
, lhs
, rhs
)
317 | Some
TK.Bar
-> Binop
(Bar
, lhs
, rhs
)
318 | Some
TK.Ampersand
-> Binop
(Amp
, lhs
, rhs
)
319 | Some
TK.Plus
-> Binop
(Plus
, lhs
, rhs
)
320 | Some
TK.Minus
-> Binop
(Minus
, lhs
, rhs
)
321 | Some
TK.Star
-> Binop
(Star
, lhs
, rhs
)
322 | Some
TK.Or
-> Binop
(BArbar
, lhs
, rhs
)
323 | Some
TK.And
-> Binop
(AMpamp
, lhs
, rhs
)
324 | Some
TK.Xor
-> Binop
(LogXor
, lhs
, rhs
)
325 | Some
TK.Carat
-> Binop
(Xor
, lhs
, rhs
)
326 | Some
TK.Slash
-> Binop
(Slash
, lhs
, rhs
)
327 | Some
TK.Dot
-> Binop
(Dot
, lhs
, rhs
)
328 | Some
TK.Percent
-> Binop
(Percent
, lhs
, rhs
)
329 | Some
TK.LessThan
-> Binop
(Lt
, lhs
, rhs
)
330 | Some
TK.GreaterThan
-> Binop
(Gt
, lhs
, rhs
)
331 | Some
TK.EqualEqual
-> Binop
(Eqeq
, lhs
, rhs
)
332 | Some
TK.LessThanEqual
-> Binop
(Lte
, lhs
, rhs
)
333 | Some
TK.GreaterThanEqual
-> Binop
(Gte
, lhs
, rhs
)
334 | Some
TK.StarStar
-> Binop
(Starstar
, lhs
, rhs
)
335 | Some
TK.ExclamationEqual
-> Binop
(Diff
, lhs
, rhs
)
336 | Some
TK.BarEqual
-> Binop
(Eq
(Some Bar
), lhs
, rhs
)
337 | Some
TK.PlusEqual
-> Binop
(Eq
(Some Plus
), lhs
, rhs
)
338 | Some
TK.MinusEqual
-> Binop
(Eq
(Some Minus
), lhs
, rhs
)
339 | Some
TK.StarEqual
-> Binop
(Eq
(Some Star
), lhs
, rhs
)
340 | Some
TK.StarStarEqual
-> Binop
(Eq
(Some Starstar
),lhs
, rhs
)
341 | Some
TK.SlashEqual
-> Binop
(Eq
(Some Slash
), lhs
, rhs
)
342 | Some
TK.DotEqual
-> Binop
(Eq
(Some Dot
), lhs
, rhs
)
343 | Some
TK.PercentEqual
-> Binop
(Eq
(Some Percent
), lhs
, rhs
)
344 | Some
TK.CaratEqual
-> Binop
(Eq
(Some Xor
), lhs
, rhs
)
345 | Some
TK.AmpersandEqual
-> Binop
(Eq
(Some Amp
), lhs
, rhs
)
346 | Some
TK.BarBar
-> Binop
(BArbar
, lhs
, rhs
)
347 | Some
TK.AmpersandAmpersand
-> Binop
(AMpamp
, lhs
, rhs
)
348 | Some
TK.LessThanLessThan
-> Binop
(Ltlt
, lhs
, rhs
)
349 | Some
TK.GreaterThanGreaterThan
-> Binop
(Gtgt
, lhs
, rhs
)
350 | Some
TK.EqualEqualEqual
-> Binop
(EQeqeq
, lhs
, rhs
)
351 | Some
TK.LessThanLessThanEqual
-> Binop
(Eq
(Some Ltlt
), lhs
, rhs
)
352 | Some
TK.GreaterThanGreaterThanEqual
-> Binop
(Eq
(Some Gtgt
), lhs
, rhs
)
353 | Some
TK.LessThanGreaterThan
-> Binop
(Diff
, lhs
, rhs
)
354 | Some
TK.ExclamationEqualEqual
-> Binop
(Diff2
, lhs
, rhs
)
355 | Some
TK.LessThanEqualGreaterThan
-> Binop
(Cmp
, lhs
, rhs
)
356 (* The ugly ducklings; In the FFP, `|>` and '??' are parsed as
357 * `BinaryOperator`s, whereas the typed AST has separate constructors for
358 * NullCoalesce, Pipe and Binop. This is why we don't just project onto a
359 * `bop`, but a `expr -> expr -> expr_`.
361 | Some
TK.BarGreaterThan
-> Pipe
(lhs
, rhs
)
362 | Some
TK.QuestionQuestion
-> NullCoalesce
(lhs
, rhs
)
363 | Some
TK.QuestionColon
-> Eif
(lhs
, None
, rhs
)
364 (* TODO: Figure out why this fails silently when used in a pBlock; probably
365 just caught somewhere *)
366 | _
-> missing_syntax "binary operator" node env
368 let pImportFlavor : import_flavor parser
= fun node env
->
369 match token_kind node
with
370 | Some
TK.Include
-> Include
371 | Some
TK.Require
-> Require
372 | Some
TK.Include_once
-> IncludeOnce
373 | Some
TK.Require_once
-> RequireOnce
374 | _
-> missing_syntax "import flavor" node env
376 let pNullFlavor : og_null_flavor parser
= fun node env
->
377 match token_kind node
with
378 | Some
TK.QuestionMinusGreaterThan
-> OG_nullsafe
379 | Some
TK.MinusGreaterThan
-> OG_nullthrows
380 | _
-> missing_syntax "null flavor" node env
388 let pModifiers node env
=
389 let f (has_async
, has_coroutine
, kinds
) node
=
390 match token_kind node
with
391 | Some
TK.Final
-> has_async
, has_coroutine
, (Final
:: kinds
)
392 | Some
TK.Static
-> has_async
, has_coroutine
, (Static
:: kinds
)
393 | Some
TK.Abstract
-> has_async
, has_coroutine
, (Abstract
:: kinds
)
394 | Some
TK.Private
-> has_async
, has_coroutine
, (Private
:: kinds
)
395 | Some
TK.Public
-> has_async
, has_coroutine
, (Public
:: kinds
)
396 | Some
TK.Protected
-> has_async
, has_coroutine
, (Protected
:: kinds
)
397 | Some
TK.Var
-> has_async
, has_coroutine
, (Public
:: kinds
)
398 | Some
TK.Async
-> true, has_coroutine
, kinds
399 | Some
TK.Coroutine
-> has_async
, true, kinds
400 | _
-> missing_syntax "kind" node env
in
401 let (has_async
, has_coroutine
, kinds
) =
402 Core_list.fold_left ~init
:(false, false, []) ~
f (as_list node
) in
403 { has_async
; has_coroutine
; kinds
= List.rev kinds
}
405 let pKinds node env
= (pModifiers node env
).kinds
407 let pParamKind : param_kind parser
= fun node env
->
408 match token_kind node
with
409 | Some
TK.Inout
-> Pinout
410 | _
-> missing_syntax "param kind" node env
412 (* TODO: Clean up string escaping *)
413 let prepString2 : node list
-> node list
=
414 let is_double_quote_or_backtick ch
= ch
= '
"' || ch = '`' in
415 let is_binary_string_header s =
416 (String.length s > 1) && (s.[0] = 'b') && (s.[1] = '"'
) in
417 let trimLeft = Token.trim_left
in
418 let trimRight = Token.trim_right
in
420 | ({ syntax
= Token t
; _
}::ss
)
421 when (Token.width t
) > 0 &&
422 ((is_double_quote_or_backtick (Token.text t
).[0])
423 || is_binary_string_header (Token.text t
)) ->
424 let rec unwind = function
425 | [{ syntax
= Token t
; _
}]
426 when (Token.width t
) > 0 &&
427 is_double_quote_or_backtick ((Token.text t
).[(Token.width t
) - 1]) ->
428 let s = make_token
(trimRight ~n
:1 t
) in
429 if width
s > 0 then [s] else []
430 | x
:: xs
-> x
:: unwind xs
431 | _
-> raise
(Invalid_argument
"Malformed String2 SyntaxList")
433 (* Trim the starting b and double quote *)
434 let left_trim = if (Token.text t
).[0] = 'b'
then 2 else 1 in
435 let s = make_token
(trimLeft ~n
:left_trim t
) in
436 if width
s > 0 then s :: unwind ss
else unwind ss
437 | ({ syntax
= Token t
; _
}::ss
)
438 when (Token.width t
) > 3 && String.sub
(Token.text t
) 0 3 = "<<<" ->
439 let rec unwind = function
440 | [{ syntax
= Token t
; _
}] when (Token.width t
) > 0 ->
441 let content = Token.text t
in
442 let len = (Token.width t
) in
443 let n = len - (String.rindex_from
content (len - 2) '
\n'
) in
444 let s = make_token
(trimRight ~
n t
) in
445 if width
s > 0 then [s] else []
446 | x
:: xs
-> x
:: unwind xs
447 | _
-> raise
(Invalid_argument
"Malformed String2 SyntaxList")
449 let content = Token.text t
in
450 let n = (String.index
content '
\n'
) + 1 in
451 let s = make_token
(trimLeft ~
n t
) in
452 if width
s > 0 then s :: unwind ss
else unwind ss
453 | x
-> x
(* unchanged *)
455 let mkStr : (string -> string) -> string -> string = fun unescaper
content ->
456 let content = if String.length content > 0 && content.[0] = 'b'
457 then String.sub
content 1 (String.length content - 1) else content in
458 let len = String.length content in
460 try (* Using String.sub; Invalid_argument when str too short *)
461 if len >= 3 && String.sub
content 0 3 = "<<<" (* The heredoc case *)
463 (* These types of strings begin with an opening line containing <<<
464 * followed by a string to use as a terminator (which is optionally
465 * quoted) and end with a line containing only the terminator and a
466 * semicolon followed by a blank line. We need to drop the opening line
467 * as well as the blank line and preceding terminator line.
469 let start = String.index
content '
\n'
+ 1 in
470 let end_ = String.rindex_from
content (len - 2) '
\n'
in
471 (* An empty heredoc, this way, will have start >= end *)
472 if start >= end_ then "" else String.sub
content start (end_ - start)
474 match String.get
content 0, String.get
content (len - 1) with
475 | '
"', '"'
| '
\''
, '
\''
| '`'
, '`'
-> String.sub
content 1 (len - 2)
477 with Invalid_argument _
-> content
479 try unescaper
no_quotes with
480 | Php_escaping.Invalid_string _
-> raise
@@
481 Failure
(Printf.sprintf
"Malformed string literal <<%s>>" no_quotes)
482 let unempty_str = function
483 | "''" | "\"\"" -> ""
485 let unesc_dbl s = unempty_str @@ Php_escaping.unescape_double
s
486 let get_quoted_content s =
488 if string_match
(regexp
"[ \t\n\r\012]*\"\\(\\(.\\|\n\\)*\\)\"") s 0
489 then matched_group
1 s
492 let whitespace = Str.regexp
"[ \t\n\r\012]+" in
493 Str.global_replace
whitespace " " s
494 let unesc_xhp_attr s =
495 unesc_dbl @@ get_quoted_content s
497 type suspension_kind
=
502 let mk_suspension_kind_ has_async has_coroutine
=
503 match has_async
, has_coroutine
with
504 | false, false -> SKSync
505 | true, false -> SKAsync
506 | false, true-> SKCoroutine
507 | true, true -> raise
(Failure
"Couroutine functions may not be async")
509 let mk_suspension_kind is_async is_coroutine
=
511 (not
(is_missing is_async
))
512 (not
(is_missing is_coroutine
))
514 let mk_fun_kind suspension_kind yield
=
515 match suspension_kind
, yield
with
516 | SKSync
, true -> FGenerator
517 | SKAsync
, true -> FAsyncGenerator
518 | SKSync
, false -> FSync
519 | SKAsync
, false -> FAsync
520 | SKCoroutine
, false -> FCoroutine
521 | SKCoroutine
, true -> raise
(Failure
"Couroutine functions may not yield")
523 let fun_template yielding node suspension_kind env
=
524 let p = pFunction node env
in
525 { f_mode
= mode_annotation env
.fi_mode
529 ; f_ret_by_ref
= false
530 ; f_name
= p, ";anonymous"
533 ; f_user_attributes
= []
534 ; f_fun_kind
= mk_fun_kind suspension_kind yielding
535 ; f_namespace
= Namespace_env.empty env
.parser_options
537 ; f_doc_comment
= None
541 let param_template node env
=
543 ; param_is_reference
= false
544 ; param_is_variadic
= false
545 ; param_id
= pos_name node env
547 ; param_modifier
= None
548 ; param_callconv
= None
549 ; param_user_attributes
= []
552 let pShapeFieldName : shape_field_name parser
= fun name env
->
553 match syntax
name with
554 | ScopeResolutionExpression
555 { scope_resolution_qualifier
; scope_resolution_name
; _
} ->
557 ( pos_name scope_resolution_qualifier env
558 , pos_name scope_resolution_name env
560 | _
-> let p, n = pos_name name env
in SFlit
(p, mkStr unesc_dbl n)
562 let mpShapeExpressionField : ('a
, (shape_field_name
* 'a
)) metaparser
=
563 fun hintParser node env
->
564 match syntax node
with
566 { field_initializer_name
= name; field_initializer_value
= ty
; _
} ->
567 let name = pShapeFieldName name env
in
568 let ty = hintParser
ty env
in
570 | _
-> missing_syntax "shape field" node env
572 let mpShapeField : ('a
, shape_field
) metaparser
=
573 fun hintParser node env
->
574 match syntax node
with
575 | FieldSpecifier
{ field_question
; field_name
; field_type
; _
} ->
576 let sf_optional = not
(is_missing field_question
) in
577 let sf_name = pShapeFieldName field_name env
in
578 let sf_hint = hintParser field_type env
in
579 { sf_optional; sf_name; sf_hint }
581 let sf_name, sf_hint = mpShapeExpressionField hintParser node env
in
582 (* Shape expressions can never have optional fields. *)
583 { sf_optional = false; sf_name; sf_hint }
585 let mpClosureParameter : ('a
, hint
* param_kind
option) metaparser
=
586 fun hintParser node env
->
587 match syntax node
with
588 | ClosureParameterTypeSpecifier
589 { closure_parameter_call_convention
590 ; closure_parameter_type
593 mpOptional pParamKind closure_parameter_call_convention env
in
594 let cp_hint = hintParser closure_parameter_type env
in
596 | _
-> missing_syntax "closure parameter" node env
598 let rec pHint : hint parser
= fun node env
->
599 let rec pHint_ : hint_ parser
= fun node env
->
600 match syntax node
with
601 (* Dirty hack; CastExpression can have type represented by token *)
603 | SimpleTypeSpecifier _
605 -> Happly
(pos_name node env
, [])
606 | ShapeTypeSpecifier
{ shape_type_fields
; shape_type_ellipsis
; _
} ->
607 let si_allows_unknown_fields =
608 not
(is_missing shape_type_ellipsis
)
610 let si_shape_field_list =
611 couldMap ~
f:(mpShapeField pHint) shape_type_fields env
in
612 Hshape
{ si_allows_unknown_fields; si_shape_field_list }
613 | TupleTypeSpecifier
{ tuple_types
; _
} ->
614 Htuple
(couldMap ~
f:pHint tuple_types env
)
615 | KeysetTypeSpecifier
{ keyset_type_keyword
= kw
; keyset_type_type
= ty; _
}
616 | VectorTypeSpecifier
{ vector_type_keyword
= kw
; vector_type_type
= ty; _
}
617 | ClassnameTypeSpecifier
{classname_keyword
= kw
; classname_type
= ty; _
}
618 | TupleTypeExplicitSpecifier
619 { tuple_type_keyword
= kw
620 ; tuple_type_types
= ty
622 | VarrayTypeSpecifier
623 { varray_keyword
= kw
626 | VectorArrayTypeSpecifier
627 { vector_array_keyword
= kw
628 ; vector_array_type
= ty
630 -> Happly
(pos_name kw env
, couldMap ~
f:pHint ty env
)
632 | DarrayTypeSpecifier
633 { darray_keyword
= kw
635 ; darray_value
= value
637 | MapArrayTypeSpecifier
638 { map_array_keyword
= kw
639 ; map_array_key
= key
640 ; map_array_value
= value
644 , pHint key env
:: couldMap ~
f:pHint value env
646 | DictionaryTypeSpecifier
647 { dictionary_type_keyword
= kw
648 ; dictionary_type_members
= members
649 ; _
} -> Happly
(pos_name kw env
, couldMap ~
f:pHint members env
)
650 | GenericTypeSpecifier
{ generic_class_type
; generic_argument_list
} ->
652 ( pos_name generic_class_type env
653 , match syntax generic_argument_list
with
654 | TypeArguments
{ type_arguments_types
; _
}
655 -> couldMap ~
f:pHint type_arguments_types env
656 | _
-> missing_syntax "generic type arguments" generic_argument_list env
658 | NullableTypeSpecifier
{ nullable_type
; _
} ->
659 Hoption
(pHint nullable_type env
)
660 | SoftTypeSpecifier
{ soft_type
; _
} ->
661 Hsoft
(pHint soft_type env
)
662 | ClosureTypeSpecifier
{
663 closure_parameter_list
;
665 closure_coroutine
; _
} ->
666 let make_variadic_hint variadic_type
=
667 if is_missing variadic_type
668 then Hvariadic
(None
)
669 else Hvariadic
(Some
(pHint variadic_type env
))
671 let (param_list
, variadic_hints
) =
672 List.partition_map ~
f:(fun x
->
674 | VariadicParameter
{ variadic_parameter_type
= vtype
; _
} ->
675 `Snd
(make_variadic_hint vtype
)
676 | _
-> `Fst
(mpClosureParameter pHint x env
))
677 (as_list closure_parameter_list
)
679 let hd_variadic_hint hints
=
680 if List.length hints
> 1 then begin
681 let msg = Printf.sprintf
682 "%d variadic parameters found. There should be no more than one."
685 invariant_failure node
msg env
687 match List.hd hints
with
689 | None
-> Hnon_variadic
691 let is_coroutine = not
(is_missing closure_coroutine
) in
692 let param_type_hints = List.map param_list fst
in
693 let param_callconvs = List.map param_list snd
in
698 , hd_variadic_hint variadic_hints
699 , pHint closure_return_type env
701 | TypeConstant
{ type_constant_left_type
; type_constant_right_type
; _
} ->
702 let child = pos_name type_constant_right_type env
in
703 (match pHint_ type_constant_left_type env
with
704 | Haccess
(b
, c
, cs
) -> Haccess
(b
, c
, cs
@ [child])
705 | Happly
(b
, []) -> Haccess
(b
, child, [])
706 | _
-> missing_syntax "type constant base" node env
708 | _
-> missing_syntax "type hint" node env
710 pPos node env
, pHint_ node env
713 { fh_suspension_kind
: suspension_kind
715 ; fh_constrs
: (hint
* constraint_kind
* hint
) list
716 ; fh_type_parameters
: tparam list
717 ; fh_parameters
: fun_param list
718 ; fh_return_type
: hint
option
719 ; fh_param_modifiers
: fun_param list
720 ; fh_ret_by_ref
: bool
724 { fh_suspension_kind
= SKSync
725 ; fh_name
= Pos.none
, "<ANONYMOUS>"
727 ; fh_type_parameters
= []
729 ; fh_return_type
= None
730 ; fh_param_modifiers
= []
731 ; fh_ret_by_ref
= false
734 let rec pSimpleInitializer node env
=
735 match syntax node
with
736 | SimpleInitializer
{ simple_initializer_value
; simple_initializer_equal
} ->
737 pExpr simple_initializer_value env
738 | _
-> missing_syntax "simple initializer" node env
740 and pFunParam
: fun_param parser
= fun node env
->
741 match syntax node
with
742 | ParameterDeclaration
743 { parameter_attribute
744 ; parameter_visibility
745 ; parameter_call_convention
748 ; parameter_default_value
750 let is_reference, is_variadic
, name =
751 match syntax parameter_name
with
752 | DecoratedExpression
753 { decorated_expression_decorator
; decorated_expression_expression
} ->
754 (* There is a chance that the expression might be nested with an
755 additional decorator, check this *)
756 begin match syntax decorated_expression_expression
with
757 | DecoratedExpression
758 { decorated_expression_decorator
= nested_decorator
759 ; decorated_expression_expression
= nested_expression
} ->
760 let decorator = text decorated_expression_decorator
in
761 let nested_decorator = text
nested_decorator in
762 decorator = "&" || nested_decorator = "&",
763 decorator = "..." || nested_decorator = "...",
766 let decorator = text decorated_expression_decorator
in
767 decorator = "&", decorator = "...", decorated_expression_expression
769 | _
-> false, false, parameter_name
771 { param_hint
= mpOptional pHint parameter_type env
772 ; param_is_reference
= is_reference
773 ; param_is_variadic
= is_variadic
774 ; param_id
= pos_name name env
776 mpOptional pSimpleInitializer parameter_default_value env
777 ; param_user_attributes
= pUserAttributes env parameter_attribute
779 mpOptional pParamKind parameter_call_convention env
780 (* implicit field via constructor parameter.
781 * This is always None except for constructors and the modifier
782 * can be only Public or Protected or Private.
785 let rec go = function
787 | x
:: _
when List.mem
[Private
; Public
; Protected
] x
-> Some x
790 go (pKinds parameter_visibility env
)
792 | VariadicParameter _
793 | Token _
when text node
= "..."
794 -> { (param_template node env
) with param_is_variadic
= true }
795 | _
-> missing_syntax "function parameter" node env
796 and pUserAttribute
: user_attribute list parser
= fun node env
->
797 match syntax node
with
798 | AttributeSpecification
{ attribute_specification_attributes
; _
} ->
799 couldMap attribute_specification_attributes env ~
f:begin function
800 | { syntax
= Attribute
{ attribute_name
; attribute_values
; _
}; _
} ->
802 { ua_name
= pos_name attribute_name env
803 ; ua_params
= couldMap ~
f:pExpr attribute_values env
805 | node
-> missing_syntax "attribute" node
807 | _
-> missing_syntax "attribute specification" node env
809 and pUserAttributes env attrs
=
810 List.concat
@@ couldMap ~
f:pUserAttribute attrs env
811 and pAField
: afield parser
= fun node env
->
812 match syntax node
with
813 | ElementInitializer
{ element_key
; element_value
; _
} ->
814 AFkvalue
(pExpr element_key env
, pExpr element_value env
)
815 | _
-> AFvalue
(pExpr node env
)
816 and pString2
: expr_location
-> node list
-> env
-> expr list
=
817 let rec convert_name_to_lvar location env
n =
819 | Token token
when Token.kind token
= TK.Name
->
820 let pos, name = pos_name n env
in
821 let id = Lvar
(pos, "$" ^
name) in
823 | SubscriptExpression
{ subscript_receiver
; subscript_index
; _
} ->
824 begin match convert_name_to_lvar location env subscript_receiver
with
826 let index = mpOptional (pExpr ~location
) subscript_index env
in
827 Some
(pPos n env
, Array_get
(recv
, index))
832 let rec aux loc l env acc
=
833 (* in PHP "${x}" in strings is treated as if it was written "$x",
834 here we recognize pattern: Dollar; EmbeddedBracedExpression { QName (Token.Name) }
835 produced by FFP and lower it into Lvar.
839 | ({ syntax
= Token token
; _
})::
840 ({ syntax
= EmbeddedBracedExpression
{
841 embedded_braced_expression_expression
= e
; _
}; _
842 } as expr_with_braces
)::
843 tl
when Token.kind token
= TK.Dollar
->
845 begin match convert_name_to_lvar loc env
e with
848 let e = pExpr ~location
:loc expr_with_braces env
in
849 fst
e, Dollar
(fst
e, BracedExpr
e)
851 aux loc tl env
(e::acc
)
852 | x
::xs
-> aux loc xs env
((pExpr ~location
:loc x env
)::acc
)
854 fun loc l env
-> aux loc l env
[]
855 and pExprL node env
=
856 (pPos node env
, Expr_list
(couldMap ~
f:pExpr node env
))
857 and pExpr ?location
:(location
=TopLevel
) : expr parser
= fun node env
->
858 let split_args_varargs arg_list
=
859 match List.rev
(as_list arg_list
) with
860 | { syntax
= DecoratedExpression
861 { decorated_expression_decorator
=
862 { syntax
= Token token
; _
}
863 ; decorated_expression_expression
= e
866 } :: xs
when Token.kind token
= TK.DotDotDot
->
867 let args = List.rev_map xs
(fun x
-> pExpr x env
) in
868 let vararg = pExpr
e env
in
871 let args = couldMap ~
f:pExpr arg_list env
in
873 let rec pExpr_ : expr_ parser
= fun node env
->
874 let pos = pPos node env
in
875 match syntax node
with
877 lambda_async
; lambda_coroutine
; lambda_signature
; lambda_body
;
878 lambda_attribute_spec
; _
} ->
879 let suspension_kind =
880 mk_suspension_kind lambda_async lambda_coroutine
in
881 let f_params, f_ret
=
882 match syntax lambda_signature
with
883 | LambdaSignature
{ lambda_parameters
; lambda_type
; _
} ->
884 ( couldMap ~
f:pFunParam lambda_parameters env
885 , mpOptional pHint lambda_type env
887 | Token _
-> ([param_template lambda_signature env
], None
)
888 | _
-> missing_syntax "lambda signature" lambda_signature env
891 mpYielding pFunctionBody lambda_body
892 (if not
(is_compound_statement lambda_body
) then env
else non_tls env
)
895 { (fun_template yield node
suspension_kind env
) with
899 ; f_user_attributes
= pUserAttributes env lambda_attribute_spec
902 | BracedExpression
{ braced_expression_expression
= expr
; _
}
903 | EmbeddedBracedExpression
904 { embedded_braced_expression_expression
= expr
; _
}
905 | ParenthesizedExpression
{ parenthesized_expression_expression
= expr
; _
}
908 | DictionaryIntrinsicExpression
909 { dictionary_intrinsic_keyword
= kw
910 ; dictionary_intrinsic_members
= members
912 | KeysetIntrinsicExpression
913 { keyset_intrinsic_keyword
= kw
914 ; keyset_intrinsic_members
= members
916 | VectorIntrinsicExpression
917 { vector_intrinsic_keyword
= kw
918 ; vector_intrinsic_members
= members
922 try Some
(couldMap ~
f:pExpr members env
) with
923 | API_Missing_syntax
(_
,_
,n)
924 when kind n = SyntaxKind.ElementInitializer
-> None
926 if env
.is_hh_file
|| env
.enable_hh_syntax
|| members_opt = None
927 then Collection
(pos_name kw env
, couldMap ~
f:pAField members env
)
929 (* If php, this is a subscript expression, not a collection. *)
930 let subscript_receiver = pExpr kw env
in
931 let members = couldMap ~
f:pExpr
members env
in
932 let subscript_index = match members with
936 let msg = "Hack keyword " ^
(text kw
) ^
" used improperly in php." in
937 invariant_failure node
msg env
in
938 Array_get
(subscript_receiver, subscript_index)
939 | CollectionLiteralExpression
940 { collection_literal_name
= collection_name
941 ; collection_literal_initializers
= members
943 let collection_name =
944 match syntax
collection_name with
945 | SimpleTypeSpecifier
{ simple_type_specifier
= class_type
}
946 (* TODO: currently type arguments are dropped on the floor,
947 though they should be properly propagated to the typechecker *)
948 | GenericTypeSpecifier
{ generic_class_type
= class_type
; _
} ->
949 pos_name class_type env
950 | _
-> pos_name collection_name env
in
951 Collection
(collection_name, couldMap ~
f:pAField
members env
)
953 | VarrayIntrinsicExpression
{ varray_intrinsic_members
= members; _
} ->
954 Varray
(couldMap ~
f:pExpr
members env
)
955 | DarrayIntrinsicExpression
{ darray_intrinsic_members
= members; _
} ->
956 let pMember node env
=
957 match syntax node
with
958 | ElementInitializer
{ element_key
; element_value
; _
} ->
959 (pExpr element_key env
, pExpr element_value env
)
960 | _
-> missing_syntax "darray intrinsic expression element" node env
962 Darray
(couldMap ~
f:pMember members env
)
963 | ArrayIntrinsicExpression
{ array_intrinsic_members
= members; _
}
964 | ArrayCreationExpression
{ array_creation_members
= members; _
}
966 (* TODO: Or tie in with other intrinsics and post-process to Array *)
967 Array
(couldMap ~
f:pAField
members env
)
969 | ListExpression
{ list_members
= members; _
} ->
970 (* TODO: Or tie in with other intrinsics and post-process to List *)
971 let pBinderOrIgnore node env
=
972 Option.value ~default
:(Pos.none
, Omitted
) @@ mpOptional pExpr node env
974 List
(couldMap ~
f:pBinderOrIgnore members env
)
976 | EvalExpression
{ eval_keyword
= recv
; eval_argument
= args; _
}
977 | EmptyExpression
{ empty_keyword
= recv
; empty_argument
= args; _
}
978 | IssetExpression
{ isset_keyword
= recv
; isset_argument_list
= args; _
}
980 { tuple_expression_keyword
= recv
981 ; tuple_expression_items
= args
983 -> Call
(pExpr recv env
, [], couldMap ~
f:pExpr
args env
, [])
984 | FunctionCallExpression
985 { function_call_receiver
= recv
986 ; function_call_argument_list
= args
990 begin match (syntax recv
) with
991 | GenericTypeSpecifier
{generic_class_type
; generic_argument_list
} ->
992 begin match syntax generic_argument_list
with
993 | TypeArguments
{ type_arguments_types
; _
}
994 -> couldMap ~
f:pHint type_arguments_types env
1000 (* preserve parens on receiver of call expression
1001 to allow distinguishing between
1002 ($a->b)() // invoke on callable property
1003 $a->b() // method call *)
1004 let pos_if_has_parens =
1005 match syntax recv
with
1006 | ParenthesizedExpression _
-> Some
(pPos recv env
)
1008 let recv = pExpr
recv env
in
1010 match snd
recv, pos_if_has_parens with
1011 | (Obj_get _
| Class_get _
), Some
p -> p, ParenthesizedExpr
recv
1013 let args, varargs
= split_args_varargs args in
1014 Call
(recv, hints, args, varargs
)
1015 | FunctionCallWithTypeArgumentsExpression
1016 { function_call_with_type_arguments_receiver
= recv
1017 ; function_call_with_type_arguments_type_args
= type_args
1018 ; function_call_with_type_arguments_argument_list
= args
1022 begin match (syntax type_args
) with
1023 | TypeArguments
{ type_arguments_types
; _
} ->
1024 couldMap ~
f:pHint type_arguments_types env
1025 | _
-> missing_syntax "type arguments" type_args env
1027 Call
(pExpr
recv env
, hints, couldMap ~
f:pExpr
args env
, [])
1028 | QualifiedName _
->
1029 if in_string location
1030 then String
(pos_qualified_name node env
)
1031 else Id
(pos_qualified_name node env
)
1033 | VariableExpression
{ variable_expression
} ->
1034 Lvar
(pos_name variable_expression env
)
1036 | PipeVariableExpression _
->
1039 | InclusionExpression
{ inclusion_require
; inclusion_filename
} ->
1041 ( pImportFlavor inclusion_require env
1042 , pExpr inclusion_filename env
1045 | MemberSelectionExpression
1046 { member_object
= recv
1047 ; member_operator
= op
1048 ; member_name
= name
1050 | SafeMemberSelectionExpression
1051 { safe_member_object
= recv
1052 ; safe_member_operator
= op
1053 ; safe_member_name
= name
1055 | EmbeddedMemberSelectionExpression
1056 { embedded_member_object
= recv
1057 ; embedded_member_operator
= op
1058 ; embedded_member_name
= name
1061 let recv = pExpr
recv env
in
1062 let name = pExpr ~location
:MemberSelect
name env
in
1063 let op = pNullFlavor op env
in
1064 Obj_get
(recv, name, op)
1066 | PrefixUnaryExpression
1067 { prefix_unary_operator
= operator
1068 ; prefix_unary_operand
= operand
1070 | PostfixUnaryExpression
1071 { postfix_unary_operand
= operand
1072 ; postfix_unary_operator
= operator
1074 | DecoratedExpression
1075 { decorated_expression_expression
= operand
1076 ; decorated_expression_decorator
= operator
1079 let expr = pExpr operand env
in
1081 * FFP does not destinguish between ++$i and $i++ on the level of token
1082 * kind annotation. Prevent duplication by switching on `postfix` for
1083 * the two operatores for which AST /does/ differentiate between
1086 let postfix = kind node
= SyntaxKind.PostfixUnaryExpression
in
1087 (match token_kind operator
with
1088 | Some
TK.PlusPlus
when postfix -> Unop
(Upincr
, expr)
1089 | Some
TK.MinusMinus
when postfix -> Unop
(Updecr
, expr)
1090 | Some
TK.PlusPlus
-> Unop
(Uincr
, expr)
1091 | Some
TK.MinusMinus
-> Unop
(Udecr
, expr)
1092 | Some
TK.Exclamation
-> Unop
(Unot
, expr)
1093 | Some
TK.Tilde
-> Unop
(Utild
, expr)
1094 | Some
TK.Plus
-> Unop
(Uplus
, expr)
1095 | Some
TK.Minus
-> Unop
(Uminus
, expr)
1096 | Some
TK.Ampersand
-> Unop
(Uref
, expr)
1097 | Some
TK.At
when env
.codegen
-> Unop
(Usilence
, expr)
1098 | Some
TK.At
-> snd
expr
1099 | Some
TK.Inout
-> Callconv
(Pinout
, expr)
1100 | Some
TK.Await
-> Await
expr
1101 | Some
TK.Suspend
-> Suspend
expr
1102 | Some
TK.Clone
-> Clone
expr
1104 Call
((pos, Id
(pos, "echo")), [], [expr], [])
1106 (match snd
expr with
1109 | Float
(p, s) when location
<> InGlobalVar
-> Lvar
(p, "$" ^
s)
1112 | _
-> missing_syntax "unary operator" node env
1115 { binary_left_operand
; binary_operator
; binary_right_operand
}
1117 pBop binary_operator env
1118 (pExpr binary_left_operand env
)
1119 (pExpr binary_right_operand env
)
1122 (match location
, Token.kind t
with
1123 | MemberSelect
, TK.Variable
-> Lvar
(pos_name node env
)
1124 | InDoubleQuotedString
, TK.HeredocStringLiteral
1125 | InDoubleQuotedString
, TK.HeredocStringLiteralHead
1126 | InDoubleQuotedString
, TK.HeredocStringLiteralTail
->
1127 String
(pos, Php_escaping.unescape_heredoc
(text node
))
1128 | InDoubleQuotedString
, _
-> String
(pos, unesc_dbl (text node
))
1129 | InBacktickedString
, _
-> String
(pos, Php_escaping.unescape_backtick
(text node
))
1132 | TopLevel
, _
-> Id
(pos_name node env
)
1135 | YieldExpression
{ yield_operand
; _
} when text yield_operand
= "break" ->
1136 env
.saw_yield
<- true;
1138 | YieldExpression
{ yield_operand
; _
} when is_missing yield_operand
->
1139 env
.saw_yield
<- true;
1140 Yield
(AFvalue
(pos, Null
))
1141 | YieldExpression
{ yield_operand
; _
} ->
1142 env
.saw_yield
<- true;
1143 Yield
(pAField yield_operand env
)
1144 | YieldFromExpression
{ yield_from_operand
; _
} ->
1145 env
.saw_yield
<- true;
1146 Yield_from
(pExpr yield_from_operand env
)
1148 | DefineExpression
{ define_keyword
; define_argument_list
; _
} -> Call
1149 ( (let name = pos_name define_keyword env
in fst
name, Id
name)
1151 , List.map ~
f:(fun x
-> pExpr x env
) (as_list define_argument_list
)
1155 | ScopeResolutionExpression
1156 { scope_resolution_qualifier
; scope_resolution_name
; _
} ->
1158 match pExpr scope_resolution_qualifier env
with
1159 | p, Lvar v
when not env
.codegen
-> p, Id v
1162 begin match syntax scope_resolution_name
with
1163 | Token token
when Token.kind token
= TK.Variable
->
1165 ( pPos scope_resolution_name env
1166 , Lvar
(pos_name scope_resolution_name env
)
1169 Class_get
(qual, name)
1171 let name = pExpr scope_resolution_name env
in
1172 begin match snd
name with
1173 | String
id | Id
id -> Class_const
(qual, id)
1174 | _
-> Class_get
(qual, name)
1177 | CastExpression
{ cast_type
; cast_operand
; _
} ->
1178 Cast
(pHint cast_type env
, pExpr cast_operand env
)
1179 | ConditionalExpression
1180 { conditional_test
; conditional_consequence
; conditional_alternative
; _
}
1182 ( pExpr conditional_test env
1183 , mpOptional pExpr conditional_consequence env
1184 , pExpr conditional_alternative env
1186 | SubscriptExpression
{ subscript_receiver; subscript_index; _
} ->
1188 ( pExpr
subscript_receiver env
1189 , mpOptional pExpr
subscript_index env
1191 | EmbeddedSubscriptExpression
1192 { embedded_subscript_receiver
; embedded_subscript_index
; _
} ->
1194 ( pExpr embedded_subscript_receiver env
1195 , mpOptional (pExpr ~location
) embedded_subscript_index env
1197 | ShapeExpression
{ shape_expression_fields
; _
} ->
1199 couldMap ~
f:(mpShapeExpressionField pExpr
) shape_expression_fields env
1201 | ObjectCreationExpression
{ object_creation_object
= obj
; _
} ->
1204 { constructor_call_argument_list
; constructor_call_type
; _
} ->
1205 let args, varargs
= split_args_varargs constructor_call_argument_list
in
1207 ( (match syntax constructor_call_type
with
1208 | GenericTypeSpecifier
{ generic_class_type
; generic_argument_list
} ->
1209 let name = pos_name generic_class_type env
in
1211 match syntax generic_argument_list
with
1212 | TypeArguments
{ type_arguments_types
; _
}
1213 -> couldMap ~
f:pHint type_arguments_types env
1215 missing_syntax "generic type arguments" generic_argument_list env
1217 fst
name, Id_type_arguments
(name, hints)
1218 | SimpleTypeSpecifier _
->
1219 let name = pos_name constructor_call_type env
in fst
name, Id
name
1220 | _
-> pExpr constructor_call_type env
1226 { anonymous_class_argument_list
= args
1227 ; anonymous_class_extends_list
= extends
1228 ; anonymous_class_implements_list
= implements
1229 ; anonymous_class_body
=
1230 { syntax
= ClassishBody
{ classish_body_elements
= elts
; _
}; _
}
1232 let args, varargs
= split_args_varargs args in
1233 let c_mode = mode_annotation env
.fi_mode
in
1234 let c_user_attributes = [] in
1235 let c_final = false in
1236 let c_is_xhp = false in
1237 let c_name = pos, "anonymous" in
1238 let c_tparams = [] in
1239 let c_extends = couldMap ~
f:pHint extends env
in
1240 let c_implements = couldMap ~
f:pHint implements env
in
1241 let c_body = List.concat
(couldMap ~
f:pClassElt elts env
) in
1242 let c_namespace = Namespace_env.empty env
.parser_options in
1243 let c_enum = None
in
1244 let c_span = pPos node env
in
1245 let c_kind = Cnormal
in
1246 let c_doc_comment = None
in
1263 NewAnonClass
(args, varargs
, cls)
1264 | GenericTypeSpecifier
1265 { generic_class_type
1266 ; generic_argument_list
1268 let name = pos_name generic_class_type env
in
1270 match syntax generic_argument_list
with
1271 | TypeArguments
{ type_arguments_types
; _
}
1272 -> couldMap ~
f:pHint type_arguments_types env
1274 missing_syntax "generic type arguments" generic_argument_list env
1276 Id_type_arguments
(name, hints)
1277 | LiteralExpression
{ literal_expression
= expr } ->
1278 (match syntax
expr with
1280 let s = text
expr in
1281 (* We allow underscores while lexing the integer literals. This function gets
1282 * rid of them before the literal is created. *)
1283 let eliminate_underscores s = s
1284 |> Str.split
(Str.regexp
"_")
1285 |> String.concat
"" in
1286 (* TODO(17796330): Get rid of linter functionality in the lowerer *)
1287 if s <> String.lowercase_ascii
s then Lint.lowercase_constant
pos s;
1288 (match location
, token_kind expr with
1289 (* TODO(T21285960): Inside strings, int indices "should" be string indices *)
1290 | InDoubleQuotedString
, _
when env
.codegen
-> String
(pos, mkStr unesc_dbl s)
1291 | InBacktickedString
, _
when env
.codegen
-> String
(pos, mkStr Php_escaping.unescape_backtick
s)
1292 | _
, Some
TK.DecimalLiteral
1293 | _
, Some
TK.OctalLiteral
1294 | _
, Some
TK.HexadecimalLiteral
1295 | _
, Some
TK.BinaryLiteral
-> Int
(pos, eliminate_underscores s)
1296 | _
, Some
TK.FloatingLiteral
-> Float
(pos, s)
1297 | _
, Some
TK.SingleQuotedStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_single
s)
1298 | _
, Some
TK.DoubleQuotedStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_double
s)
1299 | _
, Some
TK.HeredocStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_heredoc
s)
1300 | _
, Some
TK.NowdocStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_nowdoc
s)
1301 | _
, Some
TK.NullLiteral
-> Null
1302 | _
, Some
TK.BooleanLiteral
->
1303 (match String.lowercase_ascii
s with
1306 | _
-> missing_syntax ("boolean (not: " ^
s ^
")") expr env
1308 | _
, Some
TK.ExecutionStringLiteral
->
1309 Execution_operator
[pos, String
(pos, mkStr Php_escaping.unescape_backtick
s)]
1310 | _
-> missing_syntax "literal" expr env
1312 | SyntaxList
({ syntax
= Token token
; _
} :: _
as ts
)
1313 when Token.kind token
= TK.ExecutionStringLiteralHead
->
1314 Execution_operator
(pString2 InBacktickedString
(prepString2 ts
) env
)
1315 | SyntaxList ts
-> String2
(pString2 InDoubleQuotedString
(prepString2 ts
) env
)
1316 | _
-> missing_syntax "literal expression" expr env
1319 | InstanceofExpression
1320 { instanceof_left_operand
; instanceof_right_operand
; _
} ->
1322 match pExpr instanceof_right_operand env
with
1323 | p, Class_const
((_
, pid
), (_
, "")) -> p, pid
1326 InstanceOf
(pExpr instanceof_left_operand env
, ty)
1327 (* TODO: Priority fix? *)
1328 (*match pExpr instanceof_left_operand env with
1329 | p, Unop (o,e) -> Unop (0, (p, InstanceOf (e, ty)))
1330 | e -> InstanceOf (e, ty)
1333 { is_left_operand
; is_right_operand
; _
} ->
1334 Is
(pExpr is_left_operand env
, pHint is_right_operand env
)
1336 { as_left_operand
; as_right_operand
; _
} ->
1337 As
(pExpr as_left_operand env
, pHint as_right_operand env
, false)
1338 | NullableAsExpression
1339 { nullable_as_left_operand
; nullable_as_right_operand
; _
} ->
1340 As
(pExpr nullable_as_left_operand env
,
1341 pHint nullable_as_right_operand env
,
1344 { anonymous_attribute_spec
= attribute_spec
1345 ; anonymous_static_keyword
1346 ; anonymous_async_keyword
1347 ; anonymous_coroutine_keyword
1348 ; anonymous_ampersand
1349 ; anonymous_parameters
1354 | Php7AnonymousFunction
1355 { php7_anonymous_attribute_spec
= attribute_spec
1356 ; php7_anonymous_static_keyword
= anonymous_static_keyword
1357 ; php7_anonymous_async_keyword
= anonymous_async_keyword
1358 ; php7_anonymous_coroutine_keyword
= anonymous_coroutine_keyword
1359 ; php7_anonymous_ampersand
= anonymous_ampersand
1360 ; php7_anonymous_parameters
= anonymous_parameters
1361 ; php7_anonymous_type
= anonymous_type
1362 ; php7_anonymous_use
= anonymous_use
1363 ; php7_anonymous_body
= anonymous_body
1366 match syntax node
with
1367 | PrefixUnaryExpression
1368 { prefix_unary_operator
= op; prefix_unary_operand
= v
} ->
1369 pos_name v env
, token_kind op = Some
TK.Ampersand
1370 | Token _
-> pos_name node env
, false
1371 | _
-> missing_syntax "use variable" node env
1374 match syntax node
with
1375 | AnonymousFunctionUseClause
{ anonymous_use_variables
; _
} ->
1376 couldMap ~
f:pArg anonymous_use_variables
1377 | _
-> fun _env
-> []
1379 let suspension_kind =
1381 anonymous_async_keyword
1382 anonymous_coroutine_keyword
in
1384 mpYielding pFunctionBody anonymous_body
(non_tls env
)
1386 let doc_comment = match extract_docblock node
with
1387 | Some _
as doc_comment -> doc_comment
1388 | None
-> top_docblock
() in
1389 let f_ret_by_ref = is_ret_by_ref anonymous_ampersand
in
1390 let user_attributes = pUserAttributes env attribute_spec
in
1392 ( { (fun_template yield node
suspension_kind env
) with
1393 f_ret
= mpOptional pHint anonymous_type env
1394 ; f_params = couldMap ~
f:pFunParam anonymous_parameters env
1396 ; f_static
= not
(is_missing anonymous_static_keyword
)
1398 ; f_doc_comment
= doc_comment
1399 ; f_user_attributes
= user_attributes
1401 , try pUse anonymous_use env
with _
-> []
1404 | AwaitableCreationExpression
1405 { awaitable_async
; awaitable_coroutine
; awaitable_compound_statement
;
1406 awaitable_attribute_spec
; } ->
1407 let suspension_kind =
1408 mk_suspension_kind awaitable_async awaitable_coroutine
in
1409 let blk, yld
= mpYielding pFunctionBody awaitable_compound_statement env
in
1411 { (fun_template yld node
suspension_kind env
) with
1412 f_body = mk_noop (pPos awaitable_compound_statement env
) blk;
1413 f_user_attributes
= pUserAttributes env awaitable_attribute_spec
}
1415 Call
((pPos node env
, Lfun
body), [], [], [])
1418 { syntax
= XHPOpen
{ xhp_open_name
; xhp_open_attributes
; _
}; _
}
1421 env
.ignore_pos
<- false;
1423 let pos, name = pos_name xhp_open_name env
in
1431 let aggregate_tokens node
=
1432 let rec search = function (* scroll through non-token things *)
1434 | t
:: xs
when token_kind t
= Some
TK.XHPComment
-> search xs
1435 | { syntax
= Token b
; _
} as t
:: xs
-> track t b None xs
1436 | x
:: xs
-> x
:: search xs
1437 and track t b oe
= function (* keep going through consecutive tokens *)
1438 | { syntax
= Token
e; _
} :: xs
1439 when Token.kind e <> TK.XHPComment
-> track t b
(Some
e) xs
1440 | xs
-> Option.value_map oe ~default
:t ~
f:(combine b
) :: search xs
1442 search (as_list node
)
1444 let pEmbedded escaper node env
=
1445 match syntax node
with
1447 when env
.codegen
&& Token.kind token
= TK.XHPStringLiteral
->
1448 let p = pPos node env
in
1449 (* for XHP string literals (attribute values) just extract
1450 value from quotes and decode HTML entities *)
1452 Html_entities.decode
@@ get_quoted_content (full_text node
) in
1455 when env
.codegen
&& Token.kind token
= TK.XHPBody
->
1456 let p = pPos node env
in
1457 (* for XHP body - only decode HTML entities *)
1458 let text = Html_entities.decode
@@ unesc_xhp (full_text node
) in
1461 let p = pPos node env
in
1462 p, String
(p, escaper
(full_text node
))
1464 match pExpr node env
with
1465 | _
, BracedExpr
e -> e
1468 let pAttr = fun node env
->
1469 match syntax node
with
1470 | XHPSimpleAttribute
{ xhp_simple_attribute_name
; xhp_simple_attribute_expression
; _
} ->
1471 let name = pos_name xhp_simple_attribute_name env
in
1473 if is_braced_expression xhp_simple_attribute_expression
1474 && env
.fi_mode
= FileInfo.Mdecl
&& not env
.codegen
1476 else pEmbedded unesc_xhp_attr xhp_simple_attribute_expression env
1478 Xhp_simple
(name, expr)
1479 | XHPSpreadAttribute
{ xhp_spread_attribute_expression
; _
} ->
1480 Xhp_spread
( pEmbedded unesc_xhp_attr xhp_spread_attribute_expression env
)
1481 | _
-> missing_syntax "XHP attribute" node env
1483 let attrs = couldMap ~
f:pAttr xhp_open_attributes env
in
1485 List.map ~
f:(fun x
-> pEmbedded unesc_xhp x env
) (aggregate_tokens body)
1487 Xml
(name, attrs, exprs)
1488 (* FIXME; should this include Missing? ; "| Missing -> Null" *)
1489 | _
-> missing_syntax ?fallback
:(Some Null
) "expression" node env
1491 let outer_unsafes = env
.unsafes
in
1493 (* when in codegen ignore UNSAFE_EXPRs *)
1494 if env
.codegen
then true
1496 match leading_token node
with
1500 Token.filter_leading_trivia_by_kind t
TriviaKind.UnsafeExpression
1502 let f acc u
= ISet.add
(Trivia.start_offset u
) acc
in
1503 let us = List.fold ~
f ~init
:ISet.empty
us in
1504 (* Have we already dealt with 'these' UNSAFE_EXPRs? *)
1505 let us = ISet.diff
us outer_unsafes in
1506 let safe = ISet.is_empty
us in
1507 if not
safe then env
.unsafes
<- ISet.union
us outer_unsafes;
1511 match syntax node
with
1512 | BracedExpression
{ braced_expression_expression
= expr; _
}
1513 | ParenthesizedExpression
{ parenthesized_expression_expression
= expr; _
}
1515 * Peeling off braced or parenthesised expresions. When there is XHP
1516 * inside, we want the XHP node to have this own positions, rather than
1517 * those of enclosing parenthesised/braced expressions.
1519 let inner = pExpr ~location
expr env
in
1520 if Syntax.is_braced_expression node
1522 (* We elide the braces in {$x}, as it makes compilation easier *)
1523 begin match inner with
1524 | _
, (Lvar _
| String _
| Int _
| Float _
) -> inner
1525 | p, _
-> p, BracedExpr
inner
1530 * Since we need positions in XHP, regardless of the ignore_pos flag, we
1531 * parenthesise the call to pExpr_ so that the XHP expression case can
1532 * flip the switch. The key part is that `pPos` happens before the old
1533 * setting is restored.
1535 let local_ignore_pos = env
.ignore_pos
in
1536 let expr_ = pExpr_ node env
in
1537 let p = pPos node env
in
1538 env
.ignore_pos
<- local_ignore_pos;
1541 if is_safe then result else fst
result, Unsafeexpr
result
1542 and pBlock
: block parser
= fun node env
->
1543 let rec fix_last acc
= function
1544 | x
:: (_
:: _
as xs
) -> fix_last (x
::acc
) xs
1545 | [_
, Block block
] -> List.rev acc
@ block
1546 | stmt
-> List.rev acc
@ stmt
1548 let stmt = pStmtUnsafe node env
in
1550 and pFunctionBody
: block parser
= fun node env
->
1551 match syntax node
with
1553 | CompoundStatement
{compound_statements
= {syntax
= Missing
; _
}; _
} ->
1555 | CompoundStatement
{compound_statements
= {syntax
= SyntaxList
[t
]; _
}; _
}
1556 when Syntax.is_specific_token
TK.Yield t
->
1557 env
.saw_yield
<- true;
1559 | CompoundStatement _
->
1560 let block = pBlock node env
in
1561 if not env
.top_level_statements
1562 && ( env
.fi_mode
= FileInfo.Mdecl
&& not env
.codegen
1564 then [ Pos.none
, Noop
]
1567 let p, r
= pExpr node env
in
1568 [p, Return
(Some
(p, r
))]
1569 and pStmtUnsafe
: stmt list parser
= fun node env
->
1570 let stmt = pStmt node env
in
1571 match leading_token node
with
1572 | Some t
when Token.has_trivia_kind t
TriviaKind.Unsafe
-> [Pos.none
, Unsafe
; stmt]
1574 and pStmt
: stmt parser
= fun node env
->
1575 extract_and_push_docblock node
;
1576 let pos = pPos node env
in
1577 let result = match syntax node
with
1578 | SwitchStatement
{ switch_expression
=expr; switch_sections
=sections
; _
}
1579 | AlternateSwitchStatement
{ alternate_switch_expression
=expr;
1580 alternate_switch_sections
=sections
; _
} ->
1581 let pSwitchLabel : (block -> case
) parser
= fun node env cont
->
1582 match syntax node
with
1583 | CaseLabel
{ case_expression
; _
} ->
1584 Case
(pExpr case_expression env
, cont
)
1585 | DefaultLabel _
-> Default cont
1586 | _
-> missing_syntax "pSwitchLabel" node env
1588 let pSwitchSection : case list parser
= fun node env
->
1589 match syntax node
with
1591 { switch_section_labels
1592 ; switch_section_statements
1593 ; switch_section_fallthrough
1595 let rec null_out cont
= function
1597 | (x
::xs
) -> x
[] :: null_out cont xs
1598 | _
-> raise
(Failure
"Malformed block result")
1600 let blk = List.concat
@@
1601 couldMap ~
f:pStmtUnsafe switch_section_statements env
in
1603 if is_missing switch_section_fallthrough
1605 else blk @ [Pos.none
, Fallthrough
]
1607 null_out blk (couldMap ~
f:pSwitchLabel switch_section_labels env
)
1608 | _
-> missing_syntax "switch section" node env
1613 , List.concat
@@ couldMap ~
f:pSwitchSection sections env
1616 { if_condition
=cond
;
1618 if_elseif_clauses
=elseif_clause
;
1619 if_else_clause
=else_clause
; _
}
1620 | AlternateIfStatement
1621 { alternate_if_condition
=cond
;
1622 alternate_if_statement
=stmt;
1623 alternate_if_elseif_clauses
=elseif_clause
;
1624 alternate_if_else_clause
=else_clause
; _
} ->
1625 (* Because consistency is for the weak-willed, Parser_hack does *not*
1626 * produce `Noop`s for compound statements **in if statements**
1628 let de_noop = function
1629 | [p, Block
[_
, Noop
]] -> [p, Block
[]]
1632 let if_condition = pExpr cond env
in
1633 let if_statement = de_noop (pStmtUnsafe
stmt env
) in
1634 let if_elseif_statement =
1635 let pElseIf : (block -> block) parser
= fun node env
->
1636 match syntax node
with
1637 | ElseifClause
{ elseif_condition
=ei_cond
; elseif_statement
=ei_stmt
; _
}
1638 | AlternateElseifClause
{ alternate_elseif_condition
=ei_cond
;
1639 alternate_elseif_statement
=ei_stmt
; _
} ->
1641 let elseif_condition = pExpr ei_cond env
in
1642 let elseif_statement = de_noop (pStmtUnsafe ei_stmt env
) in
1643 [ pos, If
(elseif_condition, elseif_statement, next_clause
) ]
1644 | _
-> missing_syntax "elseif clause" node env
1646 List.fold_right ~
f:(@@)
1647 (couldMap ~
f:pElseIf elseif_clause env
)
1648 ~init
:( match syntax else_clause
with
1649 | ElseClause
{ else_statement
=e_stmt
; _
}
1650 | AlternateElseClause
{ alternate_else_statement
=e_stmt
; _
} ->
1651 de_noop (pStmtUnsafe e_stmt env
)
1652 | Missing
-> [Pos.none
, Noop
]
1653 | _
-> missing_syntax "else clause" else_clause env
1656 pos, If
(if_condition, if_statement, if_elseif_statement)
1657 | ExpressionStatement
{ expression_statement_expression
; _
} ->
1658 if is_missing expression_statement_expression
1660 else pos, Expr
(pExpr expression_statement_expression env
)
1661 | CompoundStatement
{ compound_statements
; compound_right_brace
; _
} ->
1663 match leading_token compound_right_brace
with
1664 | Some t
when Token.has_trivia_kind t
TriviaKind.Unsafe
-> [pos, Unsafe
]
1667 handle_loop_body
pos compound_statements
tail env
1668 | AlternateLoopStatement
{ alternate_loop_statements
; _
} ->
1669 handle_loop_body
pos alternate_loop_statements
[] env
1671 handle_loop_body
pos node
[] env
1672 | ThrowStatement
{ throw_expression
; _
} ->
1673 pos, Throw
(pExpr throw_expression env
)
1674 | DoStatement
{ do_body
; do_condition
; _
} ->
1675 pos, Do
([pos, Block
(pBlock do_body env
)], pExpr do_condition env
)
1676 | WhileStatement
{ while_condition
; while_body
; _
} ->
1677 pos, While
(pExpr while_condition env
, pStmtUnsafe while_body env
)
1678 | DeclareDirectiveStatement
{ declare_directive_expression
; _
} ->
1679 pos, Declare
(false, pExpr declare_directive_expression env
, [])
1680 | DeclareBlockStatement
{ declare_block_expression
; declare_block_body
; _
} ->
1681 pos, Declare
(true, pExpr declare_block_expression env
,
1682 pStmtUnsafe declare_block_body env
)
1683 | UsingStatementBlockScoped
1684 { using_block_await_keyword
1685 ; using_block_expressions
1689 us_is_block_scoped
= true;
1690 us_has_await
= not
(is_missing using_block_await_keyword
);
1691 us_expr
= pExprL using_block_expressions env
;
1692 us_block
= [pos, Block
(pBlock using_block_body env
)];
1694 | UsingStatementFunctionScoped
1695 { using_function_await_keyword
1696 ; using_function_expression
1698 (* All regular function scoped using statements should
1699 * be rewritten by this point
1700 * If this gets run, it means that this using statement is the only one
1701 * in the block, hence it is not in a compound statement *)
1703 us_is_block_scoped
= false;
1704 us_has_await
= not
(is_missing using_function_await_keyword
);
1705 us_expr
= pExpr using_function_expression env
;
1706 us_block
= [Pos.none
, Block
[Pos.none
, Noop
]];
1709 { for_initializer
; for_control
; for_end_of_loop
; for_body
; _
} ->
1710 let ini = pExprL for_initializer env
in
1711 let ctr = pExprL for_control env
in
1712 let eol = pExprL for_end_of_loop env
in
1713 let blk = pStmtUnsafe for_body env
in
1714 pos, For
(ini, ctr, eol, blk)
1716 { foreach_collection
1717 ; foreach_await_keyword
1722 let col = pExpr foreach_collection env
in
1724 match syntax foreach_await_keyword
with
1725 | Token token
when Token.kind token
= TK.Await
->
1726 Some
(pPos foreach_await_keyword env
)
1730 let value = pExpr foreach_value env
in
1731 Option.value_map
(mpOptional pExpr foreach_key env
)
1732 ~default
:(As_v
value)
1733 ~
f:(fun key
-> As_kv
(key
, value))
1736 match pStmtUnsafe foreach_body env
with
1737 | [p, Block
[_
, Noop
]] -> [p, Block
[]]
1740 pos, Foreach
(col, akw, akv, blk)
1742 { try_compound_statement
; try_catch_clauses
; try_finally_clause
; _
} ->
1744 ( [ pPos try_compound_statement env
, Block
(pBlock try_compound_statement env
) ]
1745 , couldMap try_catch_clauses env ~
f:begin fun node env
->
1746 match syntax node
with
1747 | CatchClause
{ catch_type
; catch_variable
; catch_body
; _
} ->
1748 ( pos_name catch_type env
1749 , pos_name catch_variable env
1750 , [ pPos catch_body env
, Block
(mpStripNoop pBlock catch_body env
) ]
1752 | _
-> missing_syntax "catch clause" node env
1754 , match syntax try_finally_clause
with
1755 | FinallyClause
{ finally_body
; _
} ->
1756 [ pPos finally_body env
, Block
(pBlock finally_body env
) ]
1759 | FunctionStaticStatement
{ static_declarations
; _
} ->
1760 let pStaticDeclarator node env
=
1761 match syntax node
with
1762 | StaticDeclarator
{ static_name
; static_initializer
} ->
1764 match pExpr static_name env
with
1765 | p, Id
(p'
, s) -> p, Lvar
(p'
, s)
1768 (match syntax static_initializer
with
1769 | SimpleInitializer
{ simple_initializer_value
; _
} ->
1770 ( pPos static_initializer env
1771 , Binop
(Eq None
, lhs, pExpr simple_initializer_value env
)
1775 | _
-> missing_syntax "static declarator" node env
1777 pos, Static_var
(couldMap ~
f:pStaticDeclarator static_declarations env
)
1778 | ReturnStatement
{ return_expression
; return_keyword
; _
} ->
1779 let expr = mpOptional pExpr return_expression env
in
1781 | Syntax.GotoLabel
{ goto_label_name
; _
} ->
1782 let pos_label = pPos goto_label_name env
in
1783 let label_name = text goto_label_name
in
1784 pos, Ast.GotoLabel
(pos_label, label_name)
1785 | GotoStatement
{ goto_statement_label_name
; _
} ->
1786 pos, Goto
(pos_name goto_statement_label_name env
)
1787 | EchoStatement
{ echo_keyword
= kw
; echo_expressions
= exprs; _
}
1788 | UnsetStatement
{ unset_keyword
= kw
; unset_variables
= exprs; _
}
1792 ( (match syntax kw
with
1794 | SimpleTypeSpecifier _
1796 -> let name = pos_name kw env
in fst
name, Id
name
1797 | _
-> missing_syntax "id" kw env
1800 , couldMap ~
f:pExpr
exprs env
1803 | BreakStatement
{ break_level
=level
; _
} ->
1804 pos, Break
(pBreak_or_continue_level env level
)
1805 | ContinueStatement
{ continue_level
=level
; _
} ->
1806 pos, Continue
(pBreak_or_continue_level env level
)
1807 | GlobalStatement
{ global_variables
; _
} ->
1808 pos, Global_var
(couldMap ~
f:(pExpr ~location
:InGlobalVar
) global_variables env
)
1809 | MarkupSection _
-> pMarkup node env
1810 | _
when env
.max_depth
> 0 ->
1811 (* OCaml optimisers; Forgive them, for they know not what they do!
1813 * The max_depth is only there to stop the *optimised* version from an
1814 * unbounded recursion. Sad times.
1816 let outer_max_depth = env
.max_depth
in
1817 let () = env
.max_depth
<- outer_max_depth - 1 in
1819 match pDef node env
with
1820 | [def
] -> pos, Def_inline def
1821 | _
-> failwith
"This should be impossible; inline definition was list."
1823 let () = env
.max_depth
<- outer_max_depth in
1825 | _
-> missing_syntax ?fallback
:(Some
(Pos.none
,Noop
)) "statement" node env
in
1829 and pMarkup node env
=
1830 match syntax node
with
1831 | MarkupSection
{ markup_text
; markup_expression
; _
} ->
1833 match syntax markup_expression
with
1835 | ExpressionStatement
{
1836 expression_statement_expression
= e
1837 ; _
} -> Some
(pExpr
e env
)
1838 | _
-> failwith
"expression expected"
1840 pPos node env
, Markup
((pPos node env
, text markup_text
), expr)
1841 | _
-> failwith
"invalid node"
1843 and pBreak_or_continue_level env level
=
1844 mpOptional pExpr level env
1846 and pTConstraintTy
: hint parser
= fun node
->
1847 match syntax node
with
1848 | TypeConstraint
{ constraint_type
; _
} -> pHint constraint_type
1849 | _
-> missing_syntax "type constraint" node
1851 and pTConstraint
: (constraint_kind
* hint
) parser
= fun node env
->
1852 match syntax node
with
1853 | TypeConstraint
{ constraint_keyword
; constraint_type
} ->
1854 ( (match token_kind constraint_keyword
with
1855 | Some
TK.As
-> Constraint_as
1856 | Some
TK.Super
-> Constraint_super
1857 | Some
TK.Equal
-> Constraint_eq
1858 | _
-> missing_syntax "constraint operator" constraint_keyword env
1860 , pHint constraint_type env
1862 | _
-> missing_syntax "type constraint" node env
1864 and pTParaml
: tparam list parser
= fun node env
->
1865 let pTParam : tparam parser
= fun node env
->
1866 match syntax node
with
1867 | TypeParameter
{ type_variance
; type_name
; type_constraints
} ->
1868 ( (match token_kind type_variance
with
1869 | Some
TK.Plus
-> Covariant
1870 | Some
TK.Minus
-> Contravariant
1873 , pos_name type_name env
1874 , couldMap ~
f:pTConstraint type_constraints env
1876 | _
-> missing_syntax "type parameter" node env
1878 match syntax node
with
1880 | TypeParameters
{ type_parameters_parameters
; _
} ->
1881 couldMap ~
f:pTParam type_parameters_parameters env
1882 | _
-> missing_syntax "type parameter list" node env
1884 and pFunHdr
: fun_hdr parser
= fun node env
->
1885 match syntax node
with
1886 | FunctionDeclarationHeader
1887 { function_modifiers
1888 ; function_ampersand
1890 ; function_where_clause
1891 ; function_type_parameter_list
1892 ; function_parameter_list
1895 let modifiers = pModifiers function_modifiers env
in
1896 let fh_parameters = couldMap ~
f:pFunParam function_parameter_list env
in
1897 let fh_return_type = mpOptional pHint function_type env
in
1898 let fh_suspension_kind =
1899 mk_suspension_kind_ modifiers.has_async
modifiers.has_coroutine
in
1900 let fh_name = pos_name function_name env
in
1902 match syntax function_where_clause
with
1904 | WhereClause
{ where_clause_constraints
; _
} ->
1906 match syntax node
with
1907 | ListItem
{ list_item
; _
} -> f list_item
1909 { where_constraint_left_type
1910 ; where_constraint_operator
1911 ; where_constraint_right_type
1913 let l = pHint where_constraint_left_type env
in
1915 match syntax where_constraint_operator
with
1916 | Token token
when Token.kind token
= TK.Equal
->
1918 | Token token
when Token.kind token
= TK.As
-> Constraint_as
1919 | Token token
when Token.kind token
= TK.Super
-> Constraint_super
1920 | _
-> missing_syntax "constraint operator" where_constraint_operator env
1922 let r = pHint where_constraint_right_type env
in
1924 | _
-> missing_syntax "where constraint" node env
1926 List.map ~
f (syntax_node_to_list where_clause_constraints
)
1927 | _
-> missing_syntax "function header constraints" node env
1929 let fh_type_parameters = pTParaml function_type_parameter_list env
in
1930 let fh_param_modifiers =
1931 List.filter ~
f:(fun p -> Option.is_some
p.param_modifier
) fh_parameters
1933 let fh_ret_by_ref = is_ret_by_ref function_ampersand
in
1934 { fh_suspension_kind
1937 ; fh_type_parameters
1940 ; fh_param_modifiers
1943 | LambdaSignature
{ lambda_parameters
; lambda_type
; _
} ->
1944 { empty_fun_hdr with
1945 fh_parameters = couldMap ~
f:pFunParam lambda_parameters env
1946 ; fh_return_type = mpOptional pHint lambda_type env
1948 | Token _
-> empty_fun_hdr
1949 | _
-> missing_syntax "function header" node env
1951 and docblock_stack
= Stack.create
()
1953 and extract_docblock
= fun node
->
1954 let source_text = leading_text node
in
1955 let parse (str
: string) : string option =
1956 let length = String.length str
in
1957 let mk (start : int) (end_ : int) : string =
1958 String.sub
source_text start (end_ - start + 1)
1960 let rec go start state idx
: string option =
1961 if idx
= length (* finished? *)
1964 let next = idx
+ 1 in
1965 match state
, str
.[idx
] with
1966 | `LineCmt
, '
\n'
-> go next `Free
next
1967 | `EndEmbedded
, '
/'
-> go next `Free
next
1968 | `EndDoc
, '
/'
-> begin match go next `Free
next with
1969 | Some doc
-> Some doc
1970 | None
-> Some
(mk start idx
)
1972 (* PHP has line comments delimited by a # *)
1973 | `Free
, '#'
-> go next `LineCmt
next
1974 (* All other comment delimiters start with a / *)
1975 | `Free
, '
/'
-> go idx `SawSlash
next
1976 (* After a / in trivia, we must see either another / or a * *)
1977 | `SawSlash
, '
/'
-> go next `LineCmt
next
1978 | `SawSlash
, '
*'
-> go start `MaybeDoc
next
1979 | `MaybeDoc
, '
*'
-> go start `MaybeDoc2
next
1980 | `MaybeDoc
, _
-> go start `EmbeddedCmt
next
1981 | `MaybeDoc2
, '
/'
-> go next `Free
next
1982 (* Doc comments have a space after the second star *)
1983 | `MaybeDoc2
, (' '
| '
\t'
| '
\n'
) -> go start `DocComment idx
1984 | `MaybeDoc2
, _
-> go start `EmbeddedCmt
next
1985 | `DocComment
, '
*'
-> go start `EndDoc
next
1986 | `DocComment
, _
-> go start `DocComment
next
1987 | `EndDoc
, _
-> go start `DocComment
next
1988 (* A * without a / does not end an embedded comment *)
1989 | `EmbeddedCmt
, '
*'
-> go start `EndEmbedded
next
1990 | `EndEmbedded
, '
*'
-> go start `EndEmbedded
next
1991 | `EndEmbedded
, _
-> go start `EmbeddedCmt
next
1992 (* Whitespace skips everywhere else *)
1993 | _
, (' '
| '
\t'
| '
\n'
) -> go start state
next
1994 (* When scanning comments, anything else is accepted *)
1995 | `LineCmt
, _
-> go start state
next
1996 | `EmbeddedCmt
, _
-> go start state
next
1997 (* Anything else; bail *)
2002 in (* Now that we have a parser *)
2003 parse (leading_text node
)
2005 and extract_and_push_docblock node
=
2006 let docblock = extract_docblock node
in
2007 Stack.push
docblock docblock_stack
2009 and handle_loop_body
pos stmts
tail env
=
2010 let rec conv acc stmts
=
2012 | [] -> List.concat
@@ List.rev acc
2013 | { syntax
= UsingStatementFunctionScoped
2014 { using_function_await_keyword
= await_kw
2015 ; using_function_expression
= expression
2018 let body = conv [] rest
in
2020 us_is_block_scoped
= false;
2021 us_has_await
= not
(is_missing await_kw
);
2022 us_expr
= pExprL expression env
;
2023 us_block
= [pos, Block
body]; } in
2024 List.concat
@@ List.rev
([pos, using] :: acc
)
2026 let h = pStmtUnsafe
h env
in
2027 conv (h :: acc
) rest
2029 let blk = conv [] (as_list stmts
) in
2030 begin match List.filter ~
f:(fun (_
, x
) -> x
<> Noop
) blk @ tail with
2031 | [] -> pos, Block
[Pos.none
, Noop
]
2032 | blk -> pos, Block
blk
2035 and pop_docblock
() =
2037 let _ = Stack.pop docblock_stack
in ()
2041 and top_docblock
() =
2043 Stack.top docblock_stack
2045 | Stack.Empty
-> None
2047 and pClassElt
: class_elt list parser
= fun node env
->
2048 let doc_comment_opt = extract_docblock node
in
2049 let pClassElt_ = function
2051 { const_abstract
; const_type_specifier
; const_declarators
; _ } ->
2052 let ty = mpOptional pHint const_type_specifier env
in
2054 couldMap const_declarators env ~
f:begin function
2055 | { syntax
= ConstantDeclarator
2056 { constant_declarator_name
; constant_declarator_initializer
}
2058 ( pos_name constant_declarator_name env
2059 (* TODO: Parse error when const is abstract and has inits *)
2060 , if is_missing const_abstract
2061 then mpOptional pSimpleInitializer constant_declarator_initializer env
2064 | node
-> missing_syntax "constant declarator" node env
2067 let rec aux absts concrs
= function
2068 | (id, None
) :: xs
-> aux (AbsConst
(ty, id) :: absts
) concrs xs
2069 | (id, Some x
) :: xs
-> aux absts
((id, x
) :: concrs
) xs
2070 | [] when concrs
= [] -> List.rev absts
2071 | [] -> Const
(ty, List.rev concrs
) :: List.rev absts
2074 | TypeConstDeclaration
2075 { type_const_abstract
2077 ; type_const_type_parameters
2078 ; type_const_type_constraint
2079 ; type_const_type_specifier
2082 { tconst_abstract
= not
(is_missing type_const_abstract
)
2083 ; tconst_name
= pos_name type_const_name env
2084 ; tconst_tparams
= pTParaml type_const_type_parameters env
2085 ; tconst_constraint
= mpOptional pTConstraintTy type_const_type_constraint env
2086 ; tconst_type
= mpOptional pHint type_const_type_specifier env
2087 ; tconst_span
= pPos node env
2090 | PropertyDeclaration
2091 { property_attribute_spec
2092 ; property_modifiers
2094 ; property_declarators
2097 (* TODO: doc comments do not have to be at the beginning, they can go in
2098 * the middle of the declaration, to be associated with individual
2099 * properties, right now we don't handle this *)
2100 let doc_comment_opt = extract_docblock node
in
2102 { cv_kinds
= pKinds property_modifiers env
2103 ; cv_hint
= mpOptional pHint property_type env
2104 ; cv_is_promoted_variadic
= false
2105 ; cv_names
= couldMap property_declarators env ~
f:begin fun node env
->
2106 match syntax node
with
2107 | PropertyDeclarator
{ property_name
; property_initializer
} ->
2108 ( let _, n as name = pos_name property_name env
in
2111 then drop_pstr 1 name
2114 , mpOptional pSimpleInitializer property_initializer env
2117 | _ -> missing_syntax "property declarator" node env
2119 ; cv_doc_comment
= if env
.quick_mode
then None
else doc_comment_opt
2120 ; cv_user_attributes
= List.concat
@@
2121 couldMap ~
f:pUserAttribute property_attribute_spec env
2124 | MethodishDeclaration
2125 { methodish_attribute
2126 ; methodish_function_decl_header
= {
2127 syntax
= FunctionDeclarationHeader
h; _
2129 ; methodish_function_body
2131 let classvar_init : fun_param
-> stmt * class_elt
= fun param
->
2132 let p, _ as cvname
= drop_pstr 1 param
.param_id
in (* Drop the '$' *)
2134 match param
.param_expr
with
2135 | Some
(pos_end, _) -> Pos.btw
p pos_end
2138 ( (p, Expr
(p, Binop
(Eq None
,
2139 (p, Obj_get
((p, Lvar
(p, "$this")), (p, Id cvname
), OG_nullthrows
)),
2140 (p, Lvar param
.param_id
))
2143 { cv_kinds
= Option.to_list param
.param_modifier
2144 ; cv_hint
= param
.param_hint
2145 ; cv_is_promoted_variadic
= param
.param_is_variadic
2146 ; cv_names
= [span, cvname
, None
]
2147 ; cv_doc_comment
= None
2148 ; cv_user_attributes
= param
.param_user_attributes
2152 let hdr = pFunHdr header env
in
2153 let member_init, member_def
=
2155 List.filter_map
hdr.fh_parameters ~
f:(fun p ->
2156 Option.map ~
f: (fun _ -> classvar_init p) p.param_modifier
2159 let pBody = fun node env
->
2160 let body = pFunctionBody node env
in
2163 then List.rev
member_init
2168 let body, body_has_yield
= mpYielding pBody methodish_function_body env
in
2169 let kind = pKinds h.function_modifiers env
in
2170 member_def
@ [Method
2172 ; m_tparams
= hdr.fh_type_parameters
2173 ; m_constrs
= hdr.fh_constrs
2174 ; m_name
= hdr.fh_name
2175 ; m_params
= hdr.fh_parameters
2177 ; m_user_attributes
= pUserAttributes env methodish_attribute
2178 ; m_ret
= hdr.fh_return_type
2179 ; m_ret_by_ref
= hdr.fh_ret_by_ref
2180 ; m_span
= pFunction node env
2181 ; m_fun_kind
= mk_fun_kind hdr.fh_suspension_kind body_has_yield
2182 ; m_doc_comment
= doc_comment_opt
2184 | TraitUseConflictResolution
2185 { trait_use_conflict_resolution_names
2186 ; trait_use_conflict_resolution_clauses
2189 let pTraitUseConflictResolutionItem node env
=
2190 match syntax node
with
2191 | TraitUsePrecedenceItem
2192 { trait_use_precedence_item_name
= name
2193 ; trait_use_precedence_item_removed_names
= removed_names
2196 let qualifier, name =
2197 match syntax
name with
2198 | ScopeResolutionExpression
2199 { scope_resolution_qualifier
; scope_resolution_name
; _ } ->
2200 pos_name scope_resolution_qualifier env
,
2201 pos_name scope_resolution_name env
2202 | _ -> missing_syntax "trait use precedence item" node env
2205 couldMap ~
f:(fun n _e
-> pos_name n env
) removed_names env
2207 ClassUsePrecedence
(qualifier, name, removed_names)
2209 { trait_use_alias_item_aliasing_name
= aliasing_name
2210 ; trait_use_alias_item_modifiers
= modifiers
2211 ; trait_use_alias_item_aliased_name
= aliased_name
2214 let qualifier, name =
2215 match syntax aliasing_name
with
2216 | ScopeResolutionExpression
2217 { scope_resolution_qualifier
; scope_resolution_name
; _ } ->
2218 Some
(pos_name scope_resolution_qualifier env
),
2219 pos_name scope_resolution_name env
2220 | _ -> None
, pos_name aliasing_name env
2222 let modifiers = pKinds modifiers env
in
2223 let is_visibility = function
2224 | Public
| Private
| Protected
-> true
2227 if List.is_empty
modifiers || List.exists
modifiers ~
f:is_visibility
2229 else Public
:: modifiers in
2231 Option.some_if
(not
(is_missing
aliased_name)) (pos_name aliased_name env
)
2233 ClassUseAlias
(qualifier, name, aliased_name, modifiers)
2234 | _ -> missing_syntax "trait use conflict resolution item" node env
2236 (couldMap ~
f:(fun n e ->
2237 ClassUse
(pHint n e)) trait_use_conflict_resolution_names env
)
2238 @ (couldMap ~
f:pTraitUseConflictResolutionItem
2239 trait_use_conflict_resolution_clauses env
)
2240 | TraitUse
{ trait_use_names
; _ } ->
2241 couldMap ~
f:(fun n e -> ClassUse
(pHint n e)) trait_use_names env
2242 | RequireClause
{ require_kind
; require_name
; _ } ->
2244 ( (match token_kind require_kind
with
2245 | Some
TK.Implements
-> MustImplement
2246 | Some
TK.Extends
-> MustExtend
2247 | _ -> missing_syntax "trait require kind" require_kind env
2249 , pHint require_name env
2252 | XHPClassAttributeDeclaration
{ xhp_attribute_attributes
; _ } ->
2253 let pXHPAttr node env
=
2254 match syntax node
with
2256 { xhp_attribute_decl_type
= ty
2257 ; xhp_attribute_decl_name
= name
2258 ; xhp_attribute_decl_initializer
= init
2259 ; xhp_attribute_decl_required
= req
2261 let (p, name) = pos_name name env
in
2262 let pos = if is_missing init
then p else Pos.btw
p (pPos init env
) in
2264 ( mpOptional pHint ty env
2265 , (pos, (p, ":" ^
name), mpOptional pSimpleInitializer init env
)
2266 , not
(is_missing req
)
2267 , match syntax
ty with
2268 | XHPEnumType
{ xhp_enum_optional
; xhp_enum_values
; _ } ->
2269 let p = pPos ty env
in
2270 let opt = not
(is_missing xhp_enum_optional
) in
2271 let vals = couldMap ~
f:pExpr xhp_enum_values env
in
2275 | XHPSimpleClassAttribute
{ xhp_simple_class_attribute_type
= attr
} ->
2276 XhpAttrUse
(pPos attr env
, Happly
(pos_name attr env
, []))
2278 XhpAttrUse
(pPos node env
, Happly
(pos_name node env
, []))
2279 | _ -> missing_syntax "XHP attribute" node env
2281 couldMap ~
f:pXHPAttr xhp_attribute_attributes env
2282 | XHPChildrenDeclaration
{ xhp_children_expression
; _; } ->
2283 let p = pPos node env
in
2284 [ XhpChild
(p, pXhpChild xhp_children_expression env
) ]
2285 | XHPCategoryDeclaration
{ xhp_category_categories
= cats
; _ } ->
2286 let p = pPos node env
in
2287 let pNameSansPercent node _env
= drop_pstr 1 (pos_name node env
) in
2288 [ XhpCategory
(p, couldMap ~
f:pNameSansPercent cats env
) ]
2289 | _ -> missing_syntax "class element" node env
2291 try pClassElt_ (syntax node
) with
2292 | API_Missing_syntax
(expecting
, env
, node
) when env
.fail_open
->
2293 let () = log_missing ~caught
:true ~env ~expecting node
in
2295 and pXhpChild
: xhp_child parser
= fun node env
->
2296 match syntax node
with
2297 | Token t
-> ChildName
(pos_name node env
)
2298 | PostfixUnaryExpression
{ postfix_unary_operand
; postfix_unary_operator
;} ->
2299 let operand = pXhpChild postfix_unary_operand env
in
2302 match token_kind postfix_unary_operator
with
2303 | Some
TK.Question
-> ChildQuestion
2304 | Some
TK.Plus
-> ChildPlus
2305 | Some
TK.Star
-> ChildStar
2306 | _ -> missing_syntax "xhp children operator" node env
2308 ChildUnary
(operand, operator)
2310 { binary_left_operand
; binary_right_operand
; _ } ->
2311 let left = pXhpChild binary_left_operand env
in
2312 let right = pXhpChild binary_right_operand env
in
2313 ChildBinary
(left, right)
2314 | XHPChildrenParenthesizedList
{xhp_children_list_xhp_children
; _} ->
2315 let children = as_list xhp_children_list_xhp_children
in
2316 let children = List.map ~
f:(fun x
-> pXhpChild x env
) children in
2318 | _ -> missing_syntax "xhp children" node env
2321 (*****************************************************************************(
2322 * Parsing definitions (AST's `def`)
2323 )*****************************************************************************)
2324 and pNamespaceUseClause ~prefix env
kind node
=
2325 match syntax node
with
2326 | NamespaceUseClause
2327 { namespace_use_name
= name
2328 ; namespace_use_alias
= alias
2329 ; namespace_use_clause_kind
= clause_kind
2332 match prefix
, pos_name name env
with
2333 | None
, (p, n) -> (p, n)
2334 | Some prefix
, (p, n) -> p, (snd
@@ pos_name prefix env
) ^
n
2336 let x = Str.search_forward
(Str.regexp
"[^\\\\]*$") n 0 in
2337 let key = drop_pstr x name in
2338 let kind = if is_missing clause_kind
then kind else clause_kind
in
2339 let alias = if is_missing
alias then key else pos_name alias env
in
2340 begin match String.lowercase_ascii
(snd
alias) with
2341 | "null" | "true" | "false" -> env
.saw_std_constant_redefinition
<- true
2345 match syntax
kind with
2346 | Token token
when Token.kind token
= TK.Namespace
-> NSNamespace
2347 | Token token
when Token.kind token
= TK.Type
-> NSClass
2348 | Token token
when Token.kind token
= TK.Function
-> NSFun
2349 | Token token
when Token.kind token
= TK.Const
-> NSConst
2350 | Missing
-> NSClassAndNamespace
2351 | _ -> missing_syntax "namespace use kind" kind env
2354 , (p, if n.[0] = '
\\'
then n else "\\" ^
n)
2357 | _ -> missing_syntax "namespace use clause" node env
2359 and pDef
: def list parser
= fun node env
->
2360 let doc_comment_opt = extract_docblock node
in
2361 match syntax node
with
2362 | FunctionDeclaration
2363 { function_attribute_spec
; function_declaration_header
; function_body
} ->
2364 let env = non_tls env in
2365 let hdr = pFunHdr function_declaration_header
env in
2367 if is_semicolon function_body
then [], false else
2368 mpYielding pFunctionBody function_body
env
2371 { (fun_template yield node
hdr.fh_suspension_kind env) with
2372 f_tparams
= hdr.fh_type_parameters
2373 ; f_ret
= hdr.fh_return_type
2374 ; f_constrs
= hdr.fh_constrs
2375 ; f_name
= hdr.fh_name
2376 ; f_params = hdr.fh_parameters
2377 ; f_ret_by_ref = hdr.fh_ret_by_ref
2380 let containsUNSAFE node
=
2381 let tokens = all_tokens node
in
2382 let has_unsafe t
= Token.has_trivia_kind t
TriviaKind.Unsafe
in
2383 List.exists ~
f:has_unsafe tokens
2386 | [p, Noop
] when containsUNSAFE function_body
-> [p, Unsafe
]
2389 ; f_user_attributes
= pUserAttributes
env function_attribute_spec
2390 ; f_doc_comment
= doc_comment_opt
2392 | ClassishDeclaration
2393 { classish_attribute
= attr
2394 ; classish_modifiers
= mods
2395 ; classish_keyword
= kw
2396 ; classish_name
= name
2397 ; classish_type_parameters
= tparaml
2398 ; classish_extends_list
= exts
2399 ; classish_implements_list
= impls
2401 { syntax
= ClassishBody
{ classish_body_elements
= elts
; _ }; _ }
2403 let env = non_tls env in
2404 let c_mode = mode_annotation env.fi_mode
in
2405 let c_user_attributes = pUserAttributes
env attr
in
2406 let c_final = List.mem
(pKinds mods
env) Final
in
2408 match token_kind name with
2409 | Some
(TK.XHPElementName
| TK.XHPClassName
) -> true
2412 let c_name = pos_name name env in
2413 let c_tparams = pTParaml tparaml
env in
2414 let c_extends = couldMap ~
f:pHint exts
env in
2415 let c_implements = couldMap ~
f:pHint impls
env in
2417 let rec aux acc ns
=
2420 | { syntax
= PropertyDeclaration
{ property_modifiers
; _ }; _ } :: _
2421 when not
env.codegen
&&
2422 List.exists ~
f:Syntax.is_var
(as_list property_modifiers
) ->
2423 (* Break off remaining class body parse; legacy compliance *)
2426 let elt = pClassElt
n env in
2429 List.concat
@@ List.rev
(aux [] (as_list elts
))
2431 let c_namespace = Namespace_env.empty
env.parser_options in
2432 let c_enum = None
in
2433 let c_span = pPos node
env in
2435 let is_abs = List.mem
(pKinds mods
env) Abstract
in
2436 match token_kind kw
with
2437 | Some
TK.Class
when is_abs -> Cabstract
2438 | Some
TK.Class
-> Cnormal
2439 | Some
TK.Interface
-> Cinterface
2440 | Some
TK.Trait
-> Ctrait
2441 | Some
TK.Enum
-> Cenum
2442 | _ -> missing_syntax "class kind" kw
env
2444 let c_doc_comment = doc_comment_opt in
2461 | ConstDeclaration
{ const_keyword
= kw
; _ }
2462 when env.fi_mode
= FileInfo.Mdecl
2464 Option.value_map ~default
:"" ~
f:Token.text (Syntax.get_token kw
)
2465 -> [] (* Legacy parity; case-sensitive global const declarations *)
2467 { const_type_specifier
= ty
2468 ; const_declarators
= decls
2470 let declarations = List.map ~
f:syntax
(as_list decls
) in
2472 | ConstantDeclarator
2473 { constant_declarator_name
= name
2474 ; constant_declarator_initializer
= init
2477 { cst_mode
= mode_annotation env.fi_mode
2478 ; cst_kind
= Cst_const
2479 ; cst_name
= pos_name name env
2480 ; cst_type
= mpOptional pHint ty env
2481 ; cst_value
= pSimpleInitializer init
env
2482 ; cst_namespace
= Namespace_env.empty
env.parser_options
2483 ; cst_span
= pPos node
env
2485 | _ -> missing_syntax "constant declaration" decls
env
2487 List.map ~
f declarations
2489 { alias_attribute_spec
= attr
2490 ; alias_keyword
= kw
2492 ; alias_generic_parameter
= tparams
2493 ; alias_constraint
= constr
2497 { t_id
= pos_name name env
2498 ; t_tparams
= pTParaml tparams
env
2499 ; t_constraint
= Option.map ~
f:snd
@@
2500 mpOptional pTConstraint constr
env
2501 ; t_user_attributes
= List.concat
@@
2502 List.map ~
f:(fun x -> pUserAttribute
x env) (as_list attr
)
2503 ; t_namespace
= Namespace_env.empty
env.parser_options
2504 ; t_mode
= mode_annotation env.fi_mode
2506 match token_kind kw
with
2507 | Some
TK.Newtype
-> NewType
(pHint hint
env)
2508 | Some
TK.Type
-> Alias
(pHint hint
env)
2509 | _ -> missing_syntax "kind" kw
env
2512 { enum_attribute_spec
= attrs
2515 ; enum_type
= constr
2516 ; enum_enumerators
= enums
2518 let pEnumerator node
=
2519 match syntax node
with
2520 | Enumerator
{ enumerator_name
= name; enumerator_value
= value; _ } ->
2521 fun env -> Const
(None
, [pos_name name env, pExpr
value env])
2522 | _ -> missing_syntax "enumerator" node
2525 { c_mode = mode_annotation env.fi_mode
2526 ; c_user_attributes = pUserAttributes
env attrs
2530 ; c_name = pos_name name env
2534 ; c_body = couldMap enums
env ~
f:pEnumerator
2535 ; c_namespace = Namespace_env.empty
env.parser_options
2536 ; c_span = pPos node
env
2538 { e_base
= pHint base
env
2539 ; e_constraint
= mpOptional pTConstraintTy constr
env
2541 ; c_doc_comment = doc_comment_opt
2543 | InclusionDirective
2544 { inclusion_expression
2545 ; inclusion_semicolon
= _
2546 } when env.fi_mode
<> FileInfo.Mdecl
&& env.fi_mode
<> FileInfo.Mphp
2548 let expr = pExpr inclusion_expression
env in
2549 [ Stmt
(pPos node
env, Expr
expr) ]
2550 | NamespaceDeclaration
2551 { namespace_name
= name
2553 { syntax
= NamespaceBody
{ namespace_declarations
= decls
; _ }; _ }
2555 let env = non_tls env in
2558 , List.concat_map ~
f:(fun x -> pDef
x env) (as_list decls
)
2560 | NamespaceDeclaration
{ namespace_name
= name; _ } ->
2561 [ Namespace
(pos_name name env, []) ]
2562 | NamespaceGroupUseDeclaration
2563 { namespace_group_use_kind
= kind
2564 ; namespace_group_use_prefix
= prefix
2565 ; namespace_group_use_clauses
= clauses
2567 let f = pNamespaceUseClause
env kind ~prefix
:(Some prefix
) in
2568 [ NamespaceUse
(List.map ~
f (as_list clauses
)) ]
2569 | NamespaceUseDeclaration
2570 { namespace_use_kind
= kind
2571 ; namespace_use_clauses
= clauses
2573 let f = pNamespaceUseClause
env kind ~prefix
:None
in
2574 [ NamespaceUse
(List.map ~
f (as_list clauses
)) ]
2575 | _ when env.fi_mode
= FileInfo.Mdecl
|| env.fi_mode
= FileInfo.Mphp
2576 && not
env.codegen
-> []
2577 | _ -> [ Stmt
(pStmt node
env) ]
2578 let pProgram : program parser
= fun node
env ->
2579 let rec post_process program
=
2580 let span (p : 'a
-> bool) =
2581 let rec go yes
= function
2582 | (x::xs
) when p x -> go (x::yes
) xs
2583 | xs
-> (List.rev yes
, xs
)
2586 let not_namespace = function
2587 | Namespace
_ -> false
2592 | (Namespace
(n, [])::el
) ->
2593 let body, remainder
= span not_namespace el
in
2594 Namespace
(n, body) :: post_process remainder
2595 | (Namespace
(n, il
)::el
) ->
2596 Namespace
(n, post_process il
) :: post_process el
2597 | (Stmt
(_, Noop
) :: el
) -> post_process el
2598 | ((Stmt
(_, Expr
(pos, (Call
2599 ( (_, (Id
(_, "define")))
2601 , [ (_, (String
name))
2606 )))) :: el
) -> Constant
2607 { cst_mode
= mode_annotation env.fi_mode
2608 ; cst_kind
= Cst_define
2612 ; cst_namespace
= Namespace_env.empty
env.parser_options
2614 } :: post_process el
2615 | (e::el
) -> e :: post_process el
2618 (* The list of top-level things in a file is somewhat special. *)
2619 let rec aux env acc
= function
2621 (* EOF happens only as the last token in the list. *)
2622 | [{ syntax
= EndOfFile
_; _ }]
2623 -> List.concat
(List.rev acc
)
2624 (* HaltCompiler stops processing the list *)
2625 | { syntax
= ExpressionStatement
2626 { expression_statement_expression
=
2627 { syntax
= HaltCompilerExpression
_ ; _ } ; _ } ; _ } as cur_node
:: nodel
2629 (* If we saw COMPILER_HALT_OFFSET, calculate the position of HALT_COMPILER *)
2630 if env.saw_compiler_halt_offset
<> None
then
2632 let local_ignore_pos = env.ignore_pos
in
2633 let () = env.ignore_pos
<- false in
2634 let pos = pPos cur_node
env in
2635 (* __COMPILER_HALT_OFFSET__ takes value equal to halt_compiler's end position *)
2636 let s = File_pos.offset
@@ Pos.pos_end pos in
2637 let () = env.saw_compiler_halt_offset
<- Some
s in
2638 env.ignore_pos
<- local_ignore_pos
2641 (* There's an incompatibility between the Full-Fidelity (FF) and the AST view
2642 * of the world; `define` is an *expression* in FF, but a *definition* in AST.
2643 * Luckily, `define` only happens at the level of definitions.
2645 | { syntax
= ExpressionStatement
2646 { expression_statement_expression
=
2647 { syntax
= DefineExpression
2648 { define_keyword
; define_argument_list
= args; _ }
2651 ; _ } as cur_node
:: nodel
when not
env.quick_mode
->
2653 match List.map ~
f:(fun x -> pExpr
x env) (as_list args) with
2654 | [ _, String
name; e ] -> Constant
2655 { cst_mode
= mode_annotation env.fi_mode
2656 ; cst_kind
= Cst_define
2660 ; cst_namespace
= Namespace_env.empty
env.parser_options
2661 ; cst_span
= pPos cur_node
env
2664 let name = pos_name define_keyword
env in
2665 Stmt
(pPos cur_node
env,
2666 Expr
(fst
name, Call
((fst
name, Id
name), [], args, [])))
2668 aux env ([def] :: acc
) nodel
2671 match pDef node
env with
2672 | exception API_Missing_syntax
(expecting
, env, node
)
2673 when env.fail_open
->
2674 let () = log_missing ~caught
:true ~
env ~expecting node
in
2680 let nodes = as_list node
in
2681 let nodes = aux env [] nodes in
2684 let pScript node
env =
2685 match syntax node
with
2686 | Script
{ script_declarations
; _ } -> pProgram script_declarations
env
2687 | _ -> missing_syntax "script" node
env
2689 (* The full fidelity parser considers all comments "simply" trivia. Some
2690 * comments have meaning, though. This meaning can either be relevant for the
2691 * type checker (like UNSAFE, HH_FIXME, etc.), but also for other uses, like
2692 * Codex, where comments are used for documentation generation.
2694 * Inlining the scrape for comments in the lowering code would be prohibitively
2695 * complicated, but a separate pass is fine.
2698 type fixmes
= Pos.t
IMap.t
IMap.t
2699 type scoured_comment
= Pos.t
* comment
2700 type scoured_comments
= scoured_comment list
2701 type accumulator
= scoured_comments
* fixmes
2704 (path
: Relative_path.t
)
2705 (source_text : SourceText.t
)
2709 let pos_of_offset = SourceText.relative_pos path
source_text in
2710 let go (node
: node
) (cmts
, fm
as acc : accumulator
) (t
: Trivia.t
)
2712 match Trivia.kind t
with
2713 | TriviaKind.WhiteSpace
2714 | TriviaKind.EndOfLine
2716 | TriviaKind.UnsafeExpression
2717 | TriviaKind.FallThrough
2718 | TriviaKind.ExtraTokenError
2719 | TriviaKind.AfterHaltCompiler
2721 | TriviaKind.DelimitedComment
->
2722 (* For legacy compliance, block comments should have the position of
2725 let start = Trivia.start_offset t
+ 2 (* for the '/*' *) in
2726 let end_ = Trivia.end_offset t
in
2727 let len = end_ - start - 1 in
2728 let p = pos_of_offset (end_ - 1) end_ in
2729 let t = String.sub
(Trivia.text t) 2 len in
2730 (p, CmtBlock
t) :: cmts
, fm
2731 | TriviaKind.SingleLineComment
->
2732 let text = SourceText.text (Trivia.source_text t) in
2733 let start = Trivia.start_offset
t in
2734 let start = start + if text.[start] = '#'
then 1 else 2 in
2735 let end_ = Trivia.end_offset
t in
2736 let len = end_ - start + 1 in
2737 let p = pos_of_offset start end_ in
2738 let t = String.sub
text start len in
2739 (p, CmtLine
(t ^
"\n")) :: cmts
, fm
2741 | TriviaKind.IgnoreError
2743 let pos = pPos node
env in
2744 let line = Pos.line pos in
2745 let ignores = try IMap.find
line fm
with Not_found
-> IMap.empty
in
2746 let reg = regexp
"HH_\\(FIXME\\|IGNORE_ERROR\\)[ \\t\\n]*\\[?\\([0-9]+\\)\\]?" in
2747 let txt = Trivia.text t in
2748 (try ignore
(search_forward
reg txt 0) with
2750 let msg = Printf.sprintf
2751 "Inconsistent trivia classification: Received %s, but failed to \
2752 match regexp for FIXME or IGNORE_ERROR. Either the lexer has a \
2753 bug in its trivia classification, or the lowerer is more \
2754 restrictive than the lexer. Source: %s"
2755 (TriviaKind.to_string
(Trivia.kind t))
2756 (Pos.string (Pos.to_absolute
pos))
2760 let p = pos_of_offset (Trivia.start_offset
t) (Pos.start_cnum
pos) in
2761 let code = int_of_string
(matched_group
2 txt) in
2762 let ignores = IMap.add
code p ignores in
2763 cmts
, IMap.add
line ignores fm
2765 let rec aux (cmts
, fm
as acc : accumulator
) (node
: node
) : accumulator
=
2766 match syntax node
with
2769 let trivia = Token.leading
t in
2770 let acc = List.fold_left ~
f ~init
:acc trivia in
2771 let trivia = Token.trailing
t in
2772 List.fold_left ~
f ~init
:acc trivia
2773 | _ -> List.fold_left ~
f:aux ~init
:acc (children node
)
2775 aux ([], IMap.empty
) tree
2777 (*****************************************************************************(
2779 )*****************************************************************************)
2781 let elaborate_toplevel_and_std_constants ast
env source_text =
2782 match env.elaborate_namespaces
, env.saw_std_constant_redefinition
with
2784 let elaborate_std_constants nsenv
def =
2785 let visitor = object(self
)
2786 inherit [_] endo
as super
2787 method! on_expr
env expr =
2789 | p, True
| p, False
| p, Null
when p <> Pos.none
->
2790 let s = File_pos.offset
@@ Pos.pos_start p in
2791 let e = File_pos.offset
@@ Pos.pos_end p in
2792 let text = SourceText.sub
source_text s (e - s) in
2793 let was_renamed, _ =
2794 NS.elaborate_id_impl
2799 if was_renamed then p, Ast.Id
(p, text)
2801 | _ -> super#on_expr
env expr
2803 visitor#on_def nsenv
def in
2804 let parser_options = env.parser_options in
2805 NS.elaborate_map_toplevel_defs
parser_options ast
elaborate_std_constants
2807 NS.elaborate_toplevel_defs
env.parser_options ast
2810 let elaborate_halt_compiler ast
env source_text =
2811 match env.saw_compiler_halt_offset
with
2813 let elaborate_halt_compiler_const defs
=
2814 let visitor = object(self
)
2815 inherit [_] endo
as super
2816 method! on_expr
env expr =
2818 | p, Id
(_, "__COMPILER_HALT_OFFSET__") ->
2819 let start_offset = File_pos.offset
@@ Pos.pos_start p in
2820 (* Construct a new position and id *)
2821 let id = string_of_int
x in
2822 let end_offset = start_offset + (String.length id) in
2823 let pos_file = Pos.filename
p in
2824 let pos = SourceText.relative_pos
pos_file source_text start_offset end_offset in
2825 pos, Ast.Int
(pos, id)
2826 | _ -> super#on_expr
env expr
2828 visitor#on_program
() defs
in
2829 elaborate_halt_compiler_const ast
2833 let lower env ~
source_text ~script
: result =
2834 let ast = runP pScript script
env in
2835 let ast = elaborate_toplevel_and_std_constants ast env source_text in
2836 let ast = elaborate_halt_compiler ast env source_text in
2837 let comments, fixmes
= scour_comments env.file source_text script
env in
2838 let comments = if env.include_line_comments
then comments else
2839 List.filter ~
f:(fun (_,c
) -> not
(Prim_defs.is_line_comment c
)) comments
2841 let () = if env.keep_errors
then Fixmes.HH_FIXMES.add
env.file fixmes
in
2842 { fi_mode
= env.fi_mode
2843 ; is_hh_file
= env.is_hh_file
2845 ; content = SourceText.text source_text
2849 end (* FromPositionedSyntax *)
2851 (* TODO: Make these not default to positioned_syntax *)
2852 module PosSyntax
= Full_fidelity_positioned_syntax
2854 module CoroutineSC
= Coroutine_smart_constructor.WithSyntax
(PosSyntax
)
2856 module SyntaxTree_
= Full_fidelity_syntax_tree
2857 .WithSyntax
(PosSyntax
)
2858 module SyntaxTree
= SyntaxTree_.WithSmartConstructors
(CoroutineSC
)
2860 module ParserErrors_
= Full_fidelity_parser_errors
2861 .WithSyntax
(PosSyntax
)
2862 module ParserErrors
= ParserErrors_.WithSmartConstructors
(CoroutineSC
)
2864 module SourceText
= Full_fidelity_source_text
2865 module DeclModeSC
= DeclModeSmartConstructors.WithSyntax
(PosSyntax
)
2866 module DeclModeParser_
= Full_fidelity_parser.WithSyntax
(PosSyntax
)
2867 module DeclModeParser
= DeclModeParser_.WithSmartConstructors
(DeclModeSC
)
2868 module FromPositionedSyntax
= WithPositionedSyntax
(PosSyntax
)
2869 module FromEditablePositionedSyntax
=
2870 WithPositionedSyntax
(Full_fidelity_editable_positioned_syntax
)
2872 let from_text (env : env) (source_text : SourceText.t) : result =
2873 let lang, mode
= Full_fidelity_parser.get_language_and_mode
source_text in
2876 Full_fidelity_parser_env.make
2877 ~hhvm_compat_mode
:env.codegen
2878 ~php5_compat_mode
:env.php5_compat_mode
2883 if env.quick_mode
then
2884 let parser = DeclModeParser.make
env'
source_text in
2885 let (parser, root
) = DeclModeParser.parse_script
parser in
2886 let errors = DeclModeParser.errors parser in
2887 SyntaxTree.create
source_text root
errors lang mode
false
2889 SyntaxTree.make ~
env:env'
source_text
2891 let lower_coroutines = env.lower_coroutines && SyntaxTree.sc_state
tree in
2892 let () = if env.codegen
&& not
lower_coroutines then
2893 let hhvm_compat_mode = if env.systemlib_compat_mode
2894 then ParserErrors.SystemLibCompat
2895 else ParserErrors.HHVMCompat
in
2896 let error_env = ParserErrors.make_env tree
2898 ~enable_hh_syntax
:env.enable_hh_syntax
2899 ~disallow_elvis_space
:env.disallow_elvis_space
2901 let errors = ParserErrors.parse_errors
error_env in
2902 (* Prioritize runtime errors *)
2903 let runtime_errors =
2904 List.filter
errors ~
f:SyntaxError.(fun e -> error_type
e = RuntimeError
) in
2905 match errors, runtime_errors with
2909 -> raise
@@ SyntaxError.ParserFatal
e
2911 let () = if env.keep_errors
then
2912 match List.last
(SyntaxTree.all_errors
tree) with
2915 let so = SyntaxError.start_offset e in
2916 let eo = SyntaxError.end_offset e in
2917 let p = SourceText.relative_pos
env.file source_text so eo in
2918 Errors.parsing_error
(p, SyntaxError.message
e)
2920 let mode = Option.value ~default
:FileInfo.Mpartial
mode in
2921 let mode = if lang = FileInfo.PhpFile
then FileInfo.Mphp
else mode in
2922 let mode = if mode = FileInfo.Mdecl
&& env.codegen
then FileInfo.Mphp
else mode in
2923 let env = { env with fi_mode
= mode } in
2924 let env = { env with is_hh_file
= lang == FileInfo.HhFile
} in
2925 let popt = env.parser_options in
2926 (* If we are generating code and this is an hh file or hh syntax is enabled,
2927 * then we want to inject auto import types into HH namespace during namespace
2930 let popt = ParserOptions.with_hh_syntax_for_hhvm
popt
2931 (env.codegen
&& (ParserOptions.enable_hh_syntax_for_hhvm
popt || env.is_hh_file
)) in
2932 let popt = ParserOptions.with_disallow_elvis_space
popt
2933 env.disallow_elvis_space
in
2934 let env = { env with parser_options = popt } in
2935 let script = SyntaxTree.root
tree in
2939 Full_fidelity_editable_positioned_syntax.from_positioned_syntax
script
2940 |> Ppl_class_rewriter.rewrite_ppl_classes
2941 |> Coroutine_lowerer.lower_coroutines in
2942 FromEditablePositionedSyntax.lower
2947 FromPositionedSyntax.lower
2952 let from_file (env : env) : result =
2953 let source_text = SourceText.from_file env.file in
2954 from_text env source_text
2956 (*****************************************************************************(
2957 * Backward compatibility matter (should be short-lived)
2958 )*****************************************************************************)
2960 let legacy (x : result) : Parser_hack.parser_return
=
2961 { Parser_hack.file_mode
= Option.some_if
(x.fi_mode
<> FileInfo.Mphp
) x.fi_mode
2962 ; Parser_hack.is_hh_file
= x.is_hh_file
2963 ; Parser_hack.comments = x.comments
2964 ; Parser_hack.ast = x.ast
2965 ; Parser_hack.content = x.content
2968 let from_text_with_legacy (env : env) (content : string)
2969 : Parser_hack.parser_return
=
2970 let source_text = SourceText.make
env.file content in
2971 legacy @@ from_text env source_text
2973 let from_file_with_legacy env = legacy (from_file env)