Improve error message on misplaced async modifiers
[hiphop-php.git] / hphp / hack / src / parser / full_fidelity_parser_errors.ml
blobbc4af04d5a32610e5896c27b833e126ab0107265
1 (**
2 * Copyright (c) 2016, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Core_kernel
12 module WithSyntax(Syntax : Syntax_sig.Syntax_S) = struct
13 module WithSmartConstructors(SCI : SmartConstructors.SmartConstructors_S
14 with type r = Syntax.t
15 with module Token = Syntax.Token
16 ) = struct
18 open Syntax
20 module SyntaxTree_ = Full_fidelity_syntax_tree
21 .WithSyntax(Syntax)
22 module SyntaxTree = SyntaxTree_.WithSmartConstructors(SCI)
24 module Token = Syntax.Token
25 module SyntaxError = Full_fidelity_syntax_error
26 module SyntaxKind = Full_fidelity_syntax_kind
27 module TokenKind = Full_fidelity_token_kind
29 module SN = Naming_special_names
31 type location = {
32 start_offset: int;
33 end_offset: int
36 let text node =
37 Option.value ~default:"<text_extraction_failure>" (Syntax.extract_text node)
39 let make_location s e =
40 match position Relative_path.default s, position Relative_path.default e with
41 | Some s, Some e ->
42 let start_offset, _ = Pos.info_raw s in
43 let _, end_offset = Pos.info_raw e in
44 { start_offset ; end_offset }
45 | _ ->
46 failwith "Could not determine positions of parse tree nodes."
48 let make_location_of_node n =
49 make_location n n
51 let start_offset n =
52 let s = Syntax.position Relative_path.default n in
53 let s, _ = Pos.info_raw (Option.value ~default:Pos.none s) in
56 let end_offset n =
57 let e = Syntax.position Relative_path.default n in
58 let _, e = Pos.info_raw (Option.value ~default:Pos.none e) in
61 type namespace_type =
62 | Unspecified
63 | Bracketed of location
64 | Unbracketed of location
66 type name_kind =
67 | Name_use (* `use` construct *)
68 | Name_def (* definition e.g. `class` or `trait` *)
69 | Name_implicit_use (* implicit `use` e.g. HH type in type hint *)
71 type first_use_or_def = {
72 f_location: location;
73 f_kind: name_kind;
74 f_name: string;
75 f_global: bool;
78 type error_level = Minimum | Typical | Maximum
80 type hhvm_compat_mode = NoCompat | HHVMCompat
82 type context =
83 { active_classish : Syntax.t option
84 ; active_methodish : Syntax.t option
85 ; active_callable : Syntax.t option
86 ; active_callable_attr_spec : Syntax.t option
87 (* true if active callable is reactive if it is a function or method, or there is a reactive
88 * proper ancestor (including lambdas) but not beyond the enclosing function or method *)
89 ; active_is_rx_or_enclosing_for_lambdas : bool
90 ; active_const : Syntax.t option
91 (* Named (not anonymous) namespaces that the current expression is enclosed within. *)
92 ; nested_namespaces : Syntax.t list
95 type env =
96 { syntax_tree : SyntaxTree.t
97 ; level : error_level
98 ; hhvm_compat_mode : hhvm_compat_mode
99 ; codegen : bool
100 ; hhi_mode : bool
101 ; parser_options : ParserOptions.t
102 ; context : context
105 let make_env
106 ?(level = Typical )
107 ?(hhvm_compat_mode = NoCompat )
108 ?(hhi_mode = false )
109 ~(parser_options : ParserOptions.t)
110 (syntax_tree : SyntaxTree.t)
111 ~(codegen : bool)
112 : env
114 let context =
115 { active_classish = None
116 ; active_methodish = None
117 ; active_callable = None
118 ; active_callable_attr_spec = None
119 ; active_is_rx_or_enclosing_for_lambdas = false
120 ; active_const = None
121 ; nested_namespaces = []
122 } in
123 { syntax_tree
124 ; level
125 ; hhvm_compat_mode
126 ; codegen
127 ; hhi_mode
128 ; parser_options
129 ; context
132 and is_hhvm_compat env = env.hhvm_compat_mode <> NoCompat
134 let is_typechecker env = not env.codegen
136 let global_namespace_name = "\\"
138 let combine_names n1 n2 =
139 let has_leading_slash = String.length n2 > 0 && String.get n2 0 = '\\' in
140 let len = String.length n1 in
141 let has_trailing_slash = String.get n1 (len - 1) = '\\' in
142 match has_leading_slash, has_trailing_slash with
143 | true, true -> n1 ^ (String.sub n2 1 (String.length n2 - 1))
144 | false, false -> n1 ^ "\\" ^ n2
145 | _ -> n1 ^ n2
147 let make_first_use_or_def ~kind ?(is_method=false) location namespace_name name =
149 f_location = location;
150 f_kind = kind;
151 f_name = combine_names namespace_name name;
152 f_global = not is_method && namespace_name = global_namespace_name;
155 type 'a strmap =
156 | YesCase of 'a SMap.t
157 | NoCase of 'a LSMap.t
160 type used_names = {
161 t_classes: first_use_or_def strmap; (* NoCase *)
162 t_namespaces: first_use_or_def strmap; (* NoCase *)
163 t_functions: first_use_or_def strmap; (* NoCase *)
164 t_constants: first_use_or_def strmap; (* YesCase *)
167 let empty_names = {
168 t_classes = NoCase LSMap.empty;
169 t_namespaces = NoCase LSMap.empty;
170 t_functions = NoCase LSMap.empty;
171 t_constants = YesCase SMap.empty;
174 let strmap_mem : string -> 'a strmap -> bool =
175 fun k m ->
176 begin match m with
177 | NoCase m -> LSMap.mem k m
178 | YesCase m -> SMap.mem k m
181 let strmap_add : string -> 'a -> 'a strmap -> 'a strmap =
182 fun k v m ->
183 begin match m with
184 | NoCase m' -> NoCase (LSMap.add k v m')
185 | YesCase m' -> YesCase (SMap.add k v m')
188 let strmap_get : string -> 'a strmap -> 'a option =
189 fun k m ->
190 begin match m with
191 | NoCase m' -> LSMap.get k m'
192 | YesCase m' -> SMap.get k m'
195 let strmap_find_first_opt : (string -> bool) -> 'a strmap -> (string * 'a) option =
196 fun k m ->
197 begin match m with
198 | NoCase m' -> LSMap.find_first_opt k m'
199 | YesCase m' -> SMap.find_first_opt k m'
202 let strmap_filter : (string -> 'a -> bool) -> 'a strmap -> 'a strmap =
203 fun f m ->
204 begin match m with
205 | NoCase m' -> NoCase (LSMap.filter f m')
206 | YesCase m' -> YesCase (SMap.filter f m')
209 let strmap_union : 'a strmap -> 'a strmap -> 'a strmap =
210 fun x y ->
211 begin match x, y with
212 | NoCase x', NoCase y' -> NoCase (LSMap.union x' y')
213 | YesCase x', YesCase y' -> YesCase (SMap.union x' y')
214 | NoCase _, YesCase _ -> failwith "cannot union NoCase and YesCase."
215 | YesCase _, NoCase _ -> failwith "cannot union YesCase and NoCase."
218 let empty_trait_require_clauses = NoCase LSMap.empty
220 let get_short_name_from_qualified_name name alias =
221 if String.length alias <> 0 then alias
222 else match String.rindex name '\\' with
223 | Some i -> String.sub name (i + 1) (String.length name - i - 1)
224 | None -> name
226 type accumulator = {
227 errors : SyntaxError.t list;
228 namespace_type : namespace_type;
229 namespace_name: string;
230 names: used_names;
231 trait_require_clauses: TokenKind.t strmap;
232 is_in_concurrent_block: bool;
235 let make_acc
236 acc errors namespace_type names namespace_name trait_require_clauses
237 is_in_concurrent_block =
238 if phys_equal acc.errors errors &&
239 phys_equal acc.namespace_type namespace_type &&
240 phys_equal acc.names names &&
241 phys_equal acc.namespace_name namespace_name &&
242 phys_equal acc.trait_require_clauses trait_require_clauses &&
243 acc.is_in_concurrent_block = is_in_concurrent_block
244 then acc
245 else { errors
246 ; namespace_type
247 ; names
248 ; namespace_name
249 ; trait_require_clauses
250 ; is_in_concurrent_block
253 let fold_child_nodes ?(cleanup = (fun x -> x)) env f node parents acc =
254 Syntax.children node
255 |> List.fold_left ~init:acc ~f:(fun acc c -> f env acc c (node :: parents))
256 |> cleanup
258 (* Turns a syntax node into a list of nodes; if it is a separated syntax
259 list then the separators are filtered from the resulting list. *)
260 let syntax_to_list include_separators node =
261 let rec aux acc syntax_list =
262 match syntax_list with
263 | [] -> acc
264 | h :: t ->
265 begin
266 match syntax h with
267 | ListItem { list_item; list_separator } ->
268 let acc = list_item :: acc in
269 let acc =
270 if include_separators then (list_separator :: acc ) else acc in
271 aux acc t
272 | _ -> aux (h :: acc) t
273 end in
274 match syntax node with
275 | Missing -> [ ]
276 | SyntaxList s -> List.rev (aux [] s)
277 | ListItem { list_item; list_separator } ->
278 if include_separators then [ list_item; list_separator ] else [ list_item ]
279 | _ -> [ node ]
281 let syntax_to_list_no_separators = syntax_to_list false
282 let syntax_to_list_with_separators = syntax_to_list true
284 let assert_last_in_list assert_fun node =
285 let rec aux lst =
286 match lst with
287 | []
288 | _ :: [] -> None
289 | h :: _ when assert_fun h -> Some h
290 | _ :: t -> aux t in
291 aux (syntax_to_list_no_separators node)
293 let is_decorated_expression ~f node =
294 begin match syntax node with
295 | DecoratedExpression { decorated_expression_decorator; _ } ->
296 f decorated_expression_decorator
297 | _ -> false
300 let test_decorated_expression_child ~f node =
301 begin match syntax node with
302 | DecoratedExpression { decorated_expression_expression; _ } ->
303 f decorated_expression_expression
304 | _ -> false
307 (* Test two levels in case ...& or &... hiding under *)
308 let rec is_reference_expression node =
309 is_decorated_expression ~f:is_ampersand node ||
310 test_decorated_expression_child node ~f:is_reference_expression
312 let rec is_variadic_expression node =
313 is_decorated_expression ~f:is_ellipsis node ||
314 test_decorated_expression_child node ~f:is_variadic_expression
316 let is_reference_variadic node =
317 is_decorated_expression ~f:is_ellipsis node &&
318 test_decorated_expression_child node ~f:is_reference_expression
320 let is_variadic_reference node =
321 is_decorated_expression ~f:is_ampersand node &&
322 test_decorated_expression_child node ~f:is_variadic_expression
324 let is_double_variadic node =
325 is_decorated_expression ~f:is_ellipsis node &&
326 test_decorated_expression_child node ~f:is_variadic_expression
328 let is_double_reference node =
329 is_decorated_expression ~f:is_ampersand node &&
330 test_decorated_expression_child node ~f:is_reference_expression
332 let is_variadic_parameter_variable node =
333 (* TODO: This shouldn't be a decorated *expression* because we are not
334 expecting an expression at all. We're expecting a declaration. *)
335 is_variadic_expression node
337 let is_variadic_parameter_declaration node =
338 begin match syntax node with
339 | VariadicParameter _ -> true
340 | ParameterDeclaration { parameter_name; _ } ->
341 is_variadic_parameter_variable parameter_name
342 | _ -> false
345 let misplaced_variadic_param params =
346 assert_last_in_list is_variadic_parameter_declaration params
348 let misplaced_variadic_arg args =
349 assert_last_in_list is_variadic_expression args
351 (* If a list ends with a variadic parameter followed by a comma, return it *)
352 let ends_with_variadic_comma params =
353 let rec aux params =
354 match params with
355 | [] -> None
356 | x :: y :: [] when is_variadic_parameter_declaration x && is_comma y ->
357 Some y
358 | _ :: t -> aux t in
359 aux (syntax_to_list_with_separators params)
361 (* Extract variadic parameter from a parameter list *)
362 let variadic_param params =
363 let rec aux params =
364 match params with
365 | [] -> None
366 | x :: _ when is_variadic_parameter_declaration x -> Some x
367 | _ :: t -> aux t in
368 aux (syntax_to_list_with_separators params)
370 let is_parameter_with_default_value param =
371 match syntax param with
372 | ParameterDeclaration { parameter_default_value; _ } ->
373 not (is_missing parameter_default_value)
374 | _ -> false
376 (* test a node is a syntaxlist and that the list contains an element
377 * satisfying a given predicate *)
378 let list_contains_predicate p node =
379 match syntax node with
380 | SyntaxList lst ->
381 List.exists ~f:p lst
382 | _ -> false
384 let list_first_duplicate node : Syntax.t option =
385 let module SyntaxMap = Caml.Map.Make (
386 struct
387 type t = Syntax.t
388 let compare a b = match syntax a, syntax b with
389 | Token x, Token y ->
390 Token.(compare (kind x) (kind y))
391 | _, _ -> Pervasives.compare a b
393 ) in
394 match syntax node with
395 | SyntaxList lst ->
396 let check_fun (tbl, acc) el =
397 if SyntaxMap.mem el tbl then (tbl, Some el)
398 else (SyntaxMap.add el () tbl, acc)
400 let (_, result) = List.fold_left ~f:check_fun ~init:(SyntaxMap.empty, None) lst in
401 result
402 | _ -> None
404 let is_empty_list_or_missing node =
405 match syntax node with
406 | SyntaxList [] | Missing -> true
407 | _ -> false
409 let token_kind node =
410 match syntax node with
411 | Token t -> Some (Token.kind t)
412 | _ -> None
414 (* Helper function for common code pattern *)
415 let is_token_kind node kind =
416 (token_kind node) = Some kind
418 let active_classish_kind context =
419 Option.value_map context.active_classish ~default:None ~f:(function
420 | { syntax = ClassishDeclaration cd; _ } -> token_kind cd.classish_keyword
421 | _ -> None)
423 let modifiers_of_function_decl_header_exn node =
424 match syntax node with
425 | FunctionDeclarationHeader { function_modifiers = m; _ } -> m
426 | _ -> failwith "expected to get FunctionDeclarationHeader"
428 let get_modifiers_of_declaration node =
429 match syntax node with
430 | MethodishDeclaration { methodish_function_decl_header = header; _ } ->
431 Some (modifiers_of_function_decl_header_exn header)
432 | PropertyDeclaration { property_modifiers; _} ->
433 Some (property_modifiers)
434 | _ -> None
436 (* tests whether the methodish contains a modifier that satisfies [p] *)
437 let methodish_modifier_contains_helper p node =
438 get_modifiers_of_declaration node
439 |> Option.exists ~f:(list_contains_predicate p)
441 (* test the methodish node contains the Final keyword *)
442 let methodish_contains_final node =
443 methodish_modifier_contains_helper is_final node
445 (* test the methodish node contains the Abstract keyword *)
446 let methodish_contains_abstract node =
447 methodish_modifier_contains_helper is_abstract node
449 (* test the methodish node contains the Static keyword *)
450 let methodish_contains_static node =
451 methodish_modifier_contains_helper is_static node
453 (* test the methodish node contains the Private keyword *)
454 let methodish_contains_private node =
455 methodish_modifier_contains_helper is_private node
457 let is_visibility x =
458 is_public x || is_private x || is_protected x
460 let contains_async_not_last mods =
461 let mod_list = syntax_to_list_no_separators mods in
462 List.exists ~f:is_async mod_list
463 && not @@ is_async @@ List.nth_exn mod_list (List.length mod_list - 1)
465 let has_static node context f =
466 match node with
467 | FunctionDeclarationHeader node ->
468 let label = node.function_name in
469 (f label) &&
470 context.active_methodish |> Option.exists ~f:methodish_contains_static
471 | _ -> false
473 (* Given a function declaration header, confirm that it is a constructor
474 * and that the methodish containing it has a static keyword *)
476 let is_clone label =
477 String.lowercase (text label) = SN.SpecialFunctions.clone
479 let class_constructor_has_static node context =
480 has_static node context is_construct
482 let class_destructor_cannot_be_static node context =
483 has_static node context is_destruct
485 let clone_cannot_be_static node context =
486 has_static node context is_clone
488 let promoted_params (params : Syntax.t list) : Syntax.t list =
489 List.filter params (fun node ->
490 match syntax node with
491 | ParameterDeclaration { parameter_visibility; _ } ->
492 parameter_visibility |> is_missing |> not
493 | _ -> false)
495 (* Given a function declaration header, confirm that it is NOT a constructor
496 * and that the header containing it has visibility modifiers in parameters
498 let class_non_constructor_has_visibility_param node _context =
499 match node with
500 | FunctionDeclarationHeader node ->
501 let params = syntax_to_list_no_separators node.function_parameter_list in
502 not (is_construct node.function_name) && promoted_params params <> []
503 | _ -> false
505 (* Don't allow a promoted parameter in a constructor if the class
506 * already has a property with the same name. Return the clashing name found.
508 let class_constructor_param_promotion_clash node context : string option =
509 let class_elts node =
510 match Option.map node ~f:syntax with
511 | Some (ClassishDeclaration cd) -> begin
512 match syntax cd.classish_body with
513 | ClassishBody { classish_body_elements; _} ->
514 syntax_to_list_no_separators classish_body_elements
515 | _ -> []
517 | _ -> []
519 (* A property declaration may include multiple property names:
520 public int $x, $y;
522 let prop_names elt : string list =
523 match syntax elt with
524 | PropertyDeclaration { property_declarators; _} ->
525 let declarators = syntax_to_list_no_separators property_declarators in
526 let names = List.map declarators ~f:(function decl ->
527 match syntax decl with
528 | PropertyDeclarator {property_name; _} ->
529 Some (text property_name)
530 | _ -> None)
532 List.filter_opt names
533 | _ -> []
535 let param_names params =
536 let names = List.map params (fun p -> match syntax p with
537 | ParameterDeclaration { parameter_name; _ } ->
538 Some (text parameter_name)
539 | _ -> None)
541 List.filter_opt names
544 match node with
545 | FunctionDeclarationHeader node ->
546 if is_construct node.function_name then
547 let class_var_names =
548 List.concat_map (class_elts context.active_classish) ~f:prop_names
550 let params = syntax_to_list_no_separators node.function_parameter_list in
551 let promoted_param_names = param_names (promoted_params params) in
552 List.find promoted_param_names ~f:(fun name -> List.mem class_var_names name ~equal:(=))
553 else
554 None
555 | _ -> None
557 (* Ban parameter promotion in abstract constructors. *)
558 let abstract_class_constructor_has_visibility_param node _context =
559 match node with
560 | FunctionDeclarationHeader node ->
561 let label = node.function_name in
562 let params = syntax_to_list_no_separators node.function_parameter_list in
563 is_construct label &&
564 list_contains_predicate is_abstract node.function_modifiers &&
565 promoted_params params <> []
566 | _ -> false
568 (* Ban parameter promotion in interfaces and traits. *)
569 let interface_or_trait_has_visibility_param node context =
570 match node with
571 | FunctionDeclarationHeader node ->
572 let is_interface_or_trait =
573 context.active_classish |> Option.exists ~f:(fun parent_classish ->
574 match syntax parent_classish with
575 | ClassishDeclaration cd ->
576 let kind = token_kind cd.classish_keyword in
577 kind = Some TokenKind.Interface || kind = Some TokenKind.Trait
578 | _ -> false)
580 let params = syntax_to_list_no_separators node.function_parameter_list in
581 promoted_params params <> [] && is_interface_or_trait
582 | _ -> false
584 (* check that a constructor or a destructor is type annotated *)
585 let class_constructor_destructor_has_non_void_type env node _context =
586 if not (is_typechecker env) then false
587 else
588 match node with
589 | FunctionDeclarationHeader node ->
590 let label = node.function_name in
591 let type_ano = node.function_type in
592 let function_colon = node.function_colon in
593 let is_missing = is_missing type_ano && is_missing function_colon in
594 let is_void = match syntax type_ano with
595 | SimpleTypeSpecifier spec ->
596 is_void spec.simple_type_specifier
597 | _ -> false
599 (is_construct label || is_destruct label) &&
600 not (is_missing || is_void)
601 | _ -> false
603 let async_magic_method node _context =
604 match node with
605 | FunctionDeclarationHeader node ->
606 let name = String.lowercase @@ text node.function_name in
607 begin match name with
608 | _ when name = String.lowercase SN.Members.__disposeAsync -> false
609 | _ when SSet.mem name SN.Members.as_lowercase_set ->
610 list_contains_predicate is_async node.function_modifiers
611 | _ -> false
613 | _ -> false
615 let call_static_method node _context =
616 match node with
617 | FunctionDeclarationHeader node ->
618 let name = String.lowercase @@ text node.function_name in
619 name = String.lowercase SN.Members.__callStatic
620 | _ -> false
622 let clone_destruct_takes_no_arguments _method_name node _context =
623 match node with
624 | FunctionDeclarationHeader { function_parameter_list = l; function_name = name; _} ->
625 let num_params = List.length (syntax_to_list_no_separators l) in
626 ((is_clone name) || (is_destruct name)) && num_params <> 0
627 | _ -> false
629 (* whether a methodish decl has body *)
630 let methodish_has_body node =
631 match syntax node with
632 | MethodishDeclaration syntax ->
633 let body = syntax.methodish_function_body in
634 not (is_missing body)
635 | _ -> false
637 (* whether a methodish decl is native *)
638 let methodish_is_native node =
639 match syntax node with
640 | MethodishDeclaration { methodish_attribute = {
641 syntax = AttributeSpecification {
642 attribute_specification_attributes = attrs; _}; _}; _} ->
643 let attrs = syntax_to_list_no_separators attrs in
644 List.exists attrs
645 ~f:(function { syntax = ConstructorCall {constructor_call_type; _}; _} ->
646 String.lowercase @@ text constructor_call_type = "__native"
647 | _ -> false)
648 | _ -> false
650 (* By checking the third parent of a methodish node, tests whether the methodish
651 * node is inside an interface. *)
652 let methodish_inside_interface context =
653 context.active_classish |> Option.exists ~f:(fun parent_classish ->
654 match syntax parent_classish with
655 | ClassishDeclaration cd when token_kind cd.classish_keyword = Some TokenKind.Interface -> true
656 | _ -> false
658 (* Test whether node is a non-abstract method without a body and not native.
659 * Here node is the methodish node
660 * And methods inside interfaces are inherently considered abstract *)
661 let methodish_non_abstract_without_body_not_native env node _ =
662 let non_abstract = not (methodish_contains_abstract node
663 || methodish_inside_interface env.context) in
664 let not_has_body = not (methodish_has_body node) in
665 let not_native = not (methodish_is_native node) in
666 let not_hhi = not (env.hhi_mode) in
667 not_hhi && non_abstract && not_has_body && not_native
669 (* Test whether node is a method that is both abstract and private
671 let methodish_abstract_conflict_with_private node =
672 let is_abstract = methodish_contains_abstract node in
673 let has_private = methodish_contains_private node in
674 is_abstract && has_private
676 (* Test whether node is a method that is both abstract and final
678 let methodish_abstract_conflict_with_final node =
679 let is_abstract = methodish_contains_abstract node in
680 let has_final = methodish_contains_final node in
681 is_abstract && has_final
683 let methodish_abstract_inside_interface context node =
684 let is_abstract = methodish_contains_abstract node in
685 let is_in_interface = methodish_inside_interface context in
686 is_abstract && is_in_interface
688 let using_statement_function_scoped_is_legal parents =
689 match parents with
690 (* using is allowed in the toplevel, and also in toplevel async blocks *)
691 | _ ::
692 { syntax = CompoundStatement _; _ } ::
693 { syntax = (
694 FunctionDeclaration _ |
695 MethodishDeclaration _ |
696 AnonymousFunction _ |
697 LambdaExpression _ |
698 AwaitableCreationExpression _); _ } :: _ -> true
699 | _ -> false
701 let make_error_from_nodes
702 ?(error_type=SyntaxError.ParseError) start_node end_node error =
703 let s = start_offset start_node in
704 let e = end_offset end_node in
705 SyntaxError.make ~error_type s e error
707 let make_error_from_node ?(error_type=SyntaxError.ParseError) node error =
708 make_error_from_nodes ~error_type node node error
710 let is_invalid_xhp_attr_enum_item_literal literal_expression =
711 match syntax literal_expression with
712 | Token t -> begin
713 Full_fidelity_token_kind.(match Token.kind t with
714 | DecimalLiteral | SingleQuotedStringLiteral
715 | DoubleQuotedStringLiteral -> false
716 | _ -> true)
718 | _ -> true
720 let is_invalid_xhp_attr_enum_item node =
721 match syntax node with
722 | LiteralExpression {literal_expression} ->
723 is_invalid_xhp_attr_enum_item_literal literal_expression
724 | _ -> true
726 let xhp_errors env node errors =
727 match syntax node with
728 | XHPEnumType enumType when
729 (is_typechecker env) &&
730 (is_missing enumType.xhp_enum_values) ->
731 make_error_from_node enumType.xhp_enum_values SyntaxError.error2055 :: errors
732 | XHPEnumType enumType when
733 (is_typechecker env) ->
734 let invalid_enum_items = List.filter ~f:is_invalid_xhp_attr_enum_item
735 (syntax_to_list_no_separators enumType.xhp_enum_values) in
736 let mapper errors item =
737 make_error_from_node item SyntaxError.error2063 :: errors in
738 List.fold_left ~f:mapper ~init:errors invalid_enum_items
739 | XHPExpression
740 { xhp_open =
741 { syntax = XHPOpen { xhp_open_name; _ }; _ }
742 ; xhp_close =
743 { syntax = XHPClose { xhp_close_name; _ }; _ }
744 ; _ } when text xhp_open_name <> text xhp_close_name ->
745 make_error_from_node node (SyntaxError.error2070
746 ~open_tag:(text xhp_open_name)
747 ~close_tag:(text xhp_close_name)) :: errors
748 | _ -> errors
750 (* error on 'public private function foo() *)
751 let multiple_visibility_errors modifiers msg errors =
752 let modifiers = match syntax modifiers with
753 | SyntaxList lst -> lst
754 | _ -> []
756 let modifiers = List.filter modifiers is_visibility in
757 if (List.length modifiers > 1) then
758 make_error_from_node (List.last_exn modifiers) msg :: errors
759 else
760 errors
762 (* error on 'final final function foo() *)
763 let multiple_modifiers_errors modifiers msg errors =
764 match list_first_duplicate modifiers with
765 | Some duplicate ->
766 make_error_from_node duplicate msg :: errors
767 | None -> errors
770 (* helper since there are so many kinds of errors *)
771 let produce_error acc check node error error_node =
772 if check node then
773 (make_error_from_node error_node error) :: acc
774 else acc
776 let produce_error_from_check acc check node error =
777 match check node with
778 | Some error_node ->
779 (make_error_from_node error_node error) :: acc
780 | _ -> acc
782 let produce_error_context acc check node context error error_node =
783 if check node context then
784 (make_error_from_node error_node error) :: acc
785 else acc
787 let produce_error_for_header acc check node error error_node =
788 (* use [check] to check properties of function *)
789 let function_header_check_helper check node = check (syntax node) in
790 produce_error_context acc (function_header_check_helper check) node
791 error error_node
794 let cant_be_classish_name name =
795 match String.lowercase name with
796 | "callable" | "classname" | "darray" | "false" | "null" | "parent" | "self"
797 | "this" | "true" | "varray" -> true
798 | _ -> false
800 (* Given a function_declaration_header node, returns its function_name
801 * as a string opt. *)
802 let extract_function_name header_node =
803 (* The '_' arm of this match will never be reached, but the type checker
804 * doesn't allow a direct extraction of function_name from
805 * function_declaration_header. *)
806 match syntax header_node with
807 | FunctionDeclarationHeader fdh ->
808 Syntax.extract_text fdh.function_name
809 | _ -> None
811 (* Return, as a string opt, the name of the function or method given the context *)
812 let first_parent_function_name context =
813 (* Note: matching on either is sound because functions and/or methods cannot be nested *)
814 match context.active_methodish with
815 | Some { syntax = FunctionDeclaration { function_declaration_header = header; _ }; _ }
816 | Some { syntax = MethodishDeclaration { methodish_function_decl_header = header; _ }; _ }
817 -> extract_function_name header
818 | _ -> None
820 (* Given a particular TokenKind.(Trait/Interface), tests if a given
821 * classish_declaration node is both of that kind and declared abstract. *)
822 let is_classish_kind_declared_abstract _env cd_node =
823 match syntax cd_node with
824 | ClassishDeclaration { classish_keyword; classish_modifiers; _ }
825 when is_token_kind classish_keyword TokenKind.Trait
826 || is_token_kind classish_keyword TokenKind.Interface ->
827 list_contains_predicate is_abstract classish_modifiers
828 | _ -> false
830 let is_immediately_in_lambda context =
831 Option.value_map context.active_callable ~default:false ~f:(fun node ->
832 match syntax node with
833 | AnonymousFunction _
834 | LambdaExpression _
835 | AwaitableCreationExpression _ -> true
836 | _ -> false
839 (* Returns the whether the current context is in an active class scope *)
840 let is_in_active_class_scope context = Option.is_some context.active_classish
842 (* Returns the first ClassishDeclaration node in the given context,
843 * or None if there isn't one or classish_kind does not match. *)
844 let first_parent_classish_node classish_kind context =
845 Option.value_map context.active_classish ~default:None ~f:(function
846 { syntax = ClassishDeclaration cd; _ } as node ->
847 Some (node, is_token_kind cd.classish_keyword classish_kind)
848 | _ -> None
849 ) |> Option.value_map ~default:None
850 ~f:(function (n, true) -> Some n | _ -> None)
852 (* Return, as a string opt, the name of the closest enclosing classish entity in
853 the given context (not just Classes ) *)
854 let active_classish_name context =
855 context.active_classish |> Option.value_map ~default:None ~f:(fun node ->
856 match syntax node with
857 | ClassishDeclaration cd -> Syntax.extract_text cd.classish_name
858 | _ -> None
861 (* Return, as a string opt, the name of the Class in the given context *)
862 let first_parent_class_name context =
863 context.active_classish |> Option.value_map ~default:None ~f:(fun parent_classish ->
864 match syntax parent_classish with
865 | ClassishDeclaration cd when token_kind cd.classish_keyword = Some TokenKind.Class ->
866 active_classish_name context
867 | _ -> None (* This arm is never reached *)
870 (* Given a classish_ or methodish_ declaration node, returns the modifier node
871 from its list of modifiers, or None if there isn't one. *)
872 let extract_keyword modifier declaration_node =
873 let aux modifiers_list =
874 List.find ~f:modifier (syntax_to_list_no_separators modifiers_list)
877 match syntax declaration_node with
878 | ClassishDeclaration { classish_modifiers = modifiers_list ; _ } ->
879 aux modifiers_list
880 | _ ->
881 Option.bind (get_modifiers_of_declaration declaration_node) aux
883 (* Wrapper function that uses above extract_keyword function to test if node
884 contains is_abstract keyword *)
885 let is_abstract_declaration declaration_node =
886 not (Option.is_none (extract_keyword is_abstract declaration_node))
888 (* Given a context, tests if the immediate classish parent is an
889 * interface. *)
890 let is_inside_interface context =
891 Option.is_some (first_parent_classish_node TokenKind.Interface context)
893 (* Given a context, tests if the immediate classish parent is a
894 * trait. *)
895 let is_inside_trait context =
896 Option.is_some (first_parent_classish_node TokenKind.Trait context)
898 (* Returns the 'async'-annotation syntax node from the methodish_declaration
899 * node. The returned node may have syntax kind 'Missing', but it will only be
900 * None if something other than a methodish_declaration node was provided as
901 * input. *)
902 let extract_async_node md_node =
903 get_modifiers_of_declaration md_node
904 |> Option.value_map ~default:[] ~f:syntax_to_list_no_separators
905 |> List.find ~f:is_async
907 let is_abstract_and_async_method md_node _context =
908 let async_node = extract_async_node md_node in
909 match async_node with
910 | None -> false
911 | Some async_node ->
912 is_abstract_declaration md_node
913 && not (is_missing async_node)
915 let is_interface_and_async_method md_node context =
916 let async_node = extract_async_node md_node in
917 match async_node with
918 | None -> false
919 | Some async_node ->
920 is_inside_interface context
921 && not (is_missing async_node)
923 let get_params_for_enclosing_callable context =
924 context.active_callable |> Option.bind ~f:(fun callable ->
925 match syntax callable with
926 | FunctionDeclaration { function_declaration_header = header; _ }
927 | MethodishDeclaration { methodish_function_decl_header = header; _ } ->
928 begin match syntax header with
929 | FunctionDeclarationHeader fdh ->
930 Some fdh.function_parameter_list
931 | _ -> None
933 | LambdaExpression { lambda_signature; _} ->
934 begin match syntax lambda_signature with
935 | LambdaSignature { lambda_parameters; _ } ->
936 Some lambda_parameters
937 | _ -> None
939 | _ -> None
942 let first_parent_function_attributes_contains context name =
943 match context.active_methodish with
944 | Some { syntax = FunctionDeclaration { function_attribute_spec = {
945 syntax = AttributeSpecification {
946 attribute_specification_attributes; _ }; _ }; _ }; _ }
947 | Some { syntax = MethodishDeclaration { methodish_attribute = {
948 syntax = AttributeSpecification {
949 attribute_specification_attributes; _ }; _ }; _ }; _ }
951 let attrs =
952 syntax_to_list_no_separators attribute_specification_attributes in
953 List.exists attrs
954 ~f:(function { syntax = ConstructorCall { constructor_call_type; _}; _} ->
955 text constructor_call_type = name | _ -> false)
956 | _ -> false
957 let is_parameter_with_callconv param =
958 match syntax param with
959 | ParameterDeclaration { parameter_call_convention; _ } ->
960 not @@ is_missing parameter_call_convention
961 | ClosureParameterTypeSpecifier { closure_parameter_call_convention; _ } ->
962 not @@ is_missing closure_parameter_call_convention
963 | VariadicParameter { variadic_parameter_call_convention; _ } ->
964 not @@ is_missing variadic_parameter_call_convention
965 | _ -> false
967 let has_inout_params context =
968 match get_params_for_enclosing_callable context with
969 | Some function_parameter_list ->
970 let params = syntax_to_list_no_separators function_parameter_list in
971 List.exists params ~f:is_parameter_with_callconv
972 | _ -> false
974 let is_inside_async_method context =
975 Option.value_map context.active_callable ~f:(fun node ->
976 match syntax node with
977 | FunctionDeclaration { function_declaration_header = header; _ }
978 | MethodishDeclaration { methodish_function_decl_header = header; _ } ->
979 begin match syntax header with
980 | FunctionDeclarationHeader fdh ->
981 List.exists ~f:is_async @@
982 syntax_to_list_no_separators fdh.function_modifiers
983 | _ -> false
985 | AnonymousFunction { anonymous_async_keyword; _ } ->
986 not @@ is_missing anonymous_async_keyword
987 | LambdaExpression { lambda_async; _ } ->
988 not @@ is_missing lambda_async
989 | AwaitableCreationExpression _ -> true
990 | _ -> false
991 ) ~default:false
993 let make_name_already_used_error node name short_name original_location
994 report_error =
995 let name = Utils.strip_ns name in
996 let original_location_error =
997 SyntaxError.make
998 original_location.start_offset
999 original_location.end_offset
1000 SyntaxError.original_definition in
1001 let s = start_offset node in
1002 let e = end_offset node in
1003 SyntaxError.make
1004 ~child:(Some original_location_error) s e (report_error ~name ~short_name)
1006 let check_type_name_reference _env name_text location names errors =
1007 if not (Hh_autoimport.is_hh_autoimport name_text)
1008 || strmap_mem name_text names.t_classes
1009 then names, errors
1010 else
1011 let def = make_first_use_or_def ~kind:Name_implicit_use location "HH" name_text in
1012 let names = { names with t_classes = strmap_add name_text def names.t_classes} in
1013 names, errors
1015 let check_type_hint env node names errors =
1016 let rec check (names, errors) node =
1017 let names, errors =
1018 List.fold_left (Syntax.children node) ~f:check ~init:(names, errors) in
1019 match syntax node with
1020 | SimpleTypeSpecifier { simple_type_specifier = s; _ }
1021 | GenericTypeSpecifier { generic_class_type = s; _ } ->
1022 check_type_name_reference env (text s) (make_location_of_node node) names errors
1023 | _ ->
1024 names, errors
1026 check (names, errors) node
1028 let extract_callconv_node node =
1029 match syntax node with
1030 | ParameterDeclaration { parameter_call_convention; _ } ->
1031 Some parameter_call_convention
1032 | ClosureParameterTypeSpecifier { closure_parameter_call_convention; _ } ->
1033 Some closure_parameter_call_convention
1034 | VariadicParameter { variadic_parameter_call_convention; _ } ->
1035 Some variadic_parameter_call_convention
1036 | _ -> None
1038 (* Given a node, checks if it is a abstract ConstDeclaration *)
1039 let is_abstract_const declaration =
1040 match syntax declaration with
1041 | ConstDeclaration x -> not (is_missing x.const_abstract)
1042 | _ -> false
1044 (* Given a ConstDeclarator node, test whether it is abstract, but has an
1045 initializer. *)
1046 let constant_abstract_with_initializer init context =
1047 let is_abstract =
1048 match context.active_const with
1049 | Some p_const_declaration
1050 when is_abstract_const p_const_declaration -> true
1051 | _ -> false
1053 let has_initializer =
1054 not (is_missing init) in
1055 is_abstract && has_initializer
1057 (* Given a node, checks if it is a concrete ConstDeclaration *)
1058 let is_concrete_const declaration =
1059 match syntax declaration with
1060 | ConstDeclaration x -> is_missing x.const_abstract
1061 | _ -> false
1063 (* Given a ConstDeclarator node, test whether it is concrete, but has no
1064 initializer. *)
1065 let constant_concrete_without_initializer init context =
1066 let is_concrete = match context.active_const with
1067 | Some p_const_declaration ->
1068 is_concrete_const p_const_declaration
1069 | _ -> false in
1070 is_concrete && is_missing init
1072 (* Given a PropertyDeclaration node, tests whether parent class is abstract
1073 final but child variable is non-static *)
1075 let is_byref_expression node =
1076 is_decorated_expression ~f:is_ampersand node
1078 let is_byref_parameter_variable node =
1079 (* TODO: This shouldn't be a decorated *expression* because we are not
1080 expecting an expression at all. We're expecting a declaration. *)
1081 is_byref_expression node
1083 let is_param_by_ref node =
1084 match syntax node with
1085 | ParameterDeclaration { parameter_name; _ } ->
1086 is_byref_parameter_variable parameter_name
1087 | _ -> false
1089 let attribute_constructor_name node =
1090 match syntax node with
1091 | ListItem {
1092 list_item = { syntax = ConstructorCall { constructor_call_type; _ }; _ }; _
1093 } -> Syntax.extract_text constructor_call_type
1094 | _ -> None
1096 let attribute_specification_contains node name =
1097 match syntax node with
1098 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
1099 List.exists (syntax_node_to_list attrs) ~f:begin fun n ->
1100 attribute_constructor_name n = Some name
1102 | _ -> false
1104 let fold_attribute_spec node ~f ~init =
1105 match syntax node with
1106 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
1107 List.fold (syntax_node_to_list attrs) ~init ~f
1108 | _ -> init
1110 let methodish_contains_attribute node attribute =
1111 match node with
1112 | MethodishDeclaration { methodish_attribute = attr_spec; _ } ->
1113 attribute_specification_contains attr_spec attribute
1114 | _ -> false
1116 let methodish_memoize_lsb_on_non_static node errors =
1117 if methodish_contains_attribute (syntax node) SN.UserAttributes.uaMemoizeLSB &&
1118 not (methodish_contains_static node)
1119 then
1120 let e = make_error_from_node node SyntaxError.memoize_lsb_on_non_static
1121 in e :: errors
1122 else errors
1124 let function_declaration_contains_attribute node attribute =
1125 match syntax node with
1126 | FunctionDeclaration { function_attribute_spec = attr_spec; _ } ->
1127 attribute_specification_contains attr_spec attribute
1128 | _ -> false
1130 let methodish_contains_memoize env node _context =
1131 (is_typechecker env) && is_inside_interface env.context
1132 && (methodish_contains_attribute node SN.UserAttributes.uaMemoize)
1134 let is_some_reactivity_attribute_name name =
1135 name = SN.UserAttributes.uaReactive ||
1136 name = SN.UserAttributes.uaShallowReactive ||
1137 name = SN.UserAttributes.uaLocalReactive ||
1138 name = SN.UserAttributes.uaNonRx
1140 let attribute_matches_criteria f n =
1141 match syntax n with
1142 | ListItem {
1143 list_item = {
1144 syntax = ConstructorCall {
1145 constructor_call_type;
1146 constructor_call_argument_list = args; _
1147 }; _
1148 }; _
1149 } ->
1150 begin match Syntax.extract_text constructor_call_type with
1151 | Some n when f n -> Some args
1152 | _ -> None
1154 | _ -> None
1156 let is_some_reactivity_attribute n =
1157 attribute_matches_criteria is_some_reactivity_attribute_name n
1158 |> Option.is_some
1160 let attribute_first_reactivity_annotation attr_spec =
1161 match syntax attr_spec with
1162 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
1163 List.find (syntax_node_to_list attrs) ~f:is_some_reactivity_attribute
1164 | _ -> None
1167 let attribute_has_reactivity_annotation attr_spec =
1168 Option.is_some (attribute_first_reactivity_annotation attr_spec)
1170 let attribute_missing_reactivity_for_condition attr_spec =
1171 let has_attr attr = attribute_specification_contains attr_spec attr in
1172 not (attribute_has_reactivity_annotation attr_spec) && (
1173 has_attr SN.UserAttributes.uaOnlyRxIfImpl ||
1174 has_attr SN.UserAttributes.uaAtMostRxAsArgs
1177 let error_if_memoize_function_returns_mutable attrs errors =
1178 let (has_memoize, mutable_node, mut_return_node) =
1179 fold_attribute_spec attrs ~init:(false, None, None) ~f:(
1180 fun ((has_memoize, mutable_node, mut_return_node) as acc) node ->
1181 match attribute_constructor_name node with
1182 | Some n when n = SN.UserAttributes.uaMutableReturn ->
1183 (has_memoize, mutable_node, (Some node))
1184 | Some n when n = SN.UserAttributes.uaMemoize ||
1185 n = SN.UserAttributes.uaMemoizeLSB ->
1186 (true, mutable_node, mut_return_node)
1187 | Some n when n = SN.UserAttributes.uaMutable ->
1188 (has_memoize, Some node, mut_return_node)
1189 | _ -> acc
1190 ) in
1191 if has_memoize
1192 then
1193 let errors =
1194 match mutable_node with
1195 | Some n ->
1196 make_error_from_node
1197 n (SyntaxError.mutable_parameter_in_memoize_function ~is_this:true) :: errors
1198 | None -> errors in
1199 let errors =
1200 match mut_return_node with
1201 | Some n ->
1202 make_error_from_node n SyntaxError.mutable_return_in_memoize_function :: errors
1203 | None -> errors in
1204 errors
1205 else errors
1207 let methodish_missing_reactivity_for_condition node =
1208 match syntax node with
1209 | MethodishDeclaration { methodish_attribute = attr_spec; _ } ->
1210 attribute_missing_reactivity_for_condition attr_spec
1211 | _ -> false
1213 let methodish_contains_owned_mutable_attribute node =
1214 methodish_contains_attribute (syntax node) SN.UserAttributes.uaOwnedMutable
1216 let check_nonrx_annotation node errors =
1217 let err_decl () =
1218 let e = make_error_from_node node
1219 SyntaxError.invalid_non_rx_argument_for_declaration in
1220 e :: errors in
1221 let err_lambda () =
1222 let e = make_error_from_node node
1223 SyntaxError.invalid_non_rx_argument_for_lambda in
1224 e :: errors in
1225 let attr_spec =
1226 match syntax node with
1227 | MethodishDeclaration { methodish_attribute = s; _ }
1228 | FunctionDeclaration { function_attribute_spec = s; _ } ->
1229 Some (syntax s, true)
1230 | AnonymousFunction { anonymous_attribute_spec = s; _ }
1231 | LambdaExpression { lambda_attribute_spec = s; _ }
1232 | AwaitableCreationExpression { awaitable_attribute_spec = s; _ } ->
1233 Some (syntax s, false)
1234 | _ -> None in
1235 match attr_spec with
1236 | Some (AttributeSpecification { attribute_specification_attributes = attrs; _ }, is_decl) ->
1237 (* try find argument list *)
1238 let args =
1239 List.find_map (syntax_node_to_list attrs)
1240 ~f:(attribute_matches_criteria ((=)SN.UserAttributes.uaNonRx)) in
1241 begin match args with
1242 (* __NonRx attribute not found *)
1243 | None -> errors
1244 (* __NonRx attribute is found and argument list is empty.
1245 This is ok for lambdas but error for declarations *)
1246 | Some { syntax = Missing; _ } ->
1247 if is_decl then err_decl ()
1248 else errors
1249 (* __NonRx attribute is found with single string argument.
1250 This is ok for declarations for not allowed for lambdas *)
1251 | Some { syntax = SyntaxList [
1252 { syntax = ListItem {
1253 list_item = { syntax = LiteralExpression {
1254 literal_expression = { syntax = Token token; _ }; _
1255 }; _ }; _
1256 }; _ }
1257 ]; _ } when Token.kind token = TokenKind.DoubleQuotedStringLiteral ||
1258 Token.kind token = TokenKind.SingleQuotedStringLiteral ->
1259 if is_decl then errors
1260 else err_lambda ()
1261 (* __NonRx attribute is found but argument list is not suitable
1262 nor for declarations, neither for lambdas *)
1263 | Some _ ->
1264 if is_decl then err_decl ()
1265 else err_lambda ()
1267 | _ -> errors
1269 let function_missing_reactivity_for_condition node =
1270 match syntax node with
1271 | FunctionDeclaration { function_attribute_spec = attr_spec; _ } ->
1272 attribute_missing_reactivity_for_condition attr_spec
1273 | _ -> false
1275 let function_declaration_contains_only_rx_if_impl_attribute node =
1276 function_declaration_contains_attribute node SN.UserAttributes.uaOnlyRxIfImpl
1278 let function_declaration_contains_owned_mutable_attribute node =
1279 function_declaration_contains_attribute node SN.UserAttributes.uaOwnedMutable
1281 let attribute_multiple_reactivity_annotations attr_spec =
1282 let rec check l seen =
1283 match l with
1284 | [] -> false
1285 | x :: xs when is_some_reactivity_attribute x ->
1286 if seen then true else check xs true
1287 | _ :: xs -> check xs seen in
1288 match syntax attr_spec with
1289 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
1290 check (syntax_node_to_list attrs) false
1291 | _ -> false
1293 let methodish_multiple_reactivity_annotations node =
1294 match syntax node with
1295 | MethodishDeclaration { methodish_attribute = attr_spec; _ } ->
1296 attribute_multiple_reactivity_annotations attr_spec
1297 | _ -> false
1299 let function_multiple_reactivity_annotations node =
1300 match syntax node with
1301 | FunctionDeclaration { function_attribute_spec = attr_spec; _ } ->
1302 attribute_multiple_reactivity_annotations attr_spec
1303 | _ -> false
1305 let function_declaration_header_memoize_lsb context errors =
1306 match context.active_methodish, context.active_classish with
1307 | Some node, None (* a function, not a method *)
1308 when function_declaration_contains_attribute node SN.UserAttributes.uaMemoizeLSB ->
1309 let e = make_error_from_node node SyntaxError.memoize_lsb_on_non_method
1310 in e :: errors
1311 | _ -> errors
1313 let special_method_param_errors node context errors =
1314 match syntax node with
1315 | FunctionDeclarationHeader {function_name; function_parameter_list; _}
1316 when SSet.mem (String.lowercase @@ text function_name)
1317 SN.Members.as_lowercase_set ->
1318 let params = syntax_to_list_no_separators function_parameter_list in
1319 let len = List.length params in
1320 let name = text function_name in
1321 let full_name = match first_parent_class_name context with
1322 | None -> name
1323 | Some c_name -> c_name ^ "::" ^ name ^ "()"
1325 let s = String.lowercase name in
1326 let num_args_opt =
1327 match s with
1328 | _ when s = SN.Members.__call && len <> 2 -> Some 2
1329 | _ when s = SN.Members.__get && len <> 1 -> Some 1
1330 | _ when s = SN.Members.__set && len <> 2 -> Some 2
1331 | _ when s = SN.Members.__isset && len <> 1 -> Some 1
1332 | _ when s = SN.Members.__unset && len <> 1 -> Some 1
1333 | _ -> None
1335 let errors = match num_args_opt with
1336 | None -> errors
1337 | Some n ->
1338 make_error_from_node
1339 node (SyntaxError.invalid_number_of_args full_name n) :: errors
1341 let errors = if (s = SN.Members.__call
1342 || s = SN.Members.__get
1343 || s = SN.Members.__set
1344 || s = SN.Members.__isset
1345 || s = SN.Members.__unset)
1346 && List.exists ~f:is_param_by_ref params then
1347 make_error_from_node
1348 node (SyntaxError.invalid_args_by_ref full_name) :: errors
1349 else errors
1351 errors
1352 | _ -> errors
1354 let is_in_reified_class context =
1355 match context.active_classish with
1356 | Some { syntax = ClassishDeclaration {
1357 classish_type_parameters = {
1358 syntax = TypeParameters {
1359 type_parameters_parameters = l; _ }; _}; _ }; _ } ->
1360 syntax_to_list_no_separators l
1361 |> List.exists ~f:(fun p ->
1362 match syntax p with
1363 | TypeParameter { type_reified; _} -> not @@ is_missing type_reified
1364 | _ -> false
1366 | _ -> false
1368 let methodish_errors env node errors =
1369 match syntax node with
1370 (* TODO how to narrow the range of error *)
1371 | FunctionDeclarationHeader { function_parameter_list; function_type; _} ->
1372 let errors =
1373 produce_error_for_header errors
1374 (class_constructor_destructor_has_non_void_type env)
1375 node env.context SyntaxError.error2018 function_type in
1376 let errors =
1377 produce_error_for_header errors class_non_constructor_has_visibility_param
1378 node env.context SyntaxError.error2010 function_parameter_list in
1379 let errors =
1380 match class_constructor_param_promotion_clash (syntax node) env.context with
1381 | Some clashing_name ->
1382 let class_name = Option.value (active_classish_name env.context) ~default:"" in
1383 let error_msg = SyntaxError.error2025 class_name clashing_name in
1384 let error = (make_error_from_node function_parameter_list error_msg) in
1385 error :: errors
1386 | None -> errors
1388 let errors =
1389 produce_error_for_header errors abstract_class_constructor_has_visibility_param
1390 node env.context SyntaxError.error2023 function_parameter_list in
1391 let errors =
1392 produce_error_for_header errors interface_or_trait_has_visibility_param
1393 node env.context SyntaxError.error2024 function_parameter_list in
1394 let errors =
1395 function_declaration_header_memoize_lsb env.context errors in
1396 errors
1397 | FunctionDeclaration fd ->
1398 let function_attrs = fd.function_attribute_spec in
1399 let errors =
1400 produce_error errors
1401 function_multiple_reactivity_annotations node
1402 SyntaxError.multiple_reactivity_annotations function_attrs in
1403 let errors =
1404 error_if_memoize_function_returns_mutable function_attrs errors in
1405 let errors =
1406 produce_error errors
1407 function_declaration_contains_only_rx_if_impl_attribute node
1408 SyntaxError.functions_cannot_implement_reactive function_attrs in
1409 let errors =
1410 check_nonrx_annotation node errors in
1411 let errors =
1412 produce_error errors
1413 function_missing_reactivity_for_condition node
1414 SyntaxError.missing_reactivity_for_condition function_attrs in
1415 let errors =
1416 produce_error errors
1417 function_declaration_contains_owned_mutable_attribute node
1418 SyntaxError.misplaced_owned_mutable function_attrs in
1419 errors
1420 | MethodishDeclaration md ->
1421 let header_node = md.methodish_function_decl_header in
1422 let modifiers = modifiers_of_function_decl_header_exn header_node in
1423 let class_name = Option.value (active_classish_name env.context)
1424 ~default:"" in
1425 let method_name = Option.value (extract_function_name
1426 md.methodish_function_decl_header) ~default:"" in
1427 let method_attrs = md.methodish_attribute in
1428 let errors =
1429 error_if_memoize_function_returns_mutable method_attrs errors in
1430 let errors =
1431 produce_error_for_header errors
1432 (methodish_contains_memoize env)
1433 node env.context SyntaxError.interface_with_memoize header_node in
1434 let errors =
1435 produce_error_for_header errors
1436 (class_constructor_has_static) header_node
1437 env.context (SyntaxError.error2009 class_name method_name) modifiers in
1438 let errors =
1439 produce_error_for_header errors
1440 (class_destructor_cannot_be_static)
1441 header_node env.context
1442 (SyntaxError.class_destructor_cannot_be_static class_name method_name) modifiers in
1443 let errors =
1444 produce_error_for_header errors async_magic_method header_node env.context
1445 (SyntaxError.async_magic_method ~name:method_name) modifiers in
1446 let errors =
1447 produce_error_for_header errors call_static_method header_node env.context
1448 (SyntaxError.call_static_method) modifiers in
1449 let errors =
1450 produce_error_for_header errors
1451 (clone_destruct_takes_no_arguments method_name) header_node env.context
1452 (SyntaxError.clone_destruct_takes_no_arguments class_name method_name) modifiers in
1453 let errors =
1454 produce_error_for_header errors (clone_cannot_be_static) header_node env.context
1455 (SyntaxError.clone_cannot_be_static class_name method_name) modifiers in
1456 let errors =
1457 multiple_visibility_errors modifiers SyntaxError.error2017 errors in
1458 let errors =
1459 multiple_modifiers_errors modifiers SyntaxError.error2013 errors in
1460 let errors =
1461 if methodish_contains_static node && (
1462 attribute_specification_contains method_attrs SN.UserAttributes.uaMutable ||
1463 attribute_specification_contains method_attrs SN.UserAttributes.uaMaybeMutable
1465 then make_error_from_node node
1466 SyntaxError.mutability_annotation_on_static_method :: errors
1467 else errors in
1468 let errors =
1469 if String.lowercase method_name = SN.Members.__construct && (
1470 attribute_specification_contains method_attrs SN.UserAttributes.uaMutable ||
1471 attribute_specification_contains method_attrs SN.UserAttributes.uaMaybeMutable ||
1472 attribute_specification_contains method_attrs SN.UserAttributes.uaMutableReturn
1474 then make_error_from_node node
1475 SyntaxError.mutability_annotation_on_constructor :: errors
1476 else errors in
1477 let fun_semicolon = md.methodish_semicolon in
1478 let errors =
1479 produce_error errors
1480 (methodish_non_abstract_without_body_not_native env node) ()
1481 (SyntaxError.error2015 class_name method_name) fun_semicolon in
1482 let errors =
1483 produce_error errors
1484 methodish_abstract_conflict_with_private
1485 node (SyntaxError.error2016 class_name method_name) modifiers in
1486 let errors =
1487 produce_error errors
1488 methodish_abstract_conflict_with_final
1489 node (SyntaxError.error2019 class_name method_name) modifiers in
1490 let errors =
1491 produce_error errors
1492 (methodish_abstract_inside_interface env.context)
1493 node SyntaxError.error2045 modifiers in
1494 let errors =
1495 methodish_memoize_lsb_on_non_static node errors in
1496 let async_annotation = Option.value (extract_async_node node) ~default:node in
1497 let errors =
1498 produce_error errors
1499 (is_interface_and_async_method node) env.context
1500 (SyntaxError.error2046 "a method in an interface") async_annotation in
1501 let errors =
1502 produce_error errors
1503 (is_abstract_and_async_method node) env.context
1504 (SyntaxError.error2046 "an abstract method") async_annotation in
1505 let errors =
1506 if is_typechecker env
1507 then produce_error errors
1508 contains_async_not_last
1509 modifiers SyntaxError.async_not_last modifiers
1510 else errors in
1511 let errors =
1512 special_method_param_errors
1513 md.methodish_function_decl_header env.context errors in
1514 let errors =
1515 produce_error errors
1516 methodish_multiple_reactivity_annotations node
1517 SyntaxError.multiple_reactivity_annotations method_attrs in
1518 let errors =
1519 check_nonrx_annotation node errors in
1520 let errors =
1521 produce_error errors
1522 methodish_missing_reactivity_for_condition node
1523 SyntaxError.missing_reactivity_for_condition method_attrs in
1524 let errors =
1525 produce_error errors
1526 methodish_contains_owned_mutable_attribute node
1527 SyntaxError.misplaced_owned_mutable method_attrs in
1528 errors
1529 | _ -> errors
1531 let is_hashbang text =
1532 match Syntax.extract_text text with
1533 | None -> false
1534 | Some text ->
1535 let r = Str.regexp "^#!.*\n" in
1536 let count = List.length @@ String_utils.split_on_newlines text in
1537 count = 1 && Str.string_match r text 0 && Str.matched_string text = text
1539 let class_has_a_construct_method context =
1540 match first_parent_classish_node TokenKind.Class context with
1541 | Some ({ syntax = ClassishDeclaration
1542 { classish_body =
1543 { syntax = ClassishBody
1544 { classish_body_elements = methods; _}; _}; _}; _}) ->
1545 let methods = syntax_to_list_no_separators methods in
1546 List.exists methods ~f:(function
1547 { syntax = MethodishDeclaration
1548 { methodish_function_decl_header =
1549 { syntax = FunctionDeclarationHeader
1550 { function_name; _}; _}; _}; _} ->
1551 String.lowercase @@ text function_name = SN.Members.__construct
1552 | _ -> false)
1553 | _ -> false
1555 let is_in_construct_method context = if is_immediately_in_lambda context then false else
1556 match first_parent_function_name context, first_parent_class_name context with
1557 | None, _ -> false
1558 (* Function name is __construct *)
1559 | Some s, _ when String.lowercase s = SN.Members.__construct -> true
1560 (* Function name is same as class name *)
1561 | Some s1, Some s2 ->
1562 context.nested_namespaces = [] &&
1563 not @@ class_has_a_construct_method context &&
1564 String.lowercase s1 = String.lowercase s2
1565 | _ -> false
1568 (* If a variadic parameter has a default value, return it *)
1569 let variadic_param_with_default_value params =
1570 Option.filter (variadic_param params) ~f:is_parameter_with_default_value
1572 (* If a variadic parameter is marked inout, return it *)
1573 let variadic_param_with_callconv params =
1574 Option.filter (variadic_param params) ~f:is_parameter_with_callconv
1576 (* If an inout parameter has a default, return the default *)
1577 let param_with_callconv_has_default node =
1578 match syntax node with
1579 | ParameterDeclaration { parameter_default_value; _ } when
1580 is_parameter_with_callconv node &&
1581 is_parameter_with_default_value node -> Some parameter_default_value
1582 | _ -> None
1584 (* If an inout parameter is passed by reference, return it *)
1585 let param_with_callconv_is_byref node =
1586 match syntax node with
1587 | ParameterDeclaration { parameter_name; _ } when
1588 is_parameter_with_callconv node &&
1589 is_byref_parameter_variable parameter_name -> Some node
1590 | _ -> None
1592 let params_errors _env params _namespace_name names errors =
1593 let errors =
1594 produce_error_from_check errors ends_with_variadic_comma
1595 params SyntaxError.error2022 in
1596 let errors =
1597 produce_error_from_check errors misplaced_variadic_param
1598 params SyntaxError.error2021 in
1599 let errors =
1600 produce_error_from_check errors variadic_param_with_default_value
1601 params SyntaxError.error2065 in
1602 let errors =
1603 produce_error_from_check errors variadic_param_with_callconv
1604 params SyntaxError.error2073 in
1605 let param_list = syntax_to_list_no_separators params in
1606 let has_inout_param, has_reference_param, has_inout_and_ref_param =
1607 List.fold_right param_list ~init:(false, false, false)
1608 ~f:begin fun p (b1, b2, b3) ->
1609 let is_inout = is_parameter_with_callconv p in
1610 let is_ref = is_param_by_ref p in
1611 b1 || is_inout, b2 || is_ref, b3 || (is_inout && is_ref)
1614 let errors = if has_inout_param && has_reference_param then
1615 let error_type = if has_inout_and_ref_param then
1616 SyntaxError.ParseError else SyntaxError.RuntimeError in
1617 make_error_from_node ~error_type
1618 params SyntaxError.fn_with_inout_and_ref_params :: errors
1619 else errors
1621 names, errors
1623 let decoration_errors node errors =
1624 let errors = produce_error errors is_double_variadic node SyntaxError.double_variadic node in
1625 let errors = produce_error errors is_double_reference node SyntaxError.double_reference node in
1626 errors
1628 let parameter_rx_errors context errors node =
1629 match syntax node with
1630 | ParameterDeclaration { parameter_attribute = spec; parameter_name = name; _ } ->
1631 let has_owned_mutable =
1632 attribute_specification_contains spec SN.UserAttributes.uaOwnedMutable in
1633 let errors =
1634 let has_mutable =
1635 attribute_specification_contains spec SN.UserAttributes.uaMutable in
1636 let has_maybemutable =
1637 attribute_specification_contains spec SN.UserAttributes.uaMaybeMutable in
1638 let errors =
1639 match has_mutable, has_owned_mutable, has_maybemutable with
1640 | true, true, _ ->
1641 make_error_from_node node
1642 SyntaxError.conflicting_mutable_and_owned_mutable_attributes :: errors
1643 | true, _, true ->
1644 make_error_from_node node
1645 SyntaxError.conflicting_mutable_and_maybe_mutable_attributes :: errors
1646 | _, true, true ->
1647 make_error_from_node node
1648 SyntaxError.conflicting_owned_mutable_and_maybe_mutable_attributes :: errors
1649 | _ -> errors in
1650 let errors =
1651 if (has_mutable || has_owned_mutable || has_maybemutable) &&
1652 is_variadic_expression name
1653 then make_error_from_node name SyntaxError.vararg_and_mutable :: errors
1654 else errors in
1655 let is_inout = is_parameter_with_callconv node in
1656 let errors =
1657 if is_inout && (has_mutable || has_maybemutable || has_owned_mutable)
1658 then make_error_from_node
1659 node SyntaxError.mutability_annotation_on_inout_parameter :: errors
1660 else errors in
1662 let errors =
1663 if has_owned_mutable || has_mutable
1664 then begin
1665 let attrs = context.active_callable_attr_spec in
1666 let active_is_rx = context.active_is_rx_or_enclosing_for_lambdas in
1667 let parent_func_is_memoize =
1668 Option.value_map attrs ~default:false ~f:(fun spec ->
1669 attribute_specification_contains spec SN.UserAttributes.uaMemoize ||
1670 attribute_specification_contains spec SN.UserAttributes.uaMemoize
1671 ) in
1672 let errors =
1673 if has_owned_mutable && not active_is_rx
1674 then make_error_from_node node
1675 SyntaxError.mutably_owned_attribute_on_non_rx_function :: errors
1676 else errors
1678 let errors =
1679 if has_mutable && parent_func_is_memoize
1680 then make_error_from_node node
1681 (SyntaxError.mutable_parameter_in_memoize_function ~is_this:false) :: errors
1682 else errors
1684 errors
1686 else errors in
1687 errors
1689 errors
1690 | _ -> errors
1692 let does_binop_create_write_on_left = function
1693 | Some (TokenKind.Equal
1694 | TokenKind.BarEqual
1695 | TokenKind.PlusEqual
1696 | TokenKind.StarEqual
1697 | TokenKind.StarStarEqual
1698 | TokenKind.SlashEqual
1699 | TokenKind.DotEqual
1700 | TokenKind.MinusEqual
1701 | TokenKind.PercentEqual
1702 | TokenKind.CaratEqual
1703 | TokenKind.AmpersandEqual
1704 | TokenKind.LessThanLessThanEqual
1705 | TokenKind.GreaterThanGreaterThanEqual
1706 | TokenKind.QuestionQuestionEqual) -> true
1707 | _ -> false
1709 let does_unop_create_write = function
1710 | Some (TokenKind.PlusPlus | TokenKind.MinusMinus | TokenKind.Ampersand) -> true
1711 | _ -> false
1713 let does_decorator_create_write = function
1714 | Some (TokenKind.Inout) -> true
1715 | _ -> false
1717 type lval_type = LvalTypeNone | LvalTypeNonFinal | LvalTypeFinal | LvalTypeNonFinalInout
1719 let node_lval_type node parents =
1720 let rec is_in_final_lval_position node parents =
1721 match parents with
1722 | ExpressionStatement _ :: _ -> true
1723 | ForStatement {
1724 for_initializer = { syntax = for_initializer; _ };
1725 for_end_of_loop = { syntax = for_end_of_loop; _ }; _
1726 } :: _
1727 when phys_equal for_initializer node || phys_equal for_end_of_loop node ->
1728 true
1729 | (UsingStatementFunctionScoped { using_function_expression = { syntax = e; _ }; _ }
1730 | UsingStatementBlockScoped { using_block_expressions = { syntax = e; _ }; _ }) :: _
1731 when phys_equal e node -> true
1732 | (SyntaxList _ | ListItem _) as node :: parents ->
1733 is_in_final_lval_position node parents
1734 | _ -> false
1736 let rec get_arg_call_node_with_parents node parents =
1737 match parents with
1738 | (SyntaxList _ | ListItem _) as next_node :: next_parents ->
1739 get_arg_call_node_with_parents next_node next_parents
1740 | (FunctionCallExpression {
1741 function_call_argument_list = { syntax = arg_list; _ }; _ }
1742 ) as call_expression :: parents when phys_equal arg_list node ->
1743 (match parents with
1744 | PrefixUnaryExpression {
1745 prefix_unary_operator = token; _
1746 } as next_node :: next_parents
1747 when Some TokenKind.At = token_kind token ->
1748 Some (next_node, next_parents)
1749 | _ ->
1750 Some (call_expression, parents))
1751 | _ -> None
1753 let lval_ness_of_function_arg_for_inout next_node next_parents =
1754 (match get_arg_call_node_with_parents next_node next_parents with
1755 | None -> LvalTypeNone
1756 | Some (call_node, call_parents) ->
1757 if is_in_final_lval_position call_node call_parents
1758 then LvalTypeFinal
1759 else (match call_parents with
1760 | BinaryExpression {
1761 binary_operator = token;
1762 binary_right_operand = { syntax = rval; _ };
1763 _ } as next_node :: next_parents
1764 when phys_equal rval call_node &&
1765 does_binop_create_write_on_left (token_kind token) ->
1766 if is_in_final_lval_position next_node next_parents
1767 then LvalTypeFinal
1768 else LvalTypeNonFinalInout
1769 | _ -> LvalTypeNonFinalInout)
1770 ) in
1771 match parents with
1772 | DecoratedExpression {
1773 decorated_expression_decorator = token;
1774 decorated_expression_expression = { syntax = lval; _ }
1775 } as next_node :: next_parents
1776 when phys_equal lval node &&
1777 does_decorator_create_write (token_kind token) ->
1778 lval_ness_of_function_arg_for_inout next_node next_parents
1780 | PrefixUnaryExpression {
1781 prefix_unary_operator = token;
1782 prefix_unary_operand = { syntax = lval; _ }
1783 } :: _
1784 when phys_equal lval node &&
1785 Some TokenKind.Ampersand = token_kind token ->
1786 LvalTypeNone
1788 | (PrefixUnaryExpression {
1789 prefix_unary_operator = token;
1790 prefix_unary_operand = { syntax = lval; _ }
1792 | PostfixUnaryExpression {
1793 postfix_unary_operator = token;
1794 postfix_unary_operand = { syntax = lval; _ }
1795 }) as next_node :: next_parents
1796 when phys_equal lval node &&
1797 does_unop_create_write (token_kind token) ->
1798 if is_in_final_lval_position next_node next_parents
1799 then LvalTypeFinal
1800 else LvalTypeNonFinal
1802 | BinaryExpression {
1803 binary_operator = token;
1804 binary_left_operand = { syntax = lval; _ };
1805 _ } as next_node :: next_parents
1806 when phys_equal lval node &&
1807 does_binop_create_write_on_left (token_kind token) ->
1808 if is_in_final_lval_position next_node next_parents
1809 then LvalTypeFinal
1810 else LvalTypeNonFinal
1812 | ForeachStatement {
1813 foreach_key = { syntax = key; _ };
1814 foreach_value = { syntax = value; _ };
1815 _ } :: _
1816 when phys_equal key node || phys_equal value node ->
1817 LvalTypeFinal
1819 | _ -> LvalTypeNone
1821 let lval_errors env syntax_node parents errors =
1822 if not (ParserOptions.disable_lval_as_an_expression env.parser_options) then errors else
1823 let node = syntax syntax_node in
1824 let parents = List.map ~f:syntax parents in
1825 match node_lval_type node parents with
1826 | LvalTypeFinal
1827 | LvalTypeNonFinalInout
1828 | LvalTypeNone -> errors
1829 | LvalTypeNonFinal ->
1830 make_error_from_node syntax_node SyntaxError.lval_as_expression :: errors
1833 let parameter_errors env node namespace_name names errors =
1834 match syntax node with
1835 | ParameterDeclaration p ->
1837 let callconv_text = Option.value (extract_callconv_node node) ~default:node
1838 |> text in
1839 let errors =
1840 produce_error_from_check errors param_with_callconv_has_default
1841 node (SyntaxError.error2074 callconv_text) in
1842 let errors = parameter_rx_errors env.context errors node in
1843 let errors =
1844 produce_error_from_check errors param_with_callconv_is_byref
1845 node (SyntaxError.error2075 callconv_text) in
1846 let names, errors =
1847 check_type_hint env p.parameter_type names errors in
1848 let errors = if is_parameter_with_callconv node then
1849 begin
1850 let errors =
1851 if is_inside_async_method env.context then
1852 make_error_from_node ~error_type:SyntaxError.RuntimeError
1853 node SyntaxError.inout_param_in_async :: errors
1854 else errors in
1855 let errors =
1856 if is_in_construct_method env.context then
1857 make_error_from_node ~error_type:SyntaxError.RuntimeError
1858 node SyntaxError.inout_param_in_construct :: errors
1859 else errors in
1860 let inMemoize = first_parent_function_attributes_contains
1861 env.context SN.UserAttributes.uaMemoize in
1862 let inMemoizeLSB = first_parent_function_attributes_contains
1863 env.context SN.UserAttributes.uaMemoizeLSB in
1864 let errors = if (inMemoize || inMemoizeLSB) &&
1865 not @@ is_immediately_in_lambda env.context then
1866 make_error_from_node ~error_type:SyntaxError.RuntimeError
1867 node SyntaxError.memoize_with_inout :: errors
1868 else errors in
1869 errors
1870 end else errors
1872 let errors =
1873 if is_reference_variadic p.parameter_name then
1874 make_error_from_node node SyntaxError.variadic_reference :: errors
1875 else if is_variadic_reference p.parameter_name then
1876 make_error_from_node node SyntaxError.reference_variadic :: errors
1877 else errors in
1878 names, errors
1879 | FunctionDeclarationHeader { function_parameter_list = params; _ }
1880 | AnonymousFunction { anonymous_parameters = params; _ }
1881 | ClosureTypeSpecifier { closure_parameter_list = params; _ }
1882 | LambdaExpression
1883 { lambda_signature = {syntax = LambdaSignature { lambda_parameters = params; _ }; _}
1885 } ->
1886 let errors =
1887 syntax_to_list_no_separators params
1888 |> List.fold_left ~init:errors ~f:(parameter_rx_errors env.context) in
1889 params_errors env params namespace_name names errors
1890 | DecoratedExpression _ -> names, decoration_errors node errors
1891 | _ -> names, errors
1894 let redeclaration_errors env node parents namespace_name names errors =
1895 match syntax node with
1896 | FunctionDeclarationHeader f when not (is_missing f.function_name)->
1897 begin match parents with
1898 | { syntax = FunctionDeclaration _; _}
1899 :: _ :: {syntax = NamespaceBody _; _} :: _
1900 | [{ syntax = FunctionDeclaration _; _ }; _; _]
1901 | { syntax = MethodishDeclaration _; _ } :: _
1902 | { syntax = MethodishTraitResolution _; _ } :: _ ->
1903 let function_name = text f.function_name in
1904 let location = make_location_of_node f.function_name in
1905 let is_method = match parents with
1906 | { syntax = MethodishDeclaration _; _ } :: _ -> true
1907 | _ -> false
1909 let def = make_first_use_or_def ~is_method
1910 ~kind:Name_def location namespace_name function_name in
1911 let errors =
1912 match strmap_get function_name names.t_functions with
1913 | Some { f_location = { start_offset; _}; f_kind = Name_def; f_global; _ }
1914 when f_global = def.f_global ->
1915 let text = SyntaxTree.text env.syntax_tree in
1916 let line, _ =
1917 Full_fidelity_source_text.offset_to_position text start_offset in
1918 let path =
1919 Relative_path.to_absolute @@
1920 Full_fidelity_source_text.file_path text in
1921 let loc = path ^ ":" ^ string_of_int line in
1922 let err, error_type =
1923 match first_parent_class_name env.context with
1924 | None ->
1925 SyntaxError.redeclaration_of_function ~name:function_name ~loc,
1926 SyntaxError.RuntimeError
1927 | Some class_name ->
1928 let full_name = class_name ^ "::" ^ function_name in
1929 SyntaxError.redeclaration_of_method ~name:full_name,
1930 SyntaxError.ParseError
1932 make_error_from_node ~error_type node err :: errors
1933 | _ -> errors
1935 { names with
1936 t_functions = strmap_add function_name def names.t_functions }, errors
1937 | _ ->
1938 if not (is_typechecker env) then names, errors else
1939 let error = make_error_from_node
1940 ~error_type:SyntaxError.ParseError
1941 node
1942 SyntaxError.decl_outside_global_scope
1944 names, error :: errors
1946 | _ -> names, errors
1948 let is_foreach_in_for for_initializer =
1949 match syntax_node_to_list for_initializer with
1950 | ( { syntax = ListItem { list_item = item; _ }; _ } :: _) ->
1951 is_as_expression item
1952 | _ -> false
1954 let statement_errors env node parents errors =
1955 let result = match syntax node with
1956 | TryStatement { try_catch_clauses; try_finally_clause; _ }
1957 when (is_missing try_catch_clauses) && (is_missing try_finally_clause) ->
1958 Some (node, SyntaxError.error2007)
1959 | UsingStatementFunctionScoped _
1960 when not (using_statement_function_scoped_is_legal parents) ->
1961 Some (node, SyntaxError.using_st_function_scoped_top_level)
1962 | ForStatement { for_initializer ; _ }
1963 when is_foreach_in_for for_initializer ->
1964 Some (node, SyntaxError.for_with_as_expression)
1965 | CaseLabel {
1966 case_colon = {
1967 syntax = Token m;
1969 } as colon;
1972 | DefaultLabel {
1973 default_colon = {
1974 syntax = Token m;
1976 } as colon;
1978 } when is_typechecker env && Token.kind m <> TokenKind.Colon ->
1979 Some (colon, SyntaxError.error1020)
1980 | _ -> None in
1981 match result with
1982 | None -> errors
1983 | Some (error_node, error_message) ->
1984 make_error_from_node error_node error_message :: errors
1986 let check_collection_element m error_text errors =
1987 match syntax m with
1988 | PrefixUnaryExpression
1989 { prefix_unary_operator = { syntax = Token token; _ }; _ }
1990 when Token.kind token = TokenKind.Ampersand ->
1991 make_error_from_node m error_text :: errors
1992 | _ -> errors
1994 let check_collection_member errors m =
1995 match syntax m with
1996 | ElementInitializer { element_key; element_value; _ } ->
1997 let errors =
1998 check_collection_element element_key
1999 SyntaxError.reference_not_allowed_on_key errors in
2000 let errors =
2001 check_collection_element element_value
2002 SyntaxError.reference_not_allowed_on_value errors in
2003 errors
2004 | _ ->
2005 check_collection_element m
2006 SyntaxError.reference_not_allowed_on_element errors
2008 let check_collection_members members errors =
2009 syntax_to_list_no_separators members
2010 |> List.fold_left ~init:errors ~f:check_collection_member
2012 let invalid_shape_initializer_name env node errors =
2013 match syntax node with
2014 | LiteralExpression { literal_expression = expr } ->
2015 let is_str =
2016 begin match token_kind expr with
2017 | Some TokenKind.SingleQuotedStringLiteral -> true
2018 (* TODO: Double quoted string are only legal
2019 * if they contain no encapsulated expressions. *)
2020 | Some TokenKind.DoubleQuotedStringLiteral -> true
2021 | _ -> false
2024 if not is_str
2025 then make_error_from_node node SyntaxError.invalid_shape_field_name :: errors
2026 else errors
2027 | ScopeResolutionExpression _ -> errors
2028 | QualifiedName _ ->
2029 if is_typechecker env then
2030 make_error_from_node node SyntaxError.invalid_shape_field_name :: errors
2031 else errors
2032 | Token _ when is_name node ->
2033 if is_typechecker env then
2034 make_error_from_node node SyntaxError.invalid_shape_field_name :: errors
2035 else
2036 errors
2037 | _ -> make_error_from_node node SyntaxError.invalid_shape_field_name :: errors
2039 let invalid_shape_field_check env node errors =
2040 match syntax node with
2041 | FieldInitializer { field_initializer_name; _} ->
2042 invalid_shape_initializer_name env field_initializer_name errors
2043 | _ -> make_error_from_node node SyntaxError.invalid_shape_field_name :: errors
2045 let is_in_unyieldable_magic_method context =
2046 match first_parent_function_name context with
2047 | None -> false
2048 | Some s ->
2049 let s = String.lowercase s in
2050 begin match s with
2051 | _ when s = SN.Members.__call -> false
2052 | _ when s = SN.Members.__invoke -> false
2053 | _ -> SSet.mem s SN.Members.as_lowercase_set
2056 let function_call_argument_errors ~in_constructor_call node errors =
2057 match syntax node with
2058 | PrefixUnaryExpression
2059 { prefix_unary_operator = { syntax = Token token; _ }
2060 ; prefix_unary_operand
2061 } when Token.kind token = TokenKind.Ampersand &&
2062 SN.Superglobals.is_superglobal @@ text prefix_unary_operand ->
2063 make_error_from_node node SyntaxError.error2078 :: errors
2064 | DecoratedExpression
2065 { decorated_expression_decorator = { syntax = Token token ; _ }
2066 ; decorated_expression_expression = expression
2067 } when Token.kind token = TokenKind.Inout ->
2068 let result =
2069 if in_constructor_call then Some SyntaxError.inout_param_in_construct else
2070 match syntax expression with
2071 | BinaryExpression _ ->
2072 Some SyntaxError.fun_arg_inout_set
2073 | QualifiedName _ ->
2074 Some SyntaxError.fun_arg_inout_const
2075 | Token _ when is_name expression ->
2076 Some SyntaxError.fun_arg_inout_const
2077 (* TODO: Maybe be more descriptive in error messages *)
2078 | ScopeResolutionExpression _
2079 | FunctionCallExpression _
2080 | MemberSelectionExpression _
2081 | SafeMemberSelectionExpression _
2082 | SubscriptExpression
2083 { subscript_receiver = {
2084 syntax =
2085 (MemberSelectionExpression _ | ScopeResolutionExpression _)
2087 }; _ } -> Some SyntaxError.fun_arg_invalid_arg
2088 | SubscriptExpression { subscript_receiver; _ }
2089 when SN.Superglobals.is_superglobal @@ text subscript_receiver ->
2090 Some SyntaxError.fun_arg_inout_containers
2091 | _ -> None
2093 begin match result with
2094 | None -> errors
2095 | Some e -> make_error_from_node node e :: errors
2097 | _ -> errors
2099 let function_call_on_xhp_name_errors env node errors =
2100 match syntax node with
2101 | MemberSelectionExpression { member_object; member_name = name; _ }
2102 | SafeMemberSelectionExpression {
2103 safe_member_object=member_object;
2104 safe_member_name = name; _ } ->
2105 let errors =
2106 begin match syntax member_object with
2107 | XHPExpression _ when is_typechecker env ->
2108 let e =
2109 make_error_from_node node SyntaxError.method_calls_on_xhp_expression in
2110 e::errors
2111 | _ -> errors
2112 end in
2113 begin match syntax name with
2114 | Token token when Token.kind token = TokenKind.XHPClassName ->
2115 let e =
2116 make_error_from_node node SyntaxError.method_calls_on_xhp_attributes in
2117 e :: errors
2118 | _ -> errors
2120 | _ -> errors
2122 let no_async_before_lambda_body env body_node errors =
2123 match syntax body_node with
2124 | AwaitableCreationExpression _ when is_typechecker env ->
2125 (make_error_from_node body_node SyntaxError.no_async_before_lambda_body)
2126 :: errors
2127 | _ -> errors
2129 let no_memoize_attribute_on_lambda node errors =
2130 match syntax node with
2131 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
2132 List.fold (syntax_node_to_list attrs) ~init:errors ~f:begin fun errors n ->
2133 match syntax n with
2134 | ListItem {
2135 list_item = ({ syntax = ConstructorCall { constructor_call_type; _ }; _ } as attr);_
2136 } ->
2137 begin match Syntax.extract_text constructor_call_type with
2138 | Some n when n = SN.UserAttributes.uaMemoize ->
2139 let e =
2140 make_error_from_node attr SyntaxError.memoize_on_lambda in
2141 e::errors
2142 | Some n when n = SN.UserAttributes.uaMemoizeLSB ->
2143 let e =
2144 make_error_from_node attr SyntaxError.memoize_on_lambda in
2145 e::errors
2146 | _ -> errors
2148 | _ -> errors
2150 | _ -> errors
2152 let is_good_scope_resolution_qualifier node =
2153 match syntax node with
2154 | QualifiedName _ -> true
2155 | Token token ->
2156 let open TokenKind in
2157 (match Token.kind token with
2158 | XHPClassName | Name | Self | Parent | Static -> true
2159 | _ -> false
2161 | _ -> false
2163 let new_variable_errors node =
2164 let rec helper node ~inside_scope_resolution =
2165 match syntax node with
2166 | SimpleTypeSpecifier _ -> []
2167 | VariableExpression _ -> []
2168 | GenericTypeSpecifier _ -> []
2169 | PipeVariableExpression _ -> []
2170 | SubscriptExpression { subscript_index = { syntax = Missing; _ }; _ } ->
2171 [ make_error_from_node node SyntaxError.instanceof_missing_subscript_index ]
2172 | SubscriptExpression { subscript_receiver; _ } ->
2173 helper subscript_receiver ~inside_scope_resolution
2174 | MemberSelectionExpression { member_object; _ } ->
2175 if inside_scope_resolution
2176 then [
2177 make_error_from_node node SyntaxError.instanceof_memberselection_inside_scoperesolution
2179 else helper member_object ~inside_scope_resolution
2180 | ScopeResolutionExpression
2182 scope_resolution_qualifier;
2183 scope_resolution_name = { syntax = Token name; _ };
2185 } when is_good_scope_resolution_qualifier scope_resolution_qualifier
2186 && Token.kind name = TokenKind.Variable -> []
2187 | ScopeResolutionExpression
2189 scope_resolution_qualifier;
2190 scope_resolution_name = { syntax = Token name; _ };
2192 } when Token.kind name = TokenKind.Variable ->
2193 helper scope_resolution_qualifier ~inside_scope_resolution:true
2194 | ScopeResolutionExpression _ ->
2195 [ make_error_from_node node SyntaxError.instanceof_invalid_scope_resolution ]
2197 | _ ->
2198 let error_msg = SyntaxError.instanceof_new_unknown_node
2199 (SyntaxKind.to_string @@ kind node) in
2200 [ make_error_from_node node error_msg ]
2202 helper node ~inside_scope_resolution:false
2204 let class_type_designator_errors node =
2205 if is_good_scope_resolution_qualifier node then [] else
2206 match syntax node with
2207 | ParenthesizedExpression
2209 parenthesized_expression_expression = {
2210 syntax = PrefixUnaryExpression { prefix_unary_operator = { syntax = Token token; _ }; _ };
2215 when Token.kind token = TokenKind.Ampersand ->
2216 [make_error_from_node node SyntaxError.instanceof_reference]
2217 | ParenthesizedExpression _ ->
2218 (* A parenthesized expression that evaluates to a string or object is a
2219 valid RHS for instanceof and new. *)
2221 | _ -> new_variable_errors node
2223 let rec check_reference node errors =
2224 match syntax node with
2225 | PrefixUnaryExpression { prefix_unary_operator; _ }
2226 when token_kind prefix_unary_operator <> Some TokenKind.Dollar ->
2227 make_error_from_node node SyntaxError.nested_unary_reference :: errors
2228 | FunctionCallExpression _
2229 | VariableExpression _ -> errors
2230 | Token token when Token.kind token = TokenKind.Variable -> errors
2231 | PrefixUnaryExpression {
2232 prefix_unary_operator = { syntax = Token token; _ };
2233 prefix_unary_operand = {
2234 syntax = PrefixUnaryExpression { prefix_unary_operator = op; _ };
2237 } when Token.kind token = TokenKind.Dollar && token_kind op = Some TokenKind.Dollar ->
2238 errors
2239 | PrefixUnaryExpression {
2240 prefix_unary_operator = { syntax = Token token; _ };
2241 prefix_unary_operand = { syntax = BracedExpression _ | VariableExpression _; _ }
2242 } when Token.kind token = TokenKind.Dollar -> errors
2243 | ParenthesizedExpression { parenthesized_expression_expression; _ } ->
2244 check_reference parenthesized_expression_expression errors
2245 | _ -> make_error_from_node node SyntaxError.invalid_reference :: errors
2247 let rec_walk ~init ~f node =
2248 let rec rec_walk_impl parents init node =
2249 let new_init, continue_walk = f init node parents in
2250 if continue_walk then
2251 List.fold_left
2252 ~init:new_init
2253 ~f:(rec_walk_impl ((syntax node) :: parents))
2254 (Syntax.children node)
2255 else new_init in
2256 rec_walk_impl [] init node
2258 let does_binop_create_write_on_left = function
2259 | Some (TokenKind.Equal
2260 | TokenKind.BarEqual
2261 | TokenKind.PlusEqual
2262 | TokenKind.StarEqual
2263 | TokenKind.StarStarEqual
2264 | TokenKind.SlashEqual
2265 | TokenKind.DotEqual
2266 | TokenKind.MinusEqual
2267 | TokenKind.PercentEqual
2268 | TokenKind.CaratEqual
2269 | TokenKind.AmpersandEqual
2270 | TokenKind.LessThanLessThanEqual
2271 | TokenKind.GreaterThanGreaterThanEqual
2272 | TokenKind.QuestionQuestionEqual) -> true
2273 | _ -> false
2275 let find_invalid_lval_usage nodes =
2276 let get_errors = rec_walk ~f:(fun errors syntax_node parents ->
2277 let node = syntax syntax_node in
2278 match node with
2279 | AnonymousFunction _
2280 | LambdaExpression _
2281 | AwaitableCreationExpression _ ->
2282 errors, false
2283 | _ ->
2284 let errors = match node_lval_type node parents with
2285 | LvalTypeFinal
2286 | LvalTypeNone -> errors
2287 | LvalTypeNonFinalInout
2288 | LvalTypeNonFinal ->
2289 make_error_from_node syntax_node SyntaxError.lval_as_expression :: errors in
2290 errors, true
2291 ) in
2293 List.fold_left
2294 ~init:[]
2295 ~f:(fun acc node -> get_errors ~init:acc node)
2296 nodes
2298 type binop_allows_await_in_positions =
2299 | BinopAllowAwaitBoth
2300 | BinopAllowAwaitLeft
2301 | BinopAllowAwaitRight
2302 | BinopAllowAwaitNone
2304 let get_positions_binop_allows_await t =
2305 (match token_kind t with
2306 | None -> BinopAllowAwaitNone
2307 | Some t -> (match t with
2308 | TokenKind.BarBar
2309 | TokenKind.AmpersandAmpersand
2310 | TokenKind.QuestionColon
2311 | TokenKind.QuestionQuestion
2312 | TokenKind.BarGreaterThan -> BinopAllowAwaitLeft
2313 | TokenKind.Equal
2314 | TokenKind.BarEqual
2315 | TokenKind.PlusEqual
2316 | TokenKind.StarEqual
2317 | TokenKind.StarStarEqual
2318 | TokenKind.SlashEqual
2319 | TokenKind.DotEqual
2320 | TokenKind.MinusEqual
2321 | TokenKind.PercentEqual
2322 | TokenKind.CaratEqual
2323 | TokenKind.AmpersandEqual
2324 | TokenKind.LessThanLessThanEqual
2325 | TokenKind.GreaterThanGreaterThanEqual -> BinopAllowAwaitRight
2326 | TokenKind.Plus
2327 | TokenKind.Minus
2328 | TokenKind.Star
2329 | TokenKind.Slash
2330 | TokenKind.StarStar
2331 | TokenKind.EqualEqualEqual
2332 | TokenKind.LessThan
2333 | TokenKind.GreaterThan
2334 | TokenKind.Percent
2335 | TokenKind.Dot
2336 | TokenKind.EqualEqual
2337 | TokenKind.ExclamationEqual
2338 | TokenKind.ExclamationEqualEqual
2339 | TokenKind.LessThanEqual
2340 | TokenKind.LessThanEqualGreaterThan
2341 | TokenKind.GreaterThanEqual
2342 | TokenKind.Ampersand
2343 | TokenKind.Bar
2344 | TokenKind.LessThanLessThan
2345 | TokenKind.GreaterThanGreaterThan
2346 | TokenKind.Carat -> BinopAllowAwaitBoth
2347 | TokenKind.QuestionQuestionEqual
2348 | _ -> BinopAllowAwaitNone
2351 let unop_allows_await t =
2352 (match token_kind t with
2353 | None -> false
2354 | Some t -> (match t with
2355 | TokenKind.Exclamation
2356 | TokenKind.Tilde
2357 | TokenKind.Plus
2358 | TokenKind.Minus
2359 | TokenKind.At
2360 | TokenKind.Clone
2361 | TokenKind.Print -> true
2362 | _ -> false
2365 let await_as_an_expression_errors await_node parents =
2366 let rec go parents node =
2367 let n, tail =
2368 match parents with
2369 | head :: tail -> head, tail
2370 | _ -> failwith "Unexpect missing parent"
2372 match syntax n with
2373 (* statements that root for the concurrently executed await expressions *)
2374 | ExpressionStatement _
2375 | ReturnStatement _
2376 | UnsetStatement _
2377 | EchoStatement _
2378 | ThrowStatement _
2379 | LetStatement _ -> []
2380 | IfStatement { if_condition; _ }
2381 when phys_equal node if_condition -> []
2382 | ForStatement { for_initializer; _ }
2383 when phys_equal node for_initializer -> []
2384 | SwitchStatement { switch_expression; _ }
2385 when phys_equal node switch_expression -> []
2386 | ForeachStatement { foreach_collection; _ }
2387 when phys_equal node foreach_collection -> []
2388 | UsingStatementBlockScoped { using_block_expressions; _ }
2389 when phys_equal node using_block_expressions -> []
2390 | UsingStatementFunctionScoped { using_function_expression; _ }
2391 when phys_equal node using_function_expression -> []
2392 | LambdaExpression { lambda_body; _ }
2393 when phys_equal node lambda_body -> []
2395 (* Dependent awaits are not allowed currently *)
2396 | PrefixUnaryExpression { prefix_unary_operator = op; _ }
2397 when token_kind op = Some TokenKind.Await ->
2398 [make_error_from_node
2399 await_node SyntaxError.invalid_await_position_dependent]
2401 (* Unary based expressions have their own custom fanout *)
2402 | PrefixUnaryExpression { prefix_unary_operator = operator; _ }
2403 | PostfixUnaryExpression { postfix_unary_operator = operator; _ }
2404 | DecoratedExpression { decorated_expression_decorator = operator; _ }
2405 when unop_allows_await operator -> go tail n
2407 left or right operand of binary expressions are considered legal locations
2408 if operator is not short-circuiting and containing expression
2409 is in legal location *)
2410 | BinaryExpression {
2411 binary_left_operand = l;
2412 binary_right_operand = r;
2413 binary_operator = op;
2415 when
2416 (match get_positions_binop_allows_await op with
2417 | BinopAllowAwaitBoth -> true
2418 | BinopAllowAwaitLeft -> phys_equal node l
2419 | BinopAllowAwaitRight -> phys_equal node r
2420 | BinopAllowAwaitNone -> false) ->
2421 go tail n
2422 (* test part of conditional expression is considered legal location if
2423 conditional expression itself is in legal location *)
2424 | ConditionalExpression {
2425 conditional_test = test; _
2426 } when phys_equal node test -> go tail n
2427 | FunctionCallExpression {
2428 function_call_receiver = r;
2429 function_call_argument_list = args; _
2431 when phys_equal node r ||
2432 (phys_equal node args && not (is_safe_member_selection_expression r))
2433 -> go tail n
2434 (* object of member selection expression or safe member selection expression
2435 is in legal position if member selection expression itself is in legal position *)
2436 | SafeMemberSelectionExpression {
2437 safe_member_object = o; _
2438 } when phys_equal node o -> go tail n
2439 (* These are nodes where any position is valid *)
2440 | CastExpression _
2441 | MemberSelectionExpression _
2442 | ScopeResolutionExpression _
2443 | InstanceofExpression _
2444 | IsExpression _
2445 | AsExpression _
2446 | NullableAsExpression _
2447 | IssetExpression _
2448 | ParenthesizedExpression _
2449 | BracedExpression _
2450 | EmbeddedBracedExpression _
2451 | CollectionLiteralExpression _
2452 | ObjectCreationExpression _
2453 | ConstructorCall _
2454 | ShapeExpression _
2455 | TupleExpression _
2456 | ArrayCreationExpression _
2457 | ArrayIntrinsicExpression _
2458 | DarrayIntrinsicExpression _
2459 | DictionaryIntrinsicExpression _
2460 | KeysetIntrinsicExpression _
2461 | VarrayIntrinsicExpression _
2462 | VectorIntrinsicExpression _
2463 | ElementInitializer _
2464 | FieldInitializer _
2465 | SimpleInitializer _
2466 | SubscriptExpression _
2467 | EmbeddedSubscriptExpression _
2468 | YieldExpression _
2469 | XHPExpression _
2470 | XHPOpen _
2471 | XHPSimpleAttribute _
2472 | XHPSpreadAttribute _
2473 | SyntaxList _
2474 | ListItem _ -> go tail n
2475 (* otherwise report error and bail out *)
2476 | _ ->
2477 [make_error_from_node
2478 await_node SyntaxError.invalid_await_position]
2481 let conditional_errors = go parents await_node in
2482 let is_in_concurrent = List.exists
2483 ~f:(fun parent -> match syntax parent with
2484 | ConcurrentStatement _ -> true
2485 | _ -> false
2486 ) parents in
2487 let lval_errors = if is_in_concurrent then [] else
2488 let await_node_statement_parent = List.find
2489 ~f:(fun parent -> match syntax parent with
2490 | ExpressionStatement _
2491 | ReturnStatement _
2492 | UnsetStatement _
2493 | EchoStatement _
2494 | ThrowStatement _
2495 | IfStatement _
2496 | ForStatement _
2497 | SwitchStatement _
2498 | ForeachStatement _ -> true
2499 | _ -> false
2500 ) parents in
2501 match await_node_statement_parent with
2502 | Some x -> find_invalid_lval_usage [x]
2503 (* We must have already errored in "go" *)
2504 | None -> []
2506 List.append lval_errors conditional_errors
2508 let node_has_await_child = rec_walk ~init:false ~f:(fun acc node _ ->
2509 let is_new_scope = match syntax node with
2510 | AnonymousFunction _
2511 | LambdaExpression _
2512 | AwaitableCreationExpression _ ->
2513 true
2514 | _ -> false in
2515 if is_new_scope then false, false else
2516 let is_await n = match syntax n with
2517 | PrefixUnaryExpression { prefix_unary_operator = op; _ }
2518 when token_kind op = Some TokenKind.Await -> true
2519 | _ -> false in
2520 let found_await = acc || is_await node in
2521 found_await, not found_await
2524 let expression_errors env _is_in_concurrent_block namespace_name node parents errors =
2525 let is_decimal_or_hexadecimal_literal token =
2526 match Token.kind token with
2527 | TokenKind.DecimalLiteral | TokenKind.HexadecimalLiteral -> true
2528 | _ -> false
2530 match syntax node with
2531 (* It is ambiguous what `instanceof (A)` means: either instanceof a type A
2532 * or instanceof a type whose name is what the constant A evaluates to.
2533 * We therefore simply disallow this. *)
2534 | InstanceofExpression
2535 { instanceof_right_operand =
2536 { syntax = ParenthesizedExpression
2537 { parenthesized_expression_expression =
2538 { syntax = Token _; _ } as in_paren
2539 ; _ }
2540 ; _ }
2541 ; _ } when is_typechecker env ->
2542 let in_paren = text in_paren in
2543 make_error_from_node node (SyntaxError.instanceof_paren in_paren) :: errors
2544 (* We parse the right hand side of `new` and `instanceof` as a generic
2545 expression, but PHP (and therefore Hack) only allow a certain subset of
2546 expressions, so we should verify here that the expression we parsed is in
2547 that subset.
2548 Refer: https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#instanceof-operator*)
2549 | ConstructorCall ctr_call ->
2550 let typechecker_errors =
2551 if is_typechecker env then
2552 match parents with
2553 (* list item -> syntax list -> attribute *)
2554 | _ :: _ :: a :: _ when
2555 is_attribute_specification a || is_file_attribute_specification a ->
2557 | _ ->
2558 if (is_missing ctr_call.constructor_call_left_paren ||
2559 is_missing ctr_call.constructor_call_right_paren)
2560 then
2561 let node = ctr_call.constructor_call_type in
2562 let constructor_name = text ctr_call.constructor_call_type in
2563 [make_error_from_node node (SyntaxError.error2038 constructor_name)]
2564 else []
2565 else []
2567 let designator_errors = class_type_designator_errors ctr_call.constructor_call_type in
2568 let func_errors =
2569 let arg_list = syntax_to_list_no_separators ctr_call.constructor_call_argument_list in
2570 List.fold_right arg_list ~init:[]
2571 ~f:(fun p acc -> function_call_argument_errors ~in_constructor_call:true p acc)
2573 typechecker_errors @ designator_errors @ func_errors @ errors
2574 | InstanceofExpression { instanceof_right_operand = operand; _ } ->
2575 (class_type_designator_errors operand) @ errors
2576 | LiteralExpression { literal_expression = {syntax = Token token; _} as e ; _}
2577 when is_decimal_or_hexadecimal_literal token ->
2578 let text = text e in
2579 begin try ignore (Int64.of_string text); errors
2580 with _ ->
2581 let error_text =
2582 if Token.kind token = TokenKind.DecimalLiteral
2583 then SyntaxError.error2071 text
2584 else SyntaxError.error2072 text in
2585 make_error_from_node node error_text :: errors
2587 | SubscriptExpression { subscript_left_bracket; _}
2588 when (is_typechecker env)
2589 && is_left_brace subscript_left_bracket ->
2590 make_error_from_node node SyntaxError.error2020 :: errors
2591 | HaltCompilerExpression { halt_compiler_argument_list = args; _ } ->
2592 let errors =
2593 if List.is_empty (syntax_to_list_no_separators args) then errors
2594 else make_error_from_node node SyntaxError.no_args_in_halt_compiler :: errors in
2595 let errors =
2596 match parents with
2597 (* expression statement -> syntax list -> script *)
2598 | [_; _; _] -> errors
2599 | _ -> make_error_from_node node SyntaxError.halt_compiler_top_level_only :: errors in
2600 errors
2601 | FunctionCallExpression {
2602 function_call_argument_list = arg_list;
2603 function_call_receiver; _
2604 } ->
2605 let errors =
2606 match misplaced_variadic_arg arg_list with
2607 | Some h ->
2608 make_error_from_node h SyntaxError.error2033 :: errors
2609 | None -> errors
2611 let arg_list = syntax_to_list_no_separators arg_list in
2612 let errors = List.fold_right arg_list ~init:errors
2613 ~f:(fun p acc -> function_call_argument_errors ~in_constructor_call:false p acc)
2615 let errors =
2616 function_call_on_xhp_name_errors env function_call_receiver errors in
2617 errors
2618 | ListExpression { list_members; _ }
2619 when is_missing list_members && is_hhvm_compat env ->
2620 begin match parents with
2621 | { syntax = ForeachStatement { foreach_value; _ }; _ } :: _
2622 when phys_equal node foreach_value ->
2623 make_error_from_node ~error_type:SyntaxError.RuntimeError node
2624 SyntaxError.error2077 :: errors
2625 | _ -> errors
2627 | ListExpression _ ->
2628 begin match parents with
2629 | e :: _ when is_return_statement e ->
2630 make_error_from_node node SyntaxError.list_must_be_lvar :: errors
2631 | _ -> errors
2633 | ShapeExpression { shape_expression_fields; _} ->
2634 List.fold_right ~f:(invalid_shape_field_check env)
2635 (syntax_to_list_no_separators shape_expression_fields) ~init:errors
2636 | DecoratedExpression
2637 { decorated_expression_decorator = decorator
2638 ; decorated_expression_expression =
2639 { syntax = PrefixUnaryExpression { prefix_unary_operator = operator; _ }
2640 ; _ }
2642 when is_inout decorator && is_ampersand operator ->
2643 make_error_from_node node SyntaxError.error2076 :: errors
2644 | VectorIntrinsicExpression { vector_intrinsic_members = m; _ }
2645 | DictionaryIntrinsicExpression { dictionary_intrinsic_members = m; _ }
2646 | KeysetIntrinsicExpression { keyset_intrinsic_members = m; _ }
2647 | ArrayCreationExpression { array_creation_members = m; _ }
2648 | ArrayIntrinsicExpression { array_intrinsic_members = m; _ }
2649 | VarrayIntrinsicExpression { varray_intrinsic_members = m; _ }
2650 | DarrayIntrinsicExpression { darray_intrinsic_members = m; _ } ->
2651 check_collection_members m errors
2652 | YieldFromExpression _
2653 | YieldExpression _ ->
2654 let errors =
2655 if is_in_unyieldable_magic_method env.context then
2656 make_error_from_node node SyntaxError.yield_in_magic_methods :: errors
2657 else errors in
2658 let errors = match env.context.active_callable with
2659 | Some _ -> errors
2660 | None -> make_error_from_node node SyntaxError.yield_outside_function :: errors
2662 let errors =
2663 if has_inout_params env.context then
2664 let e =
2665 if is_inside_async_method env.context
2666 then SyntaxError.inout_param_in_async_generator
2667 else SyntaxError.inout_param_in_generator in
2668 make_error_from_node ~error_type:SyntaxError.RuntimeError node e :: errors
2669 else errors in
2670 errors
2671 | ScopeResolutionExpression
2672 { scope_resolution_qualifier = qualifier
2673 ; scope_resolution_name = name
2674 ; _ } ->
2675 let is_dynamic_name, is_self_or_parent, is_valid =
2676 (* PHP langspec allows string literals, variables
2677 qualified names, static, self and parent as valid qualifiers *)
2678 (* We do not allow string literals in hack *)
2679 match syntax qualifier, token_kind qualifier with
2680 | LiteralExpression _, _ ->
2681 false, false, not (is_typechecker env)
2682 | QualifiedName _, _ -> false, false, true
2683 | _, Some TokenKind.Name
2684 | _, Some TokenKind.XHPClassName
2685 | _, Some TokenKind.Static -> false, false, true
2686 | _, Some TokenKind.Self
2687 | _, Some TokenKind.Parent -> false, true, true
2688 (* ${}::class *)
2689 | PrefixUnaryExpression {
2690 prefix_unary_operator = op; _
2691 }, _ when token_kind op = Some TokenKind.Dollar ->
2692 true, false, true
2693 | PipeVariableExpression _, _
2694 | VariableExpression _, _
2695 | SimpleTypeSpecifier _, _
2696 | GenericTypeSpecifier _, _ -> true, false, true
2697 | _ -> true, false, not (is_typechecker env)
2699 let errors = if not is_valid then
2700 make_error_from_node
2701 node SyntaxError.invalid_scope_resolution_qualifier :: errors
2702 else errors in
2703 let is_name_class = String.lowercase @@ text name = "class" in
2704 let errors = if is_dynamic_name && is_name_class then
2705 make_error_from_node
2706 node SyntaxError.coloncolonclass_on_dynamic :: errors
2707 else errors in
2708 let text_name = text qualifier in
2709 let is_name_namespace = String.lowercase @@ text_name = "namespace" in
2710 let errors = if is_name_namespace
2711 then make_error_from_node ~error_type:SyntaxError.ParseError
2712 node (SyntaxError.namespace_not_a_classname)::errors
2713 else errors in
2714 let errors = if is_self_or_parent && is_name_class &&
2715 not @@ is_in_active_class_scope env.context
2716 then make_error_from_node ~error_type:SyntaxError.RuntimeError
2717 node (SyntaxError.self_or_parent_colon_colon_class_outside_of_class
2718 @@ text qualifier) :: errors
2719 else errors in
2720 errors
2721 | PrefixUnaryExpression { prefix_unary_operator; prefix_unary_operand }
2722 when token_kind prefix_unary_operator = Some TokenKind.Ampersand ->
2723 check_reference prefix_unary_operand errors
2724 | PrefixUnaryExpression { prefix_unary_operator; prefix_unary_operand }
2725 when token_kind prefix_unary_operator = Some TokenKind.Dollar ->
2726 let original_node = node in
2727 let rec check_prefix_unary_dollar node =
2728 match syntax node with
2729 | PrefixUnaryExpression { prefix_unary_operator; prefix_unary_operand }
2730 when token_kind prefix_unary_operator = Some TokenKind.Dollar ->
2731 check_prefix_unary_dollar prefix_unary_operand
2732 | BracedExpression _
2733 | SubscriptExpression _
2734 | VariableExpression _ -> errors (* these ones are valid *)
2735 | LiteralExpression _
2736 | PipeVariableExpression _ -> errors (* these ones get caught later *)
2737 | _ -> make_error_from_node original_node SyntaxError.dollar_unary :: errors
2739 check_prefix_unary_dollar prefix_unary_operand
2740 (* TODO(T21285960): Remove this bug-port, stemming from T22184312 *)
2741 | LambdaExpression { lambda_async; lambda_coroutine; lambda_signature; _ }
2742 when is_hhvm_compat env
2743 && not (is_missing lambda_async)
2744 && trailing_width lambda_async = 0
2745 && full_width lambda_coroutine = 0
2746 && leading_width lambda_signature = 0
2748 make_error_from_node node
2749 (SyntaxError.error1057 "==>") :: errors
2750 (* End of bug-port *)
2751 | IsExpression
2752 { is_right_operand = hint
2755 | AsExpression
2756 { as_right_operand = hint
2758 } ->
2759 let n = match syntax node with IsExpression _ -> "is" | _ -> "as" in
2760 begin match syntax hint with
2761 | ClosureTypeSpecifier _ when env.hhvm_compat_mode <> NoCompat ->
2762 make_error_from_node hint
2763 (SyntaxError.invalid_is_as_expression_hint n "Callable") :: errors
2764 | SoftTypeSpecifier _ ->
2765 make_error_from_node hint
2766 (SyntaxError.invalid_is_as_expression_hint n "Soft") :: errors
2767 | _ -> errors
2769 | ConditionalExpression
2770 { conditional_test
2771 ; conditional_consequence = cons
2772 ; conditional_alternative
2773 ; _ } ->
2774 let errors =
2775 if is_missing cons && is_typechecker env
2776 then make_error_from_node node SyntaxError.elvis_operator_space :: errors
2777 else errors in
2778 let errors =
2779 if is_conditional_expression conditional_test && is_typechecker env
2780 then make_error_from_node node SyntaxError.nested_ternary :: errors
2781 else errors in
2782 begin match conditional_alternative with
2783 | { syntax = LambdaExpression { lambda_body; _ }; _ }
2784 when is_conditional_expression lambda_body && is_typechecker env ->
2785 make_error_from_node node SyntaxError.nested_ternary :: errors
2786 | _ -> errors end
2787 | LambdaExpression
2788 { lambda_attribute_spec = s
2789 ; lambda_body = body
2790 ; _ } ->
2791 let errors = no_memoize_attribute_on_lambda s errors in
2792 no_async_before_lambda_body env body errors
2793 | AnonymousFunction { anonymous_attribute_spec = s; _ }
2794 | AwaitableCreationExpression { awaitable_attribute_spec = s; _ }
2795 -> no_memoize_attribute_on_lambda s errors
2796 | CollectionLiteralExpression
2797 { collection_literal_name = n
2798 ; collection_literal_initializers = initializers
2799 ; _ } ->
2800 let is_standard_collection lc_name =
2801 lc_name = "pair" || lc_name = "vector" || lc_name = "map" ||
2802 lc_name = "set" || lc_name = "immvector" || lc_name = "immmap" ||
2803 lc_name = "immset" in
2804 let is_qualified_std_collection l r =
2805 token_kind l = Some TokenKind.Name &&
2806 token_kind r = Some TokenKind.Name &&
2807 String.lowercase (text l) = "hh" &&
2808 is_standard_collection (String.lowercase (text r)) in
2809 let status =
2810 match syntax n with
2811 (* non-qualified name *)
2812 | SimpleTypeSpecifier { simple_type_specifier = ({
2813 syntax = Token t; _
2814 } as n);_ }
2815 | GenericTypeSpecifier { generic_class_type = ({
2816 syntax = Token t; _
2817 } as n);_ }
2818 when Token.kind t = TokenKind.Name ->
2819 begin match String.lowercase (text n) with
2820 | "dict" | "vec" | "keyset" -> `InvalidBraceKind
2821 | n -> if is_standard_collection n then `ValidClass n else `InvalidClass
2823 (* qualified name *)
2824 | SimpleTypeSpecifier { simple_type_specifier = {
2825 syntax = QualifiedName { qualified_name_parts = parts; _ }; _
2826 };_ }
2827 | GenericTypeSpecifier { generic_class_type = {
2828 syntax = QualifiedName { qualified_name_parts = parts; _ }; _
2829 };_ } ->
2830 begin match syntax_to_list false parts with
2831 (* HH\Vector in global namespace *)
2832 | [l; r]
2833 when namespace_name = global_namespace_name &&
2834 is_qualified_std_collection l r ->
2835 `ValidClass (String.lowercase (text r))
2836 (* \HH\Vector *)
2837 | [{ syntax = Missing; _}; l; r]
2838 when is_qualified_std_collection l r ->
2839 `ValidClass (String.lowercase (text r))
2840 | _ -> `InvalidClass
2842 | _ -> `InvalidClass in
2843 let num_initializers =
2844 List.length (syntax_to_list_no_separators initializers) in
2845 let errors = begin match status with
2846 | `ValidClass "pair" when num_initializers <> 2 ->
2847 let msg =
2848 if num_initializers = 0
2849 then SyntaxError.pair_initializer_needed
2850 else SyntaxError.pair_initializer_arity
2852 let e =
2853 make_error_from_node node msg ~error_type:SyntaxError.RuntimeError in
2854 e :: errors
2855 | `ValidClass _ -> errors
2856 | `InvalidBraceKind ->
2857 let e =
2858 make_error_from_node node SyntaxError.invalid_brace_kind_in_collection_initializer in
2859 e :: errors
2860 | `InvalidClass ->
2861 let e =
2862 make_error_from_node node SyntaxError.invalid_class_in_collection_initializer in
2863 e :: errors
2864 end in
2865 check_collection_members initializers errors
2866 | DecoratedExpression { decorated_expression_decorator = op; _ }
2867 | PrefixUnaryExpression { prefix_unary_operator = op; _ }
2868 when token_kind op = Some TokenKind.Await ->
2869 let aaae_errors = await_as_an_expression_errors node parents in
2870 List.append aaae_errors errors
2871 | _ -> errors (* Other kinds of expressions currently produce no expr errors. *)
2873 let check_repeated_properties_tconst_const full_name (errors, p_names, c_names) prop =
2874 let check errors sname names =
2875 let name = text sname in
2876 (* If the name is empty, then there was an earlier
2877 parsing error that should supercede this one. *)
2878 if name = ""
2879 then errors, names
2880 else
2881 if SSet.mem name names
2882 then make_error_from_node prop
2883 (SyntaxError.redeclaration_error
2884 ((Utils.strip_ns full_name) ^ "::" ^ name)) :: errors, names
2885 else errors, SSet.add name names
2887 match syntax prop with
2888 | PropertyDeclaration { property_declarators; _} ->
2889 let declarators = syntax_to_list_no_separators property_declarators in
2890 List.fold declarators ~init:(errors, p_names, c_names)
2891 ~f:begin fun (errors, p_names, c_names) prop ->
2892 match syntax prop with
2893 | PropertyDeclarator {property_name = name; _} ->
2894 let errors, p_names = check errors name p_names in
2895 errors, p_names, c_names
2896 | _ -> errors, p_names, c_names
2898 | ConstDeclaration { const_declarators; _ } ->
2899 let declarators = syntax_to_list_no_separators const_declarators in
2900 List.fold declarators ~init:(errors, p_names, c_names)
2901 ~f:begin fun (errors, p_names, c_names) prop ->
2902 match syntax prop with
2903 | ConstantDeclarator {constant_declarator_name = name; _} ->
2904 let errors, c_names = check errors name c_names in
2905 errors, p_names, c_names
2906 | _ -> errors, p_names, c_names
2908 | TypeConstDeclaration { type_const_name; _ } ->
2909 let errors, c_names = check errors type_const_name c_names in
2910 errors, p_names, c_names
2911 | _ -> errors, p_names, c_names
2913 let require_errors _env node trait_use_clauses errors =
2914 match syntax node with
2915 | RequireClause p ->
2916 let name = text p.require_name in
2917 let req_kind = token_kind p.require_kind in
2918 let trait_use_clauses, errors =
2919 match strmap_get name trait_use_clauses, req_kind with
2920 | None, Some tk -> strmap_add name tk trait_use_clauses, errors
2921 | Some tk1, Some tk2 when tk1 = tk2 -> (* duplicate, it is okay *)
2922 trait_use_clauses, errors
2923 | _ -> (* Conflicting entry *)
2924 trait_use_clauses,
2925 make_error_from_node node
2926 (SyntaxError.conflicting_trait_require_clauses ~name) :: errors
2928 let errors =
2929 match (active_classish_kind _env.context, req_kind) with
2930 | (Some TokenKind.Interface, Some TokenKind.Implements)
2931 | (Some TokenKind.Class, Some TokenKind.Implements) ->
2932 make_error_from_node node SyntaxError.error2030 :: errors
2933 | _ -> errors
2935 trait_use_clauses, errors
2936 | _ -> trait_use_clauses, errors
2938 let check_type_name syntax_tree name namespace_name name_text location names errors =
2939 begin match strmap_get name_text names.t_classes with
2940 | Some { f_location = location; f_kind; f_name; _ }
2941 when combine_names namespace_name name_text <> f_name && f_kind <> Name_def ->
2942 let text = SyntaxTree.text syntax_tree in
2943 let line_num, _ =
2944 Full_fidelity_source_text.offset_to_position
2945 text location.start_offset in
2946 let long_name_text = combine_names namespace_name name_text in
2947 let error =
2948 make_name_already_used_error name long_name_text name_text location
2949 (match f_kind with
2950 | Name_implicit_use -> SyntaxError.declared_name_is_already_in_use_implicit_hh ~line_num
2951 | Name_use -> SyntaxError.declared_name_is_already_in_use ~line_num
2952 | Name_def -> SyntaxError.type_name_is_already_in_use) in
2953 names, error :: errors
2954 | _ ->
2955 let def =
2956 make_first_use_or_def ~kind:Name_def location namespace_name name_text in
2957 let names =
2958 { names with
2959 t_classes = strmap_add name_text def names.t_classes} in
2960 names, errors
2963 let get_type_params_and_emit_shadowing_errors l errors =
2964 syntax_to_list_no_separators l
2965 |> List.fold_right ~init:(SSet.empty, errors)
2966 ~f:(fun p (s, e) -> match syntax p with
2967 | TypeParameter { type_reified; type_name; _}
2968 when not @@ is_missing type_reified ->
2969 let name = text type_name in
2970 if SSet.mem name s then
2971 (s, make_error_from_node p SyntaxError.shadowing_reified :: e)
2972 else (SSet.add name s, e)
2973 | _ -> s, e)
2975 let reified_parameter_errors node errors =
2976 match syntax node with
2977 | FunctionDeclarationHeader {
2978 function_type_parameter_list = {
2979 syntax = TypeParameters {
2980 type_parameters_parameters; _}; _}; _} ->
2981 snd @@ get_type_params_and_emit_shadowing_errors
2982 type_parameters_parameters errors
2983 | _ -> errors
2985 let class_reified_param_errors env node errors =
2986 match syntax node with
2987 | ClassishDeclaration cd ->
2988 let reified_params, errors = match syntax cd.classish_type_parameters with
2989 | TypeParameters { type_parameters_parameters; _ } ->
2990 get_type_params_and_emit_shadowing_errors
2991 type_parameters_parameters errors
2992 | _ -> SSet.empty, errors in
2993 let add_error e acc = match syntax e with
2994 | TypeParameter { type_reified; type_name; _}
2995 when not @@ is_missing type_reified &&
2996 SSet.mem (text type_name) reified_params ->
2997 make_error_from_node e SyntaxError.shadowing_reified :: acc
2998 |_ -> acc in
2999 let check_method e acc = match syntax e with
3000 | MethodishDeclaration {
3001 methodish_function_decl_header = {
3002 syntax = FunctionDeclarationHeader {
3003 function_type_parameter_list = {
3004 syntax = TypeParameters {
3005 type_parameters_parameters; _}; _}; _}; _}; _} ->
3006 syntax_to_list_no_separators type_parameters_parameters
3007 |> List.fold_right ~init:acc ~f:add_error
3008 | _ -> acc in
3009 let errors = match syntax cd.classish_body with
3010 | ClassishBody { classish_body_elements; _} ->
3011 syntax_to_list_no_separators classish_body_elements
3012 |> List.fold_right ~init:errors ~f:check_method
3013 | _ -> errors in
3014 let errors = if not @@ SSet.is_empty reified_params then begin
3015 if is_token_kind cd.classish_keyword TokenKind.Interface then
3016 make_error_from_node node
3017 (SyntaxError.reified_in_invalid_classish "an interface") :: errors
3018 else if is_token_kind cd.classish_keyword TokenKind.Trait then
3019 make_error_from_node node
3020 (SyntaxError.reified_in_invalid_classish "a trait") :: errors
3021 else errors
3022 end else errors in
3023 errors
3024 | PropertyDeclaration _ ->
3025 if methodish_contains_static node && is_in_reified_class env.context then
3026 make_error_from_node node SyntaxError.static_property_in_reified_class
3027 :: errors else errors
3028 | _ -> errors
3030 let attr_spec_contains_sealed node =
3031 attribute_specification_contains node SN.UserAttributes.uaSealed
3033 (* If there's more than one XHP category, report an error on the last one. *)
3034 let duplicate_xhp_category_errors (elts : Syntax.t list) errors =
3035 let category_nodes = List.filter elts ~f:(fun elt ->
3036 match syntax elt with
3037 | XHPCategoryDeclaration _ -> true
3038 | _ -> false)
3040 if List.length category_nodes > 1 then
3041 let node = List.last_exn category_nodes in
3042 let err = make_error_from_node node SyntaxError.xhp_class_multiple_category_decls in
3043 err :: errors
3044 else
3045 errors
3047 (* If there's more than one XHP children declaration, report an error
3048 on the last one. *)
3049 let duplicate_xhp_children_errors (elts : Syntax.t list) errors =
3050 let child_nodes = List.filter elts ~f:(fun elt ->
3051 match syntax elt with
3052 | XHPChildrenDeclaration _ -> true
3053 | _ -> false)
3055 if List.length child_nodes > 1 then
3056 let node = List.last_exn child_nodes in
3057 let err = make_error_from_node node SyntaxError.xhp_class_multiple_children_decls in
3058 err :: errors
3059 else
3060 errors
3062 let classish_errors env node namespace_name names errors =
3063 match syntax node with
3064 | ClassishDeclaration cd ->
3065 (* Given a ClassishDeclaration node, test whether or not it's a trait
3066 * invoking the 'extends' keyword. *)
3067 let classish_invalid_extends_keyword _ =
3068 (* Invalid if uses 'extends' and is a trait. *)
3069 token_kind cd.classish_extends_keyword = Some TokenKind.Extends &&
3070 token_kind cd.classish_keyword = Some TokenKind.Trait in
3072 (* Given a sealed ClassishDeclaration node, test whether all the params
3073 * are classnames. *)
3074 let classish_sealed_arg_not_classname _env _ =
3075 match cd.classish_attribute.syntax with
3076 | AttributeSpecification { attribute_specification_attributes = attrs; _ } ->
3077 let attrs = syntax_to_list_no_separators attrs in
3078 List.exists attrs (fun e ->
3079 match syntax e with
3080 | ConstructorCall {constructor_call_argument_list; constructor_call_type; _ } ->
3081 text constructor_call_type = SN.UserAttributes.uaSealed &&
3082 List.exists (syntax_to_list_no_separators constructor_call_argument_list) (fun e ->
3083 match syntax e with
3084 | ScopeResolutionExpression {scope_resolution_name; _ } ->
3085 text scope_resolution_name <> "class"
3086 | _ -> true)
3087 | _ -> false)
3088 | _ -> false in
3090 let classish_is_sealed =
3091 attr_spec_contains_sealed cd.classish_attribute in
3093 (* Given a ClassishDeclaration node, test whether or not length of
3094 * extends_list is appropriate for the classish_keyword. *)
3095 let classish_invalid_extends_list env _ =
3096 (* Invalid if is a class and has list of length greater than one. *)
3097 (is_typechecker env) &&
3098 token_kind cd.classish_keyword = Some TokenKind.Class &&
3099 token_kind cd.classish_extends_keyword = Some TokenKind.Extends &&
3100 match syntax_to_list_no_separators cd.classish_extends_list with
3101 | [_] -> false
3102 | _ -> true (* General bc empty list case is already caught by error1007 *) in
3103 let abstract_keyword =
3104 Option.value (extract_keyword is_abstract node) ~default:node in
3105 let errors = produce_error errors
3106 (is_classish_kind_declared_abstract env)
3107 node SyntaxError.error2042 abstract_keyword in
3108 (* Given a ClassishDeclaration node, test whether it is sealed and final. *)
3109 let classish_sealed_final _env _ =
3110 list_contains_predicate is_final cd.classish_modifiers &&
3111 classish_is_sealed in
3112 let errors = produce_error errors (classish_invalid_extends_list env) ()
3113 SyntaxError.error2037 cd.classish_extends_list in
3115 let errors =
3116 match attribute_first_reactivity_annotation cd.classish_attribute with
3117 | Some n ->
3118 make_error_from_node n SyntaxError.misplaced_reactivity_annotation :: errors
3119 | None ->
3120 errors in
3121 let errors =
3122 multiple_modifiers_errors cd.classish_modifiers
3123 SyntaxError.error2031 errors in
3124 let errors =
3125 produce_error errors
3126 (classish_sealed_arg_not_classname env) ()
3127 SyntaxError.sealed_val_not_classname cd.classish_attribute in
3128 let errors =
3129 produce_error errors
3130 classish_invalid_extends_keyword ()
3131 SyntaxError.error2036 cd.classish_extends_keyword in
3132 let errors =
3133 produce_error errors
3134 (classish_sealed_final env) ()
3135 SyntaxError.sealed_final cd.classish_attribute in
3136 let errors =
3137 let classish_name = text cd.classish_name in
3138 produce_error errors
3139 cant_be_classish_name classish_name
3140 (SyntaxError.reserved_keyword_as_class_name classish_name)
3141 cd.classish_name in
3142 let errors =
3143 if is_token_kind cd.classish_keyword TokenKind.Interface &&
3144 not (is_missing cd.classish_implements_keyword)
3145 then make_error_from_node node
3146 SyntaxError.interface_implements :: errors
3147 else errors in
3148 let name = text cd.classish_name in
3149 let errors =
3150 match syntax cd.classish_body with
3151 | ClassishBody {classish_body_elements = methods; _} ->
3152 let methods = syntax_to_list_no_separators methods in
3153 let declared_name_str =
3154 Option.value ~default:"" (Syntax.extract_text cd.classish_name) in
3155 let full_name = combine_names namespace_name declared_name_str in
3156 let errors, _, _ =
3157 List.fold methods
3158 ~f:(check_repeated_properties_tconst_const full_name)
3159 ~init:(errors, SSet.empty, SSet.empty) in
3160 let has_abstract_fn =
3161 List.exists methods ~f:methodish_contains_abstract in
3162 let has_private_method =
3163 List.exists methods
3164 ~f:(methodish_modifier_contains_helper is_private) in
3165 let errors =
3166 if has_abstract_fn &&
3167 is_token_kind cd.classish_keyword TokenKind.Class &&
3168 not (list_contains_predicate is_abstract cd.classish_modifiers)
3169 then make_error_from_node node
3170 (SyntaxError.class_with_abstract_method name) :: errors
3171 else errors in
3172 let errors =
3173 if has_private_method &&
3174 is_token_kind cd.classish_keyword TokenKind.Interface
3175 then make_error_from_node node
3176 SyntaxError.interface_has_private_method :: errors
3177 else errors in
3178 let errors = duplicate_xhp_category_errors methods errors in
3179 let errors = duplicate_xhp_children_errors methods errors in
3180 errors
3181 | _ -> errors in
3182 let names, errors =
3183 match token_kind cd.classish_keyword with
3184 | Some TokenKind.Class | Some TokenKind.Trait
3185 when not (is_missing cd.classish_name)->
3186 let location = make_location_of_node cd.classish_name in
3187 check_type_name env.syntax_tree cd.classish_name namespace_name name location names errors
3188 | _ ->
3189 names, errors in
3190 names, errors
3191 | _ -> names, errors
3193 let class_element_errors env node errors =
3194 match syntax node with
3195 | ConstDeclaration _ when is_inside_trait env.context ->
3196 make_error_from_node node SyntaxError.const_in_trait :: errors
3197 | ConstDeclaration { const_visibility; _ }
3198 when not (is_missing const_visibility) && is_typechecker env ->
3199 make_error_from_node node SyntaxError.const_visibility :: errors
3200 | _ -> errors
3203 let alias_errors env node namespace_name names errors =
3204 match syntax node with
3205 | AliasDeclaration ad ->
3206 let errors =
3207 if token_kind ad.alias_keyword = Some TokenKind.Type &&
3208 not (is_missing ad.alias_constraint)
3209 then make_error_from_node ad.alias_keyword SyntaxError.error2034 :: errors
3210 else errors in
3211 if is_missing ad.alias_name then names,errors
3212 else
3213 let name = text ad.alias_name in
3214 let location = make_location_of_node ad.alias_name in
3215 let errors = match ad.alias_type with
3217 syntax = TypeConstant _;
3219 } when is_typechecker env ->
3220 make_error_from_node ad.alias_type
3221 SyntaxError.type_alias_to_type_constant :: errors
3222 | _ -> errors in
3223 check_type_name env.syntax_tree ad.alias_name namespace_name name location names errors
3224 | _ -> names, errors
3226 let is_invalid_group_use_clause kind clause =
3227 match syntax clause with
3228 | NamespaceUseClause { namespace_use_clause_kind = clause_kind; _ } ->
3229 if is_missing kind
3230 then
3231 begin match syntax clause_kind with
3232 | Missing -> false
3233 | Token token when let k = Token.kind token in
3234 TokenKind.(k = Function || k = Const) -> false
3235 | _ -> true
3237 else not (is_missing clause_kind)
3238 | _ -> false
3240 let is_invalid_group_use_prefix prefix =
3241 not (is_namespace_prefix prefix)
3243 let group_use_errors _env node errors =
3244 match syntax node with
3245 | NamespaceGroupUseDeclaration
3246 { namespace_group_use_prefix = prefix
3247 ; namespace_group_use_clauses = clauses
3248 ; namespace_group_use_kind = kind
3249 ; _} ->
3250 let errors =
3251 let invalid_clauses = List.filter ~f:(is_invalid_group_use_clause kind)
3252 (syntax_to_list_no_separators clauses) in
3253 let mapper errors clause =
3254 make_error_from_node clause SyntaxError.error2049 :: errors in
3255 List.fold_left ~f:mapper ~init:errors invalid_clauses in
3256 produce_error errors is_invalid_group_use_prefix prefix
3257 SyntaxError.error2048 prefix
3258 | _ -> errors
3260 let use_class_or_namespace_clause_errors
3261 env is_global_namespace namespace_prefix
3262 kind (names, errors) cl =
3264 match syntax cl with
3265 | NamespaceUseClause {
3266 namespace_use_name = name;
3267 namespace_use_alias = alias;
3268 namespace_use_clause_kind; _
3269 } when not (is_missing name) ->
3270 let kind = if is_missing kind then namespace_use_clause_kind else kind in
3271 let name_text = text name in
3272 let qualified_name =
3273 match namespace_prefix with
3274 | None -> combine_names global_namespace_name name_text
3275 | Some p -> combine_names p name_text in
3276 let short_name = get_short_name_from_qualified_name name_text (text alias) in
3278 let do_check ?(case_sensitive = false) ~error_on_global_redefinition names errors
3279 get_map update_map report_error =
3280 (* We store the original names in the SMap of names for error messaging purposes
3281 but we check for case sensitivity specifically. *)
3282 let find_name name =
3283 if case_sensitive
3284 then short_name = name
3285 else (String.lowercase short_name) = String.lowercase name in
3286 let map = get_map names in
3287 match strmap_find_first_opt find_name map with
3288 | Some (_, { f_location = location; f_kind; f_global; _ }) ->
3289 if (f_kind <> Name_def
3290 || (error_on_global_redefinition && (is_global_namespace || f_global)))
3291 then
3292 let error =
3293 make_name_already_used_error name name_text
3294 short_name location report_error in
3295 names, error :: errors
3296 else
3297 names, errors
3298 | None ->
3299 let new_use =
3300 make_first_use_or_def
3301 ~kind:Name_use
3302 (make_location_of_node name)
3303 global_namespace_name qualified_name in
3304 update_map names (strmap_add short_name new_use map), errors
3307 begin match syntax kind with
3308 | Token token ->
3309 let open TokenKind in
3310 (match Token.kind token with
3311 | Namespace ->
3312 do_check ~error_on_global_redefinition:false names errors
3313 (fun n -> n.t_namespaces)
3314 (fun n v -> { n with t_namespaces = v })
3315 SyntaxError.namespace_name_is_already_in_use
3317 | Type ->
3318 do_check ~error_on_global_redefinition:false names errors
3319 (fun n -> n.t_classes)
3320 (fun n v -> { n with t_classes = v })
3321 SyntaxError.type_name_is_already_in_use
3323 | Function ->
3324 do_check ~error_on_global_redefinition:true names errors
3325 (fun n -> n.t_functions)
3326 (fun n v -> { n with t_functions = v })
3327 SyntaxError.function_name_is_already_in_use
3329 | Const ->
3330 do_check ~case_sensitive:true ~error_on_global_redefinition:true names errors
3331 (fun n -> n.t_constants)
3332 (fun n v -> { n with t_constants = v })
3333 SyntaxError.const_name_is_already_in_use
3334 | _ ->
3335 names, errors
3337 | Missing ->
3338 let errors =
3339 if name_text = "strict"
3340 then make_error_from_node name SyntaxError.strict_namespace_hh :: errors
3341 else errors in
3342 let names, errors =
3343 let location = make_location_of_node name in
3344 match strmap_get short_name names.t_classes with
3345 | Some { f_location = loc; f_name; f_kind; _ } ->
3346 if qualified_name = f_name && f_kind = Name_def then names, errors
3347 else
3348 let err_msg =
3349 if f_kind <> Name_def then
3350 let text = SyntaxTree.text env.syntax_tree in
3351 let line_num, _ =
3352 Full_fidelity_source_text.offset_to_position
3353 text loc.start_offset in
3354 if f_kind = Name_implicit_use
3355 then SyntaxError.name_is_already_in_use_implicit_hh ~line_num
3356 else SyntaxError.name_is_already_in_use_hh ~line_num
3357 else SyntaxError.name_is_already_in_use_php
3359 let error = make_name_already_used_error
3360 name name_text short_name loc err_msg in
3361 names, error :: errors
3362 | None ->
3363 let new_use =
3364 make_first_use_or_def ~kind:Name_use location global_namespace_name qualified_name in
3365 let t_classes = strmap_add short_name new_use names.t_classes in
3366 let t_namespaces =
3367 if strmap_mem short_name names.t_namespaces
3368 then names.t_namespaces
3369 else strmap_add short_name new_use names.t_namespaces in
3370 { names with t_classes; t_namespaces }, errors in
3372 names, errors
3373 | _ ->
3374 names, errors
3376 | _ ->
3377 names, errors
3379 let is_global_in_const_decl init =
3380 match syntax init with
3381 | SimpleInitializer { simple_initializer_value; _ } ->
3382 begin match syntax simple_initializer_value with
3383 | VariableExpression { variable_expression } ->
3384 SN.Superglobals.is_superglobal @@ text variable_expression
3385 | _ -> false
3387 | _ -> false
3389 let namespace_use_declaration_errors
3390 env node is_global_namespace names errors =
3391 match syntax node with
3392 | NamespaceUseDeclaration {
3393 namespace_use_kind = kind;
3394 namespace_use_clauses = clauses; _ } ->
3395 let f =
3396 use_class_or_namespace_clause_errors
3397 env is_global_namespace None kind in
3398 List.fold_left ~f ~init:(names, errors) (syntax_to_list_no_separators clauses)
3399 | NamespaceGroupUseDeclaration {
3400 namespace_group_use_kind = kind;
3401 namespace_group_use_clauses = clauses;
3402 namespace_group_use_prefix = prefix; _ } ->
3403 let f =
3404 use_class_or_namespace_clause_errors
3405 env is_global_namespace (Some (text prefix)) kind in
3406 List.fold_left ~f ~init:(names, errors) (syntax_to_list_no_separators clauses)
3407 | _ -> names, errors
3410 let rec check_constant_expression errors node =
3411 let is_namey token =
3412 match Token.kind token with
3413 TokenKind.Name -> true
3414 | _ -> false
3416 let is_good_scope_resolution_name node =
3417 match syntax node with
3418 | QualifiedName _ -> true
3419 | Token token ->
3420 let open TokenKind in
3421 (match Token.kind token with
3422 | Name | Trait | Extends | Implements | Static
3423 | Abstract | Final | Private | Protected | Public | Global
3424 | Goto | Instanceof | Insteadof | Interface | Namespace | New | Try | Use
3425 | Var | List | Clone | Include | Include_once | Throw | Array | Tuple
3426 | Print | Echo | Require | Require_once | Return | Else | Elseif | Default
3427 | Break | Continue | Switch | Yield | Function | If | Finally | For
3428 | Foreach | Case | Do | While | As | Catch | Empty | Using | Class
3429 | NullLiteral | Super | Where
3430 -> true
3431 | _ -> false
3433 | _ -> false
3435 match syntax node with
3436 | Missing
3437 | QualifiedName _
3438 | LiteralExpression _
3439 -> errors
3440 | Token token when is_namey token -> errors
3441 | PrefixUnaryExpression
3442 { prefix_unary_operand
3443 ; prefix_unary_operator = { syntax = Token token ; _ }
3444 } when ( let open TokenKind in
3445 match Token.kind token with
3446 | Exclamation | Plus | Minus | Tilde -> true
3447 | _ -> false
3448 ) ->
3449 check_constant_expression errors prefix_unary_operand
3450 | BinaryExpression
3451 { binary_left_operand
3452 ; binary_right_operand
3453 ; binary_operator = { syntax = Token token ; _ }
3454 ; _ } when ( let open TokenKind in
3455 match Token.kind token with
3456 | BarBar | AmpersandAmpersand | Carat
3457 | Bar | Ampersand | Dot | Plus | Minus | Star | Slash | Percent
3458 | LessThanLessThan | GreaterThanGreaterThan | StarStar
3459 | EqualEqual | EqualEqualEqual | ExclamationEqual
3460 | ExclamationEqualEqual | GreaterThan | GreaterThanEqual
3461 | LessThan | LessThanEqual | LessThanEqualGreaterThan
3462 | QuestionColon
3463 -> true
3464 | _ -> false
3465 ) ->
3466 let errors = check_constant_expression errors binary_left_operand in
3467 let errors = check_constant_expression errors binary_right_operand in
3468 errors
3469 | ConditionalExpression {
3470 conditional_test;
3471 conditional_consequence;
3472 conditional_alternative; _
3473 } ->
3474 let errors = check_constant_expression errors conditional_test in
3475 let errors = check_constant_expression errors conditional_consequence in
3476 let errors = check_constant_expression errors conditional_alternative in
3477 errors
3478 | SimpleInitializer { simple_initializer_value = e; _ }
3479 | ParenthesizedExpression { parenthesized_expression_expression = e; _} ->
3480 check_constant_expression errors e
3481 | CollectionLiteralExpression
3482 { collection_literal_name =
3483 { syntax =
3484 ( SimpleTypeSpecifier
3485 { simple_type_specifier = { syntax = Token token; _ } }
3486 | GenericTypeSpecifier
3487 { generic_class_type = { syntax = Token token; _ }; _ }
3491 ; collection_literal_initializers = lst
3493 } when is_namey token ->
3494 syntax_to_list_no_separators lst
3495 |> List.fold_left ~init:errors ~f:check_constant_expression
3496 | TupleExpression { tuple_expression_items = lst; _ }
3497 | KeysetIntrinsicExpression { keyset_intrinsic_members = lst; _}
3498 | VarrayIntrinsicExpression { varray_intrinsic_members = lst; _ }
3499 | DarrayIntrinsicExpression { darray_intrinsic_members = lst; _ }
3500 | VectorIntrinsicExpression { vector_intrinsic_members = lst; _ }
3501 | DictionaryIntrinsicExpression { dictionary_intrinsic_members = lst; _}
3502 | ArrayIntrinsicExpression { array_intrinsic_members = lst; _}
3503 | ArrayCreationExpression { array_creation_members = lst; _ }
3504 | ShapeExpression { shape_expression_fields = lst; _ } ->
3505 syntax_to_list_no_separators lst
3506 |> List.fold_left ~init:errors ~f:check_constant_expression
3507 | ElementInitializer { element_key = n; element_value = v; _ }
3508 | FieldInitializer { field_initializer_name = n; field_initializer_value = v; _ } ->
3509 let errors = check_constant_expression errors n in
3510 let errors = check_constant_expression errors v in
3511 errors
3512 | ScopeResolutionExpression
3513 { scope_resolution_qualifier
3514 ; scope_resolution_name
3516 ; _ } when is_good_scope_resolution_qualifier scope_resolution_qualifier &&
3517 is_good_scope_resolution_name scope_resolution_name
3518 -> errors
3519 | _ ->
3520 (make_error_from_node node SyntaxError.invalid_constant_initializer) :: errors
3522 let check_static_in_initializer initializer_ =
3523 match syntax initializer_ with
3524 | SimpleInitializer {
3525 simple_initializer_value = {
3526 syntax = ScopeResolutionExpression {
3527 scope_resolution_qualifier = { syntax = Token t; _ };
3528 scope_resolution_name = name;
3529 _}; _}; _
3530 } ->
3531 begin match Token.kind t with
3532 | TokenKind.Static -> true
3533 | TokenKind.Parent when (String.lowercase @@ text name = "class") -> true
3534 | _ -> false
3536 | _ -> false
3538 let const_decl_errors env node namespace_name names errors =
3539 match syntax node with
3540 | ConstantDeclarator cd ->
3541 let errors =
3542 produce_error_context errors constant_abstract_with_initializer
3543 cd.constant_declarator_initializer env.context
3544 SyntaxError.error2051 cd.constant_declarator_initializer in
3545 let errors =
3546 produce_error_context errors constant_concrete_without_initializer
3547 cd.constant_declarator_initializer env.context SyntaxError.error2050
3548 cd.constant_declarator_initializer in
3549 let errors =
3550 produce_error errors is_global_in_const_decl cd.constant_declarator_initializer
3551 SyntaxError.global_in_const_decl cd.constant_declarator_initializer in
3552 let errors =
3553 check_constant_expression errors cd.constant_declarator_initializer in
3554 let errors =
3555 produce_error errors check_static_in_initializer cd.constant_declarator_initializer
3556 SyntaxError.parent_static_const_decl cd.constant_declarator_initializer in
3557 let errors =
3558 match syntax cd.constant_declarator_initializer with
3559 | SimpleInitializer { simple_initializer_value = { syntax =
3560 LiteralExpression { literal_expression = { syntax =
3561 SyntaxList _; _}}; _}; _} ->
3562 make_error_from_node
3563 node SyntaxError.invalid_constant_initializer :: errors
3564 | _ -> errors in
3565 if is_missing cd.constant_declarator_name
3566 then names, errors
3567 else
3568 let constant_name = text cd.constant_declarator_name in
3569 let location = make_location_of_node cd.constant_declarator_name in
3570 let def =
3571 make_first_use_or_def ~kind:Name_def location namespace_name constant_name in
3572 let errors =
3573 match strmap_get constant_name names.t_constants with
3574 | None -> errors
3575 | Some _ ->
3576 (* Only error if this is inside a class *)
3577 begin match first_parent_class_name env.context with
3578 | None -> errors
3579 | Some class_name ->
3580 let full_name = class_name ^ "::" ^ constant_name in
3581 make_error_from_node
3582 node (SyntaxError.redeclaration_error full_name) :: errors
3585 let names = {
3586 names with t_constants =
3587 strmap_add constant_name def names.t_constants } in
3588 names, errors
3589 | _ -> names, errors
3592 let class_property_visibility_errors env node errors =
3593 match syntax node with
3594 | PropertyDeclaration { property_modifiers; _ } ->
3595 let first_parent_name = Option.value (first_parent_class_name env.context)
3596 ~default:"" in
3597 let errors =
3598 multiple_modifiers_errors
3599 property_modifiers
3600 (SyntaxError.property_has_multiple_modifiers first_parent_name) errors
3602 let errors =
3603 multiple_visibility_errors
3604 property_modifiers
3605 (SyntaxError.property_has_multiple_visibilities first_parent_name) errors
3607 let errors =
3608 produce_error errors
3609 is_empty_list_or_missing property_modifiers
3610 SyntaxError.property_requires_visibility node
3612 errors
3613 | _ -> errors
3616 let mixed_namespace_errors _env node parents namespace_type errors =
3617 match syntax node with
3618 | NamespaceBody { namespace_left_brace; namespace_right_brace; _ } ->
3619 let s = start_offset namespace_left_brace in
3620 let e = end_offset namespace_right_brace in
3621 begin match namespace_type with
3622 | Unbracketed { start_offset; end_offset } ->
3623 let child = Some
3624 (SyntaxError.make start_offset end_offset SyntaxError.error2057)
3626 SyntaxError.make ~child s e SyntaxError.error2052 :: errors
3627 | _ -> errors
3629 | NamespaceEmptyBody { namespace_semicolon; _ } ->
3630 let s = start_offset namespace_semicolon in
3631 let e = end_offset namespace_semicolon in
3632 begin match namespace_type with
3633 | Bracketed { start_offset; end_offset } ->
3634 let child = Some
3635 (SyntaxError.make start_offset end_offset SyntaxError.error2056)
3637 SyntaxError.make ~child s e SyntaxError.error2052 :: errors
3638 | _ -> errors
3640 | NamespaceDeclaration { namespace_body; _ } ->
3641 let is_first_decl, has_code_outside_namespace =
3642 match parents with
3643 | [{ syntax = SyntaxList _; _} as decls; { syntax = Script _; _}] ->
3644 let decls = syntax_to_list_no_separators decls in
3645 let rec is_first l =
3646 match l with
3647 | { syntax = MarkupSection {markup_text; _}; _} :: rest
3648 when width markup_text = 0 || is_hashbang markup_text ->
3649 is_first rest
3650 | { syntax = ( NamespaceUseDeclaration _
3651 | FileAttributeSpecification _
3652 ); _} :: rest ->
3653 is_first rest
3654 | { syntax = NamespaceDeclaration _; _} :: _ -> true
3655 | _ -> false
3657 let has_code_outside_namespace =
3658 not (is_namespace_empty_body namespace_body) &&
3659 List.exists decls
3660 ~f:(function | { syntax = MarkupSection { markup_text; _}; _}
3661 when width markup_text = 0
3662 || is_hashbang markup_text -> false
3663 | { syntax = NamespaceDeclaration _; _}
3664 | { syntax = ExpressionStatement {
3665 expression_statement_expression =
3666 { syntax = HaltCompilerExpression _; _}; _}; _}
3667 | { syntax = FileAttributeSpecification _; _}
3668 | { syntax = EndOfFile _; _}
3669 | { syntax = NamespaceUseDeclaration _; _} -> false
3670 | _ -> true)
3672 is_first decls, has_code_outside_namespace
3673 | _ -> true, false
3675 let errors = if not is_first_decl then
3676 make_error_from_node node
3677 SyntaxError.namespace_decl_first_statement :: errors else errors
3679 let errors = if has_code_outside_namespace then
3680 make_error_from_node node
3681 SyntaxError.code_outside_namespace :: errors else errors
3683 errors
3684 | _ -> errors
3686 let enumerator_errors node errors =
3687 match syntax node with
3688 | Enumerator { enumerator_name = name; enumerator_value = value; _} ->
3689 let errors = if String.lowercase @@ text name = "class" then
3690 make_error_from_node node SyntaxError.enum_elem_name_is_class :: errors
3691 else errors in
3692 let errors = check_constant_expression errors value in
3693 errors
3694 | _ -> errors
3696 let enum_decl_errors node errors =
3697 match syntax node with
3698 EnumDeclaration
3699 { enum_attribute_spec = attrs
3701 ; enum_name = name
3702 ; enum_base = base
3703 ; enum_type = constr
3704 ; enum_enumerators = enums
3706 ; _ } ->
3707 if attr_spec_contains_sealed attrs then
3708 make_error_from_node node SyntaxError.sealed_enum :: errors
3709 else errors
3710 | _ -> errors
3712 let assignment_errors _env node errors =
3713 let append_errors node errors error =
3714 make_error_from_node node error :: errors
3716 let rec check_lvalue ?(allow_reassign_this=false) loperand errors : SyntaxError.t list =
3717 let err = append_errors loperand errors in
3718 match syntax loperand with
3719 | ListExpression { list_members = members; _ } ->
3720 let members = syntax_to_list_no_separators members in
3721 List.fold_left ~f:(fun e n -> check_lvalue n e) ~init:errors members
3722 | SafeMemberSelectionExpression _ ->
3723 err (SyntaxError.not_allowed_in_write "?-> operator")
3724 | MemberSelectionExpression { member_name; _ }
3725 when token_kind member_name = Some TokenKind.XHPClassName ->
3726 err (SyntaxError.not_allowed_in_write "->: operator")
3727 | VariableExpression { variable_expression }
3728 when not allow_reassign_this
3729 && String.lowercase (text variable_expression) = SN.SpecialIdents.this ->
3730 err SyntaxError.reassign_this
3731 | DecoratedExpression { decorated_expression_decorator = op; _ }
3732 when token_kind op = Some TokenKind.Clone ->
3733 err (SyntaxError.not_allowed_in_write "Clone")
3734 | DecoratedExpression { decorated_expression_decorator = op; _ }
3735 when token_kind op = Some TokenKind.Await ->
3736 err (SyntaxError.not_allowed_in_write "Await")
3737 | DecoratedExpression { decorated_expression_decorator = op; _ }
3738 when token_kind op = Some TokenKind.Suspend ->
3739 err (SyntaxError.not_allowed_in_write "Suspend")
3740 | DecoratedExpression { decorated_expression_decorator = op; _ }
3741 when token_kind op = Some TokenKind.QuestionQuestion ->
3742 err (SyntaxError.not_allowed_in_write "?? operator")
3743 | DecoratedExpression { decorated_expression_decorator = op; _ }
3744 when token_kind op = Some TokenKind.BarGreaterThan ->
3745 err (SyntaxError.not_allowed_in_write "|> operator")
3746 | DecoratedExpression { decorated_expression_decorator = op; _ }
3747 when token_kind op = Some TokenKind.Inout ->
3748 err (SyntaxError.not_allowed_in_write "Inout")
3749 | ParenthesizedExpression { parenthesized_expression_expression = e; _} ->
3750 check_lvalue ~allow_reassign_this e errors
3751 | SubscriptExpression { subscript_receiver = e; _ } ->
3752 check_lvalue ~allow_reassign_this:true e errors
3753 | LambdaExpression _ | AnonymousFunction _
3754 | AwaitableCreationExpression _
3755 | ArrayIntrinsicExpression _ | ArrayCreationExpression _
3756 | DarrayIntrinsicExpression _
3757 | VarrayIntrinsicExpression _
3758 | ShapeExpression _
3759 | CollectionLiteralExpression _
3760 | GenericTypeSpecifier _
3761 | YieldExpression _ | YieldFromExpression _
3762 | CastExpression _
3763 | BinaryExpression _
3764 | ConditionalExpression _
3765 | InstanceofExpression _
3766 | IsExpression _
3767 | AsExpression _ | NullableAsExpression _
3768 | ConstructorCall _ | AnonymousClass _
3769 | XHPExpression _
3770 | InclusionExpression _
3771 | TupleExpression _
3772 | LiteralExpression _ ->
3773 let kind = kind loperand in
3774 let kind_str = Full_fidelity_syntax_kind.to_string kind in
3775 err (SyntaxError.not_allowed_in_write kind_str)
3776 | PrefixUnaryExpression { prefix_unary_operator = op; _ }
3777 | PostfixUnaryExpression { postfix_unary_operator = op; _ } ->
3778 begin match token_kind op with
3779 | Some TokenKind.At | Some TokenKind.Dollar -> errors
3780 | Some TokenKind.Ampersand ->
3781 err (SyntaxError.not_allowed_in_write "&")
3782 | _ -> err (SyntaxError.not_allowed_in_write "Unary expression")
3783 end (* match *)
3784 (* FIXME: Array_get ((_, Class_const _), _) is not a valid lvalue. *)
3785 | _ -> errors
3786 (* Ideally we should put all the rest of the syntax here so everytime
3787 * a new syntax is added people need to consider whether the syntax
3788 * can be a valid lvalue or not. However, there are too many of them. *)
3790 let check_rvalue roperand errors : SyntaxError.t list =
3791 match syntax roperand with
3792 | PrefixUnaryExpression { prefix_unary_operator = op; _ }
3793 when token_kind op = Some TokenKind.Ampersand ->
3794 append_errors roperand errors (SyntaxError.references_not_allowed)
3795 | _ -> errors
3797 match syntax node with
3798 | (PrefixUnaryExpression
3799 { prefix_unary_operator = op
3800 ; prefix_unary_operand = loperand
3802 | PostfixUnaryExpression
3803 { postfix_unary_operator = op
3804 ; postfix_unary_operand = loperand
3806 ) when does_unop_create_write (token_kind op) ->
3807 check_lvalue ~allow_reassign_this:true loperand errors
3808 | DecoratedExpression
3809 { decorated_expression_decorator = op
3810 ; decorated_expression_expression = loperand
3811 } when does_decorator_create_write (token_kind op) ->
3812 check_lvalue ~allow_reassign_this:true loperand errors
3813 | BinaryExpression
3814 { binary_left_operand = loperand
3815 ; binary_operator = op
3816 ; binary_right_operand = roperand
3817 } when does_binop_create_write_on_left (token_kind op) ->
3818 let errors = check_lvalue loperand errors in
3819 check_rvalue roperand errors
3820 | ForeachStatement
3821 { foreach_key = k;
3822 foreach_value = v;
3824 } ->
3825 check_lvalue k @@ check_lvalue v errors
3826 | _ -> errors
3829 let dynamic_method_call_errors node errors =
3830 match syntax node with
3831 | FunctionCallExpression
3833 function_call_type_args = type_args;
3834 function_call_receiver = receiver; _ } when not (is_missing type_args) ->
3835 let is_dynamic = match syntax receiver with
3836 | ScopeResolutionExpression { scope_resolution_name = name; _ }
3837 | MemberSelectionExpression { member_name = name; _ }
3838 | SafeMemberSelectionExpression { safe_member_name = name; _ } ->
3839 is_token_kind name TokenKind.Variable
3840 | _ -> false
3842 if not is_dynamic then errors else
3843 (make_error_from_node node SyntaxError.no_type_parameters_on_dynamic_method_calls) :: errors
3844 | _ -> errors
3846 let get_namespace_name context current_namespace_name =
3847 match context.nested_namespaces with
3848 | { syntax = NamespaceDeclaration { namespace_name = ns; _ }; _ } :: _ ->
3849 if is_missing ns then current_namespace_name
3850 else combine_names current_namespace_name (text ns)
3851 | _ -> current_namespace_name
3853 let is_invalid_hack_mode env errors =
3854 if SyntaxTree.mode env.syntax_tree = None then
3855 let root = SyntaxTree.root env.syntax_tree in
3856 let e = make_error_from_node root SyntaxError.invalid_hack_mode in
3857 e::errors
3858 else
3859 errors
3861 let find_syntax_errors env =
3862 let has_rx_attr_mutable_hack attrs =
3863 attribute_first_reactivity_annotation attrs
3864 |> Option.value_map ~default:false ~f:(fun a ->
3865 match attribute_matches_criteria ((<>) SN.UserAttributes.uaNonRx) a with
3866 | Some _ -> true
3867 | None -> false
3868 ) in
3869 let rec folder env acc node parents =
3870 let { errors
3871 ; namespace_type
3872 ; names
3873 ; namespace_name
3874 ; trait_require_clauses
3875 ; is_in_concurrent_block
3876 } = acc in
3877 let env =
3878 { env with context =
3879 let node_syntax = syntax node in
3880 match node_syntax with
3881 | ClassishDeclaration _ ->
3882 { env.context with active_classish = Some node }
3883 | FunctionDeclaration { function_attribute_spec = s; _ }
3884 | MethodishDeclaration { methodish_attribute = s; _ } ->
3885 (* named functions *)
3886 { env.context with
3887 (* a _single_ variable suffices as they cannot be nested *)
3888 active_methodish = Some node;
3889 (* inspect the rx attribute directly. *)
3890 active_is_rx_or_enclosing_for_lambdas = has_rx_attr_mutable_hack s;
3891 active_callable = Some node;
3892 active_callable_attr_spec = Some s;
3894 | AnonymousFunction { anonymous_attribute_spec = s; _ }
3895 | LambdaExpression { lambda_attribute_spec = s; _ }
3896 | AwaitableCreationExpression { awaitable_attribute_spec = s; _ } ->
3897 (* preserve context when entering lambdas (and anonymous functions) *)
3898 { env.context with
3899 active_callable = Some node;
3900 active_callable_attr_spec = Some s;
3902 | ConstDeclaration _ ->
3903 { env.context with active_const = Some node }
3904 | NamespaceDeclaration { namespace_name; _ }
3905 when not @@ is_missing namespace_name && text namespace_name <> "" ->
3906 { env.context with
3907 nested_namespaces = node :: env.context.nested_namespaces;
3909 | _ -> env.context
3910 } in
3911 let names, errors =
3912 parameter_errors env node namespace_name names errors in
3913 let trait_require_clauses, names, errors =
3914 match syntax node with
3915 | TryStatement _
3916 | UsingStatementFunctionScoped _
3917 | ForStatement _
3918 | CaseLabel _
3919 | DefaultLabel _ ->
3920 let errors = statement_errors env node parents errors in
3921 trait_require_clauses, names, errors
3922 | MethodishDeclaration _
3923 | FunctionDeclaration _
3924 | FunctionDeclarationHeader _ ->
3925 let errors = reified_parameter_errors node errors in
3926 let names, errors =
3927 redeclaration_errors env node parents namespace_name names errors in
3928 let errors =
3929 methodish_errors env node errors in
3930 trait_require_clauses, names, errors
3931 | ArrayCreationExpression _
3932 | ArrayIntrinsicExpression _ ->
3933 let errors =
3934 expression_errors env is_in_concurrent_block namespace_name node parents errors in
3935 trait_require_clauses, names, errors
3936 | InstanceofExpression _
3937 | LiteralExpression _
3938 | SafeMemberSelectionExpression _
3939 | HaltCompilerExpression _
3940 | FunctionCallExpression _
3941 | ListExpression _
3942 | ShapeExpression _
3943 | DecoratedExpression _
3944 | VectorIntrinsicExpression _
3945 | DictionaryIntrinsicExpression _
3946 | KeysetIntrinsicExpression _
3947 | VarrayIntrinsicExpression _
3948 | DarrayIntrinsicExpression _
3949 | YieldFromExpression _
3950 | YieldExpression _
3951 | ScopeResolutionExpression _
3952 | PrefixUnaryExpression _
3953 | LambdaExpression _
3954 | IsExpression _
3955 | AsExpression _
3956 | AnonymousFunction _
3957 | SubscriptExpression _
3958 | ConstructorCall _
3959 | AwaitableCreationExpression _
3960 | PipeVariableExpression _
3961 | ConditionalExpression _
3962 | CollectionLiteralExpression _
3963 | VariableExpression _ ->
3964 let errors = dynamic_method_call_errors node errors in
3965 let errors =
3966 expression_errors env is_in_concurrent_block namespace_name node parents errors in
3967 let errors = check_nonrx_annotation node errors in
3968 let errors = assignment_errors env node errors in
3969 trait_require_clauses, names, errors
3970 | RequireClause _ ->
3971 let trait_require_clauses, errors =
3972 require_errors env node trait_require_clauses errors in
3973 trait_require_clauses, names, errors
3974 | ClassishDeclaration _ ->
3975 let names, errors =
3976 classish_errors env node namespace_name names errors in
3977 let errors = class_reified_param_errors env node errors in
3978 trait_require_clauses, names, errors
3979 | ConstDeclaration _ ->
3980 let errors =
3981 class_element_errors env node errors in
3982 trait_require_clauses, names, errors
3983 | AliasDeclaration _ ->
3984 let names, errors = alias_errors env node namespace_name names errors in
3985 trait_require_clauses, names, errors
3986 | ConstantDeclarator _ ->
3987 let names, errors =
3988 const_decl_errors env node namespace_name names errors in
3989 trait_require_clauses, names, errors
3990 | NamespaceBody _
3991 | NamespaceEmptyBody _
3992 | NamespaceDeclaration _ ->
3993 let errors =
3994 mixed_namespace_errors env node parents namespace_type errors in
3995 trait_require_clauses, names, errors
3996 | NamespaceUseDeclaration _
3997 | NamespaceGroupUseDeclaration _ ->
3998 let errors = group_use_errors env node errors in
3999 let names, errors =
4000 namespace_use_declaration_errors env node
4001 (namespace_name = global_namespace_name) names errors in
4002 trait_require_clauses, names, errors
4003 | PropertyDeclaration _ ->
4004 let errors = class_property_visibility_errors env node errors in
4005 let errors = class_reified_param_errors env node errors in
4006 trait_require_clauses, names, errors
4007 | EnumDeclaration _ ->
4008 let errors = enum_decl_errors node errors in
4009 trait_require_clauses, names, errors
4010 | Enumerator _ ->
4011 let errors = enumerator_errors node errors in
4012 trait_require_clauses, names, errors
4013 | PostfixUnaryExpression _
4014 | BinaryExpression _
4015 | ForeachStatement _ ->
4016 let errors = assignment_errors env node errors in
4017 trait_require_clauses, names, errors
4018 | XHPEnumType _
4019 | XHPExpression _ ->
4020 let errors = xhp_errors env node errors in
4021 trait_require_clauses, names, errors
4022 | PropertyDeclarator { property_initializer = init; _ } ->
4023 let errors =
4024 produce_error errors check_static_in_initializer init
4025 SyntaxError.parent_static_prop_decl init in
4026 let errors = check_constant_expression errors init in
4027 trait_require_clauses, names, errors
4029 | XHPClassAttribute { xhp_attribute_decl_initializer = init; _ } ->
4030 let errors = check_constant_expression errors init in
4031 trait_require_clauses, names, errors
4032 | _ -> trait_require_clauses, names, errors in
4034 let errors = lval_errors env node parents errors in
4036 match syntax node with
4037 | LambdaExpression _
4038 | AwaitableCreationExpression _
4039 | AnonymousFunction _ ->
4040 (* reset is_in_concurrent_block for functions *)
4041 let acc1 =
4042 make_acc
4043 acc errors namespace_type names
4044 namespace_name trait_require_clauses
4045 false in
4046 (* analyze the body of lambda block *)
4047 let acc1 = fold_child_nodes env folder node parents acc1 in
4048 (* adjust is_in_concurrent_block in final result *)
4049 make_acc
4050 acc acc1.errors acc1.namespace_type acc1.names
4051 acc1.namespace_name acc1.trait_require_clauses
4052 is_in_concurrent_block
4054 | ConcurrentStatement { concurrent_statement = { syntax = statement; _ }; _ } ->
4055 (* issue error if concurrent blocks are nested *)
4056 let errors =
4057 if is_in_concurrent_block
4058 then make_error_from_node
4059 node SyntaxError.nested_concurrent_blocks :: errors
4060 else errors in
4062 (* issue error if concurrent block isn't well formed
4063 - must have at least two statements
4064 - must be a compound statement
4065 - must only contain expression statements
4066 - statement without await
4068 let errors = (match statement with
4069 | CompoundStatement { compound_statements = statements; _ } ->
4070 let statement_list = syntax_to_list_no_separators statements in
4071 let errors = (match statement_list with
4072 | _ :: _ :: _ -> errors
4073 | _ -> make_error_from_node node
4074 SyntaxError.fewer_than_two_statements_in_concurrent_block :: errors
4075 ) in
4077 let errors = List.fold_left ~init:errors ~f:(fun errors n ->
4078 match syntax n with
4079 | ExpressionStatement { expression_statement_expression = se; _ } ->
4080 if node_has_await_child se then errors else
4081 make_error_from_node n
4082 SyntaxError.statement_without_await_in_concurrent_block :: errors
4083 | _ -> make_error_from_node n SyntaxError.invalid_syntax_concurrent_block :: errors
4084 ) statement_list in
4086 List.append (find_invalid_lval_usage statement_list) errors
4087 | _ -> make_error_from_node node SyntaxError.invalid_syntax_concurrent_block :: errors) in
4089 (* adjust is_in_concurrent_block in accumulator to dive into the
4090 concurrent block *)
4091 let acc1 =
4092 make_acc
4093 acc errors namespace_type names
4094 namespace_name trait_require_clauses
4095 true in
4096 (* analyze the body of concurrent block *)
4097 let acc1 = fold_child_nodes env folder node parents acc1 in
4098 (* adjust is_in_concurrent_block in final result *)
4099 make_acc
4100 acc acc1.errors acc1.namespace_type acc1.names
4101 acc1.namespace_name acc1.trait_require_clauses
4102 is_in_concurrent_block
4104 | NamespaceBody { namespace_left_brace; namespace_right_brace; _ } ->
4105 let namespace_type =
4106 if namespace_type = Unspecified
4107 then Bracketed (make_location namespace_left_brace namespace_right_brace)
4108 else namespace_type in
4109 (* reset names/namespace_type before diving into namespace body,
4110 keeping global function names *)
4111 let namespace_name = get_namespace_name env.context namespace_name in
4112 let is_global _ f = f.f_global in
4113 let global_funs names = strmap_filter is_global names.t_functions in
4114 let new_names = {empty_names with t_functions = global_funs names} in
4115 let acc1 =
4116 make_acc
4117 acc errors namespace_type new_names
4118 namespace_name empty_trait_require_clauses
4119 is_in_concurrent_block
4121 let acc1 = fold_child_nodes env folder node parents acc1 in
4122 (* add newly declared global functions to the old set of names *)
4123 let old_names =
4124 {acc.names with t_functions =
4125 strmap_union (global_funs acc1.names) acc.names.t_functions}
4127 (* resume with old set of names and pull back
4128 accumulated errors/last seen namespace type *)
4129 make_acc
4130 acc acc1.errors namespace_type old_names
4131 acc.namespace_name acc.trait_require_clauses
4132 acc.is_in_concurrent_block
4133 | NamespaceEmptyBody { namespace_semicolon; _ } ->
4134 let namespace_type =
4135 if namespace_type = Unspecified
4136 then Unbracketed (make_location_of_node namespace_semicolon)
4137 else namespace_type
4139 let namespace_name = get_namespace_name env.context namespace_name in
4140 (* consider the rest of file to be the part of the namespace:
4141 reset names and namespace type, keep errors *)
4142 let acc =
4143 make_acc
4144 acc errors namespace_type empty_names
4145 namespace_name empty_trait_require_clauses
4146 is_in_concurrent_block
4148 fold_child_nodes env folder node parents acc
4149 | ClassishDeclaration _
4150 | AnonymousClass _ ->
4151 (* Reset the trait require clauses *)
4152 (* Reset the const declarations *)
4153 (* Reset the function declarations *)
4154 let cleanup =
4155 fun new_acc ->
4156 { new_acc with names =
4157 { new_acc.names with t_constants = acc.names.t_constants;
4158 t_functions = acc.names.t_functions }} in
4159 let names = { names with t_constants = YesCase SMap.empty;
4160 t_functions = NoCase LSMap.empty } in
4161 let acc =
4162 make_acc
4163 acc errors namespace_type names
4164 namespace_name empty_trait_require_clauses
4165 is_in_concurrent_block
4167 fold_child_nodes ~cleanup env folder node parents acc
4168 | _ ->
4169 let acc =
4170 make_acc
4171 acc errors namespace_type names
4172 namespace_name trait_require_clauses
4173 is_in_concurrent_block
4175 fold_child_nodes env folder node parents acc in
4176 let errors =
4177 if is_typechecker env then is_invalid_hack_mode env []
4178 else []
4180 let acc = fold_child_nodes env folder (SyntaxTree.root env.syntax_tree) []
4181 { errors
4182 ; namespace_type = Unspecified
4183 ; names = empty_names
4184 ; namespace_name = global_namespace_name
4185 ; trait_require_clauses = empty_trait_require_clauses
4186 ; is_in_concurrent_block = false
4187 } in
4188 acc.errors
4190 let parse_errors_impl env =
4192 Minimum: suppress cascading errors; no second-pass errors if there are
4193 any first-pass errors.
4194 Typical: suppress cascading errors; give second pass errors always.
4195 Maximum: all errors
4198 let errors1 = match env.level with
4199 | Maximum -> SyntaxTree.all_errors env.syntax_tree
4200 | _ -> SyntaxTree.errors env.syntax_tree in
4201 let errors2 =
4202 if env.level = Minimum && errors1 <> [] then []
4203 else find_syntax_errors env in
4204 List.sort SyntaxError.compare (List.append errors1 errors2)
4205 with e ->
4206 let error_msg = "UNEXPECTED_ERROR: " ^ (Exn.to_string e) in
4207 [make_error_from_node (SyntaxTree.root env.syntax_tree) error_msg]
4209 let parse_errors env =
4210 Stats_container.wrap_nullary_fn_timing
4211 ?stats:(Stats_container.get_instance ())
4212 ~key:"full_fidelity_parse_errors:parse_errors"
4213 ~f:(fun () -> parse_errors_impl env)
4215 end (* WithSmartConstructors *)
4217 include WithSmartConstructors(SyntaxSmartConstructors.WithSyntax(Syntax))
4219 end (* WithSyntax *)