diff --git a/src/typing/query_types.ml b/src/typing/query_types.ml index 9ead2e0a125..eb15403cda5 100644 --- a/src/typing/query_types.ml +++ b/src/typing/query_types.ml @@ -6,7 +6,7 @@ *) module Ast = Flow_ast -open Typed_ast_utils +open Typed_ast_finder type 'a result = | FailureNoMatch diff --git a/src/typing/type_annotation.ml b/src/typing/type_annotation.ml index f4d2819f04a..159d0d24b2a 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -257,7 +257,7 @@ module Make (ConsGen : Type_annotation_sig.ConsGen) (Statement : Statement_sig.S (Error_message.ETSSyntax { kind = Error_message.TSInOutVariance `InOut; loc }) | _ -> () ); - Typed_ast_finder.polarity variance + Typed_ast_utils.polarity variance (* Distributive tparam name helpers *) let use_distributive_tparam_name cx name name_loc tparams_map = diff --git a/src/typing/typed_ast_finder.ml b/src/typing/typed_ast_finder.ml index 4d463bd748d..e7a16e4dcee 100644 --- a/src/typing/typed_ast_finder.ml +++ b/src/typing/typed_ast_finder.ml @@ -7,14 +7,6 @@ module Ast = Flow_ast -let polarity = function - | Some (_, { Ast.Variance.kind = Ast.Variance.Plus; comments = _ }) -> Polarity.Positive - | Some (_, { Ast.Variance.kind = Ast.Variance.Minus; comments = _ }) -> Polarity.Negative - | Some (_, Ast.Variance.{ kind = InOut; comments = _ }) -> Polarity.Neutral - | Some (_, Ast.Variance.{ kind = Readonly | Out; comments = _ }) -> Polarity.Positive - | Some (_, Ast.Variance.{ kind = In; comments = _ }) -> Polarity.Negative - | None -> Polarity.Neutral - let mk_bound_t cx tparam = Flow_js_utils.generic_of_tparam cx ~f:(fun x -> x) tparam class type_parameter_mapper = @@ -56,7 +48,7 @@ class type_parameter_mapper = | None -> None | Some ((_, t), _) -> Some t in - let polarity = polarity variance in + let polarity = Typed_ast_utils.polarity variance in { Type.reason; name = Subst_name.Name name; bound; polarity; default; is_this = false } (* Record and restore the parameter environment around nodes that might @@ -177,3 +169,108 @@ module ExactMatchQuery = struct end let find_exact_match_annotation = ExactMatchQuery.find + +(* Find identifier under location *) +module Type_at_pos = struct + exception Found of ALoc.t * bool * Type.t + + (* Kinds of nodes that "type-at-pos" is interested in: + * - identifiers (handled in t_identifier) + * - type parameters (handled in type_param_identifier) + * - literal object keys (handled in object_key) + * - `this`, `super` (handled in expression) + * - private property names (handled in expression) + *) + class type_at_pos_searcher cx (target_loc : Loc.t) = + object (self) + inherit type_parameter_mapper as super + + method covers_target loc = Reason.in_range target_loc (ALoc.to_loc_exn loc) + + method find_loc + : 'a. ALoc.t -> Type.t -> is_type_identifier:bool -> tparams_rev:Type.typeparam list -> 'a + = + (fun loc t ~is_type_identifier ~tparams_rev:_ -> raise (Found (loc, is_type_identifier, t))) + + method! t_identifier (((loc, t), _) as id) = + if self#covers_target loc then + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) + else + super#t_identifier id + + method! type_identifier_reference (((loc, t), _) as id) = + if self#covers_target loc then + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:true) + else + super#t_identifier id + + method! jsx_identifier (((loc, t), _) as id) = + if self#covers_target loc then + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) + else + super#jsx_identifier id + + method! type_param ((_, { Ast.Type.TypeParam.name = (loc, _); _ }) as tparam) = + if self#covers_target loc then ( + let tparam = self#make_typeparam tparam in + rev_bound_tparams <- tparam :: rev_bound_tparams; + self#annot_with_tparams + (self#find_loc loc (mk_bound_t cx tparam) ~is_type_identifier:false) + ) else + super#type_param tparam + + method! object_key key = + let open Ast.Expression.Object.Property in + match key with + | StringLiteral ((loc, t), _) + | NumberLiteral ((loc, t), _) + | BigIntLiteral ((loc, t), _) + when self#covers_target loc -> + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) + | _ -> super#object_key key + + method! expression expr = + let open Ast.Expression in + match expr with + | ((loc, t), (This _ | Super _)) + | ((_, t), Member { Member.property = Member.PropertyPrivateName (loc, _); _ }) + | ( (_, t), + OptionalMember + { + OptionalMember.member = { Member.property = Member.PropertyPrivateName (loc, _); _ }; + _; + } + ) + when self#covers_target loc -> + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) + | _ -> super#expression expr + + method! implicit (((loc, t), _) as impl) = + if self#covers_target loc then + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) + else + super#implicit impl + + method! jsx_attribute_name_identifier (((loc, _), _) as id) = + if self#covers_target loc then + let reason = Reason.mk_reason (Reason.RCustom "jsx attr") loc in + let (_, lazy_hint) = Type_env.get_hint cx loc in + lazy_hint reason + |> Type_hint.with_hint_result + ~ok:(fun t -> + self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false)) + ~error:(fun () -> super#jsx_attribute_name_identifier id) + else + super#jsx_attribute_name_identifier id + end + + let find cx typed_ast loc = + let searcher = new type_at_pos_searcher cx loc in + try + ignore (searcher#program typed_ast); + None + with + | Found (loc, is_type_id, scheme) -> Some (ALoc.to_loc_exn loc, is_type_id, scheme) +end + +let find_type_at_pos_annotation = Type_at_pos.find diff --git a/src/typing/typed_ast_utils.ml b/src/typing/typed_ast_utils.ml index e9ffdd8c11f..c987974567e 100644 --- a/src/typing/typed_ast_utils.ml +++ b/src/typing/typed_ast_utils.ml @@ -5,121 +5,26 @@ * LICENSE file in the root directory of this source tree. *) +module Ast = Flow_ast module ALocMap = Loc_collections.ALocMap -open Typed_ast_finder - -(* Find identifier under location *) -module Type_at_pos = struct - exception Found of ALoc.t * bool * Type.t - - (* Kinds of nodes that "type-at-pos" is interested in: - * - identifiers (handled in t_identifier) - * - type parameters (handled in type_param_identifier) - * - literal object keys (handled in object_key) - * - `this`, `super` (handled in expression) - * - private property names (handled in expression) - *) - class type_at_pos_searcher cx (target_loc : Loc.t) = - object (self) - inherit type_parameter_mapper as super - - method covers_target loc = Reason.in_range target_loc (ALoc.to_loc_exn loc) - - method find_loc - : 'a. ALoc.t -> Type.t -> is_type_identifier:bool -> tparams_rev:Type.typeparam list -> 'a - = - (fun loc t ~is_type_identifier ~tparams_rev:_ -> raise (Found (loc, is_type_identifier, t))) - - method! t_identifier (((loc, t), _) as id) = - if self#covers_target loc then - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) - else - super#t_identifier id - - method! type_identifier_reference (((loc, t), _) as id) = - if self#covers_target loc then - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:true) - else - super#t_identifier id - - method! jsx_identifier (((loc, t), _) as id) = - if self#covers_target loc then - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) - else - super#jsx_identifier id - - method! type_param ((_, { Ast.Type.TypeParam.name = (loc, _); _ }) as tparam) = - if self#covers_target loc then ( - let tparam = self#make_typeparam tparam in - rev_bound_tparams <- tparam :: rev_bound_tparams; - self#annot_with_tparams - (self#find_loc loc (mk_bound_t cx tparam) ~is_type_identifier:false) - ) else - super#type_param tparam - - method! object_key key = - let open Ast.Expression.Object.Property in - match key with - | StringLiteral ((loc, t), _) - | NumberLiteral ((loc, t), _) - | BigIntLiteral ((loc, t), _) - when self#covers_target loc -> - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) - | _ -> super#object_key key - - method! expression expr = - let open Ast.Expression in - match expr with - | ((loc, t), (This _ | Super _)) - | ((_, t), Member { Member.property = Member.PropertyPrivateName (loc, _); _ }) - | ( (_, t), - OptionalMember - { - OptionalMember.member = { Member.property = Member.PropertyPrivateName (loc, _); _ }; - _; - } - ) - when self#covers_target loc -> - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) - | _ -> super#expression expr - - method! implicit (((loc, t), _) as impl) = - if self#covers_target loc then - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false) - else - super#implicit impl - - method! jsx_attribute_name_identifier (((loc, _), _) as id) = - if self#covers_target loc then - let reason = Reason.mk_reason (Reason.RCustom "jsx attr") loc in - let (_, lazy_hint) = Type_env.get_hint cx loc in - lazy_hint reason - |> Type_hint.with_hint_result - ~ok:(fun t -> - self#annot_with_tparams (self#find_loc loc t ~is_type_identifier:false)) - ~error:(fun () -> super#jsx_attribute_name_identifier id) - else - super#jsx_attribute_name_identifier id - end - - let find cx typed_ast loc = - let searcher = new type_at_pos_searcher cx loc in - try - ignore (searcher#program typed_ast); - None - with - | Found (loc, is_type_id, scheme) -> Some (ALoc.to_loc_exn loc, is_type_id, scheme) -end - -let find_type_at_pos_annotation = Type_at_pos.find - -class type_at_aloc_map_folder = + +let polarity = function + | Some (_, { Ast.Variance.kind = Ast.Variance.Plus; comments = _ }) -> Polarity.Positive + | Some (_, { Ast.Variance.kind = Ast.Variance.Minus; comments = _ }) -> Polarity.Negative + | Some (_, Ast.Variance.{ kind = InOut; comments = _ }) -> Polarity.Neutral + | Some (_, Ast.Variance.{ kind = Readonly | Out; comments = _ }) -> Polarity.Positive + | Some (_, Ast.Variance.{ kind = In; comments = _ }) -> Polarity.Negative + | None -> Polarity.Neutral + +class ['M, 'T, 'N, 'U] type_at_aloc_map_folder = object - inherit type_parameter_mapper + inherit ['M, 'M * 'T, 'N, 'N * 'U] Flow_polymorphic_ast_mapper.mapper val mutable map = ALocMap.empty - method! on_type_annot x = + method on_loc_annot x = x + + method on_type_annot x = let (loc, t) = x in map <- ALocMap.add loc t map; x @@ -127,13 +32,15 @@ class type_at_aloc_map_folder = method to_map = map end -class type_at_aloc_list_folder = +class ['M, 'T, 'N, 'U] type_at_aloc_list_folder = object - inherit type_parameter_mapper + inherit ['M, 'M * 'T, 'N, 'N * 'U] Flow_polymorphic_ast_mapper.mapper val mutable l = [] - method! on_type_annot x = + method on_loc_annot x = x + + method on_type_annot x = let (loc, t) = x in l <- (loc, t) :: l; x diff --git a/src/typing/typed_ast_utils.mli b/src/typing/typed_ast_utils.mli index a5f6490a230..78ceaac19da 100644 --- a/src/typing/typed_ast_utils.mli +++ b/src/typing/typed_ast_utils.mli @@ -5,25 +5,7 @@ * LICENSE file in the root directory of this source tree. *) -(** - * Find the first typed AST entry for "type-at-pos" related queries. A query - * succeeds if the location is within the range of a symbol in the AST. The kinds - * of symbols handled here are: - * - identifiers - * - literal object keys - * - `this`, `super` - * - private property names - * - * The first part of the return is the full span of the matching symbol. - * - * It's convenient to use Loc.t as the input query, since this is usually called - * in direct response to a client query, which are typically concrete locations. - *) -val find_type_at_pos_annotation : - Context.t -> - (ALoc.t, ALoc.t * Type.t) Flow_ast.Program.t -> - Loc.t -> - (Loc.t * bool * Type.t) option +val polarity : 'L Flow_ast.Variance.t option -> Polarity.t val typed_ast_to_map : (ALoc.t, ALoc.t * Type.t) Flow_polymorphic_ast_mapper.Ast.Program.t ->