diff --git a/src/codemods/annotate_exports.ml b/src/codemods/annotate_exports.ml index 4abadf99a8d..83646aa09af 100644 --- a/src/codemods/annotate_exports.ml +++ b/src/codemods/annotate_exports.ml @@ -96,6 +96,7 @@ module SignatureVerification = struct (fun acc err -> match err with | Type_sig.CheckError -> acc + | Type_sig.BindingValidationError _ -> acc | Type_sig.SigError err -> let open Signature_error in let (tot_errors, acc) = acc in diff --git a/src/parser_utils/file_sig.ml b/src/parser_utils/file_sig.ml index 8cde5c0f615..75eeeca6aa7 100644 --- a/src/parser_utils/file_sig.ml +++ b/src/parser_utils/file_sig.ml @@ -59,7 +59,10 @@ and require_bindings = | BindNamed of (Loc.t Ast_utils.ident * require_bindings) list [@@deriving show] -type tolerable_error = SignatureVerificationError of Loc.t Signature_error.t [@@deriving show] +type tolerable_error = + | SignatureVerificationError of Loc.t Signature_error.t + | SignatureBindingValidationError of Loc.t Signature_error.binding_validation_t +[@@deriving show] type tolerable_t = t * tolerable_error list diff --git a/src/parser_utils/file_sig.mli b/src/parser_utils/file_sig.mli index 8709dab6e2a..3ed18b05cfb 100644 --- a/src/parser_utils/file_sig.mli +++ b/src/parser_utils/file_sig.mli @@ -101,7 +101,10 @@ and require_bindings = | BindNamed of (Loc.t Flow_ast_utils.ident * require_bindings) list [@@deriving show] -type tolerable_error = SignatureVerificationError of Loc.t Signature_error.t [@@deriving show] +type tolerable_error = + | SignatureVerificationError of Loc.t Signature_error.t + | SignatureBindingValidationError of Loc.t Signature_error.binding_validation_t +[@@deriving show] type tolerable_t = t * tolerable_error list diff --git a/src/parser_utils/signature_builder/signature_error.ml b/src/parser_utils/signature_builder/signature_error.ml index 46cae833de7..9c264a9579d 100644 --- a/src/parser_utils/signature_builder/signature_error.ml +++ b/src/parser_utils/signature_builder/signature_error.ml @@ -15,4 +15,6 @@ type 'loc t = | UnexpectedExpression of 'loc * Flow_ast_utils.ExpressionSort.t [@@deriving show, iter, map] +type 'loc binding_validation_t = NameAlreadyBound of 'loc [@@deriving show, iter, map] + let compare = Stdlib.compare diff --git a/src/parser_utils/type_sig/__tests__/type_sig_tests.ml b/src/parser_utils/type_sig/__tests__/type_sig_tests.ml index 64f300a11bc..cd2fffdaf0a 100644 --- a/src/parser_utils/type_sig/__tests__/type_sig_tests.ml +++ b/src/parser_utils/type_sig/__tests__/type_sig_tests.ml @@ -5245,6 +5245,108 @@ let%expect_test "builtin_declare_namespace" = Builtin global value globalThis Builtin global value ns |}] +let%expect_test "declare_namespace_declaration_merging" = + print_builtins [{| + declare namespace ns_v { + declare const a: string; + } + declare namespace ns_v { + declare const b: string; + } + declare namespace ns_t { + type T1 = string; + } + declare namespace ns_t { + type T2 = string; + } + declare namespace ns_v_and_then_t { + declare const a: string; + } + declare namespace ns_t_and_then_v { + type T1 = string; + } + declare namespace ns_v_and_then_t { + type T1 = string; + } + declare namespace ns_t_and_then_v { + declare const a: string; + } + + declare const non_ns_value: string; + type non_ns_type = string; + // The following namespaces won't have any effect + declare namespace non_ns_value { + declare const b: string; + } + declare namespace non_ns_value { + type T1 = string; + } + declare namespace non_ns_type { + declare const b: string; + } + declare namespace non_ns_type { + type T1 = string; + } + |}]; + [%expect {| + Local defs: + 0. Variable {id_loc = [2:16-17]; name = "a"; def = (Annot (String [2:19-25]))} + 1. NamespaceBinding {id_loc = [1:18-22]; + name = "ns_v"; + values = + { "a" -> ([2:16-17], (Ref LocalRef {ref_loc = [2:16-17]; index = 0})); + "b" -> ([5:16-17], (Ref LocalRef {ref_loc = [5:16-17]; index = 2})) }; + types = {}} + 2. Variable {id_loc = [5:16-17]; name = "b"; def = (Annot (String [5:19-25]))} + 3. TypeAlias {id_loc = [8:7-9]; name = "T1"; tparams = Mono; body = (Annot (String [8:12-18]))} + 4. NamespaceBinding {id_loc = [7:18-22]; + name = "ns_t"; values = {}; + types = + { "T1" -> ([8:7-9], (Ref LocalRef {ref_loc = [8:7-9]; index = 3})); + "T2" -> ([11:7-9], (Ref LocalRef {ref_loc = [11:7-9]; index = 5})) }} + 5. TypeAlias {id_loc = [11:7-9]; + name = "T2"; tparams = Mono; + body = (Annot (String [11:12-18]))} + 6. Variable {id_loc = [14:16-17]; name = "a"; def = (Annot (String [14:19-25]))} + 7. NamespaceBinding {id_loc = [13:18-33]; + name = "ns_v_and_then_t"; + values = { "a" -> ([14:16-17], (Ref LocalRef {ref_loc = [14:16-17]; index = 6})) }; + types = { "T1" -> ([20:7-9], (Ref LocalRef {ref_loc = [20:7-9]; index = 10})) }} + 8. TypeAlias {id_loc = [17:7-9]; + name = "T1"; tparams = Mono; + body = (Annot (String [17:12-18]))} + 9. NamespaceBinding {id_loc = [16:18-33]; + name = "ns_t_and_then_v"; + values = { "a" -> ([23:16-17], (Ref LocalRef {ref_loc = [23:16-17]; index = 11})) }; + types = { "T1" -> ([17:7-9], (Ref LocalRef {ref_loc = [17:7-9]; index = 8})) }} + 10. TypeAlias {id_loc = [20:7-9]; + name = "T1"; tparams = Mono; + body = (Annot (String [20:12-18]))} + 11. Variable {id_loc = [23:16-17]; name = "a"; def = (Annot (String [23:19-25]))} + 12. Variable {id_loc = [25:14-26]; name = "non_ns_value"; def = (Annot (String [25:28-34]))} + 13. TypeAlias {id_loc = [26:5-16]; + name = "non_ns_type"; tparams = Mono; + body = (Annot (String [26:19-25]))} + 14. NamespaceBinding {id_loc = [0:0]; + name = "globalThis"; + values = + { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 14})); + "non_ns_value" -> ([25:14-26], (Ref LocalRef {ref_loc = [25:14-26]; index = 12})); + "ns_t_and_then_v" -> ([16:18-33], (Ref LocalRef {ref_loc = [16:18-33]; index = 9})); + "ns_v" -> ([1:18-22], (Ref LocalRef {ref_loc = [1:18-22]; index = 1})); + "ns_v_and_then_t" -> ([13:18-33], (Ref LocalRef {ref_loc = [13:18-33]; index = 7})) }; + types = + { "non_ns_type" -> ([26:5-16], (Ref LocalRef {ref_loc = [26:5-16]; index = 13})); + "ns_t" -> ([7:18-22], (Ref LocalRef {ref_loc = [7:18-22]; index = 4})) }} + + Builtin global value globalThis + Builtin global value non_ns_value + Builtin global value ns_t_and_then_v + Builtin global value ns_v + Builtin global value ns_v_and_then_t + Builtin global type non_ns_type + Builtin global type ns_t |}] + let%expect_test "builtin_pattern" = print_builtins [{| const o = { p: 0 }; diff --git a/src/parser_utils/type_sig/type_sig.ml b/src/parser_utils/type_sig/type_sig.ml index 41851efe810..4894974299d 100644 --- a/src/parser_utils/type_sig/type_sig.ml +++ b/src/parser_utils/type_sig/type_sig.ml @@ -615,6 +615,7 @@ type 'a op = *) type 'loc errno = | CheckError + | BindingValidationError of 'loc Signature_error.binding_validation_t | SigError of 'loc Signature_error.t [@@deriving show { with_path = false }, map] diff --git a/src/parser_utils/type_sig/type_sig_mark.ml b/src/parser_utils/type_sig/type_sig_mark.ml index 9922c60f9bd..123bd645d0f 100644 --- a/src/parser_utils/type_sig/type_sig_mark.ml +++ b/src/parser_utils/type_sig/type_sig_mark.ml @@ -266,6 +266,8 @@ let mark_exports SMap.iter (fun _ t -> mark_export ~locs_to_dirtify t) names; List.iter mark_star stars +let mark_errors = List.iter (Signature_error.iter_binding_validation_t (mark_loc ~visit_loc:ignore)) + let mark_builtin_module (loc, exports) = mark_loc ~visit_loc:ignore loc; mark_exports ~locs_to_dirtify:[] loc exports diff --git a/src/parser_utils/type_sig/type_sig_mark.mli b/src/parser_utils/type_sig/type_sig_mark.mli index 8b6fccc64a1..d17e543c95a 100644 --- a/src/parser_utils/type_sig/type_sig_mark.mli +++ b/src/parser_utils/type_sig/type_sig_mark.mli @@ -13,5 +13,8 @@ val mark_exports : Loc.t Type_sig_parse.exports -> unit +val mark_errors : + Loc.t Type_sig_collections.Locs.node Signature_error.binding_validation_t list -> unit + val mark_builtin_module : Loc.t Type_sig_collections.Locs.node * Loc.t Type_sig_parse.exports -> unit diff --git a/src/parser_utils/type_sig/type_sig_pack.ml b/src/parser_utils/type_sig/type_sig_pack.ml index c3dc209c2f8..62dc6732432 100644 --- a/src/parser_utils/type_sig/type_sig_pack.ml +++ b/src/parser_utils/type_sig/type_sig_pack.ml @@ -235,7 +235,7 @@ type 'loc pattern = type 'loc cx = { mutable errs: 'loc errno list } -let create_cx () = { errs = [] } +let create_cx errs = { errs } let pack_smap f map = let bindings = Array.of_list (SMap.bindings map) in diff --git a/src/parser_utils/type_sig/type_sig_parse.ml b/src/parser_utils/type_sig/type_sig_parse.ml index 3d75ba93274..7e23b72e56a 100644 --- a/src/parser_utils/type_sig/type_sig_parse.ml +++ b/src/parser_utils/type_sig/type_sig_parse.ml @@ -327,6 +327,7 @@ and 'loc tables = { remote_refs: 'loc remote_binding Remote_refs.builder; pattern_defs: 'loc parsed Pattern_defs.builder; patterns: 'loc pattern Patterns.builder; + mutable additional_errors: 'loc loc_node Signature_error.binding_validation_t list; } type frozen_kind = @@ -344,6 +345,7 @@ let create_tables () = remote_refs = Remote_refs.create (); pattern_defs = Pattern_defs.create (); patterns = Patterns.create (); + additional_errors = []; } let push_loc tbls = Locs.push tbls.locs @@ -1092,6 +1094,88 @@ module Scope = struct (* a `declare namespace` exports every binding. *) let finalize_declare_namespace_exn ~is_type_only scope tbls id_loc name = match scope with + (* Special declaration merging rules for namespaces in the global scope *) + | DeclareNamespace { values; types; parent = Global global_scope; _ } -> + let (values, types) = namespace_binding_of_values_and_types scope (values, types) in + let def : _ local_binding = NamespaceBinding { id_loc; name; values; types } in + let union_values_and_types existing_values existing_types values types = + let new_binding name (l, _) = + if SMap.mem name existing_values || SMap.mem name existing_types then ( + tbls.additional_errors <- Signature_error.NameAlreadyBound l :: tbls.additional_errors; + false + ) else + true + in + let values = SMap.filter new_binding values in + let types = SMap.filter new_binding types in + (SMap.union existing_values values, SMap.union existing_types types) + in + (* When we decide that the namespace binding is mergeable. We call this function to + * mutate the existing node with merged names. *) + let merge_with_existing_local_binding_node (node : _ local_binding Local_defs.node) = + Local_defs.modify node (function + | NamespaceBinding { id_loc; name; values = existing_values; types = existing_types } -> + let (values, types) = + union_values_and_types existing_values existing_types values types + in + NamespaceBinding { id_loc; name; values; types } + | b -> b + ) + in + (* This updater handle the simple case when we can directly update one kind of binding map + * (either value or type-only bindings). *) + let simple_updater = function + | Some (LocalBinding node) as existing_binding -> + merge_with_existing_local_binding_node node; + existing_binding + | Some (RemoteBinding _) as existing_binding -> existing_binding + | None -> + let node = push_local_def tbls def in + Some (LocalBinding node) + in + let updater values_map types_map = + match (is_type_only, SMap.find_opt name values_map, SMap.find_opt name types_map) with + | (_, Some _, Some _) -> + failwith "Invariant violation: a name cannot be in both values and types" + (* Simple case: no existing binding exist *) + | (false, None, None) -> (SMap.update name simple_updater values_map, types_map) + | (true, None, None) -> (values_map, SMap.update name simple_updater types_map) + (* Simple case: existing binding exist, but in the same value/type category *) + | (false, Some _, None) -> (SMap.update name simple_updater values_map, types_map) + | (true, None, Some _) -> (values_map, SMap.update name simple_updater types_map) + (* Originally not-type-only, now merging with a type-only namespace, + * so it's still not type-only *) + | (true, Some _, None) -> (SMap.update name simple_updater values_map, types_map) + (* Originally type-only, but now it's merging with a non-type-only namespace. + * The namespace binding needs to be promoted to a non-type-only namespace. + * (implemented by removing the node from type-only map, add to the value map, and + * mutate the binding node in place with additional names.) + * + * e.g. Consider + * ``` + * declare namespace ns { type A = '' }; + * declare namespace ns { declare const b: string }; + * ``` + * + * In order for the behavior to be equivalent to + + * ``` + * declare namespace ns { type A = ''; declare const b: string }; + * ``` + * We have to promote the original type-only namespace to a value binding. + *) + | (false, None, Some (LocalBinding node)) + when match Local_defs.value node with + | NamespaceBinding _ -> true + | _ -> false -> + merge_with_existing_local_binding_node node; + (SMap.add name (LocalBinding node) values_map, SMap.remove name types_map) + (* Has an existing non-namespace binding, do nothing *) + | (false, None, Some _) -> (values_map, types_map) + in + let (values, types) = updater global_scope.values global_scope.types in + global_scope.values <- values; + global_scope.types <- types | DeclareNamespace { values; types; parent; _ } -> let (values, types) = namespace_binding_of_values_and_types scope (values, types) in bind_local @@ -1100,6 +1184,7 @@ module Scope = struct tbls name (NamespaceBinding { id_loc; name; values; types }) + ignore2 | _ -> failwith "The scope must be lexical" let bind_globalThis scope tbls ~global_this_loc = @@ -4172,7 +4257,7 @@ let namespace_decl comments = _; } = match id with - | Ast.Statement.DeclareNamespace.Global _ -> ignore + | Ast.Statement.DeclareNamespace.Global _ -> () | Ast.Statement.DeclareNamespace.Local (id_loc, { Ast.Identifier.name; _ }) -> let id_loc = push_loc tbls id_loc in let stmts = @@ -4677,7 +4762,7 @@ let rec statement opts scope tbls (loc, stmt) = Scope.finalize_declare_module_exports_exn scope | S.DeclareNamespace decl -> let is_type_only = Flow_ast_utils.is_type_only_declaration_statement (loc, stmt) in - namespace_decl opts scope tbls ~is_type_only ~visit_statement:statement decl ignore2 + namespace_decl opts scope tbls ~is_type_only ~visit_statement:statement decl | S.DeclareEnum decl | S.EnumDeclaration decl -> enum_decl opts scope tbls decl ignore2 diff --git a/src/parser_utils/type_sig/type_sig_utils.ml b/src/parser_utils/type_sig/type_sig_utils.ml index dfdc2cc5390..62872f75ad2 100644 --- a/src/parser_utils/type_sig/type_sig_utils.ml +++ b/src/parser_utils/type_sig/type_sig_utils.ml @@ -28,12 +28,31 @@ let parse_libs opts ordered_asts = Scope.bind_globalThis scope tbls ~global_this_loc:Loc.none; (tbls, Scope.builtins_exn scope) +let create_pack_cx additional_errors = + Pack.create_cx + (List.map + (fun e -> + Type_sig.BindingValidationError (Signature_error.map_binding_validation_t Locs.index_exn e)) + additional_errors + ) + let pack_builtins (tbls, (global_values, global_types, global_modules)) = - let { Parse.locs; module_refs; local_defs; remote_refs; pattern_defs; patterns } = tbls in + let { + Parse.locs; + module_refs; + local_defs; + remote_refs; + pattern_defs; + patterns; + additional_errors; + } = + tbls + in (* mark *) SMap.iter (fun _ b -> Mark.mark_binding ~locs_to_dirtify:[] b) global_values; SMap.iter (fun _ b -> Mark.mark_binding ~locs_to_dirtify:[] b) global_types; SMap.iter (fun _ m -> Mark.mark_builtin_module m) global_modules; + Mark.mark_errors additional_errors; (* compact *) let locs = Locs.compact locs in let module_refs = Module_refs.Interned.compact module_refs in @@ -42,7 +61,7 @@ let pack_builtins (tbls, (global_values, global_types, global_modules)) = let pattern_defs = Pattern_defs.compact pattern_defs in let patterns = Patterns.compact patterns in (* copy *) - let cx = Pack.create_cx () in + let cx = create_pack_cx additional_errors in let (locs, _) = Locs.copy (fun x -> x) locs in let (module_refs, _) = Module_refs.copy (fun x -> x) module_refs in let (local_defs, _) = Local_defs.copy (Pack.pack_local_binding cx) local_defs in @@ -97,9 +116,20 @@ let merge_locs loc0 loc1 = (Loc.debug_to_string ~include_source:true loc1) let pack ~locs_to_dirtify source (tbls, file_loc, exports) = - let { Parse.locs; module_refs; local_defs; remote_refs; pattern_defs; patterns } = tbls in + let { + Parse.locs; + module_refs; + local_defs; + remote_refs; + pattern_defs; + patterns; + additional_errors; + } = + tbls + in (* mark *) Mark.mark_exports ~locs_to_dirtify file_loc exports; + Mark.mark_errors additional_errors; (* compact *) let locs = Locs.compact ~merge:merge_locs locs in let module_refs = Module_refs.Interned.compact module_refs in @@ -108,7 +138,7 @@ let pack ~locs_to_dirtify source (tbls, file_loc, exports) = let pattern_defs = Pattern_defs.compact pattern_defs in let patterns = Patterns.compact patterns in (* copy *) - let cx = Pack.create_cx () in + let cx = create_pack_cx additional_errors in let (locs, _) = Locs.copy (fun x -> x) locs in let (module_refs, _) = Module_refs.copy (fun x -> x) module_refs in let (local_defs, dirty_local_defs) = Local_defs.copy (Pack.pack_local_binding cx) local_defs in diff --git a/src/parsing/parsing_service_js.ml b/src/parsing/parsing_service_js.ml index ae73a528ed9..1d6e45a9e3c 100644 --- a/src/parsing/parsing_service_js.ml +++ b/src/parsing/parsing_service_js.ml @@ -211,6 +211,13 @@ let do_parse ~options ~docblock ?(locs_to_dirtify = []) content file = | Type_sig.SigError err -> let err = Signature_error.map (Type_sig_collections.Locs.get locs) err in File_sig.SignatureVerificationError err :: acc + | Type_sig.BindingValidationError err -> + let err = + Signature_error.map_binding_validation_t + (Type_sig_collections.Locs.get locs) + err + in + File_sig.SignatureBindingValidationError err :: acc | Type_sig.CheckError -> acc) [] sig_errors diff --git a/src/services/inference/inference_utils.ml b/src/services/inference/inference_utils.ml index 5257ede1ab3..7f6bc3c41bd 100644 --- a/src/services/inference/inference_utils.ml +++ b/src/services/inference/inference_utils.ml @@ -53,8 +53,12 @@ let set_of_parse_exception ~source_file = let error_of_file_sig_tolerable_error ~source_file err = let open File_sig in let flow_err = - let (SignatureVerificationError sve) = err in - Error_message.ESignatureVerification (Signature_error.map ALoc.of_loc sve) + match err with + | SignatureVerificationError sve -> + Error_message.ESignatureVerification (Signature_error.map ALoc.of_loc sve) + | SignatureBindingValidationError sve -> + Error_message.ESignatureBindingValidation + (Signature_error.map_binding_validation_t ALoc.of_loc sve) in Flow_error.error_of_msg ~source_file flow_err diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml index c87e628584b..a22123427b7 100644 --- a/src/typing/debug_js.ml +++ b/src/typing/debug_js.ml @@ -1636,6 +1636,8 @@ let dump_error_message = | EUnexpectedTemporaryBaseType loc -> spf "EUnexpectedTemporaryBaseType (%s)" (string_of_aloc loc) | ECannotDelete (l1, r1) -> spf "ECannotDelete (%s, %s)" (string_of_aloc l1) (dump_reason cx r1) + | ESignatureBindingValidation (Signature_error.NameAlreadyBound l) -> + spf "ESignatureBindingValidation (NameAlreadyBound %s)" (string_of_aloc l) | ESignatureVerification sve -> let msg = string_of_signature_error ALoc.debug_to_string sve in spf "ESignatureVerification (%s)" msg diff --git a/src/typing/errors/error_message.ml b/src/typing/errors/error_message.ml index 43d52eef0dd..45389d10f4b 100644 --- a/src/typing/errors/error_message.ml +++ b/src/typing/errors/error_message.ml @@ -378,6 +378,7 @@ and 'loc t' = | EUnnecessaryDeclareTypeOnlyExport of 'loc | EUnexpectedTemporaryBaseType of 'loc | ECannotDelete of 'loc * 'loc virtual_reason + | ESignatureBindingValidation of 'loc Signature_error.binding_validation_t | ESignatureVerification of 'loc Signature_error.t | EPrimitiveAsInterface of { use_op: 'loc virtual_use_op; @@ -1195,6 +1196,8 @@ let rec map_loc_of_error_message (f : 'a -> 'b) : 'a t' -> 'b t' = | EUnnecessaryDeclareTypeOnlyExport loc -> EUnnecessaryDeclareTypeOnlyExport (f loc) | EUnexpectedTemporaryBaseType loc -> EUnexpectedTemporaryBaseType (f loc) | ECannotDelete (l1, r1) -> ECannotDelete (f l1, map_reason r1) + | ESignatureBindingValidation sve -> + ESignatureBindingValidation (Signature_error.map_binding_validation_t f sve) | ESignatureVerification sve -> ESignatureVerification (Signature_error.map f sve) | EPrimitiveAsInterface { use_op; reason; interface_reason; kind } -> EPrimitiveAsInterface @@ -1713,6 +1716,7 @@ let util_use_op_of_msg nope util = function | EUnnecessaryDeclareTypeOnlyExport _ | EUnexpectedTemporaryBaseType _ | ECannotDelete _ + | ESignatureBindingValidation _ | ESignatureVerification _ | EExponentialSpread _ | EComputedPropertyWithUnion _ @@ -1967,6 +1971,11 @@ let loc_of_msg : 'loc t' -> 'loc option = function | EMissingTypeArgs { reason_op; _ } -> Some (loc_of_reason reason_op) | EInvalidRendersTypeArgument { loc; _ } -> Some loc | EInvalidTypeCastSyntax { loc; _ } -> Some loc + | ESignatureBindingValidation e -> + Signature_error.( + (match e with + | NameAlreadyBound loc -> Some loc) + ) | ESignatureVerification sve -> Signature_error.( (match sve with @@ -2616,6 +2625,8 @@ let friendly_message_of_msg = function Normal (MessageCannotExportRenamedDefault { name; is_reexport }) | EUnexpectedTemporaryBaseType _ -> Normal MessageUnexpectedTemporaryBaseType | ECannotDelete (_, expr) -> Normal (MessageCannotDelete expr) + | ESignatureBindingValidation (Signature_error.NameAlreadyBound _) -> + Normal MessageCannotDeclareAlreadyBoundNameInLibdef | ESignatureVerification sve -> Normal (MessageCannotBuildTypedInterface sve) | EUnreachable _ -> Normal MessageUnreachableCode | EInvalidObjectKit { reason; reason_op = _; use_op } -> @@ -3223,6 +3234,7 @@ let error_code_of_message err : error_code option = (* We don't want these to be suppressible *) | ERecursionLimit (_, _) -> None | EROArrayWrite (_, use_op) -> react_rule_of_use_op use_op ~default:CannotWrite + | ESignatureBindingValidation _ -> Some SignatureVerificationFailure | ESignatureVerification _ -> Some SignatureVerificationFailure | EThisInExportedFunction _ -> Some ThisInExportedFunction | EExportRenamedDefault _ -> Some ExportRenamedDefault diff --git a/src/typing/errors/flow_intermediate_error.ml b/src/typing/errors/flow_intermediate_error.ml index fc6618c1ec6..c035f54e303 100644 --- a/src/typing/errors/flow_intermediate_error.ml +++ b/src/typing/errors/flow_intermediate_error.ml @@ -1637,6 +1637,8 @@ let to_printable_error : ] | MessageCannotDeclareAlreadyBoundName x -> [text "Cannot declare "; Friendly.ref x; text " because the name is already bound."] + | MessageCannotDeclareAlreadyBoundNameInLibdef -> + [text "Cannot declare the name in library definition because the name is already bound."] | MessageCannotDelete expr -> [ text "Cannot delete "; diff --git a/src/typing/errors/flow_intermediate_error_types.ml b/src/typing/errors/flow_intermediate_error_types.ml index fe833c9f5bb..ad98ac1ddd1 100644 --- a/src/typing/errors/flow_intermediate_error_types.ml +++ b/src/typing/errors/flow_intermediate_error_types.ml @@ -396,6 +396,7 @@ type 'loc message = | MessageCannotCreateExactType of 'loc virtual_reason | MessageCannotDeclareAlreadyBoundGlobal of concrete_reason | MessageCannotDeclareAlreadyBoundName of concrete_reason + | MessageCannotDeclareAlreadyBoundNameInLibdef | MessageCannotDelete of 'loc virtual_reason | MessageCannotDetermineEmptyArrayLiteralType | MessageCannotDetermineModuleType diff --git a/src/typing/merge_js.ml b/src/typing/merge_js.ml index d36938eb76d..c53305507a8 100644 --- a/src/typing/merge_js.ml +++ b/src/typing/merge_js.ml @@ -884,6 +884,18 @@ let merge_lib_files ~sig_opts ordered_asts = (msg |> Error_message.loc_of_msg |> Base.Option.bind ~f:ALoc.source) in Some (Flow_error.error_of_msg ~source_file msg) + | Type_sig.BindingValidationError e -> + let e = + Signature_error.map_binding_validation_t + (fun l -> l |> Type_sig_collections.Locs.get builtin_locs |> ALoc.of_loc) + e + in + let msg = Error_message.ESignatureBindingValidation e in + let source_file = + Base.Option.value_exn + (msg |> Error_message.loc_of_msg |> Base.Option.bind ~f:ALoc.source) + in + Some (Flow_error.error_of_msg ~source_file msg) | Type_sig.CheckError -> None ) |> Flow_error.ErrorSet.of_list diff --git a/tests/declare_namespace/declare_namespace.exp b/tests/declare_namespace/declare_namespace.exp index 7bff5f83409..251a66ba4db 100644 --- a/tests/declare_namespace/declare_namespace.exp +++ b/tests/declare_namespace/declare_namespace.exp @@ -1,3 +1,19 @@ +Error ------------------------------------------------------------------------------------- flow-typed/namespaces.js:8:8 + +Cannot declare the name in library definition because the name is already bound. [signature-verification-failure] + + 8| type a = string; // error: already bound + ^ + + +Error ------------------------------------------------------------------------------------- flow-typed/namespaces.js:9:8 + +Cannot declare the name in library definition because the name is already bound. [signature-verification-failure] + + 9| type b = string; // error: already bound + ^ + + Error ---------------------------------------------------------------------------------------------- binding_test.js:2:6 Cannot declare `conflict_with_type_decl_1` [1] because the name is already bound. [name-already-bound] @@ -331,6 +347,120 @@ References: ^^^^^^^^^^^ [1] +Error ----------------------------------------------------------------------------------------------- libdef_test.js:1:1 + +Cannot cast `ns_v.a` to empty because string [1] is incompatible with empty [2]. [incompatible-cast] + + libdef_test.js:1:1 + 1| ns_v.a as empty; // error: string ~> empty + ^^^^^^ + +References: + flow-typed/namespaces.js:2:20 + 2| declare const a: string; + ^^^^^^ [1] + libdef_test.js:1:11 + 1| ns_v.a as empty; // error: string ~> empty + ^^^^^ [2] + + +Error ----------------------------------------------------------------------------------------------- libdef_test.js:2:1 + +Cannot cast `ns_v.b` to empty because string [1] is incompatible with empty [2]. [incompatible-cast] + + libdef_test.js:2:1 + 2| ns_v.b as empty; // error: string ~> empty + ^^^^^^ + +References: + flow-typed/namespaces.js:5:20 + 5| declare const b: string; + ^^^^^^ [1] + libdef_test.js:2:11 + 2| ns_v.b as empty; // error: string ~> empty + ^^^^^ [2] + + +Error ----------------------------------------------------------------------------------------------- libdef_test.js:3:1 + +Cannot resolve name `ns_t`. [cannot-resolve-name] + + 3| ns_t; // error: it's a type only namespace + ^^^^ + + +Error ----------------------------------------------------------------------------------------------- libdef_test.js:4:1 + +Cannot cast `1` to `T1` because number [1] is incompatible with string [2]. [incompatible-cast] + + libdef_test.js:4:1 + 4| 1 as ns_t.T1; // error: number ~> string + ^ [1] + +References: + libdef_test.js:4:6 + 4| 1 as ns_t.T1; // error: number ~> string + ^^^^^^^ [2] + + +Error ----------------------------------------------------------------------------------------------- libdef_test.js:5:1 + +Cannot cast `ns_v_and_then_t` to empty because namespace ns_v_and_then_t [1] is incompatible with empty [2]. +[incompatible-cast] + + libdef_test.js:5:1 + 5| ns_v_and_then_t as empty; // error: value namespace ~> empty + ^^^^^^^^^^^^^^^ + +References: + flow-typed/namespaces.js:17:19 + 17| declare namespace ns_v_and_then_t { + ^^^^^^^^^^^^^^^ [1] + libdef_test.js:5:20 + 5| ns_v_and_then_t as empty; // error: value namespace ~> empty + ^^^^^ [2] + + +Error ----------------------------------------------------------------------------------------------- libdef_test.js:6:1 + +Cannot cast `ns_t_and_then_v` to empty because namespace ns_t_and_then_v [1] is incompatible with empty [2]. +[incompatible-cast] + + libdef_test.js:6:1 + 6| ns_t_and_then_v as empty; // error: value namespace ~> empty + ^^^^^^^^^^^^^^^ + +References: + flow-typed/namespaces.js:20:19 + 20| declare namespace ns_t_and_then_v { + ^^^^^^^^^^^^^^^ [1] + libdef_test.js:6:20 + 6| ns_t_and_then_v as empty; // error: value namespace ~> empty + ^^^^^ [2] + + +Error ---------------------------------------------------------------------------------------------- libdef_test.js:8:14 + +Cannot get `non_ns_value.b` because property `b` is missing in `String` [1]. [prop-missing] + + libdef_test.js:8:14 + 8| non_ns_value.b as empty; // error: value namespace ~> empty + ^ + +References: + flow-typed/namespaces.js:30:29 + 30| declare const non_ns_value: string; + ^^^^^^ [1] + + +Error ----------------------------------------------------------------------------------------------- libdef_test.js:9:1 + +Cannot resolve name `non_ns_type`. [cannot-resolve-name] + + 9| non_ns_type; // error: it's a type + ^^^^^^^^^^^ + + Error --------------------------------------------------------------------------------- type_only_react_namespace.js:3:3 Cannot resolve name `React`. [cannot-resolve-name] @@ -777,7 +907,7 @@ References: -Found 56 errors +Found 66 errors Only showing the most relevant union/intersection branches. To see all branches, re-run Flow with --show-all-branches diff --git a/tests/declare_namespace/flow-typed/namespaces.js b/tests/declare_namespace/flow-typed/namespaces.js new file mode 100644 index 00000000000..ef477449c7e --- /dev/null +++ b/tests/declare_namespace/flow-typed/namespaces.js @@ -0,0 +1,44 @@ +declare namespace ns_v { + declare const a: string; +} +declare namespace ns_v { + declare const b: string; +} +declare namespace ns_v { + type a = string; // error: already bound + type b = string; // error: already bound +} +declare namespace ns_t { + type T1 = string; +} +declare namespace ns_t { + type T2 = string; +} +declare namespace ns_v_and_then_t { + declare const a: string; +} +declare namespace ns_t_and_then_v { + type T1 = string; +} +declare namespace ns_v_and_then_t { + type T1 = string; +} +declare namespace ns_t_and_then_v { + declare const a: string; +} + +declare const non_ns_value: string; +type non_ns_type = string; +// The following namespaces won't have any effect +declare namespace non_ns_value { + declare const b: string; +} +declare namespace non_ns_value { + type T1 = string; +} +declare namespace non_ns_type { + declare const b: string; +} +declare namespace non_ns_type { + type T1 = string; +} diff --git a/tests/declare_namespace/libdef_test.js b/tests/declare_namespace/libdef_test.js new file mode 100644 index 00000000000..676445fb939 --- /dev/null +++ b/tests/declare_namespace/libdef_test.js @@ -0,0 +1,10 @@ +ns_v.a as empty; // error: string ~> empty +ns_v.b as empty; // error: string ~> empty +ns_t; // error: it's a type only namespace +1 as ns_t.T1; // error: number ~> string +ns_v_and_then_t as empty; // error: value namespace ~> empty +ns_t_and_then_v as empty; // error: value namespace ~> empty + +non_ns_value.b as empty; // error: value namespace ~> empty +non_ns_type; // error: it's a type +type _t2 = non_ns_type; // ok