diff --git a/src/typing/codegen.ml b/src/typing/codegen.ml index 93ddf84ba02..14744cf2ea0 100644 --- a/src/typing/codegen.ml +++ b/src/typing/codegen.ml @@ -310,6 +310,7 @@ let rec gen_type t env = Type.( | DefT (_, TypeT t) -> gen_type t env | DefT (_, UnionT union) -> gen_union_list union env | DefT (_, VoidT) -> add_str "void" env + | InternalT (OptionalChainVoidT _) -> add_str "void" env (** * These types can't be expressed in code well so we fail back to `mixed`. diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml index cff03c21d00..4a0fda0938f 100644 --- a/src/typing/debug_js.ml +++ b/src/typing/debug_js.ml @@ -344,6 +344,7 @@ and _json_of_t_impl json_cx t = Hh_json.( "type", _json_of_t json_cx t ] + | InternalT (OptionalChainVoidT _) -> [] ) ) @@ -1793,6 +1794,7 @@ let rec dump_t_ (depth, tvars) cx t = ) (Key_map.elements p_neg)))) | ReposT (_, arg) | InternalT (ReposUpperT (_, arg)) -> p ~extra:(kid arg) t + | InternalT (OptionalChainVoidT _) -> p t and dump_use_t_ (depth, tvars) cx t = diff --git a/src/typing/flow_error.ml b/src/typing/flow_error.ml index 39fe6b52329..e4046f924df 100644 --- a/src/typing/flow_error.ml +++ b/src/typing/flow_error.ml @@ -2003,7 +2003,8 @@ let rec error_of_msg ~trace_reasons ~source_file = | EUnnecessaryOptionalChain (loc, lhs_reason) -> mk_error ~trace_infos ~kind:(LintError Lints.UnnecessaryOptionalChain) loc [ text "This use of optional chaining ("; code "?."; text ") is unnecessary because "; - ref lhs_reason; text " cannot be null or undefined." + ref lhs_reason; text " cannot be nullish or because an earlier "; code "?."; + text " will short-circuit the nullish case."; ] | EInexactSpread (reason, reason_op) -> diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml index 874fd51a682..8054165fb4d 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -1031,6 +1031,8 @@ module ResolvableTypeJob = struct | InternalT (ReposUpperT (_, t)) -> collect_of_type ?log_unresolved cx reason acc t + | InternalT (OptionalChainVoidT _) -> acc + | DefT (_, NumT _) | DefT (_, StrT _) | DefT (_, BoolT _) @@ -2371,7 +2373,11 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = | DefT (r, (NullT | VoidT)), OptionalChainT (r', lhs_reason, chain) -> Context.mark_optional_chain cx (loc_of_reason r') lhs_reason ~useful:true; - Nel.iter (fun (_, t_out) -> rec_flow_t cx trace (DefT (r, VoidT), t_out)) chain; + Nel.iter (fun (_, t_out) -> rec_flow_t cx trace (InternalT (OptionalChainVoidT r), t_out)) chain; + + | InternalT (OptionalChainVoidT _), OptionalChainT (r', lhs_reason, chain) -> + Context.mark_optional_chain cx (loc_of_reason r') lhs_reason ~useful:false; + Nel.iter (fun (_, t_out) -> rec_flow_t cx trace (l, t_out)) chain; | _, OptionalChainT (r', lhs_reason, chain) when ( match l with @@ -2391,6 +2397,9 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = lhs_t := t_out'; ) chain; + | InternalT (OptionalChainVoidT r), u -> + rec_flow cx trace (DefT (r, VoidT), u); + (***************) (* maybe types *) (***************) @@ -7306,6 +7315,8 @@ and check_polarity cx ?trace polarity = function | ExistsT _ -> () + | InternalT (OptionalChainVoidT _) -> () + | DefT (_, OptionalT t) | ExactT (_, t) | DefT (_, MaybeT t) @@ -11752,7 +11763,8 @@ end = struct let rec extract_type cx this_t = match this_t with | DefT (_, MaybeT ty) -> extract_type cx ty - | DefT (_, (NullT | VoidT)) -> + | DefT (_, (NullT | VoidT)) + | InternalT (OptionalChainVoidT _) -> FailureNullishType | DefT (_, AnyT) -> FailureAnyType diff --git a/src/typing/ty_normalizer.ml b/src/typing/ty_normalizer.ml index dd65670aa47..8a378054b6c 100644 --- a/src/typing/ty_normalizer.ml +++ b/src/typing/ty_normalizer.ml @@ -1217,6 +1217,7 @@ end = struct | ExtendsT _ | ReposUpperT _ -> terr ~kind:BadInternalT (Some t) + | OptionalChainVoidT r -> type__ ~env (DefT (r, VoidT)); and param_bound ~env = function | T.DefT (_, T.MixedT _) -> return None diff --git a/src/typing/type.ml b/src/typing/type.ml index dec317563b0..05cf175207a 100644 --- a/src/typing/type.ml +++ b/src/typing/type.ml @@ -274,6 +274,7 @@ module rec TypeTerm : sig function *) | IdxWrapper of reason * t | ReposUpperT of reason * t + | OptionalChainVoidT of reason and internal_use_op = | CopyEnv @@ -2070,6 +2071,7 @@ end = struct | MatchingPropT (reason, _, _) -> reason | OpaqueT (reason, _) -> reason | OpenPredT (reason, _, _, _) -> reason + | InternalT (OptionalChainVoidT reason) -> reason | ReposT (reason, _) -> reason | InternalT (ReposUpperT (reason, _)) -> reason (* HUH? cf. mod_reason below *) | ShapeT (t) -> reason_of_t t @@ -2218,6 +2220,7 @@ end = struct | MatchingPropT (reason, k, v) -> MatchingPropT (f reason, k, v) | OpaqueT (reason, opaquetype) -> OpaqueT (f reason, opaquetype) | OpenPredT (reason, t, p, n) -> OpenPredT (f reason, t, p, n) + | InternalT (OptionalChainVoidT reason) -> InternalT (OptionalChainVoidT (f reason)) | ReposT (reason, t) -> ReposT (f reason, t) | InternalT (ReposUpperT (reason, t)) -> InternalT (ReposUpperT (reason, mod_reason_of_t f t)) | ShapeT t -> ShapeT (mod_reason_of_t f t) @@ -2911,6 +2914,7 @@ let string_of_ctor = function | MatchingPropT _ -> "MatchingPropT" | OpaqueT _ -> "OpaqueT" | OpenPredT _ -> "OpenPredT" + | InternalT (OptionalChainVoidT _) -> "OptionalChainVoidT" | ReposT _ -> "ReposT" | InternalT (ReposUpperT _) -> "ReposUpperT" | ShapeT _ -> "ShapeT" diff --git a/src/typing/type_mapper.ml b/src/typing/type_mapper.ml index e4450f2dc59..cb9d22b62b9 100644 --- a/src/typing/type_mapper.ml +++ b/src/typing/type_mapper.ml @@ -164,6 +164,7 @@ class ['a] t = object(self) let t'' = self#type_ cx map_cx t' in if t'' == t' then t else InternalT (ReposUpperT (r, t'')) + | InternalT (OptionalChainVoidT _) -> t method tvar _cx _map_cx _r id = id diff --git a/src/typing/type_visitor.ml b/src/typing/type_visitor.ml index 5d96c0a553e..a77159337f8 100644 --- a/src/typing/type_visitor.ml +++ b/src/typing/type_visitor.ml @@ -120,6 +120,8 @@ class ['a] t = object(self) | InternalT (ReposUpperT (_, t)) -> self#type_ cx pole acc t + | InternalT (OptionalChainVoidT _) -> acc + method def_type cx pole acc = function | AnyT | NumT _ diff --git a/tests/optional_chaining/optional_chaining.exp b/tests/optional_chaining/optional_chaining.exp index 8c4e5f40dc1..25766d9d8ba 100644 --- a/tests/optional_chaining/optional_chaining.exp +++ b/tests/optional_chaining/optional_chaining.exp @@ -39,8 +39,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:13:2 -This use of optional chaining (`?.`) is unnecessary because `x2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `x2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:13:2 13| (x2?.["foo"]: empty); @@ -74,10 +74,25 @@ References: ^^^^^^ [3] +Error -------------------------------------------------------------------------------------- computed_properties.js:15:2 + +This use of optional chaining (`?.`) is unnecessary because `y1?.["bar"]` [1] cannot be nullish or because an earlier +`?.` will short-circuit the nullish case. (`unnecessary-optional-chain`) + + computed_properties.js:15:2 + 15| (y1?.["bar"]?.["foo"]: empty); + ^^^^^^^^^^^^^^^^^^^^ + +References: + computed_properties.js:15:2 + 15| (y1?.["bar"]?.["foo"]: empty); + ^^^^^^^^^^^ [1] + + Error -------------------------------------------------------------------------------------- computed_properties.js:16:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:16:2 16| (y2?.["bar"]?.["foo"]: empty); @@ -108,8 +123,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:16:2 -This use of optional chaining (`?.`) is unnecessary because `y2?.["bar"]` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2?.["bar"]` [1] cannot be nullish or because an earlier +`?.` will short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:16:2 16| (y2?.["bar"]?.["foo"]: empty); @@ -149,8 +164,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:18:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:18:2 18| (y2?.["baz"]?.["foo"]: empty); @@ -208,8 +223,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:21:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:21:2 21| (y2?.["bar"]["foo"]: empty); @@ -276,8 +291,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:23:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:23:2 23| (y2?.["baz"]["foo"]: empty); @@ -353,8 +368,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:25:2 -This use of optional chaining (`?.`) is unnecessary because `y1["bar"]` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y1["bar"]` [1] cannot be nullish or because an earlier `?.` +will short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:25:2 25| (y1["bar"]?.["foo"]: empty); @@ -385,8 +400,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:26:2 -This use of optional chaining (`?.`) is unnecessary because `y2["bar"]` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2["bar"]` [1] cannot be nullish or because an earlier `?.` +will short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:26:2 26| (y2["bar"]?.["foo"]: empty); @@ -506,8 +521,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:31:3 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:31:3 31| ((y2?.["bar"])["foo"]: empty); @@ -588,8 +603,8 @@ References: Error -------------------------------------------------------------------------------------- computed_properties.js:33:3 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) computed_properties.js:33:3 33| ((y2?.["baz"])["foo"]: empty); @@ -642,8 +657,8 @@ References: Error -------------------------------------------------------------------------------------------- function_calls.js:9:2 -This use of optional chaining (`?.`) is unnecessary because `x2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `x2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) function_calls.js:9:2 9| (x2?.(): empty) @@ -657,8 +672,8 @@ References: Error ------------------------------------------------------------------------------------------------ lhs_types.js:12:2 -This use of optional chaining (`?.`) is unnecessary because `x` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `x` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) lhs_types.js:12:2 12| (x?.foo: ?number); // no error, lint @@ -869,8 +884,8 @@ References: Error ---------------------------------------------------------------------------------------- private_properties.js:6:6 -This use of optional chaining (`?.`) is unnecessary because `this` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `this` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) private_properties.js:6:6 6| (this?.#foo: empty); @@ -901,8 +916,8 @@ References: Error --------------------------------------------------------------------------------------- private_properties.js:14:6 -This use of optional chaining (`?.`) is unnecessary because `this` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `this` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) private_properties.js:14:6 14| (this?.#bar: empty); @@ -938,8 +953,8 @@ References: Error --------------------------------------------------------------------------------------- private_properties.js:15:6 -This use of optional chaining (`?.`) is unnecessary because `this` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `this` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) private_properties.js:15:6 15| (this?.#baz: empty); @@ -992,8 +1007,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:13:2 -This use of optional chaining (`?.`) is unnecessary because `x2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `x2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:13:2 13| (x2?.foo: empty); @@ -1027,10 +1042,25 @@ References: ^^^^^^ [3] +Error ------------------------------------------------------------------------------------------- static_members.js:15:2 + +This use of optional chaining (`?.`) is unnecessary because `y1?.bar` [1] cannot be nullish or because an earlier `?.` +will short-circuit the nullish case. (`unnecessary-optional-chain`) + + static_members.js:15:2 + 15| (y1?.bar?.foo: empty); + ^^^^^^^^^^^^ + +References: + static_members.js:15:2 + 15| (y1?.bar?.foo: empty); + ^^^^^^^ [1] + + Error ------------------------------------------------------------------------------------------- static_members.js:16:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:16:2 16| (y2?.bar?.foo: empty); @@ -1061,8 +1091,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:16:2 -This use of optional chaining (`?.`) is unnecessary because `y2?.bar` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2?.bar` [1] cannot be nullish or because an earlier `?.` +will short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:16:2 16| (y2?.bar?.foo: empty); @@ -1102,8 +1132,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:18:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:18:2 18| (y2?.baz?.foo: empty); @@ -1161,8 +1191,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:21:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:21:2 21| (y2?.bar.foo: empty); @@ -1229,8 +1259,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:23:2 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:23:2 23| (y2?.baz.foo: empty); @@ -1306,8 +1336,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:25:2 -This use of optional chaining (`?.`) is unnecessary because `y1.bar` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y1.bar` [1] cannot be nullish or because an earlier `?.` +will short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:25:2 25| (y1.bar?.foo: empty); @@ -1338,8 +1368,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:26:2 -This use of optional chaining (`?.`) is unnecessary because `y2.bar` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2.bar` [1] cannot be nullish or because an earlier `?.` +will short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:26:2 26| (y2.bar?.foo: empty); @@ -1459,8 +1489,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:31:3 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:31:3 31| ((y2?.bar).foo: empty); @@ -1541,8 +1571,8 @@ References: Error ------------------------------------------------------------------------------------------- static_members.js:33:3 -This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be null or undefined. -(`unnecessary-optional-chain`) +This use of optional chaining (`?.`) is unnecessary because `y2` [1] cannot be nullish or because an earlier `?.` will +short-circuit the nullish case. (`unnecessary-optional-chain`) static_members.js:33:3 33| ((y2?.baz).foo: empty); @@ -1555,4 +1585,4 @@ References: -Found 119 errors +Found 121 errors diff --git a/tests/type-at-pos/type-at-pos.exp b/tests/type-at-pos/type-at-pos.exp index ab30bfb5793..f36b91c1df8 100644 --- a/tests/type-at-pos/type-at-pos.exp +++ b/tests/type-at-pos/type-at-pos.exp @@ -2926,7 +2926,7 @@ optional_chaining.js:16:11 = { "end":13 } optional_chaining.js:16:16 = { - "type":"Bar | void", + "type":"void | Bar", "reasons":[], "loc":{ "source":"optional_chaining.js", @@ -2941,7 +2941,7 @@ optional_chaining.js:16:16 = { "end":18 } optional_chaining.js:16:20 = { - "type":"Baz | void", + "type":"void | Baz", "reasons":[], "loc":{ "source":"optional_chaining.js",