2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
11 open Reordered_argument_collections
15 include AutocompleteTypes
17 module Phase
= Typing_phase
18 module TUtils
= Typing_utils
21 let autocomplete_results : autocomplete_result list
ref = ref []
22 let autocomplete_is_complete : bool ref = ref true
24 (* The position we're autocompleting at. This is used when computing completions
25 * for global identifiers. *)
26 let autocomplete_identifier: (Pos.t
* string) option ref = ref None
28 type autocomplete_type
=
35 let (argument_global_type
: autocomplete_type
option ref) = ref None
36 let auto_complete_for_global = ref ""
38 let auto_complete_suffix = "AUTO332"
39 let suffix_len = String.length
auto_complete_suffix
40 let strip_suffix s
= String.sub s
0 (String.length s
- suffix_len)
42 let matches_auto_complete_suffix x
=
43 String.length x
>= suffix_len &&
44 let suffix = String.sub x
(String.length x
- suffix_len) suffix_len in
45 suffix = auto_complete_suffix
47 let is_auto_complete x
=
48 if !autocomplete_results = []
49 then matches_auto_complete_suffix x
52 let get_replace_pos_exn ~delimit_on_namespaces
=
53 match !autocomplete_identifier with
54 | None
-> failwith
"No autocomplete position was set."
56 if Pos.length pos
< suffix_len
57 then failwith
"Matched position is shorter than autocomplete suffix."
59 let open Ide_api_types
in
60 let range = pos_to_range pos
in
61 let st = if delimit_on_namespaces
62 then match String.rindex_opt text '
\\'
with
64 { range.st with column
= range.st.column
+ index
}
67 let ed = { range.ed with column
= range.ed.column
- suffix_len } in
71 let autocomplete_result_to_json res
=
72 let func_param_to_json param
=
73 Hh_json.JSON_Object
[ "name", Hh_json.JSON_String param
.param_name
;
74 "type", Hh_json.JSON_String param
.param_ty
;
75 "variadic", Hh_json.JSON_Bool param
.param_variadic
;
78 let func_details_to_json details
=
80 | Some fd
-> Hh_json.JSON_Object
[
81 "min_arity", Hh_json.int_ fd
.min_arity
;
82 "return_type", Hh_json.JSON_String fd
.return_ty
;
83 "params", Hh_json.JSON_Array
(List.map fd
.params
func_param_to_json);
85 | None
-> Hh_json.JSON_Null
87 let name = res
.res_name
in
88 let pos = res
.res_pos
in
89 let ty = res
.res_ty
in
91 "name", Hh_json.JSON_String
name;
92 "type", Hh_json.JSON_String
ty;
94 "func_details", func_details_to_json res
.func_details
;
95 "expected_ty", Hh_json.JSON_Bool
false; (* legacy field, left here in case clients need it *)
98 let get_partial_result name ty kind class_opt
=
99 let base_class = Option.map ~f
:(fun class_
-> class_
.Typing_defs.tc_name
) class_opt
in
100 Partial
{ ty; name; kind_
=kind
; base_class; }
102 let add_res (res
: autocomplete_result
) : unit =
103 autocomplete_results := res
:: !autocomplete_results
105 let add_partial_result name ty kind class_opt
=
106 add_res (get_partial_result name ty kind class_opt
)
108 let autocomplete_token ac_type env x
=
109 if is_auto_complete (snd x
)
112 autocomplete_identifier := Some x
;
113 argument_global_type
:= Some ac_type
;
114 auto_complete_for_global := snd x
117 let autocomplete_id id env
= autocomplete_token Acid
(Some env
) id
119 let autocomplete_hint = autocomplete_token Actype None
121 let autocomplete_new cid env
=
123 | Nast.CI
(sid
, _
) -> autocomplete_token Acnew
(Some env
) sid
126 let get_class_elt_types env class_ cid elts
=
127 let elts = SMap.filter
elts begin fun _ x
->
128 Tast_env.is_visible env x
.ce_visibility cid class_
130 SMap.map
elts (fun { ce_type
= lazy ty; _
} -> ty)
132 let autocomplete_member ~is_static env class_ cid id
=
133 (* This is used for instance "$x->|" and static "Class1::|" members. *)
134 (* It's also used for "<nt:fb:text |" XHP attributes, in which case *)
135 (* class_ is ":nt:fb:text" and its attributes are in tc_props. *)
136 if is_auto_complete (snd id
)
139 autocomplete_identifier := Some id
;
140 argument_global_type
:= Some Acclass_get
;
141 let add kind
name ty = add_partial_result name (Phase.decl
ty) kind
(Some class_
) in
142 if is_static
then begin
143 SMap.iter
(get_class_elt_types env class_ cid class_
.tc_smethods
) ~f
:(add Method_kind
);
144 SMap.iter
(get_class_elt_types env class_ cid class_
.tc_sprops
) ~f
:(add Property_kind
);
145 SMap.iter
(class_
.tc_consts
) ~f
:(fun name cc
-> add Class_constant_kind
name cc
.cc_type
);
147 SMap.iter
(get_class_elt_types env class_ cid class_
.tc_methods
) ~f
:(add Method_kind
);
148 SMap.iter
(get_class_elt_types env class_ cid class_
.tc_props
) ~f
:(add Property_kind
);
152 let autocomplete_lvar id env
=
153 (* This is used for "$|" and "$x = $|" local variables. *)
154 let text = Local_id.get_name
(snd id
) in
155 if is_auto_complete text
157 argument_global_type
:= Some Acprop
;
159 autocomplete_identifier := Some
(fst id
, text);
162 let should_complete_class completion_type class_kind
=
163 match completion_type
, class_kind
with
164 | Some Acid
, Some
Ast.Cnormal
165 | Some Acid
, Some
Ast.Cabstract
166 | Some Acnew
, Some
Ast.Cnormal
167 | Some Actype
, Some _
-> true
170 let should_complete_fun completion_type
=
171 completion_type
=Some Acid
173 let get_constructor_ty c
=
174 let pos = c
.Typing_defs.tc_pos
in
175 let reason = Typing_reason.Rwitness
pos in
176 let return_ty = reason, Typing_defs.Tapply
((pos, c
.Typing_defs.tc_name
), []) in
177 match (fst c
.Typing_defs.tc_construct
) with
179 begin match elt
.ce_type
with
180 | lazy (_
as r
, Tfun fun_
) ->
181 (* We have a constructor defined, but the return type is void
182 * make it the object *)
183 let fun_ = { fun_ with Typing_defs.ft_ret
= return_ty } in
185 | _
-> (* how can a constructor not be a function? *) assert false
188 (* Nothing defined, so we need to fake the entire constructor *)
191 (Typing_env.make_ft
pos Nonreactive
(*is_coroutine*)false [] return_ty)
193 (* Global identifier autocomplete uses search service to find matching names *)
194 let search_funs_and_classes input ~limit ~on_class ~on_function
=
195 HackSearchService.MasterApi.query_autocomplete input ~limit
196 ~filter_map
:begin fun _ _ res
->
197 let name = res
.SearchUtils.name in
198 match res
.SearchUtils.result_type
with
199 | HackSearchService.Class _
-> on_class
name
200 | HackSearchService.Function
-> on_function
name
204 (* compute_complete_global: given the sets content_funs and content_classes *)
205 (* of function names and classes in the current file, returns a list of all *)
206 (* possible identifier autocompletions at the autocomplete position (which *)
207 (* is stored in a global mutable reference). The results are stored in the *)
208 (* global mutable reference 'autocomplete_results'. *)
209 (* This function has two modes of dealing with namespaces... *)
210 (* delimit_on_namespaces=true delimit_on_namespaces=false *)
211 (* St| => Str Str\compare, ... *)
212 (* Str\\c| => compare Str\compare *)
213 (* Essentially, 'delimit_on_namespaces=true' means that autocomplete treats *)
214 (* namespaces as first class entities; 'false' means that it treats them *)
215 (* purely as part of long identifier names where the symbol '\\' is not *)
216 (* really any different from the symbol '_' for example. *)
219 (* This function is also called for "<foo|", with gname="foo". *)
220 (* This is a Hack shorthand for referring to the global classname ":foo". *)
221 (* Our global dictionary of classnames stores the authoritative name ":foo", *)
222 (* and inside autocompleteService we do the job of inserting a leading ":" *)
223 (* from the user's prefix, and stripping the leading ":" when we emit. *)
224 let compute_complete_global
225 ~
(tcopt
: TypecheckerOptions.t
)
226 ~
(delimit_on_namespaces
: bool)
227 ~
(autocomplete_context
: AutocompleteTypes.legacy_autocomplete_context
)
228 ~
(content_funs
: Reordered_argument_collections.SSet.t
)
229 ~
(content_classes
: Reordered_argument_collections.SSet.t
)
231 let completion_type = !argument_global_type
in
232 let gname = Utils.strip_ns
!auto_complete_for_global in
233 let gname = strip_suffix gname in
234 let gname = if autocomplete_context
.is_xhp_classname
then (":" ^
gname) else gname in
235 (* Colon hack: in "case Foo::Bar:", we wouldn't want to show autocomplete *)
236 (* here, but we would in "<nt:" and "$a->:" and "function f():". We can *)
237 (* recognize this case by whether the prefix is empty. *)
238 if autocomplete_context
.is_after_single_colon
&& gname = "" then
242 (* Objective: given "fo|", we should suggest functions in both the current *)
243 (* namespace and also in the global namespace. We'll use gname_gns to *)
244 (* indicate what prefix to look for (if any) in the global namespace... *)
246 (* "$z = S|" => gname = "S", gname_gns = Some "S" *)
247 (* "$z = Str|" => gname = "Str", gname_gns = Some "Str" *)
248 (* "$z = Str\\|" => gname = "Str\\", gname_gns = None *)
249 (* "$z = Str\\s|" => gname = "Str\\s", gname_gns = None *)
250 (* "$z = \\s|" => gname = "\\s", gname_gns = None *)
251 (* "...; |" => gname = "", gname_gns = Some "" *)
252 let gname_gns = if should_complete_fun completion_type then
253 (* Disgusting hack alert!
255 * In PHP/Hack, namespaced function lookup falls back into the global
256 * namespace if no function in the current namespace exists. The
257 * typechecker knows everything that exists, and resolves all of this
258 * during naming -- meaning that by the time that we get to typing, not
259 * only has "gname" been fully qualified, but we've lost whatever it
260 * might have looked like originally. This makes it tough to do the full
261 * namespace fallback behavior here -- we'd like to know if whatever
262 * "gname" corresponds to in the source code has a '\' to qualify it, but
263 * since it's already fully qualified here, we can't know.
265 * [NOTE(ljw): is this really even true? if user wrote "foo|" then name
266 * lookup of "fooAUTO332" is guaranteed to fail in the current namespace,
267 * and guaranteed to fail in the global namespace, so how would the
268 * typechecker fully qualify it? to what? Experimentally I've only
269 * ever seen gname to be the exact string that the user typed.]
271 * Except, we can kinda reverse engineer and figure it out. We have the
272 * positional information, which we can use to figure out how long the
273 * original source code token was, and then figure out what portion of
274 * "gname" that corresponds to, and see if it has a '\'. Since fully
275 * qualifying a name will always prepend, this all works.
277 match !autocomplete_identifier with
280 let len = (Pos.length p
) - suffix_len in
281 let start = String.length
gname - len in
282 if start < 0 || String.contains_from
gname start '
\\'
283 then None
else Some
(strip_all_ns
gname)
286 let does_fully_qualified_name_match_prefix ?
(funky_gns_rules
=false) name =
287 let stripped_name = strip_ns
name in
288 if delimit_on_namespaces
then
289 (* name must match gname, and have no additional namespace slashes, e.g. *)
290 (* name="Str\\co" gname="S" -> false *)
291 (* name="Str\\co" gname="Str\\co" -> true *)
292 string_starts_with
stripped_name gname &&
293 not
(String.contains_from
stripped_name (String.length
gname) '
\\'
)
296 | _
when string_starts_with
stripped_name gname -> true
297 | Some gns
when funky_gns_rules
-> string_starts_with
stripped_name gns
301 let string_to_replace_prefix name =
302 let stripped_name = strip_ns
name in
303 if delimit_on_namespaces
then
304 (* returns the part of 'name' after its rightmost slash *)
306 let len = String.length
stripped_name in
307 let i = (String.rindex
stripped_name '
\\'
) + 1 in
308 String.sub
stripped_name i (len - i)
315 let result_count = ref 0 in
317 let on_class name ~seen
=
318 if SSet.mem seen
name then None
else
319 if not
(does_fully_qualified_name_match_prefix name) then None
else
320 let target = Typing_lazy_heap.get_class tcopt
name in
321 let target_kind = Option.map
target ~f
:(fun c
-> c
.Typing_defs.tc_kind
) in
322 if not
(should_complete_class completion_type target_kind) then None
else
323 Option.map
target ~f
:(fun c
->
325 if completion_type = Some Acnew
then
327 (string_to_replace_prefix name)
328 (Phase.decl
(get_constructor_ty c
))
330 (* Only do doc block fallback on constructors if they're consistent. *)
331 (if snd c
.Typing_defs.tc_construct
then Some c
else None
)
333 let kind = match c
.Typing_defs.tc_kind
with
334 | Ast.Cabstract
-> Abstract_class_kind
335 | Ast.Cnormal
-> Class_kind
336 | Ast.Cinterface
-> Interface_kind
337 | Ast.Ctrait
-> Trait_kind
338 | Ast.Cenum
-> Enum_kind
341 Typing_reason.Rwitness c
.Typing_defs.tc_pos
,
342 Typing_defs.Tapply
((c
.Typing_defs.tc_pos
, name), []) in
343 get_partial_result (string_to_replace_prefix name) (Phase.decl
ty) kind None
347 let on_function name ~seen
=
348 if autocomplete_context
.is_xhp_classname
then None
else
349 if SSet.mem seen
name then None
else
350 if not
(should_complete_fun completion_type) then None
else
351 if not
(does_fully_qualified_name_match_prefix ~funky_gns_rules
:true name) then None
else
352 Option.map
(Typing_lazy_heap.get_fun tcopt
name) ~f
:(fun fun_ ->
354 let ty = Typing_reason.Rwitness
fun_.Typing_defs.ft_pos
, Typing_defs.Tfun
fun_ in
355 get_partial_result (string_to_replace_prefix name) (Phase.decl
ty) Function_kind None
359 let on_namespace name : autocomplete_result
option =
360 (* name will have the form "Str" or "HH\\Lib\\Str" *)
361 (* Our autocomplete will show up in the list as "Str". *)
362 if autocomplete_context
.is_xhp_classname
then None
else
363 if not delimit_on_namespaces
then None
else
364 if not
(does_fully_qualified_name_match_prefix name) then None
else
366 res_pos
= Pos.none
|> Pos.to_absolute
;
367 res_replace_pos
= get_replace_pos_exn ~delimit_on_namespaces
;
368 res_base_class
= None
;
369 res_ty
= "namespace";
370 res_name
= string_to_replace_prefix name;
371 res_kind
= Namespace_kind
;
376 (* Try using the names in local content buffer first *)
378 (List.filter_map
(SSet.elements content_classes
) (on_class ~seen
:SSet.empty
))
381 (List.filter_map
(SSet.elements content_funs
) (on_function ~seen
:SSet.empty
))
384 (* Add namespaces. The hack server doesn't index namespaces themselves; it *)
385 (* only stores names of functions and classes in fully qualified form, e.g. *)
386 (* \\HH\\Lib\\Str\\length *)
387 (* If the project's .hhconfig has auto_namesspace_map "Str": "HH\Lib\\Str" *)
388 (* then the hack server will index the function just as *)
390 (* The main index, having only a global list if functions/classes, doesn't *)
391 (* actually offer any way for us to iterate over namespaces. And changing *)
392 (* its trie-indexer to do so is kind of ugly. So as a temporary workaround, *)
393 (* to give an okay user-experience at least for the Hack standard library, *)
394 (* we're just going to list all the possible standard namespaces right here *)
395 (* and see if any of them really exist in the current codebase/hhconfig! *)
396 (* This will give a good experience only for codebases where users rarely *)
397 (* define their own namespaces... *)
398 let standard_namespaces =
399 ["C"; "Vec"; "Dict"; "Str"; "Keyset"; "Math"; "Regex"; "SecureRandom"; "PHP"; "JS"] in
400 let namespace_permutations ns
= [
401 Printf.sprintf
"%s" ns
;
402 Printf.sprintf
"%s\\fb" ns
;
403 Printf.sprintf
"HH\\Lib\\%s" ns
;
404 Printf.sprintf
"HH\\Lib\\%s\\fb" ns
;
406 let all_possible_namespaces =
407 List.map
standard_namespaces ~f
:namespace_permutations |> List.concat
409 List.iter
all_possible_namespaces ~f
:(fun ns
->
410 let ns_results = search_funs_and_classes (ns ^
"\\") ~limit
:(Some
1)
411 ~
on_class:(fun _className
-> on_namespace ns
)
412 ~
on_function:(fun _functionName
-> on_namespace ns
)
414 List.iter
ns_results.With_complete_flag.value add_res
417 (* Use search results to look for matches, while excluding names we have
418 * already seen in local content buffer *)
419 let gname_results = search_funs_and_classes gname ~limit
:(Some
100)
420 ~
on_class:(on_class ~seen
:content_classes
)
421 ~
on_function:(on_function ~seen
:content_funs
)
423 autocomplete_is_complete :=
424 !autocomplete_is_complete && gname_results.With_complete_flag.is_complete
;
425 List.iter
gname_results.With_complete_flag.value add_res;
427 (* Compute global namespace fallback results for functions, if applicable *)
429 | Some
gname_gns when gname <> gname_gns ->
430 let gname_gns_results = search_funs_and_classes gname_gns ~limit
:(Some
100)
431 ~
on_class:(fun _
-> None
)
432 ~
on_function:(on_function ~seen
:content_funs
)
434 autocomplete_is_complete :=
435 !autocomplete_is_complete && gname_gns_results.With_complete_flag.is_complete
;
436 List.iter
gname_gns_results.With_complete_flag.value add_res;
440 (* Here we turn partial_autocomplete_results into complete_autocomplete_results *)
441 (* by using typing environment to convert ty information into strings. *)
444 (autocomplete_context
: legacy_autocomplete_context
)
445 (x
: partial_autocomplete_result
)
446 ~
(delimit_on_namespaces
: bool)
447 : complete_autocomplete_result
=
448 let env, ty = match x
.ty with
449 | DeclTy
ty -> Tast_env.localize_with_self
env ty
450 | LoclTy
ty -> env, ty
452 let desc_string = match x
.kind_
with
457 | Class_constant_kind
458 | Constructor_kind
-> Tast_env.print_ty
env ty
459 | Abstract_class_kind
-> "abstract class"
460 | Class_kind
-> "class"
461 | Interface_kind
-> "interface"
462 | Trait_kind
-> "trait"
463 | Enum_kind
-> "enum"
464 | Namespace_kind
-> "namespace"
465 | Keyword_kind
-> "keyword"
467 let func_details = match ty with
469 let param_to_record ?
(is_variadic
=false) param
=
471 param_name
= (match param
.fp_name
with
474 param_ty
= Tast_env.print_ty
env param
.fp_type
;
475 param_variadic
= is_variadic
;
479 return_ty = Tast_env.print_ty
env ft
.ft_ret
;
480 min_arity
= arity_min ft
.ft_arity
;
481 params
= List.map ft
.ft_params
param_to_record @
482 (match ft
.ft_arity
with
484 let empty = TUtils.default_fun_param
(Reason.none
, Tany
) in
485 [param_to_record ~is_variadic
:true empty]
486 | Fvariadic
(_
, p
) -> [param_to_record ~is_variadic
:true p
]
491 (* XHP class+attribute names are stored internally with a leading colon. *)
492 (* We'll render them without it if and only if we're in an XHP context. *)
493 (* $x = new :class1() -- do strip the colon in front of :class1 *)
494 (* $x = <:class1 -- don't strip it *)
495 (* $x->:attr1 -- don't strip the colon in front of :attr1 *)
496 (* <class1 :attr="a" -- do strip it *)
497 (* The logic is thorny here because we're relying upon regexes to figure *)
498 (* out the context. Once we switch autocomplete to FFP, it'll be cleaner. *)
499 let name = match x
.kind_
, autocomplete_context
with
500 | Property_kind
, { AutocompleteTypes.is_instance_member
= false; _
} -> lstrip x
.name ":"
501 | Abstract_class_kind
, { AutocompleteTypes.is_xhp_classname
= true; _
}
502 | Class_kind
, { AutocompleteTypes.is_xhp_classname
= true; _
} -> lstrip x
.name ":"
506 res_pos
= (fst
ty) |> Typing_reason.to_pos
|> Pos.to_absolute
;
507 res_replace_pos
= get_replace_pos_exn ~delimit_on_namespaces
;
508 res_base_class
= x
.base_class;
509 res_ty
= desc_string;
512 func_details = func_details;
515 let tast_cid_to_nast_cid env cid
=
516 let nmenv = Tast.nast_mapping_env
(Tast_env.save
env) in
517 Tast.NastMapper.map_class_id_
nmenv cid
519 let autocomplete_typed_member ~is_static
env class_ty cid mid
=
520 Tast_env.get_class_ids
env class_ty
521 |> List.iter ~f
:begin fun cname
->
522 Typing_lazy_heap.get_class
(Tast_env.get_tcopt
env) cname
523 |> Option.iter ~f
:begin fun class_
->
524 let cid = Option.map
cid (tast_cid_to_nast_cid env) in
525 autocomplete_member ~is_static
env class_
cid mid
529 let autocomplete_static_member env (ty, cid) mid
=
530 autocomplete_typed_member ~is_static
:true env ty (Some
cid) mid
533 inherit Tast_visitor.iter
as super
535 method! on_Id
env id
=
536 autocomplete_id id
env;
539 method! on_Fun_id
env id
=
540 autocomplete_id id
env;
541 super#on_Fun_id
env id
543 method! on_New
env cid el uel
=
544 autocomplete_new (tast_cid_to_nast_cid env (snd
cid)) env;
545 super#on_New
env cid el uel
547 method! on_Happly
env sid hl
=
548 autocomplete_hint sid
;
549 super#on_Happly
env sid hl
551 method! on_Lvar
env lid
=
552 autocomplete_lvar lid
env;
553 super#on_Lvar
env lid
555 method! on_Class_get
env cid mid
=
556 autocomplete_static_member env cid mid
;
557 super#on_Class_get
env cid mid
559 method! on_Class_const
env cid mid
=
560 autocomplete_static_member env cid mid
;
561 super#on_Class_const
env cid mid
563 method! on_Obj_get
env obj mid ognf
=
566 autocomplete_typed_member ~is_static
:false env (Tast.get_type obj
) None mid
569 super#on_Obj_get
env obj mid ognf
571 method! on_Xml
env sid attrs el
=
572 let cid = Nast.CI
(sid
, []) in
573 Typing_lazy_heap.get_class
(Tast_env.get_tcopt
env) (snd sid
)
574 |> Option.iter ~f
:begin fun c
->
575 List.iter attrs ~f
:begin function
576 | Tast.Xhp_simple
(id
, _
) ->
577 autocomplete_member ~is_static
:false env c
(Some
cid) id
578 | Tast.Xhp_spread _
-> ()
581 super#on_Xml
env sid attrs el
584 let auto_complete_suffix_finder = object
585 inherit [_
] Tast.reduce
588 method! on_Lvar
() (_
, id
) =
589 matches_auto_complete_suffix (Local_id.get_name id
)
592 let method_contains_cursor = auto_complete_suffix_finder#on_method_
()
593 let fun_contains_cursor = auto_complete_suffix_finder#on_fun_
()
595 class local_types
= object (self
)
596 inherit Tast_visitor.iter
as super
598 val mutable results
= Local_id.Map.empty;
599 val mutable after_cursor
= false;
601 method get_types tast
=
606 (* If we already have a type for this identifier, don't overwrite it with
607 results from after the cursor position. *)
608 if not
(Local_id.Map.mem id results
&& after_cursor
) then
609 results
<- Local_id.Map.add id
ty results
611 method! on_fun_
env f
=
612 if fun_contains_cursor f
then
615 method! on_method_
env m
=
616 if method_contains_cursor m
then begin
617 if not
(Tast_env.is_static
env) then
618 self#
add Typing_defs.this
(Tast_env.get_self
env);
619 super#on_method_
env m
622 method! on_expr
env e
=
623 let (_
, ty), e_
= e
in
625 | Tast.Lvar
(_
, id
) ->
626 if matches_auto_complete_suffix (Local_id.get_name id
)
627 then after_cursor
<- true
629 | Tast.Binop
(Ast.Eq _
, e1
, e2
) ->
630 (* Process the rvalue before the lvalue, since the lvalue is annotated
631 with its type after the assignment. *)
634 | _
-> super#on_expr
env e
636 method! on_fun_param _ fp
=
637 let id = Local_id.get fp
.Tast.param_name
in
638 let _, ty = fp
.Tast.param_annotation
in
642 let compute_complete_local tast
=
643 new local_types#get_types tast
644 |> Local_id.Map.iter
begin fun x
ty ->
645 add_partial_result (Local_id.get_name x
) (Phase.locl
ty) Variable_kind None
649 auto_complete_for_global := "";
650 argument_global_type
:= None
;
651 autocomplete_identifier := None
;
653 autocomplete_results := [];
654 autocomplete_is_complete := true
658 ~delimit_on_namespaces
661 ~autocomplete_context
666 Errors.ignore_
begin fun () ->
667 let completion_type = !argument_global_type
in
668 if completion_type = Some Acid
||
669 completion_type = Some Acnew
||
670 completion_type = Some Actype
671 then compute_complete_global
672 ~tcopt ~delimit_on_namespaces ~autocomplete_context ~content_funs ~content_classes
;
673 if completion_type = Some Acprop
then compute_complete_local tast
;
674 let env = match !ac_env with
676 | None
-> Tast_env.empty tcopt
678 let resolve (result
: autocomplete_result
) : complete_autocomplete_result
=
680 | Partial res
-> resolve_ty env autocomplete_context res ~delimit_on_namespaces
681 | Complete res
-> res
684 With_complete_flag.is_complete
= !autocomplete_is_complete;
685 value = !autocomplete_results |> List.map ~f
:resolve;