From 501116a11c48f182fe84613b49eab5ee2e8024db Mon Sep 17 00:00:00 2001 From: Panos Vekris Date: Fri, 7 Mar 2025 17:24:18 -0800 Subject: [PATCH] [flow][refactor] rename "cond" param of statement.ml to "encl_ctx" Summary: Before this change the `cond` parameter in several function in statement.ml would represent the conditional syntactic context. It only had two variants, or was absent, which meant that we were not in a conditional context. Moving forward we will leverage this parameter to inform us about the enclosing syntactic context. The information we will need for natural inference extends beyond just being in a conditional context. For example, we care about whether we are an an indexer context. To accommodate for this, we rename the type `Type.cond_context` to `Type.enclosing_context`. We also rename the parameter `cond` to `encl_ctx` in statement.ml. Also, to make sure I've addressed all cites where this was used before, I've changed its type from optional to non-optional, but I've included a `NoContext` variant. Changelog: [internal] Reviewed By: SamChou19815 Differential Revision: D70802814 fbshipit-source-id: 13d39f21054f8cf6312bd58af24cd812286ffab8 --- src/services/autocomplete/autocomplete_js.ml | 5 +- src/typing/env_resolution.ml | 40 ++--- src/typing/func_sig.ml | 2 +- src/typing/primitive_literal.ml | 19 +- src/typing/primitive_literal.mli | 4 +- src/typing/statement.ml | 174 +++++++++++-------- src/typing/statement_sig.ml | 16 +- src/typing/type.ml | 8 +- src/typing/type_operation_utils.ml | 34 ++-- src/typing/type_operation_utils.mli | 3 +- 10 files changed, 169 insertions(+), 136 deletions(-) diff --git a/src/services/autocomplete/autocomplete_js.ml b/src/services/autocomplete/autocomplete_js.ml index 41fad076263..36c0c07ce47 100644 --- a/src/services/autocomplete/autocomplete_js.ml +++ b/src/services/autocomplete/autocomplete_js.ml @@ -162,7 +162,7 @@ module Inference = struct let type_of_match_member_pattern cx loc mem = Match_pattern.type_of_member_pattern cx - ~on_identifier:(Statement.identifier ~cond:None) + ~on_identifier:(Statement.identifier ~encl_ctx:Type.NoContext) ~on_expression:Statement.expression (loc, mem) end @@ -966,7 +966,8 @@ class process_request_searcher cx ~from_trigger_character ~cursor = let member_loc = Some (compute_member_loc ~expr_loc:loc ~obj_loc:base_loc) in let obj_type () = match base with - | BaseIdentifier (loc, id) -> Inference.type_of_identifier ~cond:None cx loc id + | BaseIdentifier (loc, id) -> + Inference.type_of_identifier ~encl_ctx:Type.NoContext cx loc id | BaseMember (loc, mem) -> Inference.type_of_match_member_pattern cx loc mem in (match property with diff --git a/src/typing/env_resolution.ml b/src/typing/env_resolution.ml index 03f47ad201c..fc21ee9ae9e 100644 --- a/src/typing/env_resolution.ml +++ b/src/typing/env_resolution.ml @@ -45,12 +45,12 @@ let try_cache : 'l. check:(unit -> 'l) -> cache:('l -> unit) -> Context.t -> 'l cache e; e -let expression cx ?cond ?decl exp = +let expression cx ?encl_ctx ?decl exp = let cache = Context.node_cache cx in let ((_, t), _) = try_cache cx - ~check:(fun () -> Statement.expression ?cond ?decl cx exp) + ~check:(fun () -> Statement.expression ?encl_ctx ?decl cx exp) ~cache:(Node_cache.set_expression cache) in t @@ -102,15 +102,15 @@ let resolve_annotation cx tparams_map ?(react_deep_read_only = None) anno = if Context.typing_mode cx = Context.CheckingMode then Node_cache.set_annotation cache anno; t -let rec synthesizable_expression cx ?cond exp = +let rec synthesizable_expression cx ?(encl_ctx = Type.NoContext) exp = let open Ast.Expression in match exp with - | (loc, Identifier (_, name)) -> Statement.identifier cx ~cond name loc - | (loc, StringLiteral lit) -> Statement.string_literal cx ~cond loc lit - | (loc, BooleanLiteral lit) -> Statement.boolean_literal cx ~cond loc lit + | (loc, Identifier (_, name)) -> Statement.identifier cx ~encl_ctx name loc + | (loc, StringLiteral lit) -> Statement.string_literal cx ~encl_ctx loc lit + | (loc, BooleanLiteral lit) -> Statement.boolean_literal cx ~encl_ctx loc lit | (loc, NullLiteral _) -> Statement.null_literal loc - | (loc, NumberLiteral lit) -> Statement.number_literal cx ~cond loc lit - | (loc, BigIntLiteral lit) -> Statement.bigint_literal cx ~cond loc lit + | (loc, NumberLiteral lit) -> Statement.number_literal cx ~encl_ctx loc lit + | (loc, BigIntLiteral lit) -> Statement.bigint_literal cx ~encl_ctx loc lit | (loc, RegExpLiteral _) -> Statement.regexp_literal cx loc | (loc, ModuleRefLiteral lit) -> let (t, _lit) = Statement.module_ref_literal cx loc lit in @@ -127,7 +127,7 @@ let rec synthesizable_expression cx ?cond exp = comments = _; } ) -> - let t = synthesizable_expression cx ?cond _object in + let t = synthesizable_expression cx ~encl_ctx _object in let tout = match Refinement.get ~allow_optional:false cx exp loc with | Some t -> t @@ -138,14 +138,14 @@ let rec synthesizable_expression cx ?cond exp = Statement.get_prop ~use_op (* TODO(jmbrown) This feels incorrect *) ~hint:(Type_env.get_hint cx loc) - ~cond:None + ~encl_ctx:NoContext cx expr_reason t (prop_reason, name) in tout - | _ -> expression cx ?cond exp + | _ -> expression cx ~encl_ctx exp let mk_selector_reason_has_default cx loc = function | Selector.Elem { index = n; has_default } -> @@ -803,7 +803,7 @@ let rec resolve_binding cx reason loc b = AnyT (mk_reason RAnyImplicit loc, AnyError (Some MissingAnnotation)) | Root (For (kind, exp)) -> let reason = mk_reason (RCustom "for-in") loc (*TODO: loc should be loc of loop *) in - let right_t = expression cx ~cond:OtherTest exp in + let right_t = expression cx ~encl_ctx:OtherTest exp in (match kind with | In -> TypeAssertions.assert_for_in_rhs cx right_t; @@ -1037,22 +1037,22 @@ let resolve_type_param cx id_loc = let resolve_chain_expression cx ~cond exp = let cache = Context.node_cache cx in - let cond = + let encl_ctx = match cond with - | NonConditionalContext -> None - | OtherConditionalTest -> Some OtherTest + | NonConditionalContext -> NoContext + | OtherConditionalTest -> OtherTest in - let (t, _, exp) = Statement.optional_chain ~cond cx exp in + let (t, _, exp) = Statement.optional_chain ~encl_ctx cx exp in Node_cache.set_expression cache exp; t let resolve_write_expression cx ~cond exp = - let cond = + let encl_ctx = match cond with - | NonConditionalContext -> None - | OtherConditionalTest -> Some OtherTest + | NonConditionalContext -> NoContext + | OtherConditionalTest -> OtherTest in - synthesizable_expression cx ?cond exp + synthesizable_expression cx ~encl_ctx exp let resolve_generator_next cx reason gen = let open TypeUtil in diff --git a/src/typing/func_sig.ml b/src/typing/func_sig.ml index 664e740b429..a2e3595fb7c 100644 --- a/src/typing/func_sig.ml +++ b/src/typing/func_sig.ml @@ -465,7 +465,7 @@ struct let use_op = Frame (ImplicitTypeParam, use_op) in (use_op, t, None) | Func.FieldInit e -> - let (((_, t), _) as ast) = Statement.expression ?cond:None cx e in + let (((_, t), _) as ast) = Statement.expression ~encl_ctx:Type.NoContext cx e in let body = mk_expression_reason e in let use_op = Op (InitField { op = reason_fn; body }) in (use_op, t, Some ast) diff --git a/src/typing/primitive_literal.ml b/src/typing/primitive_literal.ml index 87bd25dd0dd..b0c608d1504 100644 --- a/src/typing/primitive_literal.ml +++ b/src/typing/primitive_literal.ml @@ -8,7 +8,7 @@ module Ast = Flow_ast type syntactic_flags = { - cond: Type.cond_context option; + encl_ctx: Type.enclosing_context; decl: Ast.Variable.kind option; as_const: bool; frozen: Type.frozen_kind; @@ -16,10 +16,21 @@ type syntactic_flags = { } let empty_syntactic_flags = - { cond = None; decl = None; as_const = false; frozen = Type.NotFrozen; has_hint = lazy false } + { + encl_ctx = Type.NoContext; + decl = None; + as_const = false; + frozen = Type.NotFrozen; + has_hint = lazy false; + } let mk_syntactic_flags - ?cond ?decl ?(as_const = false) ?(frozen = Type.NotFrozen) ?(has_hint = lazy false) () = - { cond; decl; as_const; frozen; has_hint } + ?(encl_ctx = Type.NoContext) + ?decl + ?(as_const = false) + ?(frozen = Type.NotFrozen) + ?(has_hint = lazy false) + () = + { encl_ctx; decl; as_const; frozen; has_hint } let loc_has_hint _cx _loc = (* TODO *) false diff --git a/src/typing/primitive_literal.mli b/src/typing/primitive_literal.mli index 065985a3d4b..b7630133aa1 100644 --- a/src/typing/primitive_literal.mli +++ b/src/typing/primitive_literal.mli @@ -8,7 +8,7 @@ module Ast = Flow_ast type syntactic_flags = { - cond: Type.cond_context option; + encl_ctx: Type.enclosing_context; decl: Ast.Variable.kind option; as_const: bool; frozen: Type.frozen_kind; @@ -18,7 +18,7 @@ type syntactic_flags = { val empty_syntactic_flags : syntactic_flags val mk_syntactic_flags : - ?cond:Type.cond_context -> + ?encl_ctx:Type.enclosing_context -> ?decl:Ast.Variable.kind -> ?as_const:bool -> ?frozen:Type.frozen_kind -> diff --git a/src/typing/statement.ml b/src/typing/statement.ml index f28e8dcfc8f..2cb1285f345 100644 --- a/src/typing/statement.ml +++ b/src/typing/statement.ml @@ -1172,7 +1172,7 @@ module Make ); (loc, Expression { Expression.expression = expr; directive; comments }) | (loc, If { If.test; consequent; alternate; comments }) -> - let test_ast = condition ~cond:OtherTest cx test in + let test_ast = condition ~encl_ctx:OtherTest cx test in let then_ast = statement cx consequent in let else_ast = Base.Option.map alternate ~f:(fun (loc, { If.Alternate.body; comments }) -> @@ -1285,7 +1285,7 @@ module Make let (_, fake_ast) = condition cx - ~cond: + ~encl_ctx: (SwitchTest { case_test_loc = fst expr; switch_discriminant_loc = fst discriminant } ) @@ -1368,12 +1368,12 @@ module Make } ) | (loc, While { While.test; body; comments }) -> - let test_ast = condition ~cond:OtherTest cx test in + let test_ast = condition ~encl_ctx:OtherTest cx test in let body_ast = statement cx body in (loc, While { While.test = test_ast; body = body_ast; comments }) | (loc, DoWhile { DoWhile.body; test; comments }) -> let body_ast = statement cx body in - let test_ast = condition ~cond:OtherTest cx test in + let test_ast = condition ~encl_ctx:OtherTest cx test in (loc, DoWhile { DoWhile.body = body_ast; test = test_ast; comments }) | (loc, For { For.init; test; update; body; comments }) -> let init_ast = @@ -1388,7 +1388,7 @@ module Make match test with | None -> None | Some expr -> - let expr_ast = condition ~cond:OtherTest cx expr in + let expr_ast = condition ~encl_ctx:OtherTest cx expr in Some expr_ast in let body_ast = statement cx body in @@ -1398,7 +1398,7 @@ module Make ) | (loc, ForIn { ForIn.left; right; body; each; comments }) -> let eval_right () = - let (((_, _), _) as right_ast) = condition ~cond:OtherTest cx right in + let (((_, _), _) as right_ast) = condition ~encl_ctx:OtherTest cx right in right_ast in let (left_ast, right_ast) = @@ -1508,7 +1508,7 @@ module Make in let reason = mk_reason reason_desc loc in let eval_right () = - let (((_, t), _) as right_ast) = condition ~cond:OtherTest cx right in + let (((_, t), _) as right_ast) = condition ~encl_ctx:OtherTest cx right in let elem_t = for_of_elemt cx t reason await in (* null/undefined are NOT allowed *) @@ -2767,7 +2767,8 @@ module Make * annot should become a Type.t option when we have the ability to * inspect annotations and recurse into them *) and expression - ?cond ?decl ?(as_const = false) ?(frozen = NotFrozen) ?(has_hint = lazy false) cx (loc, e) = + ?encl_ctx ?decl ?(as_const = false) ?(frozen = NotFrozen) ?(has_hint = lazy false) cx (loc, e) + = let log_slow_to_check ~f = match Context.slow_to_check_logging cx with | { Slow_to_check_logging.slow_expressions_logging_threshold = Some threshold; _ } -> @@ -2795,7 +2796,7 @@ module Make node | None -> let syntactic_flags = - Primitive_literal.mk_syntactic_flags ?cond ?decl ~as_const ~frozen ~has_hint () + Primitive_literal.mk_syntactic_flags ?encl_ctx ?decl ~as_const ~frozen ~has_hint () in let res = expression_ cx syntactic_flags loc e in if Context.typing_mode cx = Context.CheckingMode then begin @@ -2827,7 +2828,7 @@ module Make and super_ cx loc = Type_env.var_ref cx (internal_name "super") loc and expression_ cx syntactic_flags loc e : (ALoc.t, ALoc.t * Type.t) Ast.Expression.t = - let { Primitive_literal.cond; decl; as_const; frozen; has_hint } = syntactic_flags in + let { Primitive_literal.encl_ctx; decl; as_const; frozen; has_hint } = syntactic_flags in let ex = (loc, e) in let open Ast.Expression in match e with @@ -2875,7 +2876,7 @@ module Make let (t, u) = update cx loc u in ((loc, t), Update u) | Binary b -> - let (t, b) = binary cx loc ~cond b in + let (t, b) = binary cx loc ~encl_ctx b in ((loc, t), Binary b) | Logical l -> let (t, l) = logical cx syntactic_flags loc l in @@ -2998,8 +2999,8 @@ module Make Abnormal.throw_expr_control_flow_exception loc ast else ast - | Member _ -> subscript ~cond cx ex - | OptionalMember _ -> subscript ~cond cx ex + | Member _ -> subscript ~encl_ctx cx ex + | OptionalMember _ -> subscript ~encl_ctx cx ex | Object { Object.properties; comments } -> let (t, properties) = object_ ~frozen:(frozen = FrozenDirect) ~as_const ~has_hint cx loc properties @@ -3239,12 +3240,12 @@ module Make ( (loc, t), New { New.callee = callee_ast; targs = targs_ast; arguments = arguments_ast; comments } ) - | Call _ -> subscript ~cond cx ex - | OptionalCall _ -> subscript ~cond cx ex + | Call _ -> subscript ~encl_ctx cx ex + | OptionalCall _ -> subscript ~encl_ctx cx ex | Conditional { Conditional.test; consequent; alternate; comments } -> let has_hint = lazy (Lazy.force has_hint || Primitive_literal.loc_has_hint cx loc) in let reason = mk_reason RConditional loc in - let test = condition ~cond:OtherTest cx test in + let test = condition ~encl_ctx:OtherTest cx test in let ((((_, t1), _) as consequent), then_throws) = Abnormal.catch_expr_control_flow_exception (fun () -> expression cx ?decl ~has_hint consequent @@ -3635,7 +3636,7 @@ module Make short-circuiting and non short-circuiting (i.e. representing the actual range of possible types of the expression) *) - and optional_chain ~cond cx ((loc, e) as ex) = + and optional_chain ~encl_ctx cx ((loc, e) as ex) = let open Ast.Expression in let normalize_voided_out t = let ts = Flow.possible_concrete_types_for_inspection cx (reason_of_t t) t in @@ -4144,7 +4145,7 @@ module Make let arguments = Base.List.map ~f:(Base.Fn.compose snd (expression_or_spread cx)) arguments in - let (((_, cond_t), _) as cond) = condition ~cond:OtherTest cx cond in + let (((_, cond_t), _) as cond) = condition ~encl_ctx:OtherTest cx cond in let concretized_cond_t = Flow.singleton_concrete_type_for_inspection cx (TypeUtil.reason_of_t cond_t) cond_t in @@ -4218,7 +4219,7 @@ module Make let use_op = Op (GetProperty (mk_expression_reason ex)) in get_prop ~use_op - ~cond + ~encl_ctx ~hint:(Type_env.get_hint cx loc) cx expr_reason @@ -4400,7 +4401,7 @@ module Make | NewChain -> let lhs_reason = mk_expression_reason obj_ in let ((filtered_t, voided_t, object_ast) as object_data) = - optional_chain ~cond:None cx obj_ + optional_chain ~encl_ctx:NoContext cx obj_ in begin match refine () with @@ -4422,7 +4423,7 @@ module Make ) | None -> handle_new_chain conf lhs_reason loc object_data end - | ContinueChain -> handle_continue_chain conf (optional_chain ~cond:None cx obj_) + | ContinueChain -> handle_continue_chain conf (optional_chain ~encl_ctx:NoContext cx obj_) in let specialize_callee callee specialized_callee = let (Specialized_callee { finalized; _ }) = specialized_callee in @@ -4577,7 +4578,7 @@ module Make let use_op = Op (GetProperty expr_reason) in let opt_use = get_prop_opt_use - ~cond + ~encl_ctx expr_reason ~use_op ~hint:(Type_env.get_hint cx loc) @@ -4949,28 +4950,30 @@ module Make in (filtered_out, voided_out, ((loc, lhs_t), exp object_ast)) | _ -> - let (((_, t), _) as res) = expression ?cond cx ex in + let (((_, t), _) as res) = expression ~encl_ctx cx ex in (t, [], res)) and arg_list cx (args_loc, { Ast.Expression.ArgList.arguments; comments }) = let (argts, arg_asts) = arguments |> Base.List.map ~f:(expression_or_spread cx) |> List.split in (argts, (args_loc, { Ast.Expression.ArgList.arguments = arg_asts; comments })) - and subscript ~cond cx ex = - let (_, _, ast) = optional_chain ~cond cx ex in + and subscript ~encl_ctx cx ex = + let (_, _, ast) = optional_chain ~encl_ctx cx ex in ast (* traverse a unary expression, return result type *) and unary cx syntactic_flags loc = - let { Primitive_literal.cond; as_const; frozen; _ } = syntactic_flags in + let { Primitive_literal.encl_ctx; as_const; frozen; _ } = syntactic_flags in let open Ast.Expression.Unary in function | { operator = Not; argument; comments } -> - let (((_, arg), _) as argument) = expression cx ~cond:OtherTest argument in + let (((_, arg), _) as argument) = expression cx ~encl_ctx:OtherTest argument in let tout = - match cond with - | Some _ -> BoolModuleT.at loc - | None -> + match encl_ctx with + | SwitchTest _ + | OtherTest -> + BoolModuleT.at loc + | NoContext -> let reason = mk_reason (RUnaryOperator ("not", desc_of_t arg)) loc in Operators.unary_not cx reason arg in @@ -5068,12 +5071,13 @@ module Make (result_t, { expr with argument = arg_ast }) (* Returns a function that type check LHS or RHS of eq_test under correct conditional context. *) - and visit_eq_test cx ~cond loc left right = - let check ~cond = expression cx ?cond in - match cond with - | None -> check ~cond:None - | Some c -> - let reconstruct_ast = check ~cond:(Some OtherTest) in + and visit_eq_test cx ~encl_ctx loc left right = + let check ~encl_ctx = expression cx ~encl_ctx in + match encl_ctx with + | NoContext -> check ~encl_ctx:NoContext + | SwitchTest _ + | OtherTest -> + let reconstruct_ast = check ~encl_ctx:OtherTest in Eq_test.visit_eq_test (* Strict and sense don't influence whether we should propagate cond context. *) ~sense:false @@ -5084,11 +5088,11 @@ module Make ~on_void_test:(fun ~sense:_ ~strict:_ ~check_for_bound_undefined:_ _ _ _ -> reconstruct_ast) ~on_member_eq_other:(fun _ _value -> reconstruct_ast) ~on_other_eq_member:(fun _value _ -> reconstruct_ast) - ~on_other_eq_test:(fun _ _ -> check ~cond:None) + ~on_other_eq_test:(fun _ _ -> check ~encl_ctx:NoContext) ~is_switch_cond_context: - (match c with + (match encl_ctx with | SwitchTest _ -> true - | OtherTest -> false) + | _ -> false) loc left right @@ -5115,12 +5119,12 @@ module Make | _ -> () (* traverse a binary expression, return result type *) - and binary cx loc ~cond { Ast.Expression.Binary.operator; left; right; comments } = + and binary cx loc ~encl_ctx { Ast.Expression.Binary.operator; left; right; comments } = let open Ast.Expression.Binary in match operator with | Equal | NotEqual -> - let reconstruct_ast = visit_eq_test cx ~cond loc left right in + let reconstruct_ast = visit_eq_test cx ~encl_ctx loc left right in let (((_, t1), _) as left) = reconstruct_ast left in let (((_, t2), _) as right) = reconstruct_ast right in Operators.check_eq cx (t1, t2); @@ -5133,19 +5137,22 @@ module Make (BoolModuleT.at loc, { operator; left; right; comments }) | StrictEqual | StrictNotEqual -> - let reconstruct_ast = visit_eq_test cx ~cond loc left right in + let reconstruct_ast = visit_eq_test cx ~encl_ctx loc left right in let (((_, t1), _) as left) = reconstruct_ast left in let (((_, t2), _) as right) = reconstruct_ast right in - Base.Option.iter - ~f:(fun _ -> + begin + match encl_ctx with + | NoContext -> () + | SwitchTest _ + | OtherTest -> matching_prop_check cx left right; (* If this is a switch statement only consider the case where the object * access in the discriminant. *) - match cond with - | Some (SwitchTest _) -> () + (match encl_ctx with + | SwitchTest _ -> () | _ -> matching_prop_check cx right left) - cond; - Operators.check_strict_eq ~cond_context:cond cx (t1, t2); + end; + Operators.check_strict_eq ~encl_ctx cx (t1, t2); (BoolModuleT.at loc, { operator; left; right; comments }) | Instanceof -> let left = expression cx left in @@ -5185,7 +5192,7 @@ module Make and logical cx syntactic_flags loc { Ast.Expression.Logical.operator; left; right; comments } = let open Ast.Expression.Logical in - let { Primitive_literal.cond; has_hint; _ } = syntactic_flags in + let { Primitive_literal.encl_ctx; has_hint; _ } = syntactic_flags in let has_hint = lazy (Lazy.force has_hint || Primitive_literal.loc_has_hint cx loc) in (* With logical operators the LHS is always evaluated. So if the LHS throws, the whole * expression throws. To model this we do not catch abnormal exceptions on the LHS. @@ -5197,9 +5204,11 @@ module Make match operator with | Or -> let () = check_default_pattern cx left right in - let (((_, t1), _) as left) = condition ~cond:OtherTest ~has_hint cx left in + let (((_, t1), _) as left) = condition ~encl_ctx:OtherTest ~has_hint cx left in let ((((_, t2), _) as right), right_throws) = - Abnormal.catch_expr_control_flow_exception (fun () -> expression cx ?cond ~has_hint right) + Abnormal.catch_expr_control_flow_exception (fun () -> + expression cx ~encl_ctx ~has_hint right + ) in let t2 = if right_throws then @@ -5210,9 +5219,11 @@ module Make let reason = mk_reason (RLogical ("||", desc_of_t t1, desc_of_t t2)) loc in (Operators.logical_or cx reason t1 t2, { operator = Or; left; right; comments }) | And -> - let (((_, t1), _) as left) = condition ~cond:OtherTest ~has_hint cx left in + let (((_, t1), _) as left) = condition ~encl_ctx:OtherTest ~has_hint cx left in let ((((_, t2), _) as right), right_throws) = - Abnormal.catch_expr_control_flow_exception (fun () -> expression cx ?cond ~has_hint right) + Abnormal.catch_expr_control_flow_exception (fun () -> + expression cx ~encl_ctx ~has_hint right + ) in let t2 = if right_throws then @@ -5305,7 +5316,7 @@ module Make *) match (optional, mode) with | ((NewChain | ContinueChain), Delete) -> - let (o, _, _object) = optional_chain ~cond:None cx obj in + let (o, _, _object) = optional_chain ~encl_ctx:NoContext cx obj in (o, _object) | _ -> let (((_, o), _) as _object) = expression cx obj in @@ -5564,7 +5575,7 @@ module Make let () = update_env result_t in (lhs_t, lhs_pattern_ast, rhs_ast) | Assignment.AndAssign -> - let ((_, lhs_t), _) = condition ~cond:OtherTest cx left_expr in + let ((_, lhs_t), _) = condition ~encl_ctx:OtherTest cx left_expr in let ((((_, rhs_t), _) as rhs_ast), right_throws) = Abnormal.catch_expr_control_flow_exception (fun () -> expression cx rhs) in @@ -5579,7 +5590,7 @@ module Make (lhs_t, lhs_pattern_ast, rhs_ast) | Assignment.OrAssign -> let () = check_default_pattern cx left_expr rhs in - let ((_, lhs_t), _) = condition ~cond:OtherTest cx left_expr in + let ((_, lhs_t), _) = condition ~encl_ctx:OtherTest cx left_expr in let ((((_, rhs_t), _) as rhs_ast), right_throws) = Abnormal.catch_expr_control_flow_exception (fun () -> expression cx rhs) in @@ -5705,7 +5716,14 @@ module Make let reason = mk_reason (RIdentifier (OrdinaryName "React.Fragment")) expr_loc in let react = Type_env.var_ref ~lookup_mode:ForValue cx (OrdinaryName "React") expr_loc in let use_op = Op (GetProperty reason) in - get_prop ~cond:None cx reason ~use_op ~hint:hint_unavailable react (reason, "Fragment") + get_prop + ~encl_ctx:NoContext + cx + reason + ~use_op + ~hint:hint_unavailable + react + (reason, "Fragment") in let (unresolved_params, frag_children) = collapse_children cx frag_children in let props = @@ -6208,7 +6226,7 @@ module Make let react_t = Type_env.var_ref ~lookup_mode:ForValue cx (OrdinaryName "React") loc_element in let create_element_t = get_prop - ~cond:None + ~encl_ctx:NoContext cx reason ~use_op @@ -6432,8 +6450,8 @@ module Make accesses are provisionally allowed even when such properties do not exist. This accommodates the common JavaScript idiom of testing for the existence of a property before using that property. *) - and condition cx ~cond ?has_hint e : (ALoc.t, ALoc.t * Type.t) Ast.Expression.t = - expression ~cond ?has_hint cx e + and condition cx ~encl_ctx ?has_hint e : (ALoc.t, ALoc.t * Type.t) Ast.Expression.t = + expression ~encl_ctx ?has_hint cx e and get_private_field_opt_use cx reason ~use_op name = let class_entries = Type_env.get_class_entries cx in @@ -6446,12 +6464,14 @@ module Make expressions out of `expression`, somewhat like what assignment_lhs does. That would make everything involving Refinement be in the same place. *) - and get_prop_opt_use ~cond reason ~use_op ~hint (prop_reason, name) = + and get_prop_opt_use ~encl_ctx reason ~use_op ~hint (prop_reason, name) = let id = mk_id () in let prop_name = OrdinaryName name in - if Base.Option.is_some cond then + match encl_ctx with + | SwitchTest _ + | OtherTest -> OptTestPropT (use_op, reason, id, mk_named_prop ~reason:prop_reason prop_name, hint) - else + | NoContext -> OptGetPropT { use_op; @@ -6461,8 +6481,8 @@ module Make hint; } - and get_prop ~cond cx reason ~use_op ~hint tobj (prop_reason, name) = - let opt_use = get_prop_opt_use ~cond reason ~use_op ~hint (prop_reason, name) in + and get_prop ~encl_ctx cx reason ~use_op ~hint tobj (prop_reason, name) = + let opt_use = get_prop_opt_use ~encl_ctx reason ~use_op ~hint (prop_reason, name) in Tvar_resolver.mk_tvar_and_fully_resolve_no_wrap_where cx reason (fun t -> let get_prop_u = apply_opt_use opt_use t in Flow.flow cx (tobj, get_prop_u) @@ -8601,21 +8621,21 @@ module Make in { enum_name; enum_id; members; representation_t; has_unknown_members } - let expression ?cond ?decl ?as_const cx (loc, e) = - expression ?cond ?decl ?as_const ~frozen:NotFrozen cx (loc, e) + let expression ?encl_ctx ?decl ?as_const cx (loc, e) = + expression ?encl_ctx ?decl ?as_const ~frozen:NotFrozen cx (loc, e) - let identifier ~cond cx id loc = - identifier cx { empty_syntactic_flags with Primitive_literal.cond } id loc + let identifier ~encl_ctx cx id loc = + identifier cx { empty_syntactic_flags with Primitive_literal.encl_ctx } id loc - let string_literal cx ~cond loc v = - string_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + let string_literal cx ~encl_ctx loc v = + string_literal cx { empty_syntactic_flags with Primitive_literal.encl_ctx } loc v - let number_literal cx ~cond loc v = - number_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + let number_literal cx ~encl_ctx loc v = + number_literal cx { empty_syntactic_flags with Primitive_literal.encl_ctx } loc v - let boolean_literal cx ~cond loc v = - boolean_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + let boolean_literal cx ~encl_ctx loc v = + boolean_literal cx { empty_syntactic_flags with Primitive_literal.encl_ctx } loc v - let bigint_literal cx ~cond loc v = - bigint_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + let bigint_literal cx ~encl_ctx loc v = + bigint_literal cx { empty_syntactic_flags with Primitive_literal.encl_ctx } loc v end diff --git a/src/typing/statement_sig.ml b/src/typing/statement_sig.ml index 14aeea1fbcc..74a3d030bab 100644 --- a/src/typing/statement_sig.ml +++ b/src/typing/statement_sig.ml @@ -73,7 +73,7 @@ module type S = sig Context.t -> (ALoc.t, ALoc.t) Ast.Expression.CallTypeArgs.t option -> Type.targ list option val expression : - ?cond:Type.cond_context -> + ?encl_ctx:Type.enclosing_context -> ?decl:Ast.Variable.kind -> ?as_const:bool -> Context.t -> @@ -230,24 +230,24 @@ module type S = sig Type.enum_concrete_info val optional_chain : - cond:Type.cond_context Base.Option.t -> + encl_ctx:Type.enclosing_context -> Context.t -> (ALoc.t, ALoc.t) Ast.Expression.t -> Type.t * Type.t list * (ALoc.t, ALoc.t * Type.t) Ast.Expression.t val string_literal : - Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.StringLiteral.t -> Type.t + Context.t -> encl_ctx:Type.enclosing_context -> ALoc.t -> ALoc.t Ast.StringLiteral.t -> Type.t val boolean_literal : - Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.BooleanLiteral.t -> Type.t + Context.t -> encl_ctx:Type.enclosing_context -> ALoc.t -> ALoc.t Ast.BooleanLiteral.t -> Type.t val null_literal : ALoc.t -> Type.t val number_literal : - Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.NumberLiteral.t -> Type.t + Context.t -> encl_ctx:Type.enclosing_context -> ALoc.t -> ALoc.t Ast.NumberLiteral.t -> Type.t val bigint_literal : - Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.BigIntLiteral.t -> Type.t + Context.t -> encl_ctx:Type.enclosing_context -> ALoc.t -> ALoc.t Ast.BigIntLiteral.t -> Type.t val regexp_literal : Context.t -> ALoc.t -> Type.t @@ -258,10 +258,10 @@ module type S = sig Type.t * (ALoc.t, ALoc.t * Type.t) Ast.ModuleRefLiteral.t val identifier : - cond:Type.cond_context option -> Context.t -> ALoc.t Ast.Identifier.t' -> ALoc.t -> Type.t + encl_ctx:Type.enclosing_context -> Context.t -> ALoc.t Ast.Identifier.t' -> ALoc.t -> Type.t val get_prop : - cond:Type.cond_context option -> + encl_ctx:Type.enclosing_context -> Context.t -> Reason.t -> use_op:Type.use_op -> diff --git a/src/typing/type.ml b/src/typing/type.ml index 504d3a09fa7..ff60cf78925 100644 --- a/src/typing/type.ml +++ b/src/typing/type.ml @@ -885,11 +885,17 @@ module rec TypeTerm : sig } | EnumExhaustiveCheckInvalid of ALoc.t list - and cond_context = + (* Information about the enclosing syntactic context of an expression. Each + * one of these variants corresponds to the context of `C` in the expression + * or statement shown in comment above it. *) + and enclosing_context = + | NoContext + (* `switch(C){...}` *) | SwitchTest of { case_test_loc: ALoc.t; switch_discriminant_loc: ALoc.t; } + (* `if(C){}`, `while(C){}`, `C?e1:e2`, `invariant(C)`, ... *) | OtherTest (* Bindings created from destructuring annotations should themselves act like diff --git a/src/typing/type_operation_utils.ml b/src/typing/type_operation_utils.ml index 861bd580acb..4424860692f 100644 --- a/src/typing/type_operation_utils.ml +++ b/src/typing/type_operation_utils.ml @@ -236,12 +236,12 @@ module Operators = struct false | _ -> true in - let strict_equatable_error cond_context (l, r) = + let strict_equatable_error encl_ctx (l, r) = let comparison_error = let open TypeUtil in lazy - (match cond_context with - | Some (SwitchTest { case_test_loc; switch_discriminant_loc }) -> + (match encl_ctx with + | SwitchTest { case_test_loc; switch_discriminant_loc } -> let use_op = Op (SwitchRefinementCheck @@ -255,7 +255,8 @@ module Operators = struct use_op; explanation = None; } - | _ -> + | NoContext + | OtherTest -> let reasons = FlowError.ordered_reasons (reason_of_t l, reason_of_t r) in Error_message.EComparison reasons) in @@ -278,10 +279,10 @@ module Operators = struct (* We allow the comparison of enums to null and void outside of switches. *) | (DefT (_, EnumValueT _), DefT (_, (NullT | VoidT))) | (DefT (_, (NullT | VoidT)), DefT (_, EnumValueT _)) -> begin - match cond_context with - | Some (SwitchTest _) -> Some (Lazy.force comparison_error) - | None - | Some _ -> + match encl_ctx with + | SwitchTest _ -> Some (Lazy.force comparison_error) + | NoContext + | OtherTest -> None end (* We don't allow the comparison of enums and other types in general. *) @@ -293,7 +294,7 @@ module Operators = struct (* We don't check other strict equality comparisons. *) | _ -> None in - let rec distribute cx ~cond_context (t1, t2) = + let rec distribute cx ~encl_ctx (t1, t2) = match (t1, t2) with | (DefT (_, EmptyT), _) | (_, DefT (_, EmptyT)) @@ -302,9 +303,7 @@ module Operators = struct () | (IntersectionT (r1, rep1), t2) -> let cases = - Base.List.map (InterRep.members rep1) ~f:(fun t1 () -> - distribute cx ~cond_context (t1, t2) - ) + Base.List.map (InterRep.members rep1) ~f:(fun t1 () -> distribute cx ~encl_ctx (t1, t2)) in Speculation_flow.try_custom cx @@ -312,9 +311,7 @@ module Operators = struct cases | (t1, IntersectionT (r2, rep2)) -> let cases = - Base.List.map (InterRep.members rep2) ~f:(fun t2 () -> - distribute cx ~cond_context (t1, t2) - ) + Base.List.map (InterRep.members rep2) ~f:(fun t2 () -> distribute cx ~encl_ctx (t1, t2)) in Speculation_flow.try_custom cx @@ -324,7 +321,7 @@ module Operators = struct let t1_needs_concretization = eq_needs_concretization t1 in let t2_needs_concretization = eq_needs_concretization t2 in if (not t1_needs_concretization) && not t2_needs_concretization then - match strict_equatable_error cond_context (t1, t2) with + match strict_equatable_error encl_ctx (t1, t2) with | Some error -> add_output cx error | None -> () else @@ -349,11 +346,10 @@ module Operators = struct () else Base.List.iter t1s ~f:(fun t1 -> - Base.List.iter t2s ~f:(fun t2 -> distribute cx ~cond_context (t1, t2)) + Base.List.iter t2s ~f:(fun t2 -> distribute cx ~encl_ctx (t1, t2)) ) in - - (fun ~cond_context -> distribute ~cond_context) + (fun ~encl_ctx -> distribute ~encl_ctx) let unary_arith cx reason kind t = Tvar_resolver.mk_tvar_and_fully_resolve_where cx reason (fun tout -> diff --git a/src/typing/type_operation_utils.mli b/src/typing/type_operation_utils.mli index 8dbedf37279..2cca5da566e 100644 --- a/src/typing/type_operation_utils.mli +++ b/src/typing/type_operation_utils.mli @@ -38,8 +38,7 @@ module Operators : sig val check_eq : Context.t -> Type.t * Type.t -> unit - val check_strict_eq : - cond_context:Type.cond_context option -> Context.t -> Type.t * Type.t -> unit + val check_strict_eq : encl_ctx:Type.enclosing_context -> Context.t -> Type.t * Type.t -> unit val logical_and : Context.t -> reason -> Type.t -> Type.t -> Type.t