diff --git a/src/services/autocomplete/autocomplete_js.ml b/src/services/autocomplete/autocomplete_js.ml index e2586cf79a6..41fad076263 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 + ~on_identifier:(Statement.identifier ~cond:None) ~on_expression:Statement.expression (loc, mem) end @@ -966,7 +966,7 @@ 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 cx loc id + | BaseIdentifier (loc, id) -> Inference.type_of_identifier ~cond:None 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 dc1a1a25440..39767cce754 100644 --- a/src/typing/env_resolution.ml +++ b/src/typing/env_resolution.ml @@ -105,12 +105,12 @@ let resolve_annotation cx tparams_map ?(react_deep_read_only = None) anno = let rec synthesizable_expression cx ?cond exp = let open Ast.Expression in match exp with - | (loc, Identifier (_, name)) -> Statement.identifier cx name loc - | (loc, StringLiteral lit) -> Statement.string_literal cx ~singleton:false loc lit - | (loc, BooleanLiteral lit) -> Statement.boolean_literal ~singleton:false loc lit + | (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, NullLiteral _) -> Statement.null_literal loc - | (loc, NumberLiteral lit) -> Statement.number_literal ~singleton:false loc lit - | (loc, BigIntLiteral lit) -> Statement.bigint_literal ~singleton:false loc lit + | (loc, NumberLiteral lit) -> Statement.number_literal cx ~cond loc lit + | (loc, BigIntLiteral lit) -> Statement.bigint_literal cx ~cond loc lit | (loc, RegExpLiteral _) -> Statement.regexp_literal cx loc | (loc, ModuleRefLiteral lit) -> let (t, _lit) = Statement.module_ref_literal cx loc lit in diff --git a/src/typing/primitive_literal.ml b/src/typing/primitive_literal.ml new file mode 100644 index 00000000000..f23419eb6d5 --- /dev/null +++ b/src/typing/primitive_literal.ml @@ -0,0 +1,20 @@ +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +module Ast = Flow_ast + +type syntactic_flags = { + cond: Type.cond_context option; + decl: Ast.Variable.kind option; + as_const: bool; + frozen: Type.frozen_kind; +} + +let empty_syntactic_flags = { cond = None; decl = None; as_const = false; frozen = Type.NotFrozen } + +let mk_syntactic_flags ?cond ?decl ?(as_const = false) ?(frozen = Type.NotFrozen) () = + { cond; decl; as_const; frozen } diff --git a/src/typing/primitive_literal.mli b/src/typing/primitive_literal.mli new file mode 100644 index 00000000000..d0374676e80 --- /dev/null +++ b/src/typing/primitive_literal.mli @@ -0,0 +1,25 @@ +(* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +module Ast = Flow_ast + +type syntactic_flags = { + cond: Type.cond_context option; + decl: Ast.Variable.kind option; + as_const: bool; + frozen: Type.frozen_kind; +} + +val empty_syntactic_flags : syntactic_flags + +val mk_syntactic_flags : + ?cond:Type.cond_context -> + ?decl:Ast.Variable.kind -> + ?as_const:bool -> + ?frozen:Type.frozen_kind -> + unit -> + syntactic_flags diff --git a/src/typing/statement.ml b/src/typing/statement.ml index 7513258c55d..1f9b6cc9684 100644 --- a/src/typing/statement.ml +++ b/src/typing/statement.ml @@ -64,10 +64,7 @@ module Make let empty_seen_names = { static_names = SMap.empty; instance_names = SMap.empty } - type frozen_kind = - | NotFrozen - | FrozenProp - | FrozenDirect + let empty_syntactic_flags = Primitive_literal.empty_syntactic_flags module ObjectExpressionAcc = struct type element = @@ -737,7 +734,7 @@ module Make (* Values *) (**********) - let identifier_ cx name loc = + let identifier_ cx _syntactic_flags name loc = let get_checking_mode_type () = let t = Type_env.var_ref ~lookup_mode:ForValue cx (OrdinaryName name) loc in (* We want to make sure that the reason description for the type we return @@ -762,17 +759,18 @@ module Make else get_checking_mode_type () - let identifier cx { Ast.Identifier.name; comments = _ } loc = - let t = identifier_ cx name loc in + let identifier cx syntactic_flags { Ast.Identifier.name; comments = _ } loc = + let t = identifier_ cx syntactic_flags name loc in t - let string_literal_value cx ~singleton loc value = + let string_literal_value cx syntactic_flags loc value = + let { Primitive_literal.as_const; frozen; _ } = syntactic_flags in if Type_inference_hooks_js.dispatch_literal_hook cx loc then let (_, lazy_hint) = Type_env.get_hint cx loc in let hint = lazy_hint (mk_reason RString loc) ~expected_only:false in let error () = EmptyT.at loc in Type_hint.with_hint_result hint ~ok:Base.Fn.id ~error - else if singleton then + else if as_const || frozen = FrozenProp then let reason = mk_annot_reason (RStringLit (OrdinaryName value)) loc in DefT (reason, SingletonStrT { from_annot = true; value = OrdinaryName value }) else @@ -785,11 +783,12 @@ module Make let reason = mk_annot_reason (RLongStringLit max_literal_length) loc in DefT (reason, StrGeneralT AnyLiteral) - let string_literal cx ~singleton loc { Ast.StringLiteral.value; _ } = - string_literal_value cx ~singleton loc value + let string_literal cx syntactic_flags loc { Ast.StringLiteral.value; _ } = + string_literal_value cx syntactic_flags loc value - let boolean_literal ~singleton loc { Ast.BooleanLiteral.value; _ } = - if singleton then + let boolean_literal _cx syntactic_flags loc { Ast.BooleanLiteral.value; _ } = + let { Primitive_literal.as_const; frozen; _ } = syntactic_flags in + if as_const || frozen = FrozenProp then let reason = mk_annot_reason (RBooleanLit value) loc in DefT (reason, SingletonBoolT { from_annot = true; value }) else @@ -798,16 +797,18 @@ module Make let null_literal loc = NullT.at loc - let number_literal ~singleton loc { Ast.NumberLiteral.value; raw; _ } = - if singleton then + let number_literal _cx syntactic_flags loc { Ast.NumberLiteral.value; raw; _ } = + let { Primitive_literal.as_const; frozen; _ } = syntactic_flags in + if as_const || frozen = FrozenProp then let reason = mk_annot_reason (RNumberLit raw) loc in DefT (reason, SingletonNumT { from_annot = true; value = (value, raw) }) else let reason = mk_annot_reason RNumber loc in DefT (reason, NumT_UNSOUND (None, (value, raw))) - let bigint_literal ~singleton loc { Ast.BigIntLiteral.value; raw; _ } = - if singleton then + let bigint_literal _cx syntactic_flags loc { Ast.BigIntLiteral.value; raw; _ } = + let { Primitive_literal.as_const; frozen; _ } = syntactic_flags in + if as_const || frozen = FrozenProp then let reason = mk_annot_reason (RBigIntLit raw) loc in DefT (reason, SingletonBigIntT { from_annot = true; value = (value, raw) }) else @@ -1220,7 +1221,7 @@ module Make Flow_ast.Expression.Identifier (Flow_ast_utils.match_root_ident case_loc) ) pattern - ~on_identifier:identifier + ~on_identifier:(fun cx -> identifier cx empty_syntactic_flags) ~on_expression:expression ~on_binding:(fun ~use_op ~name_loc ~kind name t -> init_var kind cx ~use_op t name_loc; @@ -2760,7 +2761,7 @@ module Make (* can raise Abnormal.(Exn (_, _)) * annot should become a Type.t option when we have the ability to * inspect annotations and recurse into them *) - and expression ?cond ?(as_const = false) ?(frozen = NotFrozen) cx (loc, e) = + and expression ?cond ?decl ?(as_const = false) ?(frozen = NotFrozen) 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; _ } -> @@ -2787,7 +2788,10 @@ module Make (lazy [spf "Expression cache hit at %s" (ALoc.debug_to_string loc)]); node | None -> - let res = expression_ ~cond ~as_const ~frozen cx loc e in + let syntactic_flags = + Primitive_literal.mk_syntactic_flags ?cond ?decl ~as_const ~frozen () + in + let res = expression_ cx syntactic_flags loc e in if Context.typing_mode cx = Context.CheckingMode then begin Context.constraint_cache cx := FlowSet.empty; Context.eval_repos_cache cx := EvalReposCacheMap.empty; @@ -2816,24 +2820,25 @@ module Make and super_ cx loc = Type_env.var_ref cx (internal_name "super") loc - and expression_ ~cond ~as_const ~frozen cx loc e : (ALoc.t, ALoc.t * Type.t) Ast.Expression.t = + and expression_ cx syntactic_flags loc e : (ALoc.t, ALoc.t * Type.t) Ast.Expression.t = + let { Primitive_literal.cond; decl; as_const; frozen; _ } = syntactic_flags in let ex = (loc, e) in let open Ast.Expression in match e with | StringLiteral lit -> - let t = string_literal cx ~singleton:(as_const || frozen = FrozenProp) loc lit in + let t = string_literal cx syntactic_flags loc lit in ((loc, t), StringLiteral lit) | BooleanLiteral lit -> - let t = boolean_literal ~singleton:(as_const || frozen = FrozenProp) loc lit in + let t = boolean_literal cx syntactic_flags loc lit in ((loc, t), BooleanLiteral lit) | NullLiteral lit -> let t = null_literal loc in ((loc, t), NullLiteral lit) | NumberLiteral lit -> - let t = number_literal ~singleton:(as_const || frozen = FrozenProp) loc lit in + let t = number_literal cx syntactic_flags loc lit in ((loc, t), NumberLiteral lit) | BigIntLiteral lit -> - let t = bigint_literal ~singleton:(as_const || frozen = FrozenProp) loc lit in + let t = bigint_literal cx syntactic_flags loc lit in ((loc, t), BigIntLiteral lit) | RegExpLiteral lit -> let t = regexp_literal cx loc in @@ -2850,14 +2855,15 @@ module Make let t = VoidT.make (mk_reason RVoid loc) in ((loc, t), Identifier ((id_loc, t), name)) | Identifier (id_loc, name) -> - let t = identifier cx name loc in + let t = identifier cx syntactic_flags name loc in ((loc, t), Identifier ((id_loc, t), name)) | This this -> let t = this_ cx loc this in ((loc, t), This this) - | Super s -> ((loc, identifier cx (mk_ident ~comments:None "super") loc), Super s) + | Super s -> + ((loc, identifier cx empty_syntactic_flags (mk_ident ~comments:None "super") loc), Super s) | Unary u -> - let (t, u) = unary cx ~cond ~as_const ~frozen loc u in + let (t, u) = unary cx syntactic_flags loc u in ((loc, t), Unary u) | Update u -> let (t, u) = update cx loc u in @@ -2866,7 +2872,7 @@ module Make let (t, b) = binary cx loc ~cond b in ((loc, t), Binary b) | Logical l -> - let (t, l) = logical cx loc ~cond l in + let (t, l) = logical cx syntactic_flags loc l in ((loc, t), Logical l) | TypeCast ({ TypeCast.expression = e; annot; comments } as cast) -> let casting_syntax = Context.casting_syntax cx in @@ -2933,7 +2939,7 @@ module Make cx (case_loc, Identifier (Flow_ast_utils.match_root_ident case_loc)) pattern - ~on_identifier:identifier + ~on_identifier:(fun cx -> identifier cx empty_syntactic_flags) ~on_expression:expression ~on_binding:(fun ~use_op ~name_loc ~kind name t -> init_var kind cx ~use_op t name_loc; @@ -2953,7 +2959,7 @@ module Make | None -> (None, false) in let ((((_, t), _) as body), body_throws) = - Abnormal.catch_expr_control_flow_exception (fun () -> expression cx body) + Abnormal.catch_expr_control_flow_exception (fun () -> expression cx ?decl body) in let case_ast = (case_loc, { Flow_ast.Match.Case.pattern; body; guard; comments }) in let throws = guard_throws || body_throws in @@ -3067,7 +3073,7 @@ module Make (argts, Some arges) | None -> ([], None) in - let id_t = identifier cx name callee_loc in + let id_t = identifier cx empty_syntactic_flags name callee_loc in let callee_annot = (callee_loc, id_t) in (match targts_opt with | None -> @@ -3178,7 +3184,7 @@ module Make | None -> (None, None) in - let id_t = identifier cx name callee_loc in + let id_t = identifier cx empty_syntactic_flags name callee_loc in let reason_call = mk_reason (RConstructorCall (desc_of_t id_t)) loc in let use_op = Op @@ -3234,10 +3240,10 @@ module Make let reason = mk_reason RConditional loc in let test = condition ~cond:OtherTest cx test in let ((((_, t1), _) as consequent), then_throws) = - Abnormal.catch_expr_control_flow_exception (fun () -> expression cx consequent) + Abnormal.catch_expr_control_flow_exception (fun () -> expression cx ?decl consequent) in let ((((_, t2), _) as alternate), else_throws) = - Abnormal.catch_expr_control_flow_exception (fun () -> expression cx alternate) + Abnormal.catch_expr_control_flow_exception (fun () -> expression cx ?decl alternate) in let combined_type = match (then_throws, else_throws) with @@ -3409,7 +3415,7 @@ module Make ) = head in - let t = string_literal_value cx ~singleton:false elem_loc cooked in + let t = string_literal_value cx empty_syntactic_flags elem_loc cooked in (t, []) | _ -> let t_out = StrModuleT.at loc in @@ -4945,7 +4951,8 @@ module Make ast (* traverse a unary expression, return result type *) - and unary cx ~cond ~as_const ~frozen loc = + and unary cx syntactic_flags loc = + let { Primitive_literal.cond; as_const; frozen; _ } = syntactic_flags in let open Ast.Expression.Unary in function | { operator = Not; argument; comments } -> @@ -5172,8 +5179,9 @@ module Make { operator; left = left_ast; right = right_ast; comments } ) - and logical cx loc ~cond { Ast.Expression.Logical.operator; left; right; comments } = + and logical cx syntactic_flags loc { Ast.Expression.Logical.operator; left; right; comments } = let open Ast.Expression.Logical in + let { Primitive_literal.cond; _ } = syntactic_flags 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. * As such, we only analyze the RHS expression if the LHS does not throw. @@ -5230,7 +5238,7 @@ module Make | ( pat_loc, Ast.Pattern.Identifier { Ast.Pattern.Identifier.name = (loc, name); optional; annot } ) -> - let t = identifier cx name loc in + let t = identifier cx empty_syntactic_flags name loc in ( (pat_loc, t), Ast.Pattern.Identifier { @@ -5793,7 +5801,7 @@ module Make in let c = if name = String.capitalize_ascii name then - identifier cx (mk_ident ~comments:None name) loc + identifier cx empty_syntactic_flags (mk_ident ~comments:None name) loc else begin Type_env.intrinsic_ref cx (OrdinaryName name) loc |> Base.Option.iter ~f:(fun (t, def_loc) -> @@ -5987,7 +5995,7 @@ module Make match value with (* *) | Some (Attribute.StringLiteral (loc, lit)) -> - let t = string_literal cx ~singleton:false loc lit in + let t = string_literal cx empty_syntactic_flags loc lit in (t, Some (Attribute.StringLiteral ((loc, t), lit))) (* *) | Some @@ -6975,16 +6983,16 @@ module Make let (annot_t, annot_ast) = match (expr, annot) with | ((loc, Ast.Expression.StringLiteral lit), Ast.Type.Missing annot_loc) -> - let t = string_literal cx ~singleton:false loc lit in + let t = string_literal cx empty_syntactic_flags loc lit in (t, Ast.Type.Missing (annot_loc, t)) | ((loc, Ast.Expression.BooleanLiteral lit), Ast.Type.Missing annot_loc) -> - let t = boolean_literal ~singleton:false loc lit in + let t = boolean_literal cx empty_syntactic_flags loc lit in (t, Ast.Type.Missing (annot_loc, t)) | ((loc, Ast.Expression.NumberLiteral lit), Ast.Type.Missing annot_loc) -> - let t = number_literal ~singleton:false loc lit in + let t = number_literal cx empty_syntactic_flags loc lit in (t, Ast.Type.Missing (annot_loc, t)) | ((loc, Ast.Expression.BigIntLiteral lit), Ast.Type.Missing annot_loc) -> - let t = bigint_literal loc ~singleton:false lit in + let t = bigint_literal cx empty_syntactic_flags loc lit in (t, Ast.Type.Missing (annot_loc, t)) | ((loc, Ast.Expression.RegExpLiteral _), Ast.Type.Missing annot_loc) -> let t = regexp_literal cx loc in @@ -8587,6 +8595,21 @@ module Make in { enum_name; enum_id; members; representation_t; has_unknown_members } - let expression ?cond ?as_const cx (loc, e) = - expression ?cond ?as_const ~frozen:NotFrozen cx (loc, e) + let expression ?cond ?decl ?as_const cx (loc, e) = + expression ?cond ?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 string_literal cx ~cond loc v = + string_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + + let number_literal cx ~cond loc v = + number_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + + let boolean_literal cx ~cond loc v = + boolean_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v + + let bigint_literal cx ~cond loc v = + bigint_literal cx { empty_syntactic_flags with Primitive_literal.cond } loc v end diff --git a/src/typing/statement_sig.ml b/src/typing/statement_sig.ml index 8c9210cd8b8..14aeea1fbcc 100644 --- a/src/typing/statement_sig.ml +++ b/src/typing/statement_sig.ml @@ -74,6 +74,7 @@ module type S = sig val expression : ?cond:Type.cond_context -> + ?decl:Ast.Variable.kind -> ?as_const:bool -> Context.t -> (ALoc.t, ALoc.t) Flow_ast.Expression.t -> @@ -234,15 +235,19 @@ module type S = sig (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 -> singleton:bool -> ALoc.t -> ALoc.t Ast.StringLiteral.t -> Type.t + val string_literal : + Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.StringLiteral.t -> Type.t - val boolean_literal : singleton:bool -> ALoc.t -> ALoc.t Ast.BooleanLiteral.t -> Type.t + val boolean_literal : + Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.BooleanLiteral.t -> Type.t val null_literal : ALoc.t -> Type.t - val number_literal : singleton:bool -> ALoc.t -> ALoc.t Ast.NumberLiteral.t -> Type.t + val number_literal : + Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.NumberLiteral.t -> Type.t - val bigint_literal : singleton:bool -> ALoc.t -> ALoc.t Ast.BigIntLiteral.t -> Type.t + val bigint_literal : + Context.t -> cond:Type.cond_context option -> ALoc.t -> ALoc.t Ast.BigIntLiteral.t -> Type.t val regexp_literal : Context.t -> ALoc.t -> Type.t @@ -252,7 +257,8 @@ module type S = sig (ALoc.t, ALoc.t) Ast.ModuleRefLiteral.t -> Type.t * (ALoc.t, ALoc.t * Type.t) Ast.ModuleRefLiteral.t - val identifier : Context.t -> ALoc.t Ast.Identifier.t' -> ALoc.t -> Type.t + val identifier : + cond:Type.cond_context option -> Context.t -> ALoc.t Ast.Identifier.t' -> ALoc.t -> Type.t val get_prop : cond:Type.cond_context option -> diff --git a/src/typing/type.ml b/src/typing/type.ml index 01adf66229e..504d3a09fa7 100644 --- a/src/typing/type.ml +++ b/src/typing/type.ml @@ -1728,6 +1728,11 @@ module rec TypeTerm : sig external use_t_compare : use_t -> use_t -> int = "caml_fast_generic_compare" [@@noalloc] external type_term_compare : t -> t -> int = "caml_fast_generic_compare" [@@noalloc] + + type frozen_kind = + | NotFrozen + | FrozenProp + | FrozenDirect end = TypeTerm