Skip to content

Commit

Permalink
[indexed-access] Type normalizer support for indexed access
Browse files Browse the repository at this point in the history
Summary:
Add custom behaviour rather than just piggy-backing off of `$ElementType<...>`. This will be required anyway to support optional indexed access, which does not have a legacy analog.

In the future, we will eliminate `$ElementType<...>` and rename/replace `ElementType` in the code with `IndexedAccessType`, and remove the `is_indexed_access`.

Reviewed By: panagosg7

Differential Revision: D26891740

fbshipit-source-id: 633824572ec619b4f450c07960164a39ae9d108a
  • Loading branch information
gkz authored and facebook-github-bot committed Mar 9, 2021
1 parent 22091a4 commit 4b1c518
Show file tree
Hide file tree
Showing 21 changed files with 87 additions and 28 deletions.
26 changes: 16 additions & 10 deletions src/common/ty/ty.ml
Expand Up @@ -53,6 +53,10 @@ type t =
| Utility of utility
| Mu of int * t
| CharSet of string
| IndexedAccess of {
_object: t;
index: t;
}

and tvar = RVar of int [@@unboxed]

Expand Down Expand Up @@ -437,15 +441,16 @@ class ['A] comparator_ty =
| Generic _ -> 14
| TypeOf _ -> 15
| Utility _ -> 16
| Tup _ -> 17
| Arr _ -> 18
| Fun _ -> 19
| Obj _ -> 20
| Inter _ -> 21
| Union _ -> 22
| Mu _ -> 23
| InlineInterface _ -> 24
| CharSet _ -> 25
| IndexedAccess _ -> 17
| Tup _ -> 18
| Arr _ -> 19
| Fun _ -> 20
| Obj _ -> 21
| Inter _ -> 22
| Union _ -> 23
| Mu _ -> 24
| InlineInterface _ -> 25
| CharSet _ -> 26

method tag_of_decl _ =
function
Expand Down Expand Up @@ -657,7 +662,8 @@ let rec mk_exact ty =
| Union _
| Inter _
| TypeOf _
| Utility _ ->
| Utility _
| IndexedAccess _ ->
Utility (Exact ty)

let mk_array ~readonly ~literal t =
Expand Down
5 changes: 5 additions & 0 deletions src/common/ty/ty_debug.ml
Expand Up @@ -244,6 +244,8 @@ and dump_t ?(depth = 10) t =
(spf "{ %s %s }" (dump_list (dump_prop ~depth) if_props) dict)
| TypeOf v -> spf "Typeof (%s)" (builtin_value v)
| Utility u -> dump_utility ~depth u
| IndexedAccess { _object; index } ->
spf "IndexedAccess (%s) (%s)" (dump_t ~depth _object) (dump_t ~depth index)
| Mu (i, t) -> spf "Mu (%d, %s)" i (dump_t ~depth t)
| CharSet s -> spf "CharSet (%s)" s

Expand Down Expand Up @@ -317,6 +319,7 @@ let string_of_ctor_t = function
| InlineInterface _ -> "InlineInterface"
| TypeOf _ -> "Typeof"
| Utility _ -> "Utility"
| IndexedAccess _ -> "IndexedAccess"
| Mu _ -> "Mu"
| CharSet _ -> "CharSet"

Expand Down Expand Up @@ -398,6 +401,8 @@ let json_of_elt ~strip_root =
])
| TypeOf b -> [("name", json_of_builtin_value b)]
| Utility u -> json_of_utility u
| IndexedAccess { _object; index } ->
[("object", json_of_t _object); ("index", json_of_t index)]
| Mu (i, t) -> [("mu_var", int_ i); ("type", json_of_t t)]
| CharSet s -> [("literal", JSON_String s)])
and json_of_tvar (RVar i) = Hh_json.[("id", int_ i)]
Expand Down
2 changes: 2 additions & 0 deletions src/common/ty/ty_printer.ml
Expand Up @@ -98,6 +98,8 @@ let layout_of_elt ?(size = 5000) ?(with_comments = true) ~exact_by_default elt =
| Union (t1, t2, ts) -> type_union ~depth (t1 :: t2 :: ts)
| Inter (t1, t2, ts) -> type_intersection ~depth (t1 :: t2 :: ts)
| Utility s -> utility ~depth s
| IndexedAccess { _object; index } ->
fuse [type_ ~depth _object; Atom "["; type_ ~depth index; Atom "]"]
| Tup ts ->
list
~wrap:(Atom "[", Atom "]")
Expand Down
4 changes: 4 additions & 0 deletions src/common/ty/ty_serializer.ml
Expand Up @@ -100,6 +100,10 @@ let type_ options =
| Union (t0, t1, ts) as t -> union t (t0, t1, ts)
| Inter (t0, t1, ts) -> intersection (t0, t1, ts)
| Utility s -> utility s
| IndexedAccess { _object; index } ->
let%bind _object = type_ _object in
let%map index = type_ index in
(Loc.none, T.IndexedAccess { T.IndexedAccess._object; index; comments = None })
| InlineInterface i -> inline_interface i
| CharSet s ->
let id = id_from_string "CharSet" in
Expand Down
1 change: 1 addition & 0 deletions src/services/autocomplete/autocompleteService_js.ml
Expand Up @@ -85,6 +85,7 @@ let lsp_completion_of_type =
| Arr _
| TypeOf _
| Utility _
| IndexedAccess _
| Mu _
| CharSet _ ->
Some Lsp.Completion.Variable
Expand Down
2 changes: 1 addition & 1 deletion src/typing/flow_js.ml
Expand Up @@ -6865,7 +6865,7 @@ struct
| PropertyType x ->
let reason_op = replace_desc_reason (RProperty (Some x)) reason in
GetPropT (use_op, reason, Named (reason_op, x), tout)
| ElementType t -> GetElemT (use_op, reason, t, tout)
| ElementType { index_type; _ } -> GetElemT (use_op, reason, index_type, tout)
| Bind t -> BindT (use_op, reason, mk_boundfunctioncalltype t None [] tout, true)
| SpreadType (options, todo_rev, head_slice) ->
Object.(
Expand Down
8 changes: 4 additions & 4 deletions src/typing/merge_js.ml
Expand Up @@ -900,12 +900,12 @@ module ContextOptimizer = struct
| PropertyType s ->
SigHash.add_name sig_hash s;
t
| ElementType t' ->
let t'' = self#type_ cx map_cx t' in
if t'' == t' then
| ElementType { index_type; is_indexed_access } ->
let index_type' = self#type_ cx map_cx index_type in
if index_type' == index_type then
t
else
ElementType t''
ElementType { index_type = index_type'; is_indexed_access }
| Bind t' ->
let t'' = self#type_ cx map_cx t' in
if t'' == t' then
Expand Down
2 changes: 1 addition & 1 deletion src/typing/resolvableTypeJob.ml
Expand Up @@ -235,7 +235,7 @@ and collect_of_type ?log_unresolved cx acc = function
and collect_of_destructor ?log_unresolved cx acc = function
| NonMaybeType -> acc
| PropertyType _ -> acc
| ElementType t -> collect_of_type ?log_unresolved cx acc t
| ElementType { index_type; _ } -> collect_of_type ?log_unresolved cx acc index_type
| Bind t -> collect_of_type ?log_unresolved cx acc t
| ReadOnlyType -> acc
| SpreadType (_, ts, head_slice) ->
Expand Down
1 change: 1 addition & 0 deletions src/typing/ty_members.ml
Expand Up @@ -191,6 +191,7 @@ let rec members_of_ty : Ty.t -> Ty.t member_info NameUtils.Map.t * string list =
| InlineInterface _
| TypeOf _
| Utility _
| IndexedAccess _
| Mu _
| CharSet _ ->
(NameUtils.Map.empty, [])
Expand Down
9 changes: 6 additions & 3 deletions src/typing/ty_normalizer.ml
Expand Up @@ -1741,9 +1741,12 @@ end = struct
| T.NonMaybeType -> return (Ty.Utility (Ty.NonMaybeType ty))
| T.ReadOnlyType -> return (Ty.Utility (Ty.ReadOnly ty))
| T.ValuesType -> return (Ty.Utility (Ty.Values ty))
| T.ElementType t' ->
let%map ty' = type__ ~env t' in
Ty.Utility (Ty.ElementType (ty, ty'))
| T.ElementType { index_type; is_indexed_access } ->
let%map index_type' = type__ ~env index_type in
if is_indexed_access then
Ty.IndexedAccess { _object = ty; index = index_type' }
else
Ty.Utility (Ty.ElementType (ty, index_type'))
| T.CallType ts ->
let%map tys = mapM (type__ ~env) ts in
Ty.Utility (Ty.Call (ty, tys))
Expand Down
6 changes: 5 additions & 1 deletion src/typing/type.ml
Expand Up @@ -1290,7 +1290,11 @@ module rec TypeTerm : sig
and destructor =
| NonMaybeType
| PropertyType of name
| ElementType of t
| ElementType of {
index_type: t;
(* For type normalizer purposes - in the future ElementType will be removed. *)
is_indexed_access: bool;
}
| Bind of t
| ReadOnlyType
| SpreadType of
Expand Down
12 changes: 10 additions & 2 deletions src/typing/type_annotation.ml
Expand Up @@ -265,7 +265,9 @@ let rec convert cx tparams_map =
(IndexedTypeAccess { _object = reason_of_t object_type; index = reason_of_t index_type })
in
EvalT
(object_type, TypeDestructorT (use_op, reason, ElementType index_type), mk_eval_id cx loc)
( object_type,
TypeDestructorT (use_op, reason, ElementType { index_type; is_indexed_access = true }),
mk_eval_id cx loc )
in
((loc, t), IndexedAccess { IndexedAccess._object; index; comments })
(* TODO *)
Expand Down Expand Up @@ -496,7 +498,13 @@ let rec convert cx tparams_map =
| ([t; e], targs) ->
let reason = mk_reason (RType (OrdinaryName "$ElementType")) loc in
reconstruct_ast
(EvalT (t, TypeDestructorT (use_op reason, reason, ElementType e), mk_eval_id cx loc))
(EvalT
( t,
TypeDestructorT
( use_op reason,
reason,
ElementType { index_type = e; is_indexed_access = false } ),
mk_eval_id cx loc ))
targs
| _ -> assert false)
(* $NonMaybeType<T> acts as the type T without null and void *)
Expand Down
1 change: 1 addition & 0 deletions src/typing/type_asserts.ml
Expand Up @@ -50,6 +50,7 @@ let check_type_visitor wrap =
| TypeOf _
| Generic _
| Utility _
| IndexedAccess _
| CharSet _
| InlineInterface _ ->
()
Expand Down
8 changes: 4 additions & 4 deletions src/typing/type_mapper.ml
Expand Up @@ -518,12 +518,12 @@ class virtual ['a, 'phase] t =
| NonMaybeType
| PropertyType _ ->
t
| ElementType t' ->
let t'' = self#type_ cx map_cx t' in
if t'' == t' then
| ElementType { index_type; is_indexed_access } ->
let index_type' = self#type_ cx map_cx index_type in
if index_type' == index_type then
t
else
ElementType t''
ElementType { index_type = index_type'; is_indexed_access }
| Bind t' ->
let t'' = self#type_ cx map_cx t' in
if t'' == t' then
Expand Down
7 changes: 6 additions & 1 deletion src/typing/type_sig_merge.ml
Expand Up @@ -638,7 +638,12 @@ and merge_annot file = function
let obj = merge file obj in
let elem = merge file elem in
let id = Type.Eval.id_of_aloc_id (Context.make_aloc_id file.cx loc) in
Type.(EvalT (obj, TypeDestructorT (use_op, reason, Type.ElementType elem), id))
Type.(
EvalT
( obj,
TypeDestructorT
(use_op, reason, Type.ElementType { index_type = elem; is_indexed_access = false }),
id ))
| NonMaybeType (loc, t) ->
let reason = Reason.(mk_reason (RType (OrdinaryName "$NonMaybeType")) loc) in
let use_op = Type.Op (Type.TypeApplication { type' = reason }) in
Expand Down
2 changes: 1 addition & 1 deletion src/typing/type_visitor.ml
Expand Up @@ -206,7 +206,7 @@ class ['a, 'phase] t =
| ReactElementRefType ->
acc
| ReactConfigType default_props -> self#type_ cx pole_TODO acc default_props
| ElementType t -> self#type_ cx pole_TODO acc t
| ElementType { index_type; _ } -> self#type_ cx pole_TODO acc index_type
| Bind t -> self#type_ cx pole_TODO acc t
| SpreadType (_, ts, head_slice) ->
let acc = self#list (self#object_kit_spread_operand cx) acc ts in
Expand Down
2 changes: 2 additions & 0 deletions tests/type_at_pos_indexed_access/.flowconfig
@@ -0,0 +1,2 @@
[options]
indexed_access=true
1 change: 1 addition & 0 deletions tests/type_at_pos_indexed_access/.testconfig
@@ -0,0 +1 @@
shell: test.sh
4 changes: 4 additions & 0 deletions tests/type_at_pos_indexed_access/test.js
@@ -0,0 +1,4 @@
type O = {a: number};

type T = O['a'];
// ^
7 changes: 7 additions & 0 deletions tests/type_at_pos_indexed_access/test.sh
@@ -0,0 +1,7 @@
#!/bin/bash
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

queries_in_file "type-at-pos" "test.js"
@@ -0,0 +1,5 @@
test.js:3:6
Flags:
type T = O["a"]
test.js:3:6,3:6

0 comments on commit 4b1c518

Please sign in to comment.