From 7091dc2e6b8fa6273fd37feb4a17cf36aee17e39 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 27 Aug 2026 20:09:07 +0200 Subject: [PATCH 1/6] Upgrade vendored Flow parser to 0.320.0 Signed-off-by: Christoph Knittel --- compiler/flow_parser/README.md | 10 +- compiler/flow_parser/collections/iMap.ml | 7 +- compiler/flow_parser/collections/iSet.ml | 7 +- compiler/flow_parser/collections/immQueue.ml | 7 +- compiler/flow_parser/collections/immQueue.mli | 7 +- compiler/flow_parser/collections/intKey.ml | 7 +- .../flow_parser/collections/priorityQueue.ml | 7 +- .../reordered_argument_collections.ml | 94 - compiler/flow_parser/collections/sMap.ml | 7 +- compiler/flow_parser/collections/sSet.ml | 7 +- compiler/flow_parser/collections/stringKey.ml | 7 +- .../flow_parser/collections/union_find.ml | 7 +- .../flow_parser/collections/wrappedMap.ml | 7 +- .../flow_parser/collections/wrappedMap.mli | 7 +- .../flow_parser/collections/wrappedMap_sig.ml | 7 +- .../flow_sedlexing/flow_sedlexing.ml | 9 + .../flow_sedlexing/flow_sedlexing.mli | 4 +- .../flow_sedlexing_ppx/ppx_sedlex.ml | 10 +- .../flow_parser/parser/comment_attachment.ml | 42 +- .../flow_parser/parser/declaration_parser.ml | 447 ++++- compiler/flow_parser/parser/enum_common.ml | 21 - compiler/flow_parser/parser/enum_parser.ml | 314 +--- .../flow_parser/parser/estree_translator.ml | 1404 +++++++++++---- .../flow_parser/parser/expression_parser.ml | 366 ++-- compiler/flow_parser/parser/file_key.ml | 249 ++- compiler/flow_parser/parser/flow_ast.ml | 511 +++++- .../flow_parser/parser/flow_ast_mapper.ml | 1028 ++++++++--- compiler/flow_parser/parser/flow_ast_utils.ml | 184 +- .../flow_parser/parser/flow_ast_utils.mli | 40 + compiler/flow_parser/parser/flow_lexer.ml | 887 +++++---- compiler/flow_parser/parser/flow_lexer.mli | 4 + compiler/flow_parser/parser/js_id_unicode.ml | 5 +- compiler/flow_parser/parser/jsdoc.ml | 50 +- compiler/flow_parser/parser/jsdoc.mli | 4 +- compiler/flow_parser/parser/lex_env.ml | 12 + compiler/flow_parser/parser/lex_result.ml | 2 + compiler/flow_parser/parser/loc.ml | 2 + compiler/flow_parser/parser/loc.mli | 2 + .../parser/match_pattern_parser.ml | 155 +- compiler/flow_parser/parser/object_parser.ml | 1536 +++++++++++++--- compiler/flow_parser/parser/parse_error.ml | 194 +- compiler/flow_parser/parser/parser_common.ml | 61 +- compiler/flow_parser/parser/parser_env.ml | 187 +- compiler/flow_parser/parser/parser_env.mli | 41 +- compiler/flow_parser/parser/parser_flow.ml | 126 +- compiler/flow_parser/parser/pattern_parser.ml | 61 +- .../flow_parser/parser/statement_parser.ml | 1582 +++++++++++++---- compiler/flow_parser/parser/token.ml | 6 + .../flow_parser/parser/token_translator.ml | 1 - compiler/flow_parser/parser/type_parser.ml | 990 ++++++++++- 50 files changed, 7870 insertions(+), 2862 deletions(-) delete mode 100644 compiler/flow_parser/collections/reordered_argument_collections.ml delete mode 100644 compiler/flow_parser/parser/enum_common.ml diff --git a/compiler/flow_parser/README.md b/compiler/flow_parser/README.md index fd45180bba8..06d33f59630 100644 --- a/compiler/flow_parser/README.md +++ b/compiler/flow_parser/README.md @@ -2,17 +2,17 @@ This directory contains the OCaml Flow parser used by the ReScript compiler. -- Upstream fork: https://github.com/rescript-lang/flow -- Flow parser version: `0.267.0` -- Source commit: `9ea4062c0b7e037415c4413a7634c459ebd5c31b` +- Upstream repository: https://github.com/facebook/flow +- Flow parser version: `0.320.0` +- Source commit: `7c64d4b077bc6fc45c12cee3cfa7368fdb2186ce` - Original source directories: `src/parser`, `src/third-party/sedlex`, `src/third-party/sedlex-ppx`, and `src/hack_forked/utils/collections` The Dune files were adapted to build these sources as private libraries inside the ReScript repository. Sources used only by the upstream JavaScript and C API targets are not included. One ambiguous Sedlex documentation comment was -converted to a regular comment so the vendored sources build with ReScript's -warning settings. +converted to a regular comment and one unused loop index was renamed so the +vendored sources build with ReScript's warning settings. Vendored sources are excluded from the repository-wide OCamlformat check so that they remain comparable with their upstream versions. diff --git a/compiler/flow_parser/collections/iMap.ml b/compiler/flow_parser/collections/iMap.ml index 93c66823ebc..2d0103ac1b4 100644 --- a/compiler/flow_parser/collections/iMap.ml +++ b/compiler/flow_parser/collections/iMap.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) include WrappedMap.Make (IntKey) diff --git a/compiler/flow_parser/collections/iSet.ml b/compiler/flow_parser/collections/iSet.ml index 471f51dda4a..30e3896b3d9 100644 --- a/compiler/flow_parser/collections/iSet.ml +++ b/compiler/flow_parser/collections/iSet.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) include Flow_set.Make (IntKey) diff --git a/compiler/flow_parser/collections/immQueue.ml b/compiler/flow_parser/collections/immQueue.ml index 3f326a45855..ae119f15472 100644 --- a/compiler/flow_parser/collections/immQueue.ml +++ b/compiler/flow_parser/collections/immQueue.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) type 'a t = { incoming: 'a list; diff --git a/compiler/flow_parser/collections/immQueue.mli b/compiler/flow_parser/collections/immQueue.mli index 67743a82b5e..810a9fc5fd7 100644 --- a/compiler/flow_parser/collections/immQueue.mli +++ b/compiler/flow_parser/collections/immQueue.mli @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) (* * Immutable queue implementation. Modeled loosely after the mutable stdlib diff --git a/compiler/flow_parser/collections/intKey.ml b/compiler/flow_parser/collections/intKey.ml index 4701376db44..01d02145bd8 100644 --- a/compiler/flow_parser/collections/intKey.ml +++ b/compiler/flow_parser/collections/intKey.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) type t = int diff --git a/compiler/flow_parser/collections/priorityQueue.ml b/compiler/flow_parser/collections/priorityQueue.ml index cc2b52866f8..119795503b9 100644 --- a/compiler/flow_parser/collections/priorityQueue.ml +++ b/compiler/flow_parser/collections/priorityQueue.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) module Make (Ord : Set.OrderedType) = struct type elt = Ord.t diff --git a/compiler/flow_parser/collections/reordered_argument_collections.ml b/compiler/flow_parser/collections/reordered_argument_collections.ml deleted file mode 100644 index 052743beb93..00000000000 --- a/compiler/flow_parser/collections/reordered_argument_collections.ml +++ /dev/null @@ -1,94 +0,0 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -module Reordered_argument_map (S : WrappedMap.S) = struct - include S - - let add m ~key ~data = add key data m - - let filter m ~f = filter f m - - let fold m ~init ~f = fold f m init - - let find_opt m k = find_opt k m - - let find m k = find k m - - let iter m ~f = iter f m - - let map m ~f = map f m - - let mapi m ~f = mapi f m - - let mem m v = mem v m - - let remove m v = remove v m - - let exists m ~f = exists f m - - let merge m1 m2 ~f = merge f m1 m2 - - let filter m ~f = filter m ~f - - let partition m ~f = partition f m -end - -module Reordered_argument_set (S : Flow_set.S) = struct - include S - - let add s v = add v s - - let filter s ~f = filter f s - - let fold s ~init ~f = fold f s init - - let iter s ~f = iter f s - - let mem s v = mem v s - - let remove s v = remove v s - - let exists s ~f = exists f s - - let of_list l = List.fold_left add S.empty l - - let make_pp pp fmt x = - Format.fprintf fmt "@[{"; - let elts = elements x in - (match elts with - | [] -> () - | _ -> Format.fprintf fmt " "); - ignore - (List.fold_left - (fun sep elt -> - if sep then Format.fprintf fmt ";@ "; - let () = pp fmt elt in - true) - false - elts - ); - (match elts with - | [] -> () - | _ -> Format.fprintf fmt " "); - Format.fprintf fmt "}@]" -end - -module SSet = struct - include Reordered_argument_set (SSet) - - let pp = SSet.pp - - let show = SSet.show -end - -module SMap = struct - include Reordered_argument_map (SMap) - - let pp = SMap.pp - - let show = SMap.show -end diff --git a/compiler/flow_parser/collections/sMap.ml b/compiler/flow_parser/collections/sMap.ml index e179a06c9c1..27c9fee4928 100644 --- a/compiler/flow_parser/collections/sMap.ml +++ b/compiler/flow_parser/collections/sMap.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) include WrappedMap.Make (StringKey) diff --git a/compiler/flow_parser/collections/sSet.ml b/compiler/flow_parser/collections/sSet.ml index b197a0f2bd7..e9a1c731d80 100644 --- a/compiler/flow_parser/collections/sSet.ml +++ b/compiler/flow_parser/collections/sSet.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) include Flow_set.Make (StringKey) diff --git a/compiler/flow_parser/collections/stringKey.ml b/compiler/flow_parser/collections/stringKey.ml index 777cbda1ba6..cecc381a6f9 100644 --- a/compiler/flow_parser/collections/stringKey.ml +++ b/compiler/flow_parser/collections/stringKey.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) type t = string diff --git a/compiler/flow_parser/collections/union_find.ml b/compiler/flow_parser/collections/union_find.ml index 2b4b466f511..97c2bc80e90 100644 --- a/compiler/flow_parser/collections/union_find.ml +++ b/compiler/flow_parser/collections/union_find.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) type ident = int diff --git a/compiler/flow_parser/collections/wrappedMap.ml b/compiler/flow_parser/collections/wrappedMap.ml index 777ed8ad484..ccabf045bc2 100644 --- a/compiler/flow_parser/collections/wrappedMap.ml +++ b/compiler/flow_parser/collections/wrappedMap.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) module type S = WrappedMap_sig.S diff --git a/compiler/flow_parser/collections/wrappedMap.mli b/compiler/flow_parser/collections/wrappedMap.mli index cc0e9bad665..16486176e39 100644 --- a/compiler/flow_parser/collections/wrappedMap.mli +++ b/compiler/flow_parser/collections/wrappedMap.mli @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) module type S = WrappedMap_sig.S diff --git a/compiler/flow_parser/collections/wrappedMap_sig.ml b/compiler/flow_parser/collections/wrappedMap_sig.ml index d8abefca90f..c2acf487ef5 100644 --- a/compiler/flow_parser/collections/wrappedMap_sig.ml +++ b/compiler/flow_parser/collections/wrappedMap_sig.ml @@ -1,9 +1,4 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) +(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) module type S = sig include Flow_map.S diff --git a/compiler/flow_parser/flow_sedlexing/flow_sedlexing.ml b/compiler/flow_parser/flow_sedlexing/flow_sedlexing.ml index 1aec0e2ee51..b1ff829ef26 100644 --- a/compiler/flow_parser/flow_sedlexing/flow_sedlexing.ml +++ b/compiler/flow_parser/flow_sedlexing/flow_sedlexing.ml @@ -285,3 +285,12 @@ let string_of_utf8 (lexbuf : int array) : string = let backoff lexbuf npos = lexbuf.pos <- lexbuf.pos - npos + +let peek lexbuf n = + let i = lexbuf.pos + n in + if i >= lexbuf.len then + -1 + else + lexbuf.buf.!(i) + +let bump lexbuf n = lexbuf.pos <- lexbuf.pos + n diff --git a/compiler/flow_parser/flow_sedlexing/flow_sedlexing.mli b/compiler/flow_parser/flow_sedlexing/flow_sedlexing.mli index bbae88219dc..e0ee791524e 100644 --- a/compiler/flow_parser/flow_sedlexing/flow_sedlexing.mli +++ b/compiler/flow_parser/flow_sedlexing/flow_sedlexing.mli @@ -32,7 +32,7 @@ module Utf8 : sig val from_string : string -> lexbuf val sub_lexeme : lexbuf -> int -> int -> string val lexeme : lexbuf -> string - (* This API avoids another allocation. *) + (* This API avoids another allocation *) val lexeme_to_buffer : lexbuf -> Buffer.t -> unit val lexeme_to_buffer2 : lexbuf -> Buffer.t -> Buffer.t -> unit end @@ -45,3 +45,5 @@ val string_of_utf8 : int array -> string val current_code_point : lexbuf -> int val backoff : lexbuf -> int -> unit val set_lexeme_start : lexbuf -> int -> unit +val peek : lexbuf -> int -> int +val bump : lexbuf -> int -> unit diff --git a/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml b/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml index 80f0db4bfcb..44c063e7fe9 100644 --- a/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml +++ b/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml @@ -4,7 +4,6 @@ open Ppxlib open Ast_builder.Default -open Ast_helper (* let ocaml_version = Versions.ocaml_408 *) @@ -360,14 +359,7 @@ let gen_state lexbuf auto i (trans, final) = (appfun (partition_name partition) [[%expr Sedlexing.__private__next_int [%e evar ~loc lexbuf]]]) (cases @ [case ~lhs:[%pat? _] ~guard:None ~rhs:[%expr Sedlexing.backtrack [%e evar ~loc lexbuf]]]) in - let ret body = - let lhs = pvar ~loc lexbuf in - [ - value_binding ~loc - ~pat:(pvar ~loc (state_fun i)) - ~expr:(Exp.fun_ ~loc Nolabel None lhs body); - ] - in + let ret body = [ value_binding ~loc ~pat:(pvar ~loc (state_fun i)) ~expr:(pexp_function ~loc [case ~lhs:(pvar ~loc lexbuf) ~guard:None ~rhs:body]) ] in match best_final final with | None -> ret (body ()) | Some _ when Array.length trans = 0 -> [] diff --git a/compiler/flow_parser/parser/comment_attachment.ml b/compiler/flow_parser/parser/comment_attachment.ml index bf1f1e8a440..3d0b283a536 100644 --- a/compiler/flow_parser/parser/comment_attachment.ml +++ b/compiler/flow_parser/parser/comment_attachment.ml @@ -134,7 +134,7 @@ class ['loc] trailing_comments_remover ~after_pos = let open Ast.Class.Implements.Interface in let (loc, { id = id_; targs }) = interface in if targs = None then - id this#identifier id_ interface (fun id' -> (loc, { id = id'; targs })) + id this#generic_identifier_type id_ interface (fun id' -> (loc, { id = id'; targs })) else id (map_opt this#type_args) targs interface (fun targs' -> (loc, { id = id_; targs = targs' }) @@ -143,7 +143,7 @@ class ['loc] trailing_comments_remover ~after_pos = method! component_declaration _loc component = let open Ast.Statement.ComponentDeclaration in let { body; comments; _ } = component in - let body' = this#component_body body in + let body' = map_opt this#component_body body in let comments' = this#syntax_opt comments in if body == body' && comments == comments' then component @@ -189,7 +189,7 @@ class ['loc] trailing_comments_remover ~after_pos = (loc, { params with comments = comments' }) ) - method! function_type _loc func = + method! function_type func = let open Ast.Type.Function in let { return; comments; _ } = func in let return' = this#function_type_return_annotation return in @@ -209,8 +209,9 @@ class ['loc] trailing_comments_remover ~after_pos = git else Qualified (loc, { qualified with id = id' }) + | ImportTypeAnnot _ -> git - method! import _loc expr = + method! import expr = let open Ast.Expression.Import in let { comments; _ } = expr in id this#syntax_opt comments expr (fun comments' -> { expr with comments = comments' }) @@ -218,7 +219,10 @@ class ['loc] trailing_comments_remover ~after_pos = method! interface_type _loc t = let open Ast.Type.Interface in let { body; comments; _ } = t in - let body' = map_loc this#object_type body in + let body' = + let (bloc, b) = body in + id this#object_type b body (fun b' -> (bloc, b')) + in let comments' = this#syntax_opt comments in if body == body' && comments == comments' then t @@ -300,7 +304,7 @@ class ['loc] trailing_comments_remover ~after_pos = let { comments; _ } = expr in id this#syntax_opt comments expr (fun comments' -> { expr with comments = comments' }) - method! object_type _loc obj = + method! object_type obj = let open Ast.Type.Object in let { comments; _ } = obj in id this#syntax_opt comments obj (fun comments' -> { obj with comments = comments' }) @@ -485,12 +489,19 @@ let object_key_remove_trailing env key = let generic_type_remove_trailing env ty = let { remove_trailing; _ } = trailing_and_remover env in - remove_trailing ty (fun remover ty -> map_loc remover#generic_type ty) + remove_trailing ty (fun remover ty -> + let (tyloc, t) = ty in + id remover#generic_type t ty (fun t' -> (tyloc, t')) + ) let generic_type_list_remove_trailing env extends = let { remove_trailing; _ } = trailing_and_remover env in remove_trailing extends (fun remover extends -> - id_list_last (map_loc remover#generic_type) extends + id_list_last + (fun ty -> + let (tyloc, t) = ty in + id remover#generic_type t ty (fun t' -> (tyloc, t'))) + extends ) let class_implements_remove_trailing env implements = @@ -499,7 +510,7 @@ let class_implements_remove_trailing env implements = let string_literal_remove_trailing env str = let { remove_trailing; _ } = trailing_and_remover env in - remove_trailing str (fun remover (loc, str) -> (loc, remover#string_literal loc str)) + remove_trailing str (fun remover (loc, str) -> (loc, remover#string_literal str)) let statement_add_comments ((loc, stmt) : (Loc.t, Loc.t) Statement.t) (comments : (Loc.t, unit) Syntax.t option) : @@ -557,6 +568,11 @@ let statement_add_comments { s with ExportDefaultDeclaration.comments = merge_comments comments } | ExportNamedDeclaration ({ ExportNamedDeclaration.comments; _ } as s) -> ExportNamedDeclaration { s with ExportNamedDeclaration.comments = merge_comments comments } + | ExportAssignment ({ ExportAssignment.comments; _ } as s) -> + ExportAssignment { s with ExportAssignment.comments = merge_comments comments } + | NamespaceExportDeclaration ({ NamespaceExportDeclaration.comments; _ } as s) -> + NamespaceExportDeclaration + { s with NamespaceExportDeclaration.comments = merge_comments comments } | Expression ({ Expression.comments; _ } as s) -> Expression { s with Expression.comments = merge_comments comments } | For ({ For.comments; _ } as s) -> For { s with For.comments = merge_comments comments } @@ -569,12 +585,16 @@ let statement_add_comments | If ({ If.comments; _ } as s) -> If { s with If.comments = merge_comments comments } | ImportDeclaration ({ ImportDeclaration.comments; _ } as s) -> ImportDeclaration { s with ImportDeclaration.comments = merge_comments comments } + | ImportEqualsDeclaration ({ ImportEqualsDeclaration.comments; _ } as s) -> + ImportEqualsDeclaration { s with ImportEqualsDeclaration.comments = merge_comments comments } | InterfaceDeclaration ({ Interface.comments; _ } as s) -> InterfaceDeclaration { s with Interface.comments = merge_comments comments } | Labeled ({ Labeled.comments; _ } as s) -> Labeled { s with Labeled.comments = merge_comments comments } | Match ({ Match.comments; _ } as s) -> Match { s with Match.comments = merge_comments comments } + | RecordDeclaration ({ RecordDeclaration.comments; _ } as s) -> + RecordDeclaration { s with RecordDeclaration.comments = merge_comments comments } | Return ({ Return.comments; _ } as s) -> Return { s with Return.comments = merge_comments comments } | Switch ({ Switch.comments; _ } as s) -> @@ -737,6 +757,10 @@ let object_type_property_comment_bounds property = let collector = new comment_bounds_collector ~loc in ignore (collector#object_mapped_type_property p); collector + | PrivateField ((loc, _) as p) -> + let collector = new comment_bounds_collector ~loc in + ignore (collector#object_private_field_type p); + collector in collect_without_trailing_line_comment collector diff --git a/compiler/flow_parser/parser/declaration_parser.ml b/compiler/flow_parser/parser/declaration_parser.ml index d4dbd23c806..992c4c4b80e 100644 --- a/compiler/flow_parser/parser/declaration_parser.ml +++ b/compiler/flow_parser/parser/declaration_parser.ml @@ -61,7 +61,18 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : let (_, { Ast.Function.Params.params; rest; this_ = _; comments = _ }) = params in let acc = List.fold_left - (fun acc (_, { Function.Param.argument; default = _ }) -> check_param acc argument) + (fun acc (loc, param) -> + match param with + | Function.Param.RegularParam { argument; default = _ } -> check_param acc argument + | Function.Param.ParamProperty { Class.Property.key; annot; _ } -> + (match key with + | Expression.Object.Property.Identifier ((id_loc, _) as name) -> + check_param + acc + (id_loc, Pattern.Identifier { Pattern.Identifier.name; annot; optional = false }) + | _ -> + error_at env (loc, Parse_error.ExpectedPatternFoundExpression); + acc)) (env, SSet.empty) params in @@ -94,6 +105,7 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : { Ast.Pattern.Object.properties = pattern_obj_props; annot = Ast.Type.Missing Loc.none; + optional = false; comments = None; } ) @@ -104,6 +116,55 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : ignore (check_param acc argument) | None -> () + let variance env ~parse_property_variance_keyword is_async is_generator = + let loc = Peek.loc env in + let variance = + match Peek.token env with + | T_PLUS -> + let leading = Peek.comments env in + Eat.token env; + Some + ( loc, + { Variance.kind = Variance.Plus; comments = Flow_ast_utils.mk_comments_opt ~leading () } + ) + | T_MINUS -> + let leading = Peek.comments env in + Eat.token env; + Some + ( loc, + { + Variance.kind = Variance.Minus; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + | T_IDENTIFIER { raw = "readonly"; _ } when parse_property_variance_keyword -> + let leading = Peek.comments env in + Eat.token env; + Some + ( loc, + { + Variance.kind = Variance.Readonly; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + | T_IDENTIFIER { raw = "writeonly"; _ } when parse_property_variance_keyword -> + let leading = Peek.comments env in + Eat.token env; + Some + ( loc, + { + Variance.kind = Variance.Writeonly; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + | _ -> None + in + match variance with + | Some (loc, _) when is_async || is_generator -> + error_at env (loc, Parse_error.UnexpectedVariance); + None + | _ -> variance + type param_type = | FunctionParams of (Loc.t, Loc.t) Ast.Function.Params.t | ComponentParams of (Loc.t, Loc.t) Ast.Statement.ComponentDeclaration.Params.t @@ -144,14 +205,14 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : let strict_component_post_check env ~contains_use_strict id params = strict_post_check env ~contains_use_strict (Some id) (ComponentParams params) - let rest_param env t = + let rest_param ~allow_optional env t = if t = T_ELLIPSIS then let leading = Peek.comments env in let (loc, id) = with_loc (fun env -> Expect.token env T_ELLIPSIS; - Parse.pattern env Parse_error.StrictParamName) + Parse.pattern env ~allow_optional Parse_error.StrictParamName) env in let comments = Flow_ast_utils.mk_comments_opt ~leading () in @@ -160,24 +221,84 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : None let function_params = + let param_property env ts_accessibility = + let variance = variance env ~parse_property_variance_keyword:true false false in + let leading = Peek.comments env in + let (name_loc, name_id) = Parse.identifier env in + let key = + Ast.Expression.Object.Property.Identifier + (name_loc, { name_id with Ast.Identifier.comments = None }) + in + let annot = Type.annotation_opt env in + let value = + if Peek.token env = T_ASSIGN then ( + Expect.token env T_ASSIGN; + Ast.Class.Property.Initialized (Parse.assignment env) + ) else + Ast.Class.Property.Uninitialized + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + { + Ast.Class.Property.key; + value; + annot; + static = false; + override = false; + optional = false; + variance; + ts_accessibility; + decorators = []; + comments; + } + in let rec param = with_loc (fun env -> if Peek.token env = T_THIS then error env Parse_error.ThisParamMustBeFirst; - let argument = Parse.pattern env Parse_error.StrictParamName in - let default = - if Peek.token env = T_ASSIGN then ( - Expect.token env T_ASSIGN; - Some (Parse.assignment env) - ) else - None - in - { Function.Param.argument; default } + match Peek.token env with + | (T_PUBLIC as t) + | (T_PRIVATE as t) + | (T_PROTECTED as t) + when Peek.ith_is_identifier ~i:1 env -> + let ts_accessibility = + (with_loc (fun env -> + let leading = Peek.comments env in + Eat.token env; + let kind = + match t with + | T_PUBLIC -> Ast.Class.TSAccessibility.Public + | T_PRIVATE -> Ast.Class.TSAccessibility.Private + | T_PROTECTED -> Ast.Class.TSAccessibility.Protected + | _ -> failwith "Must be one of the above" + in + { + Ast.Class.TSAccessibility.kind; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + ) + env + in + Function.Param.ParamProperty (param_property env (Some ts_accessibility)) + | T_IDENTIFIER { raw = "readonly"; _ } when Peek.ith_is_identifier ~i:1 env -> + Function.Param.ParamProperty (param_property env None) + | T_IDENTIFIER { raw = "writeonly"; _ } when Peek.ith_is_identifier ~i:1 env -> + Function.Param.ParamProperty (param_property env None) + | _ -> + let argument = Parse.pattern env ~allow_optional:true Parse_error.StrictParamName in + let default = + if Peek.token env = T_ASSIGN then ( + Expect.token env T_ASSIGN; + Some (Parse.assignment env) + ) else + None + in + Function.Param.RegularParam { argument; default } ) and param_list env acc = match Peek.token env with | (T_EOF | T_RPAREN | T_ELLIPSIS) as t -> let rest = - rest_param env t + rest_param ~allow_optional:false env t |> Option.map (fun (loc, id, comments) -> (loc, { Function.RestParam.argument = id; comments }) ) @@ -250,45 +371,6 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : in (Function.BodyBlock body_block, contains_use_strict) - let variance env ~parse_readonly is_async is_generator = - let loc = Peek.loc env in - let variance = - match Peek.token env with - | T_PLUS -> - let leading = Peek.comments env in - Eat.token env; - Some - ( loc, - { Variance.kind = Variance.Plus; comments = Flow_ast_utils.mk_comments_opt ~leading () } - ) - | T_MINUS -> - let leading = Peek.comments env in - Eat.token env; - Some - ( loc, - { - Variance.kind = Variance.Minus; - comments = Flow_ast_utils.mk_comments_opt ~leading (); - } - ) - | T_IDENTIFIER { raw = "readonly"; _ } when parse_readonly -> - let leading = Peek.comments env in - Eat.token env; - Some - ( loc, - { - Variance.kind = Variance.Readonly; - comments = Flow_ast_utils.mk_comments_opt ~leading (); - } - ) - | _ -> None - in - match variance with - | Some (loc, _) when is_async || is_generator -> - error_at env (loc, Parse_error.UnexpectedVariance); - None - | _ -> variance - let generator env = if Peek.token env = T_MULT then ( let leading = Peek.comments env in @@ -307,6 +389,85 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : else (false, []) + (* Convert a Function.Param to Type.Function.Param for implicit declare function. + Returns Ok type_param or Error error_reason. *) + let convert_function_param_to_type_param (param_loc, param) = + let open Function.Param in + match param with + | RegularParam { argument; default = _ } -> + (match argument with + | (_, Pattern.Identifier { Pattern.Identifier.name; annot; optional }) -> + (match annot with + | Ast.Type.Available (_, annot_type) -> + Ok (param_loc, Ast.Type.Function.Param.Labeled { name; annot = annot_type; optional }) + | Ast.Type.Missing _ -> + let (_, { Identifier.name = param_name; _ }) = name in + Error (Printf.sprintf "parameter '%s' is missing a type annotation" param_name)) + | (_, Pattern.Object { Pattern.Object.annot = Ast.Type.Available _; _ }) + | (_, Pattern.Array { Pattern.Array.annot = Ast.Type.Available _; _ }) -> + Ok (param_loc, Ast.Type.Function.Param.Destructuring argument) + | (_, Pattern.Object _) + | (_, Pattern.Array _) -> + Error "destructuring parameter is missing a type annotation" + | _ -> Error "complex parameter patterns are not allowed") + | ParamProperty _ -> Error "parameter properties are not allowed" + + (* Convert Function.Params to Type.Function.Params for implicit declare function. + Returns Ok type_params or Error error_reasons. *) + let convert_function_params_to_type_params (params_loc, params) = + let open Function.Params in + let { this_; params = param_list; rest; comments } = params in + let (converted, errors) = + List.fold_left + (fun (acc_params, acc_errors) param -> + match convert_function_param_to_type_param param with + | Ok p -> (p :: acc_params, acc_errors) + | Error e -> (acc_params, e :: acc_errors)) + ([], []) + param_list + in + (* Convert this_ param *) + let type_this = + match this_ with + | Some (loc, { Function.ThisParam.annot; comments = this_comments }) -> + Some (loc, { Ast.Type.Function.ThisParam.annot; comments = this_comments }) + | None -> None + in + (* Convert rest param - simplified, may need more robust handling *) + let (type_rest, rest_errors) = + match rest with + | Some (rest_loc, { Function.RestParam.argument; comments = rest_comments }) -> + (match + convert_function_param_to_type_param + (rest_loc, Function.Param.RegularParam { argument; default = None }) + with + | Ok (_, type_param) -> + let rest_param = + ( rest_loc, + { + Ast.Type.Function.RestParam.argument = (rest_loc, type_param); + comments = rest_comments; + } + ) + in + (Some rest_param, []) + | Error e -> (None, [e])) + | None -> (None, []) + in + let all_errors = errors @ rest_errors in + if all_errors = [] then + Ok + ( params_loc, + { + Ast.Type.Function.Params.this_ = type_this; + params = List.rev converted; + rest = type_rest; + comments; + } + ) + else + Error all_errors + let _function = with_loc (fun env -> let (async, leading_async) = async env in @@ -319,7 +480,7 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : | T_FUNCTION -> Eat.token env; (Function.Arbitrary, generator env) - | T_IDENTIFIER { raw = "hook"; _ } when not async -> + | T_IDENTIFIER { raw = "hook"; _ } -> Eat.token env; (Function.Hook, (false, [])) | t -> @@ -385,25 +546,103 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : (generator, effect_, tparams, id, params, return, predicate, leading)) env in - let simple_params = is_simple_parameter_list params in - let (body, contains_use_strict) = - function_body env ~async ~generator ~expression:false ~simple_params + (* Check for implicit declare function: in ambient context with semicolon instead of body. + We check all conversion conditions BEFORE consuming the semicolon. If conversion + would fail, we fall through to normal body parsing which will emit appropriate errors. + We also accept implicit semicolons (ASI). *) + let implicit_declare_conversion = + if + in_ambient_context env + && (Peek.token env = T_SEMICOLON || Peek.is_implicit_semicolon env) + && (not async) + && (not generator) + && effect_ <> Function.Hook + && + match return with + | Function.ReturnAnnot.Missing _ -> false + | _ -> true + then + match convert_function_params_to_type_params params with + | Ok tp -> Some tp + | Error _ -> None + else + None in - strict_function_post_check env ~contains_use_strict id params; - Statement.FunctionDeclaration - { - Function.id; - params; - body; - generator; - effect_; - async; - predicate; - return; - tparams; - sig_loc; - comments = Flow_ast_utils.mk_comments_opt ~leading (); - } + match implicit_declare_conversion with + | Some type_params -> + (* Consume the semicolon if explicit, or handle ASI trailing comments *) + let trailing = + if Peek.token env = T_SEMICOLON then begin + Eat.token env; + if Peek.is_line_terminator env then + Eat.comments_until_next_line env + else + [] + end else + (* Implicit semicolon (ASI): don't consume any token *) + Eat.comments_until_next_line env + in + let annot_loc = + match tparams with + | Some (tparams_loc, _) -> tparams_loc + | None -> fst params + in + let return_annot = + match return with + | Function.ReturnAnnot.Available (_, (ret_loc, t)) -> + Ast.Type.Function.Available (ret_loc, t) + | Function.ReturnAnnot.TypeGuard (_, tg) -> Ast.Type.Function.TypeGuard tg + | Function.ReturnAnnot.Missing _ -> + (* Should not happen - already checked above *) + Ast.Type.Function.Available (Loc.none, Ast.Type.Any None) + in + let fn_type = + Ast.Type.Function + { + Ast.Type.Function.tparams; + params = type_params; + return = return_annot; + comments = None; + effect_; + } + in + let annot = (annot_loc, (annot_loc, fn_type)) in + (* Convert predicate if present *) + let type_predicate = + match predicate with + | Some (loc, { Flow_ast.Type.Predicate.kind; comments = pred_comments }) -> + Some (loc, { Flow_ast.Type.Predicate.kind; comments = pred_comments }) + | None -> None + in + Statement.DeclareFunction + { + Statement.DeclareFunction.id; + annot; + predicate = type_predicate; + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + implicit_declare = true; + } + | None -> + (* Normal function: parse body *) + let simple_params = is_simple_parameter_list params in + let (body, contains_use_strict) = + function_body env ~async ~generator ~expression:false ~simple_params + in + strict_function_post_check env ~contains_use_strict id params; + Statement.FunctionDeclaration + { + Function.id; + params; + body; + generator; + effect_; + async; + predicate; + return; + tparams; + sig_loc; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } ) let variable_declaration_list = @@ -411,7 +650,7 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : let (loc, (decl, err)) = with_loc (fun env -> - let id = Parse.pattern env Parse_error.StrictVarName in + let id = Parse.pattern env ~allow_optional:false Parse_error.StrictVarName in let (init, err) = if Eat.maybe env T_ASSIGN then (Some (Parse.assignment env), None) @@ -443,8 +682,6 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : let declarations token env = let leading = Peek.comments env in Expect.token env token; - if (parse_options env).enums && token = T_CONST && Peek.token env = T_ENUM then - error env Parse_error.EnumInvalidConstPrefix; let (declarations, errs) = variable_declaration_list env in (declarations, leading, errs) @@ -453,13 +690,18 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : let const env = let env = env |> with_no_let true in let (declarations, leading_comments, errs) = declarations T_CONST env in - (* Make sure all consts defined are initialized *) + (* Make sure all consts defined are initialized, unless we're in an ambient context with type annotation *) let errs = List.fold_left (fun errs decl -> match decl with - | (loc, { Statement.VariableDeclaration.Declarator.init = None; _ }) -> - (loc, Parse_error.NoUninitializedConst) :: errs + | (loc, { Statement.VariableDeclaration.Declarator.init = None; id }) -> + (* In ambient context, allow uninitialized const only if there's a type annotation *) + if Parser_env.in_ambient_context env && Flow_ast_utils.pattern_has_type_annotation id + then + errs + else + (loc, Parse_error.NoUninitializedConst) :: errs | _ -> errs) errs declarations @@ -470,9 +712,9 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : let env = env |> with_no_let true in declarations T_LET env - let enum_declaration ?leading = + let enum_declaration ?leading ~const_ = with_loc (fun env -> - let enum = Enum.declaration ?leading env in + let enum = Enum.declaration ?leading ~const_ env in Statement.EnumDeclaration enum ) @@ -521,21 +763,23 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : (name, local, false) | _ -> Eat.token env; - let local = Parse.pattern env Parse_error.StrictParamName in + let local = Parse.pattern env ~allow_optional:true Parse_error.StrictParamName in (name, local, false)) | (_, T_IDENTIFIER { raw = "as"; _ }) -> let name = Statement.ComponentDeclaration.Param.Identifier (identifier_name env) in Expect.identifier env "as"; - (name, Parse.pattern env Parse_error.StrictParamName, false) + (name, Parse.pattern env ~allow_optional:true Parse_error.StrictParamName, false) | (T_LCURLY, _) -> error env Parse_error.InvalidComponentParamName; let fake_name_loc = Peek.loc env in let fallback_ident = (fake_name_loc, { Ast.Identifier.name = ""; comments = None }) in let name = Statement.ComponentDeclaration.Param.Identifier fallback_ident in - let local = Parse.pattern env Parse_error.StrictParamName in + let local = Parse.pattern env ~allow_optional:false Parse_error.StrictParamName in (name, local, false) | (_, _) -> - let id = Parse.identifier_with_type env Parse_error.StrictParamName in + let id = + Parse.identifier_with_type env ~allow_optional:true Parse_error.StrictParamName + in (match id with | (loc, ({ Ast.Pattern.Identifier.name; _ } as id)) -> ( Ast.Statement.ComponentDeclaration.Param.Identifier name, @@ -557,7 +801,7 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : match Peek.token env with | (T_EOF | T_RPAREN | T_ELLIPSIS) as t -> let rest = - rest_param env t + rest_param ~allow_optional:true env t |> Option.map (fun (loc, id, comments) -> if Peek.token env = T_COMMA then Eat.token env; (loc, { Statement.ComponentDeclaration.RestParam.argument = id; comments }) @@ -585,20 +829,16 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : } ) - let component_body env = - function_or_component_body - env - ~async:false - ~generator:false - ~expression:false - ~simple_params:false + let component_body ~async env = + function_or_component_body env ~async ~generator:false ~expression:false ~simple_params:false let component = with_loc (fun env -> + let (async, async_leading) = async env in let (sig_loc, (tparams, id, params, renders, leading)) = with_loc (fun env -> - let leading = Peek.comments env in + let leading = async_leading @ Peek.comments env in Expect.identifier env "component"; let id = id_remove_trailing @@ -624,7 +864,25 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : (tparams, id, params, renders, leading)) env in - let (body, contains_use_strict) = component_body env in + let (body, contains_use_strict, trailing) = + if Peek.token env <> T_LCURLY then begin + let trailing = + (* No body - consume semicolon if present *) + if Peek.token env = T_SEMICOLON then begin + Eat.token env; + if Peek.is_line_terminator env then + Eat.comments_until_next_line env + else + [] + end else + [] + in + (None, false, trailing) + end else begin + let (body, contains_use_strict) = component_body ~async env in + (Some body, contains_use_strict, []) + end + in strict_component_post_check env ~contains_use_strict id params; Statement.ComponentDeclaration { @@ -633,8 +891,9 @@ module Declaration (Parse : Parser_common.PARSER) (Type : Parser_common.TYPE) : body; renders; tparams; + async; sig_loc; - comments = Flow_ast_utils.mk_comments_opt ~leading (); + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); } ) end diff --git a/compiler/flow_parser/parser/enum_common.ml b/compiler/flow_parser/parser/enum_common.ml deleted file mode 100644 index f793c5c0241..00000000000 --- a/compiler/flow_parser/parser/enum_common.ml +++ /dev/null @@ -1,21 +0,0 @@ -(* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - *) - -type explicit_type = - | Boolean - | Number - | String - | Symbol - | BigInt -[@@deriving ord] - -let string_of_explicit_type = function - | Boolean -> "boolean" - | Number -> "number" - | String -> "string" - | Symbol -> "symbol" - | BigInt -> "bigint" diff --git a/compiler/flow_parser/parser/enum_parser.ml b/compiler/flow_parser/parser/enum_parser.ml index e2a9a5db776..710402d4a16 100644 --- a/compiler/flow_parser/parser/enum_parser.ml +++ b/compiler/flow_parser/parser/enum_parser.ml @@ -12,22 +12,16 @@ open Token module Enum (Parse : Parser_common.PARSER) : sig val declaration : - ?leading:Loc.t Comment.t list -> env -> (Loc.t, Loc.t) Statement.EnumDeclaration.t + ?leading:Loc.t Comment.t list -> + const_:bool -> + env -> + (Loc.t, Loc.t) Statement.EnumDeclaration.t end = struct open Flow_ast.Statement.EnumDeclaration - type members = { - boolean_members: (Loc.t BooleanLiteral.t, Loc.t) InitializedMember.t list; - number_members: (Loc.t NumberLiteral.t, Loc.t) InitializedMember.t list; - string_members: (Loc.t StringLiteral.t, Loc.t) InitializedMember.t list; - bigint_members: (Loc.t BigIntLiteral.t, Loc.t) InitializedMember.t list; - defaulted_members: Loc.t DefaultedMember.t list; - } - type acc = { - members: members; - seen_names: SSet.t; - has_unknown_members: bool; + members: Loc.t member list; + has_unknown_members: Loc.t option; internal_comments: Loc.t Comment.t list; } @@ -39,22 +33,7 @@ end = struct | StringInit of Loc.t * Loc.t StringLiteral.t | BigIntInit of Loc.t * Loc.t BigIntLiteral.t - let empty_members = - { - boolean_members = []; - number_members = []; - string_members = []; - bigint_members = []; - defaulted_members = []; - } - - let empty_acc = - { - members = empty_members; - seen_names = SSet.empty; - has_unknown_members = false; - internal_comments = []; - } + let empty_acc = { members = []; has_unknown_members = None; internal_comments = [] } let end_of_member_init env = match Peek.token env with @@ -92,7 +71,9 @@ end = struct | T_MINUS -> Eat.token env; (match Peek.token env with - | T_NUMBER { kind; raw } -> number_init env loc ~neg:true ~leading ~kind ~raw + | T_NUMBER { kind; raw } -> + let loc = Loc.btwn loc (Peek.loc env) in + number_init env loc ~neg:true ~leading ~kind ~raw | _ -> InvalidInit loc) | T_NUMBER { kind; raw } -> number_init env loc ~neg:false ~leading ~kind ~raw | T_STRING (loc, value, raw, octal) -> @@ -143,14 +124,21 @@ end = struct let member_raw = with_loc (fun env -> - let id = identifier_name env in + let id = + match Peek.token env with + | T_STRING (loc, value, raw, octal) -> + if octal then strict_error env Parse_error.StrictOctalLiteral; + Eat.token env; + StringLiteral (loc, { StringLiteral.value; raw; comments = None }) + | _ -> Identifier (identifier_name env) + in let init = match Peek.token env with | T_ASSIGN -> Expect.token env T_ASSIGN; member_init env | T_COLON -> - let (_, { Identifier.name = member_name; _ }) = id in + let member_name = Flow_ast_utils.string_of_enum_member_name id in error env (Parse_error.EnumInvalidInitializerSeparator { member_name }); Expect.token env T_COLON; member_init env @@ -159,96 +147,40 @@ end = struct (id, init) ) - let check_explicit_type_mismatch env ~enum_name ~explicit_type ~member_name literal_type loc = - match explicit_type with - | Some enum_type when enum_type <> literal_type -> - error_at - env - (loc, Parse_error.EnumInvalidMemberInitializer { enum_name; explicit_type; member_name }) - | _ -> () - - let is_a_to_z c = c >= 'a' && c <= 'z' - let enum_member ~enum_name ~explicit_type acc env = - let { members; seen_names; _ } = acc in let (member_loc, (id, init)) = member_raw env in - let (id_loc, { Identifier.name = member_name; _ }) = id in + let member_name = Flow_ast_utils.string_of_enum_member_name id in (* if we parsed an empty name, something has gone wrong and we should abort analysis *) if member_name = "" then acc - else ( - if is_a_to_z @@ member_name.[0] then - error_at env (id_loc, Parse_error.EnumInvalidMemberName { enum_name; member_name }); - if SSet.mem member_name seen_names then - error_at env (id_loc, Parse_error.EnumDuplicateMemberName { enum_name; member_name }); - let acc = { acc with seen_names = SSet.add member_name seen_names } in - let check_explicit_type_mismatch = - check_explicit_type_mismatch env ~enum_name ~explicit_type ~member_name - in + else match init with | BooleanInit (loc, value) -> - check_explicit_type_mismatch Enum_common.Boolean loc; - let member = (member_loc, { InitializedMember.id; init = (loc, value) }) in - { acc with members = { members with boolean_members = member :: members.boolean_members } } + let member = BooleanMember (member_loc, { InitializedMember.id; init = (loc, value) }) in + { acc with members = member :: acc.members } | NumberInit (loc, value) -> - check_explicit_type_mismatch Enum_common.Number loc; - let member = (member_loc, { InitializedMember.id; init = (loc, value) }) in - { acc with members = { members with number_members = member :: members.number_members } } + let member = NumberMember (member_loc, { InitializedMember.id; init = (loc, value) }) in + { acc with members = member :: acc.members } | StringInit (loc, value) -> - check_explicit_type_mismatch Enum_common.String loc; - let member = (member_loc, { InitializedMember.id; init = (loc, value) }) in - { acc with members = { members with string_members = member :: members.string_members } } + let member = StringMember (member_loc, { InitializedMember.id; init = (loc, value) }) in + { acc with members = member :: acc.members } | BigIntInit (loc, value) -> - check_explicit_type_mismatch Enum_common.BigInt loc; - let member = (member_loc, { InitializedMember.id; init = (loc, value) }) in - { acc with members = { members with bigint_members = member :: members.bigint_members } } + let member = BigIntMember (member_loc, { InitializedMember.id; init = (loc, value) }) in + { acc with members = member :: acc.members } | InvalidInit loc -> error_at env (loc, Parse_error.EnumInvalidMemberInitializer { enum_name; explicit_type; member_name }); acc - | NoInit -> begin - match explicit_type with - | Some Enum_common.Boolean -> - error_at - env - (member_loc, Parse_error.EnumBooleanMemberNotInitialized { enum_name; member_name }); - acc - | Some Enum_common.Number -> - error_at - env - (member_loc, Parse_error.EnumNumberMemberNotInitialized { enum_name; member_name }); - acc - | Some Enum_common.BigInt -> - error_at - env - (member_loc, Parse_error.EnumBigIntMemberNotInitialized { enum_name; member_name }); - acc - | Some Enum_common.String - | Some Enum_common.Symbol - | None -> - let member = (member_loc, { DefaultedMember.id }) in - { - acc with - members = { members with defaulted_members = member :: members.defaulted_members }; - } - end - ) + | NoInit -> + let member = DefaultedMember (member_loc, { DefaultedMember.id }) in + { acc with members = member :: acc.members } let rec enum_members ~enum_name ~explicit_type acc env = match Peek.token env with | T_RCURLY | T_EOF -> - ( { - boolean_members = List.rev acc.members.boolean_members; - number_members = List.rev acc.members.number_members; - string_members = List.rev acc.members.string_members; - bigint_members = List.rev acc.members.bigint_members; - defaulted_members = List.rev acc.members.defaulted_members; - }, - acc.has_unknown_members, - acc.internal_comments - ) + (List.rev acc.members, acc.has_unknown_members, acc.internal_comments) | T_ELLIPSIS -> let loc = Peek.loc env in (* Internal comments may appear before the ellipsis *) @@ -272,7 +204,7 @@ end = struct enum_members ~enum_name ~explicit_type - { acc with has_unknown_members = true; internal_comments } + { acc with has_unknown_members = Some loc; internal_comments } env | _ -> let acc = enum_member ~enum_name ~explicit_type acc env in @@ -286,56 +218,17 @@ end = struct | _ -> Expect.token env T_COMMA); enum_members ~enum_name ~explicit_type acc env - let string_body - ~env ~enum_name ~is_explicit ~has_unknown_members string_members defaulted_members comments = - let initialized_len = List.length string_members in - let defaulted_len = List.length defaulted_members in - let defaulted_body () = - StringBody - { - StringBody.members = StringBody.Defaulted defaulted_members; - explicit_type = is_explicit; - has_unknown_members; - comments; - } - in - let initialized_body () = - StringBody - { - StringBody.members = StringBody.Initialized string_members; - explicit_type = is_explicit; - has_unknown_members; - comments; - } - in - match (initialized_len, defaulted_len) with - | (0, 0) - | (0, _) -> - defaulted_body () - | (_, 0) -> initialized_body () - | _ when defaulted_len > initialized_len -> - List.iter - (fun (loc, _) -> - error_at env (loc, Parse_error.EnumStringMemberInconsistentlyInitialized { enum_name })) - string_members; - defaulted_body () - | _ -> - List.iter - (fun (loc, _) -> - error_at env (loc, Parse_error.EnumStringMemberInconsistentlyInitialized { enum_name })) - defaulted_members; - initialized_body () - let parse_explicit_type ~enum_name env = if Eat.maybe env T_OF then ( Eat.push_lex_mode env Lex_mode.TYPE; + let type_loc = Peek.loc env in let result = match Peek.token env with - | T_BOOLEAN_TYPE BOOLEAN -> Some Enum_common.Boolean - | T_NUMBER_TYPE -> Some Enum_common.Number - | T_STRING_TYPE -> Some Enum_common.String - | T_SYMBOL_TYPE -> Some Enum_common.Symbol - | T_BIGINT_TYPE -> Some Enum_common.BigInt + | T_BOOLEAN_TYPE BOOLEAN -> Some (type_loc, Boolean) + | T_NUMBER_TYPE -> Some (type_loc, Number) + | T_STRING_TYPE -> Some (type_loc, String) + | T_SYMBOL_TYPE -> Some (type_loc, Symbol) + | T_BIGINT_TYPE -> Some (type_loc, BigInt) | T_IDENTIFIER { value; _ } -> let supplied_type = Some value in error env (Parse_error.EnumInvalidExplicitType { enum_name; supplied_type }); @@ -350,7 +243,7 @@ end = struct ) else None - let enum_body ~enum_name ~name_loc = + let enum_body ~enum_name = with_loc (fun env -> let explicit_type = parse_explicit_type ~enum_name env in let leading = @@ -360,8 +253,9 @@ end = struct [] in Expect.token env T_LCURLY; + let explicit_type_t = Option.map snd explicit_type in let (members, has_unknown_members, internal) = - enum_members ~enum_name ~explicit_type empty_acc env + enum_members ~enum_name ~explicit_type:explicit_type_t empty_acc env in let internal = internal @ Peek.comments env in Expect.token env T_RCURLY; @@ -376,127 +270,15 @@ end = struct let comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal () in - let body = - match explicit_type with - | Some Enum_common.Boolean -> - BooleanBody - { - BooleanBody.members = members.boolean_members; - explicit_type = true; - has_unknown_members; - comments; - } - | Some Enum_common.Number -> - NumberBody - { - NumberBody.members = members.number_members; - explicit_type = true; - has_unknown_members; - comments; - } - | Some Enum_common.String -> - string_body - ~env - ~enum_name - ~is_explicit:true - ~has_unknown_members - members.string_members - members.defaulted_members - comments - | Some Enum_common.Symbol -> - SymbolBody - { SymbolBody.members = members.defaulted_members; has_unknown_members; comments } - | Some Enum_common.BigInt -> - BigIntBody - { - BigIntBody.members = members.bigint_members; - explicit_type = true; - has_unknown_members; - comments; - } - | None -> - let bools_len = List.length members.boolean_members in - let nums_len = List.length members.number_members in - let bigints_len = List.length members.bigint_members in - let strs_len = List.length members.string_members in - let defaulted_len = List.length members.defaulted_members in - let empty () = - StringBody - { - StringBody.members = StringBody.Defaulted []; - explicit_type = false; - has_unknown_members; - comments; - } - in - begin - match (bools_len, nums_len, bigints_len, strs_len, defaulted_len) with - | (0, 0, 0, 0, 0) -> empty () - | (0, 0, 0, _, _) -> - string_body - ~env - ~enum_name - ~is_explicit:false - ~has_unknown_members - members.string_members - members.defaulted_members - comments - | (_, 0, 0, 0, _) when bools_len >= defaulted_len -> - List.iter - (fun (loc, { DefaultedMember.id = (_, { Identifier.name = member_name; _ }) }) -> - error_at - env - (loc, Parse_error.EnumBooleanMemberNotInitialized { enum_name; member_name })) - members.defaulted_members; - BooleanBody - { - BooleanBody.members = members.boolean_members; - explicit_type = false; - has_unknown_members; - comments; - } - | (0, _, 0, 0, _) when nums_len >= defaulted_len -> - List.iter - (fun (loc, { DefaultedMember.id = (_, { Identifier.name = member_name; _ }) }) -> - error_at - env - (loc, Parse_error.EnumNumberMemberNotInitialized { enum_name; member_name })) - members.defaulted_members; - NumberBody - { - NumberBody.members = members.number_members; - explicit_type = false; - has_unknown_members; - comments; - } - | (0, 0, _, 0, _) when bigints_len >= defaulted_len -> - List.iter - (fun (loc, { DefaultedMember.id = (_, { Identifier.name = member_name; _ }) }) -> - error_at - env - (loc, Parse_error.EnumNumberMemberNotInitialized { enum_name; member_name })) - members.defaulted_members; - BigIntBody - { - BigIntBody.members = members.bigint_members; - explicit_type = false; - has_unknown_members; - comments; - } - | _ -> - error_at env (name_loc, Parse_error.EnumInconsistentMemberValues { enum_name }); - empty () - end - in - body + { Body.members; explicit_type; has_unknown_members; comments } ) - let declaration ?(leading = []) env = + let declaration ?(leading = []) ~const_ env = let leading = leading @ Peek.comments env in Expect.token env T_ENUM; let id = Parse.identifier env in - let (name_loc, { Identifier.name = enum_name; _ }) = id in - let body = enum_body ~enum_name ~name_loc env in + let (_, { Identifier.name = enum_name; _ }) = id in + let body = enum_body ~enum_name env in let comments = Flow_ast_utils.mk_comments_opt ~leading () in - { Statement.EnumDeclaration.id; body; comments } + { Statement.EnumDeclaration.id; body; const_; comments } end diff --git a/compiler/flow_parser/parser/estree_translator.ml b/compiler/flow_parser/parser/estree_translator.ml index ee435f5341b..6cd5e0c4ae4 100644 --- a/compiler/flow_parser/parser/estree_translator.ml +++ b/compiler/flow_parser/parser/estree_translator.ml @@ -9,6 +9,8 @@ module Ast = Flow_ast module type Config = sig val include_locs : bool + + val include_filename : bool end module Translate (Impl : Translator_intf.S) (Config : Config) : sig @@ -45,13 +47,12 @@ with type t = Impl.t = struct let loc location = let source = - match Loc.source location with - | Some (File_key.LibFile src) - | Some (File_key.SourceFile src) - | Some (File_key.JsonFile src) - | Some (File_key.ResourceFile src) -> - string src - | None -> null + if Config.include_filename then + match Loc.source location with + | Some file_key -> string (File_key.suffix file_key) + | None -> null + else + null in obj [ @@ -84,6 +85,15 @@ with type t = Impl.t = struct ] ) in + (* Optional-chain rewrite: mirror upstream Hermes' mapChainExpression by + emitting `OptionalMember`/`OptionalCall` AST nodes as + `MemberExpression`/`CallExpression` with `optional` flags, wrapped in a + single `ChainExpression` at the chain root. Plain `Member`/`Call` inside + an optional chain mark a parenthesis boundary: emit with `optional: + false` and reset the chain state for their children so an inner optional + access starts a new chain. The chain state is threaded explicitly as + the [in_optional_chain] argument of [expression] (and propagated through + [call_node_properties] / [member_node_properties]). *) let rec node _type location ?comments props = let locs = if Config.include_locs then @@ -178,6 +188,81 @@ with type t = Impl.t = struct "SwitchStatement" loc [("discriminant", expression discriminant); ("cases", array_of_list case cases)] + | ( loc, + RecordDeclaration + { RecordDeclaration.id; tparams; implements; body; comments; invalid_syntax = _ } + ) -> + let record_property + ( loc, + { RecordDeclaration.Property.key; annot; default_value; comments; invalid_syntax = _ } + ) = + let (key, computed, comments) = property_key ~comments key in + if computed then failwith "Records cannot have computed keys"; + node + ?comments + "RecordDeclarationProperty" + loc + [ + ("key", key); + ("typeAnnotation", type_annotation annot); + ("defaultValue", option expression default_value); + ] + in + let record_static_property + ( loc, + { RecordDeclaration.StaticProperty.key; annot; value; comments; invalid_syntax = _ } + ) = + let (key, computed, comments) = property_key ~comments key in + if computed then failwith "Records cannot have computed keys"; + node + ?comments + "RecordDeclarationStaticProperty" + loc + [("key", key); ("typeAnnotation", type_annotation annot); ("value", expression value)] + in + + let record_element element = + let open RecordDeclaration.Body in + match element with + | Property prop -> record_property prop + | StaticProperty prop -> record_static_property prop + | Method meth -> class_method meth + in + let record_body (loc, { RecordDeclaration.Body.body; comments }) = + node + ?comments + "RecordDeclarationBody" + loc + [("elements", array_of_list record_element body)] + in + let record_implements (loc, { Class.Implements.Interface.id; targs }) = + let id = + match id with + | Type.Generic.Identifier.Unqualified id -> identifier id + | Type.Generic.Identifier.Qualified q -> generic_type_qualified_identifier q + | Type.Generic.Identifier.ImportTypeAnnot it -> import_type it + in + node + "RecordDeclarationImplements" + loc + [("id", id); ("typeArguments", option type_args targs)] + in + let implements = + match implements with + | Some (_, { Class.Implements.interfaces; comments = _ }) -> + array_of_list record_implements interfaces + | None -> array [] + in + node + ?comments + "RecordDeclaration" + loc + [ + ("id", identifier id); + ("typeParameters", option type_parameter_declaration tparams); + ("implements", implements); + ("body", record_body body); + ] | (loc, Return { Return.argument; comments; return_out = _ }) -> node ?comments "ReturnStatement" loc [("argument", option expression argument)] | (loc, Throw { Throw.argument; comments }) -> @@ -265,20 +350,7 @@ with type t = Impl.t = struct | DeclareModule.Identifier id -> identifier id in node ?comments "DeclareModule" loc [("id", id); ("body", block body)] - | (loc, DeclareNamespace { DeclareNamespace.id; body; comments }) -> - let (id, global) = - match id with - | DeclareNamespace.Local id -> (identifier id, false) - | DeclareNamespace.Global id -> (identifier id, true) - in - let props = [("id", id); ("body", block body)] in - let props = - if global then - ("global", bool global) :: props - else - props - in - node ?comments "DeclareNamespace" loc props + | (loc, DeclareNamespace ns) -> declare_namespace (loc, ns) | ( loc, DeclareExportDeclaration { DeclareExportDeclaration.specifiers; declaration; default; source; comments } @@ -302,6 +374,7 @@ with type t = Impl.t = struct | Some (DeclareExportDeclaration.NamedOpaqueType t) -> opaque_type ~declare:true t | Some (DeclareExportDeclaration.Interface i) -> interface_declaration i | Some (DeclareExportDeclaration.Enum enum) -> declare_enum enum + | Some (DeclareExportDeclaration.Namespace n) -> declare_namespace n | None -> null in node @@ -370,15 +443,24 @@ with type t = Impl.t = struct ("declaration", declaration); ("exportKind", string (string_of_export_kind Statement.ExportValue)); ] + | (loc, ExportAssignment { ExportAssignment.rhs; comments }) -> + (match rhs with + | ExportAssignment.Expression expr -> + node ?comments "ExportAssignment" loc [("expression", expression expr)] + | ExportAssignment.DeclareFunction (fn_loc, decl) -> + node ?comments "ExportAssignment" loc [("expression", declare_function (fn_loc, decl))]) + | (loc, NamespaceExportDeclaration { NamespaceExportDeclaration.id; comments }) -> + node ?comments "NamespaceExportDeclaration" loc [("id", identifier id)] | ( loc, - ImportDeclaration { ImportDeclaration.specifiers; default; import_kind; source; comments } + ImportDeclaration + { ImportDeclaration.specifiers; default; import_kind; source; attributes; comments } ) -> let specifiers = match specifiers with | Some (ImportDeclaration.ImportNamedSpecifiers specifiers) -> List.map - (fun { ImportDeclaration.local; remote; remote_name_def_loc = _; kind } -> - import_named_specifier local remote kind) + (fun { ImportDeclaration.local; remote; remote_name_def_loc = _; kind; kind_loc } -> + import_named_specifier local remote kind kind_loc) specifiers | Some (ImportDeclaration.ImportNamespaceSpecifier id) -> [import_namespace_specifier id] | None -> [] @@ -394,26 +476,79 @@ with type t = Impl.t = struct | ImportDeclaration.ImportTypeof -> "typeof" | ImportDeclaration.ImportValue -> "value" in + let attributes_json = + match attributes with + | None -> [] + | Some (_, attrs) -> [("attributes", array (List.map import_attribute attrs))] + in node ?comments "ImportDeclaration" loc + ([ + ("specifiers", array specifiers); + ("source", string_literal source); + ("importKind", string import_kind); + ] + @ attributes_json + ) + | ( loc, + ImportEqualsDeclaration + { + ImportEqualsDeclaration.id = ident; + module_reference; + import_kind; + is_export; + comments; + } + ) -> + let module_reference_json = + let open ImportEqualsDeclaration in + match module_reference with + | ExternalModuleReference (annot_loc, lit) -> + node + "ExternalModuleReference" + annot_loc + [("expression", string_literal (annot_loc, lit))] + | Identifier git -> + let open Type.Generic.Identifier in + let generic_id = function + | Unqualified id -> identifier id + | Qualified q -> generic_type_qualified_identifier q + | ImportTypeAnnot it -> import_type it + in + generic_id git + in + let import_kind_str = + match import_kind with + | ImportDeclaration.ImportType -> "type" + | ImportDeclaration.ImportTypeof -> "typeof" + | ImportDeclaration.ImportValue -> "value" + in + node + ?comments + "ImportEqualsDeclaration" + loc [ - ("specifiers", array specifiers); - ("source", string_literal source); - ("importKind", string import_kind); + ("id", identifier ident); + ("moduleReference", module_reference_json); + ("importKind", string import_kind_str); + ("isExport", bool is_export); ] - and expression = + and expression ?(in_optional_chain = false) expr = let open Expression in - function + match expr with | (loc, This { This.comments }) -> node ?comments "ThisExpression" loc [] | (loc, Super { Super.comments }) -> node ?comments "Super" loc [] - | (loc, Array { Array.elements; comments }) -> + | (loc, Array { Array.elements; trailing_comma; comments }) -> node ?comments:(format_internal_comments comments) "ArrayExpression" loc - [("elements", array_of_list array_element elements)] + [ + ("elements", array_of_list array_element elements); + ("trailingComma", bool trailing_comma); + ] | (loc, Object { Object.properties; comments }) -> node ?comments:(format_internal_comments comments) @@ -473,6 +608,12 @@ with type t = Impl.t = struct Unary.( (match operator with | Await -> node ?comments "AwaitExpression" loc [("argument", expression argument)] + | Nonnull -> + node + ?comments + "NonNullExpression" + loc + [("argument", expression argument); ("chain", bool false)] | _ -> let operator = match operator with @@ -483,7 +624,9 @@ with type t = Impl.t = struct | Typeof -> "typeof" | Void -> "void" | Delete -> "delete" - | Await -> failwith "matched above" + | Nonnull + | Await -> + failwith "matched above" in node ?comments @@ -575,7 +718,7 @@ with type t = Impl.t = struct let (arguments, comments) = match arguments with | Some ((_, { ArgList.comments = args_comments; _ }) as arguments) -> - ( arg_list arguments, + ( arg_list ~in_optional_chain:false arguments, Flow_ast_utils.merge_comments ~inner:(format_internal_comments args_comments) ~outer:comments @@ -600,7 +743,15 @@ with type t = Impl.t = struct ~inner:(format_internal_comments args_comments) ~outer:comments in - node ?comments "CallExpression" loc (call_node_properties call) + (* Plain Call inside an optional chain marks a parenthesis boundary: + reset chain state for children so an inner optional access starts a + new chain. Otherwise, normal call. Either way emit CallExpression + with optional=false. *) + node + ?comments + "CallExpression" + loc + (call_node_properties ~in_optional_chain:false call @ [("optional", bool false)]) | ( loc, OptionalCall { @@ -616,22 +767,85 @@ with type t = Impl.t = struct ~inner:(format_internal_comments args_comments) ~outer:comments in + (match optional with + | OptionalCall.AssertNonnull -> + (* AssertNonnull (`expr!()`): emit CallExpression with + optional=false and the callee wrapped in NonNullExpression + (chain: true). Not part of the optional-chain rewrite, so + no ChainExpression wrap. *) + let wrap_callee callee = + node "NonNullExpression" loc [("argument", callee); ("chain", bool true)] + in + node + ?comments + "CallExpression" + loc + (call_node_properties ~in_optional_chain:false ~wrap_callee call + @ [("optional", bool false)] + ) + | OptionalCall.Optional + | OptionalCall.NonOptional -> + let optional_value = + match optional with + | OptionalCall.Optional -> bool true + | _ -> bool false + in + let emit_inner () = + node + ?comments + "CallExpression" + loc + (call_node_properties ~in_optional_chain:true call @ [("optional", optional_value)]) + in + if in_optional_chain then + emit_inner () + else + node "ChainExpression" loc [("expression", emit_inner ())]) + | (loc, Member ({ Member.comments; _ } as member)) -> + (* Plain Member inside an optional chain marks a parenthesis boundary; + reset chain state for children so an inner optional access starts a + new chain. Either way emit MemberExpression with optional=false. *) node ?comments - "OptionalCallExpression" + "MemberExpression" loc - (call_node_properties call @ [("optional", bool optional)]) - | (loc, Member ({ Member.comments; _ } as member)) -> - node ?comments "MemberExpression" loc (member_node_properties member) + (member_node_properties ~in_optional_chain:false member @ [("optional", bool false)]) | ( loc, OptionalMember { OptionalMember.member = { Member.comments; _ } as member; optional; filtered_out = _ } ) -> - node - ?comments - "OptionalMemberExpression" - loc - (member_node_properties member @ [("optional", bool optional)]) + (match optional with + | OptionalMember.AssertNonnull -> + let wrap_receiver receiver = + node "NonNullExpression" loc [("argument", receiver); ("chain", bool true)] + in + node + ?comments + "MemberExpression" + loc + (member_node_properties ~in_optional_chain:false ~wrap_receiver member + @ [("optional", bool false)] + ) + | OptionalMember.Optional + | OptionalMember.NonOptional -> + let optional_value = + match optional with + | OptionalMember.Optional -> bool true + | _ -> bool false + in + let emit_inner () = + node + ?comments + "MemberExpression" + loc + (member_node_properties ~in_optional_chain:true member + @ [("optional", optional_value)] + ) + in + if in_optional_chain then + emit_inner () + else + node "ChainExpression" loc [("expression", emit_inner ())]) | (loc, Yield { Yield.argument; delegate; comments; result_out = _ }) -> node ?comments @@ -663,12 +877,50 @@ with type t = Impl.t = struct "MetaProperty" loc [("meta", identifier meta); ("property", identifier property)] - | (loc, Import { Import.argument; comments }) -> - node ?comments "ImportExpression" loc [("source", expression argument)] + | (loc, Record { Record.constructor; targs; properties; comments }) -> + let properties = + let (props_loc, { Expression.Object.properties = props; comments = props_comments }) = + properties + in + node + ?comments:(format_internal_comments props_comments) + "RecordExpressionProperties" + props_loc + [("properties", array_of_list object_property props)] + in + node + ?comments + "RecordExpression" + loc + [ + ("recordConstructor", expression constructor); + ("typeArguments", option call_type_args targs); + ("properties", properties); + ] + | (loc, Import { Import.argument; options; comments }) -> + let fields = + ("source", expression argument) + :: + (match options with + | Some opts -> [("options", expression opts)] + | None -> []) + in + node ?comments "ImportExpression" loc fields and match_expression_case case = match_case "MatchExpressionCase" ~on_case_body:expression case and match_case : 'B. string -> on_case_body:('B -> Impl.t) -> (Loc.t, Loc.t, 'B) Match.Case.t -> Impl.t = - fun kind ~on_case_body (loc, { Match.Case.pattern; body; guard; comments }) -> + fun kind + ~on_case_body + ( loc, + { + Match.Case.pattern; + body; + guard; + comments; + invalid_syntax = _; + case_match_root_loc = _; + } + ) -> node ?comments kind @@ -683,7 +935,8 @@ with type t = Impl.t = struct let open MatchPattern in let literal x = node "MatchLiteralPattern" loc [("literal", x)] in match pattern with - | WildcardPattern comments -> node ?comments "MatchWildcardPattern" loc [] + | WildcardPattern { WildcardPattern.comments; _ } -> + node ?comments "MatchWildcardPattern" loc [] | StringPattern lit -> literal (string_literal (loc, lit)) | BooleanPattern lit -> literal (boolean_literal (loc, lit)) | NullPattern comments -> literal (null_literal (loc, comments)) @@ -707,78 +960,22 @@ with type t = Impl.t = struct [("operator", string operator); ("argument", argument)] | BindingPattern binding -> match_binding_pattern (loc, binding) | IdentifierPattern id -> match_identifier_pattern id - | MemberPattern mem -> - let rec member (loc, { MemberPattern.base; property; comments }) = - let member_base = function - | MemberPattern.BaseIdentifier id -> match_identifier_pattern id - | MemberPattern.BaseMember mem -> member mem - in - let member_property = function - | MemberPattern.PropertyString lit -> string_literal lit - | MemberPattern.PropertyNumber lit -> number_literal lit - | MemberPattern.PropertyBigInt lit -> bigint_literal lit - | MemberPattern.PropertyIdentifier id -> identifier id - in - node - ?comments - "MatchMemberPattern" - loc - [("base", member_base base); ("property", member_property property)] - in - member mem - | ObjectPattern { ObjectPattern.properties; rest; comments } -> - let property_key key = - match key with - | ObjectPattern.Property.StringLiteral lit -> string_literal lit - | ObjectPattern.Property.NumberLiteral lit -> number_literal lit - | ObjectPattern.Property.BigIntLiteral lit -> bigint_literal lit - | ObjectPattern.Property.Identifier id -> identifier id + | MemberPattern member -> match_member_pattern member + | ObjectPattern obj -> match_object_pattern "MatchObjectPattern" (loc, obj) + | ArrayPattern arr -> match_array_pattern (loc, arr) + | InstancePattern { InstancePattern.constructor; properties; comments } -> + let constructor = + match constructor with + | InstancePattern.IdentifierConstructor id -> match_identifier_pattern id + | InstancePattern.MemberConstructor member -> match_member_pattern member in - let property = function - | ( loc, - ObjectPattern.Property.Valid - { ObjectPattern.Property.key; pattern; shorthand; comments } - ) -> - node - ?comments - "MatchObjectPatternProperty" - loc - [ - ("key", property_key key); - ("pattern", match_pattern pattern); - ("shorthand", bool shorthand); - ] - | (loc, ObjectPattern.Property.InvalidShorthand id) -> - node - "MatchObjectPatternProperty" - loc - [ - ("key", identifier id); - ("pattern", match_identifier_pattern id); - ("shorthand", bool true); - ] - in - - node - ?comments:(format_internal_comments comments) - "MatchObjectPattern" - loc - [ - ("properties", array_of_list property properties); - ("rest", option match_rest_pattern rest); - ] - | ArrayPattern { ArrayPattern.elements; rest; comments } -> node - ?comments:(format_internal_comments comments) - "MatchArrayPattern" + ?comments + "MatchInstancePattern" loc [ - ( "elements", - array_of_list - (fun { ArrayPattern.Element.pattern; _ } -> match_pattern pattern) - elements - ); - ("rest", option match_rest_pattern rest); + ("targetConstructor", constructor); + ("properties", match_object_pattern "MatchInstanceObjectPattern" properties); ] | OrPattern { OrPattern.patterns; comments } -> node ?comments "MatchOrPattern" loc [("patterns", array_of_list match_pattern patterns)] @@ -792,9 +989,73 @@ with type t = Impl.t = struct and match_identifier_pattern id = let (loc, _) = id in node "MatchIdentifierPattern" loc [("id", identifier id)] + and match_member_pattern (loc, { MatchPattern.MemberPattern.base; property; comments }) = + let open MatchPattern.MemberPattern in + let member_base = function + | BaseIdentifier id -> match_identifier_pattern id + | BaseMember member -> match_member_pattern member + in + let member_property = function + | PropertyString lit -> string_literal lit + | PropertyNumber lit -> number_literal lit + | PropertyBigInt lit -> bigint_literal lit + | PropertyIdentifier id -> identifier id + in + node + ?comments + "MatchMemberPattern" + loc + [("base", member_base base); ("property", member_property property)] and match_binding_pattern (loc, { MatchPattern.BindingPattern.kind; id; comments }) = let kind = Flow_ast_utils.string_of_variable_kind kind in node ?comments "MatchBindingPattern" loc [("id", identifier id); ("kind", string kind)] + and match_array_pattern (loc, { MatchPattern.ArrayPattern.elements; rest; comments }) = + let open MatchPattern.ArrayPattern in + node + ?comments:(format_internal_comments comments) + "MatchArrayPattern" + loc + [ + ("elements", array_of_list (fun { Element.pattern; _ } -> match_pattern pattern) elements); + ("rest", option match_rest_pattern rest); + ] + and match_object_pattern kind (loc, { MatchPattern.ObjectPattern.properties; rest; comments }) = + let open MatchPattern.ObjectPattern in + let property_key key = + match key with + | Property.StringLiteral lit -> string_literal lit + | Property.NumberLiteral lit -> number_literal lit + | Property.BigIntLiteral lit -> bigint_literal lit + | Property.Identifier id -> identifier id + in + let property = function + | (loc, Property.Valid { Property.key; pattern; shorthand; comments }) -> + node + ?comments + "MatchObjectPatternProperty" + loc + [ + ("key", property_key key); + ("pattern", match_pattern pattern); + ("shorthand", bool shorthand); + ] + | (loc, Property.InvalidShorthand id) -> + node + "MatchObjectPatternProperty" + loc + [ + ("key", identifier id); + ("pattern", match_identifier_pattern id); + ("shorthand", bool true); + ] + in + node + ?comments:(format_internal_comments comments) + kind + loc + [ + ("properties", array_of_list property properties); ("rest", option match_rest_pattern rest); + ] and match_rest_pattern (loc, { MatchPattern.RestPattern.argument; comments }) = node ?comments "MatchRestPattern" loc [("argument", option match_binding_pattern argument)] and function_declaration @@ -825,7 +1086,7 @@ with type t = Impl.t = struct in let (node_name, nonhook_attrs) = if effect_ = Function.Hook then - ("HookDeclaration", []) + ("HookDeclaration", [("async", bool async)]) else ( "FunctionDeclaration", [ @@ -916,11 +1177,11 @@ with type t = Impl.t = struct ("typeAnnotation", hint type_annotation annot); ("optional", bool optional); ] - and arg_list (_loc, { Expression.ArgList.arguments; comments = _ }) = + and arg_list ~in_optional_chain (_loc, { Expression.ArgList.arguments; comments = _ }) = (* ESTree does not have a unique node for argument lists, so there's nowhere to include the loc. *) - array_of_list expression_or_spread arguments - and case (loc, { Statement.Switch.Case.test; consequent; comments }) = + array_of_list (expression_or_spread ~in_optional_chain) arguments + and case (loc, { Statement.Switch.Case.test; case_test_loc = _; consequent; comments }) = node ?comments "SwitchCase" @@ -934,48 +1195,90 @@ with type t = Impl.t = struct "BlockStatement" loc [("body", statement_list body)] - and declare_variable (loc, { Statement.DeclareVariable.id; annot; kind; comments }) = - let id_loc = Loc.btwn (fst id) (fst annot) in - let kind = Flow_ast_utils.string_of_variable_kind kind in + and declare_variable (loc, { Statement.DeclareVariable.declarations; kind; comments }) = + let kind_str = Flow_ast_utils.string_of_variable_kind kind in node ?comments "DeclareVariable" loc [ - ( "id", - pattern_identifier - id_loc - { Pattern.Identifier.name = id; annot = Ast.Type.Available annot; optional = false } - ); - ("kind", string kind); + ("declarations", array_of_list variable_declarator declarations); ("kind", string kind_str); ] and declare_function - (loc, { Statement.DeclareFunction.id; annot; predicate = predicate_; comments }) = - let id_loc = Loc.btwn (fst id) (fst annot) in + ( loc, + { + Statement.DeclareFunction.id; + annot; + predicate = predicate_; + comments; + implicit_declare; + } + ) = + let id_loc = + match id with + | Some id -> Loc.btwn (fst id) (fst annot) + | None -> fst annot + in let (name, predicate) = match annot with | (_, (_, Type.Function { Type.Function.effect_ = Function.Hook; _ })) -> ("DeclareHook", []) | _ -> ("DeclareFunction", [("predicate", option predicate predicate_)]) in + let annot_field = + (* Only output if we aren't putting the annot on the `id` *) + if Option.is_none id then + [("typeAnnotation", type_annotation annot)] + else + [] + in node ?comments name loc ([ ( "id", - pattern_identifier - id_loc - { Pattern.Identifier.name = id; annot = Ast.Type.Available annot; optional = false } + match id with + | Some id -> + pattern_identifier + id_loc + { + Pattern.Identifier.name = id; + annot = Ast.Type.Available annot; + optional = false; + } + | None -> null ); + ("implicitDeclare", bool implicit_declare); ] + @ annot_field @ predicate ) and declare_class - (loc, { Statement.DeclareClass.id; tparams; body; extends; implements; mixins; comments }) = + ( loc, + { + Statement.DeclareClass.id; + tparams; + body; + extends; + implements; + mixins; + abstract; + comments; + } + ) = (* TODO: extends shouldn't return an array *) + let rec declare_class_extends_to_estree (loc, ext) = + match ext with + | Statement.DeclareClass.ExtendsIdent generic -> interface_extends (loc, generic) + | Statement.DeclareClass.ExtendsCall { callee; arg } -> + node + "DeclareClassExtendsCall" + loc + [("callee", generic_type callee); ("argument", declare_class_extends_to_estree arg)] + in let extends = match extends with - | Some extends -> array [interface_extends extends] + | Some ext -> array [declare_class_extends_to_estree ext] | None -> array [] in let implements = @@ -988,19 +1291,26 @@ with type t = Impl.t = struct ?comments "DeclareClass" loc - [ - ("id", identifier id); - ("typeParameters", option type_parameter_declaration tparams); - ("body", object_type ~include_inexact:false body); - ("extends", extends); - ("implements", implements); - ("mixins", array_of_list interface_extends mixins); - ] + ([ + ("id", identifier id); + ("typeParameters", option type_parameter_declaration tparams); + ("body", object_type ~include_inexact:false body); + ("extends", extends); + ("implements", implements); + ("mixins", array_of_list interface_extends mixins); + ] + @ + if abstract then + [("abstract", bool abstract)] + else + [] + ) and declare_component (loc, component) = let { Statement.DeclareComponent.id; tparams; - params = (_, { Type.Component.Params.comments = params_comments; _ }) as params; + params = + (_, { Statement.ComponentDeclaration.Params.comments = params_comments; _ }) as params; renders; comments = component_comments; } = @@ -1011,16 +1321,13 @@ with type t = Impl.t = struct ~outer:component_comments ~inner:(format_internal_comments params_comments) in - let (_, { Type.Component.Params.params = param_list; rest; comments = _ }) = params in node ?comments "DeclareComponent" loc [ ("id", identifier id); - ("params", component_type_params param_list); - ("rest", option component_type_rest_param rest); - ("params", component_type_params param_list); + ("params", component_params params); ("rendersType", renders_annotation renders); ("typeParameters", option type_parameter_declaration tparams); ] @@ -1081,8 +1388,15 @@ with type t = Impl.t = struct "ComponentTypeParameter" loc [("name", name'); ("typeAnnotation", _type annot); ("optional", bool optional)] - and declare_enum (loc, { Statement.EnumDeclaration.id; body; comments }) = - node ?comments "DeclareEnum" loc [("id", identifier id); ("body", enum_body body)] + and declare_enum (loc, { Statement.EnumDeclaration.id; body; const_; comments }) = + let props = [("id", identifier id); ("body", enum_body body)] in + let props = + if const_ then + ("const", bool true) :: props + else + props + in + node ?comments "DeclareEnum" loc props and declare_interface (loc, { Statement.Interface.id; tparams; body; extends; comments }) = node ?comments @@ -1094,6 +1408,33 @@ with type t = Impl.t = struct ("body", object_type ~include_inexact:false body); ("extends", array_of_list interface_extends extends); ] + and declare_namespace + (loc, { Statement.DeclareNamespace.id; body; comments; implicit_declare; keyword }) = + let (id, global) = + match id with + | Statement.DeclareNamespace.Local id -> (identifier id, false) + | Statement.DeclareNamespace.Global id -> (identifier id, true) + in + let keyword_str = + match keyword with + | Statement.DeclareNamespace.Namespace -> "namespace" + | Statement.DeclareNamespace.Module -> "module" + in + let props = + [ + ("id", id); + ("body", block body); + ("implicitDeclare", bool implicit_declare); + ("keyword", string keyword_str); + ] + in + let props = + if global then + ("global", bool global) :: props + else + props + in + node ?comments "DeclareNamespace" loc props and string_of_export_kind = function | Statement.ExportType -> "type" | Statement.ExportValue -> "value" @@ -1129,7 +1470,18 @@ with type t = Impl.t = struct ("right", _type right); ] and opaque_type - ~declare (loc, { Statement.OpaqueType.id; tparams; impltype; supertype; comments }) = + ~declare + ( loc, + { + Statement.OpaqueType.id; + tparams; + impl_type; + lower_bound; + upper_bound; + legacy_upper_bound; + comments; + } + ) = let name = if declare then "DeclareOpaqueType" @@ -1143,13 +1495,16 @@ with type t = Impl.t = struct [ ("id", identifier id); ("typeParameters", option type_parameter_declaration tparams); - ("impltype", option _type impltype); - ("supertype", option _type supertype); + ("impltype", option _type impl_type); + ("lowerBound", option _type lower_bound); + ("upperBound", option _type upper_bound); + ("supertype", option _type legacy_upper_bound); ] and class_declaration ast = class_helper "ClassDeclaration" ast and class_expression ast = class_helper "ClassExpression" ast and class_helper - node_type (loc, { Class.id; extends; body; tparams; implements; class_decorators; comments }) + node_type + (loc, { Class.id; extends; body; tparams; implements; class_decorators; abstract; comments }) = let (super, super_targs, comments) = match extends with @@ -1169,32 +1524,74 @@ with type t = Impl.t = struct ?comments node_type loc - [ - (* estree hasn't come around to the idea that class decls can have - optional ids, but acorn, babel, espree and esprima all have, so let's - do it too. see https://github.com/estree/estree/issues/98 *) - ("id", option identifier id); - ("body", class_body body); - ("typeParameters", option type_parameter_declaration tparams); - ("superClass", option expression super); - ("superTypeParameters", option type_args super_targs); - ("implements", implements); - ("decorators", array_of_list class_decorator class_decorators); - ] + ([ + (* estree hasn't come around to the idea that class decls can have + optional ids, but acorn, babel, espree and esprima all have, so let's + do it too. see https://github.com/estree/estree/issues/98 *) + ("id", option identifier id); + ("body", class_body body); + ("typeParameters", option type_parameter_declaration tparams); + ("superClass", option expression super); + ("superTypeArguments", option type_args super_targs); + ("implements", implements); + ("decorators", array_of_list class_decorator class_decorators); + ] + @ + if abstract then + [("abstract", bool abstract)] + else + [] + ) and class_decorator (loc, { Class.Decorator.expression = expr; comments }) = node ?comments "Decorator" loc [("expression", expression expr)] and class_implements (loc, { Class.Implements.Interface.id; targs }) = - node "ClassImplements" loc [("id", identifier id); ("typeParameters", option type_args targs)] + let id = + match id with + | Type.Generic.Identifier.Unqualified id -> identifier id + | Type.Generic.Identifier.Qualified q -> generic_type_qualified_identifier q + | Type.Generic.Identifier.ImportTypeAnnot it -> import_type it + in + node "ClassImplements" loc [("id", id); ("typeParameters", option type_args targs)] and class_body (loc, { Class.Body.body; comments }) = node ?comments "ClassBody" loc [("body", array_of_list class_element body)] + and ts_accessibility_to_string ts_accessibility = + match ts_accessibility with + | Some (_, { Class.TSAccessibility.kind = Class.TSAccessibility.Public; _ }) -> Some "public" + | Some (_, { Class.TSAccessibility.kind = Class.TSAccessibility.Protected; _ }) -> + Some "protected" + | Some (_, { Class.TSAccessibility.kind = Class.TSAccessibility.Private; _ }) -> + Some "private" + | None -> None and class_element = Class.Body.( function | Method m -> class_method m | PrivateField p -> class_private_field p | Property p -> class_property p + | StaticBlock (loc, { Class.StaticBlock.body; comments }) -> + node + ?comments:(format_internal_comments comments) + "StaticBlock" + loc + [("body", statement_list body)] + | DeclareMethod dm -> class_declare_method dm + | AbstractMethod am -> class_abstract_method am + | AbstractProperty ap -> class_abstract_property ap + | IndexSignature i -> object_type_indexer i ) - and class_method (loc, { Class.Method.key; value; kind; static; decorators; comments }) = + and class_method + ( loc, + { + Class.Method.key; + value; + kind; + static; + override; + ts_accessibility; + decorators; + comments; + } + ) = let (key, computed, comments) = let open Expression.Object.Property in match key with @@ -1222,14 +1619,117 @@ with type t = Impl.t = struct ?comments "MethodDefinition" loc - [ - ("key", key); - ("value", function_expression value); - ("kind", string kind); - ("static", bool static); - ("computed", bool computed); - ("decorators", array_of_list class_decorator decorators); - ] + ([ + ("key", key); + ("value", function_expression value); + ("kind", string kind); + ("static", bool static); + ("computed", bool computed); + ("decorators", array_of_list class_decorator decorators); + ] + @ ( if override then + [("override", bool override)] + else + [] + ) + @ + match ts_accessibility_to_string ts_accessibility with + | Some v -> [("tsAccessibility", string v)] + | None -> [] + ) + and class_declare_method + (loc, { Class.DeclareMethod.kind; key; annot; static; override; optional; comments }) = + let (key, computed, comments) = + let open Expression.Object.Property in + match key with + | StringLiteral lit -> (string_literal lit, false, comments) + | NumberLiteral lit -> (number_literal lit, false, comments) + | BigIntLiteral lit -> (bigint_literal lit, false, comments) + | Identifier id -> (identifier id, false, comments) + | PrivateName name -> (private_identifier name, false, comments) + | Computed (_, { ComputedKey.expression = expr; comments = computed_comments }) -> + ( expression expr, + true, + Flow_ast_utils.merge_comments ~outer:comments ~inner:computed_comments + ) + in + let kind_prop = + let open Class.Method in + match kind with + | Get -> [("kind", string "get")] + | Set -> [("kind", string "set")] + | Method + | Constructor -> + [] + in + node + ?comments + "DeclareMethodDefinition" + loc + ([ + ("key", key); + ("value", type_annotation annot); + ("static", bool static); + ("optional", bool optional); + ("computed", bool computed); + ] + @ ( if override then + [("override", bool override)] + else + [] + ) + @ kind_prop + ) + and class_abstract_method + (loc, { Class.AbstractMethod.key; annot; override; ts_accessibility; comments }) = + let (key, computed, comments) = property_key ~comments key in + node + ?comments + "AbstractMethodDefinition" + loc + ([("key", key); ("value", function_type annot); ("computed", bool computed)] + @ ( if override then + [("override", bool override)] + else + [] + ) + @ + match ts_accessibility_to_string ts_accessibility with + | Some v -> [("tsAccessibility", string v)] + | None -> [] + ) + and class_abstract_property + ( loc, + { + Class.AbstractProperty.key; + annot; + override; + ts_accessibility; + variance = variance_; + comments; + } + ) = + let (key, computed, comments) = property_key ~comments key in + node + ?comments + "AbstractPropertyDefinition" + loc + ([ + ("key", key); + ("value", hint type_annotation annot); + ("computed", bool computed); + ("variance", option variance variance_); + ] + @ ( if override then + [("override", bool override)] + else + [] + ) + @ + match ts_accessibility_to_string ts_accessibility with + | Some v -> [("tsAccessibility", string v)] + | None -> [] + ) and class_private_field ( loc, { @@ -1237,7 +1737,10 @@ with type t = Impl.t = struct value; annot; static; + override; + optional; variance = variance_; + ts_accessibility; decorators; comments; } @@ -1255,8 +1758,17 @@ with type t = Impl.t = struct ("typeAnnotation", hint type_annotation annot); ("computed", bool false); ("static", bool static); + ("optional", bool optional); ("variance", option variance variance_); ] + @ ( if override then + [("override", bool override)] + else + [] + ) + @ (match ts_accessibility_to_string ts_accessibility with + | Some v -> [("tsAccessibility", string v)] + | None -> []) @ ( if decorators = [] then [] else @@ -1269,22 +1781,32 @@ with type t = Impl.t = struct [] in node ?comments "PropertyDefinition" loc props - and class_property - ( loc, - { Class.Property.key; value; annot; static; variance = variance_; decorators; comments } - ) = - let (key, computed, comments) = - match key with - | Expression.Object.Property.StringLiteral lit -> (string_literal lit, false, comments) - | Expression.Object.Property.NumberLiteral lit -> (number_literal lit, false, comments) - | Expression.Object.Property.BigIntLiteral lit -> (bigint_literal lit, false, comments) - | Expression.Object.Property.Identifier id -> (identifier id, false, comments) - | Expression.Object.Property.PrivateName _ -> - failwith "Internal Error: Private name found in class prop" - | Expression.Object.Property.Computed - (_, { ComputedKey.expression = expr; comments = key_comments }) -> - (expression expr, true, Flow_ast_utils.merge_comments ~outer:comments ~inner:key_comments) - in + and property_key ~comments = function + | Expression.Object.Property.StringLiteral lit -> (string_literal lit, false, comments) + | Expression.Object.Property.NumberLiteral lit -> (number_literal lit, false, comments) + | Expression.Object.Property.BigIntLiteral lit -> (bigint_literal lit, false, comments) + | Expression.Object.Property.Identifier id -> (identifier id, false, comments) + | Expression.Object.Property.PrivateName name -> (private_identifier name, false, comments) + | Expression.Object.Property.Computed + (_, { ComputedKey.expression = expr; comments = key_comments }) -> + (expression expr, true, Flow_ast_utils.merge_comments ~outer:comments ~inner:key_comments) + and class_property (loc, prop) = class_property_helper "PropertyDefinition" loc prop + and class_property_helper + type_ + loc + { + Class.Property.key; + value; + annot; + static; + override; + optional; + variance = variance_; + ts_accessibility; + decorators; + comments; + } = + let (key, computed, comments) = property_key ~comments key in let (value, declare) = match value with | Class.Property.Declared -> (None, true) @@ -1298,8 +1820,17 @@ with type t = Impl.t = struct ("typeAnnotation", hint type_annotation annot); ("computed", bool computed); ("static", bool static); + ("optional", bool optional); ("variance", option variance variance_); ] + @ ( if override then + [("override", bool override)] + else + [] + ) + @ (match ts_accessibility_to_string ts_accessibility with + | Some v -> [("tsAccessibility", string v)] + | None -> []) @ ( if decorators = [] then [] else @@ -1311,7 +1842,7 @@ with type t = Impl.t = struct else [] in - node ?comments "PropertyDefinition" loc props + node ?comments type_ loc props and component_declaration (loc, component) = let open Statement.ComponentDeclaration in let { @@ -1320,6 +1851,7 @@ with type t = Impl.t = struct params = (_, { Params.comments = params_comments; _ }) as params; body; renders; + async; comments = component_comments; sig_loc = _; } = @@ -1330,13 +1862,20 @@ with type t = Impl.t = struct ~outer:component_comments ~inner:(format_internal_comments params_comments) in + let (node_type, implicit_declare) = + match body with + | None -> ("DeclareComponent", true) + | Some _ -> ("ComponentDeclaration", false) + in node ?comments - "ComponentDeclaration" + node_type loc [ - ("body", block body); + ("async", bool async); + ("body", option block body); ("id", identifier id); + ("implicitDeclare", bool implicit_declare); ("params", component_params params); ("rendersType", renders_annotation renders); ("typeParameters", option type_parameter_declaration tparams); @@ -1378,94 +1917,46 @@ with type t = Impl.t = struct [("name", name'); ("local", local'); ("shorthand", bool shorthand)] and enum_body body = let open Statement.EnumDeclaration in - match body with - | (loc, BooleanBody { BooleanBody.members; explicit_type; has_unknown_members; comments }) -> - node - ?comments:(format_internal_comments comments) - "EnumBooleanBody" - loc - [ - ( "members", - array_of_list - (fun (loc, { InitializedMember.id; init }) -> - node - "EnumBooleanMember" - loc - [("id", identifier id); ("init", boolean_literal init)]) - members - ); - ("explicitType", bool explicit_type); - ("hasUnknownMembers", bool has_unknown_members); - ] - | (loc, NumberBody { NumberBody.members; explicit_type; has_unknown_members; comments }) -> - node - ?comments:(format_internal_comments comments) - "EnumNumberBody" - loc - [ - ( "members", - array_of_list - (fun (loc, { InitializedMember.id; init }) -> - node "EnumNumberMember" loc [("id", identifier id); ("init", number_literal init)]) - members - ); - ("explicitType", bool explicit_type); - ("hasUnknownMembers", bool has_unknown_members); - ] - | (loc, StringBody { StringBody.members; explicit_type; has_unknown_members; comments }) -> - let members = - match members with - | StringBody.Defaulted defaulted_members -> - List.map - (fun (loc, { DefaultedMember.id }) -> - node "EnumDefaultedMember" loc [("id", identifier id)]) - defaulted_members - | StringBody.Initialized initialized_members -> - List.map - (fun (loc, { InitializedMember.id; init }) -> - node "EnumStringMember" loc [("id", identifier id); ("init", string_literal init)]) - initialized_members - in - node - ?comments:(format_internal_comments comments) - "EnumStringBody" - loc - [ - ("members", array members); - ("explicitType", bool explicit_type); - ("hasUnknownMembers", bool has_unknown_members); - ] - | (loc, SymbolBody { SymbolBody.members; has_unknown_members; comments }) -> - node - ?comments:(format_internal_comments comments) - "EnumSymbolBody" - loc - [ - ( "members", - array_of_list - (fun (loc, { DefaultedMember.id }) -> - node "EnumDefaultedMember" loc [("id", identifier id)]) - members - ); - ("hasUnknownMembers", bool has_unknown_members); - ] - | (loc, BigIntBody { BigIntBody.members; explicit_type; has_unknown_members; comments }) -> - node - ?comments:(format_internal_comments comments) - "EnumBigIntBody" - loc - [ - ( "members", - array_of_list - (fun (loc, { InitializedMember.id; init }) -> - node "EnumBigIntMember" loc [("id", identifier id); ("init", bigint_literal init)]) - members - ); - ("explicitType", bool explicit_type); - ("hasUnknownMembers", bool has_unknown_members); - ] - and enum_declaration (loc, { Statement.EnumDeclaration.id; body; comments }) = - node ?comments "EnumDeclaration" loc [("id", identifier id); ("body", enum_body body)] + let (loc, { Body.members; explicit_type; has_unknown_members; comments }) = body in + let enum_member_name = function + | Identifier id -> identifier id + | StringLiteral sl -> string_literal sl + in + let enum_member = function + | BooleanMember (loc, { InitializedMember.id; init }) -> + node "EnumBooleanMember" loc [("id", enum_member_name id); ("init", boolean_literal init)] + | NumberMember (loc, { InitializedMember.id; init }) -> + node "EnumNumberMember" loc [("id", enum_member_name id); ("init", number_literal init)] + | StringMember (loc, { InitializedMember.id; init }) -> + node "EnumStringMember" loc [("id", enum_member_name id); ("init", string_literal init)] + | BigIntMember (loc, { InitializedMember.id; init }) -> + node "EnumBigIntMember" loc [("id", enum_member_name id); ("init", bigint_literal init)] + | DefaultedMember (loc, { DefaultedMember.id }) -> + node "EnumDefaultedMember" loc [("id", enum_member_name id)] + in + let explicit_type_str = + match explicit_type with + | Some (_, et) -> string (Flow_ast_utils.string_of_enum_explicit_type et) + | None -> null + in + node + ?comments:(format_internal_comments comments) + "EnumBody" + loc + [ + ("members", array_of_list enum_member members); + ("explicitType", explicit_type_str); + ("hasUnknownMembers", bool (has_unknown_members <> None)); + ] + and enum_declaration (loc, { Statement.EnumDeclaration.id; body; const_; comments }) = + let props = [("id", identifier id); ("body", enum_body body)] in + let props = + if const_ then + ("const", bool true) :: props + else + props + in + node ?comments "EnumDeclaration" loc props and interface_declaration (loc, { Statement.Interface.id; tparams; body; extends; comments }) = node ?comments @@ -1482,12 +1973,13 @@ with type t = Impl.t = struct match id with | Type.Generic.Identifier.Unqualified id -> identifier id | Type.Generic.Identifier.Qualified q -> generic_type_qualified_identifier q + | Type.Generic.Identifier.ImportTypeAnnot it -> import_type it in node ?comments "InterfaceExtends" loc [("id", id); ("typeParameters", option type_args targs)] and pattern = Pattern.( function - | (loc, Object { Object.properties; annot; comments }) -> + | (loc, Object { Object.properties; annot; optional; comments }) -> node ?comments:(format_internal_comments comments) "ObjectPattern" @@ -1495,8 +1987,9 @@ with type t = Impl.t = struct [ ("properties", array_of_list object_pattern_property properties); ("typeAnnotation", hint type_annotation annot); + ("optional", bool optional); ] - | (loc, Array { Array.elements; annot; comments }) -> + | (loc, Array { Array.elements; annot; optional; comments }) -> node ?comments:(format_internal_comments comments) "ArrayPattern" @@ -1504,15 +1997,20 @@ with type t = Impl.t = struct [ ("elements", array_of_list array_pattern_element elements); ("typeAnnotation", hint type_annotation annot); + ("optional", bool optional); ] | (loc, Identifier pattern_id) -> pattern_identifier loc pattern_id | (_loc, Expression expr) -> expression expr ) - and function_param (loc, { Ast.Function.Param.argument; default }) = - match default with - | Some default -> - node "AssignmentPattern" loc [("left", pattern argument); ("right", expression default)] - | None -> pattern argument + and function_param (loc, param) = + let open Ast.Function.Param in + match param with + | RegularParam { argument; default } -> + (match default with + | Some default -> + node "AssignmentPattern" loc [("left", pattern argument); ("right", expression default)] + | None -> pattern argument) + | ParamProperty prop -> class_property_helper "ParameterProperty" loc prop and this_param (loc, { Function.ThisParam.annot; comments }) = node ?comments @@ -1577,19 +2075,7 @@ with type t = Impl.t = struct | Set { key; value = (loc, func); comments } -> (key, function_expression (loc, func), "set", false, false, comments) in - let (key, computed, comments) = - match key with - | StringLiteral lit -> (string_literal lit, false, comments) - | NumberLiteral lit -> (number_literal lit, false, comments) - | BigIntLiteral lit -> (bigint_literal lit, false, comments) - | Identifier id -> (identifier id, false, comments) - | PrivateName _ -> failwith "Internal Error: Found private field in object props" - | Computed (_, { ComputedKey.expression = expr; comments = key_comments }) -> - ( expression expr, - true, - Flow_ast_utils.merge_comments ~outer:comments ~inner:key_comments - ) - in + let (key, computed, comments) = property_key ~comments key in node ?comments "Property" @@ -1638,32 +2124,23 @@ with type t = Impl.t = struct ("computed", bool computed); ] | RestElement (loc, el) -> rest_element loc el - and spread_element (loc, { Expression.SpreadElement.argument; comments }) = - node ?comments "SpreadElement" loc [("argument", expression argument)] - and expression_or_spread = + and spread_element ~in_optional_chain (loc, { Expression.SpreadElement.argument; comments }) = + node ?comments "SpreadElement" loc [("argument", expression ~in_optional_chain argument)] + and expression_or_spread ~in_optional_chain = let open Expression in function - | Expression expr -> expression expr - | Spread spread -> spread_element spread + | Expression expr -> expression ~in_optional_chain expr + | Spread spread -> spread_element ~in_optional_chain spread and array_element = let open Expression.Array in function | Hole _ -> null | Expression expr -> expression expr - | Spread spread -> spread_element spread + | Spread spread -> spread_element ~in_optional_chain:false spread and number_literal (loc, { NumberLiteral.value; raw; comments }) = node ?comments "Literal" loc [("value", number value); ("raw", string raw)] - and bigint_literal (loc, { BigIntLiteral.value; raw; comments }) = - (* https://github.com/estree/estree/blob/master/es2020.md#bigintliteral - * `bigint` property is the string representation of the `BigInt` value. - * It must contain only decimal digits and not include numeric separators `_` or the suffix `n`. - *) - let bigint = - match value with - | Some value -> Int64.to_string value - | None -> - String.sub raw 0 (String.length raw - 1) |> String.split_on_char '_' |> String.concat "" - in + and bigint_literal (loc, ({ BigIntLiteral.raw; value = _; comments } as bigint)) = + let bigint = Flow_ast_utils.string_of_bigint bigint in node ?comments "Literal" loc [("value", null); ("bigint", string bigint); ("raw", string raw)] and string_literal (loc, { StringLiteral.value; raw; comments }) = node ?comments "Literal" loc [("value", string value); ("raw", string raw)] @@ -1702,12 +2179,16 @@ with type t = Impl.t = struct ) = let value = obj [("raw", string raw); ("cooked", string cooked)] in node "TemplateElement" loc [("value", value); ("tail", bool tail)] - and tagged_template (loc, { Expression.TaggedTemplate.tag; quasi; comments }) = + and tagged_template (loc, { Expression.TaggedTemplate.tag; targs; quasi; comments }) = node ?comments "TaggedTemplateExpression" loc - [("tag", expression tag); ("quasi", template_literal quasi)] + [ + ("tag", expression tag); + ("typeArguments", option call_type_args targs); + ("quasi", template_literal quasi); + ] and variable_declaration (loc, { Statement.VariableDeclaration.kind; declarations; comments }) = let kind = Flow_ast_utils.string_of_variable_kind kind in node @@ -1724,6 +2205,7 @@ with type t = Impl.t = struct | Plus -> "plus" | Minus -> "minus" | Readonly -> "readonly" + | Writeonly -> "writeonly" | In -> "in" | Out -> "out" | InOut -> "in-out" @@ -1764,10 +2246,13 @@ with type t = Impl.t = struct | NumberLiteral n -> number_literal_type (loc, n) | BigIntLiteral n -> bigint_literal_type (loc, n) | BooleanLiteral b -> boolean_literal_type (loc, b) + | TemplateLiteral t -> template_literal_type (loc, t) | Exists comments -> exists_type loc comments | Unknown comments -> unknown_type loc comments | Never comments -> never_type loc comments | Undefined comments -> undefined_type loc comments + | UniqueSymbol comments -> unique_symbol_type loc comments + | ConstructorType ct -> constructor_type (loc, ct) ) and any_type loc comments = node ?comments "AnyTypeAnnotation" loc [] and mixed_type loc comments = node ?comments "MixedTypeAnnotation" loc [] @@ -1784,8 +2269,15 @@ with type t = Impl.t = struct and unknown_type loc comments = node ?comments "UnknownTypeAnnotation" loc [] and never_type loc comments = node ?comments "NeverTypeAnnotation" loc [] and undefined_type loc comments = node ?comments "UndefinedTypeAnnotation" loc [] + and unique_symbol_type loc comments = + node + ?comments + "TypeOperator" + loc + [("operator", string "unique"); ("typeAnnotation", node "SymbolTypeAnnotation" loc [])] and return_annotation = function - | Ast.Type.Function.TypeAnnotation t -> _type t + | Ast.Type.Function.Missing _ -> null + | Ast.Type.Function.Available t -> _type t | Ast.Type.Function.TypeGuard g -> type_guard g and type_guard (loc, { Ast.Type.TypeGuard.kind; guard = (x, t); comments }) = let kind = @@ -1838,16 +2330,53 @@ with type t = Impl.t = struct else [("this", option function_type_this_constraint this_)] ) - and function_type_param ?comments (loc, { Type.Function.Param.name; annot; optional }) = + and constructor_type + ( loc, + { + Type.ConstructorType.abstract_; + func = + { + Type.Function.params = + (_, { Type.Function.Params.this_ = _; params; rest; comments = params_comments }); + return; + tparams; + effect_ = _; + comments = func_comments; + }; + } + ) = + let comments = + Flow_ast_utils.merge_comments + ~inner:(format_internal_comments params_comments) + ~outer:func_comments + in node ?comments - "FunctionTypeParam" + "ConstructorTypeAnnotation" loc [ - ("name", option identifier name); - ("typeAnnotation", _type annot); - ("optional", bool optional); + ("abstract", bool abstract_); + ("params", array_of_list function_type_param params); + ("returnType", return_annotation return); + ("rest", option function_type_rest rest); + ("typeParameters", option type_parameter_declaration tparams); ] + and function_type_param ?comments (loc, param) = + let open Type.Function.Param in + match param with + | Anonymous annot -> + node + ?comments + "FunctionTypeParam" + loc + [("name", null); ("typeAnnotation", _type annot); ("optional", bool false)] + | Labeled { name; annot; optional } -> + node + ?comments + "FunctionTypeParam" + loc + [("name", identifier name); ("typeAnnotation", _type annot); ("optional", bool optional)] + | Destructuring patt -> pattern patt and function_type_rest (_loc, { Type.Function.RestParam.argument; comments }) = (* TODO: add a node for the rest param itself, including the `...`, like we do with RestElement on normal functions. This should be @@ -1888,7 +2417,10 @@ with type t = Impl.t = struct (props, ixs, calls, slot :: slots) | MappedType m -> let mapped_type = object_type_mapped_type m in - (mapped_type :: props, ixs, calls, slots)) + (mapped_type :: props, ixs, calls, slots) + | PrivateField pf -> + let prop = object_type_private_field pf in + (prop :: props, ixs, calls, slots)) ([], [], [], []) properties in @@ -1919,23 +2451,18 @@ with type t = Impl.t = struct proto; variance = variance_; _method; + abstract; + override; + ts_accessibility; + init = init_; comments; } ) = - let key = - match key with - | Expression.Object.Property.StringLiteral lit -> string_literal lit - | Expression.Object.Property.NumberLiteral lit -> number_literal lit - | Expression.Object.Property.BigIntLiteral lit -> bigint_literal lit - | Expression.Object.Property.Identifier id -> identifier id - | Expression.Object.Property.PrivateName _ -> - failwith "Internal Error: Found private field in object props" - | Expression.Object.Property.Computed _ -> - failwith "There should not be computed object type property keys" - in + let (key, computed, comments) = property_key ~comments key in let (value, kind) = match value with - | Type.Object.Property.Init value -> (_type value, "init") + | Type.Object.Property.Init (Some value) -> (_type value, "init") + | Type.Object.Property.Init None -> (null, "init") | Type.Object.Property.Get (loc, f) -> (function_type (loc, f), "get") | Type.Object.Property.Set (loc, f) -> (function_type (loc, f), "set") in @@ -1943,31 +2470,56 @@ with type t = Impl.t = struct ?comments "ObjectTypeProperty" loc - [ - ("key", key); - ("value", value); - ("method", bool _method); - ("optional", bool optional); - ("static", bool static); - ("proto", bool proto); - ("variance", option variance variance_); - ("kind", string kind); - ] + ([ + ("key", key); + ("value", value); + ("method", bool _method); + ("optional", bool optional); + ("static", bool static); + ("proto", bool proto); + ("abstract", bool abstract); + ("variance", option variance variance_); + ("kind", string kind); + ("init", option expression init_); + ] + @ ( if computed then + [("computed", bool computed)] + else + [] + ) + @ ( if override then + [("override", bool override)] + else + [] + ) + @ + match ts_accessibility_to_string ts_accessibility with + | Some v -> [("tsAccessibility", string v)] + | None -> [] + ) and object_type_spread_property (loc, { Type.Object.SpreadProperty.argument; comments }) = node ?comments "ObjectTypeSpreadProperty" loc [("argument", _type argument)] and object_type_indexer - (loc, { Type.Object.Indexer.id; key; value; static; variance = variance_; comments }) = + ( loc, + { Type.Object.Indexer.id; key; value; static; variance = variance_; optional; comments } + ) = node ?comments "ObjectTypeIndexer" loc - [ - ("id", option identifier id); - ("key", _type key); - ("value", _type value); - ("static", bool static); - ("variance", option variance variance_); - ] + ([ + ("id", option identifier id); + ("key", _type key); + ("value", _type value); + ("static", bool static); + ("variance", option variance variance_); + ] + @ + if optional then + [("optional", bool optional)] + else + [] + ) and object_type_call_property (loc, { Type.Object.CallProperty.value; static; comments }) = node ?comments @@ -1980,7 +2532,9 @@ with type t = Impl.t = struct Type.Object.MappedType.key_tparam; prop_type; source_type; + name_type; variance = variance_; + variance_op; comments; optional; } @@ -1994,6 +2548,12 @@ with type t = Impl.t = struct | NoOptionalFlag -> null ) in + let variance_op = + match variance_op with + | Some Type.Object.MappedType.Add -> string "+" + | Some Type.Object.MappedType.Remove -> string "-" + | None -> null + in node ?comments "ObjectTypeMappedTypeProperty" @@ -2002,7 +2562,9 @@ with type t = Impl.t = struct ("keyTparam", type_param key_tparam); ("propType", _type prop_type); ("sourceType", _type source_type); + ("nameType", option _type name_type); ("variance", option variance variance_); + ("varianceOp", variance_op); ("optional", optional_flag optional); ] and object_type_internal_slot @@ -2018,6 +2580,8 @@ with type t = Impl.t = struct ("method", bool _method); ("value", _type value); ] + and object_type_private_field (loc, { Type.Object.PrivateField.key; comments }) = + node ?comments "ObjectTypePrivateField" loc [("key", private_identifier key)] and interface_type (loc, { Type.Interface.extends; body; comments }) = node ?comments @@ -2043,24 +2607,37 @@ with type t = Impl.t = struct ] and infer_type loc { Type.Infer.tparam; comments } = node ?comments "InferTypeAnnotation" loc [("typeParameter", type_param tparam)] + and import_type (loc, { Type.Generic.Identifier.argument; comments }) = + node ?comments "ImportType" loc [("argument", string_literal argument)] and generic_type_qualified_identifier (loc, { Type.Generic.Identifier.id; qualification }) = let qualification = match qualification with | Type.Generic.Identifier.Unqualified id -> identifier id | Type.Generic.Identifier.Qualified q -> generic_type_qualified_identifier q + | Type.Generic.Identifier.ImportTypeAnnot it -> import_type it in node "QualifiedTypeIdentifier" loc [("qualification", qualification); ("id", identifier id)] and generic_type (loc, { Type.Generic.id; targs; comments }) = - let id = - match id with - | Type.Generic.Identifier.Unqualified id -> identifier id - | Type.Generic.Identifier.Qualified q -> generic_type_qualified_identifier q - in - node - ?comments - "GenericTypeAnnotation" - loc - [("id", id); ("typeParameters", option type_args targs)] + (* Mirror upstream Hermes' mapGenericTypeAnnotation: collapse the + no-targs `this` identifier case to a ThisTypeAnnotation leaf node. + OCaml's parser produces `Type.Generic { id: Unqualified "this"; + targs: None }` for both `type T = this` and `(this) => void` / + `m(): this`. *) + match (targs, id) with + | (None, Type.Generic.Identifier.Unqualified (_, { Identifier.name = "this"; _ })) -> + node ?comments "ThisTypeAnnotation" loc [] + | _ -> + let id = + match id with + | Type.Generic.Identifier.Unqualified id -> identifier id + | Type.Generic.Identifier.Qualified q -> generic_type_qualified_identifier q + | Type.Generic.Identifier.ImportTypeAnnot it -> import_type it + in + node + ?comments + "GenericTypeAnnotation" + loc + [("id", id); ("typeParameters", option type_args targs)] and indexed_access_properties { Type.IndexedAccess._object; index; comments = _ } = [("objectType", _type _object); ("indexType", _type index)] and indexed_access (loc, ({ Type.IndexedAccess.comments; _ } as ia)) = @@ -2097,6 +2674,7 @@ with type t = Impl.t = struct match id with | Type.Typeof.Target.Unqualified id -> identifier id | Type.Typeof.Target.Qualified q -> typeof_qualifier q + | Type.Typeof.Target.Import it -> import_type it and typeof_qualifier (loc, { Type.Typeof.Target.id; qualification }) = let qualification = typeof_expr qualification in node "QualifiedTypeofIdentifier" loc [("qualification", qualification); ("id", identifier id)] @@ -2130,7 +2708,15 @@ with type t = Impl.t = struct ( "elementTypes", array_of_list (function - | (_, Type.Tuple.UnlabeledElement annot) -> _type annot + | (loc, Type.Tuple.UnlabeledElement { Type.Tuple.UnlabeledElement.annot; optional }) + -> + if optional then + node + "TupleTypeElement" + loc + [("elementType", _type annot); ("optional", bool true)] + else + _type annot | (loc, Type.Tuple.LabeledElement e) -> tuple_labeled_element loc e | (loc, Type.Tuple.SpreadElement e) -> tuple_spread_element loc e) elements @@ -2185,6 +2771,25 @@ with type t = Impl.t = struct ) ); ] + and template_literal_type (loc, { Ast.Type.TemplateLiteral.quasis; types; comments }) = + node + ?comments + "TemplateLiteralTypeAnnotation" + loc + [ + ("quasis", array_of_list template_element_type quasis); + ("types", array_of_list _type types); + ] + and template_element_type + ( loc, + { + Ast.Type.TemplateLiteral.Element.value = + { Ast.Type.TemplateLiteral.Element.raw; cooked }; + tail; + } + ) = + let value = obj [("raw", string raw); ("cooked", string cooked)] in + node "TemplateElement" loc [("value", value); ("tail", bool tail)] and exists_type loc comments = node ?comments "ExistsTypeAnnotation" loc [] and type_annotation (loc, ty) = node "TypeAnnotation" loc [("typeAnnotation", _type ty)] and type_guard_annotation (loc, (loc1, guard)) = @@ -2214,7 +2819,11 @@ with type t = Impl.t = struct (* we track the location of the name, but don't expose it here for backwards-compatibility. TODO: change this? *) ("name", string name); - ("bound", hint type_annotation bound); + (* Hermes' deserializeTypeParameter reads `bound` as a plain type + node, NOT a TypeAnnotation-wrapped node. Emit the inner annotation + directly rather than going through `type_annotation` (which writes + a TypeAnnotation header). When the bound is missing, write null. *) + ("bound", hint (fun (_loc, ty) -> _type ty) bound); ("const", bool (Option.is_some const)); ("variance", option variance tp_var); ("default", option _type default); @@ -2377,6 +2986,7 @@ with type t = Impl.t = struct { Statement.ExportNamedDeclaration.ExportSpecifier.exported; local; + export_kind; from_remote = _; imported_name_def_loc = _; } @@ -2386,17 +2996,29 @@ with type t = Impl.t = struct | Some exported -> identifier exported | None -> identifier local in - node "ExportSpecifier" loc [("local", identifier local); ("exported", exported)] + node + "ExportSpecifier" + loc + [ + ("local", identifier local); + ("exported", exported); + ("exportKind", string (string_of_export_kind export_kind)); + ] and import_default_specifier { Statement.ImportDeclaration.identifier = id; remote_default_name_def_loc = _ } = node "ImportDefaultSpecifier" (fst id) [("local", identifier id)] and import_namespace_specifier (loc, id) = node "ImportNamespaceSpecifier" loc [("local", identifier id)] - and import_named_specifier local_id remote_id kind = + and import_named_specifier local_id remote_id kind kind_loc = + let start_loc = + match kind_loc with + | Some kl -> kl + | None -> fst remote_id + in let span_loc = match local_id with - | Some local_id -> Loc.btwn (fst remote_id) (fst local_id) - | None -> fst remote_id + | Some local_id -> Loc.btwn start_loc (fst local_id) + | None -> Loc.btwn start_loc (fst remote_id) in let local_id = match local_id with @@ -2418,6 +3040,14 @@ with type t = Impl.t = struct null ); ] + and import_attribute { Statement.ImportDeclaration.loc; key; value } = + let key_json = + match key with + | Statement.ImportDeclaration.Identifier id -> identifier id + | Statement.ImportDeclaration.StringLiteral (loc, lit) -> string_literal (loc, lit) + in + let value_json = string_literal value in + node "ImportAttribute" loc [("key", key_json); ("value", value_json)] and comment_list comments = array_of_list comment comments and comment (loc, c) = Comment.( @@ -2436,20 +3066,32 @@ with type t = Impl.t = struct | Inferred -> ("InferredPredicate", []) in node ?comments _type loc value - and call_node_properties { Expression.Call.callee; targs; arguments; comments = _ } = + and call_node_properties + ~in_optional_chain ?wrap_callee { Expression.Call.callee; targs; arguments; comments = _ } = + let callee = + match wrap_callee with + | None -> expression ~in_optional_chain callee + | Some wrap -> wrap (expression ~in_optional_chain callee) + in [ - ("callee", expression callee); + ("callee", callee); ("typeArguments", option call_type_args targs); - ("arguments", arg_list arguments); + ("arguments", arg_list ~in_optional_chain arguments); ] - and member_node_properties { Expression.Member._object; property; comments = _ } = + and member_node_properties + ~in_optional_chain ?wrap_receiver { Expression.Member._object; property; comments = _ } = let (property, computed) = match property with | Expression.Member.PropertyIdentifier id -> (identifier id, false) | Expression.Member.PropertyPrivateName name -> (private_identifier name, false) - | Expression.Member.PropertyExpression expr -> (expression expr, true) + | Expression.Member.PropertyExpression expr -> (expression ~in_optional_chain expr, true) + in + let _object = + match wrap_receiver with + | None -> expression ~in_optional_chain _object + | Some wrap -> wrap (expression ~in_optional_chain _object) in - [("object", expression _object); ("property", property); ("computed", bool computed)] + [("object", _object); ("property", property); ("computed", bool computed)] in { program; expression } diff --git a/compiler/flow_parser/parser/expression_parser.ml b/compiler/flow_parser/parser/expression_parser.ml index ec4d89a875b..dee263b815e 100644 --- a/compiler/flow_parser/parser/expression_parser.ml +++ b/compiler/flow_parser/parser/expression_parser.ml @@ -37,7 +37,7 @@ module Expression in a_prec >= b_prec - let is_assignable_lhs = + let rec is_assignable_lhs = let open Expression in function | ( _, @@ -65,6 +65,7 @@ module Expression | (_, MetaProperty _) | (_, Object _) -> true + | (_, Unary { Unary.operator = Unary.Nonnull; argument; _ }) -> is_assignable_lhs argument | (_, ArrowFunction _) | (_, AsConstExpression _) | (_, AsExpression _) @@ -89,6 +90,7 @@ module Expression | (_, New _) | (_, OptionalCall _) | (_, OptionalMember _) + | (_, Record _) | (_, Sequence _) | (_, Super _) | (_, TaggedTemplate _) @@ -214,6 +216,7 @@ module Expression with_loc (fun env -> if in_formal_parameters env then error env Parse_error.YieldInFormalParameters; + if in_match_expression env then error env Parse_error.MatchExpressionYield; let leading = Peek.comments env in let start_loc = Peek.loc env in Expect.token env T_YIELD; @@ -284,6 +287,7 @@ module Expression | (_, Member _) | (_, MetaProperty _) -> true + | (_, Unary { Unary.operator = Unary.Nonnull; argument; _ }) -> is_lhs argument | (_, Array _) | (_, ArrowFunction _) | (_, AsConstExpression _) @@ -310,6 +314,7 @@ module Expression | (_, Object _) | (_, OptionalCall _) | (_, OptionalMember _) + | (_, Record _) | (_, Sequence _) | (_, Super _) | (_, TaggedTemplate _) @@ -511,6 +516,7 @@ module Expression (is_unary, expr)) env in + let expr_loc = ref expr_loc in let next = Peek.token env in ( if next = T_LESS_THAN then match expr with @@ -526,16 +532,17 @@ module Expression let (stack, expr) = match stack with | (left, (lop, lpri), lloc) :: rest when is_tighter lpri (Left_assoc 6) -> - let expr_loc = Loc.btwn lloc expr_loc in - let expr = make_binary left expr lop expr_loc in + let loc = Loc.btwn lloc !expr_loc in + let expr = make_binary left expr lop loc in (rest, expr) | _ -> (stack, expr) in - let (expr_loc, _) = expr in + let (new_expr_loc, _) = expr in let expr = - if keyword = "satisfies" then + if keyword = "satisfies" then ( let ((annot_loc, _) as annot) = Type._type env in - let loc = Loc.btwn expr_loc annot_loc in + let loc = Loc.btwn new_expr_loc annot_loc in + expr_loc := loc; Cover_expr ( loc, Expression.TSSatisfies @@ -545,8 +552,9 @@ module Expression comments = None; } ) - else if Peek.token env = T_CONST then ( - let loc = Loc.btwn expr_loc (Peek.loc env) in + ) else if Peek.token env = T_CONST then ( + let loc = Loc.btwn new_expr_loc (Peek.loc env) in + expr_loc := loc; Eat.token env; Cover_expr ( loc, @@ -555,7 +563,8 @@ module Expression ) ) else let ((annot_loc, _) as annot) = Type._type env in - let loc = Loc.btwn expr_loc annot_loc in + let loc = Loc.btwn new_expr_loc annot_loc in + expr_loc := loc; Cover_expr ( loc, Expression.AsExpression @@ -576,12 +585,12 @@ module Expression | ([], None) -> expr | (_, None) -> let expr = as_expression env expr in - Cover_expr (collapse_stack expr expr_loc stack) + Cover_expr (collapse_stack expr !expr_loc stack) | (_, Some (rop, rpri)) -> if is_unary && rop = Expression.Binary.Exp then - error_at env (expr_loc, Parse_error.InvalidLHSInExponentiation); + error_at env (!expr_loc, Parse_error.InvalidLHSInExponentiation); let expr = as_expression env expr in - helper env (add_to_stack expr (rop, rpri) expr_loc stack) + helper env (add_to_stack expr (rop, rpri) !expr_loc stack) in (fun env -> helper env []) @@ -602,6 +611,7 @@ module Expression * Babel does it. *) | T_AWAIT when allow_await env -> if in_formal_parameters env then error env Parse_error.AwaitInAsyncFormalParameters; + if in_match_expression env then error env Parse_error.MatchExpressionAwait; Some Await | _ -> None @@ -785,26 +795,36 @@ module Expression let leading_arg = Peek.comments env in Expect.token env T_LPAREN; let argument = add_comments (assignment (with_no_in false env)) ~leading:leading_arg in + let options = + if Eat.maybe env T_COMMA then + Some (assignment (with_no_in false env)) + else + None + in Expect.token env T_RPAREN; let trailing = Eat.trailing_comments env in Expression.Import { Expression.Import.argument; + options; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); }) env and call_cover ?(allow_optional_chain = true) ?(in_optional_chain = false) env start_loc left = let left = member_cover ~allow_optional_chain ~in_optional_chain env start_loc left in - let optional = - match last_token env with - | Some T_PLING_PERIOD -> true - | _ -> false - in let left_to_callee env = let { remove_trailing; _ } = trailing_and_remover env in remove_trailing (as_expression env left) (fun remover left -> remover#expression left) in + let optional = + match last_token env with + | Some T_PLING_PERIOD -> Some Expression.OptionalCall.Optional + | Some T_NOT when in_optional_chain && (parse_options env).assert_operator -> + Some Expression.OptionalCall.AssertNonnull + | _ when in_optional_chain -> Some Expression.OptionalCall.NonOptional + | _ -> None + in let arguments ?targs env callee = let (args_loc, arguments) = arguments env in let loc = Loc.btwn start_loc args_loc in @@ -812,20 +832,44 @@ module Expression { Expression.Call.callee; targs; arguments = (args_loc, arguments); comments = None } in let call = - if optional || in_optional_chain then + match optional with + | Some optional -> let open Expression in OptionalCall { OptionalCall.call; optional; filtered_out = loc } - else - Expression.Call call + | None -> Expression.Call call in - let in_optional_chain = in_optional_chain || optional in + let in_optional_chain = Option.is_some optional in call_cover ~allow_optional_chain ~in_optional_chain env start_loc (Cover_expr (loc, call)) in + let is_a_to_z c = c >= 'a' && c <= 'z' in + let should_parse_record env constructor = + (parse_options env).records + && (not (Peek.ith_is_line_terminator ~i:0 env)) + && (not (no_record env)) + && + match constructor with + | (_, Expression.Identifier (_, { Identifier.name; _ })) + when name != "" && not (is_a_to_z name.[0]) -> + true + | (_, Expression.Member _) -> true + | _ -> false + in + let parse_record env ~constructor ~targs = + let (obj_loc, obj, { if_expr; if_patt = _ }) = Parse.object_initializer env in + List.iter (error_at env) if_expr; + Cover_expr + ( Loc.btwn start_loc obj_loc, + Expression.Record + { Expression.Record.constructor; targs; properties = (obj_loc, obj); comments = None } + ) + in if no_call env then left else match Peek.token env with | T_LPAREN -> arguments env (left_to_callee env) + | T_LCURLY when should_parse_record env (as_expression env left) -> + parse_record env ~constructor:(left_to_callee env) ~targs:None | T_LSHIFT | T_LESS_THAN when should_parse_types env -> @@ -838,7 +882,13 @@ module Expression Try.or_else env ~fallback:left (fun env -> let callee = left_to_callee env in let targs = call_type_args env in - arguments ?targs env callee + match Peek.token env with + | T_LCURLY when should_parse_record env callee -> + parse_record env ~constructor:callee ~targs + | T_TEMPLATE_PART part -> + let expr = tagged_template ?targs env start_loc callee part in + call_cover ~allow_optional_chain:true env start_loc (Cover_expr expr) + | _ -> arguments ?targs env callee ) | _ -> left @@ -1016,13 +1066,7 @@ module Expression env and member_cover = - let dynamic - ?(allow_optional_chain = true) - ?(in_optional_chain = false) - ?(optional = false) - env - start_loc - left = + let dynamic ~allow_optional_chain ~optional env start_loc left = let expr = Parse.expression (env |> with_no_call false) in let last_loc = Peek.loc env in Expect.token env T_RBRACKET; @@ -1037,21 +1081,20 @@ module Expression in let member = - if in_optional_chain then - let open Expression in - OptionalMember { OptionalMember.member; optional; filtered_out = loc } - else - Expression.Member member + match optional with + | Some optional -> + Expression.OptionalMember + { Expression.OptionalMember.member; optional; filtered_out = loc } + | None -> Expression.Member member in - call_cover ~allow_optional_chain ~in_optional_chain env start_loc (Cover_expr (loc, member)) - in - let static - ?(allow_optional_chain = true) - ?(in_optional_chain = false) - ?(optional = false) + call_cover + ~allow_optional_chain + ~in_optional_chain:(Option.is_some optional) env start_loc - left = + (Cover_expr (loc, member)) + in + let static ~allow_optional_chain ~optional env start_loc left = let open Expression.Member in let (id_loc, property) = match Peek.token env with @@ -1075,19 +1118,30 @@ module Expression Expression.Member.{ _object = as_expression env left; property; comments = None } in let member = - if in_optional_chain then - let open Expression in - OptionalMember { OptionalMember.member; optional; filtered_out = loc } - else - Expression.Member member + match optional with + | Some optional -> + Expression.OptionalMember + { Expression.OptionalMember.member; optional; filtered_out = loc } + | None -> Expression.Member member in - call_cover ~allow_optional_chain ~in_optional_chain env start_loc (Cover_expr (loc, member)) + call_cover + ~allow_optional_chain + ~in_optional_chain:(Option.is_some optional) + env + start_loc + (Cover_expr (loc, member)) in fun ?(allow_optional_chain = true) ?(in_optional_chain = false) env start_loc left -> + let default_optional = + if in_optional_chain then + Some Expression.OptionalMember.NonOptional + else + None + in + let left = assert_operator_cover env ~in_optional_chain start_loc left in match Peek.token env with | T_PLING_PERIOD -> if not allow_optional_chain then error env Parse_error.OptionalChainNew; - Expect.token env T_PLING_PERIOD; begin match Peek.token env with @@ -1098,22 +1152,88 @@ module Expression | T_LESS_THAN when should_parse_types env -> left | T_LBRACKET -> Eat.token env; - dynamic ~allow_optional_chain ~in_optional_chain:true ~optional:true env start_loc left + dynamic + ~allow_optional_chain + ~optional:(Some Expression.OptionalMember.Optional) + env + start_loc + left | _ -> - static ~allow_optional_chain ~in_optional_chain:true ~optional:true env start_loc left + static + ~allow_optional_chain + ~optional:(Some Expression.OptionalMember.Optional) + env + start_loc + left end | T_LBRACKET -> Eat.token env; - dynamic ~allow_optional_chain ~in_optional_chain env start_loc left + dynamic ~allow_optional_chain ~optional:default_optional env start_loc left | T_PERIOD -> Eat.token env; - static ~allow_optional_chain ~in_optional_chain env start_loc left + static ~allow_optional_chain ~optional:default_optional env start_loc left + | T_NOT when in_optional_chain && (parse_options env).assert_operator -> begin + match Peek.ith_token ~i:1 env with + | T_TEMPLATE_PART _ -> + error env Parse_error.OptionalChainTemplate; + Eat.token env; + left + | T_LPAREN -> + Eat.token env; + left + | T_LESS_THAN when should_parse_types env -> + Eat.token env; + left + | T_LBRACKET -> + Eat.token env; + Eat.token env; + dynamic + ~allow_optional_chain + ~optional:(Some Expression.OptionalMember.AssertNonnull) + env + start_loc + left + | T_PERIOD -> + Eat.token env; + Eat.token env; + static + ~allow_optional_chain + ~optional:(Some Expression.OptionalMember.AssertNonnull) + env + start_loc + left + | _ -> left + end | T_TEMPLATE_PART part -> if in_optional_chain then error env Parse_error.OptionalChainTemplate; let expr = tagged_template env start_loc (as_expression env left) part in call_cover ~allow_optional_chain:true env start_loc (Cover_expr expr) | _ -> left + and assert_operator_cover env ~in_optional_chain start_loc left = + match (Peek.token env, Peek.ith_token ~i:1 env) with + | (T_NOT, ((T_PERIOD | T_LBRACKET | T_LESS_THAN | T_LPAREN) as next)) + when in_optional_chain && (next <> T_LPAREN || should_parse_types env) -> + left + | (T_NOT, _) when (parse_options env).assert_operator -> + let argument = as_expression env left in + let end_loc = Peek.loc env in + Eat.token env; + let trailing = Eat.trailing_comments env in + let loc = Loc.btwn start_loc end_loc in + Cover_expr + ( loc, + Expression.( + Unary + { + Unary.operator = Unary.Nonnull; + argument; + comments = Flow_ast_utils.mk_comments_opt ~trailing (); + } + ) + ) + | _ -> left + and member ?(allow_optional_chain = true) env start_loc left = as_expression env (member_cover ~allow_optional_chain env start_loc (Cover_expr left)) @@ -1276,8 +1396,8 @@ module Expression let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in let expr = let opts = parse_options env in - match (opts.module_ref_prefix, opts.module_ref_prefix_LEGACY_INTEROP) with - | (Some prefix, _) when String.starts_with ~prefix value -> + match opts.module_ref_prefix with + | Some prefix when String.starts_with ~prefix value -> let prefix_len = String.length prefix in Expression.ModuleRefLiteral { @@ -1285,19 +1405,6 @@ module Expression require_loc = loc; def_loc_opt = None; prefix_len; - legacy_interop = false; - raw; - comments; - } - | (_, Some prefix) when String.starts_with ~prefix value -> - let prefix_len = String.length prefix in - Expression.ModuleRefLiteral - { - Ast.ModuleRefLiteral.value; - require_loc = loc; - def_loc_opt = None; - prefix_len; - legacy_interop = true; raw; comments; } @@ -1350,6 +1457,7 @@ module Expression (* `match () {` *) if (not (Peek.is_line_terminator env)) && Peek.token env = T_LCURLY then let arg = Parser_common.reparse_arguments_as_match_argument env args in + let env = with_in_match_expression true env in Cover_expr (match_expression ~match_keyword_loc ~leading ~arg env) else (* It's actually a call expression of the form `match(...)` *) @@ -1392,6 +1500,15 @@ module Expression and match_expression env ~match_keyword_loc ~leading ~arg = let case env = let leading = Peek.comments env in + let case_match_root_loc = Peek.loc env |> Loc.start_loc in + let invalid_prefix_case = + if Peek.token env = T_CASE then ( + let loc = Peek.loc env in + Eat.token env; + Some loc + ) else + None + in let pattern = Parse.match_pattern env in let guard = if Eat.maybe env T_IF then ( @@ -1402,17 +1519,40 @@ module Expression ) else None in - (* Continue parsing colon until hermes-parser is also updated. *) - if not @@ Eat.maybe env T_COLON then Expect.token env T_ARROW; + let invalid_infix_colon = + if Peek.token env = T_COLON then ( + let loc = Peek.loc env in + Eat.token env; + Some loc + ) else ( + Expect.token env T_ARROW; + None + ) + in let body = assignment env in - (match Peek.token env with - | T_EOF - | T_RCURLY -> - () - | _ -> Expect.token env T_COMMA); + let invalid_suffix_semicolon = + match Peek.token env with + | T_EOF + | T_RCURLY -> + None + | T_SEMICOLON -> + let loc = Peek.loc env in + Eat.token env; + Some loc + | _ -> + Expect.token env T_COMMA; + None + in let trailing = Eat.trailing_comments env in let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - { Match.Case.pattern; body; guard; comments } + let invalid_syntax = + { + Match.Case.InvalidSyntax.invalid_prefix_case; + invalid_infix_colon; + invalid_suffix_semicolon; + } + in + { Match.Case.pattern; body; guard; comments; invalid_syntax; case_match_root_loc } in let rec case_list env acc = match Peek.token env with @@ -1441,9 +1581,10 @@ module Expression let rec template_parts env quasis expressions = let expr = Parse.expression env in let expressions = expr :: expressions in + let prev_lex_env = Parser_env.Peek.lex_env env in match Peek.token env with | T_RCURLY -> - Eat.push_lex_mode env Lex_mode.TEMPLATE; + Eat.rescan_as_template_from env prev_lex_env; let (loc, part, is_tail) = match Peek.token env with | T_TEMPLATE_PART (loc, cooked, raw, _, tail) -> @@ -1452,7 +1593,6 @@ module Expression (loc, { Element.value = { Element.cooked; raw }; tail }, tail) | _ -> assert false in - Eat.pop_lex_mode env; let quasis = (loc, part) :: quasis in if is_tail then (loc, List.rev quasis, List.rev expressions) @@ -1501,11 +1641,11 @@ module Expression } ) - and tagged_template env start_loc tag part = + and tagged_template ?targs env start_loc tag part = let tag = expression_remove_trailing env tag in let quasi = template_literal env part in ( Loc.btwn start_loc (fst quasi), - Expression.(TaggedTemplate TaggedTemplate.{ tag; quasi; comments = None }) + Expression.(TaggedTemplate TaggedTemplate.{ tag; targs; quasi; comments = None }) ) and group env = @@ -1614,6 +1754,8 @@ module Expression optional_member with OptionalMember.member = { member with Member.comments = merge_comments comments }; } + | Record ({ Record.comments; _ } as e) -> + Record { e with Record.comments = merge_comments comments } | Sequence ({ Sequence.comments; _ } as e) -> Sequence { e with Sequence.comments = merge_comments comments } | Super { Super.comments; _ } -> Super { Super.comments = merge_comments comments } @@ -1635,15 +1777,20 @@ module Expression ) and array_initializer = - let rec elements env (acc, errs) = + (* `trailing` tracks whether the most recently consumed token before the + eventual T_RBRACKET was a T_COMMA without a following element — i.e. + whether the source had a trailing comma like `[1, 2,]`. Reset to false + whenever we add an element (since the comma was followed by content); + set to true when we consume a comma whose next peek is T_RBRACKET. *) + let rec elements env (acc, errs, trailing) = match Peek.token env with | T_EOF | T_RBRACKET -> - (List.rev acc, Pattern_cover.rev_errors errs) + (List.rev acc, Pattern_cover.rev_errors errs, trailing) | T_COMMA -> let loc = Peek.loc env in Eat.token env; - elements env (Expression.Array.Hole loc :: acc, errs) + elements env (Expression.Array.Hole loc :: acc, errs, false) | T_ELLIPSIS -> let leading = Peek.comments env in let (loc, (argument, new_errs)) = @@ -1675,30 +1822,43 @@ module Expression else new_errs in - if not is_last then Expect.token env T_COMMA; + let trailing = + if not is_last then begin + Expect.token env T_COMMA; + Peek.token env = T_RBRACKET + end else + false + in let acc = elem :: acc in let errs = Pattern_cover.rev_append_errors new_errs errs in - elements env (acc, errs) + elements env (acc, errs, trailing) | _ -> let (elem, new_errs) = match assignment_cover env with | Cover_expr elem -> (elem, Pattern_cover.empty_errors) | Cover_patt (elem, new_errs) -> (elem, new_errs) in - if Peek.token env <> T_RBRACKET then Expect.token env T_COMMA; + let trailing = + if Peek.token env <> T_RBRACKET then begin + Expect.token env T_COMMA; + Peek.token env = T_RBRACKET + end else + false + in let acc = Expression.Array.Expression elem :: acc in let errs = Pattern_cover.rev_append_errors new_errs errs in - elements env (acc, errs) + elements env (acc, errs, trailing) in fun env -> let leading = Peek.comments env in Expect.token env T_LBRACKET; - let (elems, errs) = elements env ([], Pattern_cover.empty_errors) in + let (elems, errs, trailing_comma) = elements env ([], Pattern_cover.empty_errors, false) in let internal = Peek.comments env in Expect.token env T_RBRACKET; let trailing = Eat.trailing_comments env in ( { Ast.Expression.Array.elements = elems; + trailing_comma; comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal (); }, errs @@ -1721,6 +1881,12 @@ module Expression ("", "", "", []) in Eat.pop_lex_mode env; + (* Mirror upstream Hermes: invalid regex flags are not a parse error. + The literal is syntactically well-formed; flag validation is left to + the JS [RegExp] constructor at adapter time. We still keep the + [filtered_flags] computation because downstream tools (e.g. AST + printers) consume the recognized-only flags subset, but emit no + diagnostic when [flags <> raw_flags]. *) let filtered_flags = Buffer.create (String.length raw_flags) in String.iter (function @@ -1728,7 +1894,6 @@ module Expression | _ -> ()) raw_flags; let flags = Buffer.contents filtered_flags in - if flags <> raw_flags then error env (Parse_error.InvalidRegExpFlags raw_flags); let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in (loc, Expression.RegExpLiteral { Ast.RegExpLiteral.pattern; flags; raw; comments }) @@ -1792,18 +1957,19 @@ module Expression in let param = ( loc, - { - Ast.Function.Param.argument = - ( loc, - Pattern.Identifier - { - Pattern.Identifier.name; - annot = Ast.Type.Missing (Peek.loc_skip_lookahead env); - optional = false; - } - ); - default = None; - } + Ast.Function.Param.RegularParam + { + argument = + ( loc, + Pattern.Identifier + { + Pattern.Identifier.name; + annot = Ast.Type.Missing (Peek.loc_skip_lookahead env); + optional = false; + } + ); + default = None; + } ) in ( tparams, diff --git a/compiler/flow_parser/parser/file_key.ml b/compiler/flow_parser/parser/file_key.ml index fb51ba41177..318cac1bd18 100644 --- a/compiler/flow_parser/parser/file_key.ml +++ b/compiler/flow_parser/parser/file_key.ml @@ -5,6 +5,19 @@ * LICENSE file in the root directory of this source tree. *) +(* File_key.t stores relative path suffixes internally, following Hack's + Relative_path.t approach. The project root and flowlib root are set once + at startup via global refs. to_string returns the absolute path on demand. + + LibFile paths under the flowlib root use a marker prefix internally to + distinguish them from project-root LibFiles. This marker is stripped in + to_string/to_absolute and replaced with the flowlib root. + + IMPORTANT: The global root refs (project_root, flowlib_root) are plain + OCaml ref cells. This is safe with fork-based MultiWorkerLwt workers + (each gets a copy-on-write snapshot). If OCaml 5 domains are ever + introduced, these must become domain-local or atomic. *) + type t = | LibFile of string | SourceFile of string @@ -14,19 +27,167 @@ type t = | ResourceFile of string [@@deriving show, eq] -let to_string = function +(* Marker for flowlib LibFile paths. This is the single source of truth — + saved_state.ml uses File_key.flowlib_marker, not a separate constant. *) +let flowlib_marker = "///" + +(* Global root refs — set once at startup, before any File_key construction + or to_string calls. All roots have enforced trailing slashes so that + strip_prefix always splits at a directory boundary. + + Using [string option ref] (not [string ref]) so that "not yet initialized" + is distinguishable from "set to empty". Following Hack's Relative_path + convention: accessing an unset root raises an immediate, descriptive error + rather than silently producing a broken path. + + See set_project_root/set_flowlib_root for the setters. *) +let project_root = ref None + +let flowlib_root = ref None + +let enforce_trailing_sep ~dir_sep s = + let len = String.length s in + if len > 0 && s.[len - 1] <> '/' && s.[len - 1] <> '\\' then + s ^ dir_sep + else + s + +(* Normalize a path's directory separators to '/'. The dir_sep_char + parameter specifies which character to treat as a directory separator + (in addition to '/' which is always recognized). On Unix this is '/' + (making this a no-op); on Windows it is '\\'. *) +let normalize_dir_sep_with ~dir_sep_char s = + if Char.equal dir_sep_char '/' then + s + else if String.contains s dir_sep_char then + String.map + (fun c -> + if Char.equal c dir_sep_char then + '/' + else + c) + s + else + s + +let normalize_dir_sep = normalize_dir_sep_with ~dir_sep_char:Filename.dir_sep.[0] + +(* dirname that works with '/' on any platform. Filename.dirname is + platform-specific and won't split on '/' on Windows. *) +let dirname_slash s = + match String.rindex_opt s '/' with + | Some 0 -> "/" + | Some i -> String.sub s 0 i + | None -> s + +(* Resolve a relative suffix against a root directory, handling ".." and "." + segments. Same approach as Files.normalize_path, which can't be used + directly due to parser -> common circular dep. + ~dir_sep controls which character is treated as a directory separator + for normalization (defaults to the platform separator). *) +let resolve_root_with ~is_relative ?(dir_sep = Filename.dir_sep) get_root suffix = + let len = String.length suffix in + if len = 0 || suffix = "-" then + suffix + else if not (is_relative suffix) then + suffix + else + let normalized = normalize_dir_sep_with ~dir_sep_char:dir_sep.[0] suffix in + let parts = String.split_on_char '/' normalized in + let has_dots = List.exists (fun s -> s = "." || s = "..") parts in + if not has_dots then + (* No ".." or "." segments — simple concatenation preserves the + original separators, important for cross-platform round-trips. *) + get_root () ^ suffix + else + let rec resolve dir = function + | [] -> dir + | "." :: rest -> resolve dir rest + | ".." :: rest -> resolve (dirname_slash dir) rest + | remaining -> + let sep = + if String.length dir > 0 && dir.[String.length dir - 1] = '/' then + "" + else + "/" + in + dir ^ sep ^ String.concat "/" remaining + in + (* Normalize root separators so dirname_slash (which only splits on '/') + can traverse up correctly on Windows where roots use backslashes. *) + let root = normalize_dir_sep_with ~dir_sep_char:dir_sep.[0] (get_root ()) in + (* Strip trailing separator — get_root() returns roots with enforced + trailing slashes, but dirname_slash "/foo/" just strips the slash + instead of going up a directory. *) + let rlen = String.length root in + let root = + if rlen > 1 && root.[rlen - 1] = '/' then + String.sub root 0 (rlen - 1) + else + root + in + resolve root parts + +let enforce_trailing_slash s = enforce_trailing_sep ~dir_sep:Filename.dir_sep s + +let set_project_root root = project_root := Some (enforce_trailing_slash root) + +let set_flowlib_root root = flowlib_root := Some (enforce_trailing_slash root) + +(* Retrieve a root, crashing immediately if not set. Following Hack's + Relative_path convention: an unset root is a programmer error that + should be caught at the call site, not masked downstream. *) +let get_project_root () = + match !project_root with + | Some r -> r + | None -> failwith "File_key: project_root has not been set" + +let get_flowlib_root () = + match !flowlib_root with + | Some r -> r + | None -> failwith "File_key: flowlib_root has not been set" + +(* Returns the relative suffix — used internally for comparison and + SharedMem hashing. For flowlib LibFiles, includes the flowlib_marker + prefix. This is the zero-cost path for hot operations like compare. *) +let suffix = function | LibFile x | SourceFile x | JsonFile x | ResourceFile x -> x -let to_path = function - | LibFile x +(* Resolves a stored suffix back to an absolute path. + - Empty suffix ("") and the stdin sentinel ("-") are returned unchanged. + - Suffixes that are already absolute (Filename.is_relative returns false) + are returned unchanged. + - Relative suffixes (including those with leading ".." for out-of-root + files) are resolved against get_root () via resolve_root_with. *) +let resolve_root get_root suffix = + resolve_root_with ~is_relative:Filename.is_relative get_root suffix + +(* Returns the full absolute path — resolves the root on demand. + Allocates a new string via concatenation. Use suffix for hot paths + that don't need the absolute path (compare, SharedMem hashing). *) +let to_absolute = function + | LibFile x -> + if String.starts_with ~prefix:flowlib_marker x then + let marker_len = String.length flowlib_marker in + resolve_root get_flowlib_root (String.sub x marker_len (String.length x - marker_len)) + else + resolve_root get_project_root x | SourceFile x | JsonFile x | ResourceFile x -> - Ok x + resolve_root get_project_root x + +(* to_string returns the absolute path so all existing callers + (destructuring, file I/O, display, path manipulation) work correctly + without changes. For hot paths that only need identity/comparison, + use suffix instead to avoid the string allocation. *) +let to_string = to_absolute + +let to_path t = Ok (to_absolute t) let compare = (* libs, then source and json files at the same priority since JSON files are @@ -43,7 +204,7 @@ let compare = if k <> 0 then k else - String.compare (to_string a) (to_string b) + String.compare (suffix a) (suffix b) let compare_opt a b = match (a, b) with @@ -71,8 +232,80 @@ let exists f = function | ResourceFile filename -> f filename -let check_suffix filename suffix = exists (fun fn -> Filename.check_suffix fn suffix) filename +let check_suffix filename sfx = exists (fun fn -> Filename.check_suffix fn sfx) filename + +let chop_suffix filename sfx = map (fun fn -> Filename.chop_suffix fn sfx) filename + +let with_suffix filename sfx = map (fun fn -> fn ^ sfx) filename + +(* Strip a root prefix from an absolute path. The prefix must have a + trailing slash (enforced by set_project_root/set_flowlib_root) so that + stripping always occurs at a directory boundary — e.g., root "/data/foo/" + won't match "/data/foobar/file.js". *) +let strip_prefix prefix path = + let plen = String.length prefix in + if plen > 0 && String.starts_with ~prefix path then + String.sub path plen (String.length path - plen) + else + path + +(* Compute the relative path from [root] to [path]. Both must be absolute. + Normalizes directory separators to forward slashes, splits on '/', finds + the common prefix, adds ".." for each remaining root component, then + appends the remaining path components. *) +let relative_path_from ?(dir_sep = Filename.dir_sep) root path = + let split s = + String.split_on_char '/' (normalize_dir_sep_with ~dir_sep_char:dir_sep.[0] s) + |> List.filter (fun x -> x <> "") + in + let rec drop_common = function + | (r :: rs, p :: ps) when r = p -> drop_common (rs, ps) + | (rs, ps) -> (rs, ps) + in + let (remaining_root, remaining_path) = drop_common (split root, split path) in + let ups = List.map (fun _ -> "..") remaining_root in + String.concat "/" (ups @ remaining_path) + +(* Strip the project root from an absolute path. If the path is under the + root, the root prefix is stripped to produce a relative suffix. If the path + is NOT under the root (e.g., out-of-root includes like ../../arvr/js/), + compute a relative path from the root — this ensures all suffixes are + portable relative paths, which is critical for saved state portability + across machines with different root paths. + If the root has not been set yet, returns the path unchanged. + The result always uses '/' separators for cross-platform consistency + (saved state generated on Linux must work on Windows and vice versa). *) +let strip_project_root path = + match !project_root with + | Some root -> + if String.starts_with ~prefix:root path then + normalize_dir_sep + (String.sub path (String.length root) (String.length path - String.length root)) + else if not (Filename.is_relative path) then + relative_path_from root path + else + path + | None -> path + +(* Create File_key values from absolute paths, stripping the appropriate root. + Safe to call before roots are initialized — the full path is stored as the + suffix, and to_absolute handles absolute suffixes correctly. *) +let source_file_of_absolute path = SourceFile (strip_project_root path) + +let json_file_of_absolute path = JsonFile (strip_project_root path) + +let resource_file_of_absolute path = ResourceFile (strip_project_root path) + +let lib_file_of_absolute path = + match !flowlib_root with + | Some fl when String.length fl > 0 && String.starts_with ~prefix:fl path -> + LibFile (flowlib_marker ^ normalize_dir_sep (strip_prefix fl path)) + | _ -> LibFile (strip_project_root path) + +module For_tests = struct + let enforce_trailing_sep = enforce_trailing_sep -let chop_suffix filename suffix = map (fun fn -> Filename.chop_suffix fn suffix) filename + let resolve_root_with = resolve_root_with -let with_suffix filename suffix = map (fun fn -> fn ^ suffix) filename + let relative_path_from = relative_path_from +end diff --git a/compiler/flow_parser/parser/flow_ast.ml b/compiler/flow_parser/parser/flow_ast.ml index 373e4d85e53..76479e309c6 100644 --- a/compiler/flow_parser/parser/flow_ast.ml +++ b/compiler/flow_parser/parser/flow_ast.ml @@ -95,7 +95,6 @@ and ModuleRefLiteral : sig require_loc: 'M; def_loc_opt: 'M option; prefix_len: int; - legacy_interop: bool; raw: string; comments: ('M, unit) Syntax.t option; } @@ -110,6 +109,7 @@ and Variance : sig | Plus | Minus | Readonly + | Writeonly | In | Out | InOut @@ -166,11 +166,14 @@ and Type : sig module Param : sig type ('M, 'T) t = 'M * ('M, 'T) t' - and ('M, 'T) t' = { - name: ('M, 'T) Identifier.t option; - annot: ('M, 'T) Type.t; - optional: bool; - } + and ('M, 'T) t' = + | Anonymous of ('M, 'T) Type.t + | Labeled of { + name: ('M, 'T) Identifier.t; + annot: ('M, 'T) Type.t; + optional: bool; + } + | Destructuring of ('M, 'T) Pattern.t [@@deriving show] end @@ -215,7 +218,8 @@ and Type : sig } and ('M, 'T) return_annotation = - | TypeAnnotation of ('M, 'T) Type.t + | Missing of 'M + | Available of ('M, 'T) Type.t | TypeGuard of ('M, 'T) Type.TypeGuard.t [@@deriving show] end @@ -269,6 +273,7 @@ and Type : sig type ('M, 'T) t = | Unqualified of ('M, 'T) Identifier.t | Qualified of ('M, 'T) qualified + | ImportTypeAnnot of ('M, 'T) import_type and ('M, 'T) qualified = 'M * ('M, 'T) qualified' @@ -276,6 +281,13 @@ and Type : sig qualification: ('M, 'T) t; id: ('M, 'T) Identifier.t; } + + and ('M, 'T) import_type = 'T * 'M import_type' + + and 'M import_type' = { + argument: 'M * 'M StringLiteral.t; + comments: ('M, unit) Syntax.t option; + } [@@deriving show] end @@ -315,12 +327,16 @@ and Type : sig static: bool; proto: bool; _method: bool; + abstract: bool; + override: bool; variance: 'M Variance.t option; + ts_accessibility: 'M Class.TSAccessibility.t option; + init: ('M, 'T) Expression.t option; comments: ('M, unit) Syntax.t option; } and ('M, 'T) value = - | Init of ('M, 'T) Type.t + | Init of ('M, 'T) Type.t option | Get of ('M * ('M, 'T) Function.t) | Set of ('M * ('M, 'T) Function.t) [@@deriving show] @@ -343,6 +359,7 @@ and Type : sig value: ('M, 'T) Type.t; static: bool; variance: 'M Variance.t option; + optional: bool; comments: ('M, unit) Syntax.t option; } @@ -358,11 +375,18 @@ and Type : sig | NoOptionalFlag [@@deriving show] + type variance_op = + | Add + | Remove + [@@deriving show] + type ('M, 'T) t' = { key_tparam: ('M, 'T) Type.TypeParam.t; prop_type: ('M, 'T) Type.t; source_type: ('M, 'T) Type.t; + name_type: ('M, 'T) Type.t option; variance: 'M Variance.t option; + variance_op: variance_op option; optional: optional_flag; comments: ('M, unit) Syntax.t option; } @@ -395,6 +419,16 @@ and Type : sig [@@deriving show] end + module PrivateField : sig + type ('M, 'T) t = 'M * ('M, 'T) t' + + and ('M, 'T) t' = { + key: 'M PrivateName.t; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + type ('M, 'T) t = { exact: bool; (* Inexact indicates the presence of ... in the object. It is more @@ -416,6 +450,7 @@ and Type : sig | CallProperty of ('M, 'T) CallProperty.t | InternalSlot of ('M, 'T) InternalSlot.t | MappedType of ('M, 'T) MappedType.t + | PrivateField of ('M, 'T) PrivateField.t [@@deriving show] end @@ -441,6 +476,7 @@ and Type : sig type ('M, 'T) t = | Unqualified of ('M, 'T) Identifier.t | Qualified of ('M, 'T) qualified + | Import of ('M, 'T) Type.Generic.Identifier.import_type and ('M, 'T) qualified' = { qualification: ('M, 'T) t; @@ -490,6 +526,14 @@ and Type : sig [@@deriving show] end + module ConstructorType : sig + type ('M, 'T) t = { + abstract_: bool; + func: ('M, 'T) Function.t; + } + [@@deriving show] + end + module Tuple : sig module LabeledElement : sig type ('M, 'T) t = { @@ -501,6 +545,14 @@ and Type : sig [@@deriving show] end + module UnlabeledElement : sig + type ('M, 'T) t = { + annot: ('M, 'T) Type.t; + optional: bool; + } + [@@deriving show] + end + module SpreadElement : sig type ('M, 'T) t = { name: ('M, 'T) Identifier.t option; @@ -512,7 +564,7 @@ and Type : sig type ('M, 'T) element = 'M * ('M, 'T) element' [@@deriving show] and ('M, 'T) element' = - | UnlabeledElement of ('M, 'T) Type.t + | UnlabeledElement of ('M, 'T) UnlabeledElement.t | LabeledElement of ('M, 'T) LabeledElement.t | SpreadElement of ('M, 'T) SpreadElement.t [@@deriving show] @@ -549,6 +601,30 @@ and Type : sig [@@deriving show] end + module TemplateLiteral : sig + module Element : sig + type value = { + raw: string; + cooked: string; + } + + and 'M t = 'M * t' + + and t' = { + value: value; + tail: bool; + } + [@@deriving show] + end + + type ('M, 'T) t = { + quasis: 'M Element.t list; + types: ('M, 'T) Type.t list; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + type ('M, 'T) t = 'T * ('M, 'T) t' (* Yes, we could add a little complexity here to show that Any and Void @@ -590,9 +666,12 @@ and Type : sig | NumberLiteral of 'M NumberLiteral.t | BigIntLiteral of 'M BigIntLiteral.t | BooleanLiteral of 'M BooleanLiteral.t + | TemplateLiteral of ('M, 'T) TemplateLiteral.t | Unknown of ('M, unit) Syntax.t option | Never of ('M, unit) Syntax.t option | Undefined of ('M, unit) Syntax.t option + | UniqueSymbol of ('M, unit) Syntax.t option + | ConstructorType of ('M, 'T) ConstructorType.t (* Type.annotation is a concrete syntax node with a location that starts at * the colon and ends after the type. For example, "var a: number", the @@ -626,7 +705,7 @@ and Type : sig type ('M, 'T) t = 'M * ('M, 'T) t' and ('M, 'T) t' = { - name: ('M, 'M) Identifier.t; + name: ('M, 'T) Identifier.t; bound: ('M, 'T) Type.annotation_or_hint; bound_kind: bound_kind; variance: 'M Variance.t option; @@ -769,8 +848,11 @@ and Statement : sig type ('M, 'T) t = { id: ('M, 'T) Identifier.t; tparams: ('M, 'T) Type.TypeParams.t option; - impltype: ('M, 'T) Type.t option; - supertype: ('M, 'T) Type.t option; + impl_type: ('M, 'T) Type.t option; + lower_bound: ('M, 'T) Type.t option; + upper_bound: ('M, 'T) Type.t option; + legacy_upper_bound: ('M, 'T) Type.t option; + (** Invariant: only one of legacy_upper_bound and upper_bound can exist. *) comments: ('M, unit) Syntax.t option; } [@@deriving show] @@ -784,6 +866,7 @@ and Statement : sig and ('M, 'T) t' = { test: ('M, 'T) Expression.t option; + case_test_loc: 'M option; consequent: ('M, 'T) Statement.t list; comments: ('M, unit) Syntax.t option; } @@ -920,70 +1003,48 @@ and Statement : sig end module EnumDeclaration : sig + type 'M member_name = + | Identifier of ('M, 'M) Identifier.t + | StringLiteral of ('M * 'M StringLiteral.t) + [@@deriving show] + module DefaultedMember : sig type 'M t = 'M * 'M t' - and 'M t' = { id: ('M, 'M) Identifier.t } [@@deriving show] + and 'M t' = { id: 'M member_name } [@@deriving show] end module InitializedMember : sig type ('I, 'M) t = 'M * ('I, 'M) t' and ('I, 'M) t' = { - id: ('M, 'M) Identifier.t; + id: 'M member_name; init: 'M * 'I; } [@@deriving show] end - module BooleanBody : sig - type 'M t = { - members: ('M BooleanLiteral.t, 'M) InitializedMember.t list; - explicit_type: bool; - has_unknown_members: bool; - comments: ('M, 'M Comment.t list) Syntax.t option; - } - [@@deriving show] - end - - module NumberBody : sig - type 'M t = { - members: ('M NumberLiteral.t, 'M) InitializedMember.t list; - explicit_type: bool; - has_unknown_members: bool; - comments: ('M, 'M Comment.t list) Syntax.t option; - } - [@@deriving show] - end - - module StringBody : sig - type 'M t = { - members: ('M StringLiteral.t, 'M) members; - explicit_type: bool; - has_unknown_members: bool; - comments: ('M, 'M Comment.t list) Syntax.t option; - } - - and ('I, 'M) members = - | Defaulted of 'M DefaultedMember.t list - | Initialized of ('I, 'M) InitializedMember.t list - [@@deriving show] - end + type explicit_type = + | Boolean + | Number + | String + | Symbol + | BigInt + [@@deriving ord, show] - module SymbolBody : sig - type 'M t = { - members: 'M DefaultedMember.t list; - has_unknown_members: bool; - comments: ('M, 'M Comment.t list) Syntax.t option; - } - [@@deriving show] - end + type 'M member = + | BooleanMember of ('M BooleanLiteral.t, 'M) InitializedMember.t + | NumberMember of ('M NumberLiteral.t, 'M) InitializedMember.t + | StringMember of ('M StringLiteral.t, 'M) InitializedMember.t + | BigIntMember of ('M BigIntLiteral.t, 'M) InitializedMember.t + | DefaultedMember of 'M DefaultedMember.t + [@@deriving show] - module BigIntBody : sig + module Body : sig type 'M t = { - members: ('M BigIntLiteral.t, 'M) InitializedMember.t list; - explicit_type: bool; - has_unknown_members: bool; + members: 'M member list; + explicit_type: ('M * explicit_type) option; + has_unknown_members: 'M option; comments: ('M, 'M Comment.t list) Syntax.t option; } [@@deriving show] @@ -992,18 +1053,11 @@ and Statement : sig type ('M, 'T) t = { id: ('M, 'T) Identifier.t; body: 'M body; + const_: bool; comments: ('M, unit) Syntax.t option; } - and 'M body = 'M * 'M body' - - and 'M body' = - | BooleanBody of 'M BooleanBody.t - | NumberBody of 'M NumberBody.t - | StringBody of 'M StringBody.t - | SymbolBody of 'M SymbolBody.t - | BigIntBody of 'M BigIntBody.t - [@@deriving show] + and 'M body = 'M * 'M Body.t [@@deriving show] end module ComponentDeclaration : sig @@ -1051,7 +1105,8 @@ and Statement : sig tparams: ('M, 'T) Type.TypeParams.t option; params: ('M, 'T) Params.t; renders: ('M, 'T) Type.component_renders_annotation; - body: 'M * ('M, 'T) Statement.Block.t; + body: ('M * ('M, 'T) Statement.Block.t) option; + async: bool; comments: ('M, unit) Syntax.t option; (* Location of the signature portion of a component, e.g. * component Foo(): void {} @@ -1073,14 +1128,89 @@ and Statement : sig [@@deriving show] end + module RecordDeclaration : sig + module InvalidPropertySyntax : sig + type 'M t = { + invalid_variance: 'M Variance.t option; + invalid_optional: 'M option; + invalid_suffix_semicolon: 'M option; + } + [@@deriving show] + end + + module Property : sig + type ('M, 'T) t = 'T * ('M, 'T) t' + + and ('M, 'T) t' = { + key: ('M, 'T) Expression.Object.Property.key; + annot: ('M, 'T) Type.annotation; + default_value: ('M, 'T) Expression.t option; + comments: ('M, unit) Syntax.t option; + invalid_syntax: 'M InvalidPropertySyntax.t option; + } + [@@deriving show] + end + + module StaticProperty : sig + type ('M, 'T) t = 'T * ('M, 'T) t' + + and ('M, 'T) t' = { + key: ('M, 'T) Expression.Object.Property.key; + annot: ('M, 'T) Type.annotation; + value: ('M, 'T) Expression.t; + comments: ('M, unit) Syntax.t option; + invalid_syntax: 'M InvalidPropertySyntax.t option; + } + [@@deriving show] + end + + module Body : sig + type ('M, 'T) t = 'M * ('M, 'T) t' + + and ('M, 'T) t' = { + body: ('M, 'T) element list; + comments: ('M, unit) Syntax.t option; + } + + and ('M, 'T) element = + | Method of ('M, 'T) Class.Method.t + | Property of ('M, 'T) Property.t + | StaticProperty of ('M, 'T) StaticProperty.t + [@@deriving show] + end + + module InvalidSyntax : sig + type 'M t = { invalid_infix_equals: 'M option } [@@deriving show] + end + + type ('M, 'T) t = { + id: ('M, 'T) Identifier.t; + tparams: ('M, 'T) Type.TypeParams.t option; + implements: ('M, 'T) Class.Implements.t option; + body: ('M, 'T) Body.t; + comments: ('M, unit) Syntax.t option; + invalid_syntax: 'M InvalidSyntax.t option; + } + [@@deriving show] + end + module DeclareClass : sig + type ('M, 'T) extends = + | ExtendsIdent of ('M, 'T) Type.Generic.t + | ExtendsCall of { + callee: 'M * ('M, 'T) Type.Generic.t; + arg: 'M * ('M, 'T) extends; + } + [@@deriving show] + type ('M, 'T) t = { id: ('M, 'T) Identifier.t; tparams: ('M, 'T) Type.TypeParams.t option; body: 'M * ('M, 'T) Type.Object.t; - extends: ('M * ('M, 'T) Type.Generic.t) option; + extends: ('M * ('M, 'T) extends) option; mixins: ('M * ('M, 'T) Type.Generic.t) list; implements: ('M, 'T) Class.Implements.t option; + abstract: bool; comments: ('M, unit) Syntax.t option; } [@@deriving show] @@ -1090,7 +1220,7 @@ and Statement : sig type ('M, 'T) t = { id: ('M, 'T) Identifier.t; tparams: ('M, 'T) Type.TypeParams.t option; - params: ('M, 'T) Type.Component.Params.t; + params: ('M, 'T) ComponentDeclaration.Params.t; renders: ('M, 'T) Type.component_renders_annotation; comments: ('M, unit) Syntax.t option; } @@ -1099,8 +1229,7 @@ and Statement : sig module DeclareVariable : sig type ('M, 'T) t = { - id: ('M, 'T) Identifier.t; - annot: ('M, 'T) Type.annotation; + declarations: ('M, 'T) VariableDeclaration.Declarator.t list; kind: Variable.kind; comments: ('M, unit) Syntax.t option; } @@ -1109,10 +1238,11 @@ and Statement : sig module DeclareFunction : sig type ('M, 'T) t = { - id: ('M, 'T) Identifier.t; + id: ('M, 'T) Identifier.t option; annot: ('M, 'T) Type.annotation; predicate: ('M, 'T) Type.Predicate.t option; comments: ('M, unit) Syntax.t option; + implicit_declare: bool; } [@@deriving show] end @@ -1139,6 +1269,11 @@ and Statement : sig end module DeclareNamespace : sig + type keyword = + | Namespace + | Module + [@@deriving show] + type ('M, 'T) id = | Global of ('M, 'M) Identifier.t | Local of ('M, 'T) Identifier.t @@ -1148,6 +1283,29 @@ and Statement : sig id: ('M, 'T) id; body: 'M * ('M, 'T) Block.t; comments: ('M, unit) Syntax.t option; + implicit_declare: bool; + keyword: keyword; + } + [@@deriving show] + end + + module ExportAssignment : sig + type ('M, 'T) rhs = + | Expression of ('M, 'T) Expression.t + | DeclareFunction of ('M * ('M, 'T) DeclareFunction.t) + [@@deriving show] + + type ('M, 'T) t = { + rhs: ('M, 'T) rhs; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + + module NamespaceExportDeclaration : sig + type ('M, 'T) t = { + id: ('M, 'T) Identifier.t; + comments: ('M, unit) Syntax.t option; } [@@deriving show] end @@ -1159,6 +1317,7 @@ and Statement : sig and ('M, 'T) t' = { local: ('M, 'T) Identifier.t; exported: ('M, 'T) Identifier.t option; + export_kind: Statement.export_kind; from_remote: bool; (* Imported name's definition location. It will be populated only in typed AST for `export {foo} from '...'`. *) imported_name_def_loc: 'M option; @@ -1219,6 +1378,8 @@ and Statement : sig | Interface of ('M * ('M, 'T) Interface.t) (* declare export enum *) | Enum of ('M * ('M, 'T) EnumDeclaration.t) + (* declare export namespace *) + | Namespace of ('M * ('M, 'T) DeclareNamespace.t) and ('M, 'T) t = { default: 'M option; @@ -1242,6 +1403,11 @@ and Statement : sig and ('M, 'T) named_specifier = { kind: import_kind option; + (* Location of the per-specifier `type`/`typeof` keyword when [kind] is + [Some]; [None] when [kind] is [None]. Preserved so downstream + consumers (ESTree/Babel adapters, codemods) can span the full + specifier syntax including the keyword without rescanning source. *) + kind_loc: 'M option; local: ('M, 'T) Identifier.t option; remote: ('M, 'T) Identifier.t; (* Remote name's definition location. It will be populated only in typed AST. *) @@ -1254,11 +1420,37 @@ and Statement : sig remote_default_name_def_loc: 'M option; } + and ('M, 'T) import_attribute = { + loc: 'M; + key: ('M, 'T) import_attribute_key; + value: 'T * 'M StringLiteral.t; + } + + and ('M, 'T) import_attribute_key = + | Identifier of ('M, 'T) Identifier.t + | StringLiteral of 'M * 'M StringLiteral.t + and ('M, 'T) t = { import_kind: import_kind; source: 'T * 'M StringLiteral.t; default: ('M, 'T) default_identifier option; specifiers: ('M, 'T) specifier option; + attributes: ('M * ('M, 'T) import_attribute list) option; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + + module ImportEqualsDeclaration : sig + type ('M, 'T) module_reference = + | ExternalModuleReference of ('T * 'M StringLiteral.t) + | Identifier of ('M, 'T) Type.Generic.Identifier.t + + and ('M, 'T) t = { + id: ('M, 'T) Identifier.t; + module_reference: ('M, 'T) module_reference; + import_kind: ImportDeclaration.import_kind; + is_export: bool; comments: ('M, unit) Syntax.t option; } [@@deriving show] @@ -1307,6 +1499,8 @@ and Statement : sig | EnumDeclaration of ('M, 'T) EnumDeclaration.t | ExportDefaultDeclaration of ('M, 'T) ExportDefaultDeclaration.t | ExportNamedDeclaration of ('M, 'T) ExportNamedDeclaration.t + | ExportAssignment of ('M, 'T) ExportAssignment.t + | NamespaceExportDeclaration of ('M, 'T) NamespaceExportDeclaration.t | Expression of ('M, 'T) Expression.t | For of ('M, 'T) For.t | ForIn of ('M, 'T) ForIn.t @@ -1314,9 +1508,11 @@ and Statement : sig | FunctionDeclaration of ('M, 'T) Function.t | If of ('M, 'T) If.t | ImportDeclaration of ('M, 'T) ImportDeclaration.t + | ImportEqualsDeclaration of ('M, 'T) ImportEqualsDeclaration.t | InterfaceDeclaration of ('M, 'T) Interface.t | Labeled of ('M, 'T) Labeled.t | Match of ('M, 'T) match_statement + | RecordDeclaration of ('M, 'T) RecordDeclaration.t | Return of ('M, 'T) Return.t | Switch of ('M, 'T) Switch.t | Throw of ('M, 'T) Throw.t @@ -1373,6 +1569,7 @@ and Expression : sig type ('M, 'T) t = { elements: ('M, 'T) element list; + trailing_comma: bool; comments: ('M, 'M Comment.t list) Syntax.t option; } [@@deriving show] @@ -1405,6 +1602,7 @@ and Expression : sig module TaggedTemplate : sig type ('M, 'T) t = { tag: ('M, 'T) Expression.t; + targs: ('M, 'T) Expression.CallTypeArgs.t option; quasi: 'M * ('M, 'T) TemplateLiteral.t; comments: ('M, unit) Syntax.t option; } @@ -1467,6 +1665,16 @@ and Expression : sig [@@deriving show] end + module Record : sig + type ('M, 'T) t = { + constructor: ('M, 'T) Expression.t; + targs: ('M, 'T) Expression.CallTypeArgs.t option; + properties: 'M * ('M, 'T) Object.t; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + module Sequence : sig type ('M, 'T) t = { expressions: ('M, 'T) Expression.t list; @@ -1485,6 +1693,7 @@ and Expression : sig | Void | Delete | Await + | Nonnull and ('M, 'T) t = { operator: operator; @@ -1630,10 +1839,16 @@ and Expression : sig end module OptionalCall : sig + type kind = + | Optional + | NonOptional + | AssertNonnull + [@@deriving show] + type ('M, 'T) t = { call: ('M, 'T) Call.t; filtered_out: 'T; - optional: bool; + optional: kind; } [@@deriving show] end @@ -1653,10 +1868,16 @@ and Expression : sig end module OptionalMember : sig + type kind = + | Optional + | NonOptional + | AssertNonnull + [@@deriving show] + type ('M, 'T) t = { member: ('M, 'T) Member.t; filtered_out: 'T; - optional: bool; + optional: kind; } [@@deriving show] end @@ -1726,6 +1947,7 @@ and Expression : sig module Import : sig type ('M, 'T) t = { argument: ('M, 'T) Expression.t; + options: ('M, 'T) Expression.t option; comments: ('M, unit) Syntax.t option; } [@@deriving show] @@ -1765,6 +1987,7 @@ and Expression : sig | Object of ('M, 'T) Object.t | OptionalCall of ('M, 'T) OptionalCall.t | OptionalMember of ('M, 'T) OptionalMember.t + | Record of ('M, 'T) Record.t | Sequence of ('M, 'T) Sequence.t | Super of 'M Super.t | TaggedTemplate of ('M, 'T) TaggedTemplate.t @@ -1926,6 +2149,15 @@ end = and Match : sig module Case : sig + module InvalidSyntax : sig + type 'M t = { + invalid_prefix_case: 'M option; + invalid_infix_colon: 'M option; + invalid_suffix_semicolon: 'M option; + } + [@@deriving show] + end + type ('M, 'T, 'B) t = 'M * ('M, 'T, 'B) t' and ('M, 'T, 'B) t' = { @@ -1933,6 +2165,8 @@ and Match : sig body: 'B; guard: ('M, 'T) Expression.t option; comments: ('M, unit) Syntax.t option; + invalid_syntax: 'M InvalidSyntax.t; + case_match_root_loc: 'M; } [@@deriving show] end @@ -1940,9 +2174,7 @@ and Match : sig type ('M, 'T, 'B) t = { arg: ('M, 'T) Expression.t; cases: ('M, 'T, 'B) Case.t list; - (* The type here is used to store the resulting type after the patterns - refine the arg type. *) - match_keyword_loc: 'T; + match_keyword_loc: 'M; comments: ('M, unit) Syntax.t option; } [@@deriving show] @@ -2056,6 +2288,19 @@ and MatchPattern : sig [@@deriving show] end + module InstancePattern : sig + type ('M, 'T) constructor = + | IdentifierConstructor of ('M, 'T) Identifier.t + | MemberConstructor of ('M, 'T) MemberPattern.t + + and ('M, 'T) t = { + constructor: ('M, 'T) constructor; + properties: 'M * ('M, 'T) ObjectPattern.t; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + module OrPattern : sig type ('M, 'T) t = { patterns: ('M, 'T) MatchPattern.t list; @@ -2077,10 +2322,18 @@ and MatchPattern : sig [@@deriving show] end + module WildcardPattern : sig + type 'M t = { + comments: ('M, unit) Syntax.t option; + invalid_syntax_default_keyword: bool; + } + [@@deriving show] + end + type ('M, 'T) t = 'M * ('M, 'T) t' and ('M, 'T) t' = - | WildcardPattern of ('M, unit) Syntax.t option + | WildcardPattern of 'M WildcardPattern.t | NumberPattern of 'M NumberLiteral.t | BigIntPattern of 'M BigIntLiteral.t | StringPattern of 'M StringLiteral.t @@ -2092,6 +2345,7 @@ and MatchPattern : sig | MemberPattern of ('M, 'T) MemberPattern.t | ObjectPattern of ('M, 'T) ObjectPattern.t | ArrayPattern of ('M, 'T) ArrayPattern.t + | InstancePattern of ('M, 'T) InstancePattern.t | OrPattern of ('M, 'T) OrPattern.t | AsPattern of ('M, 'T) AsPattern.t [@@deriving show] @@ -2136,6 +2390,7 @@ and Pattern : sig and ('M, 'T) t = { properties: ('M, 'T) property list; annot: ('M, 'T) Type.annotation_or_hint; + optional: bool; comments: ('M, 'M Comment.t list) Syntax.t option; } [@@deriving show] @@ -2160,6 +2415,7 @@ and Pattern : sig and ('M, 'T) t = { elements: ('M, 'T) element list; annot: ('M, 'T) Type.annotation_or_hint; + optional: bool; comments: ('M, 'M Comment.t list) Syntax.t option; } [@@deriving show] @@ -2202,6 +2458,21 @@ end = Comment and Class : sig + module TSAccessibility : sig + type kind = + | Public + | Protected + | Private + + and 'M t = 'M * 'M t' + + and 'M t' = { + kind: kind; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + module Method : sig type ('M, 'T) t = 'T * ('M, 'T) t' @@ -2216,12 +2487,56 @@ and Class : sig key: ('M, 'T) Expression.Object.Property.key; value: 'M * ('M, 'T) Function.t; static: bool; + override: bool; + ts_accessibility: 'M TSAccessibility.t option; decorators: ('M, 'T) Class.Decorator.t list; comments: ('M, unit) Syntax.t option; } [@@deriving show] end + module DeclareMethod : sig + type ('M, 'T) t = 'T * ('M, 'T) t' + + and ('M, 'T) t' = { + kind: Method.kind; + key: ('M, 'T) Expression.Object.Property.key; + annot: ('M, 'T) Type.annotation; + static: bool; + override: bool; + optional: bool; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + + module AbstractMethod : sig + type ('M, 'T) t = 'T * ('M, 'T) t' + + and ('M, 'T) t' = { + key: ('M, 'T) Expression.Object.Property.key; + annot: 'M * ('M, 'T) Type.Function.t; + override: bool; + ts_accessibility: 'M TSAccessibility.t option; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + + module AbstractProperty : sig + type ('M, 'T) t = 'T * ('M, 'T) t' + + and ('M, 'T) t' = { + key: ('M, 'T) Expression.Object.Property.key; + annot: ('M, 'T) Type.annotation_or_hint; + override: bool; + ts_accessibility: 'M TSAccessibility.t option; + variance: 'M Variance.t option; + comments: ('M, unit) Syntax.t option; + } + [@@deriving show] + end + module Property : sig type ('M, 'T) t = 'T * ('M, 'T) t' @@ -2230,7 +2545,10 @@ and Class : sig value: ('M, 'T) value; annot: ('M, 'T) Type.annotation_or_hint; static: bool; + override: bool; + optional: bool; variance: 'M Variance.t option; + ts_accessibility: 'M TSAccessibility.t option; decorators: ('M, 'T) Class.Decorator.t list; comments: ('M, unit) Syntax.t option; } @@ -2250,13 +2568,26 @@ and Class : sig value: ('M, 'T) Class.Property.value; annot: ('M, 'T) Type.annotation_or_hint; static: bool; + override: bool; + optional: bool; variance: 'M Variance.t option; + ts_accessibility: 'M TSAccessibility.t option; decorators: ('M, 'T) Class.Decorator.t list; comments: ('M, unit) Syntax.t option; } [@@deriving show] end + module StaticBlock : sig + type ('M, 'T) t = 'M * ('M, 'T) t' + + and ('M, 'T) t' = { + body: ('M, 'T) Statement.t list; + comments: ('M, 'M Comment.t list) Syntax.t option; + } + [@@deriving show] + end + module Extends : sig type ('M, 'T) t = 'M * ('M, 'T) t' @@ -2273,7 +2604,7 @@ and Class : sig type ('M, 'T) t = 'M * ('M, 'T) t' and ('M, 'T) t' = { - id: ('M, 'T) Identifier.t; + id: ('M, 'T) Type.Generic.Identifier.t; targs: ('M, 'T) Type.TypeArgs.t option; } [@@deriving show] @@ -2300,6 +2631,11 @@ and Class : sig | Method of ('M, 'T) Method.t | Property of ('M, 'T) Property.t | PrivateField of ('M, 'T) PrivateField.t + | StaticBlock of ('M, 'T) StaticBlock.t + | DeclareMethod of ('M, 'T) DeclareMethod.t + | AbstractMethod of ('M, 'T) AbstractMethod.t + | AbstractProperty of ('M, 'T) AbstractProperty.t + | IndexSignature of ('M, 'T) Type.Object.Indexer.t [@@deriving show] end @@ -2320,6 +2656,7 @@ and Class : sig extends: ('M, 'T) Extends.t option; implements: ('M, 'T) Implements.t option; class_decorators: ('M, 'T) Decorator.t list; + abstract: bool; comments: ('M, unit) Syntax.t option; } [@@deriving show] @@ -2340,10 +2677,12 @@ and Function : sig module Param : sig type ('M, 'T) t = 'M * ('M, 'T) t' - and ('M, 'T) t' = { - argument: ('M, 'T) Pattern.t; - default: ('M, 'T) Expression.t option; - } + and ('M, 'T) t' = + | RegularParam of { + argument: ('M, 'T) Pattern.t; + default: ('M, 'T) Expression.t option; + } + | ParamProperty of ('M, 'T) Class.Property.t' [@@deriving show] end @@ -2380,8 +2719,6 @@ and Function : sig type effect_ = | Hook | Arbitrary - | Idempotent - | Parametric of int [@@deriving show] type ('M, 'T) t = { diff --git a/compiler/flow_parser/parser/flow_ast_mapper.ml b/compiler/flow_parser/parser/flow_ast_mapper.ml index 9fd711268cd..79190c77b47 100644 --- a/compiler/flow_parser/parser/flow_ast_mapper.ml +++ b/compiler/flow_parser/parser/flow_ast_mapper.ml @@ -92,6 +92,7 @@ type type_params_context = | FunctionTypeTP | InferTP | ObjectMappedTypeTP + | RecordTP class ['loc] mapper = object (this) @@ -167,6 +168,12 @@ class ['loc] mapper = id_loc this#export_named_declaration loc decl stmt (fun decl -> (loc, ExportNamedDeclaration decl) ) + | (loc, ExportAssignment assign) -> + id_loc this#export_assignment loc assign stmt (fun assign -> (loc, ExportAssignment assign)) + | (loc, NamespaceExportDeclaration decl) -> + id_loc this#namespace_export_declaration loc decl stmt (fun decl -> + (loc, NamespaceExportDeclaration decl) + ) | (loc, Expression expr) -> id_loc this#expression_statement loc expr stmt (fun expr -> (loc, Expression expr)) | (loc, For for_stmt) -> @@ -181,6 +188,10 @@ class ['loc] mapper = id_loc this#if_statement loc if_stmt stmt (fun if_stmt -> (loc, If if_stmt)) | (loc, ImportDeclaration decl) -> id_loc this#import_declaration loc decl stmt (fun decl -> (loc, ImportDeclaration decl)) + | (loc, ImportEqualsDeclaration decl) -> + id_loc this#import_equals_declaration loc decl stmt (fun decl -> + (loc, ImportEqualsDeclaration decl) + ) | (loc, InterfaceDeclaration stuff) -> id_loc this#interface_declaration loc stuff stmt (fun stuff -> (loc, InterfaceDeclaration stuff) @@ -190,6 +201,10 @@ class ['loc] mapper = | (loc, Match x) -> id_loc this#match_statement loc x stmt (fun x -> (loc, Match x)) | (loc, OpaqueType otype) -> id_loc this#opaque_type loc otype stmt (fun otype -> (loc, OpaqueType otype)) + | (loc, RecordDeclaration record) -> + id_loc this#record_declaration loc record stmt (fun record -> + (loc, RecordDeclaration record) + ) | (loc, Return ret) -> id_loc this#return loc ret stmt (fun ret -> (loc, Return ret)) | (loc, Switch switch) -> id_loc this#switch loc switch stmt (fun switch -> (loc, Switch switch)) @@ -238,22 +253,17 @@ class ['loc] mapper = | (loc, Conditional x) -> id_loc this#conditional loc x expr (fun x -> (loc, Conditional x)) | (loc, Function x) -> id_loc this#function_expression loc x expr (fun x -> (loc, Function x)) | (loc, Identifier x) -> id this#identifier x expr (fun x -> (loc, Identifier x)) - | (loc, Import x) -> id (this#import loc) x expr (fun x -> (loc, Import x)) + | (loc, Import x) -> id this#import x expr (fun x -> (loc, Import x)) | (loc, JSXElement x) -> id_loc this#jsx_element loc x expr (fun x -> (loc, JSXElement x)) | (loc, JSXFragment x) -> id_loc this#jsx_fragment loc x expr (fun x -> (loc, JSXFragment x)) - | (loc, StringLiteral x) -> - id_loc this#string_literal loc x expr (fun x -> (loc, StringLiteral x)) - | (loc, BooleanLiteral x) -> - id_loc this#boolean_literal loc x expr (fun x -> (loc, BooleanLiteral x)) - | (loc, NullLiteral x) -> id_loc this#null_literal loc x expr (fun x -> (loc, NullLiteral x)) - | (loc, NumberLiteral x) -> - id_loc this#number_literal loc x expr (fun x -> (loc, NumberLiteral x)) - | (loc, BigIntLiteral x) -> - id_loc this#bigint_literal loc x expr (fun x -> (loc, BigIntLiteral x)) - | (loc, RegExpLiteral x) -> - id_loc this#regexp_literal loc x expr (fun x -> (loc, RegExpLiteral x)) + | (loc, StringLiteral x) -> id this#string_literal x expr (fun x -> (loc, StringLiteral x)) + | (loc, BooleanLiteral x) -> id this#boolean_literal x expr (fun x -> (loc, BooleanLiteral x)) + | (loc, NullLiteral x) -> id this#null_literal x expr (fun x -> (loc, NullLiteral x)) + | (loc, NumberLiteral x) -> id this#number_literal x expr (fun x -> (loc, NumberLiteral x)) + | (loc, BigIntLiteral x) -> id this#bigint_literal x expr (fun x -> (loc, BigIntLiteral x)) + | (loc, RegExpLiteral x) -> id this#regexp_literal x expr (fun x -> (loc, RegExpLiteral x)) | (loc, ModuleRefLiteral x) -> - id_loc this#module_ref_literal loc x expr (fun x -> (loc, ModuleRefLiteral x)) + id this#module_ref_literal x expr (fun x -> (loc, ModuleRefLiteral x)) | (loc, Logical x) -> id_loc this#logical loc x expr (fun x -> (loc, Logical x)) | (loc, Match x) -> id_loc this#match_expression loc x expr (fun x -> (loc, Match x)) | (loc, Member x) -> id_loc this#member loc x expr (fun x -> (loc, Member x)) @@ -264,6 +274,7 @@ class ['loc] mapper = | (loc, OptionalCall x) -> id (this#optional_call loc) x expr (fun x -> (loc, OptionalCall x)) | (loc, OptionalMember x) -> id_loc this#optional_member loc x expr (fun x -> (loc, OptionalMember x)) + | (loc, Record x) -> id_loc this#record loc x expr (fun x -> (loc, Record x)) | (loc, Sequence x) -> id_loc this#sequence loc x expr (fun x -> (loc, Sequence x)) | (loc, Super x) -> id_loc this#super_expression loc x expr (fun x -> (loc, Super x)) | (loc, TaggedTemplate x) -> @@ -279,13 +290,13 @@ class ['loc] mapper = method array _loc (expr : ('loc, 'loc) Ast.Expression.Array.t) = let open Ast.Expression in - let { Array.elements; comments } = expr in + let { Array.elements; trailing_comma; comments } = expr in let elements' = map_list this#array_element elements in let comments' = this#syntax_opt comments in if elements == elements' && comments == comments' then expr else - { Array.elements = elements'; comments = comments' } + { Array.elements = elements'; trailing_comma; comments = comments' } method array_element element = let open Ast.Expression.Array in @@ -436,7 +447,7 @@ class ['loc] mapper = method class_ _loc (cls : ('loc, 'loc) Ast.Class.t) = let open Ast.Class in - let { id; body; tparams; extends; implements; class_decorators; comments } = cls in + let { id; body; tparams; extends; implements; class_decorators; abstract; comments } = cls in let id' = map_opt this#class_identifier id in let tparams' = map_opt (this#type_params ~kind:ClassTP) tparams in let body' = this#class_body body in @@ -461,8 +472,9 @@ class ['loc] mapper = extends = extends'; implements = implements'; class_decorators = class_decorators'; - comments = comments'; + abstract; tparams = tparams'; + comments = comments'; } method class_extends _loc (extends : ('loc, 'loc) Ast.Class.Extends.t') = @@ -507,6 +519,25 @@ class ['loc] mapper = id_loc this#class_property loc prop elem (fun prop -> Property (loc, prop)) | PrivateField (loc, field) -> id_loc this#class_private_field loc field elem (fun field -> PrivateField (loc, field)) + | StaticBlock (loc, block) -> + id_loc this#class_static_block loc block elem (fun block -> StaticBlock (loc, block)) + | DeclareMethod (loc, decl_meth) -> + id_loc this#class_declare_method loc decl_meth elem (fun decl_meth -> + DeclareMethod (loc, decl_meth) + ) + | AbstractMethod (loc, abs_meth) -> + id_loc this#class_abstract_method loc abs_meth elem (fun abs_meth -> + AbstractMethod (loc, abs_meth) + ) + | AbstractProperty (loc, abs_prop) -> + id_loc this#class_abstract_property loc abs_prop elem (fun abs_prop -> + AbstractProperty (loc, abs_prop) + ) + | IndexSignature indexer -> + id this#class_indexer indexer elem (fun indexer -> IndexSignature indexer) + + method class_indexer (indexer : ('loc, 'loc) Ast.Type.Object.Indexer.t) = + this#object_indexer_property_type indexer method class_implements (implements : ('loc, 'loc) Ast.Class.Implements.t) = let open Ast.Class.Implements in @@ -521,7 +552,7 @@ class ['loc] mapper = method class_implements_interface (interface : ('loc, 'loc) Ast.Class.Implements.Interface.t) = let open Ast.Class.Implements.Interface in let (loc, { id; targs }) = interface in - let id' = this#type_identifier_reference id in + let id' = this#generic_identifier_type id in let targs' = map_opt this#type_args targs in if id == id' && targs == targs' then interface @@ -530,7 +561,18 @@ class ['loc] mapper = method class_method _loc (meth : ('loc, 'loc) Ast.Class.Method.t') = let open Ast.Class.Method in - let { kind = _; key; value; static = _; decorators; comments } = meth in + let { + kind = _; + key; + value; + static = _; + override = _; + ts_accessibility = _; + decorators; + comments; + } = + meth + in let key' = this#object_key key in let value' = map_loc this#function_expression_or_method value in let decorators' = map_list this#class_decorator decorators in @@ -540,9 +582,58 @@ class ['loc] mapper = else { meth with key = key'; value = value'; decorators = decorators'; comments = comments' } + method class_declare_method _loc (decl_meth : ('loc, 'loc) Ast.Class.DeclareMethod.t') = + let open Ast.Class.DeclareMethod in + let { kind = _; key; annot; static = _; override = _; optional = _; comments } = decl_meth in + let key' = this#object_key key in + let annot' = this#type_annotation annot in + let comments' = this#syntax_opt comments in + if key == key' && annot == annot' && comments == comments' then + decl_meth + else + { decl_meth with key = key'; annot = annot'; comments = comments' } + + method class_abstract_method _loc (abs_meth : ('loc, 'loc) Ast.Class.AbstractMethod.t') = + let open Ast.Class.AbstractMethod in + let { key; annot = (annot_loc, func); override = _; ts_accessibility = _; comments } = + abs_meth + in + let key' = this#object_key key in + let func' = this#function_type func in + let comments' = this#syntax_opt comments in + if key == key' && func == func' && comments == comments' then + abs_meth + else + { abs_meth with key = key'; annot = (annot_loc, func'); comments = comments' } + + method class_abstract_property _loc (abs_prop : ('loc, 'loc) Ast.Class.AbstractProperty.t') = + let open Ast.Class.AbstractProperty in + let { key; annot; override = _; ts_accessibility = _; variance; comments } = abs_prop in + let key' = this#object_key key in + let annot' = this#type_annotation_hint annot in + let variance' = this#variance_opt variance in + let comments' = this#syntax_opt comments in + if key == key' && annot == annot' && variance == variance' && comments == comments' then + abs_prop + else + { abs_prop with key = key'; annot = annot'; variance = variance'; comments = comments' } + method class_property _loc (prop : ('loc, 'loc) Ast.Class.Property.t') = let open Ast.Class.Property in - let { key; value; annot; static = _; variance; decorators; comments } = prop in + let { + key; + value; + annot; + static = _; + override = _; + optional = _; + variance; + ts_accessibility = _; + decorators; + comments; + } = + prop + in let key' = this#object_key key in let value' = this#class_property_value value in let annot' = this#type_annotation_hint annot in @@ -583,7 +674,20 @@ class ['loc] mapper = method class_private_field _loc (prop : ('loc, 'loc) Ast.Class.PrivateField.t') = let open Ast.Class.PrivateField in - let { key; value; annot; static = _; variance; decorators; comments } = prop in + let { + key; + value; + annot; + static = _; + override = _; + optional = _; + variance; + ts_accessibility = _; + decorators; + comments; + } = + prop + in let key' = this#private_name key in let value' = this#class_property_value value in let annot' = this#type_annotation_hint annot in @@ -610,17 +714,27 @@ class ['loc] mapper = comments = comments'; } + method class_static_block _loc (block : ('loc, 'loc) Ast.Class.StaticBlock.t') = + let open Ast.Class.StaticBlock in + let { body; comments } = block in + let body' = this#statement_list body in + let comments' = this#syntax_opt comments in + if body == body' && comments == comments' then + block + else + { body = body'; comments = comments' } + method default_opt (default : ('loc, 'loc) Ast.Expression.t option) = map_opt this#expression default method component_declaration _loc (component : ('loc, 'loc) Ast.Statement.ComponentDeclaration.t) = let open Ast.Statement.ComponentDeclaration in - let { id = ident; tparams; params; body; renders; comments; sig_loc } = component in + let { id = ident; tparams; params; body; renders; async; comments; sig_loc } = component in let ident' = this#component_identifier ident in let tparams' = map_opt (this#type_params ~kind:ComponentDeclarationTP) tparams in let params' = this#component_params params in - let body' = this#component_body body in + let body' = map_opt this#component_body body in let renders' = this#component_renders_annotation renders in let comments' = this#syntax_opt comments in if @@ -639,6 +753,7 @@ class ['loc] mapper = params = params'; body = body'; renders = renders'; + async; comments = comments'; sig_loc; } @@ -672,8 +787,9 @@ class ['loc] mapper = (param_name : ('loc, 'loc) Ast.Statement.ComponentDeclaration.Param.param_name) = let open Ast.Statement.ComponentDeclaration.Param in match param_name with - | Identifier ident -> Identifier (this#identifier ident) - | StringLiteral (str_loc, str) -> StringLiteral (str_loc, this#string_literal str_loc str) + | Identifier ident -> id this#identifier ident param_name (fun x -> Identifier x) + | StringLiteral (loc, str) -> + id this#string_literal str param_name (fun x -> StringLiteral (loc, x)) method component_param_pattern (expr : ('loc, 'loc) Ast.Pattern.t) = this#binding_pattern ~kind:Ast.Variable.Let expr @@ -731,12 +847,30 @@ class ['loc] mapper = method declare_class _loc (decl : ('loc, 'loc) Ast.Statement.DeclareClass.t) = let open Ast.Statement.DeclareClass in - let { id = ident; tparams; body; extends; mixins; implements; comments } = decl in + let { id = ident; tparams; body; extends; mixins; implements; abstract; comments } = decl in let id' = this#class_identifier ident in let tparams' = map_opt (this#type_params ~kind:DeclareClassTP) tparams in - let body' = map_loc this#object_type body in - let extends' = map_opt (map_loc this#generic_type) extends in - let mixins' = map_list (map_loc this#generic_type) mixins in + let body' = + let (bloc, b) = body in + id this#object_type b body (fun b' -> (bloc, b')) + in + let extends' = + map_opt + (fun (ext_loc, ext) -> + let ext' = this#declare_class_extends ext in + if ext' == ext then + (ext_loc, ext) + else + (ext_loc, ext')) + extends + in + let mixins' = + map_list + (fun mix -> + let (mloc, m) = mix in + id this#generic_type m mix (fun m' -> (mloc, m'))) + mixins + in let implements' = map_opt this#class_implements implements in let comments' = this#syntax_opt comments in if @@ -757,15 +891,28 @@ class ['loc] mapper = extends = extends'; mixins = mixins'; implements = implements'; + abstract; comments = comments'; } + method declare_class_extends (ext : ('loc, 'loc) Ast.Statement.DeclareClass.extends) = + let open Ast.Statement.DeclareClass in + match ext with + | ExtendsIdent e -> id this#generic_type e ext (fun e' -> ExtendsIdent e') + | ExtendsCall { callee = (callee_loc, callee); arg = (arg_loc, arg) } -> + let callee' = id this#generic_type callee callee (fun c -> c) in + let arg' = this#declare_class_extends arg in + if callee' == callee && arg' == arg then + ext + else + ExtendsCall { callee = (callee_loc, callee'); arg = (arg_loc, arg') } + method declare_component _loc (decl : ('loc, 'loc) Ast.Statement.DeclareComponent.t) = let open Ast.Statement.DeclareComponent in let { id = ident; tparams; params; renders; comments } = decl in let ident' = this#component_identifier ident in let tparams' = map_opt (this#type_params ~kind:DeclareComponentTP) tparams in - let params' = this#component_type_params params in + let params' = this#component_params params in let renders' = this#component_renders_annotation renders in let comments' = this#syntax_opt comments in if @@ -915,18 +1062,24 @@ class ['loc] mapper = decl else Enum (loc, enum') + | Namespace (loc, ns) -> + let ns' = this#declare_namespace loc ns in + if ns' == ns then + decl + else + Namespace (loc, ns') method declare_function _loc (decl : ('loc, 'loc) Ast.Statement.DeclareFunction.t) = let open Ast.Statement.DeclareFunction in - let { id = ident; annot; predicate; comments } = decl in - let id' = this#function_identifier ident in + let { id = ident; annot; predicate; comments; implicit_declare } = decl in + let id' = map_opt this#function_identifier ident in let annot' = this#type_annotation annot in let predicate' = map_opt this#predicate predicate in let comments' = this#syntax_opt comments in if id' == ident && annot' == annot && predicate' == predicate && comments' == comments then decl else - { id = id'; annot = annot'; predicate = predicate'; comments = comments' } + { id = id'; annot = annot'; predicate = predicate'; comments = comments'; implicit_declare } method declare_interface loc (decl : ('loc, 'loc) Ast.Statement.Interface.t) = this#interface loc decl @@ -954,7 +1107,7 @@ class ['loc] mapper = method declare_namespace _loc (m : ('loc, 'loc) Ast.Statement.DeclareNamespace.t) = let open Ast.Statement.DeclareNamespace in - let { id; body; comments } = m in + let { id; body; comments; implicit_declare; keyword } = m in let id' = match id with | Global g_id -> @@ -975,21 +1128,44 @@ class ['loc] mapper = if id' == id && body' == body && comments == comments' then m else - { id = id'; body = body'; comments = comments' } + { id = id'; body = body'; comments = comments'; implicit_declare; keyword } method declare_type_alias loc (decl : ('loc, 'loc) Ast.Statement.TypeAlias.t) = this#type_alias loc decl method declare_variable _loc (decl : ('loc, 'loc) Ast.Statement.DeclareVariable.t) = let open Ast.Statement.DeclareVariable in - let { id = ident; annot; kind; comments } = decl in - let id' = this#pattern_identifier ~kind ident in - let annot' = this#type_annotation annot in + let { declarations; kind; comments } = decl in + let declarations' = map_list (this#declare_variable_declarator ~kind) declarations in let comments' = this#syntax_opt comments in - if id' == ident && annot' == annot && comments' == comments then + if declarations == declarations' && comments == comments' then decl else - { id = id'; annot = annot'; kind; comments = comments' } + { declarations = declarations'; kind; comments = comments' } + + method declare_variable_declarator + ~kind (decl : ('loc, 'loc) Ast.Statement.VariableDeclaration.Declarator.t) = + let open Ast.Statement.VariableDeclaration.Declarator in + let (loc, { id; init }) = decl in + let id' = + match id with + | (ploc, Ast.Pattern.Identifier { Ast.Pattern.Identifier.name; annot; optional }) -> + let name' = this#pattern_identifier ~kind name in + let annot' = this#type_annotation_hint annot in + if name == name' && annot == annot' then + id + else + ( ploc, + Ast.Pattern.Identifier + { Ast.Pattern.Identifier.name = name'; annot = annot'; optional } + ) + | _ -> id + in + let init' = map_opt this#expression init in + if id == id' && init == init' then + decl + else + (loc, { id = id'; init = init' }) method do_while _loc (stuff : ('loc, 'loc) Ast.Statement.DoWhile.t) = let open Ast.Statement.DoWhile in @@ -1013,87 +1189,39 @@ class ['loc] mapper = method enum_declaration _loc (enum : ('loc, 'loc) Ast.Statement.EnumDeclaration.t) = let open Ast.Statement.EnumDeclaration in - let { id = ident; body; comments } = enum in + let { id = ident; body; const_; comments } = enum in let id' = this#pattern_identifier ~kind:Ast.Variable.Const ident in let body' = this#enum_body body in let comments' = this#syntax_opt comments in if ident == id' && body == body' && comments == comments' then enum else - { id = id'; body = body'; comments = comments' } + { id = id'; body = body'; const_; comments = comments' } method enum_body (body : 'loc Ast.Statement.EnumDeclaration.body) = let open Ast.Statement.EnumDeclaration in - match body with - | (loc, BooleanBody boolean_body) -> - id this#enum_boolean_body boolean_body body (fun body -> (loc, BooleanBody body)) - | (loc, NumberBody number_body) -> - id this#enum_number_body number_body body (fun body -> (loc, NumberBody body)) - | (loc, StringBody string_body) -> - id this#enum_string_body string_body body (fun body -> (loc, StringBody body)) - | (loc, SymbolBody symbol_body) -> - id this#enum_symbol_body symbol_body body (fun body -> (loc, SymbolBody body)) - | (loc, BigIntBody bigint_body) -> - id this#enum_bigint_body bigint_body body (fun body -> (loc, BigIntBody body)) - - method enum_boolean_body (body : 'loc Ast.Statement.EnumDeclaration.BooleanBody.t) = - let open Ast.Statement.EnumDeclaration.BooleanBody in - let { members; explicit_type = _; has_unknown_members = _; comments } = body in - let members' = map_list this#enum_boolean_member members in - let comments' = this#syntax_opt comments in - if members == members' && comments == comments' then - body - else - { body with members = members'; comments = comments' } - - method enum_number_body (body : 'loc Ast.Statement.EnumDeclaration.NumberBody.t) = - let open Ast.Statement.EnumDeclaration.NumberBody in - let { members; explicit_type = _; has_unknown_members = _; comments } = body in - let members' = map_list this#enum_number_member members in - let comments' = this#syntax_opt comments in - if members == members' && comments == comments' then - body - else - { body with members = members'; comments = comments' } - - method enum_string_body (body : 'loc Ast.Statement.EnumDeclaration.StringBody.t) = - let open Ast.Statement.EnumDeclaration.StringBody in - let { members; explicit_type = _; has_unknown_members = _; comments } = body in - let members' = - match members with - | Defaulted m -> id (map_list this#enum_defaulted_member) m members (fun m -> Defaulted m) - | Initialized m -> id (map_list this#enum_string_member) m members (fun m -> Initialized m) - in + let (loc, body_t) = body in + let { Body.members; explicit_type = _; has_unknown_members = _; comments } = body_t in + let members' = map_list this#enum_member members in let comments' = this#syntax_opt comments in if members == members' && comments == comments' then body else - { body with members = members'; comments = comments' } + (loc, { body_t with Body.members = members'; comments = comments' }) - method enum_symbol_body (body : 'loc Ast.Statement.EnumDeclaration.SymbolBody.t) = - let open Ast.Statement.EnumDeclaration.SymbolBody in - let { members; has_unknown_members = _; comments } = body in - let members' = map_list this#enum_defaulted_member members in - let comments' = this#syntax_opt comments in - if members == members' && comments == comments' then - body - else - { body with members = members'; comments = comments' } - - method enum_bigint_body (body : 'loc Ast.Statement.EnumDeclaration.BigIntBody.t) = - let open Ast.Statement.EnumDeclaration.BigIntBody in - let { members; explicit_type = _; has_unknown_members = _; comments } = body in - let members' = map_list this#enum_bigint_member members in - let comments' = this#syntax_opt comments in - if members == members' && comments == comments' then - body - else - { body with members = members'; comments = comments' } + method enum_member (member : 'loc Ast.Statement.EnumDeclaration.member) = + let open Ast.Statement.EnumDeclaration in + match member with + | BooleanMember m -> id this#enum_boolean_member m member (fun m -> BooleanMember m) + | NumberMember m -> id this#enum_number_member m member (fun m -> NumberMember m) + | StringMember m -> id this#enum_string_member m member (fun m -> StringMember m) + | BigIntMember m -> id this#enum_bigint_member m member (fun m -> BigIntMember m) + | DefaultedMember m -> id this#enum_defaulted_member m member (fun m -> DefaultedMember m) method enum_defaulted_member (member : 'loc Ast.Statement.EnumDeclaration.DefaultedMember.t) = let open Ast.Statement.EnumDeclaration.DefaultedMember in let (loc, { id = ident }) = member in - let id' = this#enum_member_identifier ident in + let id' = this#enum_member_name ident in if ident == id' then member else @@ -1105,44 +1233,68 @@ class ['loc] mapper = ) = let open Ast.Statement.EnumDeclaration.InitializedMember in let (loc, { id = ident; init }) = member in - let id' = this#enum_member_identifier ident in - if ident == id' then + let id' = this#enum_member_name ident in + let (init_loc, init_val) = init in + let init_val' = this#boolean_literal init_val in + if ident == id' && init_val == init_val' then member else - (loc, { id = id'; init }) + (loc, { id = id'; init = (init_loc, init_val') }) method enum_number_member (member : ('loc Ast.NumberLiteral.t, 'loc) Ast.Statement.EnumDeclaration.InitializedMember.t) = let open Ast.Statement.EnumDeclaration.InitializedMember in let (loc, { id = ident; init }) = member in - let id' = this#enum_member_identifier ident in - if ident == id' then + let id' = this#enum_member_name ident in + let (init_loc, init_val) = init in + let init_val' = this#number_literal init_val in + if ident == id' && init_val == init_val' then member else - (loc, { id = id'; init }) + (loc, { id = id'; init = (init_loc, init_val') }) method enum_string_member (member : ('loc Ast.StringLiteral.t, 'loc) Ast.Statement.EnumDeclaration.InitializedMember.t) = let open Ast.Statement.EnumDeclaration.InitializedMember in let (loc, { id = ident; init }) = member in - let id' = this#enum_member_identifier ident in - if ident == id' then + let id' = this#enum_member_name ident in + let (init_loc, init_val) = init in + let init_val' = this#string_literal init_val in + if ident == id' && init_val == init_val' then member else - (loc, { id = id'; init }) + (loc, { id = id'; init = (init_loc, init_val') }) method enum_bigint_member (member : ('loc Ast.BigIntLiteral.t, 'loc) Ast.Statement.EnumDeclaration.InitializedMember.t) = let open Ast.Statement.EnumDeclaration.InitializedMember in let (loc, { id = ident; init }) = member in - let id' = this#enum_member_identifier ident in - if ident == id' then + let id' = this#enum_member_name ident in + let (init_loc, init_val) = init in + let init_val' = this#bigint_literal init_val in + if ident == id' && init_val == init_val' then member else - (loc, { id = id'; init }) + (loc, { id = id'; init = (init_loc, init_val') }) + + method enum_member_name (id : 'loc Ast.Statement.EnumDeclaration.member_name) = + let open Ast.Statement.EnumDeclaration in + match id with + | Identifier ident -> + let ident' = this#enum_member_identifier ident in + if ident == ident' then + id + else + Identifier ident' + | StringLiteral (loc, lit) -> + let lit' = this#string_literal lit in + if lit == lit' then + id + else + StringLiteral (loc, lit') method enum_member_identifier (id : ('loc, 'loc) Ast.Identifier.t) = this#identifier id @@ -1191,13 +1343,15 @@ class ['loc] mapper = method export_named_declaration_specifier (spec : ('loc, 'loc) Ast.Statement.ExportNamedDeclaration.ExportSpecifier.t) = let open Ast.Statement.ExportNamedDeclaration.ExportSpecifier in - let (loc, { local; exported; from_remote; imported_name_def_loc }) = spec in + let (loc, { local; exported; export_kind; from_remote; imported_name_def_loc }) = spec in let local' = this#identifier local in let exported' = map_opt this#identifier exported in if local == local' && exported == exported' then spec else - (loc, { local = local'; exported = exported'; from_remote; imported_name_def_loc }) + ( loc, + { local = local'; exported = exported'; export_kind; from_remote; imported_name_def_loc } + ) method export_batch_specifier (spec : ('loc, 'loc) Ast.Statement.ExportNamedDeclaration.ExportBatchSpecifier.t) = @@ -1234,6 +1388,41 @@ class ['loc] mapper = else { value; raw; comments = comments' } + method export_assignment _loc (assign : ('loc, 'loc) Ast.Statement.ExportAssignment.t) = + let open Ast.Statement.ExportAssignment in + let { rhs; comments } = assign in + let rhs' = + match rhs with + | Expression expr -> + let expr' = this#expression expr in + if expr == expr' then + rhs + else + Expression expr' + | DeclareFunction (loc, decl) -> + let decl' = this#declare_function loc decl in + if decl == decl' then + rhs + else + DeclareFunction (loc, decl') + in + let comments' = this#syntax_opt comments in + if rhs == rhs' && comments == comments' then + assign + else + { rhs = rhs'; comments = comments' } + + method namespace_export_declaration + _loc (decl : ('loc, 'loc) Ast.Statement.NamespaceExportDeclaration.t) = + let open Ast.Statement.NamespaceExportDeclaration in + let { id; comments } = decl in + let id' = this#identifier id in + let comments' = this#syntax_opt comments in + if id == id' && comments == comments' then + decl + else + { id = id'; comments = comments' } + method expression_statement _loc (stmt : ('loc, 'loc) Ast.Statement.Expression.t) = let open Ast.Statement.Expression in let { expression = expr; directive; comments } = stmt in @@ -1330,13 +1519,31 @@ class ['loc] mapper = method function_param_type (fpt : ('loc, 'loc) Ast.Type.Function.Param.t) = let open Ast.Type.Function.Param in - let (loc, { annot; name; optional }) = fpt in - let annot' = this#type_ annot in - let name' = map_opt this#identifier name in - if annot' == annot && name' == name then - fpt - else - (loc, { annot = annot'; name = name'; optional }) + let (loc, param) = fpt in + match param with + | Anonymous annot -> + let annot' = this#type_ annot in + if annot' == annot then + fpt + else + (loc, Anonymous annot') + | Labeled { name; annot; optional } -> + let name' = this#function_param_type_identifier name in + let annot' = this#type_ annot in + if name' == name && annot' == annot then + fpt + else + (loc, Labeled { name = name'; annot = annot'; optional }) + | Destructuring pattern -> + let pattern' = this#function_param_type_pattern pattern in + if pattern' == pattern then + fpt + else + (loc, Destructuring pattern') + + method function_param_type_identifier (id : ('loc, 'loc) Ast.Identifier.t) = this#identifier id + + method function_param_type_pattern (patt : ('loc, 'loc) Ast.Pattern.t) = this#pattern patt method function_rest_param_type (frpt : ('loc, 'loc) Ast.Type.Function.RestParam.t) = let open Ast.Type.Function.RestParam in @@ -1362,10 +1569,11 @@ class ['loc] mapper = (return : ('loc, 'loc) Ast.Type.Function.return_annotation) = let open Ast.Type.Function in match return with - | TypeAnnotation t -> id this#type_ t return (fun rt -> TypeAnnotation rt) + | Available t -> id this#type_ t return (fun rt -> Available rt) | TypeGuard g -> id this#type_guard g return (fun tg -> TypeGuard tg) + | Missing _ -> return - method function_type _loc (ft : ('loc, 'loc) Ast.Type.Function.t) = + method function_type (ft : ('loc, 'loc) Ast.Type.Function.t) = let open Ast.Type.Function in let { params = (params_loc, { Params.this_; params = ps; rest = rpo; comments = params_comments }); @@ -1410,26 +1618,51 @@ class ['loc] mapper = method object_property_value_type (opvt : ('loc, 'loc) Ast.Type.Object.Property.value) = let open Ast.Type.Object.Property in match opvt with - | Init t -> id this#type_ t opvt (fun t -> Init t) + | Init (Some t) -> id this#type_ t opvt (fun t -> Init (Some t)) + | Init None -> opvt | Get t -> id this#object_type_property_getter t opvt (fun t -> Get t) | Set t -> id this#object_type_property_setter t opvt (fun t -> Set t) method object_type_property_getter getter = let (loc, ft) = getter in - id_loc this#function_type loc ft getter (fun ft -> (loc, ft)) + id this#function_type ft getter (fun ft -> (loc, ft)) method object_type_property_setter setter = let (loc, ft) = setter in - id_loc this#function_type loc ft setter (fun ft -> (loc, ft)) + id this#function_type ft setter (fun ft -> (loc, ft)) method object_property_type (opt : ('loc, 'loc) Ast.Type.Object.Property.t) = let open Ast.Type.Object.Property in - let (loc, { key; value; optional; static; proto; _method; variance; comments }) = opt in + let ( loc, + { + key; + value; + optional; + static; + proto; + _method; + abstract; + override; + variance; + ts_accessibility; + init; + comments; + } + ) = + opt + in let key' = this#object_key key in let value' = this#object_property_value_type value in let variance' = this#variance_opt variance in + let init' = map_opt this#expression init in let comments' = this#syntax_opt comments in - if key' == key && value' == value && variance' == variance && comments' == comments then + if + key' == key + && value' == value + && variance' == variance + && init' == init + && comments' == comments + then opt else ( loc, @@ -1440,7 +1673,11 @@ class ['loc] mapper = static; proto; _method; + abstract; + override; variance = variance'; + ts_accessibility; + init = init'; comments = comments'; } ) @@ -1457,7 +1694,7 @@ class ['loc] mapper = method object_indexer_property_type (opt : ('loc, 'loc) Ast.Type.Object.Indexer.t) = let open Ast.Type.Object.Indexer in - let (loc, { id; key; value; static; variance; comments }) = opt in + let (loc, { id; key; value; static; variance; optional; comments }) = opt in let key' = this#type_ key in let value' = this#type_ value in let variance' = this#variance_opt variance in @@ -1465,7 +1702,17 @@ class ['loc] mapper = if key' == key && value' == value && variance' == variance && comments' == comments then opt else - (loc, { id; key = key'; value = value'; static; variance = variance'; comments = comments' }) + ( loc, + { + id; + key = key'; + value = value'; + static; + variance = variance'; + optional; + comments = comments'; + } + ) method object_internal_slot_property_type (slot : ('loc, 'loc) Ast.Type.Object.InternalSlot.t) = let open Ast.Type.Object.InternalSlot in @@ -1481,7 +1728,7 @@ class ['loc] mapper = method object_call_property_type (call : ('loc, 'loc) Ast.Type.Object.CallProperty.t) = let open Ast.Type.Object.CallProperty in let (loc, { value = (value_loc, value); static; comments }) = call in - let value' = this#function_type value_loc value in + let value' = this#function_type value in let comments' = this#syntax_opt comments in if value == value' && comments == comments' then call @@ -1490,16 +1737,31 @@ class ['loc] mapper = method object_mapped_type_property (mt : ('loc, 'loc) Ast.Type.Object.MappedType.t) = let open Ast.Type.Object.MappedType in - let (loc, { key_tparam; prop_type; source_type; variance; comments; optional }) = mt in + let ( loc, + { + key_tparam; + prop_type; + source_type; + name_type; + variance; + variance_op; + comments; + optional; + } + ) = + mt + in let key_tparam' = this#type_param ~kind:ObjectMappedTypeTP key_tparam in let prop_type' = this#type_ prop_type in let source_type' = this#type_ source_type in + let name_type' = map_opt this#type_ name_type in let variance' = this#variance_opt variance in let comments' = this#syntax_opt comments in if key_tparam' == key_tparam && prop_type' == prop_type && source_type' == source_type + && name_type' == name_type && variance' == variance && comments' == comments then @@ -1510,13 +1772,15 @@ class ['loc] mapper = key_tparam = key_tparam'; prop_type = prop_type'; source_type = source_type'; + name_type = name_type'; variance = variance'; + variance_op; comments = comments'; optional; } ) - method object_type _loc (ot : ('loc, 'loc) Ast.Type.Object.t) = + method object_type (ot : ('loc, 'loc) Ast.Type.Object.t) = let open Ast.Type.Object in let { properties; exact; inexact; comments } = ot in let properties' = map_list this#object_type_property properties in @@ -1536,12 +1800,32 @@ class ['loc] mapper = id this#object_internal_slot_property_type p' p (fun p' -> InternalSlot p') | CallProperty p' -> id this#object_call_property_type p' p (fun p' -> CallProperty p') | MappedType p' -> id this#object_mapped_type_property p' p (fun p' -> MappedType p') + | PrivateField p' -> id this#object_private_field_type p' p (fun p' -> PrivateField p') + + method object_private_field_type (pf : ('loc, 'loc) Ast.Type.Object.PrivateField.t) = + let open Ast.Type.Object.PrivateField in + let (loc, { key; comments }) = pf in + let key' = this#private_name key in + let comments' = this#syntax_opt comments in + if key' == key && comments' == comments then + pf + else + (loc, { key = key'; comments = comments' }) method interface_type _loc (i : ('loc, 'loc) Ast.Type.Interface.t) = let open Ast.Type.Interface in let { extends; body; comments } = i in - let extends' = map_list (map_loc this#generic_type) extends in - let body' = map_loc this#object_type body in + let extends' = + map_list + (fun ext -> + let (eloc, e) = ext in + id this#generic_type e ext (fun e' -> (eloc, e'))) + extends + in + let body' = + let (bloc, b) = body in + id this#object_type b body (fun b' -> (bloc, b')) + in let comments' = this#syntax_opt comments in if extends' == extends && body' == body && comments == comments' then i @@ -1553,6 +1837,26 @@ class ['loc] mapper = match git with | Unqualified i -> id this#type_identifier_reference i git (fun i -> Unqualified i) | Qualified i -> id this#generic_qualified_identifier_type i git (fun i -> Qualified i) + | ImportTypeAnnot (annot, import') -> + let import'' = this#generic_identifier_import_type import' in + if import' == import'' then + git + else + ImportTypeAnnot (annot, import'') + + method generic_identifier_import_type (import' : 'loc Ast.Type.Generic.Identifier.import_type') + = + let open Ast.Type.Generic.Identifier in + let { argument; comments } = import' in + let argument' = + let (loc, lit) = argument in + id this#string_literal lit argument (fun lit -> (loc, lit)) + in + let comments' = this#syntax_opt comments in + if argument == argument' && comments == comments' then + import' + else + { argument = argument'; comments = comments' } method generic_qualified_identifier_type qual = let open Ast.Type.Generic.Identifier in @@ -1632,7 +1936,7 @@ class ['loc] mapper = } ) - method generic_type _loc (gt : ('loc, 'loc) Ast.Type.Generic.t) = + method generic_type (gt : ('loc, 'loc) Ast.Type.Generic.t) = let open Ast.Type.Generic in let { id; targs; comments } = gt in let id' = this#generic_identifier_type id in @@ -1663,7 +1967,7 @@ class ['loc] mapper = else { indexed_access = indexed_access'; optional } - method string_literal _loc (lit : 'loc Ast.StringLiteral.t) = + method string_literal (lit : 'loc Ast.StringLiteral.t) = let open Ast.StringLiteral in let { value; raw; comments } = lit in let comments' = this#syntax_opt comments in @@ -1672,7 +1976,7 @@ class ['loc] mapper = else { value; raw; comments = comments' } - method number_literal _loc (lit : 'loc Ast.NumberLiteral.t) = + method number_literal (lit : 'loc Ast.NumberLiteral.t) = let open Ast.NumberLiteral in let { value; raw; comments } = lit in let comments' = this#syntax_opt comments in @@ -1681,7 +1985,7 @@ class ['loc] mapper = else { value; raw; comments = comments' } - method bigint_literal _loc (lit : 'loc Ast.BigIntLiteral.t) = + method bigint_literal (lit : 'loc Ast.BigIntLiteral.t) = let open Ast.BigIntLiteral in let { value; raw; comments } = lit in let comments' = this#syntax_opt comments in @@ -1690,7 +1994,7 @@ class ['loc] mapper = else { value; raw; comments = comments' } - method boolean_literal _loc (lit : 'loc Ast.BooleanLiteral.t) = + method boolean_literal (lit : 'loc Ast.BooleanLiteral.t) = let open Ast.BooleanLiteral in let { value; comments } = lit in let comments' = this#syntax_opt comments in @@ -1699,9 +2003,9 @@ class ['loc] mapper = else { value; comments = comments' } - method null_literal _loc comments = this#syntax_opt comments + method null_literal comments = this#syntax_opt comments - method regexp_literal _loc (lit : 'loc Ast.RegExpLiteral.t) = + method regexp_literal (lit : 'loc Ast.RegExpLiteral.t) = let open Ast.RegExpLiteral in let { pattern; flags; raw; comments } = lit in let comments' = this#syntax_opt comments in @@ -1710,14 +2014,14 @@ class ['loc] mapper = else { pattern; flags; raw; comments = comments' } - method module_ref_literal _loc (lit : ('loc, 'loc) Ast.ModuleRefLiteral.t) = + method module_ref_literal (lit : ('loc, 'loc) Ast.ModuleRefLiteral.t) = let open Ast.ModuleRefLiteral in - let { value; require_loc; def_loc_opt; prefix_len; legacy_interop; raw; comments } = lit in + let { value; require_loc; def_loc_opt; prefix_len; raw; comments } = lit in let comments' = this#syntax_opt comments in if comments == comments' then lit else - { value; require_loc; def_loc_opt; prefix_len; legacy_interop; raw; comments } + { value; require_loc; def_loc_opt; prefix_len; raw; comments = comments' } method nullable_type (t : ('loc, 'loc) Ast.Type.Nullable.t) = let open Ast.Type.Nullable in @@ -1770,7 +2074,7 @@ class ['loc] mapper = let argument' = this#typeof_expression argument in let targs' = map_opt this#type_args targs in let comments' = this#syntax_opt comments in - if argument == argument' && targs = targs' && comments == comments' then + if argument == argument' && targs == targs' && comments == comments' then t else { argument = argument'; targs = targs'; comments = comments' } @@ -1780,6 +2084,12 @@ class ['loc] mapper = match git with | Unqualified i -> id this#typeof_identifier i git (fun i -> Unqualified i) | Qualified i -> id this#typeof_qualified_identifier i git (fun i -> Qualified i) + | Import (annot, import') -> + let import'' = this#generic_identifier_import_type import' in + if import' == import'' then + git + else + Import (annot, import'') method typeof_identifier id = this#identifier id @@ -1838,7 +2148,10 @@ class ['loc] mapper = method tuple_element (el : ('loc, 'loc) Ast.Type.Tuple.element) = let open Ast.Type.Tuple in match el with - | (loc, UnlabeledElement t) -> id this#type_ t el (fun t -> (loc, UnlabeledElement t)) + | (loc, UnlabeledElement { UnlabeledElement.annot; optional }) -> + id this#type_ annot el (fun annot -> + (loc, UnlabeledElement { UnlabeledElement.annot; optional }) + ) | (loc, LabeledElement e) -> id this#tuple_labeled_element e el (fun e -> (loc, LabeledElement e)) | (loc, SpreadElement e) -> id this#tuple_spread_element e el (fun e -> (loc, SpreadElement e)) @@ -1873,6 +2186,16 @@ class ['loc] mapper = else { argument = argument'; comments = comments' } + method template_literal_type (t : ('loc, 'loc) Ast.Type.TemplateLiteral.t) = + let open Ast.Type.TemplateLiteral in + let { quasis; types; comments } = t in + let types' = map_list this#type_ types in + let comments' = this#syntax_opt comments in + if types' == types && comments' == comments then + t + else + { quasis; types = types'; comments = comments' } + method union_type _loc (t : ('loc, 'loc) Ast.Type.Union.t) = let open Ast.Type.Union in let { types = (t0, t1, ts); comments } = t in @@ -1925,6 +2248,8 @@ class ['loc] mapper = id this#syntax_opt comments t (fun comments -> (loc, Never comments)) | (loc, Undefined comments) -> id this#syntax_opt comments t (fun comments -> (loc, Undefined comments)) + | (loc, UniqueSymbol comments) -> + id this#syntax_opt comments t (fun comments -> (loc, UniqueSymbol comments)) | (loc, Nullable t') -> id this#nullable_type t' t (fun t' -> (loc, Nullable t')) | (loc, Array t') -> id this#array_type t' t (fun t' -> (loc, Array t')) | (loc, Conditional t') -> id this#conditional_type t' t (fun t' -> (loc, Conditional t')) @@ -1933,23 +2258,29 @@ class ['loc] mapper = | (loc, Keyof t') -> id this#keyof_type t' t (fun t' -> (loc, Keyof t')) | (loc, Renders t') -> id this#render_type t' t (fun t' -> (loc, Renders t')) | (loc, ReadOnly t') -> id this#readonly_type t' t (fun t' -> (loc, ReadOnly t')) - | (loc, Function ft) -> id_loc this#function_type loc ft t (fun ft -> (loc, Function ft)) + | (loc, Function ft) -> id this#function_type ft t (fun ft -> (loc, Function ft)) + | (loc, ConstructorType { ConstructorType.abstract_; func }) -> + id this#function_type func t (fun func -> + (loc, ConstructorType { ConstructorType.abstract_; func }) + ) | (loc, Component ct) -> id_loc this#component_type loc ct t (fun ct -> (loc, Component ct)) - | (loc, Object ot) -> id_loc this#object_type loc ot t (fun ot -> (loc, Object ot)) + | (loc, Object ot) -> id this#object_type ot t (fun ot -> (loc, Object ot)) | (loc, Interface i) -> id_loc this#interface_type loc i t (fun i -> (loc, Interface i)) - | (loc, Generic gt) -> id_loc this#generic_type loc gt t (fun gt -> (loc, Generic gt)) + | (loc, Generic gt) -> id this#generic_type gt t (fun gt -> (loc, Generic gt)) | (loc, IndexedAccess ia) -> id_loc this#indexed_access_type loc ia t (fun ia -> (loc, IndexedAccess ia)) | (loc, OptionalIndexedAccess ia) -> id_loc this#optional_indexed_access_type loc ia t (fun ia -> (loc, OptionalIndexedAccess ia)) | (loc, StringLiteral lit) -> - id_loc this#string_literal loc lit t (fun lit -> (loc, StringLiteral lit)) + id this#string_literal lit t (fun lit -> (loc, StringLiteral lit)) | (loc, NumberLiteral lit) -> - id_loc this#number_literal loc lit t (fun lit -> (loc, NumberLiteral lit)) + id this#number_literal lit t (fun lit -> (loc, NumberLiteral lit)) | (loc, BigIntLiteral lit) -> - id_loc this#bigint_literal loc lit t (fun lit -> (loc, BigIntLiteral lit)) + id this#bigint_literal lit t (fun lit -> (loc, BigIntLiteral lit)) | (loc, BooleanLiteral lit) -> - id_loc this#boolean_literal loc lit t (fun lit -> (loc, BooleanLiteral lit)) + id this#boolean_literal lit t (fun lit -> (loc, BooleanLiteral lit)) + | (loc, TemplateLiteral t') -> + id this#template_literal_type t' t (fun t' -> (loc, TemplateLiteral t')) | (loc, Union t') -> id_loc this#union_type loc t' t (fun t' -> (loc, Union t')) | (loc, Intersection t') -> id_loc this#intersection_type loc t' t (fun t' -> (loc, Intersection t')) @@ -2063,13 +2394,21 @@ class ['loc] mapper = method function_param (param : ('loc, 'loc) Ast.Function.Param.t) = let open Ast.Function.Param in - let (loc, { argument; default }) = param in - let argument' = this#function_param_pattern argument in - let default' = this#default_opt default in - if argument == argument' && default == default' then - param - else - (loc, { argument = argument'; default = default' }) + let (loc, param') = param in + match param' with + | RegularParam { argument; default } -> + let argument' = this#function_param_pattern argument in + let default' = this#default_opt default in + if argument == argument' && default == default' then + param + else + (loc, RegularParam { argument = argument'; default = default' }) + | ParamProperty prop' -> + let prop'' = this#class_property loc prop' in + if prop' == prop'' then + param + else + (loc, ParamProperty prop'') method function_return_annotation (return : ('loc, 'loc) Ast.Function.ReturnAnnot.t) = let open Ast.Function.ReturnAnnot in @@ -2114,8 +2453,17 @@ class ['loc] mapper = let { id = ident; tparams; extends; body; comments } = interface in let id' = this#binding_type_identifier ident in let tparams' = map_opt (this#type_params ~kind:InterfaceTP) tparams in - let extends' = map_list (map_loc this#generic_type) extends in - let body' = map_loc this#object_type body in + let extends' = + map_list + (fun ext -> + let (eloc, e) = ext in + id this#generic_type e ext (fun e' -> (eloc, e'))) + extends + in + let body' = + let (bloc, b) = body in + id this#object_type b body (fun b' -> (bloc, b')) + in let comments' = this#syntax_opt comments in if id' == ident @@ -2150,15 +2498,16 @@ class ['loc] mapper = else (loc, { expression = expression'; comments = comments' }) - method import _loc (expr : ('loc, 'loc) Ast.Expression.Import.t) = + method import (expr : ('loc, 'loc) Ast.Expression.Import.t) = let open Ast.Expression.Import in - let { argument; comments } = expr in + let { argument; options; comments } = expr in let argument' = this#expression argument in + let options' = map_opt this#expression options in let comments' = this#syntax_opt comments in - if argument == argument' && comments == comments' then + if argument == argument' && options == options' && comments == comments' then expr else - { argument = argument'; comments = comments' } + { argument = argument'; options = options'; comments = comments' } method if_consequent_statement ~has_else (stmt : ('loc, 'loc) Ast.Statement.t) = ignore has_else; @@ -2193,7 +2542,7 @@ class ['loc] mapper = method import_declaration _loc (decl : ('loc, 'loc) Ast.Statement.ImportDeclaration.t) = let open Ast.Statement.ImportDeclaration in - let { import_kind; source; specifiers; default; comments } = decl in + let { import_kind; source; specifiers; default; attributes; comments } = decl in let source' = map_loc this#import_source source in let specifiers' = map_opt (this#import_specifier ~import_kind) specifiers in let default' = @@ -2206,11 +2555,13 @@ class ['loc] mapper = { identifier = identifier'; remote_default_name_def_loc }) default in + let attributes' = map_opt (map_loc this#import_attributes) attributes in let comments' = this#syntax_opt comments in if source == source' && specifiers == specifiers' && default == default' + && attributes == attributes' && comments == comments' then decl @@ -2220,6 +2571,7 @@ class ['loc] mapper = source = source'; specifiers = specifiers'; default = default'; + attributes = attributes'; comments = comments'; } @@ -2232,6 +2584,68 @@ class ['loc] mapper = else { value; raw; comments = comments' } + method import_equals_declaration + _loc (decl : ('loc, 'loc) Ast.Statement.ImportEqualsDeclaration.t) = + let open Ast.Statement.ImportEqualsDeclaration in + let { id = ident; module_reference; import_kind; is_export; comments } = decl in + let ident' = + let open Ast.Statement.ImportDeclaration in + match import_kind with + | ImportType + | ImportTypeof -> + this#binding_type_identifier ident + | ImportValue -> this#pattern_identifier ~kind:Ast.Variable.Let ident + in + let module_reference' = this#import_equals_module_reference module_reference in + let comments' = this#syntax_opt comments in + if ident == ident' && module_reference == module_reference' && comments == comments' then + decl + else + { + id = ident'; + module_reference = module_reference'; + import_kind; + is_export; + comments = comments'; + } + + method import_equals_module_reference + (ref : ('loc, 'loc) Ast.Statement.ImportEqualsDeclaration.module_reference) = + let open Ast.Statement.ImportEqualsDeclaration in + match ref with + | ExternalModuleReference (annot, lit) -> + let lit' = this#string_literal lit in + if lit == lit' then + ref + else + ExternalModuleReference (annot, lit') + | Identifier ident -> id this#generic_identifier_type ident ref (fun ident -> Identifier ident) + + method import_attributes + _loc (attrs : ('loc, 'loc) Ast.Statement.ImportDeclaration.import_attribute list) = + map_list this#import_attribute attrs + + method import_attribute (attr : ('loc, 'loc) Ast.Statement.ImportDeclaration.import_attribute) = + let open Ast.Statement.ImportDeclaration in + let { loc; key; value } = attr in + let key' = this#import_attribute_key key in + let value' = + let (vloc, vlit) = value in + id this#string_literal vlit value (fun vlit' -> (vloc, vlit')) + in + if key == key' && value == value' then + attr + else + { loc; key = key'; value = value' } + + method import_attribute_key + (key : ('loc, 'loc) Ast.Statement.ImportDeclaration.import_attribute_key) = + let open Ast.Statement.ImportDeclaration in + match key with + | Identifier ident -> id this#identifier ident key (fun ident' -> Identifier ident') + | StringLiteral (loc, lit) -> + id this#string_literal lit key (fun lit' -> StringLiteral (loc, lit')) + method import_specifier ~import_kind (specifier : ('loc, 'loc) Ast.Statement.ImportDeclaration.specifier) = let open Ast.Statement.ImportDeclaration in @@ -2255,7 +2669,7 @@ class ['loc] mapper = ~(import_kind : Ast.Statement.ImportDeclaration.import_kind) (specifier : ('loc, 'loc) Ast.Statement.ImportDeclaration.named_specifier) = let open Ast.Statement.ImportDeclaration in - let { kind; local; remote; remote_name_def_loc } = specifier in + let { kind; kind_loc; local; remote; remote_name_def_loc } = specifier in let (is_type_remote, is_type_local) = match (import_kind, kind) with | (ImportType, _) @@ -2290,7 +2704,7 @@ class ['loc] mapper = if local == local' && remote == remote' then specifier else - { kind; local = local'; remote = remote'; remote_name_def_loc } + { kind; kind_loc; local = local'; remote = remote'; remote_name_def_loc } method import_default_specifier ~import_kind (id : ('loc, 'loc) Ast.Identifier.t) = let open Ast.Statement.ImportDeclaration in @@ -2419,8 +2833,8 @@ class ['loc] mapper = = this#jsx_expression loc jsx_expr - method jsx_attribute_value_literal loc (lit : 'loc Ast.StringLiteral.t) = - this#string_literal loc lit + method jsx_attribute_value_literal _loc (lit : 'loc Ast.StringLiteral.t) = + this#string_literal lit method jsx_children ((loc, children) as orig : 'loc * ('loc, 'loc) Ast.JSX.child list) = let children' = map_list this#jsx_child children in @@ -2572,7 +2986,7 @@ class ['loc] mapper = ('loc, 'loc, 'B) Ast.Match.Case.t = fun ~on_case_body case -> let open Ast.Match.Case in - let (loc, { pattern; body; guard; comments }) = case in + let (loc, { pattern; body; guard; comments; invalid_syntax; case_match_root_loc }) = case in let pattern' = this#match_pattern pattern in let body' = on_case_body body in let guard' = map_opt this#expression guard in @@ -2580,7 +2994,16 @@ class ['loc] mapper = if pattern == pattern' && body == body' && guard == guard' && comments == comments' then case else - (loc, { pattern = pattern'; body = body'; guard = guard'; comments = comments' }) + ( loc, + { + pattern = pattern'; + body = body'; + guard = guard'; + comments = comments'; + invalid_syntax; + case_match_root_loc; + } + ) method match_expression loc (x : ('loc, 'loc) Ast.Expression.match_expression) = this#match_ loc ~on_case_body:this#expression x @@ -2591,16 +3014,14 @@ class ['loc] mapper = method match_pattern (pattern : ('loc, 'loc) Ast.MatchPattern.t) = let open Ast.MatchPattern in match pattern with - | (loc, WildcardPattern x) -> id this#syntax_opt x pattern (fun x -> (loc, WildcardPattern x)) - | (loc, StringPattern x) -> - id_loc this#string_literal loc x pattern (fun x -> (loc, StringPattern x)) + | (loc, WildcardPattern x) -> + id this#match_wildcard_pattern x pattern (fun x -> (loc, WildcardPattern x)) + | (loc, StringPattern x) -> id this#string_literal x pattern (fun x -> (loc, StringPattern x)) | (loc, BooleanPattern x) -> - id_loc this#boolean_literal loc x pattern (fun x -> (loc, BooleanPattern x)) + id this#boolean_literal x pattern (fun x -> (loc, BooleanPattern x)) | (loc, NullPattern x) -> id this#syntax_opt x pattern (fun x -> (loc, NullPattern x)) - | (loc, NumberPattern x) -> - id_loc this#number_literal loc x pattern (fun x -> (loc, NumberPattern x)) - | (loc, BigIntPattern x) -> - id_loc this#bigint_literal loc x pattern (fun x -> (loc, BigIntPattern x)) + | (loc, NumberPattern x) -> id this#number_literal x pattern (fun x -> (loc, NumberPattern x)) + | (loc, BigIntPattern x) -> id this#bigint_literal x pattern (fun x -> (loc, BigIntPattern x)) | (loc, UnaryPattern x) -> id this#match_unary_pattern x pattern (fun x -> (loc, UnaryPattern x)) | (loc, IdentifierPattern x) -> @@ -2610,9 +3031,11 @@ class ['loc] mapper = | (loc, BindingPattern x) -> id_loc this#match_binding_pattern loc x pattern (fun x -> (loc, BindingPattern x)) | (loc, ObjectPattern x) -> - id this#match_object_pattern x pattern (fun x -> (loc, ObjectPattern x)) + id_loc this#match_object_pattern loc x pattern (fun x -> (loc, ObjectPattern x)) | (loc, ArrayPattern x) -> id this#match_array_pattern x pattern (fun x -> (loc, ArrayPattern x)) + | (loc, InstancePattern x) -> + id this#match_instance_pattern x pattern (fun x -> (loc, InstancePattern x)) | (loc, OrPattern x) -> id this#match_or_pattern x pattern (fun x -> (loc, OrPattern x)) | (loc, AsPattern x) -> id this#match_as_pattern x pattern (fun x -> (loc, AsPattern x)) @@ -2629,14 +3052,12 @@ class ['loc] mapper = else { operator; argument = argument'; comments = comments' } - method match_unary_pattern_argument loc (argument : 'loc Ast.MatchPattern.UnaryPattern.argument) + method match_unary_pattern_argument _loc (argument : 'loc Ast.MatchPattern.UnaryPattern.argument) = let open Ast.MatchPattern.UnaryPattern in match argument with - | NumberLiteral lit -> - id_loc this#number_literal loc lit argument (fun lit -> NumberLiteral lit) - | BigIntLiteral lit -> - id_loc this#bigint_literal loc lit argument (fun lit -> BigIntLiteral lit) + | NumberLiteral lit -> id this#number_literal lit argument (fun lit -> NumberLiteral lit) + | BigIntLiteral lit -> id this#bigint_literal lit argument (fun lit -> BigIntLiteral lit) method match_member_pattern (member_pattern : ('loc, 'loc) Ast.MatchPattern.MemberPattern.t) = let open Ast.MatchPattern.MemberPattern in @@ -2660,11 +3081,11 @@ class ['loc] mapper = let open Ast.MatchPattern.MemberPattern in match prop with | PropertyString (loc, lit) -> - id_loc this#string_literal loc lit prop (fun lit -> PropertyString (loc, lit)) + id this#string_literal lit prop (fun lit -> PropertyString (loc, lit)) | PropertyNumber (loc, lit) -> - id_loc this#number_literal loc lit prop (fun lit -> PropertyNumber (loc, lit)) + id this#number_literal lit prop (fun lit -> PropertyNumber (loc, lit)) | PropertyBigInt (loc, lit) -> - id_loc this#bigint_literal loc lit prop (fun lit -> PropertyBigInt (loc, lit)) + id this#bigint_literal lit prop (fun lit -> PropertyBigInt (loc, lit)) | PropertyIdentifier ident -> id this#identifier ident prop (fun ident -> PropertyIdentifier ident) @@ -2679,7 +3100,8 @@ class ['loc] mapper = else { id = id'; kind; comments = comments' } - method match_object_pattern (object_pattern : ('loc, 'loc) Ast.MatchPattern.ObjectPattern.t) = + method match_object_pattern _loc (object_pattern : ('loc, 'loc) Ast.MatchPattern.ObjectPattern.t) + = let open Ast.MatchPattern.ObjectPattern in let { properties; rest; comments } = object_pattern in let properties' = map_list this#match_object_pattern_property properties in @@ -2714,11 +3136,11 @@ class ['loc] mapper = let open Ast.MatchPattern.ObjectPattern.Property in match key with | StringLiteral (loc, lit) -> - id_loc this#string_literal loc lit key (fun lit -> StringLiteral (loc, lit)) + id this#string_literal lit key (fun lit -> StringLiteral (loc, lit)) | NumberLiteral (loc, lit) -> - id_loc this#number_literal loc lit key (fun lit -> NumberLiteral (loc, lit)) + id this#number_literal lit key (fun lit -> NumberLiteral (loc, lit)) | BigIntLiteral (loc, lit) -> - id_loc this#bigint_literal loc lit key (fun lit -> BigIntLiteral (loc, lit)) + id this#bigint_literal lit key (fun lit -> BigIntLiteral (loc, lit)) | Identifier ident -> id this#identifier ident key (fun ident -> Identifier ident) method match_array_pattern (array_pattern : ('loc, 'loc) Ast.MatchPattern.ArrayPattern.t) = @@ -2742,6 +3164,28 @@ class ['loc] mapper = else { pattern = pattern'; index } + method match_instance_pattern + (instance_pattern : ('loc, 'loc) Ast.MatchPattern.InstancePattern.t) + : ('loc, 'loc) Ast.MatchPattern.InstancePattern.t = + let open Ast.MatchPattern.InstancePattern in + let { constructor; properties; comments } = instance_pattern in + let constructor' = this#match_instance_pattern_constructor constructor in + let properties' = map_loc this#match_object_pattern properties in + let comments' = this#syntax_opt comments in + if constructor == constructor' && properties == properties' && comments == comments' then + instance_pattern + else + { constructor = constructor'; properties = properties'; comments = comments' } + + method match_instance_pattern_constructor + (constructor : ('loc, 'loc) Ast.MatchPattern.InstancePattern.constructor) = + let open Ast.MatchPattern.InstancePattern in + match constructor with + | IdentifierConstructor ident -> + id this#identifier ident constructor (fun x -> IdentifierConstructor x) + | MemberConstructor member -> + id this#match_member_pattern member constructor (fun x -> MemberConstructor x) + method match_rest_pattern _loc (rest : ('loc, 'loc) Ast.MatchPattern.RestPattern.t') = let open Ast.MatchPattern.RestPattern in let { argument; comments } = rest in @@ -2781,6 +3225,15 @@ class ['loc] mapper = | Identifier ident -> id (this#pattern_identifier ~kind:Ast.Variable.Const) ident target (fun x -> Identifier x) + method match_wildcard_pattern (wildcard_pattern : 'loc Ast.MatchPattern.WildcardPattern.t) = + let open Ast.MatchPattern.WildcardPattern in + let { comments; invalid_syntax_default_keyword } = wildcard_pattern in + let comments' = this#syntax_opt comments in + if comments == comments' then + wildcard_pattern + else + { comments = comments'; invalid_syntax_default_keyword } + method member _loc (expr : ('loc, 'loc) Ast.Expression.Member.t) = let open Ast.Expression.Member in let { _object; property; comments } = expr in @@ -2818,6 +3271,24 @@ class ['loc] mapper = method member_property_expression (expr : ('loc, 'loc) Ast.Expression.t) = this#expression expr + method record _loc (expr : ('loc, 'loc) Ast.Expression.Record.t) = + let open Ast.Expression.Record in + let { constructor; targs; properties = (props_loc, props); comments } = expr in + let constructor' = this#expression constructor in + let targs' = map_opt this#call_type_args targs in + let props' = this#object_ props_loc props in + let comments' = this#syntax_opt comments in + if constructor == constructor' && targs == targs' && props == props' && comments == comments' + then + expr + else + { + constructor = constructor'; + targs = targs'; + properties = (props_loc, props'); + comments = comments'; + } + method meta_property _loc (expr : 'loc Ast.Expression.MetaProperty.t) = let open Ast.Expression.MetaProperty in let { meta; property; comments } = expr in @@ -2918,24 +3389,24 @@ class ['loc] mapper = method object_key (key : ('loc, 'loc) Ast.Expression.Object.Property.key) = let open Ast.Expression.Object.Property in match key with - | StringLiteral lit -> id this#object_key_string_literal lit key (fun lit -> StringLiteral lit) - | NumberLiteral lit -> id this#object_key_number_literal lit key (fun lit -> NumberLiteral lit) - | BigIntLiteral lit -> id this#object_key_bigint_literal lit key (fun lit -> BigIntLiteral lit) + | StringLiteral (loc, lit) -> + id this#object_key_string_literal lit key (fun lit -> StringLiteral (loc, lit)) + | NumberLiteral (loc, lit) -> + id this#object_key_number_literal lit key (fun lit -> NumberLiteral (loc, lit)) + | BigIntLiteral (loc, lit) -> + id this#object_key_bigint_literal lit key (fun lit -> BigIntLiteral (loc, lit)) | Identifier ident -> id this#object_key_identifier ident key (fun ident -> Identifier ident) | PrivateName ident -> id this#private_name ident key (fun ident -> PrivateName ident) | Computed computed -> id this#object_key_computed computed key (fun expr -> Computed expr) - method object_key_string_literal (literal : 'loc * 'loc Ast.StringLiteral.t) = - let (loc, lit) = literal in - id_loc this#string_literal loc lit literal (fun lit -> (loc, lit)) + method object_key_string_literal (literal : 'loc Ast.StringLiteral.t) = + this#string_literal literal - method object_key_number_literal (literal : 'loc * 'loc Ast.NumberLiteral.t) = - let (loc, lit) = literal in - id_loc this#number_literal loc lit literal (fun lit -> (loc, lit)) + method object_key_number_literal (literal : 'loc Ast.NumberLiteral.t) = + this#number_literal literal - method object_key_bigint_literal (literal : 'loc * 'loc Ast.BigIntLiteral.t) = - let (loc, lit) = literal in - id_loc this#bigint_literal loc lit literal (fun lit -> (loc, lit)) + method object_key_bigint_literal (literal : 'loc Ast.BigIntLiteral.t) = + this#bigint_literal literal method object_key_identifier (ident : ('loc, 'loc) Ast.Identifier.t) = this#identifier ident @@ -2943,18 +3414,23 @@ class ['loc] mapper = method opaque_type _loc (otype : ('loc, 'loc) Ast.Statement.OpaqueType.t) = let open Ast.Statement.OpaqueType in - let { id; tparams; impltype; supertype; comments } = otype in + let { id; tparams; impl_type; lower_bound; upper_bound; legacy_upper_bound; comments } = + otype + in let id' = this#binding_type_identifier id in let tparams' = map_opt (this#type_params ~kind:OpaqueTypeTP) tparams in - let impltype' = map_opt this#type_ impltype in - let supertype' = map_opt this#type_ supertype in + let impl_type' = map_opt this#type_ impl_type in + let legacy_upper_bound' = map_opt this#type_ legacy_upper_bound in + let lower_bound' = map_opt this#type_ lower_bound in + let upper_bound' = map_opt this#type_ upper_bound in let comments' = this#syntax_opt comments in if id == id' - && impltype == impltype' + && impl_type == impl_type' && tparams == tparams' - && impltype == impltype' - && supertype == supertype' + && lower_bound == lower_bound' + && upper_bound == upper_bound' + && legacy_upper_bound == legacy_upper_bound' && comments == comments' then otype @@ -2962,8 +3438,10 @@ class ['loc] mapper = { id = id'; tparams = tparams'; - impltype = impltype'; - supertype = supertype'; + impl_type = impl_type'; + lower_bound = lower_bound'; + upper_bound = upper_bound'; + legacy_upper_bound = legacy_upper_bound'; comments = comments'; } @@ -2996,22 +3474,23 @@ class ['loc] mapper = let (loc, patt) = expr in let patt' = match patt with - | Object { Object.properties; annot; comments } -> + | Object { Object.properties; annot; optional; comments } -> let properties' = map_list (this#pattern_object_p ?kind) properties in let annot' = this#type_annotation_hint annot in let comments' = this#syntax_opt comments in if properties' == properties && annot' == annot && comments' == comments then patt else - Object { Object.properties = properties'; annot = annot'; comments = comments' } - | Array { Array.elements; annot; comments } -> + Object + { Object.properties = properties'; annot = annot'; optional; comments = comments' } + | Array { Array.elements; annot; optional; comments } -> let elements' = map_list (this#pattern_array_e ?kind) elements in let annot' = this#type_annotation_hint annot in let comments' = this#syntax_opt comments in if comments == comments' && elements' == elements && annot' == annot then patt else - Array { Array.elements = elements'; annot = annot'; comments = comments' } + Array { Array.elements = elements'; annot = annot'; optional; comments = comments' } | Identifier { Identifier.name; annot; optional } -> let name' = this#pattern_identifier ?kind name in let annot' = this#type_annotation_hint annot in @@ -3030,17 +3509,17 @@ class ['loc] mapper = ignore kind; this#identifier ident - method pattern_string_literal ?kind loc (expr : 'loc Ast.StringLiteral.t) = + method pattern_string_literal ?kind _loc (expr : 'loc Ast.StringLiteral.t) = ignore kind; - this#string_literal loc expr + this#string_literal expr - method pattern_number_literal ?kind loc (expr : 'loc Ast.NumberLiteral.t) = + method pattern_number_literal ?kind _loc (expr : 'loc Ast.NumberLiteral.t) = ignore kind; - this#number_literal loc expr + this#number_literal expr - method pattern_bigint_literal ?kind loc (expr : 'loc Ast.BigIntLiteral.t) = + method pattern_bigint_literal ?kind _loc (expr : 'loc Ast.BigIntLiteral.t) = ignore kind; - this#bigint_literal loc expr + this#bigint_literal expr method pattern_object_p ?kind (p : ('loc, 'loc) Ast.Pattern.Object.property) = let open Ast.Pattern.Object in @@ -3191,7 +3670,7 @@ class ['loc] mapper = (type_guard_annotation : ('loc, 'loc) Ast.Type.type_guard_annotation) = let (loc, type_guard) = type_guard_annotation in let type_guard' = this#type_guard type_guard in - if type_guard' = type_guard then + if type_guard' == type_guard then type_guard_annotation else (loc, type_guard') @@ -3217,6 +3696,84 @@ class ['loc] mapper = else (loc, { argument = argument'; comments = comments' }) + method record_declaration _loc (record : ('loc, 'loc) Ast.Statement.RecordDeclaration.t) = + let open Ast.Statement.RecordDeclaration in + let { id; tparams; implements; body; comments; invalid_syntax } = record in + let id' = this#pattern_identifier ~kind:Ast.Variable.Const id in + let tparams' = map_opt (this#type_params ~kind:RecordTP) tparams in + let implements' = map_opt this#class_implements implements in + let body' = this#record_body body in + let comments' = this#syntax_opt comments in + if + id == id' + && tparams == tparams' + && implements == implements' + && body == body' + && comments == comments' + then + record + else + { + id = id'; + tparams = tparams'; + implements = implements'; + body = body'; + comments = comments'; + invalid_syntax; + } + + method record_body (record_body : ('loc, 'loc) Ast.Statement.RecordDeclaration.Body.t) = + let open Ast.Statement.RecordDeclaration.Body in + let (loc, { body; comments }) = record_body in + let body' = map_list this#record_element body in + let comments' = this#syntax_opt comments in + if body == body' && comments == comments' then + record_body + else + (loc, { body = body'; comments = comments' }) + + method record_element (element : ('loc, 'loc) Ast.Statement.RecordDeclaration.Body.element) = + let open Ast.Statement.RecordDeclaration.Body in + match element with + | Method (loc, meth) -> + id_loc this#class_method loc meth element (fun meth -> Method (loc, meth)) + | Property (loc, prop) -> + id_loc this#record_property loc prop element (fun prop -> Property (loc, prop)) + | StaticProperty (loc, prop) -> + id_loc this#record_static_property loc prop element (fun prop -> StaticProperty (loc, prop)) + + method record_property _loc (prop : ('loc, 'loc) Ast.Statement.RecordDeclaration.Property.t') = + let open Ast.Statement.RecordDeclaration.Property in + let { key; annot; default_value; comments; invalid_syntax } = prop in + let key' = this#object_key key in + let annot' = this#type_annotation annot in + let default_value' = map_opt this#expression default_value in + let comments' = this#syntax_opt comments in + if key' == key && annot' == annot && default_value' == default_value && comments' == comments + then + prop + else + { + key = key'; + annot = annot'; + default_value = default_value'; + comments = comments'; + invalid_syntax; + } + + method record_static_property + _loc (prop : ('loc, 'loc) Ast.Statement.RecordDeclaration.StaticProperty.t') = + let open Ast.Statement.RecordDeclaration.StaticProperty in + let { key; annot; value; comments; invalid_syntax } = prop in + let key' = this#object_key key in + let annot' = this#type_annotation annot in + let value' = this#expression value in + let comments' = this#syntax_opt comments in + if key' == key && annot' == annot && value' == value && comments' == comments then + prop + else + { key = key'; annot = annot'; value = value'; comments = comments'; invalid_syntax } + method return _loc (stmt : ('loc, 'loc) Ast.Statement.Return.t) = let open Ast.Statement.Return in let { argument; comments; return_out } = stmt in @@ -3287,25 +3844,26 @@ class ['loc] mapper = method switch_case (case : ('loc, 'loc) Ast.Statement.Switch.Case.t) = let open Ast.Statement.Switch.Case in - let (loc, { test; consequent; comments }) = case in + let (loc, { test; case_test_loc; consequent; comments }) = case in let test' = map_opt this#expression test in let consequent' = this#statement_list consequent in let comments' = this#syntax_opt comments in if test == test' && consequent == consequent' && comments == comments' then case else - (loc, { test = test'; consequent = consequent'; comments = comments' }) + (loc, { test = test'; case_test_loc; consequent = consequent'; comments = comments' }) method tagged_template _loc (expr : ('loc, 'loc) Ast.Expression.TaggedTemplate.t) = let open Ast.Expression.TaggedTemplate in - let { tag; quasi; comments } = expr in + let { tag; targs; quasi; comments } = expr in let tag' = this#expression tag in + let targs' = map_opt this#call_type_args targs in let quasi' = map_loc this#template_literal quasi in let comments' = this#syntax_opt comments in - if tag == tag' && quasi == quasi' && comments == comments' then + if tag == tag' && targs == targs' && quasi == quasi' && comments == comments' then expr else - { tag = tag'; quasi = quasi'; comments = comments' } + { tag = tag'; targs = targs'; quasi = quasi'; comments = comments' } method template_literal _loc (expr : ('loc, 'loc) Ast.Expression.TemplateLiteral.t) = let open Ast.Expression.TemplateLiteral in @@ -3380,7 +3938,7 @@ class ['loc] mapper = let expression' = this#expression expression in let annot' = this#type_annotation annot in let comments' = this#syntax_opt comments in - if expression' == expression && annot' = annot && comments' == comments then + if expression' == expression && annot' == annot && comments' == comments then expr else { expression = expression'; annot = annot'; comments = comments' } diff --git a/compiler/flow_parser/parser/flow_ast_utils.ml b/compiler/flow_parser/parser/flow_ast_utils.ml index 2c35b2050c7..50c177d1365 100644 --- a/compiler/flow_parser/parser/flow_ast_utils.ml +++ b/compiler/flow_parser/parser/flow_ast_utils.ml @@ -114,9 +114,19 @@ let rec match_pattern_has_binding = || List.exists (fun { ArrayPattern.Element.pattern; _ } -> match_pattern_has_binding pattern) elements + | (_, InstancePattern { InstancePattern.properties = (properties_loc, properties); _ }) -> + match_pattern_has_binding (properties_loc, ObjectPattern properties) | (_, OrPattern { OrPattern.patterns; _ }) -> List.exists match_pattern_has_binding patterns | (_, AsPattern _) -> true +let pattern_has_type_annotation = + let open Pattern in + function + | (_, Identifier { Identifier.annot = Type.Available _; _ }) -> true + | (_, Object { Object.annot = Type.Available _; _ }) -> true + | (_, Array { Array.annot = Type.Available _; _ }) -> true + | _ -> false + let string_of_variable_kind = function | Variable.Var -> "var" | Variable.Let -> "let" @@ -302,6 +312,26 @@ let is_module_dot_exports callee = true | _ -> false +(** Checks if an expression is a well-known Symbol like Symbol.iterator. + Returns Some "@@iterator" etc. if it is, None otherwise. *) +let well_known_symbol_name expr = + match expr with + | ( _, + E.Member + { + E.Member._object = (_, E.Identifier (_, { I.name = "Symbol"; comments = _ })); + property = E.Member.PropertyIdentifier (_, { I.name; comments = _ }); + comments = _; + } + ) -> + (match name with + | "iterator" -> Some "@@iterator" + | "asyncIterator" -> Some "@@asyncIterator" + | "dispose" -> Some "@@dispose" + | "asyncDispose" -> Some "@@asyncDispose" + | _ -> None) + | _ -> None + let get_call_to_jest_module_mocking_fn callee arguments = match (callee, arguments) with | ( ( _, @@ -361,6 +391,7 @@ let acceptable_statement_in_declaration_context ~in_declare_namespace = | Block _ -> Error "block" | Break _ -> Error "break" | ClassDeclaration _ -> Error "class declaration" + | ComponentDeclaration { ComponentDeclaration.body = None; _ } -> Ok () | ComponentDeclaration _ -> Error "component declaration" | Continue _ -> Error "continue" | Debugger _ -> Error "debugger" @@ -376,11 +407,21 @@ let acceptable_statement_in_declaration_context ~in_declare_namespace = | If _ -> Error "if" | Labeled _ -> Error "labeled" | Match _ -> Error "match" + | RecordDeclaration _ -> Error "record declaration" | Return _ -> Error "return" | Switch _ -> Error "switch" | Throw _ -> Error "throw" | Try _ -> Error "try" - | VariableDeclaration _ -> Error "variable declaration" + | VariableDeclaration { VariableDeclaration.declarations; _ } -> + (* Allow variable declarations if all have type annotations and no initializers (ambient) *) + let is_ambient_declarator = function + | (_, { VariableDeclaration.Declarator.id; init = None }) -> pattern_has_type_annotation id + | _ -> false + in + if List.for_all is_ambient_declarator declarations then + Ok () + else + Error "variable declaration" | While _ -> Error "while" | With _ -> Error "with" | ImportDeclaration _ -> @@ -406,7 +447,10 @@ let acceptable_statement_in_declaration_context ~in_declare_namespace = | DeclareVariable _ | Empty _ | EnumDeclaration _ + | ExportAssignment _ + | NamespaceExportDeclaration _ | ExportNamedDeclaration { ExportNamedDeclaration.export_kind = ExportType; _ } + | ImportEqualsDeclaration _ | InterfaceDeclaration _ | OpaqueType _ | TypeAlias _ -> @@ -427,6 +471,13 @@ let rec is_type_only_declaration_statement (_, stmt') = DeclareExportDeclaration. { declaration = Some (NamedType _ | NamedOpaqueType _ | Interface _); _ } -> true + | DeclareExportDeclaration + DeclareExportDeclaration. + { + declaration = Some (Namespace (_, { DeclareNamespace.body = (_, { Block.body; _ }); _ })); + _; + } -> + List.for_all is_type_only_declaration_statement body | DeclareNamespace { DeclareNamespace.body = (_, { Block.body; _ }); _ } -> List.for_all is_type_only_declaration_statement body | ExportNamedDeclaration { ExportNamedDeclaration.export_kind; _ } -> export_kind = ExportType @@ -438,6 +489,8 @@ let rec is_type_only_declaration_statement (_, stmt') = | Debugger _ | DoWhile _ | EnumDeclaration _ + | ExportAssignment _ + | NamespaceExportDeclaration _ | ExportDefaultDeclaration _ | Expression _ | For _ @@ -445,8 +498,10 @@ let rec is_type_only_declaration_statement (_, stmt') = | ForOf _ | FunctionDeclaration _ | If _ + | ImportEqualsDeclaration _ | Labeled _ | Match _ + | RecordDeclaration _ | Return _ | Switch _ | Throw _ @@ -593,6 +648,7 @@ module ExpressionSort = struct | Object | OptionalCall | OptionalMember + | Record | Satisfies | Sequence | Super @@ -627,6 +683,7 @@ module ExpressionSort = struct | Object -> "object" | OptionalCall -> "optional call expression" | OptionalMember -> "optional member expression" + | Record -> "record expression" | Satisfies -> "satisfies expression" | Sequence -> "sequence" | Super -> "`super` reference" @@ -741,14 +798,15 @@ let get_inferred_type_guard_candidate params body return = Function.Params.params = [ ( _, - { - Function.Param.argument = - ( _, - Pattern.Identifier - { Pattern.Identifier.name = (loc, { Identifier.name; _ }); _ } - ); - _; - } + Function.Param.RegularParam + { + argument = + ( _, + Pattern.Identifier + { Pattern.Identifier.name = (loc, { Identifier.name; _ }); _ } + ); + _; + } ); ]; rest = None; @@ -759,3 +817,111 @@ let get_inferred_type_guard_candidate params body return = | _ -> None end | _ -> None + +let rec unwrap_nonnull_lhs_expr : + 'loc 'tloc1 'tloc2. + ('loc, 'tloc1) Expression.t -> + ('loc, 'tloc1) Expression.t + * bool + * (('loc, 'tloc2) Expression.t -> + filter_nullish:('tloc1 -> 'tloc2) -> + ('loc, 'tloc2) Expression.t + ) = + fun expr -> + match expr with + | ( loc, + Expression.Unary { Expression.Unary.operator = Expression.Unary.Nonnull; argument; comments } + ) -> + let (argument, _, reconstruct) = unwrap_nonnull_lhs_expr argument in + let reconstruct argument ~filter_nullish = + ( filter_nullish loc, + Expression.Unary + { + Expression.Unary.operator = Expression.Unary.Nonnull; + argument = reconstruct ~filter_nullish argument; + comments; + } + ) + in + (argument, true, reconstruct) + | _ -> (expr, false, (fun argument ~filter_nullish:_ -> argument)) + +let unwrap_nonnull_lhs : 'loc 'tloc. ('loc, 'tloc) Pattern.t -> ('loc, 'tloc) Pattern.t * bool = + fun pat -> + match pat with + | (loc, Pattern.Expression expr) -> + let (expr, optional, _) = unwrap_nonnull_lhs_expr expr in + begin + match expr with + | (e_loc, Expression.Identifier name) -> + assert optional; + ( ( loc, + Pattern.Identifier + { Pattern.Identifier.name; optional = false; annot = Type.Missing e_loc } + ), + true + ) + | _ -> ((loc, Pattern.Expression expr), optional) + end + | _ -> (pat, false) + +let effective_export_kind ~statement_export_kind specifier_export_kind = + match specifier_export_kind with + | Statement.ExportType -> Statement.ExportType + | Statement.ExportValue -> statement_export_kind + +let export_specifiers_has_value_export ~statement_export_kind specs = + let open Statement.ExportNamedDeclaration in + List.exists + (fun (_, { ExportSpecifier.export_kind = specifier_export_kind; _ }) -> + effective_export_kind ~statement_export_kind specifier_export_kind = Statement.ExportValue) + specs + +let pattern_optional = function + | (_, Pattern.Object { Pattern.Object.optional; _ }) -> optional + | (_, Pattern.Array { Pattern.Array.optional; _ }) -> optional + | (_, Pattern.Identifier { Pattern.Identifier.optional; _ }) -> optional + | _ -> false + +let pattern_annot (patt : ('M, 'T) Pattern.t) = + match patt with + | (_, Pattern.Object { Pattern.Object.annot; _ }) -> annot + | (_, Pattern.Array { Pattern.Array.annot; _ }) -> annot + | (_, Pattern.Identifier { Pattern.Identifier.annot; _ }) -> annot + | (_, Pattern.Expression _) -> failwith "Expression patterns have no annotation" + +let function_type_param_parts param = + let open Type.Function.Param in + match param with + | Anonymous annot -> (None, annot, false) + | Labeled { name; annot; optional } -> (Some name, annot, optional) + | Destructuring pattern -> + (match pattern_annot pattern with + | Type.Available (_, annot) -> (None, annot, pattern_optional pattern) + | Type.Missing _ -> failwith "Destructuring function type param must have annotation") + +let string_of_enum_explicit_type = + let open Statement.EnumDeclaration in + function + | Boolean -> "boolean" + | Number -> "number" + | String -> "string" + | Symbol -> "symbol" + | BigInt -> "bigint" + +let string_of_enum_member_name = + let open Statement.EnumDeclaration in + function + | Identifier (_, { I.name; _ }) -> name + | StringLiteral (_, { StringLiteral.value; _ }) -> value + +let string_of_bigint { BigIntLiteral.value; raw; comments = _ } = + (* https://github.com/estree/estree/blob/master/es2020.md#bigintliteral + * `bigint` property is the string representation of the `BigInt` value. + * It must contain only decimal digits and not include numeric separators `_` or the suffix `n`. + *) + match value with + | Some value -> Int64.to_string value + | None -> + (* Remove the 'n' suffix and the underscores. *) + String.sub raw 0 (String.length raw - 1) |> String.split_on_char '_' |> String.concat "" diff --git a/compiler/flow_parser/parser/flow_ast_utils.mli b/compiler/flow_parser/parser/flow_ast_utils.mli index 531f419db71..b89717bbfa6 100644 --- a/compiler/flow_parser/parser/flow_ast_utils.mli +++ b/compiler/flow_parser/parser/flow_ast_utils.mli @@ -24,6 +24,8 @@ val pattern_has_binding : ('m, 't) Flow_ast.Pattern.t -> bool val match_pattern_has_binding : ('m, 't) Flow_ast.MatchPattern.t -> bool +val pattern_has_type_annotation : ('m, 't) Flow_ast.Pattern.t -> bool + val string_of_variable_kind : Flow_ast.Variable.kind -> string val partition_directives : @@ -51,6 +53,8 @@ val is_call_to_object_static_method : ('a, 'b) Flow_ast.Expression.t -> bool val is_module_dot_exports : ('a, 'b) Flow_ast.Expression.t -> bool +val well_known_symbol_name : ('a, 'b) Flow_ast.Expression.t -> string option + val get_call_to_jest_module_mocking_fn : ('loc, 'annot) Flow_ast.Expression.t -> ('loc, 'annot) Flow_ast.Expression.ArgList.t -> @@ -147,6 +151,7 @@ module ExpressionSort : sig | Object | OptionalCall | OptionalMember + | Record | Satisfies | Sequence | Super @@ -193,3 +198,38 @@ val get_inferred_type_guard_candidate : ('l, 't) Flow_ast.Function.body -> ('l, 't) Flow_ast.Function.ReturnAnnot.t -> ('t * string) option + +val unwrap_nonnull_lhs_expr : + 'loc 'tloc. + ('loc, 'loc) Flow_ast.Expression.t -> + ('loc, 'loc) Flow_ast.Expression.t + * bool + * (('loc, 'tloc) Flow_ast.Expression.t -> + filter_nullish:('loc -> 'tloc) -> + ('loc, 'tloc) Flow_ast.Expression.t + ) + +val unwrap_nonnull_lhs : + 'loc 'tloc. ('loc, 'tloc) Flow_ast.Pattern.t -> ('loc, 'tloc) Flow_ast.Pattern.t * bool + +val effective_export_kind : + statement_export_kind:Flow_ast.Statement.export_kind -> + Flow_ast.Statement.export_kind -> + Flow_ast.Statement.export_kind + +val export_specifiers_has_value_export : + statement_export_kind:Flow_ast.Statement.export_kind -> + ('M, 'T) Flow_ast.Statement.ExportNamedDeclaration.ExportSpecifier.t list -> + bool + +val pattern_optional : ('M, 'T) Flow_ast.Pattern.t -> bool + +val function_type_param_parts : + ('M, 'T) Flow_ast.Type.Function.Param.t' -> + ('M, 'T) Flow_ast.Identifier.t option * ('M, 'T) Flow_ast.Type.t * bool + +val string_of_enum_explicit_type : Flow_ast.Statement.EnumDeclaration.explicit_type -> string + +val string_of_enum_member_name : 'M Flow_ast.Statement.EnumDeclaration.member_name -> string + +val string_of_bigint : 'm Flow_ast.BigIntLiteral.t -> string diff --git a/compiler/flow_parser/parser/flow_lexer.ml b/compiler/flow_parser/parser/flow_lexer.ml index 41d35e96073..1e7370ad933 100644 --- a/compiler/flow_parser/parser/flow_lexer.ml +++ b/compiler/flow_parser/parser/flow_lexer.ml @@ -240,6 +240,10 @@ let loc_of_token env lex_token = | T_REGEXP (loc, _, _) -> loc | _ -> loc_of_lexbuf env env.lex_lb +let advance_loc_by_char loc = + let open Loc in + { loc with start = loc._end; _end = { loc._end with column = loc._end.column + 1 } } + let lex_error (env : Lex_env.t) loc err : Lex_env.t = let lex_errors_acc = (loc, err) :: env.lex_state.lex_errors_acc in { env with lex_state = { lex_errors_acc } } @@ -315,6 +319,18 @@ let mk_bignum_singleton kind lexeme = let value = Int64.of_string_opt postraw in T_BIGINT_SINGLETON_TYPE { kind; value; raw } +let mk_number ~for_type_token kind lexbuf = + if for_type_token then + mk_num_singleton kind (Sedlexing.lexeme lexbuf) + else + T_NUMBER { kind; raw = lexeme lexbuf } + +let mk_bigint ~for_type_token kind lexbuf = + if for_type_token then + mk_bignum_singleton kind (Sedlexing.lexeme lexbuf) + else + T_BIGINT { kind; raw = lexeme lexbuf } + (* This is valid since the escapes are already tackled*) let assert_valid_unicode_in_identifier env loc code = if Js_id.is_valid_unicode_id code then @@ -572,7 +588,7 @@ let rec template_part env cooked raw lexbuf = template_part env cooked raw lexbuf | _ -> failwith "unreachable template_part" -let token (env : Lex_env.t) lexbuf : result = +let token_base ~for_type_token (env : Lex_env.t) lexbuf : result = match%sedlex lexbuf with | line_terminator_sequence -> let env = new_line env lexbuf in @@ -588,7 +604,13 @@ let token (env : Lex_env.t) lexbuf : result = if not (is_comment_syntax_enabled env) then ( let start_pos = start_pos_of_lexbuf env lexbuf in let buf = Buffer.create 127 in - Buffer.add_string buf (String.sub pattern 2 (String.length pattern - 2)); + Buffer.add_string + buf + ( if for_type_token then + pattern + else + String.sub pattern 2 (String.length pattern - 2) + ); let (env, end_pos) = comment env buf lexbuf in Comment (env, mk_comment env start_pos end_pos buf true) ) else @@ -608,16 +630,6 @@ let token (env : Lex_env.t) lexbuf : result = Token (env, T_COLON) else Continue env - | "*/" -> - if is_in_comment_syntax env then - let env = in_comment_syntax false env in - Continue env - else ( - Sedlexing.rollback lexbuf; - match%sedlex lexbuf with - | "*" -> Token (env, T_MULT) - | _ -> failwith "expected *" - ) | "//" -> let start_pos = start_pos_of_lexbuf env lexbuf in let buf = Buffer.create 127 in @@ -626,7 +638,7 @@ let token (env : Lex_env.t) lexbuf : result = (* Support for the shebang at the beginning of a file. It is treated like a * comment at the beginning or an error elsewhere *) | "#!" -> - if Sedlexing.lexeme_start lexbuf = 0 then + if (not for_type_token) && Sedlexing.lexeme_start lexbuf = 0 then let start = start_pos_of_lexbuf env lexbuf in let buf = Buffer.create 127 in let (env, _end) = line_comment env buf lexbuf in @@ -654,71 +666,73 @@ let token (env : Lex_env.t) lexbuf : result = let _end = end_pos_of_lexbuf env lexbuf in let loc = { Loc.source = Lex_env.source env; start; _end } in Token (env, T_TEMPLATE_PART (loc, Buffer.contents value, Buffer.contents raw, true, is_tail)) + (* + * Number literals + *) | (binbigint, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | binbigint -> Token (env, T_BIGINT { kind = BIG_BINARY; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token bigint" + | binbigint -> Token (env, mk_bigint ~for_type_token BIG_BINARY lexbuf) + | _ -> failwith "unreachable token_base binbigint" ) - | binbigint -> Token (env, T_BIGINT { kind = BIG_BINARY; raw = lexeme lexbuf }) + | binbigint -> Token (env, mk_bigint ~for_type_token BIG_BINARY lexbuf) | (binnumber, (letter | '2' .. '9'), Star alphanumeric) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | binnumber -> Token (env, T_NUMBER { kind = BINARY; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token bignumber" + | binnumber -> Token (env, mk_number ~for_type_token BINARY lexbuf) + | _ -> failwith "unreachable token_base binnumber" ) - | binnumber -> Token (env, T_NUMBER { kind = BINARY; raw = lexeme lexbuf }) + | binnumber -> Token (env, mk_number ~for_type_token BINARY lexbuf) | (octbigint, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | octbigint -> Token (env, T_BIGINT { kind = BIG_OCTAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token octbigint" + | octbigint -> Token (env, mk_bigint ~for_type_token BIG_OCTAL lexbuf) + | _ -> failwith "unreachable token_base octbigint" ) - | octbigint -> Token (env, T_BIGINT { kind = BIG_OCTAL; raw = lexeme lexbuf }) + | octbigint -> Token (env, mk_bigint ~for_type_token BIG_OCTAL lexbuf) | (octnumber, (letter | '8' .. '9'), Star alphanumeric) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | octnumber -> Token (env, T_NUMBER { kind = OCTAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token octnumber" + | octnumber -> Token (env, mk_number ~for_type_token OCTAL lexbuf) + | _ -> failwith "unreachable token_base octnumber" ) - | octnumber -> Token (env, T_NUMBER { kind = OCTAL; raw = lexeme lexbuf }) + | octnumber -> Token (env, mk_number ~for_type_token OCTAL lexbuf) | (legacynonoctnumber, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | legacynonoctnumber -> - Token (env, T_NUMBER { kind = LEGACY_NON_OCTAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token legacynonoctnumber" + | legacynonoctnumber -> Token (env, mk_number ~for_type_token LEGACY_NON_OCTAL lexbuf) + | _ -> failwith "unreachable token_base legacynonoctnumber" ) - | legacynonoctnumber -> Token (env, T_NUMBER { kind = LEGACY_NON_OCTAL; raw = lexeme lexbuf }) + | legacynonoctnumber -> Token (env, mk_number ~for_type_token LEGACY_NON_OCTAL lexbuf) | (legacyoctnumber, (letter | '8' .. '9'), Star alphanumeric) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | legacyoctnumber -> Token (env, T_NUMBER { kind = LEGACY_OCTAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token legacyoctnumber" + | legacyoctnumber -> Token (env, mk_number ~for_type_token LEGACY_OCTAL lexbuf) + | _ -> failwith "unreachable token_base legacyoctnumber" ) - | legacyoctnumber -> Token (env, T_NUMBER { kind = LEGACY_OCTAL; raw = lexeme lexbuf }) + | legacyoctnumber -> Token (env, mk_number ~for_type_token LEGACY_OCTAL lexbuf) | (hexbigint, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | hexbigint -> Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token hexbigint" + | hexbigint -> Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) + | _ -> failwith "unreachable token_base hexbigint" ) - | hexbigint -> Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) + | hexbigint -> Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) | (hexnumber, non_hex_letter, Star alphanumeric) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | hexnumber -> Token (env, T_NUMBER { kind = NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token hexnumber" + | hexnumber -> Token (env, mk_number ~for_type_token NORMAL lexbuf) + | _ -> failwith "unreachable token_base hexnumber" ) - | hexnumber -> Token (env, T_NUMBER { kind = NORMAL; raw = lexeme lexbuf }) + | hexnumber -> Token (env, mk_number ~for_type_token NORMAL lexbuf) | (scibigint, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> @@ -726,21 +740,21 @@ let token (env : Lex_env.t) lexbuf : result = | scibigint -> let loc = loc_of_lexbuf env lexbuf in let env = lex_error env loc Parse_error.InvalidSciBigInt in - Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token scibigint" + Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) + | _ -> failwith "unreachable token_base scibigint" ) | scibigint -> let loc = loc_of_lexbuf env lexbuf in let env = lex_error env loc Parse_error.InvalidSciBigInt in - Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) + Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) | (scinumber, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | scinumber -> Token (env, T_NUMBER { kind = NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token scinumber" + | scinumber -> Token (env, mk_number ~for_type_token NORMAL lexbuf) + | _ -> failwith "unreachable token_base scinumber" ) - | scinumber -> Token (env, T_NUMBER { kind = NORMAL; raw = lexeme lexbuf }) + | scinumber -> Token (env, mk_number ~for_type_token NORMAL lexbuf) | (floatbigint, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> @@ -748,42 +762,49 @@ let token (env : Lex_env.t) lexbuf : result = | floatbigint -> let loc = loc_of_lexbuf env lexbuf in let env = lex_error env loc Parse_error.InvalidFloatBigInt in - Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token floatbigint" + Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) + | _ -> failwith "unreachable token_base floatbigint" ) | (wholebigint, word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with - | wholebigint -> Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token wholebigint" + | wholebigint -> Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) + | _ -> failwith "unreachable token_base wholebigint" ) | floatbigint -> let loc = loc_of_lexbuf env lexbuf in let env = lex_error env loc Parse_error.InvalidFloatBigInt in - Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) - | wholebigint -> Token (env, T_BIGINT { kind = BIG_NORMAL; raw = lexeme lexbuf }) + Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) + | wholebigint -> Token (env, mk_bigint ~for_type_token BIG_NORMAL lexbuf) | ((wholenumber | floatnumber), word) -> (* Numbers cannot be immediately followed by words *) recover env lexbuf ~f:(fun env lexbuf -> match%sedlex lexbuf with | wholenumber | floatnumber -> - Token (env, T_NUMBER { kind = NORMAL; raw = lexeme lexbuf }) - | _ -> failwith "unreachable token wholenumber" + Token (env, mk_number ~for_type_token NORMAL lexbuf) + | _ -> failwith "unreachable token_base wholenumber" ) | wholenumber | floatnumber -> - Token (env, T_NUMBER { kind = NORMAL; raw = lexeme lexbuf }) + Token (env, mk_number ~for_type_token NORMAL lexbuf) (* TODO: Use [Symbol.iterator] instead of @@iterator. *) (* `@` is not a valid unicode name *) | "@@iterator" - | "@@asyncIterator" -> + | "@@asyncIterator" + | "@@dispose" + | "@@asyncDispose" -> let loc = loc_of_lexbuf env lexbuf in let raw = lexeme lexbuf in Token (env, T_IDENTIFIER { loc; value = raw; raw }) (* Syntax *) - | "{" -> Token (env, T_LCURLY) + | "{" -> + if for_type_token && Sedlexing.peek lexbuf 0 = Char.code '|' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_LCURLYBAR) + ) else + Token (env, T_LCURLY) | "}" -> Token (env, T_RCURLY) | "(" -> Token (env, T_LPAREN) | ")" -> Token (env, T_RPAREN) @@ -794,50 +815,195 @@ let token (env : Lex_env.t) lexbuf : result = | ";" -> Token (env, T_SEMICOLON) | "," -> Token (env, T_COMMA) | ":" -> Token (env, T_COLON) - | ("?.", digit) -> - Sedlexing.rollback lexbuf; - (match%sedlex lexbuf with - | "?" -> Token (env, T_PLING) - | _ -> failwith "expected ?") - | "?." -> Token (env, T_PLING_PERIOD) - | "??" -> Token (env, T_PLING_PLING) - | "?" -> Token (env, T_PLING) - | "&&" -> Token (env, T_AND) - | "||" -> Token (env, T_OR) + | "?" -> + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '?' then + if for_type_token then + Token (env, T_PLING) + else ( + Sedlexing.bump lexbuf 1; + if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_NULLISH_ASSIGN) + ) else + Token (env, T_PLING_PLING) + ) + else if next = Char.code '.' then + if for_type_token then ( + Sedlexing.bump lexbuf 1; + Token (env, T_PLING_PERIOD) + ) else + let next2 = Sedlexing.peek lexbuf 1 in + if next2 >= Char.code '0' && next2 <= Char.code '9' then + Token (env, T_PLING) + else ( + Sedlexing.bump lexbuf 1; + Token (env, T_PLING_PERIOD) + ) + else + Token (env, T_PLING) | "===" -> Token (env, T_STRICT_EQUAL) | "!==" -> Token (env, T_STRICT_NOT_EQUAL) - | "<=" -> Token (env, T_LESS_THAN_EQUAL) - | ">=" -> Token (env, T_GREATER_THAN_EQUAL) | "==" -> Token (env, T_EQUAL) | "!=" -> Token (env, T_NOT_EQUAL) - | "++" -> Token (env, T_INCR) - | "--" -> Token (env, T_DECR) - | "<<=" -> Token (env, T_LSHIFT_ASSIGN) - | "<<" -> Token (env, T_LSHIFT) - | ">>=" -> Token (env, T_RSHIFT_ASSIGN) - | ">>>=" -> Token (env, T_RSHIFT3_ASSIGN) - | ">>>" -> Token (env, T_RSHIFT3) - | ">>" -> Token (env, T_RSHIFT) - | "+=" -> Token (env, T_PLUS_ASSIGN) - | "-=" -> Token (env, T_MINUS_ASSIGN) - | "*=" -> Token (env, T_MULT_ASSIGN) - | "**=" -> Token (env, T_EXP_ASSIGN) - | "%=" -> Token (env, T_MOD_ASSIGN) - | "&=" -> Token (env, T_BIT_AND_ASSIGN) - | "|=" -> Token (env, T_BIT_OR_ASSIGN) + | "<" -> + if for_type_token then + Token (env, T_LESS_THAN) + else + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '<' then ( + Sedlexing.bump lexbuf 1; + if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_LSHIFT_ASSIGN) + ) else + Token (env, T_LSHIFT) + ) else if next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_LESS_THAN_EQUAL) + ) else + Token (env, T_LESS_THAN) + | ">" -> + if for_type_token then + Token (env, T_GREATER_THAN) + else + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '>' then ( + Sedlexing.bump lexbuf 1; + let next2 = Sedlexing.peek lexbuf 0 in + if next2 = Char.code '>' then ( + Sedlexing.bump lexbuf 1; + if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_RSHIFT3_ASSIGN) + ) else + Token (env, T_RSHIFT3) + ) else if next2 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_RSHIFT_ASSIGN) + ) else + Token (env, T_RSHIFT) + ) else if next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_GREATER_THAN_EQUAL) + ) else + Token (env, T_GREATER_THAN) + | "+" -> + if for_type_token then + Token (env, T_PLUS) + else + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '+' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_INCR) + ) else if next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_PLUS_ASSIGN) + ) else + Token (env, T_PLUS) + | "-" -> + if for_type_token then + Token (env, T_MINUS) + else + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '-' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_DECR) + ) else if next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_MINUS_ASSIGN) + ) else + Token (env, T_MINUS) + | "*" -> + if is_in_comment_syntax env && Sedlexing.peek lexbuf 0 = Char.code '/' then ( + let env = in_comment_syntax false env in + Sedlexing.bump lexbuf 1; + Continue env + ) else if for_type_token then + Token (env, T_MULT) + else + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '*' then ( + Sedlexing.bump lexbuf 1; + if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_EXP_ASSIGN) + ) else + Token (env, T_EXP) + ) else if next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_MULT_ASSIGN) + ) else + Token (env, T_MULT) + | "%" -> + if + for_type_token + && Sedlexing.peek lexbuf 0 = Char.code 'c' + && Sedlexing.peek lexbuf 1 = Char.code 'h' + && Sedlexing.peek lexbuf 2 = Char.code 'e' + && Sedlexing.peek lexbuf 3 = Char.code 'c' + && Sedlexing.peek lexbuf 4 = Char.code 'k' + && Sedlexing.peek lexbuf 5 = Char.code 's' + then ( + Sedlexing.bump lexbuf 6; + Token (env, T_CHECKS) + ) else if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_MOD_ASSIGN) + ) else + Token (env, T_MOD) + | "|" -> + let next = Sedlexing.peek lexbuf 0 in + if for_type_token && next = Char.code '}' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_RCURLYBAR) + ) else if next = Char.code '|' then + if for_type_token then + if Sedlexing.peek lexbuf 1 = Char.code '=' then + (* ||= in type mode: split to single | *) + Token (env, T_BIT_OR) + else ( + Sedlexing.bump lexbuf 1; + Token (env, T_OR) + ) + else ( + Sedlexing.bump lexbuf 1; + if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_OR_ASSIGN) + ) else + Token (env, T_OR) + ) + else if (not for_type_token) && next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_BIT_OR_ASSIGN) + ) else + Token (env, T_BIT_OR) + | "&" -> + let next = Sedlexing.peek lexbuf 0 in + if next = Char.code '&' then + if for_type_token then + if Sedlexing.peek lexbuf 1 = Char.code '=' then + (* &&= in type mode: split to single & *) + Token (env, T_BIT_AND) + else ( + Sedlexing.bump lexbuf 1; + Token (env, T_AND) + ) + else ( + Sedlexing.bump lexbuf 1; + if Sedlexing.peek lexbuf 0 = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_AND_ASSIGN) + ) else + Token (env, T_AND) + ) + else if (not for_type_token) && next = Char.code '=' then ( + Sedlexing.bump lexbuf 1; + Token (env, T_BIT_AND_ASSIGN) + ) else + Token (env, T_BIT_AND) | "^=" -> Token (env, T_BIT_XOR_ASSIGN) - | "??=" -> Token (env, T_NULLISH_ASSIGN) - | "&&=" -> Token (env, T_AND_ASSIGN) - | "||=" -> Token (env, T_OR_ASSIGN) - | "<" -> Token (env, T_LESS_THAN) - | ">" -> Token (env, T_GREATER_THAN) - | "+" -> Token (env, T_PLUS) - | "-" -> Token (env, T_MINUS) - | "*" -> Token (env, T_MULT) - | "**" -> Token (env, T_EXP) - | "%" -> Token (env, T_MOD) - | "|" -> Token (env, T_BIT_OR) - | "&" -> Token (env, T_BIT_AND) | "^" -> Token (env, T_BIT_XOR) | "!" -> Token (env, T_NOT) | "~" -> Token (env, T_BIT_NOT) @@ -855,71 +1021,126 @@ let token (env : Lex_env.t) lexbuf : result = 4. a世界 recognized *) | '\\' -> - let env = illegal env (loc_of_lexbuf env lexbuf) in - Continue env + if for_type_token then + Token (env, T_ERROR (lexeme lexbuf)) + else + let env = illegal env (loc_of_lexbuf env lexbuf) in + Continue env | js_id_start -> let start_offset = Sedlexing.lexeme_start lexbuf in loop_id_continues lexbuf |> ignore; let end_offset = Sedlexing.lexeme_end lexbuf in let loc = loc_of_offsets env start_offset end_offset in Sedlexing.set_lexeme_start lexbuf start_offset; - (match lexeme lexbuf with - | "async" -> Token (env, T_ASYNC) - | "await" -> Token (env, T_AWAIT) - | "break" -> Token (env, T_BREAK) - | "case" -> Token (env, T_CASE) - | "catch" -> Token (env, T_CATCH) - | "class" -> Token (env, T_CLASS) - | "const" -> Token (env, T_CONST) - | "continue" -> Token (env, T_CONTINUE) - | "debugger" -> Token (env, T_DEBUGGER) - | "declare" -> Token (env, T_DECLARE) - | "default" -> Token (env, T_DEFAULT) - | "delete" -> Token (env, T_DELETE) - | "do" -> Token (env, T_DO) - | "else" -> Token (env, T_ELSE) - | "enum" -> Token (env, T_ENUM) - | "export" -> Token (env, T_EXPORT) - | "extends" -> Token (env, T_EXTENDS) - | "false" -> Token (env, T_FALSE) - | "finally" -> Token (env, T_FINALLY) - | "for" -> Token (env, T_FOR) - | "function" -> Token (env, T_FUNCTION) - | "if" -> Token (env, T_IF) - | "implements" -> Token (env, T_IMPLEMENTS) - | "import" -> Token (env, T_IMPORT) - | "in" -> Token (env, T_IN) - | "instanceof" -> Token (env, T_INSTANCEOF) - | "interface" -> Token (env, T_INTERFACE) - | "let" -> Token (env, T_LET) - | "match" -> Token (env, T_MATCH) - | "new" -> Token (env, T_NEW) - | "null" -> Token (env, T_NULL) - | "of" -> Token (env, T_OF) - | "opaque" -> Token (env, T_OPAQUE) - | "package" -> Token (env, T_PACKAGE) - | "private" -> Token (env, T_PRIVATE) - | "protected" -> Token (env, T_PROTECTED) - | "public" -> Token (env, T_PUBLIC) - | "return" -> Token (env, T_RETURN) - | "static" -> Token (env, T_STATIC) - | "super" -> Token (env, T_SUPER) - | "switch" -> Token (env, T_SWITCH) - | "this" -> Token (env, T_THIS) - | "throw" -> Token (env, T_THROW) - | "true" -> Token (env, T_TRUE) - | "try" -> Token (env, T_TRY) - | "type" -> Token (env, T_TYPE) - | "typeof" -> Token (env, T_TYPEOF) - | "var" -> Token (env, T_VAR) - | "void" -> Token (env, T_VOID) - | "while" -> Token (env, T_WHILE) - | "with" -> Token (env, T_WITH) - | "yield" -> Token (env, T_YIELD) - | _ -> + if for_type_token then let raw = Sedlexing.lexeme lexbuf in - let (nenv, value) = decode_identifier env raw in - Token (nenv, T_IDENTIFIER { loc; value; raw = Sedlexing.string_of_utf8 raw })) + let (env, value) = decode_identifier env raw in + (* keep this list in sync with Parser_env.is_reserved_type + and token_is_reserved_type *) + match value with + | "renders" -> + (match%sedlex lexbuf with + | "?" -> + Sedlexing.set_lexeme_start lexbuf start_offset; + Token (env, T_RENDERS_QUESTION) + | "*" -> + Sedlexing.set_lexeme_start lexbuf start_offset; + Token (env, T_RENDERS_STAR) + | _ -> + Sedlexing.rollback lexbuf; + Sedlexing.set_lexeme_start lexbuf start_offset; + Token (env, T_IDENTIFIER { loc; value; raw = Sedlexing.string_of_utf8 raw })) + | "any" -> Token (env, T_ANY_TYPE) + | "bigint" -> Token (env, T_BIGINT_TYPE) + | "bool" -> Token (env, T_BOOLEAN_TYPE BOOL) + | "boolean" -> Token (env, T_BOOLEAN_TYPE BOOLEAN) + | "const" -> Token (env, T_CONST) + | "empty" -> Token (env, T_EMPTY_TYPE) + | "extends" -> Token (env, T_EXTENDS) + | "false" -> Token (env, T_FALSE) + | "import" -> Token (env, T_IMPORT) + | "interface" -> Token (env, T_INTERFACE) + | "keyof" -> Token (env, T_KEYOF) + | "mixed" -> Token (env, T_MIXED_TYPE) + | "never" -> Token (env, T_NEVER_TYPE) + | "new" -> Token (env, T_NEW) + | "null" -> Token (env, T_NULL) + | "number" -> Token (env, T_NUMBER_TYPE) + | "readonly" -> Token (env, T_READONLY) + | "writeonly" -> Token (env, T_WRITEONLY) + | "infer" -> Token (env, T_INFER) + | "is" -> Token (env, T_IS) + | "asserts" -> Token (env, T_ASSERTS) + | "implies" -> Token (env, T_IMPLIES) + | "static" -> Token (env, T_STATIC) + | "string" -> Token (env, T_STRING_TYPE) + | "symbol" -> Token (env, T_SYMBOL_TYPE) + | "true" -> Token (env, T_TRUE) + | "typeof" -> Token (env, T_TYPEOF) + | "undefined" -> Token (env, T_UNDEFINED_TYPE) + | "unknown" -> Token (env, T_UNKNOWN_TYPE) + | "void" -> Token (env, T_VOID_TYPE) + | _ -> Token (env, T_IDENTIFIER { loc; value; raw = Sedlexing.string_of_utf8 raw }) + else ( + match lexeme lexbuf with + | "async" -> Token (env, T_ASYNC) + | "await" -> Token (env, T_AWAIT) + | "break" -> Token (env, T_BREAK) + | "case" -> Token (env, T_CASE) + | "catch" -> Token (env, T_CATCH) + | "class" -> Token (env, T_CLASS) + | "const" -> Token (env, T_CONST) + | "continue" -> Token (env, T_CONTINUE) + | "debugger" -> Token (env, T_DEBUGGER) + | "declare" -> Token (env, T_DECLARE) + | "default" -> Token (env, T_DEFAULT) + | "delete" -> Token (env, T_DELETE) + | "do" -> Token (env, T_DO) + | "else" -> Token (env, T_ELSE) + | "enum" -> Token (env, T_ENUM) + | "export" -> Token (env, T_EXPORT) + | "extends" -> Token (env, T_EXTENDS) + | "false" -> Token (env, T_FALSE) + | "finally" -> Token (env, T_FINALLY) + | "for" -> Token (env, T_FOR) + | "function" -> Token (env, T_FUNCTION) + | "if" -> Token (env, T_IF) + | "implements" -> Token (env, T_IMPLEMENTS) + | "import" -> Token (env, T_IMPORT) + | "in" -> Token (env, T_IN) + | "instanceof" -> Token (env, T_INSTANCEOF) + | "interface" -> Token (env, T_INTERFACE) + | "let" -> Token (env, T_LET) + | "match" -> Token (env, T_MATCH) + | "new" -> Token (env, T_NEW) + | "null" -> Token (env, T_NULL) + | "of" -> Token (env, T_OF) + | "opaque" -> Token (env, T_OPAQUE) + | "package" -> Token (env, T_PACKAGE) + | "private" -> Token (env, T_PRIVATE) + | "protected" -> Token (env, T_PROTECTED) + | "public" -> Token (env, T_PUBLIC) + | "record" -> Token (env, T_RECORD) + | "return" -> Token (env, T_RETURN) + | "static" -> Token (env, T_STATIC) + | "super" -> Token (env, T_SUPER) + | "switch" -> Token (env, T_SWITCH) + | "this" -> Token (env, T_THIS) + | "throw" -> Token (env, T_THROW) + | "true" -> Token (env, T_TRUE) + | "try" -> Token (env, T_TRY) + | "type" -> Token (env, T_TYPE) + | "typeof" -> Token (env, T_TYPEOF) + | "var" -> Token (env, T_VAR) + | "void" -> Token (env, T_VOID) + | "while" -> Token (env, T_WHILE) + | "with" -> Token (env, T_WITH) + | "yield" -> Token (env, T_YIELD) + | _ -> + let raw = Sedlexing.lexeme lexbuf in + let (nenv, value) = decode_identifier env raw in + Token (nenv, T_IDENTIFIER { loc; value; raw = Sedlexing.string_of_utf8 raw }) + ) | eof -> let env = if is_in_comment_syntax env then @@ -930,9 +1151,12 @@ let token (env : Lex_env.t) lexbuf : result = in Token (env, T_EOF) | any -> - let env = illegal env (loc_of_lexbuf env lexbuf) in - Token (env, T_ERROR (lexeme lexbuf)) - | _ -> failwith "unreachable token" + if for_type_token then + Token (env, T_ERROR (lexeme lexbuf)) + else + let env = illegal env (loc_of_lexbuf env lexbuf) in + Token (env, T_ERROR (lexeme lexbuf)) + | _ -> failwith "unreachable token_base" let rec regexp_class env buf lexbuf = match%sedlex lexbuf with @@ -1498,329 +1722,36 @@ let template_tail env lexbuf = Token (env, T_TEMPLATE_PART (loc_of_lexbuf env lexbuf, "", "", false, true)) | _ -> failwith "unreachable template_tail" -(* There are some tokens that never show up in a type and which can cause - * ambiguity. For example, Foo> ends with two angle brackets, not - * with a right shift. - *) -let type_token env lexbuf = - match%sedlex lexbuf with - | line_terminator_sequence -> - let env = new_line env lexbuf in - Continue env - | Plus whitespace -> Continue env - | "/*" -> - let start_pos = start_pos_of_lexbuf env lexbuf in - let buf = Buffer.create 127 in - let (env, end_pos) = comment env buf lexbuf in - Comment (env, mk_comment env start_pos end_pos buf true) - | ("/*", Star whitespace, (":" | "::" | "flow-include")) -> - let pattern = lexeme lexbuf in - if not (is_comment_syntax_enabled env) then ( - let start_pos = start_pos_of_lexbuf env lexbuf in - let buf = Buffer.create 127 in - Buffer.add_string buf pattern; - let (env, end_pos) = comment env buf lexbuf in - Comment (env, mk_comment env start_pos end_pos buf true) - ) else - let env = - if is_in_comment_syntax env then - let loc = loc_of_lexbuf env lexbuf in - unexpected_error env loc pattern - else - env - in - let env = in_comment_syntax true env in - let len = Sedlexing.lexeme_length lexbuf in - if - Sedlexing.Utf8.sub_lexeme lexbuf (len - 1) 1 = ":" - && Sedlexing.Utf8.sub_lexeme lexbuf (len - 2) 1 <> ":" - then - Token (env, T_COLON) - else - Continue env - | "*/" -> - if is_in_comment_syntax env then - let env = in_comment_syntax false env in - Continue env - else ( - Sedlexing.rollback lexbuf; - match%sedlex lexbuf with - | "*" -> Token (env, T_MULT) - | _ -> failwith "expected *" - ) - | "//" -> - let start_pos = start_pos_of_lexbuf env lexbuf in - let buf = Buffer.create 127 in - let (env, end_pos) = line_comment env buf lexbuf in - Comment (env, mk_comment env start_pos end_pos buf false) - | "'" - | '"' -> - let quote = lexeme lexbuf in - let start = start_pos_of_lexbuf env lexbuf in - let buf = Buffer.create 127 in - let raw = Buffer.create 127 in - Buffer.add_string raw quote; - let octal = false in - let (env, _end, octal) = string_quote env quote buf raw octal lexbuf in - let loc = { Loc.source = Lex_env.source env; start; _end } in - Token (env, T_STRING (loc, Buffer.contents buf, Buffer.contents raw, octal)) - (* - * Number literals - *) - | (binbigint, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | binbigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_BINARY num) - | _ -> failwith "unreachable type_token bigbigint" - ) - | binbigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_BINARY num) - | (binnumber, (letter | '2' .. '9'), Star alphanumeric) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | binnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton BINARY num) - | _ -> failwith "unreachable type_token binnumber" - ) - | binnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton BINARY num) - | (octbigint, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | octbigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_OCTAL num) - | _ -> failwith "unreachable type_token octbigint" - ) - | octbigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_OCTAL num) - | (octnumber, (letter | '8' .. '9'), Star alphanumeric) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | octnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton OCTAL num) - | _ -> failwith "unreachable type_token octnumber" - ) - | octnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton OCTAL num) - | (legacyoctnumber, (letter | '8' .. '9'), Star alphanumeric) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | legacyoctnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton LEGACY_OCTAL num) - | _ -> failwith "unreachable type_token legacyoctnumber" - ) - | legacyoctnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton LEGACY_OCTAL num) - | (hexbigint, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | hexbigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | _ -> failwith "unreachable type_token hexbigint" - ) - | hexbigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | (hexnumber, non_hex_letter, Star alphanumeric) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | hexnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton NORMAL num) - | _ -> failwith "unreachable type_token hexnumber" - ) - | hexnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton NORMAL num) - | (scibigint, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | scibigint -> - let num = Sedlexing.lexeme lexbuf in - let loc = loc_of_lexbuf env lexbuf in - let env = lex_error env loc Parse_error.InvalidSciBigInt in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | _ -> failwith "unreachable type_token scibigint" - ) - | scibigint -> - let num = Sedlexing.lexeme lexbuf in - let loc = loc_of_lexbuf env lexbuf in - let env = lex_error env loc Parse_error.InvalidSciBigInt in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | (scinumber, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | scinumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton NORMAL num) - | _ -> failwith "unreachable type_token scinumber" - ) - | scinumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton NORMAL num) - | (floatbigint, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | floatbigint -> - let num = Sedlexing.lexeme lexbuf in - let loc = loc_of_lexbuf env lexbuf in - let env = lex_error env loc Parse_error.InvalidFloatBigInt in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | _ -> failwith "unreachable type_token floatbigint" - ) - | (wholebigint, word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | wholebigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | _ -> failwith "unreachable type_token wholebigint" +let template_tail_start env = + let lexbuf = env.lex_lb in + let start_pos = Sedlexing.lexeme_start lexbuf in + let end_pos = Sedlexing.lexeme_end lexbuf in + let start = pos_at_offset env start_pos in + Sedlexing.rollback lexbuf; + Sedlexing.bump lexbuf (end_pos - start_pos); + Sedlexing.set_lexeme_start lexbuf end_pos; + let value = Buffer.create 127 in + let raw = Buffer.create 127 in + let (env, is_tail) = template_part env value raw lexbuf in + let _end = end_pos_of_lexbuf env lexbuf in + let loc = { Loc.source = Lex_env.source env; start; _end } in + let lex_token = + T_TEMPLATE_PART (loc, Buffer.contents value, Buffer.contents raw, false, is_tail) + in + let lex_errors_acc = env.lex_state.lex_errors_acc in + if lex_errors_acc = [] then + ( { env with lex_last_loc = loc }, + { Lex_result.lex_token; lex_loc = loc; lex_comments = []; lex_errors = [] } ) - | floatbigint -> - let num = Sedlexing.lexeme lexbuf in - let loc = loc_of_lexbuf env lexbuf in - let env = lex_error env loc Parse_error.InvalidFloatBigInt in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | wholebigint -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_bignum_singleton BIG_NORMAL num) - | ((wholenumber | floatnumber), word) -> - (* Numbers cannot be immediately followed by words *) - recover env lexbuf ~f:(fun env lexbuf -> - match%sedlex lexbuf with - | wholenumber - | floatnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton NORMAL num) - | _ -> failwith "unreachable type_token wholenumber" + else + ( { env with lex_last_loc = loc; lex_state = Lex_env.empty_lex_state }, + { + Lex_result.lex_token; + lex_loc = loc; + lex_comments = []; + lex_errors = List.rev lex_errors_acc; + } ) - | wholenumber - | floatnumber -> - let num = Sedlexing.lexeme lexbuf in - Token (env, mk_num_singleton NORMAL num) - (* Keywords *) - (* `%` is not a valid unicode name *) - | "%checks" -> Token (env, T_CHECKS) - (* Syntax *) - | "[" -> Token (env, T_LBRACKET) - | "]" -> Token (env, T_RBRACKET) - | "{" -> Token (env, T_LCURLY) - | "}" -> Token (env, T_RCURLY) - | "{|" -> Token (env, T_LCURLYBAR) - | "|}" -> Token (env, T_RCURLYBAR) - | "(" -> Token (env, T_LPAREN) - | ")" -> Token (env, T_RPAREN) - | "..." -> Token (env, T_ELLIPSIS) - | "." -> Token (env, T_PERIOD) - | ";" -> Token (env, T_SEMICOLON) - | "," -> Token (env, T_COMMA) - | ":" -> Token (env, T_COLON) - | "?." -> Token (env, T_PLING_PERIOD) - | "?" -> Token (env, T_PLING) - | "[" -> Token (env, T_LBRACKET) - | "]" -> Token (env, T_RBRACKET) - (* Generics *) - | "<" -> Token (env, T_LESS_THAN) - | ">" -> Token (env, T_GREATER_THAN) - (* Generic default *) - | "=" -> Token (env, T_ASSIGN) - (* Optional or nullable *) - | "?" -> Token (env, T_PLING) - (* Existential *) - | "*" -> Token (env, T_MULT) - (* Annotation or bound *) - | ":" -> Token (env, T_COLON) - (* Invalid - but to avoid being interpreted as invalid `|` and `&` *) - | "&&" -> Token (env, T_AND) - | "||" -> Token (env, T_OR) - (* Union *) - | '|' -> Token (env, T_BIT_OR) - (* Intersection *) - | '&' -> Token (env, T_BIT_AND) - (* Function type *) - | "=>" -> Token (env, T_ARROW) - (* Type alias *) - | '=' -> Token (env, T_ASSIGN) - (* Variance annotations *) - | '+' -> Token (env, T_PLUS) - | '-' -> Token (env, T_MINUS) - | "renders?" -> Token (env, T_RENDERS_QUESTION) - | "renders*" -> Token (env, T_RENDERS_STAR) - (* Identifiers *) - | js_id_start -> - let start_offset = Sedlexing.lexeme_start lexbuf in - loop_id_continues lexbuf |> ignore; - let end_offset = Sedlexing.lexeme_end lexbuf in - let loc = loc_of_offsets env start_offset end_offset in - Sedlexing.set_lexeme_start lexbuf start_offset; - let raw = Sedlexing.lexeme lexbuf in - let (env, value) = decode_identifier env raw in - (* keep this list in sync with Parser_env.is_reserved_type - and token_is_reserved_type *) - (match value with - | "any" -> Token (env, T_ANY_TYPE) - | "bigint" -> Token (env, T_BIGINT_TYPE) - | "bool" -> Token (env, T_BOOLEAN_TYPE BOOL) - | "boolean" -> Token (env, T_BOOLEAN_TYPE BOOLEAN) - | "const" -> Token (env, T_CONST) - | "empty" -> Token (env, T_EMPTY_TYPE) - | "extends" -> Token (env, T_EXTENDS) - | "false" -> Token (env, T_FALSE) - | "interface" -> Token (env, T_INTERFACE) - | "keyof" -> Token (env, T_KEYOF) - | "mixed" -> Token (env, T_MIXED_TYPE) - | "never" -> Token (env, T_NEVER_TYPE) - | "null" -> Token (env, T_NULL) - | "number" -> Token (env, T_NUMBER_TYPE) - | "readonly" -> Token (env, T_READONLY) - | "infer" -> Token (env, T_INFER) - | "is" -> Token (env, T_IS) - | "asserts" -> Token (env, T_ASSERTS) - | "implies" -> Token (env, T_IMPLIES) - | "static" -> Token (env, T_STATIC) - | "string" -> Token (env, T_STRING_TYPE) - | "symbol" -> Token (env, T_SYMBOL_TYPE) - | "true" -> Token (env, T_TRUE) - | "typeof" -> Token (env, T_TYPEOF) - | "undefined" -> Token (env, T_UNDEFINED_TYPE) - | "unknown" -> Token (env, T_UNKNOWN_TYPE) - | "void" -> Token (env, T_VOID_TYPE) - | _ -> Token (env, T_IDENTIFIER { loc; value; raw = Sedlexing.string_of_utf8 raw })) - (* Others *) - | eof -> - let env = - if is_in_comment_syntax env then - let loc = loc_of_lexbuf env lexbuf in - lex_error env loc Parse_error.UnexpectedEOS - else - env - in - Token (env, T_EOF) - | any -> Token (env, T_ERROR (lexeme lexbuf)) - | _ -> failwith "unreachable type_token" (* Lexing JSX children requires a string buffer to keep track of whitespace * *) @@ -1883,9 +1814,9 @@ let jsx_tag = wrap jsx_tag let template_tail = wrap template_tail -let type_token = wrap type_token +let type_token = wrap (token_base ~for_type_token:true) -let token = wrap token +let token = wrap (token_base ~for_type_token:false) let is_valid_identifier_name lexbuf = match%sedlex lexbuf with diff --git a/compiler/flow_parser/parser/flow_lexer.mli b/compiler/flow_parser/parser/flow_lexer.mli index 8609d224532..46fa3028654 100644 --- a/compiler/flow_parser/parser/flow_lexer.mli +++ b/compiler/flow_parser/parser/flow_lexer.mli @@ -13,6 +13,10 @@ val jsx_tag : Lex_env.t -> Lex_env.t * Lex_result.t val template_tail : Lex_env.t -> Lex_env.t * Lex_result.t +val template_tail_start : Lex_env.t -> Lex_env.t * Lex_result.t + +val advance_loc_by_char : Loc.t -> Loc.t + val type_token : Lex_env.t -> Lex_env.t * Lex_result.t val token : Lex_env.t -> Lex_env.t * Lex_result.t diff --git a/compiler/flow_parser/parser/js_id_unicode.ml b/compiler/flow_parser/parser/js_id_unicode.ml index 882ce20ada1..a27169f23ce 100644 --- a/compiler/flow_parser/parser/js_id_unicode.ml +++ b/compiler/flow_parser/parser/js_id_unicode.ml @@ -5,16 +5,13 @@ * LICENSE file in the root directory of this source tree. *) -(* This lists two valid unicode point ranges in tuple format. +(* This lists valid unicode point ranges in tuple format. see more details in https://mathiasbynens.be/notes/javascript-identifiers-es6 TODO: store it in a flat array add more docs *) [@@@ocamlformat "disable"] -(* JS has stricter rules with start id *) -let id_start = [|36,37;65,91;95,96;97,123;170,171;181,182;186,187;192,215;216,247;248,706;710,722;736,741;748,749;750,751;880,885;886,888;890,894;895,896;902,903;904,907;908,909;910,930;931,1014;1015,1154;1162,1328;1329,1367;1369,1370;1376,1417;1488,1515;1519,1523;1568,1611;1646,1648;1649,1748;1749,1750;1765,1767;1774,1776;1786,1789;1791,1792;1808,1809;1810,1840;1869,1958;1969,1970;1994,2027;2036,2038;2042,2043;2048,2070;2074,2075;2084,2085;2088,2089;2112,2137;2144,2155;2208,2229;2230,2238;2308,2362;2365,2366;2384,2385;2392,2402;2417,2433;2437,2445;2447,2449;2451,2473;2474,2481;2482,2483;2486,2490;2493,2494;2510,2511;2524,2526;2527,2530;2544,2546;2556,2557;2565,2571;2575,2577;2579,2601;2602,2609;2610,2612;2613,2615;2616,2618;2649,2653;2654,2655;2674,2677;2693,2702;2703,2706;2707,2729;2730,2737;2738,2740;2741,2746;2749,2750;2768,2769;2784,2786;2809,2810;2821,2829;2831,2833;2835,2857;2858,2865;2866,2868;2869,2874;2877,2878;2908,2910;2911,2914;2929,2930;2947,2948;2949,2955;2958,2961;2962,2966;2969,2971;2972,2973;2974,2976;2979,2981;2984,2987;2990,3002;3024,3025;3077,3085;3086,3089;3090,3113;3114,3130;3133,3134;3160,3163;3168,3170;3200,3201;3205,3213;3214,3217;3218,3241;3242,3252;3253,3258;3261,3262;3294,3295;3296,3298;3313,3315;3333,3341;3342,3345;3346,3387;3389,3390;3406,3407;3412,3415;3423,3426;3450,3456;3461,3479;3482,3506;3507,3516;3517,3518;3520,3527;3585,3633;3634,3636;3648,3655;3713,3715;3716,3717;3718,3723;3724,3748;3749,3750;3751,3761;3762,3764;3773,3774;3776,3781;3782,3783;3804,3808;3840,3841;3904,3912;3913,3949;3976,3981;4096,4139;4159,4160;4176,4182;4186,4190;4193,4194;4197,4199;4206,4209;4213,4226;4238,4239;4256,4294;4295,4296;4301,4302;4304,4347;4348,4681;4682,4686;4688,4695;4696,4697;4698,4702;4704,4745;4746,4750;4752,4785;4786,4790;4792,4799;4800,4801;4802,4806;4808,4823;4824,4881;4882,4886;4888,4955;4992,5008;5024,5110;5112,5118;5121,5741;5743,5760;5761,5787;5792,5867;5870,5881;5888,5901;5902,5906;5920,5938;5952,5970;5984,5997;5998,6001;6016,6068;6103,6104;6108,6109;6176,6265;6272,6313;6314,6315;6320,6390;6400,6431;6480,6510;6512,6517;6528,6572;6576,6602;6656,6679;6688,6741;6823,6824;6917,6964;6981,6988;7043,7073;7086,7088;7098,7142;7168,7204;7245,7248;7258,7294;7296,7305;7312,7355;7357,7360;7401,7405;7406,7412;7413,7415;7418,7419;7424,7616;7680,7958;7960,7966;7968,8006;8008,8014;8016,8024;8025,8026;8027,8028;8029,8030;8031,8062;8064,8117;8118,8125;8126,8127;8130,8133;8134,8141;8144,8148;8150,8156;8160,8173;8178,8181;8182,8189;8305,8306;8319,8320;8336,8349;8450,8451;8455,8456;8458,8468;8469,8470;8472,8478;8484,8485;8486,8487;8488,8489;8490,8506;8508,8512;8517,8522;8526,8527;8544,8585;11264,11311;11312,11359;11360,11493;11499,11503;11506,11508;11520,11558;11559,11560;11565,11566;11568,11624;11631,11632;11648,11671;11680,11687;11688,11695;11696,11703;11704,11711;11712,11719;11720,11727;11728,11735;11736,11743;12293,12296;12321,12330;12337,12342;12344,12349;12353,12439;12443,12448;12449,12539;12540,12544;12549,12592;12593,12687;12704,12731;12784,12800;13312,19894;19968,40944;40960,42125;42192,42238;42240,42509;42512,42528;42538,42540;42560,42607;42623,42654;42656,42736;42775,42784;42786,42889;42891,42944;42946,42951;42999,43010;43011,43014;43015,43019;43020,43043;43072,43124;43138,43188;43250,43256;43259,43260;43261,43263;43274,43302;43312,43335;43360,43389;43396,43443;43471,43472;43488,43493;43494,43504;43514,43519;43520,43561;43584,43587;43588,43596;43616,43639;43642,43643;43646,43696;43697,43698;43701,43703;43705,43710;43712,43713;43714,43715;43739,43742;43744,43755;43762,43765;43777,43783;43785,43791;43793,43799;43808,43815;43816,43823;43824,43867;43868,43880;43888,44003;44032,55204;55216,55239;55243,55292;63744,64110;64112,64218;64256,64263;64275,64280;64285,64286;64287,64297;64298,64311;64312,64317;64318,64319;64320,64322;64323,64325;64326,64434;64467,64830;64848,64912;64914,64968;65008,65020;65136,65141;65142,65277;65313,65339;65345,65371;65382,65471;65474,65480;65482,65488;65490,65496;65498,65501;65536,65548;65549,65575;65576,65595;65596,65598;65599,65614;65616,65630;65664,65787;65856,65909;66176,66205;66208,66257;66304,66336;66349,66379;66384,66422;66432,66462;66464,66500;66504,66512;66513,66518;66560,66718;66736,66772;66776,66812;66816,66856;66864,66916;67072,67383;67392,67414;67424,67432;67584,67590;67592,67593;67594,67638;67639,67641;67644,67645;67647,67670;67680,67703;67712,67743;67808,67827;67828,67830;67840,67862;67872,67898;67968,68024;68030,68032;68096,68097;68112,68116;68117,68120;68121,68150;68192,68221;68224,68253;68288,68296;68297,68325;68352,68406;68416,68438;68448,68467;68480,68498;68608,68681;68736,68787;68800,68851;68864,68900;69376,69405;69415,69416;69424,69446;69600,69623;69635,69688;69763,69808;69840,69865;69891,69927;69956,69957;69968,70003;70006,70007;70019,70067;70081,70085;70106,70107;70108,70109;70144,70162;70163,70188;70272,70279;70280,70281;70282,70286;70287,70302;70303,70313;70320,70367;70405,70413;70415,70417;70419,70441;70442,70449;70450,70452;70453,70458;70461,70462;70480,70481;70493,70498;70656,70709;70727,70731;70751,70752;70784,70832;70852,70854;70855,70856;71040,71087;71128,71132;71168,71216;71236,71237;71296,71339;71352,71353;71424,71451;71680,71724;71840,71904;71935,71936;72096,72104;72106,72145;72161,72162;72163,72164;72192,72193;72203,72243;72250,72251;72272,72273;72284,72330;72349,72350;72384,72441;72704,72713;72714,72751;72768,72769;72818,72848;72960,72967;72968,72970;72971,73009;73030,73031;73056,73062;73063,73065;73066,73098;73112,73113;73440,73459;73728,74650;74752,74863;74880,75076;77824,78895;82944,83527;92160,92729;92736,92767;92880,92910;92928,92976;92992,92996;93027,93048;93053,93072;93760,93824;93952,94027;94032,94033;94099,94112;94176,94178;94179,94180;94208,100344;100352,101107;110592,110879;110928,110931;110948,110952;110960,111356;113664,113771;113776,113789;113792,113801;113808,113818;119808,119893;119894,119965;119966,119968;119970,119971;119973,119975;119977,119981;119982,119994;119995,119996;119997,120004;120005,120070;120071,120075;120077,120085;120086,120093;120094,120122;120123,120127;120128,120133;120134,120135;120138,120145;120146,120486;120488,120513;120514,120539;120540,120571;120572,120597;120598,120629;120630,120655;120656,120687;120688,120713;120714,120745;120746,120771;120772,120780;123136,123181;123191,123198;123214,123215;123584,123628;124928,125125;125184,125252;125259,125260;126464,126468;126469,126496;126497,126499;126500,126501;126503,126504;126505,126515;126516,126520;126521,126522;126523,126524;126530,126531;126535,126536;126537,126538;126539,126540;126541,126544;126545,126547;126548,126549;126551,126552;126553,126554;126555,126556;126557,126558;126559,126560;126561,126563;126564,126565;126567,126571;126572,126579;126580,126584;126585,126589;126590,126591;126592,126602;126603,126620;126625,126628;126629,126634;126635,126652;131072,173783;173824,177973;177984,178206;178208,183970;183984,191457;194560,195102|] - (* The followed ID restriction is relaxed, this one is used in our customized unicode lexing. *) diff --git a/compiler/flow_parser/parser/jsdoc.ml b/compiler/flow_parser/parser/jsdoc.ml index 4e3d1710809..3ef373e5e89 100644 --- a/compiler/flow_parser/parser/jsdoc.ml +++ b/compiler/flow_parser/parser/jsdoc.ml @@ -77,7 +77,29 @@ module Parser = struct let empty = { description = None; params = []; deprecated = None; unrecognized_tags = [] } - let trimmed_string_of_buffer buffer = buffer |> Buffer.contents |> String.trim + let trim_end s = + let is_space = function + | ' ' + | '\012' + | '\n' + | '\r' + | '\t' -> + true + | _ -> false + in + let len = String.length s in + let j = ref (len - 1) in + while !j >= 0 && is_space (String.get s !j) do + decr j + done; + if !j = len - 1 then + s + else if !j >= 0 then + String.sub s 0 (!j + 1) + else + "" + + let trimmed_string_of_buffer buffer = buffer |> Buffer.contents |> trim_end let description_of_desc_buf desc_buf = match trimmed_string_of_buffer desc_buf with @@ -129,15 +151,24 @@ module Parser = struct | _ (* eof *) -> description_of_desc_buf desc_buf and description_or_tag desc_buf lexbuf = - match%sedlex lexbuf with - | '@' -> description_of_desc_buf desc_buf - | _ -> description desc_buf lexbuf + let rec skip_and_count_significant_whitespace acc = + match%sedlex lexbuf with + | whitespace -> skip_and_count_significant_whitespace (acc + 1) + | '@' -> None + | _ -> Some acc + in + match skip_and_count_significant_whitespace 0 with + | None -> description_of_desc_buf desc_buf + | Some count -> + for _i = 1 to count do + Buffer.add_char desc_buf ' ' + done; + description desc_buf lexbuf and description_startline desc_buf lexbuf = match%sedlex lexbuf with - | '*' - | whitespace -> - description_startline desc_buf lexbuf + | '*' -> description_or_tag desc_buf lexbuf + | whitespace -> description_startline desc_buf lexbuf | _ -> description_or_tag desc_buf lexbuf let rec param_path ?(path = Param.Name) lexbuf = @@ -260,7 +291,10 @@ let parse str = let of_comments = let open Flow_ast in let of_comment = function - | (_, Comment.{ kind = Block; text; _ }) -> parse text + | (l, Comment.{ kind = Block; text; _ }) -> + (match parse text with + | Some d -> Some (l, d) + | None -> None) | (_, Comment.{ kind = Line; _ }) -> None in let rec of_comment_list = function diff --git a/compiler/flow_parser/parser/jsdoc.mli b/compiler/flow_parser/parser/jsdoc.mli index cdded0ab4f3..f17a6081c76 100644 --- a/compiler/flow_parser/parser/jsdoc.mli +++ b/compiler/flow_parser/parser/jsdoc.mli @@ -53,4 +53,6 @@ val unrecognized_tags : t -> Unrecognized_tags.t (* parsing *) (***********) -val of_comments : ('M, 'T) Flow_ast.Syntax.t option -> t option +val parse : string -> t option + +val of_comments : ('M, 'T) Flow_ast.Syntax.t option -> ('M * t) option diff --git a/compiler/flow_parser/parser/lex_env.ml b/compiler/flow_parser/parser/lex_env.ml index 00c36c42845..f8ece1aeb31 100644 --- a/compiler/flow_parser/parser/lex_env.ml +++ b/compiler/flow_parser/parser/lex_env.ml @@ -49,6 +49,18 @@ let clone env = let lex_lb = Sedlexing.lexbuf_clone env.lex_lb in { env with lex_lb } +let set_lexbuf_pos env pos = + let lexbuf = env.lex_lb in + let start = Sedlexing.lexeme_start lexbuf in + Sedlexing.rollback lexbuf; + Sedlexing.bump lexbuf (pos - start); + Sedlexing.set_lexeme_start lexbuf pos; + env + +let set_last_loc env loc = { env with lex_last_loc = loc } + +let lex_last_loc env = env.lex_last_loc + let lexbuf env = env.lex_lb let source env = env.lex_source diff --git a/compiler/flow_parser/parser/lex_result.ml b/compiler/flow_parser/parser/lex_result.ml index 06ca8998453..1c6a23bb60e 100644 --- a/compiler/flow_parser/parser/lex_result.ml +++ b/compiler/flow_parser/parser/lex_result.ml @@ -12,6 +12,8 @@ type t = { lex_comments: Loc.t Flow_ast.Comment.t list; } +let with_comments result lex_comments = { result with lex_comments } + let token result = result.lex_token let loc result = result.lex_loc diff --git a/compiler/flow_parser/parser/loc.ml b/compiler/flow_parser/parser/loc.ml index e6786bd4efa..3ccce4d6469 100644 --- a/compiler/flow_parser/parser/loc.ml +++ b/compiler/flow_parser/parser/loc.ml @@ -186,3 +186,5 @@ let cursor source line column = { source; start = { line; column }; _end = { lin let start_loc loc = { loc with _end = loc.start } let end_loc loc = { loc with start = loc._end } + +let update_source f loc = { loc with source = f loc.source } diff --git a/compiler/flow_parser/parser/loc.mli b/compiler/flow_parser/parser/loc.mli index 1a1a4419614..b3a6788228d 100644 --- a/compiler/flow_parser/parser/loc.mli +++ b/compiler/flow_parser/parser/loc.mli @@ -74,3 +74,5 @@ val start_loc : t -> t (* Produces a location at the end of the input location *) val end_loc : t -> t + +val update_source : (File_key.t option -> File_key.t option) -> t -> t diff --git a/compiler/flow_parser/parser/match_pattern_parser.ml b/compiler/flow_parser/parser/match_pattern_parser.ml index 4e446231ac3..d0a9aec07c4 100644 --- a/compiler/flow_parser/parser/match_pattern_parser.ml +++ b/compiler/flow_parser/parser/match_pattern_parser.ml @@ -76,7 +76,14 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct Eat.token env; let trailing = Eat.trailing_comments env in let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - (loc, WildcardPattern comments) + (loc, WildcardPattern { WildcardPattern.comments; invalid_syntax_default_keyword = false }) + | T_DEFAULT -> + let leading = Peek.comments env in + let loc = Peek.loc env in + Eat.token env; + let trailing = Eat.trailing_comments env in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + (loc, WildcardPattern { WildcardPattern.comments; invalid_syntax_default_keyword = true }) | T_LPAREN -> let leading = Peek.comments env in Expect.token env T_LPAREN; @@ -120,8 +127,10 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct let trailing = Eat.trailing_comments env in let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in (loc, NullPattern comments) - | T_PLUS -> unary_pattern env ~operator:UnaryPattern.Plus - | T_MINUS -> unary_pattern env ~operator:UnaryPattern.Minus + | T_PLUS -> + with_loc (fun env -> UnaryPattern (unary_pattern env ~operator:UnaryPattern.Plus)) env + | T_MINUS -> + with_loc (fun env -> UnaryPattern (unary_pattern env ~operator:UnaryPattern.Minus)) env | T_CONST -> let (loc, binding) = binding_pattern env ~kind:Ast.Variable.Const in (loc, BindingPattern binding) @@ -131,8 +140,8 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct | T_VAR -> let (loc, binding) = binding_pattern env ~kind:Ast.Variable.Var in (loc, BindingPattern binding) - | T_LCURLY -> object_pattern env - | T_LBRACKET -> array_pattern env + | T_LCURLY -> with_loc (fun env -> ObjectPattern (object_pattern env)) env + | T_LBRACKET -> with_loc (fun env -> ArrayPattern (array_pattern env)) env | _ when Peek.is_identifier env -> let start_loc = Peek.loc env in let id = Parse.identifier env in @@ -191,12 +200,28 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct env in member (MemberPattern.BaseMember mem) - | _ -> - (match acc with - | MemberPattern.BaseIdentifier ((loc, _) as id) -> (loc, IdentifierPattern id) - | MemberPattern.BaseMember (loc, member) -> (loc, MemberPattern (loc, member))) + | _ -> acc in - member (MemberPattern.BaseIdentifier id) + let base = member (MemberPattern.BaseIdentifier id) in + (match Peek.token env with + | T_LCURLY -> + with_loc + ~start_loc + (fun env -> + let properties = with_loc (fun env -> object_pattern env) env in + let constructor = + match base with + | MemberPattern.BaseIdentifier id -> InstancePattern.IdentifierConstructor id + | MemberPattern.BaseMember member -> InstancePattern.MemberConstructor member + in + let trailing = Eat.trailing_comments env in + let comments = Flow_ast_utils.mk_comments_opt ~trailing () in + InstancePattern { InstancePattern.constructor; properties; comments }) + env + | _ -> + (match base with + | MemberPattern.BaseIdentifier ((loc, _) as id) -> (loc, IdentifierPattern id) + | MemberPattern.BaseMember (loc, member) -> (loc, MemberPattern (loc, member)))) | t -> let leading = Peek.comments env in let loc = Peek.loc env in @@ -206,41 +231,37 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct | T_ERROR _ -> Eat.token env | _ -> ()); let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing:[] () in - (loc, WildcardPattern comments) + (loc, WildcardPattern { WildcardPattern.comments; invalid_syntax_default_keyword = false }) and unary_pattern env ~operator = - with_loc - (fun env -> + let leading = Peek.comments env in + Eat.token env; + let argument = + match Peek.token env with + | T_NUMBER { kind; raw } -> let leading = Peek.comments env in - Eat.token env; - let argument = - match Peek.token env with - | T_NUMBER { kind; raw } -> - let leading = Peek.comments env in - let loc = Peek.loc env in - let value = Parse.number env kind raw in - let trailing = Eat.trailing_comments env in - let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - (loc, UnaryPattern.NumberLiteral { Ast.NumberLiteral.value; raw; comments }) - | T_BIGINT { kind; raw } -> - let leading = Peek.comments env in - let loc = Peek.loc env in - let value = Parse.bigint env kind raw in - let trailing = Eat.trailing_comments env in - let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - (loc, UnaryPattern.BigIntLiteral { Ast.BigIntLiteral.value; raw; comments }) - | _ -> - let loc = Peek.loc env in - error_unexpected ~expected:"a number literal" env; - ( loc, - UnaryPattern.NumberLiteral - { Ast.NumberLiteral.value = 0.; raw = "0"; comments = None } - ) - in + let loc = Peek.loc env in + let value = Parse.number env kind raw in let trailing = Eat.trailing_comments env in let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - UnaryPattern { UnaryPattern.operator; argument; comments }) - env + (loc, UnaryPattern.NumberLiteral { Ast.NumberLiteral.value; raw; comments }) + | T_BIGINT { kind; raw } -> + let leading = Peek.comments env in + let loc = Peek.loc env in + let value = Parse.bigint env kind raw in + let trailing = Eat.trailing_comments env in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + (loc, UnaryPattern.BigIntLiteral { Ast.BigIntLiteral.value; raw; comments }) + | _ -> + let loc = Peek.loc env in + error_unexpected ~expected:"a number literal" env; + ( loc, + UnaryPattern.NumberLiteral { Ast.NumberLiteral.value = 0.; raw = "0"; comments = None } + ) + in + let trailing = Eat.trailing_comments env in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + { UnaryPattern.operator; argument; comments } and binding_pattern env ~kind = with_loc @@ -330,21 +351,17 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct if not (Peek.token env = T_RCURLY) then Expect.token env T_COMMA; properties env (prop :: acc) in - with_loc - (fun env -> - let leading = Peek.comments env in - Expect.token env T_LCURLY; - let (properties, rest) = properties env [] in - let internal = Peek.comments env in - Expect.token env T_RCURLY; - let trailing = Eat.trailing_comments env in - ObjectPattern - { - ObjectPattern.properties; - rest; - comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal (); - }) - env + let leading = Peek.comments env in + Expect.token env T_LCURLY; + let (properties, rest) = properties env [] in + let internal = Peek.comments env in + Expect.token env T_RCURLY; + let trailing = Eat.trailing_comments env in + { + ObjectPattern.properties; + rest; + comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal (); + } and array_pattern env = let rec elements env ~start_loc acc = @@ -364,20 +381,15 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct let element = { ArrayPattern.Element.index; pattern } in elements env ~start_loc (element :: acc) in - with_loc - (fun env -> - let leading = Peek.comments env in - let start_loc = Peek.loc env in - Expect.token env T_LBRACKET; - let (elements, rest) = elements env ~start_loc [] in - let internal = Peek.comments env in - Expect.token env T_RBRACKET; - let trailing = Eat.trailing_comments env in - let comments = - Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal () - in - ArrayPattern { ArrayPattern.elements; rest; comments }) - env + let leading = Peek.comments env in + let start_loc = Peek.loc env in + Expect.token env T_LBRACKET; + let (elements, rest) = elements env ~start_loc [] in + let internal = Peek.comments env in + Expect.token env T_RBRACKET; + let trailing = Eat.trailing_comments env in + let comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal () in + { ArrayPattern.elements; rest; comments } and rest_pattern env = with_loc @@ -409,7 +421,8 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct in ( loc, match pattern with - | WildcardPattern comments -> WildcardPattern (merge_comments comments) + | WildcardPattern ({ WildcardPattern.comments; _ } as p) -> + WildcardPattern { p with WildcardPattern.comments = merge_comments comments } | NumberPattern ({ Ast.NumberLiteral.comments; _ } as p) -> NumberPattern { p with Ast.NumberLiteral.comments = merge_comments comments } | BigIntPattern ({ Ast.BigIntLiteral.comments; _ } as p) -> @@ -431,6 +444,8 @@ module Match_pattern (Parse : PARSER) : Parser_common.MATCH_PATTERN = struct ObjectPattern { p with ObjectPattern.comments = merge_comments_with_internal comments } | ArrayPattern ({ ArrayPattern.comments; _ } as p) -> ArrayPattern { p with ArrayPattern.comments = merge_comments_with_internal comments } + | InstancePattern ({ InstancePattern.comments; _ } as p) -> + InstancePattern { p with InstancePattern.comments = merge_comments comments } | OrPattern ({ OrPattern.comments; _ } as p) -> OrPattern { p with OrPattern.comments = merge_comments comments } | AsPattern ({ AsPattern.comments; _ } as p) -> diff --git a/compiler/flow_parser/parser/object_parser.ml b/compiler/flow_parser/parser/object_parser.ml index 78b6ef4d728..cde4d5f29dd 100644 --- a/compiler/flow_parser/parser/object_parser.ml +++ b/compiler/flow_parser/parser/object_parser.ml @@ -191,6 +191,54 @@ module Object in (key, value) + (* #prod-MethodDefinition *) + let parse_method ~async ~generator ~leading = + with_loc (fun env -> + (* #sec-function-definitions-static-semantics-early-errors *) + let env = env |> with_allow_super Super_prop in + let (sig_loc, (tparams, params, return)) = + with_loc + (fun env -> + let tparams = + type_params_remove_trailing + env + ~kind:Flow_ast_mapper.FunctionTP + (Type.type_params env) + in + let params = + let params = Declaration.function_params ~await:async ~yield:generator env in + if Peek.token env = T_COLON then + params + else + function_params_remove_trailing env params + in + let return = + return_annotation_remove_trailing env (Type.function_return_annotation_opt env) + in + (tparams, params, return)) + env + in + let simple_params = is_simple_parameter_list params in + let (body, contains_use_strict) = + Declaration.function_body env ~async ~generator ~expression:false ~simple_params + in + Declaration.strict_function_post_check env ~contains_use_strict None params; + { + Function.id = None; + params; + body; + generator; + effect_ = Function.Arbitrary; + async; + (* TODO: add support for object method predicates *) + predicate = None; + return; + tparams; + sig_loc; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + let _initializer = let parse_assignment_cover env = match Expression.assignment_cover env with @@ -242,54 +290,6 @@ module Object error_at env (fst expr, Parse_error.ComputedShorthandProperty); expr in - (* #prod-MethodDefinition *) - let parse_method ~async ~generator ~leading = - with_loc (fun env -> - (* #sec-function-definitions-static-semantics-early-errors *) - let env = env |> with_allow_super Super_prop in - let (sig_loc, (tparams, params, return)) = - with_loc - (fun env -> - let tparams = - type_params_remove_trailing - env - ~kind:Flow_ast_mapper.FunctionTP - (Type.type_params env) - in - let params = - let params = Declaration.function_params ~await:async ~yield:generator env in - if Peek.token env = T_COLON then - params - else - function_params_remove_trailing env params - in - let return = - return_annotation_remove_trailing env (Type.function_return_annotation_opt env) - in - (tparams, params, return)) - env - in - let simple_params = is_simple_parameter_list params in - let (body, contains_use_strict) = - Declaration.function_body env ~async ~generator ~expression:false ~simple_params - in - Declaration.strict_function_post_check env ~contains_use_strict None params; - { - Function.id = None; - params; - body; - generator; - effect_ = Function.Arbitrary; - async; - (* TODO: add support for object method predicates *) - predicate = None; - return; - tparams; - sig_loc; - comments = Flow_ast_utils.mk_comments_opt ~leading (); - } - ) - in (* PropertyName `:` AssignmentExpression *) let parse_value env = Expect.token env T_COLON; @@ -547,15 +547,7 @@ module Object let interface = with_loc (fun env -> - let id = - let id = Type.type_identifier env in - if Peek.token env <> T_LESS_THAN then - id - else - let { remove_trailing; _ } = trailing_and_remover env in - remove_trailing id (fun remover id -> remover#identifier id) - in - let targs = Type.type_args env in + let (_loc, { Ast.Type.Generic.id; targs; comments = _ }) = Type.generic env in { Ast.Class.Implements.Interface.id; targs }) env in @@ -582,7 +574,9 @@ module Object let class_extends ~leading = with_loc (fun env -> let expr = - let expr = Expression.left_hand_side (env |> with_allow_yield false) in + let expr = + Expression.left_hand_side (env |> with_allow_yield false |> with_no_record true) + in if Peek.token env <> T_LESS_THAN then expr else @@ -621,64 +615,314 @@ module Object Some (key_loc, name) | _ -> None + let adjust_private_method_key_start key start_loc = + match key with + | Ast.Expression.Object.Property.PrivateName (loc, private_name) -> + Ast.Expression.Object.Property.PrivateName + ({ loc with Loc.start = start_loc.Loc.start }, private_name) + | _ -> key + (* In the ES6 draft, all elements are methods. No properties (though there * are getter and setters allowed *) let class_element = - let get env start_loc decorators static leading = - let (loc, (key, value)) = - with_loc ~start_loc (fun env -> getter_or_setter env ~in_class_body:true true) env - in - (match (static, string_value_of_key key) with + let method_kind_of_key env ~static ~async ~generator key = + match (static, string_value_of_key key) with | (false, Some (key_loc, "constructor")) -> - error_at env (key_loc, Parse_error.ConstructorCannotBeAccessor) + if async then error_at env (key_loc, Parse_error.ConstructorCannotBeAsync); + if generator then error_at env (key_loc, Parse_error.ConstructorCannotBeGenerator); + Ast.Class.Method.Constructor | (true, Some (key_loc, "prototype")) -> error_at env ( key_loc, Parse_error.InvalidClassMemberName - { name = "prototype"; static; method_ = false; private_ = false } - ) - | _ -> ()); - let open Ast.Class in - Body.Method - ( loc, + { name = "prototype"; static; method_ = true; private_ = false } + ); + Ast.Class.Method.Method + | _ -> Ast.Class.Method.Method + in + let make_bodyless_declare_accessor + env ~start_loc ~sig_loc ~kind ~key ~tparams ~params ~return ~static ~override ~leading = + match Declaration.convert_function_params_to_type_params params with + | Ok type_params -> + let return_annot = + match return with + | Function.ReturnAnnot.Available (_, (ret_loc, t)) -> + Ast.Type.Function.Available (ret_loc, t) + | Function.ReturnAnnot.TypeGuard (_, tg) -> Ast.Type.Function.TypeGuard tg + | Function.ReturnAnnot.Missing loc -> + let default_type = + match kind with + | Class.Method.Set -> Ast.Type.Void None + | _ -> Ast.Type.Any None + in + Ast.Type.Function.Available (loc, default_type) + in + let func = { - Method.key; - value; - kind = Method.Get; - static; - decorators; - comments = Flow_ast_utils.mk_comments_opt ~leading (); + Ast.Type.Function.tparams; + params = type_params; + return = return_annot; + comments = None; + effect_ = Function.Arbitrary; } - ) - in - let set env start_loc decorators static leading = - let (loc, (key, value)) = - with_loc ~start_loc (fun env -> getter_or_setter env ~in_class_body:true false) env - in - (match (static, string_value_of_key key) with - | (false, Some (key_loc, "constructor")) -> - error_at env (key_loc, Parse_error.ConstructorCannotBeAccessor) - | (true, Some (key_loc, "prototype")) -> - error_at - env - ( key_loc, - Parse_error.InvalidClassMemberName - { name = "prototype"; static; method_ = false; private_ = false } + in + let annot_loc = + match tparams with + | Some (tparams_loc, _) -> tparams_loc + | None -> fst params + in + let annot = (annot_loc, (annot_loc, Ast.Type.Function func)) in + ignore (Eat.maybe env T_SEMICOLON); + let open Ast.Class in + Some + (Body.DeclareMethod + ( Loc.btwn start_loc sig_loc, + { + DeclareMethod.kind; + key; + annot; + static; + override; + optional = false; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) ) - | _ -> ()); - let open Ast.Class in - Body.Method - ( loc, + | Error _ -> None + in + let make_bodyless_declare_method + env + ~start_loc + ~sig_loc + ~kind + ~key + ~tparams + ~params + ~return + ~static + ~override + ~optional + ~leading = + match Declaration.convert_function_params_to_type_params params with + | Ok type_params -> + let return_annot = + match return with + | Function.ReturnAnnot.Available (_, (ret_loc, t)) -> + Ast.Type.Function.Available (ret_loc, t) + | Function.ReturnAnnot.TypeGuard (_, tg) -> Ast.Type.Function.TypeGuard tg + | Function.ReturnAnnot.Missing loc -> + if is_d_ts env then + Ast.Type.Function.Missing loc + else + Ast.Type.Function.Available (loc, Ast.Type.Any None) + in + let func = { - Method.key; - value; - kind = Method.Set; - static; - decorators; - comments = Flow_ast_utils.mk_comments_opt ~leading (); + Ast.Type.Function.tparams; + params = type_params; + return = return_annot; + comments = None; + effect_ = Function.Arbitrary; } - ) + in + let annot_loc = + match tparams with + | Some (tparams_loc, _) -> tparams_loc + | None -> fst params + in + let annot = (annot_loc, (annot_loc, Ast.Type.Function func)) in + ignore (Eat.maybe env T_SEMICOLON); + let open Ast.Class in + Some + (Body.DeclareMethod + ( Loc.btwn start_loc sig_loc, + { + DeclareMethod.kind; + key; + annot; + static; + override; + optional; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + ) + | Error _ -> None + in + let accessor + env + start_loc + accessor_key_start_loc + decorators + static + ~override + ts_accessibility + leading + ~is_getter = + if in_ambient_context env then ( + (* In ambient context, parse key + signature, then check for bodyless *) + let async = false in + let generator = false in + let (key_loc, key) = key ~class_body:true env in + let key = object_key_remove_trailing env key in + let key = adjust_private_method_key_start key accessor_key_start_loc in + (match (static, string_value_of_key key) with + | (false, Some (key_loc, "constructor")) -> + error_at env (key_loc, Parse_error.ConstructorCannotBeAccessor) + | (true, Some (key_loc, "prototype")) -> + error_at + env + ( key_loc, + Parse_error.InvalidClassMemberName + { name = "prototype"; static; method_ = false; private_ = false } + ) + | _ -> ()); + let (sig_loc, (tparams, params, return)) = + with_loc + (fun env -> + let tparams = + type_params_remove_trailing + env + ~kind:Flow_ast_mapper.FunctionTP + (Type.type_params env) + in + let params = + let params = Declaration.function_params ~await:false ~yield:false env in + if Peek.token env = T_COLON then + params + else + function_params_remove_trailing env params + in + begin + match (is_getter, params) with + | (true, (_, { Ast.Function.Params.this_ = Some _; _ })) -> + error_at env (key_loc, Parse_error.GetterMayNotHaveThisParam) + | (false, (_, { Ast.Function.Params.this_ = Some _; _ })) -> + error_at env (key_loc, Parse_error.SetterMayNotHaveThisParam) + | ( true, + (_, { Ast.Function.Params.params = []; rest = None; this_ = None; comments = _ }) + ) -> + () + | (false, (_, { Ast.Function.Params.rest = Some _; _ })) -> + error_at env (key_loc, Parse_error.SetterArity) + | ( false, + ( _, + { Ast.Function.Params.params = [_]; rest = None; this_ = None; comments = _ } + ) + ) -> + () + | (true, _) -> error_at env (key_loc, Parse_error.GetterArity) + | (false, _) -> error_at env (key_loc, Parse_error.SetterArity) + end; + let return = + return_annotation_remove_trailing env (Type.function_return_annotation_opt env) + in + (tparams, params, return)) + env + in + let kind = + if is_getter then + Class.Method.Get + else + Class.Method.Set + in + let make_accessor_method () = + let value = + with_loc + ~start_loc:sig_loc + (fun env -> + let simple_params = is_simple_parameter_list params in + let (body, contains_use_strict) = + Declaration.function_body env ~async ~generator ~expression:false ~simple_params + in + Declaration.strict_function_post_check env ~contains_use_strict None params; + { + Function.id = None; + params; + body; + generator; + async; + effect_ = Function.Arbitrary; + predicate = None; + return; + tparams; + sig_loc; + comments = None; + }) + env + in + let open Ast.Class in + Body.Method + ( Loc.btwn start_loc (fst value), + { + Method.key; + value; + kind; + static; + override; + ts_accessibility; + decorators; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + in + let is_bodyless = Peek.token env = T_SEMICOLON || Peek.is_implicit_semicolon env in + if is_bodyless then + match + make_bodyless_declare_accessor + env + ~start_loc + ~sig_loc + ~kind + ~key + ~tparams + ~params + ~return + ~static + ~override + ~leading + with + | Some decl -> decl + | None -> make_accessor_method () + else + make_accessor_method () + ) else + (* Not in ambient context - use normal getter_or_setter *) + let (loc, (key, value)) = + with_loc ~start_loc (fun env -> getter_or_setter env ~in_class_body:true is_getter) env + in + let key = adjust_private_method_key_start key accessor_key_start_loc in + (match (static, string_value_of_key key) with + | (false, Some (key_loc, "constructor")) -> + error_at env (key_loc, Parse_error.ConstructorCannotBeAccessor) + | (true, Some (key_loc, "prototype")) -> + error_at + env + ( key_loc, + Parse_error.InvalidClassMemberName + { name = "prototype"; static; method_ = false; private_ = false } + ) + | _ -> ()); + let open Ast.Class in + Body.Method + ( loc, + { + Method.key; + value; + kind = + ( if is_getter then + Method.Get + else + Method.Set + ); + static; + override; + ts_accessibility; + decorators; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) in let error_unsupported_variance env = function | Some (loc, _) -> error_at env (loc, Parse_error.UnexpectedVariance) @@ -739,7 +983,19 @@ module Object in (key, annot, value, []) in - let property env start_loc decorators key static declare variance leading = + let property + env + start_loc + decorators + key + static + declare + variance + ts_accessibility + ~abstract + ~override + ~optional + leading = let (loc, (key, annot, value, comments)) = with_loc ~start_loc @@ -762,13 +1018,70 @@ module Object (key, annot, value, Flow_ast_utils.mk_comments_opt ~leading ~trailing ())) env in - let open Ast.Class in - match key with - | Ast.Expression.Object.Property.PrivateName key -> - Body.PrivateField - (loc, { PrivateField.key; value; annot; static; variance; decorators; comments }) - | _ -> - Body.Property (loc, { Property.key; value; annot; static; variance; decorators; comments }) + if abstract then begin + (match value with + | Ast.Class.Property.Initialized _ -> + error_at env (loc, Parse_error.AbstractPropertyWithInitializer) + | _ -> ()); + let open Ast.Class in + match key with + | Ast.Expression.Object.Property.PrivateName ((key_loc, _) as private_key) -> + error_at env (key_loc, Parse_error.AbstractPrivateMember); + (* Recover as a regular PrivateField, dropping the abstract modifier. *) + Body.PrivateField + ( loc, + { + PrivateField.key = private_key; + value; + annot; + static; + override; + optional; + variance; + ts_accessibility; + decorators; + comments; + } + ) + | _ -> + Body.AbstractProperty + (loc, { AbstractProperty.key; annot; override; ts_accessibility; variance; comments }) + end else begin + let open Ast.Class in + match key with + | Ast.Expression.Object.Property.PrivateName key -> + Body.PrivateField + ( loc, + { + PrivateField.key; + value; + annot; + static; + override; + optional; + variance; + ts_accessibility; + decorators; + comments; + } + ) + | _ -> + Body.Property + ( loc, + { + Property.key; + value; + annot; + static; + override; + optional; + variance; + ts_accessibility; + decorators; + comments; + } + ) + end in let is_asi env = match Peek.token env with @@ -777,78 +1090,255 @@ module Object | _ when Peek.is_implicit_semicolon env -> true | _ -> false in - let rec init env start_loc decorators key ~async ~generator ~static ~declare variance leading = + let is_optional_method_in_ambient env = + in_ambient_context env + && + match Peek.ith_token ~i:1 env with + | T_LPAREN + | T_LESS_THAN -> + true + | _ -> false + in + let init + env + start_loc + decorators + key + ~async + ~generator + ~static + ~abstract + ~override + ~declare + variance + ts_accessibility + leading = match Peek.token env with | T_COLON | T_ASSIGN | T_SEMICOLON | T_RCURLY when (not async) && not generator -> - property env start_loc decorators key static declare variance leading - | T_PLING -> - (* TODO: add support for optional class properties *) - error_unexpected env; + property + env + start_loc + decorators + key + static + declare + variance + ts_accessibility + ~abstract + ~override + ~optional:false + leading + | T_PLING when (not async) && (not generator) && is_optional_method_in_ambient env -> + (* Optional method in ambient context: eat ? and parse as declare method *) + Eat.token env; + error_unsupported_declare env declare; + error_unsupported_variance env variance; + let key = object_key_remove_trailing env key in + let kind = method_kind_of_key env ~static ~async ~generator key in + if kind = Ast.Class.Method.Constructor then + error env Parse_error.ConstructorCannotBeOptional; + if abstract then error env Parse_error.OptionalMethodCannotBeAbstract; + (* `override constructor?()` is rejected for the same reason + `override constructor()` is in the non-T_PLING branch below. + Strip the modifier so the rest of the class still type-checks. *) + let override = + if override && kind = Ast.Class.Method.Constructor then ( + error_at env (start_loc, Parse_error.OverrideOnConstructor); + false + ) else + override + in + let (sig_loc, (tparams, params, return)) = + with_loc + (fun env -> + let tparams = + type_params_remove_trailing + env + ~kind:Flow_ast_mapper.FunctionTP + (Type.type_params env) + in + let params = + let params = Declaration.function_params ~await:false ~yield:false env in + if Peek.token env = T_COLON then + params + else + function_params_remove_trailing env params + in + let return = + return_annotation_remove_trailing env (Type.function_return_annotation_opt env) + in + (tparams, params, return)) + env + in + begin + match + make_bodyless_declare_method + env + ~start_loc + ~sig_loc + ~kind + ~key + ~tparams + ~params + ~return + ~static + ~override + ~optional:true + ~leading + with + | Some element -> element + | None -> + (* Params couldn't be converted; this shouldn't happen in ambient context. + Produce an error and emit a property as recovery. *) + error_unexpected env; + ignore (Eat.maybe env T_SEMICOLON); + Class.Body.Property + ( Loc.btwn start_loc sig_loc, + { + Class.Property.key; + value = Class.Property.Declared; + annot = Ast.Type.Missing (Peek.loc env); + static; + override; + optional = true; + variance = None; + ts_accessibility; + decorators; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + end + | T_PLING when (not async) && not generator -> Eat.token env; - init env start_loc decorators key ~async ~generator ~static ~declare variance leading + property + env + start_loc + decorators + key + static + declare + variance + ts_accessibility + ~abstract + ~override + ~optional:true + leading | _ when is_asi env -> (* an uninitialized, unannotated property *) - property env start_loc decorators key static declare variance leading + property + env + start_loc + decorators + key + static + declare + variance + ts_accessibility + ~abstract + ~override + ~optional:false + leading | _ -> error_unsupported_declare env declare; error_unsupported_variance env variance; - let (kind, env) = - match (static, string_value_of_key key) with - | (false, Some (key_loc, "constructor")) -> - if async then error_at env (key_loc, Parse_error.ConstructorCannotBeAsync); - if generator then error_at env (key_loc, Parse_error.ConstructorCannotBeGenerator); - (Ast.Class.Method.Constructor, env |> with_allow_super Super_prop_or_call) - | (true, Some (key_loc, "prototype")) -> - error_at - env - ( key_loc, - Parse_error.InvalidClassMemberName - { name = "prototype"; static; method_ = true; private_ = false } - ); - (Ast.Class.Method.Method, env |> with_allow_super Super_prop) - | _ -> (Ast.Class.Method.Method, env |> with_allow_super Super_prop) + let kind = method_kind_of_key env ~static ~async ~generator key in + (* `override constructor()` is a parse error. Recover by dropping + the `override` modifier so the rest of the class still + type-checks. *) + let override = + if override && kind = Ast.Class.Method.Constructor then ( + error_at env (start_loc, Parse_error.OverrideOnConstructor); + false + ) else + override + in + let env = + match kind with + | Ast.Class.Method.Constructor -> env |> with_allow_super Super_prop_or_call + | _ -> env |> with_allow_super Super_prop in let key = object_key_remove_trailing env key in - let value = + (* Parse method signature: type params, params, return annotation *) + let (sig_loc, (tparams, params, return)) = with_loc (fun env -> - let (sig_loc, (tparams, params, return)) = - with_loc - (fun env -> - let tparams = - type_params_remove_trailing - env - ~kind:Flow_ast_mapper.FunctionTP - (Type.type_params env) - in - let params = - let params = Declaration.function_params ~await:async ~yield:generator env in - let params = - if Peek.token env = T_COLON then - params - else - function_params_remove_trailing env params - in - Ast.Function.Params.( - match params with - | (loc, ({ this_ = Some (this_loc, _); _ } as params)) - when kind = Ast.Class.Method.Constructor -> - (* Disallow this param annotations for constructors *) - error_at env (this_loc, Parse_error.ThisParamBannedInConstructor); - (loc, { params with this_ = None }) - | params -> params - ) - in - let return = - return_annotation_remove_trailing env (Type.function_return_annotation_opt env) - in - (tparams, params, return)) + let tparams = + type_params_remove_trailing env + ~kind:Flow_ast_mapper.FunctionTP + (Type.type_params env) in + let params = + let params = Declaration.function_params ~await:async ~yield:generator env in + let params = + if Peek.token env = T_COLON then + params + else + function_params_remove_trailing env params + in + Ast.Function.Params.( + match params with + | (loc, ({ this_ = Some (this_loc, _); _ } as params)) + when kind = Ast.Class.Method.Constructor -> + (* Disallow this param annotations for constructors *) + error_at env (this_loc, Parse_error.ThisParamBannedInConstructor); + (loc, { params with this_ = None }) + | params -> params + ) + in + let return = + return_annotation_remove_trailing env (Type.function_return_annotation_opt env) + in + (tparams, params, return)) + env + in + (* Helper to build method function type *) + let make_method_func_type type_params = + let return_annot = + match return with + | Function.ReturnAnnot.Available (_, (ret_loc, t)) -> + Ast.Type.Function.Available (ret_loc, t) + | Function.ReturnAnnot.TypeGuard (_, tg) -> Ast.Type.Function.TypeGuard tg + | Function.ReturnAnnot.Missing loc -> + if is_d_ts env then + Ast.Type.Function.Missing loc + else + Ast.Type.Function.Available (Loc.none, Ast.Type.Any None) + in + let func = + { + Ast.Type.Function.tparams; + params = type_params; + return = return_annot; + comments = None; + effect_ = Function.Arbitrary; + } + in + let annot_loc = + match tparams with + | Some (tparams_loc, _) -> tparams_loc + | None -> fst params + in + (annot_loc, func) + in + (* Check for bodyless method: semicolon instead of body. + Also accept implicit semicolons (ASI) in ambient contexts. *) + let is_bodyless_method = + (Peek.token env = T_SEMICOLON + || (in_ambient_context env && Peek.is_implicit_semicolon env) + ) + && (not async) + && (not generator) + && (kind <> Ast.Class.Method.Constructor || is_d_ts env) + in + let make_method_value env = + with_loc + ~start_loc:sig_loc + (fun env -> let simple_params = is_simple_parameter_list params in let (body, contains_use_strict) = Declaration.function_body env ~async ~generator ~expression:false ~simple_params @@ -861,7 +1351,6 @@ module Object generator; async; effect_ = Function.Arbitrary; - (* TODO: add support for method predicates *) predicate = None; return; tparams; @@ -870,18 +1359,81 @@ module Object }) env in - let open Ast.Class in - Body.Method - ( Loc.btwn start_loc (fst value), - { - Method.key; - value; - kind; - static; - decorators; - comments = Flow_ast_utils.mk_comments_opt ~leading (); - } - ) + let make_method env = + let value = make_method_value env in + Class.Body.Method + ( Loc.btwn start_loc (fst value), + { + Class.Method.key; + value; + kind; + static; + override; + ts_accessibility; + decorators; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + in + if abstract && is_bodyless_method then + (* Abstract method - try to convert params to type params *) + match Declaration.convert_function_params_to_type_params params with + | Ok type_params -> + (match key with + | Ast.Expression.Object.Property.PrivateName (key_loc, _) -> + error_at env (key_loc, Parse_error.AbstractPrivateMember) + | _ -> ()); + ignore (Eat.maybe env T_SEMICOLON); + Class.Body.AbstractMethod + ( Loc.btwn start_loc sig_loc, + { + Class.AbstractMethod.key; + annot = make_method_func_type type_params; + override; + ts_accessibility; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + | Error _ -> + (* Params couldn't be converted - fall back to normal method with body *) + make_method env + else if abstract then ( + (* Abstract method with a body - error *) + error env Parse_error.AbstractMethodWithBody; + make_method env + ) else if + is_bodyless_method + && kind <> Class.Method.Get + && kind <> Class.Method.Set + && List.length decorators = 0 + && + match return with + | Function.ReturnAnnot.Missing _ -> is_d_ts env + | _ -> true + then + (* Implicit declare method *) + match + make_bodyless_declare_method + env + ~start_loc + ~sig_loc + ~kind + ~key + ~tparams + ~params + ~return + ~static + ~override + ~optional:false + ~leading + with + | Some element -> element + | None -> + (* Normal method with body *) + make_method env + else + (* Normal method with body *) + make_method env in let ith_implies_identifier ~i env = match Peek.ith_token ~i env with @@ -890,6 +1442,7 @@ module Object | T_ASSIGN | T_SEMICOLON | T_LPAREN + | T_PLING | T_RCURLY -> true | _ -> false @@ -907,22 +1460,28 @@ module Object (ret, leading) | _ -> (None, []) in - (* Error on TS class visibility modifiers. *) - (match Peek.token env with - | (T_PUBLIC as t) - | (T_PRIVATE as t) - | (T_PROTECTED as t) - when Peek.ith_is_identifier ~i:1 env -> - let kind = - match t with - | T_PUBLIC -> `Public - | T_PRIVATE -> `Private - | T_PROTECTED -> `Protected - | _ -> failwith "Must be one of the above" - in - error env (Parse_error.TSClassVisibility kind); - Eat.token env - | _ -> ()); + (* Parse TS class visibility modifiers. *) + let (ts_accessibility, leading_accessibility) = + match Peek.token env with + | (T_PUBLIC as t) + | (T_PRIVATE as t) + | (T_PROTECTED as t) + when Peek.ith_is_object_key ~i:1 ~is_class:true env -> + let kind = + match t with + | T_PUBLIC -> Ast.Class.TSAccessibility.Public + | T_PRIVATE -> Ast.Class.TSAccessibility.Private + | T_PROTECTED -> Ast.Class.TSAccessibility.Protected + | _ -> failwith "Must be one of the above" + in + let leading = Peek.comments env in + let start = Peek.loc env in + Eat.token env; + let trailing = Eat.trailing_comments env in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + (Some (start, { Ast.Class.TSAccessibility.kind; comments }), leading) + | _ -> (None, []) + in let static = Peek.token env = T_STATIC && @@ -932,6 +1491,7 @@ module Object | T_EOF (* incomplete property *) | T_LESS_THAN (* static() {} *) | T_LPAREN (* static() {} *) + | T_PLING (* static?: T *) | T_RCURLY (* end of class *) | T_SEMICOLON (* explicit semicolon *) -> false @@ -945,62 +1505,268 @@ module Object ) else [] in - let async = - Peek.token env = T_ASYNC - && (not (ith_implies_identifier ~i:1 env)) - && not (Peek.ith_is_line_terminator ~i:1 env) + (* Parse `abstract` then `override`. The reverse order is rejected + at parse time as an unexpected identifier — locking in a single + canonical order while `override` has no users to migrate. *) + let abstract = + match Peek.token env with + | T_IDENTIFIER { raw = "abstract"; _ } when Peek.ith_is_object_key ~i:1 ~is_class:true env + -> + true + | _ -> false in - (* consume `async` *) - let leading_async = - if async then ( + let leading_abstract = + if abstract then ( let leading = Peek.comments env in Eat.token env; leading ) else [] in - let (generator, leading_generator) = Declaration.generator env in - let parse_readonly = - Peek.ith_is_identifier ~i:1 env || Peek.ith_token ~i:1 env = T_LBRACKET - in - let variance = Declaration.variance env ~parse_readonly async generator in - let (generator, leading_generator) = - match (generator, variance) with - | (false, Some _) -> Declaration.generator env - | _ -> (generator, leading_generator) + let override = + match Peek.token env with + | T_IDENTIFIER { raw = "override"; _ } when Peek.ith_is_object_key ~i:1 ~is_class:true env + -> + true + | _ -> false in - let leading = - List.concat [leading_declare; leading_static; leading_async; leading_generator] + let leading_override = + if override then ( + let leading = Peek.comments env in + Eat.token env; + leading + ) else + [] in - match (async, generator, Peek.token env) with - | (false, false, T_IDENTIFIER { raw = "get"; _ }) -> - let leading_get = Peek.comments env in - let (_, key) = key ~class_body:true env in - if implies_identifier env then - init env start_loc decorators key ~async ~generator ~static ~declare variance leading - else ( - error_unsupported_declare env declare; - error_unsupported_variance env variance; - ignore (object_key_remove_trailing env key); - get env start_loc decorators static (leading @ leading_get) - ) - | (false, false, T_IDENTIFIER { raw = "set"; _ }) -> - let leading_set = Peek.comments env in - let (_, key) = key ~class_body:true env in - if implies_identifier env then - init env start_loc decorators key ~async ~generator ~static ~declare variance leading - else ( - error_unsupported_declare env declare; - error_unsupported_variance env variance; - ignore (object_key_remove_trailing env key); - set env start_loc decorators static (leading @ leading_set) - ) - | (_, _, _) -> - let (_, key) = key ~class_body:true env in - init env start_loc decorators key ~async ~generator ~static ~declare variance leading + (* Error if both static and abstract *) + if static && abstract then error env Parse_error.StaticAbstractMethod; + if static && Option.is_none declare && Eat.maybe env T_LCURLY then + Class.Body.StaticBlock + (with_loc + ~start_loc + (fun env -> + let internal = Peek.comments env in + let term_fn = function + | T_RCURLY -> true + | _ -> false + in + let body = Parse.statement_list ~term_fn env in + Expect.token env T_RCURLY; + let trailing = Eat.trailing_comments env in + let comments = + Flow_ast_utils.mk_comments_with_internal_opt + ~leading:leading_static + ~trailing + ~internal + () + in + { Class.StaticBlock.body; comments }) + env + ) + else + let async = + Peek.token env = T_ASYNC + && (not (ith_implies_identifier ~i:1 env)) + && not (Peek.ith_is_line_terminator ~i:1 env) + in + (* consume `async` *) + let leading_async = + if async then ( + let leading = Peek.comments env in + Eat.token env; + leading + ) else + [] + in + let (generator, leading_generator) = Declaration.generator env in + let parse_property_variance_keyword = Peek.ith_is_object_key ~i:1 ~is_class:true env in + let variance = Declaration.variance env ~parse_property_variance_keyword async generator in + let (generator, leading_generator) = + match (generator, variance) with + | (false, Some _) -> Declaration.generator env + | _ -> (generator, leading_generator) + in + let leading = + List.concat + [ + leading_declare; + leading_accessibility; + leading_static; + leading_abstract; + leading_override; + leading_async; + leading_generator; + ] + in + match (async, generator, Peek.token env) with + | (false, false, T_IDENTIFIER { raw = "get"; _ }) -> + let leading_get = Peek.comments env in + let (accessor_key_start_loc, key) = key ~class_body:true env in + if implies_identifier env then + init + env + start_loc + decorators + key + ~async + ~generator + ~static + ~abstract + ~override + ~declare + variance + ts_accessibility + leading + else ( + if not (in_ambient_context env) then error_unsupported_declare env declare; + error_unsupported_variance env variance; + ignore (object_key_remove_trailing env key); + accessor + env + start_loc + accessor_key_start_loc + decorators + static + ~override + ts_accessibility + (leading @ leading_get) + ~is_getter:true + ) + | (false, false, T_IDENTIFIER { raw = "set"; _ }) -> + let leading_set = Peek.comments env in + let (accessor_key_start_loc, key) = key ~class_body:true env in + if implies_identifier env then + init + env + start_loc + decorators + key + ~async + ~generator + ~static + ~abstract + ~override + ~declare + variance + ts_accessibility + leading + else ( + if not (in_ambient_context env) then error_unsupported_declare env declare; + error_unsupported_variance env variance; + ignore (object_key_remove_trailing env key); + accessor + env + start_loc + accessor_key_start_loc + decorators + static + ~override + ts_accessibility + (leading @ leading_set) + ~is_getter:false + ) + | (false, false, T_LBRACKET) when in_ambient_context env -> + (* Consume `[`, then disambiguate: index signature vs computed property. + Index signature: [id: type]: type — the token after the identifier is T_COLON. + Computed property: [expr] — anything else. *) + let leading_lbracket = Peek.comments env in + Expect.token env T_LBRACKET; + if Peek.ith_token ~i:1 env = T_COLON then begin + (* Index signature: [key: type]: type + Reject modifiers that are not valid on index signatures. *) + error_unsupported_declare env declare; + (match ts_accessibility with + | Some (loc, { Ast.Class.TSAccessibility.kind; _ }) -> + let keyword = + match kind with + | Ast.Class.TSAccessibility.Public -> "public" + | Ast.Class.TSAccessibility.Protected -> "protected" + | Ast.Class.TSAccessibility.Private -> "private" + in + error_at env (loc, Parse_error.IndexSignatureInvalidModifier keyword) + | None -> ()); + if abstract then + error_at env (start_loc, Parse_error.IndexSignatureInvalidModifier "abstract"); + let indexer = + with_loc + ~start_loc + (fun env -> + let id = identifier_name env in + Expect.token env T_COLON; + let key_type = Type._type env in + Expect.token env T_RBRACKET; + let optional = Eat.maybe env T_PLING in + let trailing = Eat.trailing_comments env in + Expect.token env T_COLON; + let value = Type._type env in + ignore (Eat.maybe env T_SEMICOLON); + { + Ast.Type.Object.Indexer.id = Some id; + key = key_type; + value; + static; + variance; + optional; + comments = + Flow_ast_utils.mk_comments_opt + ~leading:(leading @ leading_lbracket) + ~trailing + (); + }) + env + in + Ast.Class.Body.IndexSignature indexer + end else begin + (* Computed property — `[` already consumed, parse expression and `]` *) + let open Ast.Expression.Object.Property in + let (loc, computed_key) = + with_loc + ~start_loc + (fun env -> + let expr = Parse.assignment (env |> with_no_in false) in + Expect.token env T_RBRACKET; + let trailing = Eat.trailing_comments env in + { + ComputedKey.expression = expr; + comments = Flow_ast_utils.mk_comments_opt ~leading:leading_lbracket ~trailing (); + }) + env + in + let key = Computed (loc, computed_key) in + init + env + start_loc + decorators + key + ~async + ~generator + ~static + ~abstract + ~override + ~declare + variance + ts_accessibility + leading + end + | (_, _, _) -> + let (_, key) = key ~class_body:true env in + init + env + start_loc + decorators + key + ~async + ~generator + ~static + ~abstract + ~override + ~declare + variance + ts_accessibility + leading let class_body = - let rec elements env seen_constructor private_names acc = + let rec elements env ~abstract seen_constructor private_names acc = match Peek.token env with | T_EOF | T_RCURLY -> @@ -1008,7 +1774,7 @@ module Object | T_SEMICOLON -> (* Skip empty elements *) Expect.token env T_SEMICOLON; - elements env seen_constructor private_names acc + elements env ~abstract seen_constructor private_names acc | _ -> let element = class_element env in let (seen_constructor', private_names') = @@ -1065,16 +1831,28 @@ module Object | Ast.Class.Body.PrivateField (_, { Ast.Class.PrivateField.key; _ }) -> let private_names = check_private_names env private_names key `Field in (seen_constructor, private_names) + | Ast.Class.Body.StaticBlock _ -> (seen_constructor, private_names) + | Ast.Class.Body.DeclareMethod _ -> + (* DeclareMethod is a bodyless method signature, no private name checking needed *) + (seen_constructor, private_names) + | Ast.Class.Body.AbstractMethod (loc, _) -> + if not abstract then error_at env (loc, Parse_error.AbstractMethodInNonAbstractClass); + (* AbstractMethod is a bodyless method signature, no private name checking needed *) + (seen_constructor, private_names) + | Ast.Class.Body.AbstractProperty (loc, _) -> + if not abstract then error_at env (loc, Parse_error.AbstractPropertyInNonAbstractClass); + (seen_constructor, private_names) + | Ast.Class.Body.IndexSignature _ -> (seen_constructor, private_names) in - elements env seen_constructor' private_names' (element :: acc) + elements env ~abstract seen_constructor' private_names' (element :: acc) in - fun ~expression env -> + fun ~expression ~abstract env -> with_loc (fun env -> let leading = Peek.comments env in if Eat.maybe env T_LCURLY then ( enter_class env; - let body = elements env false SMap.empty [] in + let body = elements env ~abstract false SMap.empty [] in exit_class env; Expect.token env T_RCURLY; let trailing = @@ -1097,11 +1875,13 @@ module Object let env = env |> with_strict true in let decorators = decorators @ decorator_list env in let leading = Peek.comments env in - (match Peek.token env with - | T_IDENTIFIER { raw = "abstract"; _ } -> - error env Parse_error.TSAbstractClass; - Eat.token env - | _ -> ()); + let abstract = + match Peek.token env with + | T_IDENTIFIER { raw = "abstract"; _ } -> + Eat.token env; + true + | _ -> false + in Expect.token env T_CLASS; let id = let tmp_env = env |> with_no_let true in @@ -1131,9 +1911,18 @@ module Object ) in let (extends, implements) = class_heritage env in - let body = class_body env ~expression in + let body = class_body env ~expression ~abstract in let comments = Flow_ast_utils.mk_comments_opt ~leading () in - { Class.id; body; tparams; extends; implements; class_decorators = decorators; comments } + { + Class.id; + body; + tparams; + extends; + implements; + class_decorators = decorators; + abstract; + comments; + } let class_declaration env decorators = with_loc @@ -1144,4 +1933,253 @@ module Object let class_expression = with_loc (fun env -> Ast.Expression.Class (_class env ~optional_id:true ~expression:true)) + + let record_body = + let open Statement.RecordDeclaration in + let rec elements env acc = + match Peek.token env with + | T_EOF + | T_RCURLY -> + List.rev acc + | _ -> + let maybe_eat_and_get_comments token env = + let cond = + Peek.token env = token + && + match Peek.ith_token ~i:1 env with + | T_COLON (* token: T *) + | T_LESS_THAN (* token() {} *) + | T_LPAREN (* token() {} *) + | T_EOF (* incomplete property *) + | T_RCURLY (* end of record *) -> + false + | _ -> true + in + let comments = + if cond then ( + let leading = Peek.comments env in + Eat.token env; + leading + ) else + [] + in + (cond, comments) + in + let start_loc = Peek.loc env in + let (static, leading_static) = maybe_eat_and_get_comments T_STATIC env in + let (async, leading_async) = maybe_eat_and_get_comments T_ASYNC env in + let (generator, leading_generator) = Declaration.generator env in + let parse_property_variance_keyword = Peek.ith_is_object_key ~i:1 ~is_class:false env in + let variance = Declaration.variance env ~parse_property_variance_keyword async generator in + let (generator, leading_generator) = + match (generator, variance) with + | (false, Some _) -> Declaration.generator env + | _ -> (generator, leading_generator) + in + let leading_key = Peek.comments env in + let leading = List.concat [leading_static; leading_async; leading_generator; leading_key] in + if Peek.token env = T_POUND then ( + let error_loc = Peek.loc env in + Eat.token env; + error_at env (error_loc, Parse_error.RecordPrivateElementUnsupported) + ); + let (key_loc, key) = key ~class_body:false env in + let key = + match key with + | Ast.Expression.Object.Property.Computed _ -> + error_at env (key_loc, Parse_error.RecordComputedPropertyUnsupported); + Ast.Expression.Object.Property.Identifier + (key_loc, { Identifier.name = ""; comments = None }) + | _ -> key + in + let check_invalid_name env ~method_ = + match (string_value_of_key key, static) with + | (Some (key_loc, ("constructor" as key_name)), _) + | (Some (key_loc, ("prototype" as key_name)), true) -> + error_at + env + (key_loc, Parse_error.RecordInvalidPropertyName { name = key_name; static; method_ }) + | _ -> () + in + let empty_invalid_syntax = + { + InvalidPropertySyntax.invalid_suffix_semicolon = None; + invalid_optional = None; + invalid_variance = None; + } + in + let invalid_syntax = + Option.map + (fun variance -> + { empty_invalid_syntax with InvalidPropertySyntax.invalid_variance = Some variance }) + variance + in + let invalid_syntax = + if Peek.token env = T_PLING then ( + let error_loc = Peek.loc env in + Eat.token env; + let invalid_syntax = Option.value invalid_syntax ~default:empty_invalid_syntax in + Some { invalid_syntax with InvalidPropertySyntax.invalid_optional = Some error_loc } + ) else + invalid_syntax + in + let end_property () = + match Peek.token env with + | T_EOF + | T_RCURLY -> + invalid_syntax + | T_SEMICOLON -> + let semicolon_loc = Peek.loc env in + Eat.token env; + let invalid_syntax = Option.value invalid_syntax ~default:empty_invalid_syntax in + Some + { + invalid_syntax with + InvalidPropertySyntax.invalid_suffix_semicolon = Some semicolon_loc; + } + | _ -> + Expect.token env T_COMMA; + invalid_syntax + in + (match Peek.token env with + | T_COLON when static -> + check_invalid_name env ~method_:false; + let prop = + with_loc + ~start_loc + (fun env -> + let annot = Type.annotation env in + Expect.token env T_ASSIGN; + let value = Expression.assignment env in + let invalid_syntax = end_property () in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + { StaticProperty.key; annot; value; comments; invalid_syntax }) + env + in + elements env (Body.StaticProperty prop :: acc) + | T_COLON when not (async || generator) -> + check_invalid_name env ~method_:false; + let prop = + with_loc + ~start_loc + (fun env -> + let annot = Type.annotation env in + let default_value = + if Eat.maybe env T_ASSIGN then + Some (Expression.assignment env) + else + None + in + let invalid_syntax = end_property () in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + { Property.key; annot; default_value; comments; invalid_syntax }) + env + in + elements env (Body.Property prop :: acc) + | T_SEMICOLON + | T_COMMA -> + error_at env (key_loc, Parse_error.RecordPropertyAnnotationRequired); + ignore @@ end_property (); + elements env acc + | T_ASSIGN -> + error_at env (key_loc, Parse_error.RecordPropertyAnnotationRequired); + Eat.token env; + ignore @@ Expression.assignment env; + ignore @@ end_property (); + elements env acc + | _ -> + check_invalid_name env ~method_:true; + let meth = + with_loc + ~start_loc + (fun env -> + let value = parse_method env ~async ~generator ~leading in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + { + Class.Method.key; + value; + kind = Class.Method.Method; + static; + override = false; + ts_accessibility = None; + decorators = []; + comments; + }) + env + in + elements env (Body.Method meth :: acc)) + in + with_loc (fun env -> + let leading = Peek.comments env in + if Eat.maybe env T_LCURLY then ( + let body = elements env [] in + let trailing = + match Peek.token env with + | T_RCURLY + | T_EOF -> + Eat.trailing_comments env + | _ when Peek.is_line_terminator env -> Eat.comments_until_next_line env + | _ -> [] + in + Expect.token env T_RCURLY; + { Body.body; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () } + ) else ( + Expect.error env T_LCURLY; + { Body.body = []; comments = None } + ) + ) + + let record_declaration = + with_loc (fun env -> + let leading = Peek.comments env in + Expect.token env T_RECORD; + let id = Parse.identifier env in + let tparams = + match Type.type_params env with + | None -> None + | Some tparams -> + let { remove_trailing; _ } = trailing_and_remover env in + Some + (remove_trailing tparams (fun remover tparams -> + remover#type_params ~kind:Flow_ast_mapper.RecordTP tparams + ) + ) + in + let invalid_syntax = + if Peek.token env = T_ASSIGN then ( + let invalid_syntax = + { + Ast.Statement.RecordDeclaration.InvalidSyntax.invalid_infix_equals = + Some (Peek.loc env); + } + in + Eat.token env; + Some invalid_syntax + ) else + None + in + if Peek.token env = T_EXTENDS then ( + error_at env (Peek.loc env, Parse_error.RecordExtendsUnsupported); + let leading = Peek.comments env in + Eat.token env; + ignore @@ class_extends ~leading env + ); + let implements = + if Peek.token env = T_IMPLEMENTS then + Some (class_implements_remove_trailing env (class_implements env ~attach_leading:true)) + else + None + in + let body = record_body env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Ast.Statement.RecordDeclaration + { + Ast.Statement.RecordDeclaration.id; + tparams; + implements; + body; + comments; + invalid_syntax; + } + ) end diff --git a/compiler/flow_parser/parser/parse_error.ml b/compiler/flow_parser/parser/parse_error.ml index 905d3928dcf..7392b5c81a6 100644 --- a/compiler/flow_parser/parser/parse_error.ml +++ b/compiler/flow_parser/parser/parse_error.ml @@ -6,6 +6,11 @@ *) type t = + | AbstractMethodInNonAbstractClass + | AbstractMethodWithBody + | AbstractPrivateMember + | AbstractPropertyInNonAbstractClass + | AbstractPropertyWithInitializer | AccessorDataProperty | AccessorGetSet | AdjacentJSXElements @@ -17,7 +22,10 @@ type t = | ConstructorCannotBeAccessor | ConstructorCannotBeAsync | ConstructorCannotBeGenerator + | ConstructorCannotBeOptional | DeclareAsync + | DeclareAsyncComponent + | DeclareAsyncHook | DeclareClassElement | DeclareClassFieldInitializer | DeclareOpaqueTypeInitializer @@ -25,19 +33,6 @@ type t = | DuplicateExport of string | DuplicatePrivateFields of string | ElementAfterRestElement - | EnumBigIntMemberNotInitialized of { - enum_name: string; - member_name: string; - } - | EnumBooleanMemberNotInitialized of { - enum_name: string; - member_name: string; - } - | EnumDuplicateMemberName of { - enum_name: string; - member_name: string; - } - | EnumInconsistentMemberValues of { enum_name: string } | EnumInvalidEllipsis of { trailing_comma: bool } | EnumInvalidExplicitType of { enum_name: string; @@ -47,20 +42,10 @@ type t = | EnumInvalidInitializerSeparator of { member_name: string } | EnumInvalidMemberInitializer of { enum_name: string; - explicit_type: Enum_common.explicit_type option; - member_name: string; - } - | EnumInvalidMemberName of { - enum_name: string; + explicit_type: Flow_ast.Statement.EnumDeclaration.explicit_type option; member_name: string; } | EnumInvalidMemberSeparator - | EnumNumberMemberNotInitialized of { - enum_name: string; - member_name: string; - } - | EnumStringMemberInconsistentlyInitialized of { enum_name: string } - | EnumInvalidConstPrefix | ExpectedJSXClosingTag of string | ExpectedPatternFoundExpression | ExportSpecifierMissingComma @@ -68,12 +53,14 @@ type t = | GeneratorFunctionAsStatement | GetterArity | GetterMayNotHaveThisParam - | IllegalBreak + | IllegalBreak of { in_match_statement: bool } | IllegalContinue | IllegalReturn | IllegalUnicodeEscape + | ImportAttributeMissingComma | ImportSpecifierMissingComma | ImportTypeShorthandOnlyInPureImport + | IndexSignatureInvalidModifier of string | InexactInsideExact | InexactInsideNonObject | InvalidClassMemberName of { @@ -108,6 +95,8 @@ type t = | MatchNonLastRest of [ `Object | `Array ] | MatchEmptyArgument | MatchSpreadArgument + | MatchExpressionAwait + | MatchExpressionYield | MethodInDestructuring | MissingJSXClosingTag of string | MissingTypeParam @@ -121,13 +110,25 @@ type t = | NullishCoalescingUnexpectedLogical of string | OptionalChainNew | OptionalChainTemplate + | OptionalMethodCannotBeAbstract + | OverrideOnConstructor | ParameterAfterRestParameter | PrivateDelete | PrivateNotInClass | PropertyAfterRestElement + | RecordComputedPropertyUnsupported + | RecordExtendsUnsupported + | RecordInvalidPropertyName of { + name: string; + static: bool; + method_: bool; + } + | RecordPrivateElementUnsupported + | RecordPropertyAnnotationRequired | Redeclaration of string * string | SetterArity | SetterMayNotHaveThisParam + | StaticAbstractMethod | StrictCatchVariable | StrictDelete | StrictDuplicateProperty @@ -144,12 +145,10 @@ type t = | StrictReservedWord | StrictVarName | SuperPrivate - | TSAbstractClass - | TSClassVisibility of [ `Public | `Private | `Protected ] - | TSTemplateLiteralType | ThisParamAnnotationRequired | ThisParamBannedInArrowFunctions | ThisParamBannedInConstructor + | ThisParamBannedInConstructorType | ThisParamMayNotBeOptional | ThisParamMustBeFirst | TrailingCommaAfterRestElement @@ -161,6 +160,8 @@ type t = | UnexpectedProto | UnexpectedReserved | UnexpectedReservedType + | UnexpectedOptional + | OptionalDestructuringMustHaveDefault | UnexpectedSpreadType | UnexpectedStatic | UnexpectedSuper @@ -188,6 +189,13 @@ let error loc e = raise (Error ((loc, e), [])) module PP = struct let error = function + | AbstractMethodInNonAbstractClass -> + "Abstract methods can only appear within an abstract class." + | AbstractMethodWithBody -> "Abstract methods cannot have an implementation." + | AbstractPrivateMember -> "The `abstract` modifier cannot be used with a private identifier." + | AbstractPropertyInNonAbstractClass -> + "Abstract properties can only appear within an abstract class." + | AbstractPropertyWithInitializer -> "Abstract properties cannot have an initializer." | AccessorDataProperty -> "Object literal may not have data and accessor property with the same name" | AccessorGetSet -> "Object literal may not have multiple get/set accessors with the same name" @@ -203,9 +211,14 @@ module PP = struct | ConstructorCannotBeAccessor -> "Constructor can't be an accessor." | ConstructorCannotBeAsync -> "Constructor can't be an async function." | ConstructorCannotBeGenerator -> "Constructor can't be a generator." + | ConstructorCannotBeOptional -> "Constructor can't be optional." | DeclareAsync -> "async is an implementation detail and isn't necessary for your declare function statement. " ^ "It is sufficient for your declare function to just have a Promise return type." + | DeclareAsyncComponent -> + "async is an implementation detail and isn't necessary for declared components. Use `declare component` instead." + | DeclareAsyncHook -> + "async is an implementation detail and isn't necessary for declared hooks. Use `declare hook` instead." | DeclareClassElement -> "`declare` modifier can only appear on class fields." | DeclareClassFieldInitializer -> "Unexpected token `=`. Initializers are not allowed in a `declare`." @@ -218,26 +231,6 @@ module PP = struct "Private fields may only be declared once. `#%s` is declared more than once." name | ElementAfterRestElement -> "Rest element must be final element of an array pattern" - | EnumBigIntMemberNotInitialized { enum_name; member_name } -> - Printf.sprintf - "bigint enum members need to be initialized, e.g. `%s = 1n,` in enum `%s`." - member_name - enum_name - | EnumBooleanMemberNotInitialized { enum_name; member_name } -> - Printf.sprintf - "Boolean enum members need to be initialized. Use either `%s = true,` or `%s = false,` in enum `%s`." - member_name - member_name - enum_name - | EnumDuplicateMemberName { enum_name; member_name } -> - Printf.sprintf - "Enum member names need to be unique, but the name `%s` has already been used before in enum `%s`." - member_name - enum_name - | EnumInconsistentMemberValues { enum_name } -> - Printf.sprintf - "Enum `%s` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers." - enum_name | EnumInvalidEllipsis { trailing_comma } -> if trailing_comma then "The `...` must come at the end of the enum body. Remove the trailing comma." @@ -262,50 +255,27 @@ module PP = struct "Enum member names and initializers are separated with `=`. Replace `%s:` with `%s =`." member_name member_name - | EnumInvalidMemberInitializer { enum_name; explicit_type; member_name } -> begin - match explicit_type with - | Some (Enum_common.Boolean as explicit_type) - | Some (Enum_common.Number as explicit_type) - | Some (Enum_common.String as explicit_type) - | Some (Enum_common.BigInt as explicit_type) -> - let explicit_type_str = Enum_common.string_of_explicit_type explicit_type in - Printf.sprintf - "Enum `%s` has type `%s`, so the initializer of `%s` needs to be a %s literal." - enum_name - explicit_type_str - member_name - explicit_type_str - | Some Enum_common.Symbol -> + | EnumInvalidMemberInitializer { enum_name; explicit_type; member_name } -> + (match explicit_type with + | Some Flow_ast.Statement.EnumDeclaration.Symbol -> Printf.sprintf "Symbol enum members cannot be initialized. Use `%s,` in enum `%s`." member_name enum_name + | Some t -> + let type_str = Flow_ast_utils.string_of_enum_explicit_type t in + Printf.sprintf + "Enum `%s` has type `%s`, so the initializer of `%s` needs to be a %s literal." + enum_name + type_str + member_name + type_str | None -> Printf.sprintf - "The enum member initializer for `%s` needs to be a literal (either a boolean, number, or string) in enum `%s`." + "The enum member initializer for `%s` needs to be a literal (either a boolean, number, bigint, or string) in enum `%s`." member_name - enum_name - end - | EnumInvalidMemberName { enum_name; member_name } -> - (* Based on the error condition, we will only receive member names starting with [a-z] *) - let suggestion = String.capitalize_ascii member_name in - Printf.sprintf - "Enum member names cannot start with lowercase 'a' through 'z'. Instead of using `%s`, consider using `%s`, in enum `%s`." - member_name - suggestion - enum_name + enum_name) | EnumInvalidMemberSeparator -> "Enum members are separated with `,`. Replace `;` with `,`." - | EnumNumberMemberNotInitialized { enum_name; member_name } -> - Printf.sprintf - "Number enum members need to be initialized, e.g. `%s = 1,` in enum `%s`." - member_name - enum_name - | EnumStringMemberInconsistentlyInitialized { enum_name } -> - Printf.sprintf - "String enum members need to consistently either all use initializers, or use no initializers, in enum %s." - enum_name - | EnumInvalidConstPrefix -> - "`const` enums are not supported. Flow Enums are designed to allow for inlining, however the inlining itself needs to be part of the build system (whatever you use) rather than Flow itself." | ExpectedJSXClosingTag name -> Printf.sprintf "Expected corresponding JSX closing tag for %s" name | ExpectedPatternFoundExpression -> @@ -322,14 +292,24 @@ module PP = struct "Generators can only be declared at top level or immediately within another function." | GetterArity -> "Getter should have zero parameters" | GetterMayNotHaveThisParam -> "A getter cannot have a `this` parameter." - | IllegalBreak -> "Illegal break statement" + | IllegalBreak { in_match_statement } -> + let extra = + if in_match_statement then + " `break` statements are not required in `match` statements, as unlike `switch` statements, `match` statement cases do not fall-through by default." + else + "" + in + Printf.sprintf "Illegal break statement.%s" extra | IllegalContinue -> "Illegal continue statement" | IllegalReturn -> "Illegal return statement" | IllegalUnicodeEscape -> "Illegal Unicode escape" + | ImportAttributeMissingComma -> "Missing comma between import attributes" | ImportSpecifierMissingComma -> "Missing comma between import specifiers" | ImportTypeShorthandOnlyInPureImport -> "The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. " ^ "It cannot be used with `import type` or `import typeof` statements" + | IndexSignatureInvalidModifier modifier -> + Printf.sprintf "`%s` modifier cannot be used with index signatures." modifier | InexactInsideExact -> "Explicit inexact syntax cannot appear inside an explicit exact object type" | InexactInsideNonObject -> "Explicit inexact syntax can only appear inside an object type" @@ -405,6 +385,8 @@ module PP = struct Printf.sprintf "In match %s pattern, the rest must be the last element in the pattern" kind | MatchEmptyArgument -> "`match` argument must not be empty" | MatchSpreadArgument -> "`match` argument cannot contain spread elements" + | MatchExpressionAwait -> "`await` is not yet supported in `match` expressions" + | MatchExpressionYield -> "`yield` is not yet supported in `match` expressions" | MethodInDestructuring -> "Object pattern can't contain methods" | MissingJSXClosingTag name -> Printf.sprintf "JSX element %s has no corresponding closing tag." name @@ -423,13 +405,35 @@ module PP = struct operator | OptionalChainNew -> "An optional chain may not be used in a `new` expression." | OptionalChainTemplate -> "Template literals may not be used in an optional chain." + | OptionalMethodCannotBeAbstract -> "Optional methods can't be abstract." + | OverrideOnConstructor -> "'override' modifier cannot appear on a constructor declaration" | ParameterAfterRestParameter -> "Rest parameter must be final parameter of an argument list" | PrivateDelete -> "Private fields may not be deleted." | PrivateNotInClass -> "Private fields can only be referenced from within a class." | PropertyAfterRestElement -> "Rest property must be final property of an object pattern" + | RecordComputedPropertyUnsupported -> "Records do not support computed properties." + | RecordExtendsUnsupported -> + "Records to not support `extends`: they do not allow hierarchies. Implementing an interface by using `implements` is supported." + | RecordInvalidPropertyName { name; static; method_ } -> + let static_modifier = + if static then + "static " + else + "" + in + let category = + if method_ then + "methods" + else + "properties" + in + Printf.sprintf "Records may not have %s%s named `%s`." static_modifier category name + | RecordPrivateElementUnsupported -> "Records to not support private elements. Remove the `#`." + | RecordPropertyAnnotationRequired -> "Record properties must have a type annotation." | Redeclaration (what, name) -> Printf.sprintf "%s '%s' has already been declared" what name | SetterArity -> "Setter should have exactly one parameter" | SetterMayNotHaveThisParam -> "A setter cannot have a `this` parameter." + | StaticAbstractMethod -> "`static` modifier can't be used with the `abstract` modifier." | StrictCatchVariable -> "Catch variable may not be eval or arguments in strict mode" | StrictDelete -> "Delete of an unqualified identifier in strict mode." | StrictDuplicateProperty -> @@ -450,27 +454,12 @@ module PP = struct | StrictReservedWord -> "Use of reserved word in strict mode" | StrictVarName -> "Variable name may not be eval or arguments in strict mode" | SuperPrivate -> "You may not access a private field through the `super` keyword." - | TSAbstractClass -> "Flow does not support abstract classes." - | TSClassVisibility kind -> - let (keyword, append) = - match kind with - | `Private -> - ( "private", - " You can try using JavaScript private fields by prepending `#` to the field name." - ) - | `Public -> - ( "public", - " Fields and methods are public by default. You can simply omit the `public` keyword." - ) - | `Protected -> ("protected", "") - in - Printf.sprintf "Flow does not support using `%s` in classes.%s" keyword append - | TSTemplateLiteralType -> "Flow does not support template literal types." | ThisParamAnnotationRequired -> "A type annotation is required for the `this` parameter." | ThisParamBannedInArrowFunctions -> "Arrow functions cannot have a `this` parameter; arrow functions automatically bind `this` when declared." | ThisParamBannedInConstructor -> "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions." + | ThisParamBannedInConstructorType -> "Constructor types cannot have a `this` parameter." | ThisParamMayNotBeOptional -> "The `this` parameter cannot be optional." | ThisParamMustBeFirst -> "The `this` parameter must be the first function parameter." | TrailingCommaAfterRestElement -> "A trailing comma is not permitted after the rest element" @@ -486,6 +475,9 @@ module PP = struct | UnexpectedProto -> "Unexpected proto modifier" | UnexpectedReserved -> "Unexpected reserved word" | UnexpectedReservedType -> "Unexpected reserved type" + | UnexpectedOptional -> "Unexpected `?` (optional modifier not allowed here)" + | OptionalDestructuringMustHaveDefault -> + "Optional destructuring patterns must use a default value (e.g., `{...}: T = {}`)." | UnexpectedSpreadType -> "Spreading a type is only allowed inside an object type" | UnexpectedStatic -> "Unexpected static modifier" | UnexpectedSuper -> "Unexpected `super` outside of a class method" diff --git a/compiler/flow_parser/parser/parser_common.ml b/compiler/flow_parser/parser/parser_common.ml index ad67acf6a81..ed9ba4cf88e 100644 --- a/compiler/flow_parser/parser/parser_common.ml +++ b/compiler/flow_parser/parser/parser_common.ml @@ -47,7 +47,7 @@ module type PARSER = sig val identifier : ?restricted_error:Parse_error.t -> env -> (Loc.t, Loc.t) Identifier.t val identifier_with_type : - env -> ?no_optional:bool -> Parse_error.t -> Loc.t * (Loc.t, Loc.t) Pattern.Identifier.t + env -> allow_optional:bool -> Parse_error.t -> Loc.t * (Loc.t, Loc.t) Pattern.Identifier.t val block_body : env -> Loc.t * (Loc.t, Loc.t) Statement.Block.t @@ -58,7 +58,7 @@ module type PARSER = sig env -> Loc.t * [ `Element of (Loc.t, Loc.t) JSX.element | `Fragment of (Loc.t, Loc.t) JSX.fragment ] - val pattern : env -> Parse_error.t -> (Loc.t, Loc.t) Pattern.t + val pattern : env -> allow_optional:bool -> Parse_error.t -> (Loc.t, Loc.t) Pattern.t val pattern_from_expr : env -> (Loc.t, Loc.t) Expression.t -> (Loc.t, Loc.t) Pattern.t @@ -82,6 +82,8 @@ end module type TYPE = sig val _type : env -> (Loc.t, Loc.t) Type.t + val union : env -> (Loc.t, Loc.t) Type.t + val type_identifier : env -> (Loc.t, Loc.t) Identifier.t val type_params : env -> (Loc.t, Loc.t) Type.TypeParams.t option @@ -132,7 +134,7 @@ end module type PATTERN = sig val from_expr : Parser_env.env -> (Loc.t, Loc.t) Expression.t -> (Loc.t, Loc.t) Pattern.t - val pattern : Parser_env.env -> Parse_error.t -> (Loc.t, Loc.t) Pattern.t + val pattern : Parser_env.env -> allow_optional:bool -> Parse_error.t -> (Loc.t, Loc.t) Pattern.t end module type OBJECT = sig @@ -147,6 +149,8 @@ module type OBJECT = sig val class_implements : env -> attach_leading:bool -> (Loc.t, Loc.t) Class.Implements.t val decorator_list : env -> (Loc.t, Loc.t) Class.Decorator.t list + + val record_declaration : env -> (Loc.t, Loc.t) Statement.t end module type JSX = sig @@ -244,6 +248,8 @@ module type STATEMENT = sig val var : env -> (Loc.t, Loc.t) Statement.t val const : env -> (Loc.t, Loc.t) Statement.t + + val declare_namespace : env -> global:bool -> implicit_declare:bool -> (Loc.t, Loc.t) Statement.t end module type DECLARATION = sig @@ -251,7 +257,8 @@ module type DECLARATION = sig val generator : env -> bool * Loc.t Comment.t list - val variance : env -> parse_readonly:bool -> bool -> bool -> Loc.t Variance.t option + val variance : + env -> parse_property_variance_keyword:bool -> bool -> bool -> Loc.t Variance.t option val function_params : await:bool -> yield:bool -> env -> (Loc.t, Loc.t) Function.Params.t @@ -300,11 +307,21 @@ module type DECLARATION = sig * Loc.t Comment.t list * (Loc.t * Parse_error.t) list + val variable_declaration_list : + env -> + (Loc.t, Loc.t) Statement.VariableDeclaration.Declarator.t list * (Loc.t * Parse_error.t) list + val _function : env -> (Loc.t, Loc.t) Statement.t - val enum_declaration : ?leading:Loc.t Comment.t list -> env -> (Loc.t, Loc.t) Statement.t + val enum_declaration : + ?leading:Loc.t Comment.t list -> const_:bool -> env -> (Loc.t, Loc.t) Statement.t + + val component_params : env -> (Loc.t, Loc.t) Statement.ComponentDeclaration.Params.t val component : env -> (Loc.t, Loc.t) Statement.t + + val convert_function_params_to_type_params : + (Loc.t, Loc.t) Function.Params.t -> ((Loc.t, Loc.t) Type.Function.Params.t, string list) result end module type MATCH_PATTERN = sig @@ -376,6 +393,7 @@ let identifier_name_raw env = | T_OPAQUE -> "opaque" | T_ANY_TYPE -> "any" | T_MATCH -> "match" + | T_RECORD -> "record" | T_MIXED_TYPE -> "mixed" | T_EMPTY_TYPE -> "empty" | T_BOOLEAN_TYPE BOOL -> "bool" @@ -390,6 +408,8 @@ let identifier_name_raw env = | T_UNDEFINED_TYPE -> "undefined" | T_KEYOF -> "keyof" | T_READONLY -> "readonly" + | T_WRITEONLY -> "writeonly" + | T_INFER -> "infer" (* Contextual stuff *) | T_OF -> "of" | T_ASYNC -> "async" @@ -433,7 +453,11 @@ let private_identifier env = https://tc39.es/ecma262/#sec-static-semantics-issimpleparameterlist *) let is_simple_parameter_list = let is_simple_param = function - | (_, { Flow_ast.Function.Param.argument = (_, Pattern.Identifier _); default = None }) -> true + | ( _, + Flow_ast.Function.Param.RegularParam + { argument = (_, Pattern.Identifier _); default = None } + ) -> + true | _ -> false in fun (_, { Flow_ast.Function.Params.params; rest; comments = _; this_ = _ }) -> @@ -475,7 +499,10 @@ let assert_identifier_name_is_identifier | _ when is_reserved name -> error_at env (loc, Parse_error.UnexpectedReserved) | _ -> begin match restricted_error with - | Some err when is_restricted name -> strict_error_at env (loc, err) + (* In ambient contexts (e.g. declaration files), "eval" and "arguments" are + allowed as identifiers since they are type-level declarations, not runtime code. *) + | Some err when is_restricted name && not (in_ambient_context env) -> + strict_error_at env (loc, err) | _ -> () end @@ -507,19 +534,23 @@ let is_start_of_type_guard env = (* Parse the identifier part as normal code, since this can be any name that * a parameter can be. *) Eat.push_lex_mode env Lex_mode.NORMAL; + let token_1_is_identifier_name = Peek.ith_is_identifier_name ~i:0 env in let token_1 = Peek.token env in Eat.pop_lex_mode env; - let token_2 = Peek.ith_token ~i:1 env in - match (token_1, token_2) with - | (T_IDENTIFIER { raw = "asserts"; _ }, (T_IDENTIFIER _ | T_THIS)) - | (T_IDENTIFIER { raw = "implies"; _ }, (T_IDENTIFIER _ | T_THIS)) - | ((T_IDENTIFIER _ | T_THIS), (T_IS | T_IDENTIFIER { raw = "is"; _ })) -> - true - | _ -> false + if not token_1_is_identifier_name then + false + else + let token_2 = Peek.ith_token ~i:1 env in + match (token_1, token_2) with + | (T_IDENTIFIER { raw = "asserts"; _ }, (T_IDENTIFIER _ | T_THIS)) + | (T_IDENTIFIER { raw = "implies"; _ }, (T_IDENTIFIER _ | T_THIS)) + | (_, (T_IS | T_IDENTIFIER { raw = "is"; _ })) -> + true + | _ -> false let reparse_arguments_as_match_argument env (args_loc, args) = let { Expression.ArgList.arguments; _ } = args in - if Base.List.is_empty arguments then Parser_env.error_at env (args_loc, Parse_error.MatchEmptyArgument); + if List.is_empty arguments then Parser_env.error_at env (args_loc, Parse_error.MatchEmptyArgument); let filtered_args = List.filter_map (function diff --git a/compiler/flow_parser/parser/parser_env.ml b/compiler/flow_parser/parser/parser_env.ml index bc40743638c..ca6a65620dd 100644 --- a/compiler/flow_parser/parser/parser_env.ml +++ b/compiler/flow_parser/parser/parser_env.ml @@ -15,7 +15,6 @@ module Lex_mode = struct | TYPE | JSX_TAG | JSX_CHILD - | TEMPLATE | REGEXP let debug_string_of_lex_mode (mode : t) = @@ -24,7 +23,6 @@ module Lex_mode = struct | TYPE -> "TYPE" | JSX_TAG -> "JSX_TAG" | JSX_CHILD -> "JSX_CHILD" - | TEMPLATE -> "TEMPLATE" | REGEXP -> "REGEXP" end @@ -52,6 +50,10 @@ module Lookahead : sig val lex_env_0 : t -> Lex_env.t val junk : t -> unit + + val rescan_template_part : t -> Lex_env.t -> unit + + val rescan_template_part_from : t -> Lex_env.t -> Lex_env.t -> unit end = struct type la_result = (Lex_env.t * Lex_result.t) option @@ -75,7 +77,6 @@ end = struct | Lex_mode.TYPE -> Flow_lexer.type_token lex_env | Lex_mode.JSX_TAG -> Flow_lexer.jsx_tag lex_env | Lex_mode.JSX_CHILD -> Flow_lexer.jsx_child lex_env - | Lex_mode.TEMPLATE -> Flow_lexer.template_tail lex_env | Lex_mode.REGEXP -> Flow_lexer.regexp lex_env in let cloned_env = Lex_env.clone lex_env in @@ -115,6 +116,35 @@ end = struct | Some _ -> t.la_results_0 <- t.la_results_1; t.la_results_1 <- None + + (* Rescan the current position using the template tail lexer. + This replaces push_lex_mode(TEMPLATE) by directly lexing a template + continuation without modifying the lex mode stack. *) + let rescan_template_part t lex_env = + t.la_lex_env <- Lex_env.clone lex_env; + t.la_results_0 <- None; + t.la_results_1 <- None; + let (lex_env, lex_result) = Flow_lexer.template_tail t.la_lex_env in + let cloned_env = Lex_env.clone lex_env in + t.la_lex_env <- lex_env; + t.la_results_0 <- Some (cloned_env, lex_result) + + let rescan_template_part_from t lex_env prev_lex_env = + let existing_comments = + match t.la_results_0 with + | Some (_, result) -> Lex_result.comments result + | None -> [] + in + let env = Lex_env.clone prev_lex_env in + let env = Lex_env.set_last_loc env (Lex_env.lex_last_loc lex_env) in + t.la_lex_env <- env; + t.la_results_0 <- None; + t.la_results_1 <- None; + let (lex_env, lex_result) = Flow_lexer.template_tail_start t.la_lex_env in + let lex_result = Lex_result.with_comments lex_result existing_comments in + let cloned_env = Lex_env.clone lex_env in + t.la_lex_env <- lex_env; + t.la_results_0 <- Some (cloned_env, lex_result) end type token_sink_result = { @@ -127,35 +157,43 @@ type parse_options = { components: bool; (* enable parsing of Flow component syntax *) enums: bool; (** enable parsing of Flow enums *) pattern_matching: bool; + records: bool; esproposal_decorators: bool; (** enable parsing of decorators *) types: bool; (** enable parsing of Flow types *) use_strict: bool; (** treat the file as strict, without needing a "use strict" directive *) module_ref_prefix: string option; - module_ref_prefix_LEGACY_INTEROP: string option; + assert_operator: bool; + allow_return_outside_function: bool; + (** suppress the [IllegalReturn] diagnostic for top-level [return] + statements; matches hermes-parser [allowReturnOutsideFunction] *) } let default_parse_options = { components = false; enums = false; + assert_operator = false; pattern_matching = false; + records = false; esproposal_decorators = false; types = true; use_strict = false; module_ref_prefix = None; - module_ref_prefix_LEGACY_INTEROP = None; + allow_return_outside_function = false; } let permissive_parse_options = { components = true; enums = true; + assert_operator = false; pattern_matching = true; + records = true; esproposal_decorators = true; types = true; use_strict = false; module_ref_prefix = None; - module_ref_prefix_LEGACY_INTEROP = None; + allow_return_outside_function = false; } type allowed_super = @@ -175,17 +213,21 @@ type env = { in_switch: bool; in_formal_parameters: bool; in_function: bool; + in_match_expression: bool; + in_match_statement: bool; no_in: bool; no_call: bool; no_let: bool; no_anon_function_type: bool; no_conditional_type: bool; no_new: bool; + no_record: bool; allow_yield: bool; allow_await: bool; allow_directive: bool; has_simple_parameters: bool; allow_super: allowed_super; + in_ambient_context: bool; error_callback: (env -> Parse_error.t -> unit) option; lex_mode_stack: Lex_mode.t list ref; (* lex_env is the lex_env after the single lookahead has been lexed *) @@ -231,16 +273,20 @@ let init_env ?(token_sink = None) ?(parse_options = None) source content = in_switch = false; in_formal_parameters = false; in_function = false; + in_match_expression = false; + in_match_statement = false; no_in = false; no_call = false; no_let = false; no_anon_function_type = false; no_conditional_type = false; no_new = false; + no_record = false; allow_yield = false; allow_await = false; allow_directive = false; allow_super = No_super; + in_ambient_context = false; error_callback = None; lex_mode_stack = ref [Lex_mode.NORMAL]; lex_env = ref lex_env; @@ -273,6 +319,10 @@ let in_formal_parameters env = env.in_formal_parameters let in_function env = env.in_function +let in_match_expression env = env.in_match_expression + +let in_match_statement env = env.in_match_statement + let allow_yield env = env.allow_yield let allow_await env = env.allow_await @@ -281,6 +331,8 @@ let allow_directive env = env.allow_directive let allow_super env = env.allow_super +let in_ambient_context env = env.in_ambient_context + let has_simple_parameters env = env.has_simple_parameters let no_in env = env.no_in @@ -295,6 +347,8 @@ let no_conditional_type env = env.no_conditional_type let no_new env = env.no_new +let no_record env = env.no_record + let errors env = !(env.errors) let parse_options env = env.parse_options @@ -303,6 +357,14 @@ let source env = env.source let should_parse_types env = env.parse_options.types +let is_d_ts env = + match env.source with + | Some file_key -> + File_key.check_suffix file_key ".d.ts" + || File_key.check_suffix file_key ".d.mts" + || File_key.check_suffix file_key ".d.cts" + | None -> false + (* mutators: *) let error_at env (loc, e) = env.errors := (loc, e) :: !(env.errors); @@ -377,11 +439,17 @@ let with_in_formal_parameters in_formal_parameters env = else { env with in_formal_parameters } -let with_in_function in_function env = - if in_function = env.in_function then +let with_in_match_expression in_match_expression env = + if in_match_expression = env.in_match_expression then env else - { env with in_function } + { env with in_match_expression } + +let with_in_match_statement in_match_statement env = + if in_match_statement = env.in_match_statement then + env + else + { env with in_match_statement } let with_allow_yield allow_yield env = if allow_yield = env.allow_yield then @@ -407,6 +475,12 @@ let with_allow_super allow_super env = else { env with allow_super } +let with_ambient_context in_ambient_context env = + if in_ambient_context = env.in_ambient_context then + env + else + { env with in_ambient_context } + let with_no_let no_let env = if no_let = env.no_let then env @@ -443,6 +517,12 @@ let with_no_new no_new env = else { env with no_new } +let with_no_record no_record env = + if no_record = env.no_record then + env + else + { env with no_record } + let with_in_switch in_switch env = if in_switch = env.in_switch then env @@ -494,6 +574,8 @@ let enter_function env ~async ~generator ~simple_params = in_function = true; in_loop = false; in_switch = false; + in_match_expression = false; + in_match_statement = false; in_export = false; in_export_default = false; labels = SSet.empty; @@ -552,19 +634,6 @@ let is_contextually_reserved str_val = true | _ -> false -(** Words that are sometimes reserved, and sometimes allowed as identifiers - (namely "await" and "yield") - - https://tc39.es/ecma262/#sec-keywords-and-reserved-words *) -let token_is_contextually_reserved t = - let open Token in - match t with - | T_IDENTIFIER { raw; _ } -> is_contextually_reserved raw - | T_AWAIT - | T_YIELD -> - true - | _ -> false - (** Words that are always reserved (mostly keywords) https://tc39.es/ecma262/#sec-keywords-and-reserved-words *) @@ -673,6 +742,7 @@ let is_reserved_type str_val = | "null" | "number" | "readonly" + | "writeonly" | "static" | "string" | "symbol" @@ -680,8 +750,7 @@ let is_reserved_type str_val = | "typeof" | "undefined" | "unknown" - | "void" - | "_" -> + | "void" -> true | _ -> false @@ -704,6 +773,7 @@ let token_is_reserved_type t = | T_NULL | T_NUMBER_TYPE | T_READONLY + | T_WRITEONLY | T_STATIC | T_STRING_TYPE | T_SYMBOL_TYPE @@ -775,6 +845,7 @@ let token_is_type_identifier env t = | T_INTERFACE | T_LET | T_MATCH + | T_RECORD | T_NEW | T_NULL | T_OF @@ -802,6 +873,7 @@ let token_is_type_identifier env t = | T_FUNCTION | T_KEYOF | T_READONLY + | T_WRITEONLY | T_INFER | T_IS | T_ASSERTS @@ -891,7 +963,6 @@ let token_is_type_identifier env t = end | Lex_mode.JSX_TAG | Lex_mode.JSX_CHILD - | Lex_mode.TEMPLATE | Lex_mode.REGEXP -> false @@ -983,9 +1054,12 @@ module Peek = struct | T_AWAIT | T_ENUM | T_MATCH + | T_RECORD | T_POUND | T_IDENTIFIER _ - | T_READONLY -> + | T_READONLY + | T_WRITEONLY + | T_INFER -> true | _ -> false @@ -993,14 +1067,21 @@ module Peek = struct let ith_is_identifier_name ~i env = ith_is_identifier ~i env || ith_is_type_identifier ~i env + let ith_is_object_key ~i ~is_class env = + match ith_token ~i env with + | T_STRING _ + | T_NUMBER _ + | T_BIGINT _ + | T_LBRACKET -> + true + | T_POUND when is_class -> true + | t when token_is_reserved t -> true + | _ -> ith_is_identifier_name ~i env + (* This returns true if the next token is identifier-ish (even if it is an error) *) let is_identifier env = ith_is_identifier ~i:0 env - let is_identifier_name env = ith_is_identifier_name ~i:0 env - - let is_type_identifier env = ith_is_type_identifier ~i:0 env - let is_function env = token env = T_FUNCTION || token env = T_ASYNC @@ -1013,6 +1094,14 @@ module Peek = struct (parse_options env).components && ith_is_identifier ~i:1 env && (loc env)._end.line = (ith_loc ~i:1 env).start.line + | T_ASYNC + when (parse_options env).components + && (loc env)._end.line = (ith_loc ~i:1 env).start.line + && + match ith_token ~i:1 env with + | T_IDENTIFIER { raw = "hook"; _ } -> true + | _ -> false -> + true | _ -> false let is_class env = @@ -1028,12 +1117,25 @@ module Peek = struct && match token env with | T_IDENTIFIER { raw = "component"; _ } when ith_is_identifier ~i:1 env -> true + | T_ASYNC + when (loc env)._end.line = (ith_loc ~i:1 env).start.line + && + match ith_token ~i:1 env with + | T_IDENTIFIER { raw = "component"; _ } -> true + | _ -> false -> + true | _ -> false let is_renders_ident env = match token env with | T_IDENTIFIER { raw = "renders"; _ } -> true | _ -> false + + let is_record env = + (parse_options env).records + && token env = T_RECORD + && (not (ith_is_line_terminator ~i:1 env)) + && ith_is_identifier ~i:1 env end (*****************************************************************************) @@ -1087,14 +1189,25 @@ let function_as_statement_error_at env loc = module Eat = struct (* Consume a single token *) let token env = - (* If there's a token_sink, emit the lexed token before moving forward *) + (* If there's a token_sink, emit the lexed token before moving forward. + Skip T_EOF — upstream Hermes' token stream does not include a sentinel + end-of-input token, so neither should ours. The Rust port mirrors this + in `parser_env.rs::token::token`. *) (match !(env.token_sink) with | None -> () + | Some _ when Peek.token env = Token.T_EOF -> () | Some token_sink -> + let token_loc = Peek.loc env in + let token = Peek.token env in + let token_loc = + match token with + | Token.T_INTERPRETER (loc, _) -> loc + | _ -> token_loc + in token_sink { - token_loc = Peek.loc env; - token = Peek.token env; + token_loc; + token; (* * The lex mode is useful because it gives context to some * context-sensitive tokens. @@ -1144,6 +1257,14 @@ module Eat = struct env.lex_mode_stack := new_stack; env.lookahead := Lookahead.create !(env.lex_env) (lex_mode env) + (* Rescan the current position as a template continuation (template middle/tail). + This replaces the pattern of push_lex_mode(TEMPLATE) + pop_lex_mode() + by directly lexing a template tail without modifying the lex mode stack. *) + let rescan_as_template env = Lookahead.rescan_template_part !(env.lookahead) !(env.lex_env) + + let rescan_as_template_from env prev_lex_env = + Lookahead.rescan_template_part_from !(env.lookahead) !(env.lex_env) prev_lex_env + let trailing_comments env = let open Loc in let loc = Peek.loc env in diff --git a/compiler/flow_parser/parser/parser_env.mli b/compiler/flow_parser/parser/parser_env.mli index 0286af980c3..8170e3798ad 100644 --- a/compiler/flow_parser/parser/parser_env.mli +++ b/compiler/flow_parser/parser/parser_env.mli @@ -16,7 +16,6 @@ module Lex_mode : sig | TYPE | JSX_TAG | JSX_CHILD - | TEMPLATE | REGEXP val debug_string_of_lex_mode : t -> string @@ -32,11 +31,15 @@ type parse_options = { components: bool; (* enable parsing of Flow component syntax *) enums: bool; (** enable parsing of Flow enums *) pattern_matching: bool; + records: bool; esproposal_decorators: bool; (** enable parsing of decorators *) types: bool; (** enable parsing of Flow types *) use_strict: bool; (** treat the file as strict, without needing a "use strict" directive *) module_ref_prefix: string option; - module_ref_prefix_LEGACY_INTEROP: string option; + assert_operator: bool; + allow_return_outside_function: bool; + (** suppress the [IllegalReturn] diagnostic for top-level [return] + statements; matches hermes-parser [allowReturnOutsideFunction] *) } val default_parse_options : parse_options @@ -81,6 +84,10 @@ val in_formal_parameters : env -> bool val in_function : env -> bool +val in_match_expression : env -> bool + +val in_match_statement : env -> bool + val allow_yield : env -> bool val allow_await : env -> bool @@ -89,6 +96,8 @@ val allow_directive : env -> bool val allow_super : env -> allowed_super +val in_ambient_context : env -> bool + val has_simple_parameters : env -> bool val no_in : env -> bool @@ -103,6 +112,8 @@ val no_conditional_type : env -> bool val no_new : env -> bool +val no_record : env -> bool + val errors : env -> (Loc.t * Parse_error.t) list val parse_options : env -> parse_options @@ -111,6 +122,8 @@ val source : env -> File_key.t option val should_parse_types : env -> bool +val is_d_ts : env -> bool + (* mutators: *) val error_at : env -> Loc.t * Parse_error.t -> unit @@ -146,7 +159,9 @@ val with_strict : bool -> env -> env val with_in_formal_parameters : bool -> env -> env -val with_in_function : bool -> env -> env +val with_in_match_expression : bool -> env -> env + +val with_in_match_statement : bool -> env -> env val with_allow_yield : bool -> env -> env @@ -156,6 +171,8 @@ val with_allow_directive : bool -> env -> env val with_allow_super : allowed_super -> env -> env +val with_ambient_context : bool -> env -> env + val with_no_let : bool -> env -> env val with_in_loop : bool -> env -> env @@ -168,6 +185,8 @@ val with_no_conditional_type : bool -> env -> env val with_no_new : bool -> env -> env +val with_no_record : bool -> env -> env + val with_in_switch : bool -> env -> env val with_in_export : bool -> env -> env @@ -188,8 +207,6 @@ val is_contextually_reserved : string -> bool val is_reserved : string -> bool -val token_is_contextually_reserved : Token.t -> bool - val token_is_reserved : Token.t -> bool val token_is_reserved_type : Token.t -> bool @@ -225,10 +242,6 @@ module Peek : sig val is_identifier : env -> bool - val is_type_identifier : env -> bool - - val is_identifier_name : env -> bool - val is_function : env -> bool val is_hook : env -> bool @@ -237,6 +250,8 @@ module Peek : sig val is_component : env -> bool + val is_record : env -> bool + val is_renders_ident : env -> bool val ith_token : i:int -> env -> Token.t @@ -256,6 +271,10 @@ module Peek : sig val ith_is_identifier_name : i:int -> env -> bool val ith_is_type_identifier : i:int -> env -> bool + + val ith_is_object_key : i:int -> is_class:bool -> env -> bool + + val lex_env : env -> Lex_env.t end module Eat : sig @@ -269,6 +288,10 @@ module Eat : sig val double_pop_lex_mode : env -> unit + val rescan_as_template : env -> unit + + val rescan_as_template_from : env -> Lex_env.t -> unit + val trailing_comments : env -> Loc.t Flow_ast.Comment.t list val comments_until_next_line : env -> Loc.t Flow_ast.Comment.t list diff --git a/compiler/flow_parser/parser/parser_flow.ml b/compiler/flow_parser/parser/parser_flow.ml index ef0b64a8680..48d49a5c4c4 100644 --- a/compiler/flow_parser/parser/parser_flow.ml +++ b/compiler/flow_parser/parser/parser_flow.ml @@ -42,15 +42,24 @@ let filter_duplicate_errors = let check_for_duplicate_exports = let open Ast in - let record_export env seen (loc, { Identifier.name = export_name; comments = _ }) = + let record_export + env (type_seen, value_seen) kind (loc, { Identifier.name = export_name; comments = _ }) = if export_name = "" then (* empty identifiers signify an error, don't export it *) - seen - else if SSet.mem export_name seen then ( - error_at env (loc, Parse_error.DuplicateExport export_name); - seen - ) else - SSet.add export_name seen + (type_seen, value_seen) + else + let relevant_seen = + match kind with + | Statement.ExportType -> type_seen + | Statement.ExportValue -> value_seen + in + if SSet.mem export_name relevant_seen then ( + error_at env (loc, Parse_error.DuplicateExport export_name); + (type_seen, value_seen) + ) else + match kind with + | Statement.ExportType -> (SSet.add export_name type_seen, value_seen) + | Statement.ExportValue -> (type_seen, SSet.add export_name value_seen) in let extract_pattern_binding_names = let rec fold acc = @@ -83,10 +92,16 @@ let check_for_duplicate_exports = let record_export_of_statement env seen decl = match decl with | (_, Statement.ExportDefaultDeclaration { Statement.ExportDefaultDeclaration.default; _ }) -> - record_export env seen (Flow_ast_utils.ident_of_source (default, "default")) + let id = Flow_ast_utils.ident_of_source (default, "default") in + record_export env seen Statement.ExportValue id | ( _, Statement.ExportNamedDeclaration - { Statement.ExportNamedDeclaration.specifiers = Some specifiers; declaration = None; _ } + { + Statement.ExportNamedDeclaration.specifiers = Some specifiers; + declaration = None; + export_kind = statement_export_kind; + _; + } ) -> let open Statement.ExportNamedDeclaration in (match specifiers with @@ -97,13 +112,17 @@ let check_for_duplicate_exports = { Statement.ExportNamedDeclaration.ExportSpecifier.local; exported; + export_kind = spec_export_kind; from_remote = _; imported_name_def_loc = _; } ) -> + let kind = + Flow_ast_utils.effective_export_kind ~statement_export_kind spec_export_kind + in match exported with - | Some exported -> record_export env seen exported - | None -> record_export env seen local) + | Some exported -> record_export env seen kind exported + | None -> record_export env seen kind local) seen specifiers | ExportBatchSpecifier _ -> @@ -111,7 +130,12 @@ let check_for_duplicate_exports = seen) | ( _, Statement.ExportNamedDeclaration - { Statement.ExportNamedDeclaration.specifiers = None; declaration = Some declaration; _ } + { + Statement.ExportNamedDeclaration.specifiers = None; + declaration = Some declaration; + export_kind; + _; + } ) -> (match declaration with | ( loc, @@ -121,11 +145,13 @@ let check_for_duplicate_exports = | Statement.ClassDeclaration { Class.id = Some id; _ } | Statement.FunctionDeclaration { Function.id = Some id; _ } | Statement.EnumDeclaration { Statement.EnumDeclaration.id; _ } - | Statement.ComponentDeclaration { Statement.ComponentDeclaration.id; _ } ) + | Statement.ComponentDeclaration { Statement.ComponentDeclaration.id; _ } + | Statement.RecordDeclaration { Statement.RecordDeclaration.id; _ } ) ) -> record_export env seen + export_kind (Flow_ast_utils.ident_of_source (loc, Flow_ast_utils.name_of_ident id)) | (_, Statement.VariableDeclaration { Statement.VariableDeclaration.declarations; _ }) -> declarations @@ -133,7 +159,7 @@ let check_for_duplicate_exports = (fun names (_, { Statement.VariableDeclaration.Declarator.id; _ }) -> extract_pattern_binding_names names [id]) [] - |> List.fold_left (record_export env) seen + |> List.fold_left (fun seen id -> record_export env seen export_kind id) seen | ( _, Statement.( ( Block _ | Break _ @@ -142,10 +168,11 @@ let check_for_duplicate_exports = | DeclareExportDeclaration _ | DeclareFunction _ | DeclareInterface _ | DeclareModule _ | DeclareModuleExports _ | DeclareNamespace _ | DeclareTypeAlias _ | DeclareOpaqueType _ | DeclareVariable _ | DoWhile _ | Empty _ | ExportDefaultDeclaration _ - | ExportNamedDeclaration _ | Expression _ | For _ | ForIn _ | ForOf _ + | ExportNamedDeclaration _ | ExportAssignment _ | NamespaceExportDeclaration _ + | Expression _ | For _ | ForIn _ | ForOf _ | FunctionDeclaration { Function.id = None; _ } - | If _ | ImportDeclaration _ | Labeled _ | Match _ | Return _ | Switch _ | Throw _ - | Try _ | While _ | With _ )) + | If _ | ImportDeclaration _ | ImportEqualsDeclaration _ | Labeled _ | Match _ + | Return _ | Switch _ | Throw _ | Try _ | While _ | With _ )) ) -> (* these don't export names -- some are invalid, but the AST allows them *) seen) @@ -165,14 +192,16 @@ let check_for_duplicate_exports = | DeclareComponent _ | DeclareEnum _ | DeclareExportDeclaration _ | DeclareFunction _ | DeclareInterface _ | DeclareModule _ | DeclareModuleExports _ | DeclareNamespace _ | DeclareTypeAlias _ | DeclareOpaqueType _ | DeclareVariable _ | DoWhile _ | Empty _ - | EnumDeclaration _ | Expression _ | For _ | ForIn _ | ForOf _ | FunctionDeclaration _ - | ComponentDeclaration _ | If _ | ImportDeclaration _ | InterfaceDeclaration _ | Labeled _ - | Match _ | Return _ | Switch _ | Throw _ | Try _ | TypeAlias _ | OpaqueType _ - | VariableDeclaration _ | While _ | With _ )) + | EnumDeclaration _ | ExportAssignment _ | NamespaceExportDeclaration _ | Expression _ + | For _ | ForIn _ | ForOf _ | FunctionDeclaration _ | ComponentDeclaration _ | If _ + | ImportDeclaration _ | ImportEqualsDeclaration _ | InterfaceDeclaration _ | Labeled _ + | Match _ | RecordDeclaration _ | Return _ | Switch _ | Throw _ | Try _ | TypeAlias _ + | OpaqueType _ | VariableDeclaration _ | While _ | With _ )) ) -> seen in - (fun env stmts -> ignore (List.fold_left (record_export_of_statement env) SSet.empty stmts)) + fun env stmts -> + ignore (List.fold_left (record_export_of_statement env) (SSet.empty, SSet.empty) stmts) module rec Parse : PARSER = struct module Type = Type_parser.Type (Parse) @@ -194,6 +223,13 @@ module rec Parse : PARSER = struct id let rec program env = + (* Set ambient context for .flow files *) + let env = + match source env with + | Some file_key when File_key.check_suffix file_key ".flow" || is_d_ts env -> + with_ambient_context true env + | _ -> env + in let interpreter = match Peek.token env with | T_INTERPRETER (loc, value) -> @@ -320,14 +356,31 @@ module rec Parse : PARSER = struct (* Remember kids, these look like statements but they're not * statements... (see section 13) *) | T_LET -> let_ env + | T_CONST when (parse_options env).enums && Peek.ith_token ~i:1 env = T_ENUM -> + Eat.token env; + Declaration.enum_declaration ~const_:true env | T_CONST -> const env | _ when Peek.is_function env || Peek.is_hook env -> Declaration._function env | _ when Peek.is_class env -> class_declaration env decorators | T_INTERFACE -> interface env + | T_IDENTIFIER { raw = "namespace"; _ } when in_ambient_context env -> + Statement.declare_namespace ~global:false ~implicit_declare:true env + | T_IDENTIFIER { raw = "module"; _ } + when in_ambient_context env + && is_d_ts env + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER _ -> true + | _ -> false -> + Statement.declare_namespace ~global:false ~implicit_declare:true env + | T_IDENTIFIER { raw = "global"; _ } + when in_ambient_context env && Peek.ith_token ~i:1 env = T_LCURLY -> + Statement.declare_namespace ~global:true ~implicit_declare:true env | T_DECLARE -> declare env | T_TYPE -> type_alias env | T_OPAQUE -> opaque_type env - | T_ENUM when (parse_options env).enums -> Declaration.enum_declaration env + | T_ENUM when (parse_options env).enums -> Declaration.enum_declaration ~const_:false env + | T_RECORD when Peek.is_record env -> Object.record_declaration env | _ when Peek.is_component env -> Declaration.component env | _ -> statement env @@ -447,18 +500,29 @@ module rec Parse : PARSER = struct and bigint = Expression.bigint and identifier_with_type = - let with_loc_helper no_optional restricted_error env = + let with_loc_helper allow_optional restricted_error env = let name = identifier ~restricted_error env in - let optional = (not no_optional) && Peek.token env = T_PLING in - if optional then ( - if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation; - Expect.token env T_PLING - ); + let optional = + match Peek.token env with + | T_PLING -> + let optional = + if allow_optional then ( + if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation; + true + ) else ( + error env Parse_error.UnexpectedOptional; + false + ) + in + Eat.token env; + optional + | _ -> false + in let annot = Type.annotation_opt env in Ast.Pattern.Identifier.{ name; optional; annot } in - fun env ?(no_optional = false) restricted_error -> - with_loc (with_loc_helper no_optional restricted_error) env + fun env ~allow_optional restricted_error -> + with_loc (with_loc_helper allow_optional restricted_error) env and block_body env = let start_loc = Peek.loc env in diff --git a/compiler/flow_parser/parser/pattern_parser.ml b/compiler/flow_parser/parser/pattern_parser.ml index f3a790928df..d7e617dea86 100644 --- a/compiler/flow_parser/parser/pattern_parser.ml +++ b/compiler/flow_parser/parser/pattern_parser.ml @@ -72,7 +72,12 @@ struct ( loc, Pattern.( Object - { Object.properties = properties env [] props; annot = missing_annot env; comments } + { + Object.properties = properties env [] props; + annot = missing_annot env; + optional = false; + comments; + } ) ) @@ -131,10 +136,15 @@ struct elements env acc remaining | Array.Hole loc :: remaining -> elements env (Pattern.Array.Hole loc :: acc) remaining in - fun env (loc, { Ast.Expression.Array.elements = elems; comments }) -> + fun env (loc, { Ast.Expression.Array.elements = elems; trailing_comma = _; comments }) -> ( loc, Pattern.Array - { Pattern.Array.elements = elements env [] elems; annot = missing_annot env; comments } + { + Pattern.Array.elements = elements env [] elems; + annot = missing_annot env; + optional = false; + comments; + } ) and from_expr env (loc, expr) = @@ -166,14 +176,14 @@ struct | expr -> (loc, Pattern.Expression (loc, expr)) (* Parse object destructuring pattern *) - let rec object_ restricted_error = + let rec object_ ~allow_optional restricted_error = let rest_property env = let leading = Peek.comments env in let (loc, argument) = with_loc (fun env -> Expect.token env T_ELLIPSIS; - pattern env restricted_error) + pattern env ~allow_optional:false restricted_error) env in Pattern.Object.RestElement @@ -201,7 +211,7 @@ struct with_loc ~start_loc (fun env -> - let pattern = pattern env restricted_error in + let pattern = pattern env ~allow_optional:false restricted_error in let default = property_default env in (pattern, default)) env @@ -230,6 +240,10 @@ struct else if is_strict_reserved string_val then (* it is a syntax error if `name` is a strict reserved word, in strict mode *) strict_error_at env (id_loc, Parse_error.StrictReservedWord); + if Peek.token env = T_PLING then ( + error env Parse_error.UnexpectedOptional; + Eat.token env + ); let (loc, (pattern, default)) = with_loc ~start_loc @@ -302,6 +316,7 @@ struct let internal = Peek.comments env in Expect.token env T_RCURLY; let trailing = Eat.trailing_comments env in + let optional = parse_optional ~allow_optional env in let annot = if Peek.token env = T_COLON then Ast.Type.Available (Type.annotation env) @@ -312,12 +327,29 @@ struct { Pattern.Object.properties; annot; + optional; comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal (); } ) + and parse_optional ~allow_optional env = + match Peek.token env with + | T_PLING -> + if allow_optional then ( + if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation; + if not (in_ambient_context env) then + error env Parse_error.OptionalDestructuringMustHaveDefault; + Eat.token env; + true + ) else ( + error env Parse_error.UnexpectedOptional; + Eat.token env; + false + ) + | _ -> false + (* Parse array destructuring pattern *) - and array_ restricted_error = + and array_ ~allow_optional restricted_error = let rec elements env acc = match Peek.token env with | T_EOF @@ -333,7 +365,7 @@ struct with_loc (fun env -> Expect.token env T_ELLIPSIS; - pattern env restricted_error) + pattern env ~allow_optional:false restricted_error) env in let element = @@ -357,7 +389,7 @@ struct let (loc, (pattern, default)) = with_loc (fun env -> - let pattern = pattern env restricted_error in + let pattern = pattern env ~allow_optional:false restricted_error in let default = match Peek.token env with | T_ASSIGN -> @@ -378,6 +410,7 @@ struct let elements = elements env [] in let internal = Peek.comments env in Expect.token env T_RBRACKET; + let optional = parse_optional ~allow_optional env in let annot = if Peek.token env = T_COLON then Ast.Type.Available (Type.annotation env) @@ -388,14 +421,14 @@ struct let comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal () in - Pattern.Array { Pattern.Array.elements; annot; comments } + Pattern.Array { Pattern.Array.elements; annot; optional; comments } ) - and pattern env restricted_error = + and pattern env ~allow_optional restricted_error = match Peek.token env with - | T_LCURLY -> object_ restricted_error env - | T_LBRACKET -> array_ restricted_error env + | T_LCURLY -> object_ ~allow_optional restricted_error env + | T_LBRACKET -> array_ ~allow_optional restricted_error env | _ -> - let (loc, id) = Parse.identifier_with_type env restricted_error in + let (loc, id) = Parse.identifier_with_type env ~allow_optional restricted_error in (loc, Pattern.Identifier id) end diff --git a/compiler/flow_parser/parser/statement_parser.ml b/compiler/flow_parser/parser/statement_parser.ml index 2ab0ef51233..058f4818825 100644 --- a/compiler/flow_parser/parser/statement_parser.ml +++ b/compiler/flow_parser/parser/statement_parser.ml @@ -150,7 +150,7 @@ module Statement env in if label = None && not (in_loop env || in_switch env) then - error_at env (loc, Parse_error.IllegalBreak); + error_at env (loc, Parse_error.IllegalBreak { in_match_statement = in_match_statement env }); let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in (loc, Statement.Break { Statement.Break.label; comments }) @@ -488,7 +488,8 @@ module Statement and return = with_loc (fun env -> - if not (in_function env) then error env Parse_error.IllegalReturn; + if not (in_function env || (parse_options env).allow_return_outside_function) then + error env Parse_error.IllegalReturn; let leading = Peek.comments env in let start_loc = Peek.loc env in Expect.token env T_RETURN; @@ -524,15 +525,16 @@ module Statement and switch = let case ~seen_default env = let leading = Peek.comments env in - let (test, trailing) = + let (test, trailing, case_loc_opt) = match Peek.token env with | T_DEFAULT -> if seen_default then error env Parse_error.MultipleDefaultsInSwitch; Expect.token env T_DEFAULT; - (None, Eat.trailing_comments env) + (None, Eat.trailing_comments env, None) | _ -> + let case_loc = Peek.loc env in Expect.token env T_CASE; - (Some (Parse.expression env), []) + (Some (Parse.expression env), [], Some case_loc) in let seen_default = seen_default || test = None in Expect.token env T_COLON; @@ -547,7 +549,16 @@ module Statement in let consequent = Parse.statement_list ~term_fn (env |> with_in_switch true) in let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - let case = { Statement.Switch.Case.test; consequent; comments } in + let case_test_loc = + match (case_loc_opt, test) with + | (_, None) + | (None, _) -> + None + | (Some case_loc, Some (expr_loc, _)) -> Some (Loc.btwn case_loc expr_loc) + in + let case = + { Statement.Switch.Case.test; Statement.Switch.Case.case_test_loc; consequent; comments } + in (case, seen_default) in let rec case_list env (seen_default, acc) = @@ -583,6 +594,15 @@ module Statement let open Match in let case env = let leading = Peek.comments env in + let case_match_root_loc = Peek.loc env |> Loc.start_loc in + let invalid_prefix_case = + if Peek.token env = T_CASE then ( + let loc = Peek.loc env in + Eat.token env; + Some loc + ) else + None + in let pattern = Parse.match_pattern env in let guard = if Eat.maybe env T_IF then ( @@ -593,17 +613,28 @@ module Statement ) else None in - (* Continue parsing colon until hermes-parser is also updated. *) - if not @@ Eat.maybe env T_COLON then Expect.token env T_ARROW; - let body = Parse.statement ~allow_sequence:false env in - (match Peek.token env with - | T_EOF - | T_RCURLY -> - () - | _ -> ignore @@ Eat.maybe env T_COMMA); + let invalid_infix_colon = + if Peek.token env = T_COLON then ( + let loc = Peek.loc env in + Eat.token env; + Some loc + ) else ( + Expect.token env T_ARROW; + None + ) + in + let body = Parse.statement ~allow_sequence:false (env |> with_in_match_statement true) in + ignore @@ Eat.maybe env T_COMMA; let trailing = Eat.trailing_comments env in let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - { Case.pattern; body; guard; comments } + let invalid_syntax = + { + Case.InvalidSyntax.invalid_prefix_case; + invalid_infix_colon; + invalid_suffix_semicolon = None; + } + in + { Case.pattern; body; guard; comments; invalid_syntax; case_match_root_loc } in let rec case_list env acc = match Peek.token env with @@ -673,7 +704,10 @@ module Statement let param = if Peek.token env = T_LPAREN then ( Expect.token env T_LPAREN; - let p = Some (Parse.pattern env Parse_error.StrictCatchVariable) in + let p = + Some + (Parse.pattern env ~allow_optional:false Parse_error.StrictCatchVariable) + in Expect.token env T_RPAREN; p ) else @@ -949,14 +983,46 @@ module Statement id in let tparams = Type.type_params env in - let supertype = - match Peek.token env with - | T_COLON -> - Expect.token env T_COLON; + let expect_opt_super_or_extends token = + (* super and extends are treated like identifiers in type parsing context. + * We need to switch back to normal mode in order to correctly identify them. *) + Eat.push_lex_mode env Lex_mode.NORMAL; + let consumed = + if Peek.token env = token then ( + Expect.token env token; + true + ) else + false + in + Eat.pop_lex_mode env; + consumed + in + let lower_bound = + if expect_opt_super_or_extends T_SUPER then + (* Do not accept conditional type in this position. Otherwise, the sequence + * `opaque type A super B extends C = D` will fail because `B extends C` are parsed + * together as conditional type. *) + Some (Type.union env) + else + None + in + let upper_bound = + if expect_opt_super_or_extends T_EXTENDS then Some (Type._type env) - | _ -> None + else + None in - let impltype = + let legacy_upper_bound = + if Option.is_some upper_bound || Option.is_some lower_bound then + None + else + match Peek.token env with + | T_COLON -> + Expect.token env T_COLON; + Some (Type._type env) + | _ -> None + in + let impl_type = if declare then match Peek.token env with | T_ASSIGN -> @@ -973,28 +1039,52 @@ module Statement ) in Eat.pop_lex_mode env; - let (trailing, id, tparams, supertype, impltype) = - match (semicolon env, tparams, supertype, impltype) with + let (trailing, id, tparams, lower_bound, upper_bound, legacy_upper_bound, impl_type) = + match (semicolon env, tparams, lower_bound, upper_bound, legacy_upper_bound, impl_type) with (* opaque type Foo = Bar; *) - | (Explicit comments, _, _, _) -> (comments, id, tparams, supertype, impltype) + | (Explicit comments, _, _, _, _, _) -> + (comments, id, tparams, lower_bound, upper_bound, legacy_upper_bound, impl_type) (* opaque type Foo = Bar *) - | (Implicit { remove_trailing; _ }, _, _, Some impl) -> + | (Implicit { remove_trailing; _ }, _, _, _, _, Some impl) -> ( [], id, tparams, - supertype, + lower_bound, + upper_bound, + legacy_upper_bound, Some (remove_trailing impl (fun remover impl -> remover#type_ impl)) ) - (* opaque type Foo: Super *) - | (Implicit { remove_trailing; _ }, _, Some super, None) -> + (* opaque type Foo: U *) + | (Implicit { remove_trailing; _ }, _, _, Some upper_bound, _, None) -> + ( [], + id, + tparams, + lower_bound, + Some (remove_trailing upper_bound (fun remover t -> remover#type_ t)), + legacy_upper_bound, + None + ) + | (Implicit { remove_trailing; _ }, _, _, _, Some legacy_upper_bound, None) -> + ( [], + id, + tparams, + lower_bound, + upper_bound, + Some (remove_trailing legacy_upper_bound (fun remover t -> remover#type_ t)), + None + ) + (* opaque type Foo super Super *) + | (Implicit { remove_trailing; _ }, _, Some lower_bound, None, None, None) -> ( [], id, tparams, - Some (remove_trailing super (fun remover super -> remover#type_ super)), + Some (remove_trailing lower_bound (fun remover t -> remover#type_ t)), + upper_bound, + legacy_upper_bound, None ) (* opaque type Foo *) - | (Implicit { remove_trailing; _ }, Some tparams, None, None) -> + | (Implicit { remove_trailing; _ }, Some tparams, None, None, None, None) -> ( [], id, Some @@ -1003,18 +1093,29 @@ module Statement ) ), None, + None, + None, None ) (* declare opaque type Foo *) - | (Implicit { remove_trailing; _ }, None, None, None) -> - ([], remove_trailing id (fun remover id -> remover#identifier id), None, None, None) + | (Implicit { remove_trailing; _ }, None, None, None, None, None) -> + ( [], + remove_trailing id (fun remover id -> remover#identifier id), + None, + None, + None, + None, + None + ) in { Statement.OpaqueType.id; tparams; - impltype; - supertype; + impl_type; + lower_bound; + upper_bound; + legacy_upper_bound; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); } @@ -1054,9 +1155,7 @@ module Statement in let (extends, body) = Type.interface_helper env in let { remove_trailing; _ } = statement_end_trailing_comments env in - let body = - remove_trailing body (fun remover (loc, body) -> (loc, remover#object_type loc body)) - in + let body = remove_trailing body (fun remover (loc, body) -> (loc, remover#object_type body)) in { Statement.Interface.id; @@ -1095,7 +1194,18 @@ module Statement | _ -> List.rev acc (* This is identical to `interface`, except that mixins are allowed *) in - fun ~leading env -> + let rec parse_extends env = + let open Statement.DeclareClass in + let generic = Type.generic env in + match Peek.token env with + | T_LPAREN -> + Expect.token env T_LPAREN; + let arg = with_loc parse_extends env in + Expect.token env T_RPAREN; + ExtendsCall { callee = generic; arg } + | _ -> ExtendsIdent (snd generic) + in + fun ~abstract ~leading env -> let env = env |> with_strict true in let leading = leading @ Peek.comments env in Expect.token env T_CLASS; @@ -1115,10 +1225,7 @@ module Statement in let extends = if Eat.maybe env T_EXTENDS then - let extends = Type.generic env in - match Peek.token env with - | T_LCURLY -> Some (generic_type_remove_trailing env extends) - | _ -> Some extends + Some (with_loc parse_extends env) else None in @@ -1144,17 +1251,18 @@ module Statement let body = Type._object ~is_class:true env in let { remove_trailing; _ } = statement_end_trailing_comments env in let body = - remove_trailing body (fun remover (loc, body) -> (loc, remover#object_type loc body)) + remove_trailing body (fun remover (loc, body) -> (loc, remover#object_type body)) in let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareClass.{ id; tparams; body; extends; mixins; implements; comments } + Statement.DeclareClass.{ id; tparams; body; extends; mixins; implements; abstract; comments } - and declare_class_statement env = + and declare_class_statement ~abstract env = with_loc (fun env -> let leading = Peek.comments env in Expect.token env T_DECLARE; - let fn = declare_class ~leading env in + if abstract then Expect.identifier env "abstract"; + let fn = declare_class ~abstract ~leading env in Statement.DeclareClass fn) env @@ -1170,7 +1278,7 @@ module Statement let tparams = type_params_remove_trailing env ~kind:Flow_ast_mapper.DeclareComponentTP (Type.type_params env) in - let params = Type.component_param_list env in + let params = Declaration.component_params env in let (params, renders) = if Peek.is_renders_ident env then let renders = Type.renders_annotation_opt env in @@ -1206,23 +1314,82 @@ module Statement Statement.DeclareComponent component) env - and declare_enum env = + and declare_async_statement env = + with_loc + (fun env -> + let leading = Peek.comments env in + Expect.token env T_DECLARE; + (* Now T_ASYNC is at position 0 and we can peek at position 1 *) + let is_component = + (parse_options env).components + && (not (Peek.ith_is_line_terminator ~i:1 env)) + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "component"; _ } -> true + | _ -> false + in + let is_hook = + (parse_options env).components + && (not (Peek.ith_is_line_terminator ~i:1 env)) + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "hook"; _ } -> true + | _ -> false + in + if is_component then begin + error env Parse_error.DeclareAsyncComponent; + let leading_async = Peek.comments env in + Eat.token env; + let leading = leading @ leading_async in + let component = declare_component ~leading env in + Statement.DeclareComponent component + end else if is_hook then begin + error env Parse_error.DeclareAsyncHook; + Expect.token env T_ASYNC; + let fn = declare_function ~async:false ~leading env in + Statement.DeclareFunction fn + end else begin + error env Parse_error.DeclareAsync; + Expect.token env T_ASYNC; + let fn = declare_function ~async:true ~leading env in + Statement.DeclareFunction fn + end) + env + + and declare_enum ~const_ env = with_loc (fun env -> let leading = Peek.comments env in Expect.token env T_DECLARE; - let enum = Enum.declaration ~leading env in + if const_ then Expect.token env T_CONST; + let enum = Enum.declaration ~const_ ~leading env in Statement.DeclareEnum enum) env - and declare_function ~async ?(leading = []) env = + (* Handles declare const - could be `declare const enum` or `declare const var` *) + and declare_const_or_enum env = + with_loc + (fun env -> + let leading = Peek.comments env in + Expect.token env T_DECLARE; + if (parse_options env).enums && Peek.ith_token ~i:1 env = T_ENUM then begin + Expect.token env T_CONST; + let enum = Enum.declaration ~const_:true ~leading env in + Statement.DeclareEnum enum + end else begin + let var = declare_var ~kind:Ast.Variable.Const env leading in + Statement.DeclareVariable var + end) + env + + and declare_function ~async:_ ?(leading = []) env = let leading = leading @ Peek.comments env in let effect_ = match Peek.token env with | T_FUNCTION -> Eat.token env; Function.Arbitrary - | T_IDENTIFIER { raw = "hook"; _ } when not async -> + | T_IDENTIFIER { raw = "hook"; _ } -> Eat.token env; Function.Hook | t -> @@ -1249,9 +1416,9 @@ module Statement let return = Type._type env in let has_predicate = Peek.token env = T_CHECKS in if has_predicate && effect_ <> Function.Hook then - Ast.Type.Function.TypeAnnotation (type_remove_trailing env return) + Ast.Type.Function.Available (type_remove_trailing env return) else - Ast.Type.Function.TypeAnnotation return + Ast.Type.Function.Available return in Eat.pop_lex_mode env; Ast.Type.(Function { Function.params; return; tparams; comments = None; effect_ })) @@ -1269,10 +1436,11 @@ module Statement let annot = (fst annot, annot) in { - Statement.DeclareFunction.id; + Statement.DeclareFunction.id = Some id; annot; predicate; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + implicit_declare = false; } and declare_function_statement env = @@ -1298,20 +1466,11 @@ module Statement | Ast.Variable.Var -> Expect.token env T_VAR | Ast.Variable.Let -> Expect.token env T_LET | Ast.Variable.Const -> Expect.token env T_CONST); - let name = Parse.identifier ~restricted_error:Parse_error.StrictVarName env in - let annot = Type.annotation env in - let (trailing, name, annot) = - match semicolon env with - (* declare var x; *) - | Explicit trailing -> (trailing, name, annot) - (* declare var x *) - | Implicit { remove_trailing; _ } -> - ([], name, remove_trailing annot (fun remover annot -> remover#type_annotation annot)) - in - + let (declarations, errs) = Declaration.variable_declaration_list env in + List.iter (error_at env) errs; + let (trailing, declarations) = variable_declaration_end ~kind env declarations in { - Statement.DeclareVariable.id = name; - annot; + Statement.DeclareVariable.declarations; kind; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); } @@ -1330,6 +1489,7 @@ module Statement (fun env -> let leading = Peek.comments env in Expect.token env T_LCURLY; + let env = with_ambient_context true env in let body = Parse.module_body ~term_fn:(function @@ -1360,9 +1520,26 @@ module Statement (string_literal_remove_trailing env (string_literal env str)) | _ -> Statement.DeclareModule.Identifier (id_remove_trailing env (Parse.identifier env)) in - let body = declare_module_or_namespace_body env in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.(DeclareModule DeclareModule.{ id; body; comments }) + if Peek.token env <> T_LCURLY && Peek.is_implicit_semicolon env then begin + (* Shorthand ambient module: `declare module 'name'` — desugar to empty body. *) + let trailing = + match semicolon env with + | Explicit trailing -> trailing + | Implicit { trailing; _ } -> trailing + in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + let body_loc = + match last_loc env with + | Some loc -> Loc.end_loc loc + | None -> Peek.loc env |> Loc.start_loc + in + let body = (body_loc, { Statement.Block.body = []; comments = None }) in + Statement.(DeclareModule DeclareModule.{ id; body; comments }) + end else begin + let body = declare_module_or_namespace_body env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.(DeclareModule DeclareModule.{ id; body; comments }) + end in fun env -> let start_loc = Peek.loc env in @@ -1373,10 +1550,27 @@ module Statement if Peek.token env = T_PERIOD then with_loc ~start_loc (declare_module_exports ~leading) env else - with_loc ~start_loc (declare_module_ ~leading) env + match Peek.token env with + | T_STRING _ -> with_loc ~start_loc (declare_module_ ~leading) env + | _ when is_d_ts env -> + (* identifier-named: `declare module Foo {}` → treat as namespace synonym in .d.ts *) + let id = id_remove_trailing env (Parse.identifier env) in + let id = Statement.DeclareNamespace.Local id in + let body = declare_module_or_namespace_body env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + with_loc + ~start_loc + (fun _env -> + Statement.( + DeclareNamespace + DeclareNamespace. + { id; body; comments; implicit_declare = false; keyword = Module } + )) + env + | _ -> with_loc ~start_loc (declare_module_ ~leading) env and declare_namespace = - let declare_namespace_ ~leading ~global env = + let declare_namespace_ ~leading ~global ~implicit_declare ~keyword env = let id = id_remove_trailing env (Parse.identifier env) in let id = if global then @@ -1386,15 +1580,65 @@ module Statement in let body = declare_module_or_namespace_body env in let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.(DeclareNamespace DeclareNamespace.{ id; body; comments }) + Statement.(DeclareNamespace DeclareNamespace.{ id; body; comments; implicit_declare; keyword }) in - fun env ~global -> + fun env ~global ~implicit_declare -> let start_loc = Peek.loc env in let leading = Peek.comments env in - Expect.token env T_DECLARE; - let leading = leading @ Peek.comments env in - if not global then Expect.identifier env "namespace"; - with_loc ~start_loc (declare_namespace_ ~global ~leading) env + if not implicit_declare then begin + Expect.token env T_DECLARE; + let leading = leading @ Peek.comments env in + if not global then begin + let keyword = + match Peek.token env with + | T_IDENTIFIER { raw = "module"; _ } when is_d_ts env -> + Expect.identifier env "module"; + Statement.DeclareNamespace.Module + | _ -> + Expect.identifier env "namespace"; + Statement.DeclareNamespace.Namespace + in + with_loc ~start_loc (declare_namespace_ ~global ~implicit_declare ~keyword ~leading) env + end else + with_loc + ~start_loc + (declare_namespace_ + ~global + ~implicit_declare + ~keyword:Statement.DeclareNamespace.Namespace + ~leading + ) + env + end else begin + (* implicit declare: namespace/module keyword already peeked, just consume it *) + if not global then begin + let keyword = + match Peek.token env with + | T_IDENTIFIER { raw = "module"; _ } when is_d_ts env -> + Expect.identifier env "module"; + Statement.DeclareNamespace.Module + | _ -> + Expect.identifier env "namespace"; + Statement.DeclareNamespace.Namespace + in + let leading = leading @ Peek.comments env in + with_loc + ~start_loc + (declare_namespace_ ~global:false ~implicit_declare ~keyword ~leading) + env + end else begin + let leading = leading @ Peek.comments env in + with_loc + ~start_loc + (declare_namespace_ + ~global + ~implicit_declare + ~keyword:Statement.DeclareNamespace.Namespace + ~leading + ) + env + end + end and declare_module_exports ~leading env = let leading_period = Peek.comments env in @@ -1418,8 +1662,10 @@ module Statement (* eventually, just emit a wrapper AST node *) match Peek.ith_token ~i:1 env with - | T_CLASS -> declare_class_statement env - | T_ENUM when (parse_options env).enums -> declare_enum env + | T_CLASS -> declare_class_statement ~abstract:false env + | T_IDENTIFIER { raw = "abstract"; _ } -> declare_class_statement ~abstract:true env + | T_CONST -> declare_const_or_enum env + | T_ENUM when (parse_options env).enums -> declare_enum ~const_:false env | T_INTERFACE -> declare_interface env | T_TYPE -> (match Peek.token env with @@ -1427,18 +1673,18 @@ module Statement | _ -> declare_type_alias env) | T_OPAQUE -> declare_opaque_type env | T_TYPEOF when Peek.token env = T_IMPORT -> import_declaration env - | T_FUNCTION - | T_ASYNC -> - declare_function_statement env + | T_FUNCTION -> declare_function_statement env + | T_ASYNC -> declare_async_statement env | T_IDENTIFIER { raw = "hook"; _ } when (parse_options env).components -> declare_function_statement env | T_VAR -> declare_var_statement ~kind:Ast.Variable.Var env | T_LET -> declare_var_statement ~kind:Ast.Variable.Let env - | T_CONST -> declare_var_statement ~kind:Ast.Variable.Const env | T_EXPORT when in_module_or_namespace -> declare_export_declaration env | T_IDENTIFIER { raw = "module"; _ } -> declare_module env - | T_IDENTIFIER { raw = "global"; _ } -> declare_namespace ~global:true env - | T_IDENTIFIER { raw = "namespace"; _ } -> declare_namespace ~global:false env + | T_IDENTIFIER { raw = "global"; _ } -> + declare_namespace ~global:true ~implicit_declare:false env + | T_IDENTIFIER { raw = "namespace"; _ } -> + declare_namespace ~global:false ~implicit_declare:false env | T_IDENTIFIER { raw = "component"; _ } when (parse_options env).components -> declare_component_statement env | _ when in_module_or_namespace -> @@ -1465,9 +1711,7 @@ module Statement match semicolon env with | Explicit trailing -> ((source_loc, source), trailing) | Implicit { remove_trailing; _ } -> - ( ( source_loc, - remove_trailing source (fun remover source -> remover#string_literal source_loc source) - ), + ( (source_loc, remove_trailing source (fun remover source -> remover#string_literal source)), [] ) @@ -1481,20 +1725,96 @@ module Statement let specifier = with_loc (fun env -> - let local = identifier_name env in - let exported = + if Peek.token env = T_TYPE then + (* consume `type`, but we don't know yet whether this is `type foo` or + `type as foo` (a value named "type"). *) + let type_keyword_or_local = identifier_name env in match Peek.token env with - | T_IDENTIFIER { raw = "as"; _ } -> - Eat.token env; - Some (identifier_name env) - | _ -> None - in - { - Statement.ExportNamedDeclaration.ExportSpecifier.local; - exported; - from_remote = false; - imported_name_def_loc = None; - }) + (* `type` (a value named "type") *) + | T_EOF + | T_RCURLY + | T_COMMA -> + let local = type_keyword_or_local in + { + Statement.ExportNamedDeclaration.ExportSpecifier.local; + exported = None; + export_kind = Statement.ExportValue; + from_remote = false; + imported_name_def_loc = None; + } + (* `type as ...` - ambiguous *) + | T_IDENTIFIER { raw = "as"; _ } -> begin + match Peek.ith_token ~i:1 env with + (* `type as` - type export of "as" *) + | T_EOF + | T_RCURLY + | T_COMMA -> + let local = identifier_name env in + { + Statement.ExportNamedDeclaration.ExportSpecifier.local; + exported = None; + export_kind = Statement.ExportType; + from_remote = false; + imported_name_def_loc = None; + } + (* `type as as foo` - type export of "as" renamed to "foo" *) + | T_IDENTIFIER { raw = "as"; _ } -> + let local = identifier_name env in + Eat.token env; + let exported = Some (identifier_name env) in + { + Statement.ExportNamedDeclaration.ExportSpecifier.local; + exported; + export_kind = Statement.ExportType; + from_remote = false; + imported_name_def_loc = None; + } + | _ -> + (* `type as foo` - value named "type" exported as "foo" *) + let local = type_keyword_or_local in + Eat.token env; + let exported = Some (identifier_name env) in + { + Statement.ExportNamedDeclaration.ExportSpecifier.local; + exported; + export_kind = Statement.ExportValue; + from_remote = false; + imported_name_def_loc = None; + } + end + (* `type X` or `type X as Y` - type export of X *) + | _ -> + let local = identifier_name env in + let exported = + match Peek.token env with + | T_IDENTIFIER { raw = "as"; _ } -> + Eat.token env; + Some (identifier_name env) + | _ -> None + in + { + Statement.ExportNamedDeclaration.ExportSpecifier.local; + exported; + export_kind = Statement.ExportType; + from_remote = false; + imported_name_def_loc = None; + } + else + let local = identifier_name env in + let exported = + match Peek.token env with + | T_IDENTIFIER { raw = "as"; _ } -> + Eat.token env; + Some (identifier_name env) + | _ -> None + in + { + Statement.ExportNamedDeclaration.ExportSpecifier.local; + exported; + export_kind = Statement.ExportValue; + from_remote = false; + imported_name_def_loc = None; + }) env in let preceding_comma = Eat.maybe env T_COMMA in @@ -1507,19 +1827,155 @@ module Statement { Statement.ExportNamedDeclaration.ExportSpecifier.local = id; exported = _; + export_kind; from_remote = _; imported_name_def_loc = _; } ) -> - assert_identifier_name_is_identifier ~restricted_error:Parse_error.StrictVarName env id) + (match export_kind with + | Statement.ExportValue -> + assert_identifier_name_is_identifier ~restricted_error:Parse_error.StrictVarName env id + | Statement.ExportType -> ())) specifiers + (* Parse the module_reference part of an ImportEqualsDeclaration: + either `require("module")` or a qualified name like `A.B.C` *) + and import_equals_module_reference env = + match Peek.token env with + | T_IDENTIFIER { raw = "require"; _ } when Peek.ith_token ~i:1 env = T_LPAREN -> + (* require("module") *) + Eat.token env; + (* consume 'require' *) + Expect.token env T_LPAREN; + let source = + match Peek.token env with + | T_STRING str -> string_literal env str + | _ -> + error_unexpected ~expected:"a string" env; + let loc = Peek.loc_skip_lookahead env in + (loc, { StringLiteral.value = ""; raw = ""; comments = None }) + in + Expect.token env T_RPAREN; + Statement.ImportEqualsDeclaration.ExternalModuleReference source + | _ -> + (* Qualified name like A.B.C *) + let (_loc, { Ast.Type.Generic.id; targs = _; comments = _ }) = Type.generic env in + Statement.ImportEqualsDeclaration.Identifier id + + (* Parse an ImportEqualsDeclaration: `import [type] Foo = require("module")` or `import [type] Foo = A.B.C` *) + and import_equals_declaration ~import_kind ~is_export ~leading env = + let id = Parse.identifier env in + Expect.token env T_ASSIGN; + let module_reference = import_equals_module_reference env in + let trailing = + match semicolon env with + | Explicit trailing -> trailing + | Implicit { trailing; _ } -> trailing + in + Statement.ImportEqualsDeclaration + { + Statement.ImportEqualsDeclaration.id; + module_reference; + import_kind; + is_export; + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + } + and export_declaration ~decorators env = let env = env |> with_strict true |> with_in_export true in let leading = Peek.comments env in let start_loc = Peek.loc env in Expect.token env T_EXPORT; match Peek.token env with + | T_ASSIGN -> + (* export = expr; *) + with_loc + ~start_loc + (fun env -> + Eat.token env; + if in_ambient_context env && (Peek.is_function env || Peek.is_hook env) then + match Declaration._function env with + | (fn_loc, Statement.DeclareFunction decl_func) -> + Statement.ExportAssignment + { + Statement.ExportAssignment.rhs = + Statement.ExportAssignment.DeclareFunction (fn_loc, decl_func); + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + | (fn_loc, Statement.FunctionDeclaration fn) -> + let trailing = + match semicolon env with + | Explicit trailing -> trailing + | Implicit { trailing; _ } -> trailing + in + Statement.ExportAssignment + { + Statement.ExportAssignment.rhs = + Statement.ExportAssignment.Expression (fn_loc, Ast.Expression.Function fn); + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + } + | _ -> + failwith "Declaration._function must return DeclareFunction or FunctionDeclaration" + else + let expression = Parse.assignment env in + let trailing = + match semicolon env with + | Explicit trailing -> trailing + | Implicit { trailing; _ } -> trailing + in + Statement.ExportAssignment + { + Statement.ExportAssignment.rhs = Statement.ExportAssignment.Expression expression; + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + }) + env + | T_IDENTIFIER { raw = "as"; _ } + when match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "namespace"; _ } -> true + | _ -> false -> + (* export as namespace Foo; *) + with_loc + ~start_loc + (fun env -> + Expect.identifier env "as"; + Expect.identifier env "namespace"; + let id = Parse.identifier env in + let trailing = + match semicolon env with + | Explicit trailing -> trailing + | Implicit { trailing; _ } -> trailing + in + Statement.NamespaceExportDeclaration + { + Statement.NamespaceExportDeclaration.id; + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + }) + env + | T_IMPORT when Peek.ith_is_identifier ~i:1 env -> + (* export import [type] Foo = ...; *) + with_loc + ~start_loc + (fun env -> + let leading = leading @ Peek.comments env in + Eat.token env; + let import_kind = + match Peek.token env with + | T_TYPE -> begin + match Peek.ith_token ~i:1 env with + (* `export import type, ...` or `export import type from ...` or + `export import type = ...` means 'type' is the binding name, not a keyword *) + | T_COMMA + | T_ASSIGN + | T_IDENTIFIER { raw = "from"; _ } -> + Statement.ImportDeclaration.ImportValue + | _ -> + Eat.token env; + Statement.ImportDeclaration.ImportType + end + | _ -> Statement.ImportDeclaration.ImportValue + in + import_equals_declaration ~import_kind ~is_export:true ~leading env) + env | T_DEFAULT -> (* export default ... *) with_loc @@ -1529,38 +1985,90 @@ module Statement let leading = leading @ Peek.comments env in let (default, ()) = with_loc (fun env -> Expect.token env T_DEFAULT) env in let env = with_in_export_default true env in - let (declaration, trailing) = - if Peek.is_function env || Peek.is_hook env then - (* export default [async] function [foo] (...) { ... } *) - let fn = Declaration._function env in - (Declaration fn, []) - else if Peek.is_class env then - (* export default class foo { ... } *) - let _class = Object.class_declaration env decorators in - (Declaration _class, []) - else if Peek.token env = T_ENUM then - (* export default enum foo { ... } *) - (Declaration (Declaration.enum_declaration env), []) - else if Peek.is_component env then - (* export default component foo { ... } *) - (Declaration (Declaration.component env), []) - else - (* export default [assignment expression]; *) - let expr = Parse.assignment env in - let (expr, trailing) = - match semicolon env with - | Explicit trailing -> (expr, trailing) - | Implicit { remove_trailing; _ } -> - (remove_trailing expr (fun remover expr -> remover#expression expr), []) + if Peek.is_function env || Peek.is_hook env then + (* export default [async] function [foo] (...) { ... } *) + match Declaration._function env with + | (fn_loc, Statement.DeclareFunction decl_func) -> + Statement.DeclareExportDeclaration + { + Statement.DeclareExportDeclaration.default = Some default; + declaration = + Some (Statement.DeclareExportDeclaration.Function (fn_loc, decl_func)); + specifiers = None; + source = None; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + | fn -> + Statement.ExportDefaultDeclaration + { + default; + declaration = Declaration fn; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + else if Peek.is_component env then + (* export default component foo { ... } *) + match Declaration.component env with + | ( comp_loc, + Statement.ComponentDeclaration + { + Statement.ComponentDeclaration.body = None; + id; + tparams; + params; + renders; + async = _; + comments; + sig_loc = _; + } + ) -> + let decl_comp = + { Statement.DeclareComponent.id; tparams; params; renders; comments } in - (Expression expr, trailing) - in - Statement.ExportDefaultDeclaration - { - default; - declaration; - comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); - }) + Statement.DeclareExportDeclaration + { + Statement.DeclareExportDeclaration.default = Some default; + declaration = + Some (Statement.DeclareExportDeclaration.Component (comp_loc, decl_comp)); + specifiers = None; + source = None; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + | comp -> + Statement.ExportDefaultDeclaration + { + default; + declaration = Declaration comp; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + else + let (declaration, trailing) = + if Peek.is_class env then + (* export default class foo { ... } *) + let _class = Object.class_declaration env decorators in + (Declaration _class, []) + else if Peek.token env = T_ENUM then + (* export default enum foo { ... } *) + (Declaration (Declaration.enum_declaration ~const_:false env), []) + else if Peek.is_record env then + (* export default record R { ... } *) + (Declaration (Object.record_declaration env), []) + else + (* export default [assignment expression]; *) + let expr = Parse.assignment env in + let (expr, trailing) = + match semicolon env with + | Explicit trailing -> (expr, trailing) + | Implicit { remove_trailing; _ } -> + (remove_trailing expr (fun remover expr -> remover#expression expr), []) + in + (Expression expr, trailing) + in + Statement.ExportDefaultDeclaration + { + default; + declaration; + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + }) env | T_TYPE when Peek.ith_token ~i:1 env <> T_LCURLY -> (* export type ... *) @@ -1662,7 +2170,33 @@ module Statement ~start_loc (fun env -> error_on_decorators env decorators; - let stmt = Declaration._function env in + match Declaration._function env with + | (fn_loc, Statement.DeclareFunction decl_func) -> + Statement.DeclareExportDeclaration + { + Statement.DeclareExportDeclaration.default = None; + declaration = Some (Statement.DeclareExportDeclaration.Function (fn_loc, decl_func)); + specifiers = None; + source = None; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + | stmt -> + Statement.ExportNamedDeclaration + { + Statement.ExportNamedDeclaration.declaration = Some stmt; + specifiers = None; + source = None; + export_kind = Statement.ExportValue; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + }) + env + | T_LET + | T_CONST + | T_VAR -> + with_loc + ~start_loc + (fun env -> + let stmt = Parse.statement_list_item env ~decorators in Statement.ExportNamedDeclaration { Statement.ExportNamedDeclaration.declaration = Some stmt; @@ -1672,9 +2206,7 @@ module Statement comments = Flow_ast_utils.mk_comments_opt ~leading (); }) env - | T_LET - | T_CONST - | T_VAR -> + | T_ENUM when (parse_options env).enums -> with_loc ~start_loc (fun env -> @@ -1688,7 +2220,8 @@ module Statement comments = Flow_ast_utils.mk_comments_opt ~leading (); }) env - | T_ENUM when (parse_options env).enums -> + (* export record R { ... } *) + | T_RECORD when Peek.is_record env -> with_loc ~start_loc (fun env -> @@ -1707,14 +2240,114 @@ module Statement with_loc ~start_loc (fun env -> - let stmt = Declaration.component env in - Statement.ExportNamedDeclaration + match Declaration.component env with + | ( comp_loc, + Statement.ComponentDeclaration + { + Statement.ComponentDeclaration.body = None; + id; + tparams; + params; + renders; + async = _; + comments; + sig_loc = _; + } + ) -> + let decl_comp = { Statement.DeclareComponent.id; tparams; params; renders; comments } in + Statement.DeclareExportDeclaration + { + Statement.DeclareExportDeclaration.default = None; + declaration = + Some (Statement.DeclareExportDeclaration.Component (comp_loc, decl_comp)); + specifiers = None; + source = None; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + | stmt -> + Statement.ExportNamedDeclaration + { + Statement.ExportNamedDeclaration.declaration = Some stmt; + specifiers = None; + source = None; + export_kind = Statement.ExportValue; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + }) + env + | T_DECLARE -> + (* export declare ... *) + with_loc + ~start_loc + (fun env -> + if not (should_parse_types env) then error env Parse_error.UnexpectedTypeDeclaration; + let leading = leading @ Peek.comments env in + Expect.token env T_DECLARE; + declare_export_declaration_body + env + leading + ~allow_specifiers:false + ~err_expected:"a declaration after 'export declare'") + env + | T_IDENTIFIER { raw = "namespace"; _ } when in_ambient_context env -> + (* export namespace X { ... } in ambient context - implicit declare *) + with_loc + ~start_loc + (fun env -> + let namespace = + with_loc + (fun env -> + Expect.identifier env "namespace"; + let id = id_remove_trailing env (Parse.identifier env) in + let id = Statement.DeclareNamespace.Local id in + let body = declare_module_or_namespace_body env in + { + Statement.DeclareNamespace.id; + body; + comments = None; + implicit_declare = true; + keyword = Statement.DeclareNamespace.Namespace; + }) + env + in + let decl_comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration { - Statement.ExportNamedDeclaration.declaration = Some stmt; + Statement.DeclareExportDeclaration.default = None; + declaration = Some (Statement.DeclareExportDeclaration.Namespace namespace); specifiers = None; source = None; - export_kind = Statement.ExportValue; - comments = Flow_ast_utils.mk_comments_opt ~leading (); + comments = decl_comments; + }) + env + | T_IDENTIFIER { raw = "module"; _ } when in_ambient_context env && is_d_ts env -> + (* export module X { ... } in ambient context (.d.ts only) - implicit declare *) + with_loc + ~start_loc + (fun env -> + let namespace = + with_loc + (fun env -> + Expect.identifier env "module"; + let id = id_remove_trailing env (Parse.identifier env) in + let id = Statement.DeclareNamespace.Local id in + let body = declare_module_or_namespace_body env in + { + Statement.DeclareNamespace.id; + body; + comments = None; + implicit_declare = true; + keyword = Statement.DeclareNamespace.Module; + }) + env + in + let decl_comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + Statement.DeclareExportDeclaration.default = None; + declaration = Some (Statement.DeclareExportDeclaration.Namespace namespace); + specifiers = None; + source = None; + comments = decl_comments; }) env | T_MULT -> @@ -1796,219 +2429,437 @@ module Statement Parse.statement_list_item env ~decorators ) - and declare_export_declaration env = - with_loc - (fun env -> - if not (should_parse_types env) then error env Parse_error.UnexpectedTypeDeclaration; - let leading = Peek.comments env in - Expect.token env T_DECLARE; - let env = env |> with_strict true |> with_in_export true in + (** Helper function for parsing the body of a declare export declaration. + This is shared between `declare export ...` and `export declare ...` syntaxes. + The [allow_specifiers] parameter controls whether batch exports (`* from`) + and named specifiers (`{ ... }`) are allowed - these are only valid for + `declare export`, not `export declare`. + The [err_expected] parameter specifies the error message to use when an + unexpected token is encountered. *) + and declare_export_declaration_body env leading ~allow_specifiers ~err_expected = + Statement.DeclareExportDeclaration.( + match Peek.token env with + | T_DEFAULT -> + (* declare export default ... *) let leading = leading @ Peek.comments env in - Expect.token env T_EXPORT; - Statement.DeclareExportDeclaration.( + let (default, ()) = with_loc (fun env -> Expect.token env T_DEFAULT) env in + let env = with_in_export_default true env in + let (declaration, trailing) = match Peek.token env with - | T_DEFAULT -> - (* declare export default ... *) - let leading = leading @ Peek.comments env in - let (default, ()) = with_loc (fun env -> Expect.token env T_DEFAULT) env in - let env = with_in_export_default true env in - let (declaration, trailing) = - match Peek.token env with - | T_FUNCTION -> - (* declare export default function foo (...): ... *) - let fn = with_loc (declare_function ~async:false) env in - (Some (Function fn), []) - | T_CLASS -> - (* declare export default class foo { ... } *) - let class_ = with_loc (declare_class ~leading:[]) env in - (Some (Class class_), []) - | T_IDENTIFIER { raw = "component"; _ } when (parse_options env).components -> - (* declare export default component Foo() { ... } *) - let component = with_loc (declare_component ~leading:[]) env in - (Some (Component component), []) - | T_IDENTIFIER { raw = "hook"; _ } when (parse_options env).components -> - (* declare export default hook foo (...): ... *) - let fn = with_loc (declare_function ~async:false) env in - (Some (Function fn), []) - | _ -> - (* declare export default [type]; *) - let type_ = Type._type env in - let (type_, trailing) = - match semicolon env with - | Explicit trailing -> (type_, trailing) - | Implicit { remove_trailing; _ } -> - (remove_trailing type_ (fun remover type_ -> remover#type_ type_), []) - in - (Some (DefaultType type_), trailing) - in - let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - Statement.DeclareExportDeclaration - { default = Some default; declaration; specifiers = None; source = None; comments } - | T_LET - | T_CONST - | T_VAR - | T_CLASS | T_FUNCTION -> - let declaration = - match Peek.token env with - | T_FUNCTION -> - (* declare export function foo (...): ... *) - let fn = with_loc (declare_function ~async:false) env in - Some (Function fn) - | T_CLASS -> - (* declare export class foo { ... } *) - let class_ = with_loc (declare_class ~leading:[]) env in - Some (Class class_) - | T_VAR -> - (* declare export var foo: ... *) - let var = with_loc (fun env -> declare_var ~kind:Ast.Variable.Var env []) env in - Some (Variable var) - | T_LET -> - (* declare export let foo: ... *) - let var = with_loc (fun env -> declare_var ~kind:Ast.Variable.Let env []) env in - Some (Variable var) - | T_CONST -> - (* declare export const foo: ... *) - let var = with_loc (fun env -> declare_var ~kind:Ast.Variable.Const env []) env in - Some (Variable var) - | _ -> assert false - in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration - { default = None; declaration; specifiers = None; source = None; comments } - | T_IDENTIFIER { raw = "hook"; _ } when (parse_options env).components -> - let declaration = - (* declare export hook foo (...): ... *) - let fn = with_loc (declare_function ~async:false) env in - Some (Function fn) - in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration - { default = None; declaration; specifiers = None; source = None; comments } + (* declare export default function foo (...): ... *) + let fn = with_loc (declare_function ~async:false) env in + (Some (Function fn), []) + | T_CLASS -> + (* declare export default class foo { ... } *) + let class_ = with_loc (declare_class ~abstract:false ~leading:[]) env in + (Some (Class class_), []) + | T_IDENTIFIER { raw = "abstract"; _ } -> + (* declare export default abstract class foo { ... } *) + Eat.token env; + let class_ = with_loc (declare_class ~abstract:true ~leading:[]) env in + (Some (Class class_), []) | T_IDENTIFIER { raw = "component"; _ } when (parse_options env).components -> - let declaration = - (* declare export component Foo() { ... } *) - let component = with_loc (declare_component ~leading:[]) env in - Some (Component component) - in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration - { default = None; declaration; specifiers = None; source = None; comments } - | T_MULT -> - (* declare export * from 'foo' *) - let loc = Peek.loc env in - Expect.token env T_MULT; - let local_name = - match Peek.token env with - | T_IDENTIFIER { raw = "as"; _ } -> - Eat.token env; - Some (Parse.identifier env) - | _ -> None - in - let specifiers = - Statement.ExportNamedDeclaration.(Some (ExportBatchSpecifier (loc, local_name))) + (* declare export default component Foo() { ... } *) + let component = with_loc (declare_component ~leading:[]) env in + (Some (Component component), []) + | T_ASYNC + when (parse_options env).components + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "component"; _ } -> true + | _ -> false -> + (* declare export default async component Foo() { ... } *) + error env Parse_error.DeclareAsyncComponent; + Eat.token env; + let component = with_loc (declare_component ~leading:[]) env in + (Some (Component component), []) + | T_ASYNC + when (parse_options env).components + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "hook"; _ } -> true + | _ -> false -> + (* declare export default async hook foo (...): ... *) + error env Parse_error.DeclareAsyncHook; + Eat.token env; + let fn = with_loc (declare_function ~async:false) env in + (Some (Function fn), []) + | T_IDENTIFIER { raw = "hook"; _ } when (parse_options env).components -> + (* declare export default hook foo (...): ... *) + let fn = with_loc (declare_function ~async:false) env in + (Some (Function fn), []) + | _ -> + (* declare export default [type]; *) + let type_ = Type._type env in + let (type_, trailing) = + match semicolon env with + | Explicit trailing -> (type_, trailing) + | Implicit { remove_trailing; _ } -> + (remove_trailing type_ (fun remover type_ -> remover#type_ type_), []) in - let (source, trailing) = export_source_and_semicolon env in - let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - Statement.DeclareExportDeclaration - { default = None; declaration = None; specifiers; source = Some source; comments } - | T_TYPE -> - (* declare export type = ... *) - let alias = with_loc (type_alias_helper ~leading:[]) env in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration - { - default = None; - declaration = Some (NamedType alias); - specifiers = None; - source = None; - comments; - } - | T_OPAQUE -> - (* declare export opaque type = ... *) - let opaque = with_loc (opaque_type_helper ~declare:true ~leading:[]) env in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration - { - default = None; - declaration = Some (NamedOpaqueType opaque); - specifiers = None; - source = None; - comments; - } - | T_INTERFACE -> - (* declare export interface ... *) - let iface = with_loc (interface_helper ~leading:[]) env in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration + (Some (DefaultType type_), trailing) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + Statement.DeclareExportDeclaration + { default = Some default; declaration; specifiers = None; source = None; comments } + | T_LET + | T_CONST + | T_VAR + | T_CLASS + | T_FUNCTION -> + let declaration = + match Peek.token env with + | T_FUNCTION -> + (* declare export function foo (...): ... *) + let fn = with_loc (declare_function ~async:false) env in + Some (Function fn) + | T_CLASS -> + (* declare export class foo { ... } *) + let class_ = with_loc (declare_class ~abstract:false ~leading:[]) env in + Some (Class class_) + | T_VAR -> + (* declare export var foo: ... *) + let var = with_loc (fun env -> declare_var ~kind:Ast.Variable.Var env []) env in + Some (Variable var) + | T_LET -> + (* declare export let foo: ... *) + let var = with_loc (fun env -> declare_var ~kind:Ast.Variable.Let env []) env in + Some (Variable var) + | T_CONST -> + (* declare export const foo: ... or declare export const enum ... *) + if (parse_options env).enums && Peek.ith_token ~i:1 env = T_ENUM then begin + Expect.token env T_CONST; + let enum = with_loc (Enum.declaration ~const_:true) env in + Some (Enum enum) + end else begin + let var = with_loc (fun env -> declare_var ~kind:Ast.Variable.Const env []) env in + Some (Variable var) + end + | _ -> assert false + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { default = None; declaration; specifiers = None; source = None; comments } + | T_IDENTIFIER { raw = "hook"; _ } when (parse_options env).components -> + (* declare export hook foo (...): ... *) + let declaration = + let fn = with_loc (declare_function ~async:false) env in + Some (Function fn) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { default = None; declaration; specifiers = None; source = None; comments } + | T_IDENTIFIER { raw = "component"; _ } when (parse_options env).components -> + (* declare export component Foo() { ... } *) + let declaration = + let component = with_loc (declare_component ~leading:[]) env in + Some (Component component) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { default = None; declaration; specifiers = None; source = None; comments } + | T_ASYNC + when (parse_options env).components + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "component"; _ } -> true + | _ -> false -> + (* declare export async component Foo() { ... } *) + let declaration = + error env Parse_error.DeclareAsyncComponent; + Eat.token env; + let component = with_loc (declare_component ~leading:[]) env in + Some (Component component) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { default = None; declaration; specifiers = None; source = None; comments } + | T_ASYNC + when (parse_options env).components + && + match Peek.ith_token ~i:1 env with + | T_IDENTIFIER { raw = "hook"; _ } -> true + | _ -> false -> + (* declare export async hook foo (...): ... *) + let declaration = + error env Parse_error.DeclareAsyncHook; + Eat.token env; + let fn = with_loc (declare_function ~async:false) env in + Some (Function fn) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { default = None; declaration; specifiers = None; source = None; comments } + | T_IDENTIFIER { raw = "abstract"; _ } -> + (* declare export abstract class foo { ... } *) + let declaration = + Eat.token env; + let class_ = with_loc (declare_class ~abstract:true ~leading:[]) env in + Some (Class class_) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { default = None; declaration; specifiers = None; source = None; comments } + | T_MULT when allow_specifiers -> + (* declare export * from 'foo' *) + let loc = Peek.loc env in + Expect.token env T_MULT; + let local_name = + match Peek.token env with + | T_IDENTIFIER { raw = "as"; _ } -> + Eat.token env; + Some (Parse.identifier env) + | _ -> None + in + let specifiers = + Statement.ExportNamedDeclaration.(Some (ExportBatchSpecifier (loc, local_name))) + in + let (source, trailing) = export_source_and_semicolon env in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + Statement.DeclareExportDeclaration + { default = None; declaration = None; specifiers; source = Some source; comments } + | T_TYPE -> + (* declare export type = ... *) + let alias = with_loc (type_alias_helper ~leading:[]) env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = Some (NamedType alias); + specifiers = None; + source = None; + comments; + } + | T_OPAQUE -> + (* declare export opaque type = ... *) + let opaque = with_loc (opaque_type_helper ~declare:true ~leading:[]) env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = Some (NamedOpaqueType opaque); + specifiers = None; + source = None; + comments; + } + | T_INTERFACE -> + (* declare export interface ... *) + let iface = with_loc (interface_helper ~leading:[]) env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = Some (Interface iface); + specifiers = None; + source = None; + comments; + } + | T_ENUM when (parse_options env).enums -> + (* declare export enum ... *) + let enum = with_loc (Enum.declaration ~const_:false) env in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = Some (Enum enum); + specifiers = None; + source = None; + comments; + } + | T_IDENTIFIER { raw = "namespace"; _ } -> + (* declare export namespace X { ... } *) + let namespace = + with_loc + (fun env -> + Expect.identifier env "namespace"; + let id = id_remove_trailing env (Parse.identifier env) in + let id = Statement.DeclareNamespace.Local id in + let body = declare_module_or_namespace_body env in { - default = None; - declaration = Some (Interface iface); - specifiers = None; - source = None; - comments; - } - | T_ENUM when (parse_options env).enums -> - (* declare export enum ... *) - let enum = with_loc Enum.declaration env in - let comments = Flow_ast_utils.mk_comments_opt ~leading () in - Statement.DeclareExportDeclaration + Statement.DeclareNamespace.id; + body; + comments = None; + implicit_declare = false; + keyword = Statement.DeclareNamespace.Namespace; + }) + env + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = Some (Namespace namespace); + specifiers = None; + source = None; + comments; + } + | T_IDENTIFIER { raw = "module"; _ } when is_d_ts env -> + (* declare export module X { ... } (.d.ts only) *) + let namespace = + with_loc + (fun env -> + Expect.identifier env "module"; + let id = id_remove_trailing env (Parse.identifier env) in + let id = Statement.DeclareNamespace.Local id in + let body = declare_module_or_namespace_body env in { - default = None; - declaration = Some (Enum enum); - specifiers = None; - source = None; - comments; - } + Statement.DeclareNamespace.id; + body; + comments = None; + implicit_declare = false; + keyword = Statement.DeclareNamespace.Module; + }) + env + in + let comments = Flow_ast_utils.mk_comments_opt ~leading () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = Some (Namespace namespace); + specifiers = None; + source = None; + comments; + } + | _ when allow_specifiers -> + (* declare export { ... } [from ...] *) + Expect.token env T_LCURLY; + let specifiers = export_specifiers env [] in + Expect.token env T_RCURLY; + let (source, trailing) = + match Peek.token env with + | T_IDENTIFIER { raw = "from"; _ } -> + let (source, trailing) = export_source_and_semicolon env in + (Some source, trailing) | _ -> - Expect.token env T_LCURLY; - let specifiers = export_specifiers env [] in - Expect.token env T_RCURLY; - let (source, trailing) = - match Peek.token env with - | T_IDENTIFIER { raw = "from"; _ } -> - let (source, trailing) = export_source_and_semicolon env in - (Some source, trailing) - | _ -> - assert_export_specifier_identifiers env specifiers; - let trailing = - match semicolon env with - | Explicit trailing -> trailing - | Implicit { trailing; _ } -> trailing - in - (None, trailing) + assert_export_specifier_identifiers env specifiers; + let trailing = + match semicolon env with + | Explicit trailing -> trailing + | Implicit { trailing; _ } -> trailing in - let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in - Statement.DeclareExportDeclaration - { - default = None; - declaration = None; - specifiers = Some (Statement.ExportNamedDeclaration.ExportSpecifiers specifiers); - source; - comments; - } - )) + (None, trailing) + in + let comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () in + Statement.DeclareExportDeclaration + { + default = None; + declaration = None; + specifiers = Some (Statement.ExportNamedDeclaration.ExportSpecifiers specifiers); + source; + comments; + } + | _ -> + error_unexpected ~expected:err_expected env; + Statement.DeclareExportDeclaration + { + default = None; + declaration = None; + specifiers = None; + source = None; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + + (* declare export ... *) + and declare_export_declaration env = + with_loc + (fun env -> + let leading = Peek.comments env in + Expect.token env T_DECLARE; + let env = env |> with_strict true |> with_in_export true in + let leading = leading @ Peek.comments env in + Expect.token env T_EXPORT; + declare_export_declaration_body + env + leading + ~allow_specifiers:true + ~err_expected: + "a declaration, export specifier, or export batch specifier after 'declare export'") env - and import_declaration = + and import_declaration env = Statement.ImportDeclaration.( let missing_source env = (* Just make up a string for the error case *) let loc = Peek.loc_skip_lookahead env in (loc, { StringLiteral.value = ""; raw = ""; comments = None }) in + let import_attributes env = + match Peek.token env with + | T_WITH -> + with_loc_opt + (fun env -> + Eat.token env; + Expect.token env T_LCURLY; + + let rec parse_attributes acc = + match Peek.token env with + | T_RCURLY -> List.rev acc + | T_EOF -> + error_unexpected ~expected:"'}'" env; + List.rev acc + | _ -> + if acc <> [] then + if not (Eat.maybe env T_COMMA) then + error env Parse_error.ImportAttributeMissingComma; + + (* Allow trailing comma *) + (match Peek.token env with + | T_RCURLY -> List.rev acc + | _ -> + let start_loc = Peek.loc env in + let attribute = parse_attribute env start_loc in + parse_attributes (attribute :: acc)) + and parse_attribute env start_loc = + let key = + match Peek.token env with + | T_STRING str -> + let (loc, lit) = string_literal env str in + Statement.ImportDeclaration.StringLiteral (loc, lit) + | _ -> + let id = identifier_name env in + Statement.ImportDeclaration.Identifier id + in + + Expect.token env T_COLON; + + let value = + match Peek.token env with + | T_STRING str -> string_literal env str + | _ -> + error_unexpected ~expected:"string literal" env; + let loc = Peek.loc_skip_lookahead env in + (loc, { StringLiteral.value = ""; raw = ""; comments = None }) + in + let end_loc = Peek.loc_skip_lookahead env in + let loc = Loc.btwn start_loc end_loc in + + { Statement.ImportDeclaration.loc; key; value } + in + + let attributes = parse_attributes [] in + Expect.token env T_RCURLY; + Some attributes) + env + | _ -> None + in let source env = match Peek.token env with | T_IDENTIFIER { raw = "from"; _ } -> Eat.token env; - (match Peek.token env with - | T_STRING str -> string_literal env str - | _ -> - error_unexpected ~expected:"a string" env; - missing_source env) + let source = + match Peek.token env with + | T_STRING str -> string_literal env str + | _ -> + error_unexpected ~expected:"a string" env; + missing_source env + in + let attributes = import_attributes env in + (source, attributes) | _ -> error_unexpected ~expected:"the keyword `from`" env; - missing_source env + (missing_source env, None) in let is_type_import = function | T_TYPE @@ -2081,6 +2932,9 @@ module Statement (* consume `type`, but we don't know yet whether this is `type foo` or `type as foo`. *) let type_keyword_or_remote = identifier_name env in + (* If [kind] survives in a given branch, [type_keyword_or_remote] + was the per-specifier keyword and its loc is the keyword loc. *) + let kind_loc = Some (fst type_keyword_or_remote) in match Peek.token env with (* `type` (a value) *) | T_EOF @@ -2089,7 +2943,7 @@ module Statement let remote = type_keyword_or_remote in (* `type` becomes a value *) assert_identifier_name_is_identifier env remote; - { remote; local = None; remote_name_def_loc = None; kind = None } + { remote; local = None; remote_name_def_loc = None; kind = None; kind_loc = None } (* `type as foo` (value named `type`) or `type as,` (type named `as`) *) | T_IDENTIFIER { raw = "as"; _ } -> begin match Peek.ith_token ~i:1 env with @@ -2097,7 +2951,13 @@ module Statement | T_RCURLY | T_COMMA -> (* `type as` *) - { remote = Type.type_identifier env; remote_name_def_loc = None; local = None; kind } + { + remote = Type.type_identifier env; + remote_name_def_loc = None; + local = None; + kind; + kind_loc; + } | T_IDENTIFIER { raw = "as"; _ } -> (* `type as as foo` *) let remote = identifier_name env in @@ -2107,7 +2967,7 @@ module Statement (* second `as` *) let local = Some (Type.type_identifier env) in (* `foo` *) - { remote; remote_name_def_loc = None; local; kind } + { remote; remote_name_def_loc = None; local; kind; kind_loc } | _ -> (* `type as foo` *) let remote = type_keyword_or_remote in @@ -2117,16 +2977,16 @@ module Statement (* `as` *) let local = Some (Parse.identifier env) in - { remote; remote_name_def_loc = None; local; kind = None } + { remote; remote_name_def_loc = None; local; kind = None; kind_loc = None } end (* `type x`, or `type x as y` *) | _ -> let (remote, local) = with_maybe_as ~for_type:true env in - { remote; remote_name_def_loc = None; local; kind } + { remote; remote_name_def_loc = None; local; kind; kind_loc } else (* standard `x` or `x as y` *) let (remote, local) = with_maybe_as ~for_type:false env in - { remote; remote_name_def_loc = None; local; kind = None } + { remote; remote_name_def_loc = None; local; kind = None; kind_loc = None } (* specifier in an `import type { ... }` *) in let type_specifier env = @@ -2136,7 +2996,7 @@ module Statement ~for_type:true ~error_if_type:Parse_error.ImportTypeShorthandOnlyInPureImport in - { remote; remote_name_def_loc = None; local; kind = None } + { remote; remote_name_def_loc = None; local; kind = None; kind_loc = None } (* specifier in an `import typeof { ... }` *) in let typeof_specifier env = @@ -2146,7 +3006,7 @@ module Statement ~for_type:true ~error_if_type:Parse_error.ImportTypeShorthandOnlyInPureImport in - { remote; remote_name_def_loc = None; local; kind = None } + { remote; remote_name_def_loc = None; local; kind = None; kind_loc = None } in let rec specifier_list ?(preceding_comma = true) env statement_kind acc = match Peek.token env with @@ -2201,13 +3061,13 @@ module Statement | Implicit { remove_trailing; _ } -> ( [], remove_trailing source (fun remover (loc, source) -> - (loc, remover#string_literal loc source) + (loc, remover#string_literal source) ) ) in let with_specifiers import_kind env leading = let specifiers = named_or_namespace_specifier env import_kind in - let source = source env in + let (source, attributes) = source env in let (trailing, source) = semicolon_and_trailing env source in Statement.ImportDeclaration { @@ -2215,6 +3075,7 @@ module Statement source; specifiers; default = None; + attributes; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); } in @@ -2241,7 +3102,7 @@ module Statement named_or_namespace_specifier env import_kind | _ -> None in - let source = source env in + let (source, attributes) = source env in let (trailing, source) = semicolon_and_trailing env source in Statement.ImportDeclaration { @@ -2249,10 +3110,12 @@ module Statement source; specifiers = additional_specifiers; default = Some default_specifier; + attributes; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); } in - with_loc (fun env -> + with_loc + (fun env -> let env = env |> with_strict true in let leading = Peek.comments env in Expect.token env T_IMPORT; @@ -2265,6 +3128,7 @@ module Statement (* `import "ModuleName";` *) | T_STRING str -> let source = string_literal env str in + let attributes = import_attributes env in let (trailing, source) = semicolon_and_trailing env source in Statement.ImportDeclaration { @@ -2272,6 +3136,7 @@ module Statement source; specifiers = None; default = None; + attributes; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); } (* `import type [...] from "ModuleName";` @@ -2284,14 +3149,14 @@ module Statement | T_IDENTIFIER { raw = "from"; _ } -> (* Importing the exported value named "type". This is not a type-import.*) with_default ImportValue env leading + (* `import type = ...`: 'type' is the binding name, not a keyword *) + | T_ASSIGN -> + import_equals_declaration ~import_kind:ImportValue ~is_export:false ~leading env (* `import type *` is invalid, since the namespace can't be a type *) | T_MULT -> (* consume `type` *) Eat.token env; - (* unexpected `*` *) - error_unexpected env; - with_specifiers ImportType env leading | T_LCURLY -> (* consume `type` *) @@ -2302,7 +3167,11 @@ module Statement (* consume `type` *) Eat.token env; - with_default ImportType env leading + (* Check for import type Foo = ... *) + if Peek.is_identifier env && Peek.ith_token ~i:1 env = T_ASSIGN then + import_equals_declaration ~import_kind:ImportType ~is_export:false ~leading env + else + with_default ImportType env leading end (* `import typeof ... from "ModuleName";` *) | T_TYPEOF when should_parse_types env -> @@ -2314,8 +3183,13 @@ module Statement with_specifiers ImportTypeof env leading | _ -> with_default ImportTypeof env leading end - (* import Foo from "ModuleName"; *) - | _ -> with_default ImportValue env leading - ) + (* import Foo from "ModuleName"; or import Foo = ... *) + | _ -> + (* Check for import equals: import Foo = ... *) + if Peek.is_identifier env && Peek.ith_token ~i:1 env = T_ASSIGN then + import_equals_declaration ~import_kind:ImportValue ~is_export:false ~leading env + else + with_default ImportValue env leading) + env ) end diff --git a/compiler/flow_parser/parser/token.ml b/compiler/flow_parser/parser/token.ml index e6800f8bba5..5319b761b89 100644 --- a/compiler/flow_parser/parser/token.ml +++ b/compiler/flow_parser/parser/token.ml @@ -46,6 +46,7 @@ type t = | T_RETURN | T_SWITCH | T_MATCH + | T_RECORD | T_THIS | T_THROW | T_TRY @@ -175,6 +176,7 @@ type t = | T_UNDEFINED_TYPE | T_KEYOF | T_READONLY + | T_WRITEONLY | T_INFER | T_IS | T_ASSERTS @@ -220,6 +222,7 @@ let token_to_string = function | T_RETURN -> "T_RETURN" | T_SWITCH -> "T_SWITCH" | T_MATCH -> "T_MATCH" + | T_RECORD -> "T_RECORD" | T_THIS -> "T_THIS" | T_THROW -> "T_THROW" | T_TRY -> "T_TRY" @@ -329,6 +332,7 @@ let token_to_string = function | T_DECR -> "T_DECR" | T_KEYOF -> "T_KEYOF" | T_READONLY -> "T_READONLY" + | T_WRITEONLY -> "T_WRITEONLY" | T_INFER -> "T_INFER" | T_IS -> "T_IS" | T_ASSERTS -> "T_ASSERTS" @@ -395,6 +399,7 @@ let value_of_token = function | T_RETURN -> "return" | T_SWITCH -> "switch" | T_MATCH -> "match" + | T_RECORD -> "record" | T_THIS -> "this" | T_THROW -> "throw" | T_TRY -> "try" @@ -489,6 +494,7 @@ let value_of_token = function | T_DECR -> "--" | T_KEYOF -> "keyof" | T_READONLY -> "readonly" + | T_WRITEONLY -> "writeonly" | T_INFER -> "infer" | T_IS -> "is" | T_ASSERTS -> "asserts" diff --git a/compiler/flow_parser/parser/token_translator.ml b/compiler/flow_parser/parser/token_translator.ml index 02a7c7c2d83..80e359db64d 100644 --- a/compiler/flow_parser/parser/token_translator.ml +++ b/compiler/flow_parser/parser/token_translator.ml @@ -28,7 +28,6 @@ with type t = Impl.t = struct | TYPE -> "type" | JSX_TAG -> "jsxTag" | JSX_CHILD -> "jsxChild" - | TEMPLATE -> "template" | REGEXP -> "regexp" ) ); diff --git a/compiler/flow_parser/parser/type_parser.ml b/compiler/flow_parser/parser/type_parser.ml index adc926fe7f9..6389256ade3 100644 --- a/compiler/flow_parser/parser/type_parser.ml +++ b/compiler/flow_parser/parser/type_parser.ml @@ -20,7 +20,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | TupleElement of (Loc.t, Loc.t) Type.Tuple.element' | InexactTupleMarker - let maybe_variance ?(parse_readonly = false) ?(parse_in_out = false) env = + let maybe_variance ?(parse_property_variance_keyword = false) ?(parse_in_out = false) env = let loc = Peek.loc env in match Peek.token env with | T_PLUS -> @@ -37,7 +37,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ( loc, { Variance.kind = Variance.Minus; comments = Flow_ast_utils.mk_comments_opt ~leading () } ) - | T_READONLY when parse_readonly -> + | T_READONLY when parse_property_variance_keyword -> let leading = Peek.comments env in Eat.token env; Some @@ -47,6 +47,16 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct comments = Flow_ast_utils.mk_comments_opt ~leading (); } ) + | T_WRITEONLY when parse_property_variance_keyword -> + let leading = Peek.comments env in + Eat.token env; + Some + ( loc, + { + Variance.kind = Variance.Writeonly; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) | T_IDENTIFIER { raw = "in"; _ } when parse_in_out && Peek.ith_is_type_identifier ~i:1 env -> let leading = Peek.comments env in Eat.token env; @@ -340,8 +350,8 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct and typeof_expr env = raw_typeof_expr_with_identifier env (Parse.identifier env) - and raw_typeof_expr_with_identifier = - let rec identifier env (q_loc, qualification) = + and typeof_qualification env start_loc initial = + let rec chain env (q_loc, qualification) = if Peek.token env = T_PERIOD && Peek.ith_is_identifier_name ~i:1 env then let (loc, q) = with_loc @@ -353,28 +363,63 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct env in let qualification = Type.Typeof.Target.Qualified (loc, q) in - identifier env (loc, qualification) + chain env (loc, qualification) else qualification in - fun env ((loc, _) as id) -> - let id = Type.Typeof.Target.Unqualified id in - identifier env (loc, id) + chain env (start_loc, initial) + + and raw_typeof_expr_with_identifier env ((loc, _) as id) = + typeof_qualification env loc (Type.Typeof.Target.Unqualified id) + + and typeof_import_expr env = + let start_loc = Peek.loc env in + let leading = Peek.comments env in + Expect.token env T_IMPORT; + Expect.token env T_LPAREN; + + (* Parse the module specifier string *) + let argument = + match Peek.token env with + | T_STRING (loc, value, raw, octal) -> + if octal then strict_error env Parse_error.StrictOctalLiteral; + Eat.token env; + (loc, { Ast.StringLiteral.value; raw; comments = None }) + | _ -> + error_unexpected ~expected:"string literal" env; + (Peek.loc env, { Ast.StringLiteral.value = ""; raw = "\"\""; comments = None }) + in + let rparen_loc = Peek.loc env in + Expect.token env T_RPAREN; + + let import_loc = Loc.btwn start_loc rparen_loc in + let import_node = + Type.Typeof.Target.Import + ( import_loc, + { + Type.Generic.Identifier.argument; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + in + (* Chain .member qualifications *) + typeof_qualification env import_loc import_node and typeof_arg env = Eat.push_lex_mode env Lex_mode.NORMAL; let result = - if Peek.token env = T_LPAREN then ( + match Peek.token env with + | T_LPAREN -> Eat.token env; let typeof = typeof_arg env in Expect.token env T_RPAREN; typeof - ) else if Peek.is_identifier env then - Some (typeof_expr env) - else ( + | T_IMPORT -> Some (typeof_import_expr env) + | T_THIS -> Some (raw_typeof_expr_with_identifier env (identifier_name env)) + | _ when Peek.is_identifier env -> Some (typeof_expr env) + | _ -> error env Parse_error.InvalidTypeof; None - ) in Eat.pop_lex_mode env; result @@ -421,6 +466,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct { Type.Interface.extends; body; comments = Flow_ast_utils.mk_comments_opt ~leading () }) env | T_TYPEOF -> typeof env + | T_IMPORT when Peek.ith_token ~i:1 env = T_LPAREN -> import_type_generic env | T_LBRACKET -> tuple env | T_IDENTIFIER { raw = "component"; _ } when (parse_options env).components -> with_loc @@ -453,6 +499,15 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | T_RENDERS_QUESTION | T_RENDERS_STAR -> with_loc (fun env -> Type.Renders (render_type env)) env + | T_IDENTIFIER { raw = "unique"; _ } when Peek.ith_token ~i:1 env = T_SYMBOL_TYPE -> + with_loc + (fun env -> + let leading = Peek.comments env in + Eat.token env; + let trailing = Eat.trailing_comments env in + Eat.token env; + Type.UniqueSymbol (Flow_ast_utils.mk_comments_opt ~leading ~trailing ())) + env | T_IDENTIFIER { raw = "hook"; _ } when (parse_options env).components -> (match Peek.ith_token ~i:1 env with | T_LESS_THAN @@ -461,6 +516,16 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | _ -> let (loc, g) = generic env in (loc, Type.Generic g)) + | T_NEW -> + (match Peek.ith_token ~i:1 env with + | T_LESS_THAN + | T_LPAREN -> + constructor_type ~abstract_:false env + | _ -> + let (loc, g) = generic env in + (loc, Type.Generic g)) + | T_IDENTIFIER { raw = "abstract"; _ } when Peek.ith_token ~i:1 env = T_NEW -> + constructor_type ~abstract_:true env | T_IDENTIFIER _ | T_EXTENDS (* `extends` is reserved, but recover by treating it as an identifier *) | T_STATIC (* `static` is reserved, but recover by treating it as an identifier *) -> @@ -499,7 +564,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct let leading = Peek.comments env in Eat.token env; let trailing = Eat.trailing_comments env in - let argument = _type env in + let argument = prefix env in Type.Keyof { Type.Keyof.argument; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () }) env @@ -509,7 +574,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct let leading = Peek.comments env in Eat.token env; let trailing = Eat.trailing_comments env in - let argument = _type env in + let argument = prefix env in Type.ReadOnly { Type.ReadOnly.argument; @@ -551,9 +616,9 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct Type.Infer { Type.Infer.tparam; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () }) env - | T_ERROR "`" -> - error env Parse_error.TSTemplateLiteralType; - (loc, Type.Any None) + | T_TEMPLATE_PART part -> + let (template_loc, template) = template_literal_type env part in + (template_loc, Type.TemplateLiteral template) | _ -> (match primitive env with | Some t -> (loc, t) @@ -561,6 +626,69 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct error_unexpected ~expected:"a type" env; (loc, Type.Any None)) + and template_literal_type = + let rec template_parts env quasis types = + let t = _type env in + let types = t :: types in + let prev_lex_env = Parser_env.Peek.lex_env env in + match Peek.token env with + | T_RCURLY -> + Eat.rescan_as_template_from env prev_lex_env; + let (loc, part, is_tail) = + match Peek.token env with + | T_TEMPLATE_PART (loc, cooked, raw, _, tail) -> + let open Ast.Type.TemplateLiteral in + Eat.token env; + (loc, { Element.value = { Element.cooked; raw }; tail }, tail) + | _ -> assert false + in + let quasis = (loc, part) :: quasis in + if is_tail then + (loc, List.rev quasis, List.rev types) + else + template_parts env quasis types + | _ -> + (* Malformed template *) + error_unexpected ~expected:"a template literal part" env; + let imaginary_quasi = + ( fst t, + { + Ast.Type.TemplateLiteral.Element.value = + { Ast.Type.TemplateLiteral.Element.raw = ""; cooked = "" }; + tail = true; + } + ) + in + (fst t, List.rev (imaginary_quasi :: quasis), List.rev types) + in + fun env ((start_loc, cooked, raw, _, is_tail) as part) -> + let leading = Peek.comments env in + Expect.token env (T_TEMPLATE_PART part); + let (end_loc, quasis, types) = + let head = + ( start_loc, + { + Ast.Type.TemplateLiteral.Element.value = + { Ast.Type.TemplateLiteral.Element.cooked; raw }; + tail = is_tail; + } + ) + in + if is_tail then + (start_loc, [head], []) + else + template_parts env [head] [] + in + let trailing = Eat.trailing_comments env in + let loc = Loc.btwn start_loc end_loc in + ( loc, + { + Ast.Type.TemplateLiteral.quasis; + types; + comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); + } + ) + and negate env = let leading = Peek.comments env in Eat.token env; @@ -712,22 +840,50 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct (* `-1` is a valid type but not a valid tuple label. But `-foo` is only valid as a tuple label. *) maybe_variance env + | T_READONLY when Peek.ith_is_identifier ~i:1 env -> + maybe_variance ~parse_property_variance_keyword:true env + | T_WRITEONLY when Peek.ith_is_identifier ~i:1 env -> + maybe_variance ~parse_property_variance_keyword:true env | _ -> None in match (Peek.is_identifier env, Peek.ith_token ~i:1 env) with - | (true, T_PLING) + | (true, T_PLING) -> + let name = identifier_name env in + Expect.token env T_PLING; + if Eat.maybe env T_COLON then + (* foo?: type — labeled optional element *) + let annot = _type env in + TupleElement + (Type.Tuple.LabeledElement + { Type.Tuple.LabeledElement.name; annot; variance; optional = true } + ) + else ( + (* Foo? — optional unlabeled element with identifier type *) + if Option.is_some variance then error env Parse_error.InvalidTupleVariance; + let id = Type.Generic.Identifier.Unqualified name in + let (name_loc, _) = name in + let annot = + (name_loc, Type.Generic { Type.Generic.id; targs = None; comments = None }) + in + TupleElement + (Type.Tuple.UnlabeledElement + { Type.Tuple.UnlabeledElement.annot; optional = true } + ) + ) | (true, T_COLON) -> let name = identifier_name env in - let optional = Eat.maybe env T_PLING in Expect.token env T_COLON; let annot = _type env in TupleElement (Type.Tuple.LabeledElement - { Type.Tuple.LabeledElement.name; annot; variance; optional } + { Type.Tuple.LabeledElement.name; annot; variance; optional = false } ) | _ -> if Option.is_some variance then error env Parse_error.InvalidTupleVariance; - TupleElement (Type.Tuple.UnlabeledElement (_type env))) + let annot = _type env in + let optional = Eat.maybe env T_PLING in + TupleElement + (Type.Tuple.UnlabeledElement { Type.Tuple.UnlabeledElement.annot; optional })) env in let rec elements env acc = @@ -782,31 +938,49 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct variant; } - and anonymous_function_param _env annot = - (fst annot, Type.Function.Param.{ name = None; annot; optional = false }) + and anonymous_function_param _env annot = (fst annot, Type.Function.Param.Anonymous annot) and function_param_with_id env = + let (loc, (name, optional, annot)) = + with_loc + (fun env -> + Eat.push_lex_mode env Lex_mode.NORMAL; + let name = Parse.identifier env in + Eat.pop_lex_mode env; + if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation; + let optional = Eat.maybe env T_PLING in + Expect.token env T_COLON; + let annot = _type env in + (name, optional, annot)) + env + in + (loc, Type.Function.Param.Labeled { name; annot; optional }) + + and destructuring_function_param ~allow_optional env = with_loc (fun env -> - Eat.push_lex_mode env Lex_mode.NORMAL; - let name = Parse.identifier env in - Eat.pop_lex_mode env; - if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation; - let optional = Eat.maybe env T_PLING in - Expect.token env T_COLON; - let annot = _type env in - { Type.Function.Param.name = Some name; annot; optional }) + let pattern = Parse.pattern env ~allow_optional Parse_error.StrictParamName in + Type.Function.Param.Destructuring pattern) env and function_param_list_without_parens = - let param env = - match Peek.ith_token ~i:1 env with - | T_COLON - | T_PLING -> - function_param_with_id env + let param ~allow_optional env = + match Peek.token env with + | T_LCURLY + | T_LBRACKET -> + if is_d_ts env then + destructuring_function_param ~allow_optional env + else + let annot = _type env in + anonymous_function_param env annot | _ -> - let annot = _type env in - anonymous_function_param env annot + (match Peek.ith_token ~i:1 env with + | T_COLON + | T_PLING -> + function_param_with_id env + | _ -> + let annot = _type env in + anonymous_function_param env annot) in let rec param_list env this_ acc = match Peek.token env with @@ -819,7 +993,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct let leading = Peek.comments env in Expect.token env T_ELLIPSIS; { - Type.Function.RestParam.argument = param env; + Type.Function.RestParam.argument = param ~allow_optional:false env; comments = Flow_ast_utils.mk_comments_opt ~leading (); }) env @@ -847,7 +1021,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct if Peek.token env <> T_RPAREN then Expect.token env T_COMMA; param_list env (Some this_) acc | _ -> - let acc = param env :: acc in + let acc = param ~allow_optional:true env :: acc in if Peek.token env <> T_RPAREN then Expect.token env T_COMMA; param_list env this_ acc in @@ -995,6 +1169,20 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | T_LPAREN -> Type (_type env) | _ -> function_param_or_generic_type env) + | T_NEW + | T_READONLY + | T_WRITEONLY + | T_KEYOF + | T_INFER + | T_ASSERTS + | T_IS + | T_IMPLIES -> + (match Peek.ith_token ~i:1 env with + | T_PLING + | T_COLON -> + ParamList (function_param_list_without_parens env []) + | _ -> Type (_type env)) + | T_IDENTIFIER { raw = "abstract"; _ } when Peek.ith_token ~i:1 env = T_NEW -> Type (_type env) | T_IDENTIFIER _ | T_STATIC (* `static` is reserved in strict mode, but still an identifier *) -> (* This could be a function parameter or a generic type *) @@ -1008,6 +1196,38 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct (* Ok this is definitely a parameter *) ParamList (function_param_list_without_parens env []) | _ -> Type (_type env)) + | T_LCURLY + | T_LBRACKET + when is_d_ts env -> + (* In .d.ts files, ({...} or ([... could be a parenthesized type or + a destructuring function parameter. Try parsing as type first. *) + let try_type env = + let error_callback _ _ = raise Try.Rollback in + let env = with_error_callback error_callback env in + let t = _type env in + (* After parsing, verify we're at ) or , — otherwise this isn't + a simple parenthesized type *) + (match Peek.token env with + | T_RPAREN + | T_COMMA -> + () + | _ -> raise Try.Rollback); + t + in + (match Try.to_parse env try_type with + | Try.ParsedSuccessfully t -> Type t + | Try.FailedToParse -> + (* Type parse failed. Try destructuring. If that also fails, + re-parse as type to produce type-level error messages. *) + (match + Try.to_parse env (fun env -> + let error_callback _ _ = raise Try.Rollback in + let env = with_error_callback error_callback env in + function_param_list_without_parens env [] + ) + with + | Try.ParsedSuccessfully params -> ParamList params + | Try.FailedToParse -> Type (_type env))) | _ -> (* All params start with an identifier or `...` *) Type (_type env) @@ -1102,11 +1322,38 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct let params = function_param_list env in function_with_params ~effect_:Function.Hook env start_loc tparams params + and constructor_type ~abstract_ env = + let start_loc = Peek.loc env in + if abstract_ then Eat.token env; + Eat.token env; + let tparams = + type_params_remove_trailing env ~kind:Flow_ast_mapper.FunctionTP (type_params env) + in + let params = function_param_list env in + (match params with + | (_, { Type.Function.Params.this_ = Some _; _ }) -> + error_at env (start_loc, Parse_error.ThisParamBannedInConstructorType) + | _ -> ()); + let effect_ = Function.Arbitrary in + with_loc + ~start_loc + (fun env -> + Expect.token env T_ARROW; + let return = function_return_type env in + Type.( + ConstructorType + { + ConstructorType.abstract_; + func = { Function.params; return; tparams; comments = None; effect_ }; + } + )) + env + and function_return_type env = if is_start_of_type_guard env then Type.Function.TypeGuard (type_guard env) else - Type.Function.TypeAnnotation (_type env) + Type.Function.Available (_type env) and type_guard env = let parse_is_type_guard env = @@ -1146,18 +1393,31 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct and type_guard_annotation env ~start_loc = with_loc ~start_loc type_guard env + and ith_is_object_key ~i ~is_class env = + Eat.push_lex_mode env Lex_mode.NORMAL; + let result = Peek.ith_is_object_key ~i ~is_class env in + Eat.pop_lex_mode env; + result + and _object = let methodish env start_loc tparams = with_loc ~start_loc (fun env -> let params = function_param_list env in - Expect.token env T_COLON; - let return = function_return_type env in + let return = + if Peek.token env <> T_COLON then + Type.Function.Missing (Peek.loc_skip_lookahead env) + else ( + Expect.token env T_COLON; + function_return_type env + ) + in { Type.Function.params; return; tparams; comments = None; effect_ = Function.Arbitrary }) env in - let method_property env start_loc static key ~leading = + let method_property + env start_loc static ~abstract ~override key ~optional ~ts_accessibility ~leading = let key = object_key_remove_trailing env key in let tparams = type_params_remove_trailing env ~kind:Flow_ast_mapper.FunctionTypeTP (type_params env) @@ -1169,12 +1429,16 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ( fst value, { Property.key; - value = Property.Init value; - optional = false; + value = Property.Init (Some value); + optional; static = static <> None; proto = false; _method = true; + abstract; + override; variance = None; + ts_accessibility; + init = None; comments = Flow_ast_utils.mk_comments_opt ~leading (); } ) @@ -1200,7 +1464,18 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct in Type.Object.CallProperty prop in - let init_property env start_loc ~variance ~static ~proto ~leading (key_loc, key) = + let init_property + env + start_loc + ~is_class + ~variance + ~static + ~proto + ~abstract + ~override + ~ts_accessibility + ~leading + (key_loc, key) = ignore proto; if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation; let prop = @@ -1208,28 +1483,53 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ~start_loc (fun env -> let optional = Eat.maybe env T_PLING in + let ate_colon = + if is_class then + Eat.maybe env T_COLON + else + Expect.token_maybe env T_COLON + in let value = - if Expect.token_maybe env T_COLON then - _type env + if ate_colon then + Type.Object.Property.Init (Some (_type env)) + else if is_class && Peek.token env = T_ASSIGN then + Type.Object.Property.Init None + else if is_class then + (* Class field with neither : nor = — type checker + will emit DeclareClassPropertyMissingAnnotationOrInit *) + Type.Object.Property.Init None else - (key_loc, Type.Any None) + Type.Object.Property.Init (Some (key_loc, Type.Any None)) + in + let init = + if is_class && Eat.maybe env T_ASSIGN then begin + Eat.push_lex_mode env Lex_mode.NORMAL; + let init_expr = Parse.assignment env in + Eat.pop_lex_mode env; + Some init_expr + end else + None in Type.Object.Property. { key; - value = Init value; + value; optional; static = static <> None; proto = proto <> None; _method = false; + abstract; + override; variance; + ts_accessibility; + init; comments = Flow_ast_utils.mk_comments_opt ~leading (); }) env in Type.Object.Property prop in - let getter_or_setter ~is_getter ~leading env start_loc static key = + let getter_or_setter ~is_getter ~leading ~override ~ts_accessibility env start_loc static key = let prop = with_loc ~start_loc @@ -1268,7 +1568,11 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct static = static <> None; proto = false; _method = false; + abstract = false; + override; variance = None; + ts_accessibility; + init = None; comments = Flow_ast_utils.mk_comments_opt ~leading (); }) env @@ -1290,6 +1594,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct in let key = _type env in Expect.token env T_RBRACKET; + let optional = Eat.maybe env T_PLING in let trailing = Eat.trailing_comments env in Expect.token env T_COLON; let value = _type env in @@ -1299,6 +1604,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct value; static = static <> None; variance; + optional; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); }) env @@ -1306,7 +1612,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct Type.Object.Indexer indexer in - let mapped_type env start_loc variance ~leading = + let mapped_type env start_loc variance ~variance_op ~leading = let mapped_type = with_loc ~start_loc @@ -1326,6 +1632,13 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct * "in" identifier. Now we eat it. *) Eat.token env; let source_type = _type env in + let name_type = + match Peek.token env with + | T_IDENTIFIER { raw = "as"; _ } -> + Eat.token env; + Some (_type env) + | _ -> None + in Expect.token env T_RBRACKET; let optional = Type.Object.MappedType.( @@ -1351,7 +1664,9 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct Type.Object.MappedType.key_tparam = (key_name_loc, key_tparam); source_type; prop_type; + name_type; variance; + variance_op; optional; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing (); }) @@ -1360,15 +1675,6 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct Type.Object.MappedType mapped_type in - let mapped_type_or_indexer env start_loc static variance ~leading = - let leading = leading @ Peek.comments env in - Expect.token env T_LBRACKET; - match Peek.ith_token ~i:1 env with - | T_IDENTIFIER { raw = "in"; _ } when static = None -> - mapped_type env start_loc variance ~leading - | _ -> indexer_property env start_loc static variance ~leading - in - let internal_slot env start_loc static ~leading = let islot = with_loc @@ -1435,12 +1741,95 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct Eat.token env | T_RCURLYBAR when exact -> () | T_RCURLY when not exact -> () + | _ when is_d_ts env && Peek.is_implicit_semicolon env -> () | _ -> Expect.error env T_COMMA in let error_unexpected_variance env = function | Some (loc, _) -> error_at env (loc, Parse_error.UnexpectedVariance) | None -> () in + let computed_key_property + env + start_loc + static + ~abstract + ~override + variance + ~ts_accessibility + ~is_class + computed_loc + key = + match Peek.token env with + | T_LESS_THAN + | T_LPAREN -> + error_unexpected_variance env variance; + method_property + env + start_loc + static + ~abstract + ~override + key + ~optional:false + ~ts_accessibility + ~leading:[] + | T_PLING + when match Peek.ith_token ~i:1 env with + | T_LPAREN + | T_LESS_THAN -> + true + | _ -> false -> + Eat.token env; + error_unexpected_variance env variance; + method_property + env + start_loc + static + ~abstract + ~override + key + ~optional:true + ~ts_accessibility + ~leading:[] + | _ -> + init_property + env + start_loc + ~is_class + ~variance + ~static + ~proto:None + ~abstract + ~override + ~ts_accessibility + ~leading:[] + (computed_loc, key) + in + let computed_property + env start_loc static ~abstract ~override variance ~ts_accessibility ~is_class ~leading = + Eat.push_lex_mode env Lex_mode.NORMAL; + let expr = Parse.assignment env in + Eat.pop_lex_mode env; + let computed_loc = Loc.btwn start_loc (Peek.loc env) in + Expect.token env T_RBRACKET; + let key = + Expression.Object.Property.Computed + ( computed_loc, + { ComputedKey.expression = expr; comments = Flow_ast_utils.mk_comments_opt ~leading () } + ) + in + computed_key_property + env + start_loc + static + ~abstract + ~override + variance + ~ts_accessibility + ~is_class + computed_loc + key + in let error_unexpected_proto env = function | Some loc -> error_at env (loc, Parse_error.UnexpectedProto) | None -> () @@ -1460,6 +1849,104 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ) | _ -> () in + let well_known_symbol_property + env start_loc static ~abstract ~override variance ~ts_accessibility ~is_class ~leading = + (* We've already consumed '['. Now consume 'Symbol', '.', and the property name. *) + let sym_loc = Peek.loc env in + Eat.token env; + (* consume 'Symbol' *) + Expect.token env T_PERIOD; + let name_loc = Peek.loc env in + let name = + match Peek.token env with + | T_IDENTIFIER { raw; _ } -> raw + | _ -> + error_unexpected ~expected:"a symbol name" env; + "" + in + Eat.token env; + (* Build a Member expression AST: Symbol.X *) + let member_expr = + let _object = + (sym_loc, Expression.Identifier (sym_loc, { Identifier.name = "Symbol"; comments = None })) + in + let property = + Expression.Member.PropertyIdentifier (name_loc, { Identifier.name; comments = None }) + in + ( Loc.btwn sym_loc name_loc, + Expression.Member { Expression.Member._object; property; comments = None } + ) + in + let computed_loc = Loc.btwn start_loc (Peek.loc env) in + Expect.token env T_RBRACKET; + let key = + Expression.Object.Property.Computed + ( computed_loc, + { + ComputedKey.expression = member_expr; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + in + computed_key_property + env + start_loc + static + ~abstract + ~override + variance + ~ts_accessibility + ~is_class + computed_loc + key + in + let bracket_property + env + ~abstract + ~override + start_loc + static + variance + ~variance_op + ~ts_accessibility + ~is_class + ~leading = + let leading = leading @ Peek.comments env in + Expect.token env T_LBRACKET; + match (Peek.token env, Peek.ith_token ~i:1 env) with + | (_, T_IDENTIFIER { raw = "in"; _ }) when static = None -> + mapped_type env start_loc variance ~variance_op ~leading + | (_, T_COLON) -> + if Option.is_some variance_op then error_unexpected_variance env variance; + indexer_property env start_loc static variance ~leading + | (T_IDENTIFIER { raw = "Symbol"; _ }, T_PERIOD) -> + if Option.is_some variance_op then error_unexpected_variance env variance; + well_known_symbol_property + env + start_loc + static + ~abstract + ~override + variance + ~ts_accessibility + ~is_class + ~leading + | _ -> + if Option.is_some variance_op then error_unexpected_variance env variance; + if is_d_ts env then + computed_property + env + start_loc + static + ~abstract + ~override + variance + ~ts_accessibility + ~is_class + ~leading + else + indexer_property env start_loc static variance ~leading + in let rec properties ~is_class ~allow_inexact ~allow_spread ~exact env ((props, inexact, internal) as acc) = (* no `static ...A` *) @@ -1538,9 +2025,15 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ~is_class ~allow_static:is_class ~allow_proto:is_class + ~allow_abstract:is_class + ~allow_accessibility:is_class ~variance:None + ~variance_op:None ~static:None ~proto:None + ~abstract:false + ~override:false + ~ts_accessibility:None ~leading:[] in semicolon exact env; @@ -1552,7 +2045,21 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct env (prop :: props, inexact, internal) and property - env ~is_class ~allow_static ~allow_proto ~variance ~static ~proto ~leading start_loc = + env + ~is_class + ~allow_static + ~allow_proto + ~allow_abstract + ~allow_accessibility + ~variance + ~variance_op + ~static + ~proto + ~abstract + ~override + ~ts_accessibility + ~leading + start_loc = match Peek.token env with | T_PLUS | T_MINUS @@ -1563,9 +2070,15 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ~is_class ~allow_static:false ~allow_proto:false + ~allow_abstract:false + ~allow_accessibility:false ~variance + ~variance_op:None ~static ~proto + ~abstract + ~override + ~ts_accessibility ~leading start_loc | T_STATIC when allow_static -> @@ -1580,9 +2093,15 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ~is_class ~allow_static:false ~allow_proto:false + ~allow_abstract + ~allow_accessibility:false ~variance + ~variance_op:None ~static ~proto + ~abstract + ~override + ~ts_accessibility ~leading start_loc | T_IDENTIFIER { raw = "proto"; _ } when allow_proto -> @@ -1597,32 +2116,150 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ~is_class ~allow_static:false ~allow_proto:false + ~allow_abstract + ~allow_accessibility:false + ~variance + ~variance_op:None + ~static + ~proto + ~abstract + ~override + ~ts_accessibility + ~leading + start_loc + | T_IDENTIFIER { raw = "override"; _ } + when is_class && (not override) && ith_is_object_key ~i:1 ~is_class env -> + let leading = leading @ Peek.comments env in + Eat.token env; + property + env + ~is_class + ~allow_static:false + ~allow_proto:false + ~allow_abstract + ~allow_accessibility:false + ~variance + ~variance_op:None + ~static + ~proto + ~abstract + ~override:true + ~ts_accessibility + ~leading + start_loc + | T_IDENTIFIER { raw = "abstract"; _ } + when allow_abstract && ith_is_object_key ~i:1 ~is_class env -> + if static <> None then error env Parse_error.StaticAbstractMethod; + let leading = leading @ Peek.comments env in + Eat.token env; + property + env + ~is_class + ~allow_static:false + ~allow_proto:false + ~allow_abstract:false + ~allow_accessibility:false + ~variance + ~variance_op:None + ~static + ~proto + ~abstract:true + ~override + ~ts_accessibility + ~leading + start_loc + | T_IDENTIFIER { raw = ("private" | "protected" | "public") as raw; _ } + when allow_accessibility + && (ith_is_object_key ~i:1 ~is_class env || Peek.ith_token ~i:1 env = T_STATIC) -> + let kind = + match raw with + | "private" -> Ast.Class.TSAccessibility.Private + | "protected" -> Ast.Class.TSAccessibility.Protected + | "public" -> Ast.Class.TSAccessibility.Public + | _ -> failwith "Must be one of the above" + in + let acc_loc = Peek.loc env in + let leading = leading @ Peek.comments env in + Eat.token env; + let ts_accessibility = + Some (acc_loc, { Ast.Class.TSAccessibility.kind; comments = None }) + in + property + env + ~is_class + ~allow_static:is_class + ~allow_proto:false + ~allow_abstract + ~allow_accessibility:false ~variance + ~variance_op:None ~static ~proto + ~abstract + ~override + ~ts_accessibility ~leading start_loc | T_READONLY - when variance = None - && (Peek.ith_is_identifier ~i:1 env || Peek.ith_token ~i:1 env = T_LBRACKET) -> - let variance = maybe_variance ~parse_readonly:true env in + | T_WRITEONLY + when (match variance with + | None + | Some (_, { Variance.kind = Variance.Plus | Variance.Minus; _ }) -> + true + | _ -> false) + && ith_is_object_key ~i:1 ~is_class env -> + let variance_op = + match variance with + | Some (_, { Variance.kind = Variance.Plus; _ }) -> Some Type.Object.MappedType.Add + | Some (_, { Variance.kind = Variance.Minus; _ }) -> Some Type.Object.MappedType.Remove + | _ -> None + in + let variance = maybe_variance ~parse_property_variance_keyword:true env in property env ~is_class ~allow_static:false ~allow_proto:false + ~allow_abstract:false + ~allow_accessibility:false ~variance + ~variance_op ~static ~proto + ~abstract + ~override + ~ts_accessibility ~leading start_loc + | T_POUND when is_class -> + error_unexpected_proto env proto; + error_unexpected_variance env variance; + if static <> None then error env Parse_error.UnexpectedStatic; + let leading = leading @ Peek.comments env in + let key = private_identifier env in + let loc = Loc.btwn start_loc (fst key) in + Type.Object.PrivateField + ( loc, + { Type.Object.PrivateField.key; comments = Flow_ast_utils.mk_comments_opt ~leading () } + ) | T_LBRACKET -> error_unexpected_proto env proto; (match Peek.ith_token ~i:1 env with | T_LBRACKET -> error_unexpected_variance env variance; internal_slot env start_loc static ~leading - | _ -> mapped_type_or_indexer env start_loc static variance ~leading) + | _ -> + bracket_property + env + ~abstract + ~override + start_loc + static + variance + ~variance_op + ~ts_accessibility + ~is_class + ~leading) | T_LESS_THAN | T_LPAREN -> (* Note that `static(): void` is a static callable property if we @@ -1645,7 +2282,18 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ) in let static = None in - init_property env start_loc ~variance ~static ~proto ~leading:[] (static_loc, key) + init_property + env + start_loc + ~is_class + ~variance + ~static + ~proto + ~abstract:false + ~override:false + ~ts_accessibility:None + ~leading:[] + (static_loc, key) | (None, Some proto_loc, (T_PLING | T_COLON)) -> (* We speculatively parsed `proto` as a proto modifier, but now that we've parsed the next token, we changed our minds and want @@ -1658,7 +2306,18 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct ) in let proto = None in - init_property env start_loc ~variance ~static ~proto ~leading:[] (proto_loc, key) + init_property + env + start_loc + ~is_class + ~variance + ~static + ~proto + ~abstract:false + ~override:false + ~ts_accessibility:None + ~leading:[] + (proto_loc, key) | _ -> let object_key env = Eat.push_lex_mode env Lex_mode.NORMAL; @@ -1678,10 +2337,50 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | T_LPAREN -> error_unexpected_proto env proto; error_unexpected_variance env variance; - method_property env start_loc static key ~leading + method_property + env + start_loc + static + ~abstract + ~override + key + ~optional:false + ~ts_accessibility + ~leading + | T_PLING + when match Peek.ith_token ~i:1 env with + | T_LPAREN + | T_LESS_THAN -> + true + | _ -> false -> + Eat.token env; + error_unexpected_proto env proto; + error_unexpected_variance env variance; + method_property + env + start_loc + static + ~abstract + ~override + key + ~optional:true + ~ts_accessibility + ~leading | T_COLON - | T_PLING -> - init_property env start_loc ~variance ~static ~proto ~leading (key_loc, key) + | T_PLING + | T_ASSIGN -> + init_property + env + start_loc + ~is_class + ~variance + ~static + ~proto + ~abstract + ~override + ~ts_accessibility + ~leading + (key_loc, key) | _ -> ignore (object_key_remove_trailing env key); let key = object_key env in @@ -1689,7 +2388,15 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct let leading = leading @ leading_key in error_unexpected_proto env proto; error_unexpected_variance env variance; - getter_or_setter ~is_getter ~leading env start_loc static key + getter_or_setter + ~is_getter + ~leading + ~override + ~ts_accessibility + env + start_loc + static + key end | (key_loc, key) -> begin match Peek.token env with @@ -1697,10 +2404,49 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | T_LPAREN -> error_unexpected_proto env proto; error_unexpected_variance env variance; - method_property env start_loc static key ~leading + method_property + env + start_loc + static + ~abstract + ~override + key + ~optional:false + ~ts_accessibility + ~leading + | T_PLING + when match Peek.ith_token ~i:1 env with + | T_LPAREN + | T_LESS_THAN -> + true + | _ -> false -> + Eat.token env; + error_unexpected_proto env proto; + error_unexpected_variance env variance; + method_property + env + start_loc + static + ~abstract + ~override + key + ~optional:true + ~ts_accessibility + ~leading | _ -> error_invalid_property_name env is_class static key; - init_property env start_loc ~variance ~static ~proto ~leading (key_loc, key) + init_property + env + start_loc + ~is_class + ~variance + ~static + ~proto + ~abstract + ~override + ~ts_accessibility + ~leading + (key_loc, key) end)) in fun ~is_class ~allow_exact ~allow_spread env -> @@ -1930,44 +2676,85 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct and generic env = raw_generic_with_identifier env (type_identifier env) - and raw_generic_with_identifier = - let rec identifier env (q_loc, qualification) = - if Peek.token env = T_PERIOD && Peek.ith_is_type_identifier ~i:1 env then + (* Shared recursive helper to build qualified chain from any starting qualification *) + and generic_identifier_with_qualification env start_loc initial_qualification = + let rec loop (q_loc, qualification) = + if Peek.token env = T_PERIOD && Peek.ith_is_identifier_name ~i:1 env then let (loc, q) = with_loc ~start_loc:q_loc (fun env -> Expect.token env T_PERIOD; - let id = type_identifier env in + let id = identifier_name env in { Type.Generic.Identifier.qualification; id }) env in let qualification = Type.Generic.Identifier.Qualified (loc, q) in - identifier env (loc, qualification) + loop (loc, qualification) else (q_loc, qualification) in - fun env id -> - with_loc - ~start_loc:(fst id) - (fun env -> - let id = (fst id, Type.Generic.Identifier.Unqualified id) in - let id = - let (_id_loc, id) = identifier env id in - if Peek.token env <> T_LESS_THAN then - id - else - let { remove_trailing; _ } = trailing_and_remover env in - remove_trailing id (fun remover id -> remover#generic_identifier_type id) - in - let targs = type_args env in - { Type.Generic.id; targs; comments = None }) - env + let id = + let (_id_loc, id) = loop (start_loc, initial_qualification) in + if Peek.token env <> T_LESS_THAN then + id + else + let { remove_trailing; _ } = trailing_and_remover env in + remove_trailing id (fun remover id -> remover#generic_identifier_type id) + in + let targs = type_args env in + { Type.Generic.id; targs; comments = None } + + and raw_generic_with_identifier env id = + with_loc + ~start_loc:(fst id) + (fun env -> + let initial = Type.Generic.Identifier.Unqualified id in + generic_identifier_with_qualification env (fst id) initial) + env and generic_type_with_identifier env id = let (loc, generic) = raw_generic_with_identifier env id in (loc, Type.Generic generic) + and import_type_generic env = + with_loc + (fun env -> + let leading = Peek.comments env in + let start_loc = Peek.loc env in + Eat.token env; + (* consume 'import' *) + Expect.token env T_LPAREN; + + (* Parse the module specifier string *) + let argument = + match Peek.token env with + | T_STRING (loc, value, raw, octal) -> + if octal then strict_error env Parse_error.StrictOctalLiteral; + Eat.token env; + (loc, { Ast.StringLiteral.value; raw; comments = None }) + | _ -> + error_unexpected ~expected:"string literal" env; + (Peek.loc env, { Ast.StringLiteral.value = ""; raw = "\"\""; comments = None }) + in + let rparen_loc = Peek.loc env in + Expect.token env T_RPAREN; + + let import_loc = Loc.btwn start_loc rparen_loc in + let initial = + Type.Generic.Identifier.ImportTypeAnnot + ( import_loc, + { + Type.Generic.Identifier.argument; + comments = Flow_ast_utils.mk_comments_opt ~leading (); + } + ) + in + + (* Reuse shared logic for qualified chain and type args *) + Type.Generic (generic_identifier_with_qualification env import_loc initial)) + env + and function_return_annotation_opt env = match Peek.token env with | T_COLON -> function_return_annotation env @@ -2036,6 +2823,7 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct | Unknown comments -> Unknown (merge_comments comments) | Never comments -> Never (merge_comments comments) | Undefined comments -> Undefined (merge_comments comments) + | UniqueSymbol comments -> UniqueSymbol (merge_comments comments) | Nullable ({ Nullable.comments; _ } as t) -> Nullable { t with Nullable.comments = merge_comments comments } | Function ({ Function.comments; _ } as t) -> @@ -2089,6 +2877,14 @@ module Type (Parse : Parser_common.PARSER) : Parser_common.TYPE = struct BigIntLiteral { t with BigIntLiteral.comments = merge_comments comments } | BooleanLiteral ({ BooleanLiteral.comments; _ } as t) -> BooleanLiteral { t with BooleanLiteral.comments = merge_comments comments } + | TemplateLiteral ({ Type.TemplateLiteral.comments; _ } as t) -> + TemplateLiteral { t with Type.TemplateLiteral.comments = merge_comments comments } + | ConstructorType ({ ConstructorType.func = { Function.comments; _ } as func; _ } as t) -> + ConstructorType + { + t with + ConstructorType.func = { func with Function.comments = merge_comments comments }; + } ) let predicate_checks_contents env ~leading = From 299bf33a0fb55f1529e5d93a71bb3fe1dbcd02d5 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Fri, 28 Aug 2026 10:17:50 +0200 Subject: [PATCH 2/6] Add Flow parser 0.320.0 changelog entry Signed-off-by: Christoph Knittel --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8de7cc3c5e3..10773839f0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ #### :house: Internal +- Upgrade the vendored Flow parser from 0.267.0 to 0.320.0, the final release of the OCaml implementation. https://github.com/rescript-lang/rescript/pull/8588 - Vendor the Flow parser 0.267.0 sources used by the compiler, removing the external `flow_parser` dependency and establishing a maintained baseline for future OCaml upgrades. https://github.com/rescript-lang/rescript/pull/8587 - Store processed external declarations as structured data instead of serialized values in `pval_prim`, and lower external calls during Lambda translation. This removes `Pccall`, `external_spec`, and the unsupported `%absfloat` primitive. The AST, CMI, and CMT magic numbers are bumped (`ResImpl01301`/`ResIntf01301`, `Caml1999I025`, `Caml1999T026`). https://github.com/rescript-lang/rescript/pull/8581 - Resolve dynamic-import targets during Lambda translation and store the module and export path directly in `Pimport`. This removes the `dynamic_import` flags from `Pjs_call` and `Lglobal_module`, along with backend expression-shape detection. https://github.com/rescript-lang/rescript/pull/8582 From 3810089d85552658962703e092c035755b05aa00 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Fri, 28 Aug 2026 10:27:22 +0200 Subject: [PATCH 3/6] Keep Flow parser compatible with OCaml 5.0 Signed-off-by: Christoph Knittel --- compiler/flow_parser/README.md | 3 ++- compiler/flow_parser/parser/parser_common.ml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/flow_parser/README.md b/compiler/flow_parser/README.md index 06d33f59630..cd787e5d913 100644 --- a/compiler/flow_parser/README.md +++ b/compiler/flow_parser/README.md @@ -12,7 +12,8 @@ The Dune files were adapted to build these sources as private libraries inside the ReScript repository. Sources used only by the upstream JavaScript and C API targets are not included. One ambiguous Sedlex documentation comment was converted to a regular comment and one unused loop index was renamed so the -vendored sources build with ReScript's warning settings. +vendored sources build with ReScript's warning settings. One `List.is_empty` +call uses an empty-list comparison to retain OCaml 5.0 compatibility. Vendored sources are excluded from the repository-wide OCamlformat check so that they remain comparable with their upstream versions. diff --git a/compiler/flow_parser/parser/parser_common.ml b/compiler/flow_parser/parser/parser_common.ml index ed9ba4cf88e..432116ce699 100644 --- a/compiler/flow_parser/parser/parser_common.ml +++ b/compiler/flow_parser/parser/parser_common.ml @@ -550,7 +550,7 @@ let is_start_of_type_guard env = let reparse_arguments_as_match_argument env (args_loc, args) = let { Expression.ArgList.arguments; _ } = args in - if List.is_empty arguments then Parser_env.error_at env (args_loc, Parse_error.MatchEmptyArgument); + if arguments = [] then Parser_env.error_at env (args_loc, Parse_error.MatchEmptyArgument); let filtered_args = List.filter_map (function From ec30963702d4e4565bbb20e28731cc7bfe3b5e57 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Fri, 28 Aug 2026 10:31:04 +0200 Subject: [PATCH 4/6] Retain MIT-licensed Flow collection sources Signed-off-by: Christoph Knittel --- compiler/flow_parser/README.md | 14 +++++++++++--- compiler/flow_parser/collections/iMap.ml | 7 ++++++- compiler/flow_parser/collections/iSet.ml | 7 ++++++- compiler/flow_parser/collections/immQueue.ml | 7 ++++++- compiler/flow_parser/collections/immQueue.mli | 7 ++++++- compiler/flow_parser/collections/intKey.ml | 7 ++++++- compiler/flow_parser/collections/priorityQueue.ml | 7 ++++++- compiler/flow_parser/collections/sMap.ml | 7 ++++++- compiler/flow_parser/collections/sSet.ml | 7 ++++++- compiler/flow_parser/collections/stringKey.ml | 7 ++++++- compiler/flow_parser/collections/union_find.ml | 7 ++++++- compiler/flow_parser/collections/wrappedMap.ml | 7 ++++++- compiler/flow_parser/collections/wrappedMap.mli | 7 ++++++- compiler/flow_parser/collections/wrappedMap_sig.ml | 7 ++++++- 14 files changed, 89 insertions(+), 16 deletions(-) diff --git a/compiler/flow_parser/README.md b/compiler/flow_parser/README.md index cd787e5d913..3e7e6947ad1 100644 --- a/compiler/flow_parser/README.md +++ b/compiler/flow_parser/README.md @@ -4,10 +4,17 @@ This directory contains the OCaml Flow parser used by the ReScript compiler. - Upstream repository: https://github.com/facebook/flow - Flow parser version: `0.320.0` -- Source commit: `7c64d4b077bc6fc45c12cee3cfa7368fdb2186ce` +- Parser and Sedlex source commit: `7c64d4b077bc6fc45c12cee3cfa7368fdb2186ce` +- Collection source commit: `9ea4062c0b7e037415c4413a7634c459ebd5c31b` + from Flow parser 0.267.0 - Original source directories: `src/parser`, `src/third-party/sedlex`, `src/third-party/sedlex-ppx`, and `src/hack_forked/utils/collections` +The collection helpers retain the MIT-licensed 0.267.0 sources. Their code is +identical in Flow 0.320.0, but that release replaces their MIT notices with +"Confidential and proprietary" notices that do not grant redistribution +rights. + The Dune files were adapted to build these sources as private libraries inside the ReScript repository. Sources used only by the upstream JavaScript and C API targets are not included. One ambiguous Sedlex documentation comment was @@ -18,5 +25,6 @@ call uses an empty-list comparison to retain OCaml 5.0 compatibility. Vendored sources are excluded from the repository-wide OCamlformat check so that they remain comparable with their upstream versions. -The Flow sources are licensed under the MIT licence in `LICENSE`. Vendored -Sedlex and collections sources retain their own licence files. +The Flow parser and retained collection sources are licensed under the MIT +licence in `LICENSE` and their source headers. Vendored Sedlex and collection +third-party sources retain the licence files in their respective directories. diff --git a/compiler/flow_parser/collections/iMap.ml b/compiler/flow_parser/collections/iMap.ml index 2d0103ac1b4..93c66823ebc 100644 --- a/compiler/flow_parser/collections/iMap.ml +++ b/compiler/flow_parser/collections/iMap.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) include WrappedMap.Make (IntKey) diff --git a/compiler/flow_parser/collections/iSet.ml b/compiler/flow_parser/collections/iSet.ml index 30e3896b3d9..471f51dda4a 100644 --- a/compiler/flow_parser/collections/iSet.ml +++ b/compiler/flow_parser/collections/iSet.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) include Flow_set.Make (IntKey) diff --git a/compiler/flow_parser/collections/immQueue.ml b/compiler/flow_parser/collections/immQueue.ml index ae119f15472..3f326a45855 100644 --- a/compiler/flow_parser/collections/immQueue.ml +++ b/compiler/flow_parser/collections/immQueue.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) type 'a t = { incoming: 'a list; diff --git a/compiler/flow_parser/collections/immQueue.mli b/compiler/flow_parser/collections/immQueue.mli index 810a9fc5fd7..67743a82b5e 100644 --- a/compiler/flow_parser/collections/immQueue.mli +++ b/compiler/flow_parser/collections/immQueue.mli @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) (* * Immutable queue implementation. Modeled loosely after the mutable stdlib diff --git a/compiler/flow_parser/collections/intKey.ml b/compiler/flow_parser/collections/intKey.ml index 01d02145bd8..4701376db44 100644 --- a/compiler/flow_parser/collections/intKey.ml +++ b/compiler/flow_parser/collections/intKey.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) type t = int diff --git a/compiler/flow_parser/collections/priorityQueue.ml b/compiler/flow_parser/collections/priorityQueue.ml index 119795503b9..cc2b52866f8 100644 --- a/compiler/flow_parser/collections/priorityQueue.ml +++ b/compiler/flow_parser/collections/priorityQueue.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) module Make (Ord : Set.OrderedType) = struct type elt = Ord.t diff --git a/compiler/flow_parser/collections/sMap.ml b/compiler/flow_parser/collections/sMap.ml index 27c9fee4928..e179a06c9c1 100644 --- a/compiler/flow_parser/collections/sMap.ml +++ b/compiler/flow_parser/collections/sMap.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) include WrappedMap.Make (StringKey) diff --git a/compiler/flow_parser/collections/sSet.ml b/compiler/flow_parser/collections/sSet.ml index e9a1c731d80..b197a0f2bd7 100644 --- a/compiler/flow_parser/collections/sSet.ml +++ b/compiler/flow_parser/collections/sSet.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) include Flow_set.Make (StringKey) diff --git a/compiler/flow_parser/collections/stringKey.ml b/compiler/flow_parser/collections/stringKey.ml index cecc381a6f9..777cbda1ba6 100644 --- a/compiler/flow_parser/collections/stringKey.ml +++ b/compiler/flow_parser/collections/stringKey.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) type t = string diff --git a/compiler/flow_parser/collections/union_find.ml b/compiler/flow_parser/collections/union_find.ml index 97c2bc80e90..2b4b466f511 100644 --- a/compiler/flow_parser/collections/union_find.ml +++ b/compiler/flow_parser/collections/union_find.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) type ident = int diff --git a/compiler/flow_parser/collections/wrappedMap.ml b/compiler/flow_parser/collections/wrappedMap.ml index ccabf045bc2..777ed8ad484 100644 --- a/compiler/flow_parser/collections/wrappedMap.ml +++ b/compiler/flow_parser/collections/wrappedMap.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) module type S = WrappedMap_sig.S diff --git a/compiler/flow_parser/collections/wrappedMap.mli b/compiler/flow_parser/collections/wrappedMap.mli index 16486176e39..cc0e9bad665 100644 --- a/compiler/flow_parser/collections/wrappedMap.mli +++ b/compiler/flow_parser/collections/wrappedMap.mli @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) module type S = WrappedMap_sig.S diff --git a/compiler/flow_parser/collections/wrappedMap_sig.ml b/compiler/flow_parser/collections/wrappedMap_sig.ml index c2acf487ef5..d8abefca90f 100644 --- a/compiler/flow_parser/collections/wrappedMap_sig.ml +++ b/compiler/flow_parser/collections/wrappedMap_sig.ml @@ -1,4 +1,9 @@ -(* (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. *) +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) module type S = sig include Flow_map.S From 613e438bb112fb6edd285a6462e31e515ac7bc4d Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Fri, 28 Aug 2026 10:36:31 +0200 Subject: [PATCH 5/6] Clarify vendored error catalog scope Signed-off-by: Christoph Knittel --- tests/ERROR_VARIANTS.md | 5 +++++ .../expected/warning_103_flow_parser.res.expected | 8 ++++++++ .../super_errors/fixtures/warning_103_flow_parser.res | 1 + 3 files changed, 14 insertions(+) create mode 100644 tests/build_tests/super_errors/expected/warning_103_flow_parser.res.expected create mode 100644 tests/build_tests/super_errors/fixtures/warning_103_flow_parser.res diff --git a/tests/ERROR_VARIANTS.md b/tests/ERROR_VARIANTS.md index bbc6605fd01..efca757da94 100644 --- a/tests/ERROR_VARIANTS.md +++ b/tests/ERROR_VARIANTS.md @@ -35,6 +35,11 @@ here because there's no constructor to track. If someone wants to add coverage for one of those messages, the first step is refactoring it into a named variant. +Diagnostic types in vendored third-party sources are also excluded. In +particular, Flow parser `Parse_error.t` values cross into the ReScript compiler +as warning 103 (`Bs_ffi_warning`), whose integration boundary is covered by +[`warning_103_flow_parser.res`](build_tests/super_errors/fixtures/warning_103_flow_parser.res). + ## Fixture paths - Single-file fixtures live in [`build_tests/super_errors/fixtures/`](build_tests/super_errors/fixtures/) with expected output in [`build_tests/super_errors/expected/`](build_tests/super_errors/expected/). diff --git a/tests/build_tests/super_errors/expected/warning_103_flow_parser.res.expected b/tests/build_tests/super_errors/expected/warning_103_flow_parser.res.expected new file mode 100644 index 00000000000..27916f77498 --- /dev/null +++ b/tests/build_tests/super_errors/expected/warning_103_flow_parser.res.expected @@ -0,0 +1,8 @@ + + Warning number 103 + /.../fixtures/warning_103_flow_parser.res:1:31 + + 1 │ let _invalidRaw = %raw("1 +") + 2 │ + + FFI warning: Unexpected end of input \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/warning_103_flow_parser.res b/tests/build_tests/super_errors/fixtures/warning_103_flow_parser.res new file mode 100644 index 00000000000..ce404cfff2a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/warning_103_flow_parser.res @@ -0,0 +1 @@ +let _invalidRaw = %raw("1 +") From 53a429bbcae1fef02993c8b5a7c8609eb159ef64 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Fri, 28 Aug 2026 10:53:12 +0200 Subject: [PATCH 6/6] Support newer ppxlib in vendored Sedlex PPX Signed-off-by: Christoph Knittel --- compiler/flow_parser/README.md | 4 +++- compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/flow_parser/README.md b/compiler/flow_parser/README.md index 3e7e6947ad1..df4bf9c2ee1 100644 --- a/compiler/flow_parser/README.md +++ b/compiler/flow_parser/README.md @@ -20,7 +20,9 @@ the ReScript repository. Sources used only by the upstream JavaScript and C API targets are not included. One ambiguous Sedlex documentation comment was converted to a regular comment and one unused loop index was renamed so the vendored sources build with ReScript's warning settings. One `List.is_empty` -call uses an empty-list comparison to retain OCaml 5.0 compatibility. +call uses an empty-list comparison to retain OCaml 5.0 compatibility. The +Sedlex PPX uses `Ast_helper.Exp.fun_` to generate single-argument functions +with newer ppxlib versions. Vendored sources are excluded from the repository-wide OCamlformat check so that they remain comparable with their upstream versions. diff --git a/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml b/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml index 44c063e7fe9..1e399790cec 100644 --- a/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml +++ b/compiler/flow_parser/flow_sedlexing_ppx/ppx_sedlex.ml @@ -359,7 +359,7 @@ let gen_state lexbuf auto i (trans, final) = (appfun (partition_name partition) [[%expr Sedlexing.__private__next_int [%e evar ~loc lexbuf]]]) (cases @ [case ~lhs:[%pat? _] ~guard:None ~rhs:[%expr Sedlexing.backtrack [%e evar ~loc lexbuf]]]) in - let ret body = [ value_binding ~loc ~pat:(pvar ~loc (state_fun i)) ~expr:(pexp_function ~loc [case ~lhs:(pvar ~loc lexbuf) ~guard:None ~rhs:body]) ] in + let ret body = [ value_binding ~loc ~pat:(pvar ~loc (state_fun i)) ~expr:(Ast_helper.Exp.fun_ ~loc Nolabel None (pvar ~loc lexbuf) body) ] in match best_final final with | None -> ret (body ()) | Some _ when Array.length trans = 0 -> []