diff --git a/lib/core.js b/lib/core.js index 590bc1dc798..d224ecc44ba 100644 --- a/lib/core.js +++ b/lib/core.js @@ -908,3 +908,5 @@ declare var console: { warn(...data: Array): void, ... }; + +declare var globalThis: $GlobalThis; \ No newline at end of file diff --git a/src/commands/commandUtils.ml b/src/commands/commandUtils.ml index 11796e851c0..e7565d167ac 100644 --- a/src/commands/commandUtils.ml +++ b/src/commands/commandUtils.ml @@ -1095,6 +1095,7 @@ let make_options ~flowconfig_name ~flowconfig ~lazy_mode ~root (options_flags: O opt_no_saved_state = options_flags.no_saved_state; opt_arch; opt_abstract_locations; + opt_declaration_merging = FlowConfig.declaration_merging flowconfig; opt_include_suppressions = options_flags.include_suppressions; opt_trust_mode = Option.value options_flags.trust_mode ~default:(FlowConfig.trust_mode flowconfig); opt_recursion_limit = FlowConfig.recursion_limit flowconfig; diff --git a/src/commands/config/flowConfig.ml b/src/commands/config/flowConfig.ml index 475cdabd1da..c2ff410bab0 100644 --- a/src/commands/config/flowConfig.ml +++ b/src/commands/config/flowConfig.ml @@ -43,6 +43,7 @@ module Opts = struct type opt_error = int * error_kind type t = { + declaration_merging: bool; abstract_locations: bool; all: bool; emoji: bool; @@ -134,6 +135,7 @@ module Opts = struct |> SSet.add ".webm" let default_options = { + declaration_merging = false; abstract_locations = false; all = false; emoji = false; @@ -617,6 +619,9 @@ module Opts = struct "experimental.abstract_locations", boolean (fun opts v -> Ok { opts with abstract_locations = v }); + "experimental.declaration_merging", + boolean (fun opts v -> Ok { opts with declaration_merging = v }); + "no_flowlib", boolean (fun opts v -> Ok { opts with no_flowlib = v }); @@ -1014,6 +1019,7 @@ let includes config = config.includes let libs config = config.libs (* options *) +let declaration_merging c = c.options.Opts.declaration_merging let abstract_locations c = c.options.Opts.abstract_locations let all c = c.options.Opts.all let emoji c = c.options.Opts.emoji diff --git a/src/commands/config/flowConfig.mli b/src/commands/config/flowConfig.mli index 45a21a30355..2f46e7aa062 100644 --- a/src/commands/config/flowConfig.mli +++ b/src/commands/config/flowConfig.mli @@ -38,6 +38,7 @@ val includes: config -> string list val libs: config -> string list (* options *) +val declaration_merging: config -> bool val abstract_locations: config -> bool val all: config -> bool val emoji: config -> bool diff --git a/src/common/options.ml b/src/common/options.ml index 6ff71845b17..dac1f311346 100644 --- a/src/common/options.ml +++ b/src/common/options.ml @@ -58,6 +58,7 @@ type trust_mode = | SilentTrust type t = { + opt_declaration_merging : bool; opt_abstract_locations : bool; opt_all : bool; opt_debug : bool; @@ -119,6 +120,7 @@ type t = { opt_type_asserts: bool; } +let declaration_merging opts = opts.opt_declaration_merging let all opts = opts.opt_all let arch opts = opts.opt_arch let max_literal_length opts = opts.opt_max_literal_length diff --git a/src/flow_dot_js.ml b/src/flow_dot_js.ml index 0c346d4b5bb..cf2441bf31c 100644 --- a/src/flow_dot_js.ml +++ b/src/flow_dot_js.ml @@ -143,6 +143,7 @@ let stub_metadata ~root ~checked = { Context. include_suppressions = false; (* global *) + declaration_merging = false; max_literal_length = 100; enable_const_params = false; enable_enums = true; diff --git a/src/services/inference/init_js.ml b/src/services/inference/init_js.ml index baeb4219d50..dd13c6f0b90 100644 --- a/src/services/inference/init_js.ml +++ b/src/services/inference/init_js.ml @@ -66,10 +66,17 @@ let parse_lib_file ~reader options file = let load_lib_files ~sig_cx ~options ~reader files = let verbose = Options.verbose options in + let declaration_merging = Options.declaration_merging options in + + let files = if declaration_merging then + files + else + List.rev files + in (* iterate in reverse override order *) let%lwt (_, result) = - List.rev files + files |> Lwt_list.fold_left_s ( fun (exclude_syms, results) file -> diff --git a/src/typing/context.ml b/src/typing/context.ml index b07ecd26f83..d321ed66f3e 100644 --- a/src/typing/context.ml +++ b/src/typing/context.ml @@ -29,6 +29,7 @@ type metadata = { strict_local: bool; (* global *) + declaration_merging: bool; max_literal_length: int; enable_const_params: bool; enable_enums: bool; @@ -156,6 +157,7 @@ let metadata_of_options options = { strict_local = false; (* global *) + declaration_merging = Options.declaration_merging options; max_literal_length = Options.max_literal_length options; enable_const_params = Options.enable_const_params options; enable_enums = Options.enums options; @@ -271,6 +273,7 @@ let trust_constructor cx = cx.trust_constructor let cx_with_trust cx trust = { cx with trust_constructor = trust } let metadata cx = cx.metadata +let declaration_merging cx = cx.metadata.declaration_merging let max_literal_length cx = cx.metadata.max_literal_length let enable_const_params cx = cx.metadata.enable_const_params || cx.metadata.strict || cx.metadata.strict_local diff --git a/src/typing/context.mli b/src/typing/context.mli index 68c8b2cbf59..29af57a8ef9 100644 --- a/src/typing/context.mli +++ b/src/typing/context.mli @@ -29,6 +29,7 @@ type metadata = { strict: bool; strict_local: bool; (* global *) + declaration_merging: bool; max_literal_length: int; enable_const_params: bool; enable_enums: bool; @@ -71,6 +72,7 @@ val find_module_sig: sig_t -> string -> Type.t (* accessors *) val all_unresolved: t -> ISet.t IMap.t val metadata: t -> metadata +val declaration_merging: t -> bool val max_literal_length: t -> int val enable_const_params: t -> bool val enable_enums: t -> bool diff --git a/src/typing/coverage.ml b/src/typing/coverage.ml index a233fdbbefe..c664af782b7 100644 --- a/src/typing/coverage.ml +++ b/src/typing/coverage.ml @@ -189,6 +189,7 @@ class visitor = object (self) | FunProtoApplyT _ | FunProtoBindT _ | FunProtoCallT _ + | GlobalThisT _ | InternalT _ | KeysT _ | MaybeT _ diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml index c4722c28113..03395bb32cc 100644 --- a/src/typing/debug_js.ml +++ b/src/typing/debug_js.ml @@ -173,6 +173,7 @@ and _json_of_t_impl json_cx t = Hh_json.( | FunProtoApplyT _ | FunProtoBindT _ | FunProtoCallT _ + | GlobalThisT _ -> [] | DefT (_, _, FunT (static, proto, funtype)) -> [ @@ -497,6 +498,7 @@ and _json_of_use_t_impl json_cx t = Hh_json.( ] | SetPropT (_, _, name, _, t, _) + | OverwritePropT (_, _, name, t) | GetPropT (_, _, name, t) | MatchPropT (_, _, name, t) | TestPropT (_, _, name, t) -> [ @@ -561,6 +563,11 @@ and _json_of_use_t_impl json_cx t = Hh_json.( "rightType", _json_of_t json_cx r ] + | MergeTypesT (_, _, _, _, l, r) -> [ + "leftType", _json_of_t json_cx l; + "rightType", _json_of_t json_cx r + ] + | ComparatorT (_, _, t) -> [ "type", _json_of_t json_cx t ] @@ -1796,7 +1803,8 @@ let rec dump_t_ (depth, tvars) cx t = | FunProtoT _ | FunProtoApplyT _ | FunProtoBindT _ - | FunProtoCallT _ -> p t + | FunProtoCallT _ + | GlobalThisT _ -> p t | DefT (_, trust, PolyT (_, tps, c, id)) -> p ~trust:(Some trust) ~extra:(spf "%s [%s] #%d" (kid c) (String.concat "; " (Core_list.map ~f:(fun tp -> tp.name) (Nel.to_list tps))) @@ -2097,6 +2105,9 @@ and dump_use_t_ (depth, tvars) cx t = (if Context.trust_tracking cx then string_of_trust_rep (lookup_trust cx) trust else "") (kid t) | UseT (use_op, t) -> spf "UseT (%s, %s)" (string_of_use_op use_op) (kid t) + | MergeTypesT (_, _, _, _, x, y) -> p ~extra:(spf "%s, %s" + (kid x) + (kid y)) t | AdderT (use_op, _, _, x, y) -> p ~extra:(spf "%s, %s, %s" (string_of_use_op use_op) (kid x) @@ -2229,6 +2240,10 @@ and dump_use_t_ (depth, tvars) cx t = (string_of_use_op use_op) (propref prop) (kid ptype)) t + | OverwritePropT (use_op, _, prop, ptype) -> p ~extra:(spf "%s, (%s), %s" + (string_of_use_op use_op) + (propref prop) + (kid ptype)) t | SetPrivatePropT (_, _, prop, _, _, ptype, _) -> p ~extra:(spf "(%s), %s" (prop) (kid ptype)) t diff --git a/src/typing/env.ml b/src/typing/env.ml index e9423a8504d..ad1eebb41d4 100644 --- a/src/typing/env.ml +++ b/src/typing/env.ml @@ -274,7 +274,9 @@ let trunc_env = (* initialize a new environment (once per module) *) let init_env ?(exclude_syms=SSet.empty) cx module_scope = - set_exclude_symbols exclude_syms; + if not (Context.declaration_merging cx) then + set_exclude_symbols exclude_syms; + havoc_current_activation (); let global_scope = Scope.fresh ~var_scope_kind:Global () in push_var_scope cx global_scope; diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml index 27aa6780f33..200f0c10812 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -3732,6 +3732,9 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = let reason = reason_of_t l in rec_flow cx trace (l, UseT (use_op, fix_this_class cx trace reason (r, i))) + | l, MergeTypesT (reason, flip, propref, original_t, r, u) -> + merge_builtin cx trace reason flip propref original_t l r u + (** This rule is hit when a polymorphic type appears outside a type application expression - i.e. not followed by a type argument list delimited by angle brackets. @@ -5244,6 +5247,13 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = Option.iter ~f:(fun t -> rec_flow_t cx trace (AnyT.untyped reason_op, t)) prop_t; rec_flow cx trace (t, UseT (use_op, AnyT.untyped reason_op)) + | DefT (_, _, ObjT { props_tmap; _ }), OverwritePropT (_, _, Named (_, name), t) -> + let p = Field (None, t, Polarity.Neutral) in + Context.set_prop cx props_tmap name p; + + | ModuleT (_, { exports_tmap; _ }, _), OverwritePropT (_, _, Named (_, name), t) -> + Context.set_export cx exports_tmap name (None, t); + | DefT (reason_obj, _, ObjT o), MatchPropT (use_op, reason_op, propref, proptype) -> match_obj_prop cx trace ~use_op o propref reason_obj reason_op proptype @@ -6522,6 +6532,14 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = | FunProtoCallT reason, _ -> rec_flow cx trace (FunProtoT reason, u) + | GlobalThisT _, + SetPropT (_, _, Named (_, name), _, tin, _) -> + overwrite_builtin cx ~trace name tin; + + | GlobalThisT _, _ -> + let t = builtins cx in + rec_flow cx trace (t, u) + | _, LookupT (_, _, _, propref, lookup_action) -> let use_op = use_op_of_lookup_action lookup_action in add_output cx ~trace (Error_message.EIncompatibleProp { @@ -6932,6 +6950,7 @@ and empty_success flavor u = either by specially propagating it or selecting cases, etc. *) | _, UseT (_, ExactT _) | _, AdderT _ + | _, MergeTypesT _ | _, AndT _ | _, OrT _ (* Propagation cases: these cases don't use the fact that the LHS is @@ -7037,6 +7056,7 @@ and empty_success flavor u = | _, SetElemT _ | _, SetPrivatePropT _ | _, SetPropT _ + | _, OverwritePropT _ | _, SpecializeT _ | _, SubstOnPredT _ | _, SuperT _ @@ -7132,6 +7152,7 @@ and any_propagated cx trace any u = true | AdderT _ + | MergeTypesT _ | AndT _ | ArrRestT _ | BecomeT _ @@ -7192,6 +7213,7 @@ and any_propagated cx trace any u = | SentinelPropTestT _ | SetElemT _ | SetPropT _ + | OverwritePropT _ | ModuleExportsAssignT _ | SpecializeT _ | SubstOnPredT _ (* Should be impossible. We only generate these with OpenPredTs. *) @@ -7308,7 +7330,8 @@ and any_propagated_use cx trace use_op any l = | FunProtoCallT _ | FunProtoT _ | ObjProtoT _ - | NullProtoT _ -> + | NullProtoT _ + | GlobalThisT _ -> true (* Handled already in __flow *) @@ -7729,6 +7752,7 @@ and check_polarity cx ?trace polarity = function | FunProtoApplyT _ | FunProtoBindT _ | FunProtoCallT _ + | GlobalThisT _ | EvalT _ | InternalT (ExtendsT _) | InternalT (ChoiceKitT _) @@ -11112,10 +11136,73 @@ and extract_non_spread cx ~trace = function add_output cx ~trace (Error_message.(EUnsupportedSyntax (loc, SpreadArgument))); AnyT.error reason +and merge_builtin cx trace reason flip propref original_t l r u = + let string_reason = string_of_reason reason in + ignore string_reason; + if needs_resolution r then begin + (* TODO: this doesn't work without writing to property first *) + rec_flow cx trace (u, OverwritePropT (unknown_use, reason, propref, l)); + (* *) + + rec_flow cx trace (r, MergeTypesT (reason, not flip, propref, original_t, l, u)) + end else begin + let (l, r) = if flip then (r, l) else (l, r) in + begin match l, r with + + (* polymorphic interface ~> polymorphic class *) + | DefT (_, _, PolyT (_, _, DefT (_, _, ClassT (DefT (_, _, InstanceT (_, _, _, { structural = true; own_props = o1_id; _ })))), _)), + DefT (_, _, PolyT (_, _, ThisClassT (_, DefT (_, _, InstanceT (_, _, _, { structural = false; own_props = o2_id; _ }))), _)) -> + let o1 = Context.find_props cx o1_id in + let o2 = Context.find_props cx o2_id in + ignore o1; + ignore o2; + Context.add_property_map cx o2_id (SMap.union o1 o2); + + (* interface ~> class *) + | DefT (_, _, ClassT (DefT (_, _, InstanceT (_, _, _, { structural = true; own_props = o1_id; _ })))), + ThisClassT (_, DefT (_, _, InstanceT (_, _, _, { structural = false; own_props = o2_id; _ }))) -> + let o1 = Context.find_props cx o1_id in + let o2 = Context.find_props cx o2_id in + ignore o1; + ignore o2; + Context.add_property_map cx o2_id (SMap.union o1 o2); + + (* module ~> module *) + | ModuleT (_, {exports_tmap = exports1_id; _}, _), ModuleT (_, {exports_tmap = exports2_id; _}, _) -> + let exports1 = Context.find_exports cx exports1_id in + let exports2 = Context.find_exports cx exports2_id in + let exports = SMap.union exports1 exports2 in + Context.add_export_map cx exports2_id exports; + ignore @@ SMap.union ~combine:(fun key (_, t1) (_, t2) -> + rec_flow cx trace (t2, MergeTypesT (reason, false, Named (reason_of_t t1, key), t2, t1, r)); + None + ) exports1 exports2; + () + + (* any ~> any *) + | _, _ -> + () + end + end + and set_builtin cx ?trace x t = + let reason = builtin_reason (RCustom x) in + let obj = builtins cx in + let propref = Named (reason, x) in + let old_t = Tvar.mk_derivable_where cx reason (fun t -> + flow_opt cx ?trace (obj, GetPropT (unknown_use, reason, propref, t)); + ) + in + if Context.declaration_merging cx then + flow_opt cx ?trace (t, MergeTypesT (reason, false, propref, t, old_t, obj)) + else + flow_opt cx ?trace (obj, SetPropT (unknown_use, reason, propref, Normal, t, None)) + +and overwrite_builtin cx ?trace x t = let reason = builtin_reason (RCustom x) in let propref = Named (reason, x) in - flow_opt cx ?trace (builtins cx, SetPropT (unknown_use, reason, propref, Normal, t, None)) + let obj = builtins cx in + flow_opt cx ?trace (obj, OverwritePropT (unknown_use, reason, propref, t)) (* Wrapper functions around __flow that manage traces. Use these functions for all recursive calls in the implementation of __flow. *) diff --git a/src/typing/members.ml b/src/typing/members.ml index efbe9420eb4..a33b2390647 100644 --- a/src/typing/members.ml +++ b/src/typing/members.ml @@ -431,6 +431,7 @@ let rec extract_type cx this_t = match this_t with | FunProtoBindT _ | FunProtoCallT _ | FunProtoT _ + | GlobalThisT _ | KeysT (_, _) | DefT (_, _, MixedT _) | NullProtoT _ diff --git a/src/typing/resolvableTypeJob.ml b/src/typing/resolvableTypeJob.ml index b2381056218..20bb67a7cdf 100644 --- a/src/typing/resolvableTypeJob.ml +++ b/src/typing/resolvableTypeJob.ml @@ -251,6 +251,7 @@ and collect_of_type ?log_unresolved cx acc = function | FunProtoT _ | NullProtoT _ | ObjProtoT _ + | GlobalThisT _ | CustomFunT (_, _) | ExistsT _ diff --git a/src/typing/sigHash.ml b/src/typing/sigHash.ml index a119ac8895a..34299687193 100644 --- a/src/typing/sigHash.ml +++ b/src/typing/sigHash.ml @@ -48,6 +48,7 @@ type hash = | FunProtoApplyH | FunProtoBindH | FunProtoCallH + | GlobalThisH | ObjH | ObjProtoH | MatchingPropH @@ -86,6 +87,7 @@ type hash = | CallH | MethodH | SetPropH + | OverwritePropH | SetPrivatePropH | GetPropH | MatchPropH @@ -103,6 +105,7 @@ type hash = | SuperH | ImplementsH | MixinH + | MergeTypesH | AdderH | ComparatorH | UnaryMinusH @@ -222,6 +225,7 @@ let hash_of_ctor = Type.(function | FunProtoApplyT _ -> FunProtoApplyH | FunProtoBindT _ -> FunProtoBindH | FunProtoCallT _ -> FunProtoCallH + | GlobalThisT _ -> GlobalThisH | IntersectionT _ -> IntersectionH | KeysT _ -> KeysH | MaybeT _ -> MaybeH @@ -246,6 +250,7 @@ let hash_of_use_ctor = Type.(function | CallT _ -> CallH | MethodT _ -> MethodH | SetPropT _ -> SetPropH + | OverwritePropT _ -> OverwritePropH | SetPrivatePropT _ -> SetPrivatePropH | GetPropT _ -> GetPropH | MatchPropT _ -> MatchPropH @@ -263,6 +268,7 @@ let hash_of_use_ctor = Type.(function | SuperT _ -> SuperH | ImplementsT _ -> ImplementsH | MixinT _ -> MixinH + | MergeTypesT _ -> MergeTypesH | AdderT _ -> AdderH | ComparatorT _ -> ComparatorH | UnaryMinusT _ -> UnaryMinusH diff --git a/src/typing/ty_normalizer.ml b/src/typing/ty_normalizer.ml index cafdaff6951..65f514672a4 100644 --- a/src/typing/ty_normalizer.ml +++ b/src/typing/ty_normalizer.ml @@ -705,6 +705,7 @@ end = struct | ModuleT (reason, _, _) -> module_t env reason t | DefT (_, _, CharSetT _) + | GlobalThisT _ | NullProtoT _ -> terr ~kind:UnsupportedTypeCtor (Some t) diff --git a/src/typing/type.ml b/src/typing/type.ml index f53a38b1ce8..8e40382d6aa 100644 --- a/src/typing/type.ml +++ b/src/typing/type.ml @@ -86,7 +86,7 @@ module rec TypeTerm : sig (* exact *) | ExactT of reason * t - | FunProtoT of reason (* Function.prototype *) + | FunProtoT of reason (* Function.prototype *) | ObjProtoT of reason (* Object.prototype *) (* Signifies the end of the prototype chain. Distinct from NullT when it @@ -97,6 +97,8 @@ module rec TypeTerm : sig | FunProtoBindT of reason (* Function.prototype.bind *) | FunProtoCallT of reason (* Function.prototype.call *) + | GlobalThisT of reason (* globalThis *) + (* generalizations of AnyT *) | AnyWithLowerBoundT of t (* any supertype of t *) | AnyWithUpperBoundT of t (* any subtype of t *) @@ -363,6 +365,7 @@ module rec TypeTerm : sig | MethodT of use_op * (* call *) reason * (* lookup *) reason * propref * funcalltype * t option (* Similar to the last element of the MethodT *) | SetPropT of use_op * reason * propref * write_ctx * t * t option + | OverwritePropT of use_op * reason * propref * t (* The boolean flag indicates whether or not it is a static lookup. We cannot know this when * we generate the constraint, since the lower bound may be an unresolved OpenT. If it * resolves to a ClassT, we flip the flag to true, which causes us to check the private static @@ -398,6 +401,8 @@ module rec TypeTerm : sig | MixinT of reason * t | ToStringT of reason * use_t + | MergeTypesT of reason * bool (* flip *) * propref * t * t * t + (* overloaded +, could be subsumed by general overloading *) | AdderT of use_op * reason * bool * t * t (* overloaded relational operator, could be subsumed by general @@ -2143,6 +2148,7 @@ end = struct | FunProtoApplyT reason -> reason | FunProtoBindT reason -> reason | FunProtoCallT reason -> reason + | GlobalThisT reason -> reason | KeysT (reason, _) -> reason | ModuleT (reason, _, _) -> reason | NullProtoT reason -> reason @@ -2171,6 +2177,7 @@ end = struct and reason_of_use_t = function | UseT (_, t) -> reason_of_t t | AdderT (_,reason,_,_,_) -> reason + | MergeTypesT (reason,_,_,_,_,_) -> reason | AndT (reason, _, _) -> reason | ArrRestT (_, reason, _, _) -> reason | AssertArithmeticOperandT reason -> reason @@ -2243,6 +2250,7 @@ end = struct | SentinelPropTestT (_, _, _, _, _, result) -> reason_of_t result | SetElemT (_,reason,_,_,_) -> reason | SetPropT (_,reason,_,_,_,_) -> reason + | OverwritePropT (_,reason,_,_) -> reason | SetPrivatePropT (_,reason,_,_,_,_,_) -> reason | SetProtoT (reason,_) -> reason | SpecializeT(_,_,reason,_,_,_) -> reason @@ -2307,6 +2315,7 @@ end = struct | FunProtoT (reason) -> FunProtoT (f reason) | FunProtoBindT (reason) -> FunProtoBindT (f reason) | FunProtoCallT (reason) -> FunProtoCallT (f reason) + | GlobalThisT (reason) -> GlobalThisT (f reason) | KeysT (reason, t) -> KeysT (f reason, t) | ModuleT (reason, exports, is_strict) -> ModuleT (f reason, exports, is_strict) | NullProtoT reason -> NullProtoT (f reason) @@ -2329,6 +2338,7 @@ end = struct and mod_reason_of_use_t f = function | UseT (_, t) -> UseT (Op UnknownUse, mod_reason_of_t f t) | AdderT (use_op, reason, flip, rt, lt) -> AdderT (use_op, f reason, flip, rt, lt) + | MergeTypesT (reason, flip, propref, t, rt, lt) -> MergeTypesT (f reason, flip, propref, t, rt, lt) | AndT (reason, t1, t2) -> AndT (f reason, t1, t2) | ArrRestT (use_op, reason, i, t) -> ArrRestT (use_op, f reason, i, t) | AssertArithmeticOperandT reason -> AssertArithmeticOperandT (f reason) @@ -2419,6 +2429,7 @@ end = struct SentinelPropTestT (reason_op, l, key, sense, sentinel, mod_reason_of_t f result) | SetElemT (use_op, reason, it, et, t) -> SetElemT (use_op, f reason, it, et, t) | SetPropT (use_op, reason, n, i, t, tp) -> SetPropT (use_op, f reason, n, i, t, tp) + | OverwritePropT (use_op, reason, n, i) -> OverwritePropT (use_op, f reason, n, i) | SetPrivatePropT (use_op, reason, n, scopes, static, t, tp) -> SetPrivatePropT (use_op, f reason, n, scopes, static, t, tp) | SetProtoT (reason, t) -> SetProtoT (f reason, t) @@ -2473,6 +2484,7 @@ end = struct | CallT (op, r, f) -> util op (fun op -> CallT (op, r, f)) | MethodT (op, r1, r2, p, f, tm) -> util op (fun op -> MethodT (op, r1, r2, p, f, tm)) | SetPropT (op, r, p, w, t, tp) -> util op (fun op -> SetPropT (op, r, p, w, t, tp)) + | OverwritePropT (op, r, p, w) -> util op (fun op -> OverwritePropT (op, r, p, w)) | SetPrivatePropT (op, r, s, c, b, t, tp) -> util op (fun op -> SetPrivatePropT (op, r, s, c, b, t, tp)) | GetPropT (op, r, p, t) -> util op (fun op -> GetPropT (op, r, p, t)) @@ -2561,6 +2573,7 @@ end = struct | SubstOnPredT (_, _, _) | RefineT (_, _, _) | CondT (_, _, _, _) + | MergeTypesT (_, _, _, _, _, _) | ReactPropsToOut _ | ReactInToProps _ | DestructuringT _ @@ -3118,6 +3131,7 @@ let string_of_ctor = function | FunProtoApplyT _ -> "FunProtoApplyT" | FunProtoBindT _ -> "FunProtoBindT" | FunProtoCallT _ -> "FunProtoCallT" + | GlobalThisT _ -> "GlobalThisT" | KeysT _ -> "KeysT" | ModuleT _ -> "ModuleT" | NullProtoT _ -> "NullProtoT" @@ -3197,6 +3211,7 @@ let string_of_use_op_rec : use_op -> string = let string_of_use_ctor = function | UseT (op, t) -> spf "UseT(%s, %s)" (string_of_use_op op) (string_of_ctor t) + | MergeTypesT _ -> "MergeTypesT" | AdderT _ -> "AdderT" | AndT _ -> "AndT" | ArrRestT _ -> "ArrRestT" @@ -3290,6 +3305,7 @@ let string_of_use_ctor = function | SentinelPropTestT _ -> "SentinelPropTestT" | SetElemT _ -> "SetElemT" | SetPropT _ -> "SetPropT" + | OverwritePropT _ -> "OverwritePropT" | MatchPropT _ -> "MatchPropT" | SetPrivatePropT _ -> "SetPrivatePropT" | SetProtoT _ -> "SetProtoT" diff --git a/src/typing/type_annotation.ml b/src/typing/type_annotation.ml index 746fdb97f20..33fa0eb921e 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -851,6 +851,12 @@ let rec convert cx tparams_map = Ast.Type.(function reconstruct_ast (AnyT.make Annotated reason) None ) + | "$GlobalThis" -> + check_type_arg_arity cx loc t_ast targs 0 (fun () -> + let reason = mk_reason (RCustom "global this") loc in + reconstruct_ast (GlobalThisT reason) None + ) + | "Function$Prototype$Apply" -> check_type_arg_arity cx loc t_ast targs 0 (fun () -> let reason = mk_reason RFunctionType loc in diff --git a/src/typing/type_mapper.ml b/src/typing/type_mapper.ml index 04f7ce8c309..04e80751f62 100644 --- a/src/typing/type_mapper.ml +++ b/src/typing/type_mapper.ml @@ -107,7 +107,8 @@ class virtual ['a] t = object(self) | NullProtoT _ | FunProtoApplyT _ | FunProtoBindT _ - | FunProtoCallT _ -> t + | FunProtoCallT _ + | GlobalThisT _ -> t | AnyWithLowerBoundT t' -> let t'' = self#type_ cx map_cx t' in if t'' == t' then t @@ -618,6 +619,11 @@ class virtual ['a] t_with_uses = object(self) let prop_t' = OptionUtils.ident_map (self#type_ cx map_cx) prop_t in if prop' == prop && t'' == t' && prop_t' == prop_t then t else SetPropT (use_op, r, prop', i, t'', prop_t') + | OverwritePropT (use_op, r, prop, t') -> + let prop' = self#prop_ref cx map_cx prop in + let t'' = self#type_ cx map_cx t' in + if prop' == prop && t'' == t' then t + else OverwritePropT (use_op, r, prop', t'') | SetPrivatePropT (use_op, r, prop, scopes, static, t', prop_t) -> let t'' = self#type_ cx map_cx t' in let scopes' = ListUtils.ident_map (self#class_binding cx map_cx) scopes in @@ -704,6 +710,12 @@ class virtual ['a] t_with_uses = object(self) let t'' = self#use_type cx map_cx t' in if t'' == t' then t else ToStringT (r, t'') + | MergeTypesT (r, flip, propref, t', t1, t2) -> + let t'' = self#type_ cx map_cx t' in + let t1' = self#type_ cx map_cx t1 in + let t2' = self#type_ cx map_cx t2 in + if t1' == t1 && t2' == t2 && t'' == t' then t + else MergeTypesT (r, flip, propref, t'', t1', t2') | AdderT (op, r, flip, t1, t2) -> let t1' = self#type_ cx map_cx t1 in let t2' = self#type_ cx map_cx t2 in diff --git a/src/typing/type_visitor.ml b/src/typing/type_visitor.ml index fc14976c7e4..d8a07a27947 100644 --- a/src/typing/type_visitor.ml +++ b/src/typing/type_visitor.ml @@ -37,6 +37,7 @@ class ['a] t = object(self) | FunProtoCallT _ | ObjProtoT _ | NullProtoT _ + | GlobalThisT _ -> acc | CustomFunT (_, kind) -> self#custom_fun_kind cx acc kind @@ -279,6 +280,11 @@ class ['a] t = object(self) let acc = self#opt (self#type_ cx pole_TODO) acc prop_t in acc + | OverwritePropT (_, _, p, t) -> + let acc = self#propref cx acc p in + let acc = self#type_ cx pole_TODO acc t in + acc + | SetPropT (_, _, p, _, t, prop_t) -> let acc = self#propref cx acc p in let acc = self#type_ cx pole_TODO acc t in @@ -343,6 +349,11 @@ class ['a] t = object(self) | MixinT (_, t) -> self#type_ cx pole_TODO acc t | ToStringT (_, t) -> self#use_type_ cx acc t + | MergeTypesT (_, _, _, _, a, b) -> + let acc = self#type_ cx pole_TODO acc a in + let acc = self#type_ cx pole_TODO acc b in + acc + | AdderT (_, _, _, a, b) -> let acc = self#type_ cx pole_TODO acc a in let acc = self#type_ cx pole_TODO acc b in diff --git a/tests/lib_declaration_merging/.flowconfig b/tests/lib_declaration_merging/.flowconfig new file mode 100644 index 00000000000..8ed8835814e --- /dev/null +++ b/tests/lib_declaration_merging/.flowconfig @@ -0,0 +1,3 @@ +[options] +no_flowlib=true +experimental.declaration_merging=true diff --git a/tests/lib_declaration_merging/flow-typed/00_wat_module.js b/tests/lib_declaration_merging/flow-typed/00_wat_module.js new file mode 100644 index 00000000000..c61fe55c133 --- /dev/null +++ b/tests/lib_declaration_merging/flow-typed/00_wat_module.js @@ -0,0 +1,7 @@ +// This would fail if order of files is different + +declare module "wat" { + declare class Bar { + foo: number; + } +} diff --git a/tests/lib_declaration_merging/flow-typed/array_override.js b/tests/lib_declaration_merging/flow-typed/array_override.js new file mode 100644 index 00000000000..aba8794592b --- /dev/null +++ b/tests/lib_declaration_merging/flow-typed/array_override.js @@ -0,0 +1,3 @@ +interface Array { + bar: string; +} diff --git a/tests/lib_declaration_merging/flow-typed/foo_override.js b/tests/lib_declaration_merging/flow-typed/foo_override.js new file mode 100644 index 00000000000..e38fd66a634 --- /dev/null +++ b/tests/lib_declaration_merging/flow-typed/foo_override.js @@ -0,0 +1,6 @@ +declare module 'foo' { + // Doesn't work since Node.js modules are orthogonal to `declare module` + declare export interface Foo { + b: string; + } +} diff --git a/tests/lib_declaration_merging/flow-typed/override.js b/tests/lib_declaration_merging/flow-typed/override.js new file mode 100644 index 00000000000..f7ad9abb273 --- /dev/null +++ b/tests/lib_declaration_merging/flow-typed/override.js @@ -0,0 +1,7 @@ +// This would fail if order of files is different + +declare module 'wat' { + declare interface Bar { + bar: string; + } +} diff --git a/tests/lib_declaration_merging/index.js b/tests/lib_declaration_merging/index.js new file mode 100644 index 00000000000..6252c1ae62c --- /dev/null +++ b/tests/lib_declaration_merging/index.js @@ -0,0 +1,12 @@ +// @flow + +import { Bar } from "wat"; + +declare var wat: Bar; + +;(wat.foo: number); // ok +;(wat.bar: string); // ok + +const foo = [1, 2, 3]; + +(foo.bar: string); // ok diff --git a/tests/lib_declaration_merging/lib_declaration_merging.exp b/tests/lib_declaration_merging/lib_declaration_merging.exp new file mode 100644 index 00000000000..41a36506ef7 --- /dev/null +++ b/tests/lib_declaration_merging/lib_declaration_merging.exp @@ -0,0 +1,16 @@ +Error ----------------------------------------------------------------------------------------- test_node_modules.js:8:1 + +Cannot get `foo.b` because property `b` is missing in `Foo` [1]. + + test_node_modules.js:8:1 + 8| foo.b; // error + ^^^^^ + +References: + test_node_modules.js:5:18 + 5| declare var foo: Foo; + ^^^ [1] + + + +Found 1 error diff --git a/tests/lib_declaration_merging/node_modules/foo/index.js.flow b/tests/lib_declaration_merging/node_modules/foo/index.js.flow new file mode 100644 index 00000000000..719cf9a9ce0 --- /dev/null +++ b/tests/lib_declaration_merging/node_modules/foo/index.js.flow @@ -0,0 +1,7 @@ +// @flow + +// Node.js module + +declare export class Foo { + a: number; +} diff --git a/tests/lib_declaration_merging/node_modules/foo/package.json b/tests/lib_declaration_merging/node_modules/foo/package.json new file mode 100644 index 00000000000..3a8d4114836 --- /dev/null +++ b/tests/lib_declaration_merging/node_modules/foo/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "main": "./index.js.flow" +} diff --git a/tests/lib_declaration_merging/test_node_modules.js b/tests/lib_declaration_merging/test_node_modules.js new file mode 100644 index 00000000000..8321b2c98d2 --- /dev/null +++ b/tests/lib_declaration_merging/test_node_modules.js @@ -0,0 +1,10 @@ +// @flow + +import {Foo} from 'foo' + +declare var foo: Foo; + +foo.a; + +// Doesn't work since Node.js modules are orthogonal to `declare module` +foo.b; // error