2 * Copyright (c) 2016, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 module SyntaxTree
= Full_fidelity_syntax_tree
12 .WithSyntax
(Full_fidelity_positioned_syntax
)
13 module Syntax
= Full_fidelity_editable_positioned_syntax
14 module SyntaxValue
= Syntax.Value
15 module NS
= Namespaces
17 module SyntaxKind
= Full_fidelity_syntax_kind
18 module TK
= Full_fidelity_token_kind
19 module Trivia
= Full_fidelity_positioned_trivia
20 module TriviaKind
= Full_fidelity_trivia_kind
21 module ParserErrors
= Full_fidelity_parser_errors
22 .WithSyntax
(Full_fidelity_positioned_syntax
)
27 (* What we're lowering from *)
29 type node
= Syntax.t
(* Let's be more explicit *)
30 (* What we're lowering to *)
33 let drop_pstr : int -> pstring
-> pstring
= fun cnt
(pos
, str
) ->
34 let len = String.length str
in
35 pos
, if cnt
>= len then "" else String.sub str cnt
(len - cnt
)
37 (* Context of the file being parsed, as (hopefully some day read-only) state. *)
39 { hhvm_compat_mode
: bool
42 ; php5_compat_mode
: bool
43 ; elaborate_namespaces
: bool
44 ; include_line_comments
: bool
47 ; lower_coroutines
: bool
48 ; enable_hh_syntax
: bool
49 ; top_level_statements
: bool (* Whether we are (still) considering TLSs*)
50 ; parser_options
: ParserOptions.t
51 ; fi_mode
: FileInfo.mode
52 ; file
: Relative_path.t
53 ; stats
: Stats_container.t
option
54 (* Changing parts; should disappear in future. `mutable` saves allocations. *)
55 ; mutable ignore_pos
: bool
56 ; mutable max_depth
: int (* Filthy hack around OCaml bug *)
57 ; mutable saw_yield
: bool (* Information flowing back up *)
58 ; mutable unsafes
: ISet.t
(* Offsets of UNSAFE_EXPR in trivia *)
59 ; mutable saw_std_constant_redefinition
: bool
62 let non_tls env
= if not env
.top_level_statements
then env
else
63 { env
with top_level_statements
= false }
65 type +'a parser
= node
-> env
-> 'a
66 type ('a
, 'b
) metaparser
= 'a parser
-> 'b parser
68 let mode_annotation = function
69 | FileInfo.Mphp
-> FileInfo.Mdecl
72 let pPos : Pos.t parser
= fun node env
->
73 if env
.ignore_pos
|| value node
= Value.Synthetic
76 let pos_file = env
.file
in
77 let text = source_text node
in
78 let start_offset = start_offset node
in
79 let end_offset = end_offset node
in
80 SourceText.relative_pos
pos_file text start_offset end_offset
83 (* HHVM starts range of function declaration from the 'function' keyword *)
84 let pFunction node env
=
85 let p = pPos node env
in
86 match syntax node
with
87 | FunctionDeclaration
{ function_declaration_header
= h
; _
}
88 | MethodishDeclaration
{ methodish_function_decl_header
= h
; _
}
89 when env
.hhvm_compat_mode
->
90 begin match syntax h
with
91 | FunctionDeclarationHeader
{ function_keyword
= f
; _
}
92 when not
(is_missing f
) ->
93 let pos_start = Pos.pos_start @@ pPos f env
in
94 let pos_end = Pos.pos_end p in
95 let pos_file = env
.file
in
96 Pos.make_from_file_pos ~
pos_file ~
pos_start ~
pos_end
102 exception Lowerer_invariant_failure
of string * string
103 let invariant_failure node msg env
=
104 let pos = Pos.string (Pos.to_absolute
(pPos node env
)) in
105 raise
(Lowerer_invariant_failure
(pos, msg
))
107 exception API_Missing_syntax
of string * env
* node
108 let missing_syntax : string -> node
-> env
-> 'a
= fun s n env
->
109 raise
(API_Missing_syntax
(s
, env
, n
))
110 let handle_missing_syntax : string -> node
-> env
-> 'a
= fun s n e
->
111 let pos = Pos.string (Pos.to_absolute
(pPos n e
)) in
112 let msg = Printf.sprintf
121 (SyntaxKind.to_string
(kind n
))
125 let runP : 'a parser
-> node
-> env
-> 'a
= fun pThing thing env
->
126 try pThing thing env
with
127 | API_Missing_syntax
(ex
, env
, n
) -> handle_missing_syntax ex n env
131 (* TODO: Cleanup this hopeless Noop mess *)
132 let mk_noop pos : stmt list
-> stmt list
= function
135 let mpStripNoop pThing node env
= match pThing node env
with
139 let mpOptional : ('a
, 'a
option) metaparser
= fun p -> fun node env
->
140 Option.try_with
(fun () -> p node env
)
141 let mpYielding : ('a
, ('a
* bool)) metaparser
= fun p node env
->
142 let outer_saw_yield = env
.saw_yield
in
143 let () = env
.saw_yield
<- false in
144 let result = p node env
in
145 let result = result, env
.saw_yield
in
146 let () = env
.saw_yield
<- outer_saw_yield in
152 | InDoubleQuotedString
157 l
= InDoubleQuotedString
|| l
= InBacktickedString
159 let pos_qualified_name node env
=
162 | ListItem li
-> (text li
.list_item
) ^
(text li
.list_separator
)
164 let p = pPos node env
in
166 match syntax node
with
168 qualified_name_parts
= { syntax
= SyntaxList l
; _
};
170 String.concat
"" @@ List.map ~f
:aux l
171 | _
-> missing_syntax "qualified name" node env
in
174 let rec pos_name node env
=
175 match syntax node
with
176 | QualifiedName _
-> pos_qualified_name node env
177 | SimpleTypeSpecifier
{ simple_type_specifier
= s
} -> pos_name s env
179 let name = text node
in
180 let local_ignore_pos = env
.ignore_pos
in
181 (* Special case for __LINE__; never ignore position for that special name *)
182 if name = "__LINE__" then env
.ignore_pos
<- false;
183 let p = pPos node env
in
184 env
.ignore_pos
<- local_ignore_pos;
187 let is_ret_by_ref node
= not
@@ is_missing node
189 let couldMap : 'a
. f
:'a parser
-> 'a list parser
= fun ~f
-> fun node env
->
190 let rec synmap : 'a
. 'a parser
-> 'a list parser
= fun f node env
->
191 match syntax node
with
192 | SyntaxList l
-> List.concat_map l ~f
:(fun n
-> go ~f n env
)
193 | ListItem i
-> [f i
.list_item env
]
195 and go
: 'a
. f
:'a parser
-> 'a list parser
= fun ~f
-> function
196 | node
when is_missing node
-> fun _env
-> []
197 | node
-> synmap f node
201 let as_list : node
-> node list
=
202 let strip_list_item = function
203 | { syntax
= ListItem
{ list_item
= i
; _
}; _
} -> i
206 | { syntax
= SyntaxList
({syntax
= ListItem _
; _
}::_
as synl
); _
} ->
207 List.map ~f
:strip_list_item synl
208 | { syntax
= SyntaxList synl
; _
} -> synl
209 | { syntax
= Missing
; _
} -> []
212 let token_kind : node
-> TK.t
option = function
213 | { syntax
= Token t
; _
} -> Some
(Token.kind t
)
216 let pBop : (expr
-> expr
-> expr_
) parser
= fun node env lhs rhs
->
217 match token_kind node
with
218 | Some
TK.Equal
-> Binop
(Eq None
, lhs
, rhs
)
219 | Some
TK.Bar
-> Binop
(Bar
, lhs
, rhs
)
220 | Some
TK.Ampersand
-> Binop
(Amp
, lhs
, rhs
)
221 | Some
TK.Plus
-> Binop
(Plus
, lhs
, rhs
)
222 | Some
TK.Minus
-> Binop
(Minus
, lhs
, rhs
)
223 | Some
TK.Star
-> Binop
(Star
, lhs
, rhs
)
224 | Some
TK.Or
-> Binop
(BArbar
, lhs
, rhs
)
225 | Some
TK.And
-> Binop
(AMpamp
, lhs
, rhs
)
226 | Some
TK.Xor
-> Binop
(LogXor
, lhs
, rhs
)
227 | Some
TK.Carat
-> Binop
(Xor
, lhs
, rhs
)
228 | Some
TK.Slash
-> Binop
(Slash
, lhs
, rhs
)
229 | Some
TK.Dot
-> Binop
(Dot
, lhs
, rhs
)
230 | Some
TK.Percent
-> Binop
(Percent
, lhs
, rhs
)
231 | Some
TK.LessThan
-> Binop
(Lt
, lhs
, rhs
)
232 | Some
TK.GreaterThan
-> Binop
(Gt
, lhs
, rhs
)
233 | Some
TK.EqualEqual
-> Binop
(Eqeq
, lhs
, rhs
)
234 | Some
TK.LessThanEqual
-> Binop
(Lte
, lhs
, rhs
)
235 | Some
TK.GreaterThanEqual
-> Binop
(Gte
, lhs
, rhs
)
236 | Some
TK.StarStar
-> Binop
(Starstar
, lhs
, rhs
)
237 | Some
TK.ExclamationEqual
-> Binop
(Diff
, lhs
, rhs
)
238 | Some
TK.BarEqual
-> Binop
(Eq
(Some Bar
), lhs
, rhs
)
239 | Some
TK.PlusEqual
-> Binop
(Eq
(Some Plus
), lhs
, rhs
)
240 | Some
TK.MinusEqual
-> Binop
(Eq
(Some Minus
), lhs
, rhs
)
241 | Some
TK.StarEqual
-> Binop
(Eq
(Some Star
), lhs
, rhs
)
242 | Some
TK.StarStarEqual
-> Binop
(Eq
(Some Starstar
),lhs
, rhs
)
243 | Some
TK.SlashEqual
-> Binop
(Eq
(Some Slash
), lhs
, rhs
)
244 | Some
TK.DotEqual
-> Binop
(Eq
(Some Dot
), lhs
, rhs
)
245 | Some
TK.PercentEqual
-> Binop
(Eq
(Some Percent
), lhs
, rhs
)
246 | Some
TK.CaratEqual
-> Binop
(Eq
(Some Xor
), lhs
, rhs
)
247 | Some
TK.AmpersandEqual
-> Binop
(Eq
(Some Amp
), lhs
, rhs
)
248 | Some
TK.BarBar
-> Binop
(BArbar
, lhs
, rhs
)
249 | Some
TK.AmpersandAmpersand
-> Binop
(AMpamp
, lhs
, rhs
)
250 | Some
TK.LessThanLessThan
-> Binop
(Ltlt
, lhs
, rhs
)
251 | Some
TK.GreaterThanGreaterThan
-> Binop
(Gtgt
, lhs
, rhs
)
252 | Some
TK.EqualEqualEqual
-> Binop
(EQeqeq
, lhs
, rhs
)
253 | Some
TK.LessThanLessThanEqual
-> Binop
(Eq
(Some Ltlt
), lhs
, rhs
)
254 | Some
TK.GreaterThanGreaterThanEqual
-> Binop
(Eq
(Some Gtgt
), lhs
, rhs
)
255 | Some
TK.LessThanGreaterThan
-> Binop
(Diff
, lhs
, rhs
)
256 | Some
TK.ExclamationEqualEqual
-> Binop
(Diff2
, lhs
, rhs
)
257 | Some
TK.LessThanEqualGreaterThan
-> Binop
(Cmp
, lhs
, rhs
)
258 (* The ugly ducklings; In the FFP, `|>` and '??' are parsed as
259 * `BinaryOperator`s, whereas the typed AST has separate constructors for
260 * NullCoalesce, Pipe and Binop. This is why we don't just project onto a
261 * `bop`, but a `expr -> expr -> expr_`.
263 | Some
TK.BarGreaterThan
-> Pipe
(lhs
, rhs
)
264 | Some
TK.QuestionQuestion
-> NullCoalesce
(lhs
, rhs
)
265 | Some
TK.QuestionColon
-> Eif
(lhs
, None
, rhs
)
266 (* TODO: Figure out why this fails silently when used in a pBlock; probably
267 just caught somewhere *)
268 | _
-> missing_syntax "binary operator" node env
270 let pImportFlavor : import_flavor parser
= fun node env
->
271 match token_kind node
with
272 | Some
TK.Include
-> Include
273 | Some
TK.Require
-> Require
274 | Some
TK.Include_once
-> IncludeOnce
275 | Some
TK.Require_once
-> RequireOnce
276 | _
-> missing_syntax "import flavor" node env
278 let pNullFlavor : og_null_flavor parser
= fun node env
->
279 match token_kind node
with
280 | Some
TK.QuestionMinusGreaterThan
-> OG_nullsafe
281 | Some
TK.MinusGreaterThan
-> OG_nullthrows
282 | _
-> missing_syntax "null flavor" node env
290 let pModifiers node env
=
291 let f (has_async
, has_coroutine
, kinds
) node
=
292 match token_kind node
with
293 | Some
TK.Final
-> has_async
, has_coroutine
, (Final
:: kinds
)
294 | Some
TK.Static
-> has_async
, has_coroutine
, (Static
:: kinds
)
295 | Some
TK.Abstract
-> has_async
, has_coroutine
, (Abstract
:: kinds
)
296 | Some
TK.Private
-> has_async
, has_coroutine
, (Private
:: kinds
)
297 | Some
TK.Public
-> has_async
, has_coroutine
, (Public
:: kinds
)
298 | Some
TK.Protected
-> has_async
, has_coroutine
, (Protected
:: kinds
)
299 | Some
TK.Var
-> has_async
, has_coroutine
, (Public
:: kinds
)
300 | Some
TK.Async
-> true, has_coroutine
, kinds
301 | Some
TK.Coroutine
-> has_async
, true, kinds
302 | _
-> missing_syntax "kind" node env
in
303 let (has_async
, has_coroutine
, kinds
) =
304 Core_list.fold_left ~init
:(false, false, []) ~
f (as_list node
) in
305 { has_async
; has_coroutine
; kinds
= List.rev kinds
}
307 let pKinds node env
= (pModifiers node env
).kinds
309 let pParamKind : param_kind parser
= fun node env
->
310 match token_kind node
with
311 | Some
TK.Inout
-> Pinout
312 | _
-> missing_syntax "param kind" node env
314 let syntax_of_token : Token.t
-> node
= fun t
->
316 SyntaxValue.from_token t
in
319 (* TODO: Clean up string escaping *)
320 let prepString2 : node list
-> node list
=
321 let is_double_quote_or_backtick ch
= ch
= '
"' || ch = '`' in
324 with_updated_original_source_data
328 leading_width = leading_width t + n;
336 with_updated_original_source_data
340 trailing_width = trailing_width t + n;
347 | ({ syntax = Token t; _ }::ss)
348 when (Token.width t) > 0 && is_double_quote_or_backtick (Token.text t).[0] ->
349 let rec unwind = function
350 | [{ syntax = Token t; _ }]
351 when (Token.width t) > 0 &&
352 is_double_quote_or_backtick ((Token.text t).[(Token.width t) - 1]) ->
353 let s = syntax_of_token (trimRight ~n:1 t) in
354 if width s > 0 then [s] else []
355 | x :: xs -> x :: unwind xs
356 | _ -> raise (Invalid_argument "Malformed String2 SyntaxList
")
358 let s = syntax_of_token (trimLeft ~n:1 t) in
359 if width s > 0 then s :: unwind ss else unwind ss
360 | ({ syntax = Token t; _ }::ss)
361 when (Token.width t) > 3 && String.sub (Token.text t) 0 3 = "<<<" ->
362 let rec unwind = function
363 | [{ syntax = Token t; _ }] when (Token.width t) > 0 ->
364 let content = Token.text t in
365 let len = (Token.width t) in
366 let n = len - (String.rindex_from content (len - 2) '\n') in
367 let s = syntax_of_token (trimRight ~n t) in
368 if width s > 0 then [s] else []
369 | x :: xs -> x :: unwind xs
370 | _ -> raise (Invalid_argument "Malformed String2 SyntaxList
")
372 let content = Token.text t in
373 let n = (String.index content '\n') + 1 in
374 let s = syntax_of_token (trimLeft ~n t) in
375 if width s > 0 then s :: unwind ss else unwind ss
376 | x -> x (* unchanged *)
378 let mkStr : (string -> string) -> string -> string = fun unescaper content ->
379 let len = String.length content in
381 if len >= 3 && String.sub content 0 3 = "<<<" (* The heredoc case *)
383 (* These types of strings begin with an opening line containing <<<
384 * followed by a string to use as a terminator (which is optionally
385 * quoted) and end with a line containing only the terminator and a
386 * semicolon followed by a blank line. We need to drop the opening
387 * line as well as the blank line and preceding terminator line. *)
388 let start = (String.index content '\n') + 1 in
389 let end_ = (String.rindex_from content (len - 2) '\n') in
390 (* in case of empty heredoc expected start position will be
391 located after the end *)
392 if start >= end_ then ""
393 else String.sub content start (end_ - start)
395 let transform_binary_string_to_string_if_needed str =
396 if String.get str 0 = 'b'
397 then String.sub str 1 (String.length str - 1)
399 let content = transform_binary_string_to_string_if_needed content in
401 let len = String.length str in
403 let first_char = String.get content 0 in
404 let last_char = String.get content (len - 1) in
405 let first_and_last_are ch = (first_char == ch && last_char == ch) in
406 List.exists ['"'
; '
\''
; '`'
] ~
f:first_and_last_are
408 if has_quotes content
409 then String.sub
content 1 (String.length
content - 2)
412 | Invalid_argument _
-> content
414 try unescaper
no_quotes with
415 | Php_escaping.Invalid_string _
-> raise
@@
416 Failure
(Printf.sprintf
"Malformed string literal <<%s>>" no_quotes)
417 let unempty_str = function
418 | "''" | "\"\"" -> ""
420 let unesc_dbl s = unempty_str @@ Php_escaping.unescape_double
s
422 let whitespace = Str.regexp
"[ \t\n\r\012]+" in
423 Str.global_replace
whitespace " " s
424 let unesc_xhp_attr s =
427 if string_match
(regexp
"[ \t\n\r\012]*\"\\(\\(.\\|\n\\)*\\)\"") s 0
428 then matched_group
1 s
431 type suspension_kind
=
436 let mk_suspension_kind_ has_async has_coroutine
=
437 match has_async
, has_coroutine
with
438 | false, false -> SKSync
439 | true, false -> SKAsync
440 | false, true-> SKCoroutine
441 | true, true -> raise
(Failure
"Couroutine functions may not be async")
443 let mk_suspension_kind is_async is_coroutine
=
445 (not
(is_missing is_async
))
446 (not
(is_missing is_coroutine
))
448 let mk_fun_kind suspension_kind yield
=
449 match suspension_kind
, yield
with
450 | SKSync
, true -> FGenerator
451 | SKAsync
, true -> FAsyncGenerator
452 | SKSync
, false -> FSync
453 | SKAsync
, false -> FAsync
454 (* TODO(t17335630): Implement an FCoroutine fun_kind *)
455 | SKCoroutine
, false -> assert false
456 | SKCoroutine
, true -> raise
(Failure
"Couroutine functions may not yield")
458 let fun_template yielding node suspension_kind env
=
459 let p = pFunction node env
in
460 { f_mode
= mode_annotation env
.fi_mode
464 ; f_ret_by_ref
= false
465 ; f_name
= p, ";anonymous"
468 ; f_user_attributes
= []
469 ; f_fun_kind
= mk_fun_kind suspension_kind yielding
470 ; f_namespace
= Namespace_env.empty env
.parser_options
472 ; f_doc_comment
= None
476 let param_template node env
=
478 ; param_is_reference
= false
479 ; param_is_variadic
= false
480 ; param_id
= pos_name node env
482 ; param_modifier
= None
483 ; param_callconv
= None
484 ; param_user_attributes
= []
487 let pShapeFieldName : shape_field_name parser
= fun name env
->
488 match syntax
name with
489 | ScopeResolutionExpression
490 { scope_resolution_qualifier
; scope_resolution_name
; _
} ->
492 ( pos_name scope_resolution_qualifier env
493 , pos_name scope_resolution_name env
495 | _
-> let p, n = pos_name name env
in SFlit
(p, mkStr unesc_dbl n)
497 let mpShapeExpressionField : ('a
, (shape_field_name
* 'a
)) metaparser
=
498 fun hintParser node env
->
499 match syntax node
with
501 { field_initializer_name
= name; field_initializer_value
= ty
; _
} ->
502 let name = pShapeFieldName name env
in
503 let ty = hintParser
ty env
in
505 | _
-> missing_syntax "shape field" node env
507 let mpShapeField : ('a
, shape_field
) metaparser
=
508 fun hintParser node env
->
509 match syntax node
with
510 | FieldSpecifier
{ field_question
; field_name
; field_type
; _
} ->
511 let sf_optional = not
(is_missing field_question
) in
512 let sf_name = pShapeFieldName field_name env
in
513 let sf_hint = hintParser field_type env
in
514 { sf_optional; sf_name; sf_hint }
516 let sf_name, sf_hint = mpShapeExpressionField hintParser node env
in
517 (* Shape expressions can never have optional fields. *)
518 { sf_optional = false; sf_name; sf_hint }
520 let mpClosureParameter : ('a
, hint
* param_kind
option) metaparser
=
521 fun hintParser node env
->
522 match syntax node
with
523 | ClosureParameterTypeSpecifier
524 { closure_parameter_call_convention
525 ; closure_parameter_type
528 mpOptional pParamKind closure_parameter_call_convention env
in
529 let cp_hint = hintParser closure_parameter_type env
in
531 | _
-> missing_syntax "closure parameter" node env
533 let rec pHint : hint parser
= fun node env
->
534 let rec pHint_ : hint_ parser
= fun node env
->
535 match syntax node
with
536 (* Dirty hack; CastExpression can have type represented by token *)
538 | SimpleTypeSpecifier _
540 -> Happly
(pos_name node env
, [])
541 | ShapeTypeSpecifier
{ shape_type_fields
; shape_type_ellipsis
; _
} ->
542 let si_allows_unknown_fields =
543 not
(is_missing shape_type_ellipsis
)
545 let si_shape_field_list =
546 couldMap ~
f:(mpShapeField pHint) shape_type_fields env
in
547 Hshape
{ si_allows_unknown_fields; si_shape_field_list }
548 | TupleTypeSpecifier
{ tuple_types
; _
} ->
549 Htuple
(couldMap ~
f:pHint tuple_types env
)
550 | KeysetTypeSpecifier
{ keyset_type_keyword
= kw
; keyset_type_type
= ty; _
}
551 | VectorTypeSpecifier
{ vector_type_keyword
= kw
; vector_type_type
= ty; _
}
552 | ClassnameTypeSpecifier
{classname_keyword
= kw
; classname_type
= ty; _
}
553 | TupleTypeExplicitSpecifier
554 { tuple_type_keyword
= kw
555 ; tuple_type_types
= ty
557 | VarrayTypeSpecifier
558 { varray_keyword
= kw
561 | VectorArrayTypeSpecifier
562 { vector_array_keyword
= kw
563 ; vector_array_type
= ty
565 -> Happly
(pos_name kw env
, couldMap ~
f:pHint ty env
)
567 | DarrayTypeSpecifier
568 { darray_keyword
= kw
570 ; darray_value
= value
572 | MapArrayTypeSpecifier
573 { map_array_keyword
= kw
574 ; map_array_key
= key
575 ; map_array_value
= value
579 , pHint key env
:: couldMap ~
f:pHint value env
581 | DictionaryTypeSpecifier
582 { dictionary_type_keyword
= kw
583 ; dictionary_type_members
= members
584 ; _
} -> Happly
(pos_name kw env
, couldMap ~
f:pHint members env
)
585 | GenericTypeSpecifier
{ generic_class_type
; generic_argument_list
} ->
587 ( pos_name generic_class_type env
588 , match syntax generic_argument_list
with
589 | TypeArguments
{ type_arguments_types
; _
}
590 -> couldMap ~
f:pHint type_arguments_types env
591 | _
-> missing_syntax "generic type arguments" generic_argument_list env
593 | NullableTypeSpecifier
{ nullable_type
; _
} ->
594 Hoption
(pHint nullable_type env
)
595 | SoftTypeSpecifier
{ soft_type
; _
} ->
596 Hsoft
(pHint soft_type env
)
597 | ClosureTypeSpecifier
{
598 closure_parameter_list
;
600 closure_coroutine
; _
} ->
601 let make_variadic_hint variadic_type
=
602 if is_missing variadic_type
603 then Hvariadic
(None
)
604 else Hvariadic
(Some
(pHint variadic_type env
))
606 let (param_list
, variadic_hints
) =
607 List.partition_map ~
f:(fun x
->
609 | VariadicParameter
{ variadic_parameter_type
= vtype
; _
} ->
610 `Snd
(make_variadic_hint vtype
)
611 | _
-> `Fst
(mpClosureParameter pHint x env
))
612 (as_list closure_parameter_list
)
614 let hd_variadic_hint hints
=
615 if List.length hints
> 1 then begin
616 let msg = Printf.sprintf
617 "%d variadic parameters found. There should be no more than one."
620 invariant_failure node
msg env
622 match List.hd hints
with
624 | None
-> Hnon_variadic
626 let is_coroutine = not
(is_missing closure_coroutine
) in
627 let param_type_hints = List.map param_list fst
in
628 let param_callconvs = List.map param_list snd
in
633 , hd_variadic_hint variadic_hints
634 , pHint closure_return_type env
636 | TypeConstant
{ type_constant_left_type
; type_constant_right_type
; _
} ->
637 let child = pos_name type_constant_right_type env
in
638 (match pHint_ type_constant_left_type env
with
639 | Haccess
(b
, c
, cs
) -> Haccess
(b
, c
, cs
@ [child])
640 | Happly
(b
, []) -> Haccess
(b
, child, [])
641 | _
-> missing_syntax "type constant base" node env
643 | _
-> missing_syntax "type hint" node env
645 pPos node env
, pHint_ node env
648 { fh_suspension_kind
: suspension_kind
650 ; fh_constrs
: (hint
* constraint_kind
* hint
) list
651 ; fh_type_parameters
: tparam list
652 ; fh_parameters
: fun_param list
653 ; fh_return_type
: hint
option
654 ; fh_param_modifiers
: fun_param list
655 ; fh_ret_by_ref
: bool
659 { fh_suspension_kind
= SKSync
660 ; fh_name
= Pos.none
, "<ANONYMOUS>"
662 ; fh_type_parameters
= []
664 ; fh_return_type
= None
665 ; fh_param_modifiers
= []
666 ; fh_ret_by_ref
= false
669 let rec pSimpleInitializer node env
=
670 match syntax node
with
671 | SimpleInitializer
{ simple_initializer_value
; simple_initializer_equal
} ->
672 pExpr simple_initializer_value env
673 | _
-> missing_syntax "simple initializer" node env
675 and pFunParam
: fun_param parser
= fun node env
->
676 match syntax node
with
677 | ParameterDeclaration
678 { parameter_attribute
679 ; parameter_visibility
680 ; parameter_call_convention
683 ; parameter_default_value
685 let is_reference, is_variadic
, name =
686 match syntax parameter_name
with
687 | DecoratedExpression
688 { decorated_expression_decorator
; decorated_expression_expression
} ->
689 (* There is a chance that the expression might be nested with an
690 additional decorator, check this *)
691 begin match syntax decorated_expression_expression
with
692 | DecoratedExpression
693 { decorated_expression_decorator
= nested_decorator
694 ; decorated_expression_expression
= nested_expression
} ->
695 let decorator = text decorated_expression_decorator
in
696 let nested_decorator = text nested_decorator in
697 decorator = "&" || nested_decorator = "&",
698 decorator = "..." || nested_decorator = "...",
701 let decorator = text decorated_expression_decorator
in
702 decorator = "&", decorator = "...", decorated_expression_expression
704 | _
-> false, false, parameter_name
706 { param_hint
= mpOptional pHint parameter_type env
707 ; param_is_reference
= is_reference
708 ; param_is_variadic
= is_variadic
709 ; param_id
= pos_name name env
711 mpOptional pSimpleInitializer parameter_default_value env
712 ; param_user_attributes
= List.concat
@@
713 couldMap ~
f:pUserAttribute parameter_attribute env
715 mpOptional pParamKind parameter_call_convention env
716 (* implicit field via constructor parameter.
717 * This is always None except for constructors and the modifier
718 * can be only Public or Protected or Private.
721 let rec go = function
723 | x
:: _
when List.mem
[Private
; Public
; Protected
] x
-> Some x
726 go (pKinds parameter_visibility env
)
728 | VariadicParameter _
729 | Token _
when text node
= "..."
730 -> { (param_template node env
) with param_is_variadic
= true }
731 | _
-> missing_syntax "function parameter" node env
732 and pUserAttribute
: user_attribute list parser
= fun node env
->
733 match syntax node
with
734 | AttributeSpecification
{ attribute_specification_attributes
; _
} ->
735 couldMap attribute_specification_attributes env ~
f:begin function
736 | { syntax
= Attribute
{ attribute_name
; attribute_values
; _
}; _
} ->
738 { ua_name
= pos_name attribute_name env
739 ; ua_params
= couldMap ~
f:pExpr attribute_values env
741 | node
-> missing_syntax "attribute" node
743 | _
-> missing_syntax "attribute specification" node env
744 and pAField
: afield parser
= fun node env
->
745 match syntax node
with
746 | ElementInitializer
{ element_key
; element_value
; _
} ->
747 AFkvalue
(pExpr element_key env
, pExpr element_value env
)
748 | _
-> AFvalue
(pExpr node env
)
749 and pString2
: expr_location
-> node list
-> env
-> expr list
=
750 let rec convert_name_to_lvar location env
n =
752 | Token
{ Token.kind
= TK.Name
; _
} ->
753 let pos, name = pos_name n env
in
754 let id = Lvar
(pos, "$" ^
name) in
756 | SubscriptExpression
{ subscript_receiver
; subscript_index
; _
} ->
757 begin match convert_name_to_lvar location env subscript_receiver
with
759 let index = mpOptional (pExpr ~location
) subscript_index env
in
760 Some
(pPos n env
, Array_get
(recv
, index))
765 let rec aux loc l env acc
=
766 (* in PHP "${x}" in strings is treated as if it was written "$x",
767 here we recognize pattern: Dollar; EmbeddedBracedExpression { QName (Token.Name) }
768 produced by FFP and lower it into Lvar.
772 | ({ syntax
= Token
{ Token.kind
= TK.Dollar
; _
}; _
})::
773 ({ syntax
= EmbeddedBracedExpression
{
774 embedded_braced_expression_expression
= e
; _
}; _
775 } as expr_with_braces
)::
778 begin match convert_name_to_lvar loc env
e with
781 let e = pExpr ~location
:loc expr_with_braces env
in
782 fst
e, Dollar
(fst
e, BracedExpr
e)
784 aux loc tl env
(e::acc
)
785 | x
::xs
-> aux loc xs env
((pExpr ~location
:loc x env
)::acc
)
787 fun loc l env
-> aux loc l env
[]
788 and pExprL node env
=
789 (pPos node env
, Expr_list
(couldMap ~
f:pExpr node env
))
790 and pExpr ?location
:(location
=TopLevel
) : expr parser
= fun node env
->
791 let split_args_varargs arg_list
=
792 match List.rev
(as_list arg_list
) with
793 | { syntax
= DecoratedExpression
794 { decorated_expression_decorator
=
795 { syntax
= Token
{ Token.kind
= Token.TokenKind.DotDotDot
; _
}; _
}
796 ; decorated_expression_expression
= e
800 let args = List.rev_map xs
(fun x
-> pExpr x env
) in
801 let vararg = pExpr
e env
in
804 let args = couldMap ~
f:pExpr arg_list env
in
806 let rec pExpr_ : expr_ parser
= fun node env
->
807 let pos = pPos node env
in
808 match syntax node
with
810 lambda_async
; lambda_coroutine
; lambda_signature
; lambda_body
; _
} ->
811 let suspension_kind =
812 mk_suspension_kind lambda_async lambda_coroutine
in
813 let f_params, f_ret
=
814 match syntax lambda_signature
with
815 | LambdaSignature
{ lambda_parameters
; lambda_type
; _
} ->
816 ( couldMap ~
f:pFunParam lambda_parameters env
817 , mpOptional pHint lambda_type env
819 | Token _
-> ([param_template lambda_signature env
], None
)
820 | _
-> missing_syntax "lambda signature" lambda_signature env
823 mpYielding pFunctionBody lambda_body
824 (if not
(is_compound_statement lambda_body
) then env
else non_tls env
)
827 { (fun_template yield node
suspension_kind env
) with
833 | BracedExpression
{ braced_expression_expression
= expr
; _
}
834 | EmbeddedBracedExpression
835 { embedded_braced_expression_expression
= expr
; _
}
836 | ParenthesizedExpression
{ parenthesized_expression_expression
= expr
; _
}
839 | DictionaryIntrinsicExpression
840 { dictionary_intrinsic_keyword
= kw
841 ; dictionary_intrinsic_members
= members
843 | KeysetIntrinsicExpression
844 { keyset_intrinsic_keyword
= kw
845 ; keyset_intrinsic_members
= members
847 | VectorIntrinsicExpression
848 { vector_intrinsic_keyword
= kw
849 ; vector_intrinsic_members
= members
853 try Some
(couldMap ~
f:pExpr members env
) with
854 | API_Missing_syntax
(_
,_
,n)
855 when kind
n = SyntaxKind.ElementInitializer
-> None
857 if env
.is_hh_file
|| env
.enable_hh_syntax
|| members_opt = None
858 then Collection
(pos_name kw env
, couldMap ~
f:pAField members env
)
860 (* If php, this is a subscript expression, not a collection. *)
861 let subscript_receiver = pExpr kw env
in
862 let members = couldMap ~
f:pExpr
members env
in
863 let subscript_index = match members with
867 let msg = "Hack keyword " ^
(text kw
) ^
" used improperly in php." in
868 invariant_failure node
msg env
in
869 Array_get
(subscript_receiver, subscript_index)
870 | CollectionLiteralExpression
871 { collection_literal_name
= collection_name
872 ; collection_literal_initializers
= members
874 let collection_name =
875 match syntax
collection_name with
876 | SimpleTypeSpecifier
{ simple_type_specifier
= class_type
}
877 (* TODO: currently type arguments are dropped on the floor,
878 though they should be properly propagated to the typechecker *)
879 | GenericTypeSpecifier
{ generic_class_type
= class_type
; _
} ->
880 pos_name class_type env
881 | _
-> pos_name collection_name env
in
882 Collection
(collection_name, couldMap ~
f:pAField
members env
)
884 | VarrayIntrinsicExpression
{ varray_intrinsic_members
= members; _
} ->
885 Varray
(couldMap ~
f:pExpr
members env
)
886 | DarrayIntrinsicExpression
{ darray_intrinsic_members
= members; _
} ->
887 let pMember node env
=
888 match syntax node
with
889 | ElementInitializer
{ element_key
; element_value
; _
} ->
890 (pExpr element_key env
, pExpr element_value env
)
891 | _
-> missing_syntax "darray intrinsic expression element" node env
893 Darray
(couldMap ~
f:pMember members env
)
894 | ArrayIntrinsicExpression
{ array_intrinsic_members
= members; _
}
895 | ArrayCreationExpression
{ array_creation_members
= members; _
}
897 (* TODO: Or tie in with other intrinsics and post-process to Array *)
898 Array
(couldMap ~
f:pAField
members env
)
900 | ListExpression
{ list_members
= members; _
} ->
901 (* TODO: Or tie in with other intrinsics and post-process to List *)
902 let pBinderOrIgnore node env
=
903 Option.value ~default
:(Pos.none
, Omitted
) @@ mpOptional pExpr node env
905 List
(couldMap ~
f:pBinderOrIgnore members env
)
907 | EvalExpression
{ eval_keyword
= recv
; eval_argument
= args; _
}
908 | EmptyExpression
{ empty_keyword
= recv
; empty_argument
= args; _
}
909 | IssetExpression
{ isset_keyword
= recv
; isset_argument_list
= args; _
}
911 { tuple_expression_keyword
= recv
912 ; tuple_expression_items
= args
914 -> Call
(pExpr recv env
, [], couldMap ~
f:pExpr
args env
, [])
915 | FunctionCallExpression
916 { function_call_receiver
= recv
917 ; function_call_argument_list
= args
921 begin match (syntax recv
) with
922 | GenericTypeSpecifier
{generic_class_type
; generic_argument_list
} ->
923 begin match syntax generic_argument_list
with
924 | TypeArguments
{ type_arguments_types
; _
}
925 -> couldMap ~
f:pHint type_arguments_types env
931 (* preserve parens on receiver of call expression
932 to allow distinguishing between
933 ($a->b)() // invoke on callable property
934 $a->b() // method call *)
935 let pos_if_has_parens =
936 match syntax recv
with
937 | ParenthesizedExpression _
-> Some
(pPos recv env
)
939 let recv = pExpr
recv env
in
941 match snd
recv, pos_if_has_parens with
942 | (Obj_get _
| Class_get _
), Some
p -> p, ParenthesizedExpr
recv
944 let args, varargs
= split_args_varargs args in
945 Call
(recv, hints, args, varargs
)
946 | FunctionCallWithTypeArgumentsExpression
947 { function_call_with_type_arguments_receiver
= recv
948 ; function_call_with_type_arguments_type_args
= type_args
949 ; function_call_with_type_arguments_argument_list
= args
953 begin match (syntax type_args
) with
954 | TypeArguments
{ type_arguments_types
; _
} ->
955 couldMap ~
f:pHint type_arguments_types env
956 | _
-> missing_syntax "no type arguments for annotated function call" type_args env
958 Call
(pExpr
recv env
, hints, couldMap ~
f:pExpr
args env
, [])
960 if in_string location
961 then String
(pos_qualified_name node env
)
962 else Id
(pos_qualified_name node env
)
964 | VariableExpression
{ variable_expression
} ->
965 Lvar
(pos_name variable_expression env
)
967 | PipeVariableExpression _
->
970 | InclusionExpression
{ inclusion_require
; inclusion_filename
} ->
972 ( pImportFlavor inclusion_require env
973 , pExpr inclusion_filename env
976 | MemberSelectionExpression
977 { member_object
= recv
978 ; member_operator
= op
981 | SafeMemberSelectionExpression
982 { safe_member_object
= recv
983 ; safe_member_operator
= op
984 ; safe_member_name
= name
986 | EmbeddedMemberSelectionExpression
987 { embedded_member_object
= recv
988 ; embedded_member_operator
= op
989 ; embedded_member_name
= name
992 let recv = pExpr
recv env
in
993 let name = pExpr ~location
:MemberSelect
name env
in
994 let op = pNullFlavor op env
in
995 Obj_get
(recv, name, op)
997 | PrefixUnaryExpression
998 { prefix_unary_operator
= operator
999 ; prefix_unary_operand
= operand
1001 | PostfixUnaryExpression
1002 { postfix_unary_operand
= operand
1003 ; postfix_unary_operator
= operator
1005 | DecoratedExpression
1006 { decorated_expression_expression
= operand
1007 ; decorated_expression_decorator
= operator
1010 let expr = pExpr operand env
in
1012 * FFP does not destinguish between ++$i and $i++ on the level of token
1013 * kind annotation. Prevent duplication by switching on `postfix` for
1014 * the two operatores for which AST /does/ differentiate between
1017 let postfix = kind node
= SyntaxKind.PostfixUnaryExpression
in
1018 (match token_kind operator
with
1019 | Some
TK.PlusPlus
when postfix -> Unop
(Upincr
, expr)
1020 | Some
TK.MinusMinus
when postfix -> Unop
(Updecr
, expr)
1021 | Some
TK.PlusPlus
-> Unop
(Uincr
, expr)
1022 | Some
TK.MinusMinus
-> Unop
(Udecr
, expr)
1023 | Some
TK.Exclamation
-> Unop
(Unot
, expr)
1024 | Some
TK.Tilde
-> Unop
(Utild
, expr)
1025 | Some
TK.Plus
-> Unop
(Uplus
, expr)
1026 | Some
TK.Minus
-> Unop
(Uminus
, expr)
1027 | Some
TK.Ampersand
-> Unop
(Uref
, expr)
1028 | Some
TK.At
when env
.codegen
-> Unop
(Usilence
, expr)
1029 | Some
TK.At
-> snd
expr
1030 | Some
TK.Inout
-> Callconv
(Pinout
, expr)
1031 | Some
TK.Await
-> Await
expr
1032 | Some
TK.Suspend
-> Suspend
expr
1033 | Some
TK.Clone
-> Clone
expr
1035 Call
((pos, Id
(pos, "echo")), [], [expr], [])
1037 (match snd
expr with
1040 | Float
(p, s) when location
<> InGlobalVar
-> Lvar
(p, "$" ^
s)
1043 | _
-> missing_syntax "unary operator" node env
1046 { binary_left_operand
; binary_operator
; binary_right_operand
}
1048 pBop binary_operator env
1049 (pExpr binary_left_operand env
)
1050 (pExpr binary_right_operand env
)
1053 (match location
with
1054 | MemberSelect
when Token.kind t
= TK.Variable
-> Lvar
(pos_name node env
)
1055 | InDoubleQuotedString
-> String
(pos, unesc_dbl (text node
))
1056 | InBacktickedString
-> String
(pos, Php_escaping.unescape_backtick
(text node
))
1059 | TopLevel
-> Id
(pos_name node env
)
1062 | YieldExpression
{ yield_operand
; _
} when text yield_operand
= "break" ->
1063 env
.saw_yield
<- true;
1065 | YieldExpression
{ yield_operand
; _
} when is_missing yield_operand
->
1066 env
.saw_yield
<- true;
1067 Yield
(AFvalue
(pos, Null
))
1068 | YieldExpression
{ yield_operand
; _
} ->
1069 env
.saw_yield
<- true;
1070 Yield
(pAField yield_operand env
)
1071 | YieldFromExpression
{ yield_from_operand
; _
} ->
1072 env
.saw_yield
<- true;
1073 Yield_from
(pExpr yield_from_operand env
)
1075 | DefineExpression
{ define_keyword
; define_argument_list
; _
} -> Call
1076 ( (let name = pos_name define_keyword env
in fst
name, Id
name)
1078 , List.map ~
f:(fun x
-> pExpr x env
) (as_list define_argument_list
)
1082 | ScopeResolutionExpression
1083 { scope_resolution_qualifier
; scope_resolution_name
; _
} ->
1085 match pExpr scope_resolution_qualifier env
with
1086 | p, Lvar v
when not env
.codegen
-> p, Id v
1089 begin match syntax scope_resolution_name
with
1090 | Token
{ Token.kind
= TK.Variable
; _
} ->
1092 ( pPos scope_resolution_name env
1093 , Lvar
(pos_name scope_resolution_name env
)
1096 Class_get
(qual, name)
1098 let name = pExpr scope_resolution_name env
in
1099 begin match snd
name with
1100 | String
id | Id
id -> Class_const
(qual, id)
1101 | _
-> Class_get
(qual, name)
1104 | CastExpression
{ cast_type
; cast_operand
; _
} ->
1105 Cast
(pHint cast_type env
, pExpr cast_operand env
)
1106 | ConditionalExpression
1107 { conditional_test
; conditional_consequence
; conditional_alternative
; _
}
1109 ( pExpr conditional_test env
1110 , mpOptional pExpr conditional_consequence env
1111 , pExpr conditional_alternative env
1113 | SubscriptExpression
{ subscript_receiver; subscript_index; _
} ->
1115 ( pExpr
subscript_receiver env
1116 , mpOptional pExpr
subscript_index env
1118 | EmbeddedSubscriptExpression
1119 { embedded_subscript_receiver
; embedded_subscript_index
; _
} ->
1121 ( pExpr embedded_subscript_receiver env
1122 , mpOptional (pExpr ~location
) embedded_subscript_index env
1124 | ShapeExpression
{ shape_expression_fields
; _
} ->
1126 couldMap ~
f:(mpShapeExpressionField pExpr
) shape_expression_fields env
1128 | ObjectCreationExpression
{ object_creation_object
= obj
; _
} ->
1131 { constructor_call_argument_list
; constructor_call_type
; _
} ->
1132 let args, varargs
= split_args_varargs constructor_call_argument_list
in
1134 ( (match syntax constructor_call_type
with
1135 | GenericTypeSpecifier
{ generic_class_type
; generic_argument_list
} ->
1136 let name = pos_name generic_class_type env
in
1138 match syntax generic_argument_list
with
1139 | TypeArguments
{ type_arguments_types
; _
}
1140 -> couldMap ~
f:pHint type_arguments_types env
1142 missing_syntax "generic type arguments" generic_argument_list env
1144 fst
name, Id_type_arguments
(name, hints)
1145 | SimpleTypeSpecifier _
->
1146 let name = pos_name constructor_call_type env
in fst
name, Id
name
1147 | _
-> pExpr constructor_call_type env
1153 { anonymous_class_argument_list
= args
1154 ; anonymous_class_extends_list
= extends
1155 ; anonymous_class_implements_list
= implements
1156 ; anonymous_class_body
=
1157 { syntax
= ClassishBody
{ classish_body_elements
= elts
; _
}; _
}
1159 let args, varargs
= split_args_varargs args in
1160 let c_mode = mode_annotation env
.fi_mode
in
1161 let c_user_attributes = [] in
1162 let c_final = false in
1163 let c_is_xhp = false in
1164 let c_name = pos, "anonymous" in
1165 let c_tparams = [] in
1166 let c_extends = couldMap ~
f:pHint extends env
in
1167 let c_implements = couldMap ~
f:pHint implements env
in
1168 let c_body = List.concat
(couldMap ~
f:pClassElt elts env
) in
1169 let c_namespace = Namespace_env.empty env
.parser_options
in
1170 let c_enum = None
in
1171 let c_span = pPos node env
in
1172 let c_kind = Cnormal
in
1173 let c_doc_comment = None
in
1190 NewAnonClass
(args, varargs
, cls)
1191 | GenericTypeSpecifier
1192 { generic_class_type
1193 ; generic_argument_list
1195 let name = pos_name generic_class_type env
in
1197 match syntax generic_argument_list
with
1198 | TypeArguments
{ type_arguments_types
; _
}
1199 -> couldMap ~
f:pHint type_arguments_types env
1201 missing_syntax "generic type arguments" generic_argument_list env
1203 Id_type_arguments
(name, hints)
1204 | LiteralExpression
{ literal_expression
= expr } ->
1205 (match syntax
expr with
1207 let s = text expr in
1208 (* We allow underscores while lexing the integer literals. This function gets
1209 * rid of them before the literal is created. *)
1210 let eliminate_underscores s = s
1211 |> Str.split
(Str.regexp
"_")
1212 |> String.concat
"" in
1213 (* TODO(17796330): Get rid of linter functionality in the lowerer *)
1214 if s <> String.lowercase_ascii
s then Lint.lowercase_constant
pos s;
1215 (match location
, token_kind expr with
1216 (* TODO(T21285960): Inside strings, int indices "should" be string indices *)
1217 | InDoubleQuotedString
, _
when env
.hhvm_compat_mode
-> String
(pos, mkStr unesc_dbl s)
1218 | InBacktickedString
, _
when env
.hhvm_compat_mode
-> String
(pos, mkStr Php_escaping.unescape_backtick
s)
1219 | _
, Some
TK.DecimalLiteral
1220 | _
, Some
TK.OctalLiteral
1221 | _
, Some
TK.HexadecimalLiteral
1222 | _
, Some
TK.BinaryLiteral
-> Int
(pos, eliminate_underscores s)
1223 | _
, Some
TK.FloatingLiteral
-> Float
(pos, s)
1224 | _
, Some
TK.SingleQuotedStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_single
s)
1225 | _
, Some
TK.DoubleQuotedStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_double
s)
1226 | _
, Some
TK.HeredocStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_heredoc
s)
1227 | _
, Some
TK.NowdocStringLiteral
-> String
(pos, mkStr Php_escaping.unescape_nowdoc
s)
1228 | _
, Some
TK.NullLiteral
-> Null
1229 | _
, Some
TK.BooleanLiteral
->
1230 (match String.lowercase_ascii
s with
1233 | _
-> missing_syntax ("boolean (not: " ^
s ^
")") expr env
1235 | _
, Some
TK.ExecutionStringLiteral
->
1236 Execution_operator
[pos, String
(pos, mkStr Php_escaping.unescape_backtick
s)]
1237 | _
-> missing_syntax "literal" expr env
1240 { syntax
= Token
{ Token.kind
= TK.ExecutionStringLiteralHead
; _
}; _
}
1242 Execution_operator
(pString2 InBacktickedString
(prepString2 ts
) env
)
1243 | SyntaxList ts
-> String2
(pString2 InDoubleQuotedString
(prepString2 ts
) env
)
1244 | _
-> missing_syntax "literal expression" expr env
1247 | InstanceofExpression
1248 { instanceof_left_operand
; instanceof_right_operand
; _
} ->
1250 match pExpr instanceof_right_operand env
with
1251 | p, Class_const
((_
, pid
), (_
, "")) -> p, pid
1254 InstanceOf
(pExpr instanceof_left_operand env
, ty)
1255 (* TODO: Priority fix? *)
1256 (*match pExpr instanceof_left_operand env with
1257 | p, Unop (o,e) -> Unop (0, (p, InstanceOf (e, ty)))
1258 | e -> InstanceOf (e, ty)
1261 { is_left_operand
; is_right_operand
; _
} ->
1262 Is
(pExpr is_left_operand env
, pHint is_right_operand env
)
1264 { anonymous_static_keyword
1265 ; anonymous_async_keyword
1266 ; anonymous_coroutine_keyword
1267 ; anonymous_parameters
1272 | Php7AnonymousFunction
1273 { php7_anonymous_static_keyword
= anonymous_static_keyword
1274 ; php7_anonymous_async_keyword
= anonymous_async_keyword
1275 ; php7_anonymous_coroutine_keyword
= anonymous_coroutine_keyword
1276 ; php7_anonymous_parameters
= anonymous_parameters
1277 ; php7_anonymous_type
= anonymous_type
1278 ; php7_anonymous_use
= anonymous_use
1279 ; php7_anonymous_body
= anonymous_body
1282 match syntax node
with
1283 | PrefixUnaryExpression
1284 { prefix_unary_operator
= op; prefix_unary_operand
= v
} ->
1285 pos_name v env
, token_kind op = Some
TK.Ampersand
1286 | Token _
-> pos_name node env
, false
1287 | _
-> missing_syntax "use variable" node env
1290 match syntax node
with
1291 | AnonymousFunctionUseClause
{ anonymous_use_variables
; _
} ->
1292 couldMap ~
f:pArg anonymous_use_variables
1293 | _
-> fun _env
-> []
1295 let suspension_kind =
1297 anonymous_async_keyword
1298 anonymous_coroutine_keyword
in
1300 mpYielding pFunctionBody anonymous_body
(non_tls env
)
1302 let doc_comment = match extract_docblock node
with
1303 | Some _
as doc_comment -> doc_comment
1304 | None
-> top_docblock
() in
1306 ( { (fun_template yield node
suspension_kind env
) with
1307 f_ret
= mpOptional pHint anonymous_type env
1308 ; f_params = couldMap ~
f:pFunParam anonymous_parameters env
1310 ; f_static
= not
(is_missing anonymous_static_keyword
)
1311 ; f_doc_comment
= doc_comment
1313 , try pUse anonymous_use env
with _
-> []
1316 | AwaitableCreationExpression
1317 { awaitable_async
; awaitable_coroutine
; awaitable_compound_statement
} ->
1318 let suspension_kind =
1319 mk_suspension_kind awaitable_async awaitable_coroutine
in
1320 let blk, yld
= mpYielding pFunctionBody awaitable_compound_statement env
in
1322 { (fun_template yld node
suspension_kind env
) with
1323 f_body = mk_noop (pPos awaitable_compound_statement env
) blk }
1325 Call
((pPos node env
, Lfun
body), [], [], [])
1328 { syntax
= XHPOpen
{ xhp_open_name
; xhp_open_attributes
; _
}; _
}
1331 env
.ignore_pos
<- false;
1333 let pos, name = pos_name xhp_open_name env
in
1337 syntax_of_token Token.(
1341 let aggregate_tokens node
=
1342 let rec search = function (* scroll through non-token things *)
1344 | t
:: xs
when token_kind t
= Some
TK.XHPComment
-> search xs
1345 | { syntax
= Token b
; _
} as t
:: xs
-> track t b None xs
1346 | x
:: xs
-> x
:: search xs
1347 and track t b oe
= function (* keep going through consecutive tokens *)
1348 | { syntax
= Token
e; _
} :: xs
-> track t b
(Some
e) xs
1349 | xs
-> Option.value_map oe ~default
:t ~
f:(combine b
) :: search xs
1351 search (as_list node
)
1353 let pEmbedded escaper node env
=
1354 match syntax node
with
1356 let p = pPos node env
in
1357 p, String
(p, escaper
(full_text node
))
1359 match pExpr node env
with
1360 | _
, BracedExpr
e -> e
1363 let pAttr = fun node env
->
1364 match syntax node
with
1365 | XHPSimpleAttribute
{ xhp_simple_attribute_name
; xhp_simple_attribute_expression
; _
} ->
1366 let name = pos_name xhp_simple_attribute_name env
in
1368 if is_braced_expression xhp_simple_attribute_expression
1369 && env
.fi_mode
= FileInfo.Mdecl
&& not env
.hhvm_compat_mode
1371 else pEmbedded unesc_xhp_attr xhp_simple_attribute_expression env
1373 Xhp_simple
(name, expr)
1374 | XHPSpreadAttribute
{ xhp_spread_attribute_expression
; _
} ->
1375 Xhp_spread
( pEmbedded unesc_xhp_attr xhp_spread_attribute_expression env
)
1376 | _
-> missing_syntax "XHP attribute" node env
1378 let attrs = couldMap ~
f:pAttr xhp_open_attributes env
in
1380 List.map ~
f:(fun x
-> pEmbedded unesc_xhp x env
) (aggregate_tokens body)
1382 Xml
(name, attrs, exprs)
1383 (* FIXME; should this include Missing? ; "| Missing -> Null" *)
1384 | _
-> missing_syntax "expression" node env
1386 let outer_unsafes = env
.unsafes
in
1388 match leading_token node
with
1392 Token.filter_leading_trivia_by_kind t
TriviaKind.UnsafeExpression
1394 let f acc u
= ISet.add
(Trivia.start_offset u
) acc
in
1395 let us = List.fold ~
f ~init
:ISet.empty
us in
1396 (* Have we already dealt with 'these' UNSAFE_EXPRs? *)
1397 let us = ISet.diff
us outer_unsafes in
1398 let safe = ISet.is_empty
us in
1399 if not
safe then env
.unsafes
<- ISet.union
us outer_unsafes;
1403 match syntax node
with
1404 | BracedExpression
{ braced_expression_expression
= expr; _
}
1405 | ParenthesizedExpression
{ parenthesized_expression_expression
= expr; _
}
1407 * Peeling off braced or parenthesised expresions. When there is XHP
1408 * inside, we want the XHP node to have this own positions, rather than
1409 * those of enclosing parenthesised/braced expressions.
1411 let inner = pExpr ~location
expr env
in
1412 if Syntax.is_braced_expression node
1414 (* We elide the braces in {$x}, as it makes compilation easier *)
1415 begin match inner with
1416 | _
, (Lvar _
| String _
| Int _
| Float _
) -> inner
1417 | p, _
-> p, BracedExpr
inner
1422 * Since we need positions in XHP, regardless of the ignore_pos flag, we
1423 * parenthesise the call to pExpr_ so that the XHP expression case can
1424 * flip the switch. The key part is that `pPos` happens before the old
1425 * setting is restored.
1427 let local_ignore_pos = env
.ignore_pos
in
1428 let expr_ = pExpr_ node env
in
1429 let p = pPos node env
in
1430 env
.ignore_pos
<- local_ignore_pos;
1433 if is_safe then result else fst
result, Unsafeexpr
result
1434 and pBlock
: block parser
= fun node env
->
1435 let rec fix_last acc
= function
1436 | x
:: (_
:: _
as xs
) -> fix_last (x
::acc
) xs
1437 | [_
, Block block
] -> List.rev acc
@ block
1438 | stmt
-> List.rev acc
@ stmt
1440 let stmt = pStmtUnsafe node env
in
1442 and pFunctionBody
: block parser
= fun node env
->
1443 match syntax node
with
1445 | CompoundStatement _
->
1446 let block = pBlock node env
in
1447 if not env
.top_level_statements
1448 && ( env
.fi_mode
= FileInfo.Mdecl
&& not env
.hhvm_compat_mode
1450 then [ Pos.none
, Noop
]
1453 let p, r
= pExpr node env
in
1454 [p, Return
(Some
(p, r
))]
1455 and pStmtUnsafe
: stmt list parser
= fun node env
->
1456 let stmt = pStmt node env
in
1457 match leading_token node
with
1458 | Some t
when Token.has_trivia_kind t
TriviaKind.Unsafe
-> [Pos.none
, Unsafe
; stmt]
1460 and pStmt
: stmt parser
= fun node env
->
1461 extract_and_push_docblock node
;
1462 let pos = pPos node env
in
1463 let result = match syntax node
with
1464 | SwitchStatement
{ switch_expression
; switch_sections
; _
} ->
1465 let pSwitchLabel : (block -> case
) parser
= fun node env cont
->
1466 match syntax node
with
1467 | CaseLabel
{ case_expression
; _
} ->
1468 Case
(pExpr case_expression env
, cont
)
1469 | DefaultLabel _
-> Default cont
1470 | _
-> missing_syntax "pSwitchLabel" node env
1472 let pSwitchSection : case list parser
= fun node env
->
1473 match syntax node
with
1475 { switch_section_labels
1476 ; switch_section_statements
1477 ; switch_section_fallthrough
1479 let rec null_out cont
= function
1481 | (x
::xs
) -> x
[] :: null_out cont xs
1482 | _
-> raise
(Failure
"Malformed block result")
1484 let blk = List.concat
@@
1485 couldMap ~
f:pStmtUnsafe switch_section_statements env
in
1487 if is_missing switch_section_fallthrough
1489 else blk @ [Pos.none
, Fallthrough
]
1491 null_out blk (couldMap ~
f:pSwitchLabel switch_section_labels env
)
1492 | _
-> missing_syntax "switch section" node env
1496 ( pExpr switch_expression env
1497 , List.concat
@@ couldMap ~
f:pSwitchSection switch_sections env
1500 { if_condition
; if_statement
; if_elseif_clauses
; if_else_clause
; _
} ->
1501 (* Because consistency is for the weak-willed, Parser_hack does *not*
1502 * produce `Noop`s for compound statements **in if statements**
1504 let de_noop = function
1505 | [p, Block
[_
, Noop
]] -> [p, Block
[]]
1508 let if_condition = pExpr
if_condition env
in
1509 let if_statement = de_noop (pStmtUnsafe
if_statement env
) in
1510 let if_elseif_statement =
1511 let pElseIf : (block -> block) parser
= fun node env
->
1512 match syntax node
with
1513 | ElseifClause
{ elseif_condition
; elseif_statement
; _
} ->
1515 let elseif_condition = pExpr
elseif_condition env
in
1516 let elseif_statement = de_noop (pStmtUnsafe
elseif_statement env
) in
1517 [ pos, If
(elseif_condition, elseif_statement, next_clause
) ]
1518 | _
-> missing_syntax "elseif clause" node env
1520 List.fold_right ~
f:(@@)
1521 (couldMap ~
f:pElseIf if_elseif_clauses env
)
1522 ~init
:( match syntax if_else_clause
with
1523 | ElseClause
{ else_statement
; _
} ->
1524 de_noop (pStmtUnsafe else_statement env
)
1525 | Missing
-> [Pos.none
, Noop
]
1526 | _
-> missing_syntax "else clause" if_else_clause env
1529 pos, If
(if_condition, if_statement, if_elseif_statement)
1530 | ExpressionStatement
{ expression_statement_expression
; _
} ->
1531 if is_missing expression_statement_expression
1533 else pos, Expr
(pExpr expression_statement_expression env
)
1534 | CompoundStatement
{ compound_statements
; compound_right_brace
; _
} ->
1536 match leading_token compound_right_brace
with
1537 | Some t
when Token.has_trivia_kind t
TriviaKind.Unsafe
-> [pos, Unsafe
]
1540 handle_loop_body
pos compound_statements
tail env
1541 | AlternateLoopStatement
{ alternate_loop_statements
; _
} ->
1542 handle_loop_body
pos alternate_loop_statements
[] env
1543 | ThrowStatement
{ throw_expression
; _
} ->
1544 pos, Throw
(pExpr throw_expression env
)
1545 | DoStatement
{ do_body
; do_condition
; _
} ->
1546 pos, Do
([pos, Block
(pBlock do_body env
)], pExpr do_condition env
)
1547 | WhileStatement
{ while_condition
; while_body
; _
} ->
1548 pos, While
(pExpr while_condition env
, pStmtUnsafe while_body env
)
1549 | DeclareDirectiveStatement
{ declare_directive_expression
; _
} ->
1550 pos, Declare
(false, pExpr declare_directive_expression env
, [])
1551 | DeclareBlockStatement
{ declare_block_expression
; declare_block_body
; _
} ->
1552 pos, Declare
(true, pExpr declare_block_expression env
,
1553 pStmtUnsafe declare_block_body env
)
1554 | UsingStatementBlockScoped
1555 { using_block_await_keyword
1556 ; using_block_expressions
1560 us_is_block_scoped
= true;
1561 us_has_await
= not
(is_missing using_block_await_keyword
);
1562 us_expr
= pExprL using_block_expressions env
;
1563 us_block
= [pos, Block
(pBlock using_block_body env
)];
1565 | UsingStatementFunctionScoped
1566 { using_function_await_keyword
1567 ; using_function_expression
1569 (* All regular function scoped using statements should
1570 * be rewritten by this point
1571 * If this gets run, it means that this using statement is the only one
1572 * in the block, hence it is not in a compound statement *)
1574 us_is_block_scoped
= false;
1575 us_has_await
= not
(is_missing using_function_await_keyword
);
1576 us_expr
= pExpr using_function_expression env
;
1577 us_block
= [Pos.none
, Block
[Pos.none
, Noop
]];
1580 { for_initializer
; for_control
; for_end_of_loop
; for_body
; _
} ->
1581 let ini = pExprL for_initializer env
in
1582 let ctr = pExprL for_control env
in
1583 let eol = pExprL for_end_of_loop env
in
1584 let blk = pStmtUnsafe for_body env
in
1585 pos, For
(ini, ctr, eol, blk)
1587 { foreach_collection
1588 ; foreach_await_keyword
1593 let col = pExpr foreach_collection env
in
1595 if is_specific_token
TK.Await foreach_await_keyword
1596 then Some
(pPos foreach_await_keyword env
)
1600 let value = pExpr foreach_value env
in
1601 Option.value_map
(mpOptional pExpr foreach_key env
)
1602 ~default
:(As_v
value)
1603 ~
f:(fun key
-> As_kv
(key
, value))
1606 match pStmtUnsafe foreach_body env
with
1607 | [p, Block
[_
, Noop
]] -> [p, Block
[]]
1610 pos, Foreach
(col, akw, akv, blk)
1612 { try_compound_statement
; try_catch_clauses
; try_finally_clause
; _
} ->
1614 ( [ pPos try_compound_statement env
, Block
(pBlock try_compound_statement env
) ]
1615 , couldMap try_catch_clauses env ~
f:begin fun node env
->
1616 match syntax node
with
1617 | CatchClause
{ catch_type
; catch_variable
; catch_body
; _
} ->
1618 ( pos_name catch_type env
1619 , pos_name catch_variable env
1620 , [ pPos catch_body env
, Block
(mpStripNoop pBlock catch_body env
) ]
1622 | _
-> missing_syntax "catch clause" node env
1624 , match syntax try_finally_clause
with
1625 | FinallyClause
{ finally_body
; _
} ->
1626 [ pPos finally_body env
, Block
(pBlock finally_body env
) ]
1629 | FunctionStaticStatement
{ static_declarations
; _
} ->
1630 let pStaticDeclarator node env
=
1631 match syntax node
with
1632 | StaticDeclarator
{ static_name
; static_initializer
} ->
1634 match pExpr static_name env
with
1635 | p, Id
(p'
, s) -> p, Lvar
(p'
, s)
1638 (match syntax static_initializer
with
1639 | SimpleInitializer
{ simple_initializer_value
; _
} ->
1640 ( pPos static_initializer env
1641 , Binop
(Eq None
, lhs, pExpr simple_initializer_value env
)
1645 | _
-> missing_syntax "static declarator" node env
1647 pos, Static_var
(couldMap ~
f:pStaticDeclarator static_declarations env
)
1648 | ReturnStatement
{ return_expression
; return_keyword
; _
} ->
1649 let expr = mpOptional pExpr return_expression env
in
1651 | Syntax.GotoLabel
{ goto_label_name
; _
} ->
1652 let pos_label = pPos goto_label_name env
in
1653 let label_name = text goto_label_name
in
1654 pos, Ast.GotoLabel
(pos_label, label_name)
1655 | GotoStatement
{ goto_statement_label_name
; _
} ->
1656 pos, Goto
(pos_name goto_statement_label_name env
)
1657 | EchoStatement
{ echo_keyword
= kw
; echo_expressions
= exprs; _
}
1658 | UnsetStatement
{ unset_keyword
= kw
; unset_variables
= exprs; _
}
1662 ( (match syntax kw
with
1664 | SimpleTypeSpecifier _
1666 -> let name = pos_name kw env
in fst
name, Id
name
1667 | _
-> missing_syntax "id" kw env
1670 , couldMap ~
f:pExpr
exprs env
1673 | BreakStatement
{ break_level
=level
; _
} ->
1674 pos, Break
(pBreak_or_continue_level env level
)
1675 | ContinueStatement
{ continue_level
=level
; _
} ->
1676 pos, Continue
(pBreak_or_continue_level env level
)
1677 | GlobalStatement
{ global_variables
; _
} ->
1678 pos, Global_var
(couldMap ~
f:(pExpr ~location
:InGlobalVar
) global_variables env
)
1679 | MarkupSection _
-> pMarkup node env
1680 | _
when env
.max_depth
> 0 ->
1681 (* OCaml optimisers; Forgive them, for they know not what they do!
1683 * The max_depth is only there to stop the *optimised* version from an
1684 * unbounded recursion. Sad times.
1686 let outer_max_depth = env
.max_depth
in
1687 let () = env
.max_depth
<- outer_max_depth - 1 in
1689 match pDef node env
with
1690 | [def
] -> pos, Def_inline def
1691 | _
-> failwith
"This should be impossible; inline definition was list."
1693 let () = env
.max_depth
<- outer_max_depth in
1695 | _
-> missing_syntax "statement" node env
in
1699 and pMarkup node env
=
1700 match syntax node
with
1701 | MarkupSection
{ markup_text
; markup_expression
; _
} ->
1703 match syntax markup_expression
with
1705 | ExpressionStatement
{
1706 expression_statement_expression
= e
1707 ; _
} -> Some
(pExpr
e env
)
1708 | _
-> failwith
"expression expected"
1710 pPos node env
, Markup
((pPos node env
, text markup_text
), expr)
1711 | _
-> failwith
"invalid node"
1713 and pBreak_or_continue_level env level
=
1714 mpOptional pExpr level env
1716 and pTConstraintTy
: hint parser
= fun node
->
1717 match syntax node
with
1718 | TypeConstraint
{ constraint_type
; _
} -> pHint constraint_type
1719 | _
-> missing_syntax "type constraint" node
1721 and pTConstraint
: (constraint_kind
* hint
) parser
= fun node env
->
1722 match syntax node
with
1723 | TypeConstraint
{ constraint_keyword
; constraint_type
} ->
1724 ( (match token_kind constraint_keyword
with
1725 | Some
TK.As
-> Constraint_as
1726 | Some
TK.Super
-> Constraint_super
1727 | Some
TK.Equal
-> Constraint_eq
1728 | _
-> missing_syntax "constraint operator" constraint_keyword env
1730 , pHint constraint_type env
1732 | _
-> missing_syntax "type constraint" node env
1734 and pTParaml
: tparam list parser
= fun node env
->
1735 let pTParam : tparam parser
= fun node env
->
1736 match syntax node
with
1737 | TypeParameter
{ type_variance
; type_name
; type_constraints
} ->
1738 ( (match token_kind type_variance
with
1739 | Some
TK.Plus
-> Covariant
1740 | Some
TK.Minus
-> Contravariant
1743 , pos_name type_name env
1744 , couldMap ~
f:pTConstraint type_constraints env
1746 | _
-> missing_syntax "type parameter" node env
1748 match syntax node
with
1750 | TypeParameters
{ type_parameters_parameters
; _
} ->
1751 couldMap ~
f:pTParam type_parameters_parameters env
1752 | _
-> missing_syntax "type parameter list" node env
1754 and pFunHdr
: fun_hdr parser
= fun node env
->
1755 match syntax node
with
1756 | FunctionDeclarationHeader
1757 { function_modifiers
1758 ; function_ampersand
1760 ; function_where_clause
1761 ; function_type_parameter_list
1762 ; function_parameter_list
1765 let modifiers = pModifiers function_modifiers env
in
1766 let fh_parameters = couldMap ~
f:pFunParam function_parameter_list env
in
1767 let fh_return_type = mpOptional pHint function_type env
in
1768 let fh_suspension_kind =
1769 mk_suspension_kind_ modifiers.has_async
modifiers.has_coroutine
in
1770 let fh_name = pos_name function_name env
in
1772 match syntax function_where_clause
with
1774 | WhereClause
{ where_clause_constraints
; _
} ->
1776 match syntax node
with
1777 | ListItem
{ list_item
; _
} -> f list_item
1779 { where_constraint_left_type
1780 ; where_constraint_operator
1781 ; where_constraint_right_type
1783 let l = pHint where_constraint_left_type env
in
1785 match syntax where_constraint_operator
with
1786 | Token
{ Token.kind
= TK.Equal
; _
} -> Constraint_eq
1787 | Token
{ Token.kind
= TK.As
; _
} -> Constraint_as
1788 | Token
{ Token.kind
= TK.Super
; _
} -> Constraint_super
1789 | _
-> missing_syntax "constraint operator" where_constraint_operator env
1791 let r = pHint where_constraint_right_type env
in
1793 | _
-> missing_syntax "where constraint" node env
1795 List.map ~
f (syntax_node_to_list where_clause_constraints
)
1796 | _
-> missing_syntax "function header constraints" node env
1798 let fh_type_parameters = pTParaml function_type_parameter_list env
in
1799 let fh_param_modifiers =
1800 List.filter ~
f:(fun p -> Option.is_some
p.param_modifier
) fh_parameters
1802 let fh_ret_by_ref = is_ret_by_ref function_ampersand
in
1803 { fh_suspension_kind
1806 ; fh_type_parameters
1809 ; fh_param_modifiers
1812 | LambdaSignature
{ lambda_parameters
; lambda_type
; _
} ->
1813 { empty_fun_hdr with
1814 fh_parameters = couldMap ~
f:pFunParam lambda_parameters env
1815 ; fh_return_type = mpOptional pHint lambda_type env
1817 | Token _
-> empty_fun_hdr
1818 | _
-> missing_syntax "function header" node env
1820 and docblock_stack
= Stack.create
()
1822 and extract_docblock
= fun node
->
1823 let source_text = leading_text node
in
1824 let parse (str
: string) : string option =
1825 let length = String.length str
in
1826 let mk (start : int) (end_ : int) : string =
1827 String.sub
source_text start (end_ - start + 1)
1829 let rec go start state idx
: string option =
1830 if idx
= length (* finished? *)
1833 let next = idx
+ 1 in
1834 match state
, str
.[idx
] with
1835 | `LineCmt
, '
\n'
-> go next `Free
next
1836 | `EndEmbedded
, '
/'
-> go next `Free
next
1837 | `EndDoc
, '
/'
-> begin match go next `Free
next with
1838 | Some doc
-> Some doc
1839 | None
-> Some
(mk start idx
)
1841 (* PHP has line comments delimited by a # *)
1842 | `Free
, '#'
-> go next `LineCmt
next
1843 (* All other comment delimiters start with a / *)
1844 | `Free
, '
/'
-> go idx `SawSlash
next
1845 (* After a / in trivia, we must see either another / or a * *)
1846 | `SawSlash
, '
/'
-> go next `LineCmt
next
1847 | `SawSlash
, '
*'
-> go start `MaybeDoc
next
1848 | `MaybeDoc
, '
*'
-> go start `MaybeDoc2
next
1849 | `MaybeDoc
, _
-> go start `EmbeddedCmt
next
1850 | `MaybeDoc2
, '
/'
-> go next `Free
next
1851 | `MaybeDoc2
, _
-> go start `DocComment
next
1852 | `DocComment
, '
*'
-> go start `EndDoc
next
1853 | `DocComment
, _
-> go start `DocComment
next
1854 | `EndDoc
, _
-> go start `DocComment
next
1855 (* A * without a / does not end an embedded comment *)
1856 | `EmbeddedCmt
, '
*'
-> go start `EndEmbedded
next
1857 | `EndEmbedded
, '
*'
-> go start `EndEmbedded
next
1858 | `EndEmbedded
, _
-> go start `EmbeddedCmt
next
1859 (* Whitespace skips everywhere else *)
1860 | _
, (' '
| '
\t'
| '
\n'
) -> go start state
next
1861 (* When scanning comments, anything else is accepted *)
1862 | `LineCmt
, _
-> go start state
next
1863 | `EmbeddedCmt
, _
-> go start state
next
1864 (* Anything else; bail *)
1869 in (* Now that we have a parser *)
1870 parse (leading_text node
)
1872 and extract_and_push_docblock node
=
1873 let docblock = extract_docblock node
in
1874 Stack.push
docblock docblock_stack
1876 and handle_loop_body
pos stmts
tail env
=
1877 let rec conv acc stmts
=
1879 | [] -> List.concat
@@ List.rev acc
1880 | { syntax
= UsingStatementFunctionScoped
1881 { using_function_await_keyword
= await_kw
1882 ; using_function_expression
= expression
1885 let body = conv [] rest
in
1887 us_is_block_scoped
= false;
1888 us_has_await
= not
(is_missing await_kw
);
1889 us_expr
= pExprL expression env
;
1890 us_block
= [pos, Block
body]; } in
1891 List.concat
@@ List.rev
([pos, using] :: acc
)
1893 let h = pStmtUnsafe
h env
in
1894 conv (h :: acc
) rest
1896 let blk = conv [] (as_list stmts
) in
1897 begin match List.filter ~
f:(fun (_
, x
) -> x
<> Noop
) blk @ tail with
1898 | [] -> pos, Block
[Pos.none
, Noop
]
1899 | blk -> pos, Block
blk
1902 and pop_docblock
() =
1904 let _ = Stack.pop docblock_stack
in ()
1908 and top_docblock
() =
1910 Stack.top docblock_stack
1912 | Stack.Empty
-> None
1914 and pClassElt
: class_elt list parser
= fun node env
->
1915 let doc_comment_opt = extract_docblock node
in
1916 match syntax node
with
1918 { const_abstract
; const_type_specifier
; const_declarators
; _ } ->
1919 let ty = mpOptional pHint const_type_specifier env
in
1921 couldMap const_declarators env ~
f:begin function
1922 | { syntax
= ConstantDeclarator
1923 { constant_declarator_name
; constant_declarator_initializer
}
1925 ( pos_name constant_declarator_name env
1926 (* TODO: Parse error when const is abstract and has inits *)
1927 , if is_missing const_abstract
1928 then mpOptional pSimpleInitializer constant_declarator_initializer env
1931 | node
-> missing_syntax "constant declarator" node env
1934 let rec aux absts concrs
= function
1935 | (id, None
) :: xs
-> aux (AbsConst
(ty, id) :: absts
) concrs xs
1936 | (id, Some x
) :: xs
-> aux absts
((id, x
) :: concrs
) xs
1937 | [] when concrs
= [] -> List.rev absts
1938 | [] -> Const
(ty, List.rev concrs
) :: List.rev absts
1941 | TypeConstDeclaration
1942 { type_const_abstract
1944 ; type_const_type_parameters
1945 ; type_const_type_constraint
1946 ; type_const_type_specifier
1949 { tconst_abstract
= not
(is_missing type_const_abstract
)
1950 ; tconst_name
= pos_name type_const_name env
1951 ; tconst_tparams
= pTParaml type_const_type_parameters env
1952 ; tconst_constraint
= mpOptional pTConstraintTy type_const_type_constraint env
1953 ; tconst_type
= mpOptional pHint type_const_type_specifier env
1954 ; tconst_span
= pPos node env
1957 | PropertyDeclaration
1958 { property_modifiers
; property_type
; property_declarators
; _ } ->
1959 (* TODO: doc comments do not have to be at the beginning, they can go in
1960 * the middle of the declaration, to be associated with individual
1961 * properties, right now we don't handle this *)
1962 let doc_comment_opt = extract_docblock node
in
1964 { cv_kinds
= pKinds property_modifiers env
1965 ; cv_hint
= mpOptional pHint property_type env
1966 ; cv_is_promoted_variadic
= false
1967 ; cv_names
= couldMap property_declarators env ~
f:begin fun node env
->
1968 match syntax node
with
1969 | PropertyDeclarator
{ property_name
; property_initializer
} ->
1970 ( let _, n as name = pos_name property_name env
in
1973 then drop_pstr 1 name
1976 , mpOptional pSimpleInitializer property_initializer env
1979 | _ -> missing_syntax "property declarator" node env
1981 ; cv_doc_comment
= if env
.quick_mode
then None
else doc_comment_opt
1982 ; cv_user_attributes
= [] (* TODO figure out how to inject attributes *)
1985 | MethodishDeclaration
1986 { methodish_attribute
1987 ; methodish_function_decl_header
= {
1988 syntax
= FunctionDeclarationHeader
h; _
1990 ; methodish_function_body
1992 let classvar_init : fun_param
-> stmt * class_elt
= fun param
->
1993 let p, _ as cvname
= drop_pstr 1 param
.param_id
in (* Drop the '$' *)
1995 match param
.param_expr
with
1996 | Some
(pos_end, _) -> Pos.btw
p pos_end
1999 ( (p, Expr
(p, Binop
(Eq None
,
2000 (p, Obj_get
((p, Lvar
(p, "$this")), (p, Id cvname
), OG_nullthrows
)),
2001 (p, Lvar param
.param_id
))
2004 { cv_kinds
= Option.to_list param
.param_modifier
2005 ; cv_hint
= param
.param_hint
2006 ; cv_is_promoted_variadic
= param
.param_is_variadic
2007 ; cv_names
= [span, cvname
, None
]
2008 ; cv_doc_comment
= None
2009 ; cv_user_attributes
= param
.param_user_attributes
2013 let hdr = pFunHdr header env
in
2014 let member_init, member_def
=
2016 List.filter_map
hdr.fh_parameters ~
f:(fun p ->
2017 Option.map ~
f: (fun _ -> classvar_init p) p.param_modifier
2020 let pBody = fun node env
->
2021 let body = pFunctionBody node env
in
2023 if env
.hhvm_compat_mode
|| env
.codegen
2024 then List.rev
member_init
2029 let body, body_has_yield
= mpYielding pBody methodish_function_body env
in
2030 let kind = pKinds h.function_modifiers env
in
2031 member_def
@ [Method
2033 ; m_tparams
= hdr.fh_type_parameters
2034 ; m_constrs
= hdr.fh_constrs
2035 ; m_name
= hdr.fh_name
2036 ; m_params
= hdr.fh_parameters
2038 ; m_user_attributes
= List.concat
@@
2039 couldMap ~
f:pUserAttribute methodish_attribute env
2040 ; m_ret
= hdr.fh_return_type
2041 ; m_ret_by_ref
= hdr.fh_ret_by_ref
2042 ; m_span
= pFunction node env
2043 ; m_fun_kind
= mk_fun_kind hdr.fh_suspension_kind body_has_yield
2044 ; m_doc_comment
= doc_comment_opt
2046 | TraitUseConflictResolution
2047 { trait_use_conflict_resolution_names
2048 ; trait_use_conflict_resolution_clauses
2051 let pTraitUseConflictResolutionItem node env
=
2052 match syntax node
with
2053 | TraitUsePrecedenceItem
2054 { trait_use_precedence_item_name
= name
2055 ; trait_use_precedence_item_removed_names
= removed_names
2058 let qualifier, name =
2059 match syntax
name with
2060 | ScopeResolutionExpression
2061 { scope_resolution_qualifier
; scope_resolution_name
; _ } ->
2062 pos_name scope_resolution_qualifier env
,
2063 pos_name scope_resolution_name env
2064 | _ -> missing_syntax "trait use precedence item" node env
2067 couldMap ~
f:(fun n _e
-> pos_name n env
) removed_names env
2069 ClassUsePrecedence
(qualifier, name, removed_names)
2071 { trait_use_alias_item_aliasing_name
= aliasing_name
2072 ; trait_use_alias_item_modifiers
= modifiers
2073 ; trait_use_alias_item_aliased_name
= aliased_name
2076 let qualifier, name =
2077 match syntax aliasing_name
with
2078 | ScopeResolutionExpression
2079 { scope_resolution_qualifier
; scope_resolution_name
; _ } ->
2080 Some
(pos_name scope_resolution_qualifier env
),
2081 pos_name scope_resolution_name env
2082 | _ -> None
, pos_name aliasing_name env
2084 let modifiers = pKinds modifiers env
in
2085 let is_visibility = function
2086 | Public
| Private
| Protected
-> true
2089 if List.is_empty
modifiers || List.exists
modifiers ~
f:is_visibility
2091 else Public
:: modifiers in
2093 Option.some_if
(not
(is_missing
aliased_name)) (pos_name aliased_name env
)
2095 ClassUseAlias
(qualifier, name, aliased_name, modifiers)
2096 | _ -> missing_syntax "trait use conflict resolution item" node env
2098 (couldMap ~
f:(fun n e ->
2099 ClassUse
(pHint n e)) trait_use_conflict_resolution_names env
)
2100 @ (couldMap ~
f:pTraitUseConflictResolutionItem
2101 trait_use_conflict_resolution_clauses env
)
2102 | TraitUse
{ trait_use_names
; _ } ->
2103 couldMap ~
f:(fun n e -> ClassUse
(pHint n e)) trait_use_names env
2104 | RequireClause
{ require_kind
; require_name
; _ } ->
2106 ( (match token_kind require_kind
with
2107 | Some
TK.Implements
-> MustImplement
2108 | Some
TK.Extends
-> MustExtend
2109 | _ -> missing_syntax "trait require kind" require_kind env
2111 , pHint require_name env
2114 | XHPClassAttributeDeclaration
{ xhp_attribute_attributes
; _ } ->
2115 let pXHPAttr node env
=
2116 match syntax node
with
2118 { xhp_attribute_decl_type
= ty
2119 ; xhp_attribute_decl_name
= name
2120 ; xhp_attribute_decl_initializer
= init
2121 ; xhp_attribute_decl_required
= req
2123 let (p, name) = pos_name name env
in
2125 ( mpOptional pHint ty env
2126 , (Pos.none
, (p, ":" ^
name), mpOptional pSimpleInitializer init env
)
2127 , not
(is_missing req
)
2128 , match syntax
ty with
2129 | XHPEnumType
{ xhp_enum_optional
; xhp_enum_values
; _ } ->
2130 let p = pPos ty env
in
2131 let opt = not
(is_missing xhp_enum_optional
) in
2132 let vals = couldMap ~
f:pExpr xhp_enum_values env
in
2136 | XHPSimpleClassAttribute
{ xhp_simple_class_attribute_type
= attr
} ->
2137 XhpAttrUse
(pPos attr env
, Happly
(pos_name attr env
, []))
2139 XhpAttrUse
(pPos node env
, Happly
(pos_name node env
, []))
2140 | _ -> missing_syntax "XHP attribute" node env
2142 couldMap ~
f:pXHPAttr xhp_attribute_attributes env
2143 | XHPChildrenDeclaration
{ xhp_children_expression
; _; } ->
2144 [ XhpChild
(pXhpChild xhp_children_expression env
) ]
2145 | XHPCategoryDeclaration
{ xhp_category_categories
= cats
; _ } ->
2146 let pNameSansPercent node _env
= drop_pstr 1 (pos_name node env
) in
2147 [ XhpCategory
(couldMap ~
f:pNameSansPercent cats env
) ]
2148 | _ -> missing_syntax "expression" node env
2150 and pXhpChild
: xhp_child parser
= fun node env
->
2151 match syntax node
with
2152 | Token t
-> ChildName
(pos_name node env
)
2153 | PostfixUnaryExpression
{ postfix_unary_operand
; postfix_unary_operator
;} ->
2154 let operand = pXhpChild postfix_unary_operand env
in
2157 match token_kind postfix_unary_operator
with
2158 | Some
TK.Question
-> ChildQuestion
2159 | Some
TK.Plus
-> ChildPlus
2160 | Some
TK.Star
-> ChildStar
2161 | _ -> missing_syntax "xhp children operator" node env
2163 ChildUnary
(operand, operator)
2165 { binary_left_operand
; binary_right_operand
; _ } ->
2166 let left = pXhpChild binary_left_operand env
in
2167 let right = pXhpChild binary_right_operand env
in
2168 ChildBinary
(left, right)
2169 | XHPChildrenParenthesizedList
{xhp_children_list_xhp_children
; _} ->
2170 let children = as_list xhp_children_list_xhp_children
in
2171 let children = List.map ~
f:(fun x
-> pXhpChild x env
) children in
2173 | _ -> missing_syntax "xhp children" node env
2176 (*****************************************************************************(
2177 * Parsing definitions (AST's `def`)
2178 )*****************************************************************************)
2179 and pNamespaceUseClause ~prefix env
kind node
=
2180 match syntax node
with
2181 | NamespaceUseClause
2182 { namespace_use_name
= name
2183 ; namespace_use_alias
= alias
2184 ; namespace_use_clause_kind
= clause_kind
2187 match prefix
, pos_name name env
with
2188 | None
, (p, n) -> (p, n)
2189 | Some prefix
, (p, n) -> p, (snd
@@ pos_name prefix env
) ^
n
2191 let x = Str.search_forward
(Str.regexp
"[^\\\\]*$") n 0 in
2192 let key = drop_pstr x name in
2193 let kind = if is_missing clause_kind
then kind else clause_kind
in
2194 let alias = if is_missing
alias then key else pos_name alias env
in
2195 begin match String.lowercase_ascii
(snd
alias) with
2196 | "null" | "true" | "false" -> env
.saw_std_constant_redefinition
<- true
2200 match syntax
kind with
2201 | Token
{ Token.kind = TK.Namespace
; _ } -> NSNamespace
2202 | Token
{ Token.kind = TK.Type
; _ } -> NSClass
2203 | Token
{ Token.kind = TK.Function
; _ } -> NSFun
2204 | Token
{ Token.kind = TK.Const
; _ } -> NSConst
2205 | Missing
-> NSClassAndNamespace
2206 | _ -> missing_syntax "namespace use kind" kind env
2209 , (p, if n.[0] = '
\\'
then n else "\\" ^
n)
2212 | _ -> missing_syntax "namespace use clause" node env
2214 and pDef
: def list parser
= fun node env
->
2215 let doc_comment_opt = extract_docblock node
in
2216 match syntax node
with
2217 | FunctionDeclaration
2218 { function_attribute_spec
; function_declaration_header
; function_body
} ->
2219 let env = non_tls env in
2220 let hdr = pFunHdr function_declaration_header
env in
2222 if is_semicolon function_body
then [], false else
2223 mpYielding pFunctionBody function_body
env
2226 { (fun_template yield node
hdr.fh_suspension_kind env) with
2227 f_tparams
= hdr.fh_type_parameters
2228 ; f_ret
= hdr.fh_return_type
2229 ; f_constrs
= hdr.fh_constrs
2230 ; f_name
= hdr.fh_name
2231 ; f_params = hdr.fh_parameters
2232 ; f_ret_by_ref
= hdr.fh_ret_by_ref
2235 let containsUNSAFE node
=
2236 let tokens = all_tokens node
in
2237 let has_unsafe t
= Token.has_trivia_kind t
TriviaKind.Unsafe
in
2238 List.exists ~
f:has_unsafe tokens
2241 | [p, Noop
] when containsUNSAFE function_body
-> [p, Unsafe
]
2244 ; f_user_attributes
=
2245 List.concat
@@ couldMap ~
f:pUserAttribute function_attribute_spec
env
2246 ; f_doc_comment
= doc_comment_opt
2248 | ClassishDeclaration
2249 { classish_attribute
= attr
2250 ; classish_modifiers
= mods
2251 ; classish_keyword
= kw
2252 ; classish_name
= name
2253 ; classish_type_parameters
= tparaml
2254 ; classish_extends_list
= exts
2255 ; classish_implements_list
= impls
2257 { syntax
= ClassishBody
{ classish_body_elements
= elts
; _ }; _ }
2259 let env = non_tls env in
2260 let c_mode = mode_annotation env.fi_mode
in
2261 let c_user_attributes = List.concat
@@ couldMap ~
f:pUserAttribute attr
env in
2262 let c_final = List.mem
(pKinds mods
env) Final
in
2264 match token_kind name with
2265 | Some
(TK.XHPElementName
| TK.XHPClassName
) -> true
2268 let c_name = pos_name name env in
2269 let c_tparams = pTParaml tparaml
env in
2270 let c_extends = couldMap ~
f:pHint exts
env in
2271 let c_implements = couldMap ~
f:pHint impls
env in
2273 let rec aux acc ns
=
2276 | { syntax
= PropertyDeclaration
{ property_modifiers
; _ }; _ } :: _
2277 when not
env.codegen
&& not
env.hhvm_compat_mode
&&
2278 List.exists ~
f:Syntax.is_var
(as_list property_modifiers
) ->
2279 (* Break off remaining class body parse; legacy compliance *)
2282 let elt = pClassElt
n env in
2285 List.concat
@@ List.rev
(aux [] (as_list elts
))
2287 let c_namespace = Namespace_env.empty
env.parser_options
in
2288 let c_enum = None
in
2289 let c_span = pPos node
env in
2291 let is_abs = Str.(string_match
(regexp
".*abstract.*") (text mods
) 0) in
2292 match token_kind kw
with
2293 | Some
TK.Class
when is_abs -> Cabstract
2294 | Some
TK.Class
-> Cnormal
2295 | Some
TK.Interface
-> Cinterface
2296 | Some
TK.Trait
-> Ctrait
2297 | Some
TK.Enum
-> Cenum
2298 | _ -> missing_syntax "class kind" kw
env
2300 let c_doc_comment = doc_comment_opt in
2317 | ConstDeclaration
{ const_keyword
= kw
; _ }
2318 when env.fi_mode
= FileInfo.Mdecl
2320 Option.value_map ~default
:"" ~
f:Token.text (Syntax.get_token kw
)
2321 -> [] (* Legacy parity; case-sensitive global const declarations *)
2323 { const_type_specifier
= ty
2324 ; const_declarators
= decls
2326 let declarations = List.map ~
f:syntax
(as_list decls
) in
2328 | ConstantDeclarator
2329 { constant_declarator_name
= name
2330 ; constant_declarator_initializer
= init
2333 { cst_mode
= mode_annotation env.fi_mode
2334 ; cst_kind
= Cst_const
2335 ; cst_name
= pos_name name env
2336 ; cst_type
= mpOptional pHint ty env
2337 ; cst_value
= pSimpleInitializer init
env
2338 ; cst_namespace
= Namespace_env.empty
env.parser_options
2339 ; cst_span
= pPos node
env
2341 | _ -> missing_syntax "constant declaration" decls
env
2343 List.map ~
f declarations
2345 { alias_attribute_spec
= attr
2346 ; alias_keyword
= kw
2348 ; alias_generic_parameter
= tparams
2349 ; alias_constraint
= constr
2353 { t_id
= pos_name name env
2354 ; t_tparams
= pTParaml tparams
env
2355 ; t_constraint
= Option.map ~
f:snd
@@
2356 mpOptional pTConstraint constr
env
2357 ; t_user_attributes
= List.concat
@@
2358 List.map ~
f:(fun x -> pUserAttribute
x env) (as_list attr
)
2359 ; t_namespace
= Namespace_env.empty
env.parser_options
2360 ; t_mode
= mode_annotation env.fi_mode
2362 match token_kind kw
with
2363 | Some
TK.Newtype
-> NewType
(pHint hint
env)
2364 | Some
TK.Type
-> Alias
(pHint hint
env)
2365 | _ -> missing_syntax "kind" kw
env
2368 { enum_attribute_spec
= attrs
2371 ; enum_type
= constr
2372 ; enum_enumerators
= enums
2374 let pEnumerator node
=
2375 match syntax node
with
2376 | Enumerator
{ enumerator_name
= name; enumerator_value
= value; _ } ->
2377 fun env -> Const
(None
, [pos_name name env, pExpr
value env])
2378 | _ -> missing_syntax "enumerator" node
2381 { c_mode = mode_annotation env.fi_mode
2382 ; c_user_attributes = List.concat
@@ couldMap ~
f:pUserAttribute
attrs env
2386 ; c_name = pos_name name env
2390 ; c_body = couldMap enums
env ~
f:pEnumerator
2391 ; c_namespace = Namespace_env.empty
env.parser_options
2392 ; c_span = pPos node
env
2394 { e_base
= pHint base
env
2395 ; e_constraint
= mpOptional pTConstraintTy constr
env
2397 ; c_doc_comment = doc_comment_opt
2399 | InclusionDirective
2400 { inclusion_expression
2401 ; inclusion_semicolon
= _
2402 } when env.fi_mode
<> FileInfo.Mdecl
&& env.fi_mode
<> FileInfo.Mphp
2403 || env.hhvm_compat_mode
|| env.codegen
->
2404 let expr = pExpr inclusion_expression
env in
2405 [ Stmt
(pPos node
env, Expr
expr) ]
2406 | NamespaceDeclaration
2407 { namespace_name
= name
2409 { syntax
= NamespaceBody
{ namespace_declarations
= decls
; _ }; _ }
2411 let env = non_tls env in
2414 , List.concat_map ~
f:(fun x -> pDef
x env) (as_list decls
)
2416 | NamespaceDeclaration
{ namespace_name
= name; _ } ->
2417 [ Namespace
(pos_name name env, []) ]
2418 | NamespaceGroupUseDeclaration
2419 { namespace_group_use_kind
= kind
2420 ; namespace_group_use_prefix
= prefix
2421 ; namespace_group_use_clauses
= clauses
2423 let f = pNamespaceUseClause
env kind ~prefix
:(Some prefix
) in
2424 [ NamespaceUse
(List.map ~
f (as_list clauses
)) ]
2425 | NamespaceUseDeclaration
2426 { namespace_use_kind
= kind
2427 ; namespace_use_clauses
= clauses
2429 let f = pNamespaceUseClause
env kind ~prefix
:None
in
2430 [ NamespaceUse
(List.map ~
f (as_list clauses
)) ]
2431 | _ when env.fi_mode
= FileInfo.Mdecl
|| env.fi_mode
= FileInfo.Mphp
2432 && not
(env.hhvm_compat_mode
|| env.codegen
)-> []
2433 | _ -> [ Stmt
(pStmt node
env) ]
2434 let pProgram : program parser
= fun node
env ->
2435 let rec post_process program
=
2436 let span (p : 'a
-> bool) =
2437 let rec go yes
= function
2438 | (x::xs
) when p x -> go (x::yes
) xs
2439 | xs
-> (List.rev yes
, xs
)
2442 let not_namespace = function
2443 | Namespace
_ -> false
2448 | (Namespace
(n, [])::el
) ->
2449 let body, remainder
= span not_namespace el
in
2450 Namespace
(n, body) :: post_process remainder
2451 | (Namespace
(n, il
)::el
) ->
2452 Namespace
(n, post_process il
) :: post_process el
2453 | (Stmt
(_, Noop
) :: el
) -> post_process el
2454 | ((Stmt
(_, Expr
(pos, (Call
2455 ( (_, (Id
(_, "define")))
2457 , [ (_, (String
name))
2462 )))) :: el
) -> Constant
2463 { cst_mode
= mode_annotation env.fi_mode
2464 ; cst_kind
= Cst_define
2468 ; cst_namespace
= Namespace_env.empty
env.parser_options
2470 } :: post_process el
2471 | (e::el
) -> e :: post_process el
2474 (* The list of top-level things in a file is somewhat special. *)
2475 let rec aux env acc
= function
2477 (* EOF happens only as the last token in the list. *)
2478 | [{ syntax
= EndOfFile
_; _ }]
2479 -> List.concat
(List.rev acc
)
2480 (* HaltCompiler stops processing the list *)
2481 | [{ syntax
= ExpressionStatement
2482 { expression_statement_expression
=
2483 { syntax
= HaltCompilerExpression
_ ; _ } ; _ } ; _ }]
2484 -> List.concat
(List.rev acc
)
2485 (* There's an incompatibility between the Full-Fidelity (FF) and the AST view
2486 * of the world; `define` is an *expression* in FF, but a *definition* in AST.
2487 * Luckily, `define` only happens at the level of definitions.
2489 | { syntax
= ExpressionStatement
2490 { expression_statement_expression
=
2491 { syntax
= DefineExpression
2492 { define_keyword
; define_argument_list
= args; _ }
2495 ; _ } as cur_node
:: nodel
when not
env.quick_mode
->
2497 match List.map ~
f:(fun x -> pExpr
x env) (as_list args) with
2498 | [ _, String
name; e ] -> Constant
2499 { cst_mode
= mode_annotation env.fi_mode
2500 ; cst_kind
= Cst_define
2504 ; cst_namespace
= Namespace_env.empty
env.parser_options
2505 ; cst_span
= pPos cur_node
env
2508 let name = pos_name define_keyword
env in
2509 Stmt
(pPos cur_node
env,
2510 Expr
(fst
name, Call
((fst
name, Id
name), [], args, [])))
2512 aux env ([def] :: acc
) nodel
2514 let def = pDef node
env in
2515 aux env (def :: acc
) nodel
2517 let nodes = as_list node
in
2518 let nodes = aux env [] nodes in
2521 let pScript node
env =
2522 match syntax node
with
2523 | Script
{ script_declarations
; _ } -> pProgram script_declarations
env
2524 | _ -> missing_syntax "script" node
env
2526 (* The full fidelity parser considers all comments "simply" trivia. Some
2527 * comments have meaning, though. This meaning can either be relevant for the
2528 * type checker (like UNSAFE, HH_FIXME, etc.), but also for other uses, like
2529 * Codex, where comments are used for documentation generation.
2531 * Inlining the scrape for comments in the lowering code would be prohibitively
2532 * complicated, but a separate pass is fine.
2535 type fixmes
= Pos.t
IMap.t
IMap.t
2536 type scoured_comment
= Pos.t
* comment
2537 type scoured_comments
= scoured_comment list
2538 type accumulator
= scoured_comments
* fixmes
2541 (path
: Relative_path.t
)
2542 (source_text : SourceText.t
)
2546 let pos_of_offset = SourceText.relative_pos path
source_text in
2547 let go (node
: node
) (cmts
, fm
as acc
: accumulator
) (t
: Trivia.t
)
2549 match Trivia.kind t
with
2550 | TriviaKind.WhiteSpace
2551 | TriviaKind.EndOfLine
2553 | TriviaKind.UnsafeExpression
2554 | TriviaKind.FallThrough
2555 | TriviaKind.ExtraTokenError
2556 | TriviaKind.AfterHaltCompiler
2558 | TriviaKind.DelimitedComment
->
2559 let start = Trivia.start_offset t
+ 2 (* for the '/*' *) in
2560 let end_ = Trivia.end_offset t
- 2 (* for the '*/' *) in
2561 let len = end_ - start + 1 in
2562 let p = pos_of_offset start end_ in
2563 let t = String.sub
(Trivia.text t) 2 len in
2564 (p, CmtBlock
t) :: cmts
, fm
2565 | TriviaKind.SingleLineComment
->
2566 let text = SourceText.text (Trivia.source_text t) in
2567 let start = Trivia.start_offset t in
2568 let start = start + if text.[start] = '#'
then 1 else 2 in
2569 let end_ = Trivia.end_offset t in
2570 let len = end_ - start + 1 in
2571 let p = pos_of_offset start end_ in
2572 let t = String.sub
text start len in
2573 (p, CmtLine
t) :: cmts
, fm
2575 | TriviaKind.IgnoreError
2577 let pos = pPos node
env in
2578 let line = Pos.line pos in
2579 let ignores = try IMap.find
line fm
with Not_found
-> IMap.empty
in
2580 let reg = regexp
"HH_\\(FIXME\\|IGNORE_ERROR\\)[ \\t\\n]*\\[?\\([0-9]+\\)\\]?" in
2581 let txt = Trivia.text t in
2582 (try ignore
(search_forward
reg txt 0) with
2584 let msg = Printf.sprintf
2585 "Inconsistent trivia classification: Received %s, but failed to \
2586 match regexp for FIXME or IGNORE_ERROR. Either the lexer has a \
2587 bug in its trivia classification, or the lowerer is more \
2588 restrictive than the lexer. Source: %s"
2589 (TriviaKind.to_string
(Trivia.kind t))
2590 (Pos.string (Pos.to_absolute
pos))
2594 let p = pos_of_offset (Trivia.start_offset t) (Pos.start_cnum
pos) in
2595 let code = int_of_string
(matched_group
2 txt) in
2596 let ignores = IMap.add
code p ignores in
2597 cmts
, IMap.add
line ignores fm
2599 let rec aux (cmts
, fm
as acc
: accumulator
) (node
: node
) : accumulator
=
2600 match syntax node
with
2603 let trivia = Token.leading
t in
2604 let acc = List.fold_left ~
f ~init
:acc trivia in
2605 let trivia = Token.trailing
t in
2606 List.fold_left ~
f ~init
:acc trivia
2607 | _ -> List.fold_left ~
f:aux ~init
:acc (children node
)
2609 aux ([], IMap.empty
) tree
2611 (*****************************************************************************(
2613 )*****************************************************************************)
2616 { fi_mode
: FileInfo.mode
2620 ; file
: Relative_path.t
2621 ; comments
: (Pos.t * comment
) list
2625 ?
(hhvm_compat_mode
= false )
2627 ?
(php5_compat_mode
= false )
2628 ?
(elaborate_namespaces
= true )
2629 ?
(include_line_comments
= false )
2630 ?
(keep_errors
= true )
2631 ?
(ignore_pos
= false )
2632 ?
(quick_mode
= false )
2633 ?
(lower_coroutines
= true )
2634 ?
(enable_hh_syntax
= false )
2635 ?
(parser_options
= ParserOptions.default
)
2636 ?
(fi_mode
= FileInfo.Mpartial
)
2637 ?
(is_hh_file
= false )
2639 (file
: Relative_path.t)
2641 = { hhvm_compat_mode
2643 ; codegen
= codegen
|| hhvm_compat_mode
2645 ; elaborate_namespaces
2646 ; include_line_comments
2648 ; quick_mode
= not hhvm_compat_mode
&& (match fi_mode
with
2657 ParserOptions.with_hh_syntax_for_hhvm parser_options
2658 (codegen
&& (enable_hh_syntax
|| is_hh_file
))
2663 ; top_level_statements
= true
2666 ; unsafes
= ISet.empty
2667 ; saw_std_constant_redefinition
= false
2670 let elaborate_toplevel_and_std_constants ast
env source_text =
2671 match env.elaborate_namespaces
, env.saw_std_constant_redefinition
with
2673 let elaborate_std_constants nsenv
def =
2674 let visitor = object(self
)
2675 inherit [_] endo
as super
2676 method! on_expr
env expr =
2678 | p, True
| p, False
| p, Null
when p <> Pos.none
->
2679 let s = File_pos.offset
@@ Pos.pos_start p in
2680 let e = File_pos.offset
@@ Pos.pos_end p in
2681 let text = SourceText.sub
source_text s (e - s) in
2682 let was_renamed, _ =
2683 NS.elaborate_id_impl
2688 if was_renamed then p, Ast.Id
(p, text)
2690 | _ -> super#on_expr
env expr
2692 visitor#on_def nsenv
def in
2693 let parser_options = env.parser_options in
2694 NS.elaborate_map_toplevel_defs
parser_options ast
elaborate_std_constants
2696 NS.elaborate_toplevel_defs
env.parser_options ast
2699 let lower env ~
source_text ~script
: result =
2700 let ast = runP pScript script
env in
2701 let ast = elaborate_toplevel_and_std_constants ast env source_text in
2702 let comments, fixmes
= scour_comments env.file
source_text script
env in
2703 let comments = if env.include_line_comments
then comments else
2704 List.filter ~
f:(fun (_,c
) -> not
(Prim_defs.is_line_comment c
)) comments
2706 let () = if env.keep_errors
then Fixmes.HH_FIXMES.add
env.file fixmes
in
2707 { fi_mode
= env.fi_mode
2708 ; is_hh_file
= env.is_hh_file
2710 ; content = SourceText.text source_text
2715 let from_text (env : env) (source_text : SourceText.t) : result =
2718 Full_fidelity_parser_env.make
2719 ~hhvm_compat_mode
:env.hhvm_compat_mode
2720 ~php5_compat_mode
:env.php5_compat_mode
2722 SyntaxTree.make ~
env source_text in
2723 let () = if env.hhvm_compat_mode
then
2725 ParserErrors.parse_errors
2726 ~enable_hh_syntax
:env.enable_hh_syntax
2727 ~level
:ParserErrors.HHVMCompatibility
2730 (* Prioritize runtime errors *)
2731 let runtime_errors =
2733 ~
f:(fun e -> Full_fidelity_syntax_error.error_type
e =
2734 Full_fidelity_syntax_error.RuntimeError
) in
2735 match errors, runtime_errors with
2739 raise
@@ Full_fidelity_syntax_error.ParserFatal
e
2741 let script = SyntaxTree.root
tree in
2742 let script = from_positioned_syntax
script in
2744 if env.lower_coroutines
then
2745 Coroutine_lowerer.lower_coroutines
script
2748 let fi_mode = if SyntaxTree.is_php
tree then FileInfo.Mphp
else
2749 let mode_string = String.trim
(SyntaxTree.mode
tree) in
2751 try List.hd
(Str.split
(Str.regexp
" +") mode_string) with
2754 Option.value_map
mode_word ~default
:FileInfo.Mpartial ~
f:(function
2755 | "decl" when env.hhvm_compat_mode
-> FileInfo.Mphp
2756 | "decl" -> FileInfo.Mdecl
2757 | "strict" -> FileInfo.Mstrict
2758 | ("partial" | "") -> FileInfo.Mpartial
2759 (* TODO: Come up with better mode detection *)
2760 | _ -> FileInfo.Mpartial
2763 let env = if env.fi_mode = fi_mode then env else { env with fi_mode } in
2764 let env = { env with is_hh_file
= SyntaxTree.is_hack
tree } in
2765 (* If we are generating code and this is an hh file or hh syntax is enabled,
2766 * then we want to inject auto import types into HH namespace during namespace
2769 let env = { env with parser_options =
2770 ParserOptions.with_hh_syntax_for_hhvm
env.parser_options
2771 (env.codegen
&& (ParserOptions.enable_hh_syntax_for_hhvm
env.parser_options || env.is_hh_file
)) } in
2777 let from_file (env : env) : result =
2778 let source_text = SourceText.from_file env.file
in
2779 from_text env source_text
2781 (*****************************************************************************(
2782 * Backward compatibility matter (should be short-lived)
2783 )*****************************************************************************)
2785 let legacy (x : result) : Parser_hack.parser_return
=
2786 { Parser_hack.file_mode
= Option.some_if
(x.fi_mode <> FileInfo.Mphp
) x.fi_mode
2787 ; Parser_hack.is_hh_file
= x.is_hh_file
2788 ; Parser_hack.comments = x.comments
2789 ; Parser_hack.ast = x.ast
2790 ; Parser_hack.content = x.content
2793 let from_text_with_legacy (env : env) (content : string)
2794 : Parser_hack.parser_return
=
2795 let source_text = SourceText.make
env.file
content in
2796 legacy @@ from_text env source_text
2798 let from_file_with_legacy env = legacy (from_file env)