diff --git a/src/services/autocomplete/autocomplete_js.ml b/src/services/autocomplete/autocomplete_js.ml index 630f81cb2a0..e98fa35898b 100644 --- a/src/services/autocomplete/autocomplete_js.ml +++ b/src/services/autocomplete/autocomplete_js.ml @@ -156,6 +156,15 @@ module Inference = struct match Statement.statement cx (loc, ClassDeclaration cls) with | (_, ClassDeclaration { Flow_ast.Class.id = Some ((_, t), _); _ }) -> t | _ -> raise (Internal_exn "typed AST structure mismatch") + + let type_of_identifier cx loc id = Statement.identifier cx id loc + + let type_of_match_member_pattern cx loc mem = + Match_pattern.type_of_member_pattern + cx + ~on_identifier:Statement.identifier + ~on_expression:Statement.expression + (loc, mem) end class process_request_searcher cx ~from_trigger_character ~cursor = @@ -939,6 +948,56 @@ class process_request_searcher cx ~from_trigger_character ~cursor = this#find loc token Ac_ignored else key + + method! match_member_pattern member_pattern = + let open Ast.MatchPattern.MemberPattern in + let (loc, { base; property; _ }) = member_pattern in + let base_loc = + match base with + | BaseIdentifier (loc, _) + | BaseMember (loc, _) -> + loc + in + 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 + | BaseMember (loc, mem) -> Inference.type_of_match_member_pattern cx loc mem + in + (match property with + | PropertyIdentifier (prop_loc, { Ast.Identifier.name; _ }) when this#covers_target prop_loc + -> + this#find + prop_loc + name + (Ac_member + { + obj_type = obj_type (); + in_optional_chain = false; + bracket_syntax = None; + member_loc; + is_type_annotation = false; + is_super = false; + } + ) + | PropertyString (prop_loc, { Ast.StringLiteral.raw = token; _ }) + when this#covers_target prop_loc -> + let obj_type = obj_type () in + this#find + prop_loc + token + (Ac_member + { + obj_type; + in_optional_chain = false; + bracket_syntax = Some (this#default_bracket_syntax obj_type); + member_loc; + is_type_annotation = false; + is_super = false; + } + ) + | _ -> ()); + super#match_member_pattern member_pattern end let autocomplete_id ~cursor _cx _ac_name ac_loc = covers_target cursor ac_loc diff --git a/src/typing/match_pattern.ml b/src/typing/match_pattern.ml index 264579c20f1..7a1fcd8bc0c 100644 --- a/src/typing/match_pattern.ml +++ b/src/typing/match_pattern.ml @@ -223,3 +223,7 @@ and object_properties cx ~on_identifier ~on_expression ~on_binding ~in_or_patter let pattern cx ~on_identifier ~on_expression ~on_binding acc (loc, p) = pattern_ cx ~on_identifier ~on_expression ~on_binding ~in_or_pattern:false acc (loc, p) + +let type_of_member_pattern cx ~on_identifier ~on_expression mem = + let (_, ((_, t), _)) = member cx ~on_identifier ~on_expression mem in + t diff --git a/src/typing/match_pattern.mli b/src/typing/match_pattern.mli index 390cfd391fa..8c711fb80df 100644 --- a/src/typing/match_pattern.mli +++ b/src/typing/match_pattern.mli @@ -17,3 +17,11 @@ val pattern : (ALoc.t, ALoc.t) Ast.Expression.t -> (ALoc.t, ALoc.t) Ast.MatchPattern.t -> (ALoc.t, ALoc.t * Type.t) Ast.MatchPattern.t + +val type_of_member_pattern : + Context.t -> + on_identifier:(Context.t -> ALoc.t Ast.Identifier.t' -> ALoc.t -> Type.t) -> + on_expression: + (Context.t -> (ALoc.t, ALoc.t) Ast.Expression.t -> (ALoc.t, ALoc.t * Type.t) Ast.Expression.t) -> + (ALoc.t, ALoc.t) Ast.MatchPattern.MemberPattern.t -> + Type.t diff --git a/tests/autocomplete_match/autocomplete_match.exp b/tests/autocomplete_match/autocomplete_match.exp index 9461f293d93..1e655270473 100644 --- a/tests/autocomplete_match/autocomplete_match.exp +++ b/tests/autocomplete_match/autocomplete_match.exp @@ -45,3 +45,31 @@ pattern-object.js:6:5 Flags: --pretty {"result":[]} +pattern-member.js:6:6 +Flags: --pretty +{ + "result":[ + {"name":"[\"zip-zap\"]","type":"3"}, + {"name":"bar","type":"{baz: 2}"}, + {"name":"foo","type":"1"} + ] +} + +pattern-member.js:8:7 +Flags: --pretty +{"result":[{"name":"foo","type":"1"}]} + +pattern-member.js:10:11 +Flags: --pretty +{"result":[{"name":"baz","type":"2"}]} + +pattern-member.js:12:6 +Flags: --pretty +{ + "result":[ + {"name":"\"bar\"","type":"{baz: 2}"}, + {"name":"\"foo\"","type":"1"}, + {"name":"\"zip-zap\"","type":"3"} + ] +} + diff --git a/tests/autocomplete_match/pattern-member.js b/tests/autocomplete_match/pattern-member.js new file mode 100644 index 00000000000..298ee7bb32c --- /dev/null +++ b/tests/autocomplete_match/pattern-member.js @@ -0,0 +1,14 @@ +declare const x: number; + +declare const O: {foo: 1, bar: {baz: 2}, 'zip-zap': 3}; + +const e = match (x) { + O. : 0, +// ^ + O.f : 0, +// ^ + O.bar.b : 0, +// ^ + O[ ]: 0, +// ^ +}; diff --git a/tests/autocomplete_match/test.sh b/tests/autocomplete_match/test.sh index 5a0cc1e9188..d9b14f7478d 100644 --- a/tests/autocomplete_match/test.sh +++ b/tests/autocomplete_match/test.sh @@ -10,3 +10,4 @@ queries_in_file autocomplete "keyword-expression.js" --pretty queries_in_file autocomplete "pattern-identifier.js" --pretty queries_in_file autocomplete "pattern-binding.js" --pretty queries_in_file autocomplete "pattern-object.js" --pretty +queries_in_file autocomplete "pattern-member.js" --pretty