hphp/hack/src/server/identifySymbolService.ml (788 lines of code) (raw):
(*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the "hack" directory of this source tree.
*
*)
open Hh_prelude
open SymbolOccurrence
open Typing_defs
module SN = Naming_special_names
module Result_set = Caml.Set.Make (struct
type t = Relative_path.t SymbolOccurrence.t
let compare : t -> t -> int = SymbolOccurrence.compare Relative_path.compare
end)
let is_target target_line target_char { pos; _ } =
let (l, start, end_) = Pos.info_pos pos in
l = target_line && start <= target_char && target_char - 1 <= end_
let process_class_id
?(is_declaration = false) ?(class_id_type = ClassId) (pos, cid) =
Result_set.singleton
{ name = cid; type_ = Class class_id_type; is_declaration; pos }
let process_attribute (pos, name) class_name method_ =
let type_ =
match (name, class_name, method_) with
| (name, Some (_, class_name), Some ((_, method_name), is_static))
when String.equal name Naming_special_names.UserAttributes.uaOverride ->
Attribute (Some { class_name; method_name; is_static })
| _ -> Attribute None
in
Result_set.singleton { name; type_; is_declaration = false; pos }
let process_xml_attrs class_name attrs =
List.fold attrs ~init:Result_set.empty ~f:(fun acc attr ->
match attr with
| Aast.Xhp_simple { Aast.xs_name = (pos, name); _ } ->
Result_set.add
{
name;
type_ = XhpLiteralAttr (class_name, Utils.add_xhp_ns name);
is_declaration = false;
pos;
}
acc
| _ -> acc)
let clean_member_name name = String_utils.lstrip name "$"
let process_member ?(is_declaration = false) recv_class id ~is_method ~is_const
=
let member_name = snd id in
let type_ =
if is_const then
ClassConst (recv_class, member_name)
else if is_method then
Method (recv_class, member_name)
else
(*
Per comment in symbolOcurrence.mli, XhpLiteralAttr
is only used for attributes in XHP literals. Since
process_member is not being used to handle XML attributes
it is fine to define every symbol as Property.
*)
Property (recv_class, member_name)
in
let c_name =
match recv_class with
| ClassName name -> name
| UnknownClass -> "_"
in
Result_set.singleton
{
name = c_name ^ "::" ^ clean_member_name member_name;
type_;
is_declaration;
pos = fst id;
}
let process_fun_id ?(is_declaration = false) id =
Result_set.singleton
{ name = snd id; type_ = Function; is_declaration; pos = fst id }
let process_global_const ?(is_declaration = false) id =
Result_set.singleton
{ name = snd id; type_ = GConst; is_declaration; pos = fst id }
let process_lvar_id ?(is_declaration = false) id =
Result_set.singleton
{ name = snd id; type_ = LocalVar; is_declaration; pos = fst id }
let process_typeconst ?(is_declaration = false) (class_name, tconst_name, pos) =
Result_set.singleton
{
name = class_name ^ "::" ^ tconst_name;
type_ = Typeconst (class_name, tconst_name);
is_declaration;
pos;
}
let process_class class_ =
let acc = process_class_id ~is_declaration:true class_.Aast.c_name in
let c_name = snd class_.Aast.c_name in
let (constructor, static_methods, methods) =
Aast.split_methods class_.Aast.c_methods
in
let all_methods = static_methods @ methods in
let acc =
List.fold all_methods ~init:acc ~f:(fun acc method_ ->
Result_set.union acc
@@ process_member
(ClassName c_name)
method_.Aast.m_name
~is_declaration:true
~is_method:true
~is_const:false)
in
let all_props = class_.Aast.c_vars in
let acc =
List.fold all_props ~init:acc ~f:(fun acc prop ->
Result_set.union acc
@@ process_member
(ClassName c_name)
prop.Aast.cv_id
~is_declaration:true
~is_method:false
~is_const:false)
in
let acc =
List.fold class_.Aast.c_consts ~init:acc ~f:(fun acc const ->
Result_set.union acc
@@ process_member
(ClassName c_name)
const.Aast.cc_id
~is_declaration:true
~is_method:false
~is_const:true)
in
let acc =
List.fold class_.Aast.c_typeconsts ~init:acc ~f:(fun acc typeconst ->
let (pos, tconst_name) = typeconst.Aast.c_tconst_name in
Result_set.union acc
@@ process_typeconst ~is_declaration:true (c_name, tconst_name, pos))
in
(* We don't check anything about xhp attributes, so the hooks won't fire when
typechecking the class. Need to look at them individually. *)
let acc =
List.fold class_.Aast.c_xhp_attr_uses ~init:acc ~f:(fun acc attr ->
match attr with
| (_, Aast.Happly (cid, _)) ->
Result_set.union acc @@ process_class_id cid
| _ -> acc)
in
match constructor with
| Some method_ ->
let id = (fst method_.Aast.m_name, SN.Members.__construct) in
Result_set.union acc
@@ process_member
(ClassName c_name)
id
~is_declaration:true
~is_method:true
~is_const:false
| None -> acc
let typed_member_id env receiver_ty mid ~is_method ~is_const =
Tast_env.get_receiver_ids env receiver_ty
|> List.map ~f:(function
| Tast_env.RIclass cid -> ClassName cid
| Tast_env.RIdynamic
| Tast_env.RIerr
| Tast_env.RIany ->
UnknownClass)
|> List.map ~f:(fun rid -> process_member rid mid ~is_method ~is_const)
|> List.fold ~init:Result_set.empty ~f:Result_set.union
let typed_method = typed_member_id ~is_method:true ~is_const:false
let typed_const = typed_member_id ~is_method:false ~is_const:true
let typed_property = typed_member_id ~is_method:false ~is_const:false
let typed_constructor env ty pos =
typed_method env ty (pos, SN.Members.__construct)
let typed_class_id ?(class_id_type = ClassId) env ty pos =
Tast_env.get_class_ids env ty
|> List.map ~f:(fun cid -> process_class_id ~class_id_type (pos, cid))
|> List.fold ~init:Result_set.empty ~f:Result_set.union
(* When we detect a function reference encapsulated in a string,
* we want to update the function reference without removing the apostrophes.
*
* Example: class_meth(myclass::class, 'myfunc');
*
* In this case, we only want to replace the text 'myfunc' - so we need
* to shrink our positional data by the apostrophes. *)
let remove_apostrophes_from_function_eval (mid : Ast_defs.pstring) :
Ast_defs.pstring =
let (pos, member_name) = mid in
let new_pos = Pos.shrink_by_one_char_both_sides pos in
(new_pos, member_name)
let visitor =
let class_name = ref None in
let method_name = ref None in
object (self)
inherit [_] Tast_visitor.reduce as super
method zero = Result_set.empty
method plus = Result_set.union
method! on_expr env expr =
let (_, pos, expr_) = expr in
let ( + ) = self#plus in
let acc =
match expr_ with
| Aast.Hole (_e, _ty, _ty2, Aast.UnsafeCast _) ->
process_fun_id (pos, SN.PseudoFunctions.unsafe_cast)
| Aast.New ((ty, p, _), _, _, _, _) -> typed_constructor env ty p
| Aast.Obj_get ((ty, _, _), (_, _, Aast.Id mid), _, _) ->
typed_property env ty mid
| Aast.Class_const ((ty, _, _), mid) -> typed_const env ty mid
| Aast.Class_get ((ty, _, _), Aast.CGstring mid, _) ->
typed_property env ty mid
| Aast.Xml (cid, attrs, _) ->
let class_id = process_class_id cid in
let xhp_attributes = process_xml_attrs (snd cid) attrs in
self#plus class_id xhp_attributes
| Aast.Fun_id id ->
process_fun_id (pos, SN.AutoimportedFunctions.fun_)
+ process_fun_id (remove_apostrophes_from_function_eval id)
| Aast.FunctionPointer (Aast.FP_id id, _targs) -> process_fun_id id
| Aast.FunctionPointer (Aast.FP_class_const ((ty, _, _cid), mid), _targs)
->
typed_method env ty mid
| Aast.Method_id ((ty, _, _), mid) ->
process_fun_id (pos, SN.AutoimportedFunctions.inst_meth)
+ typed_method env ty (remove_apostrophes_from_function_eval mid)
| Aast.Smethod_id ((ty, _, _), mid) ->
process_fun_id (pos, SN.AutoimportedFunctions.class_meth)
+ typed_method env ty (remove_apostrophes_from_function_eval mid)
| Aast.Method_caller (((_, cid) as pcid), mid) ->
process_fun_id (pos, SN.AutoimportedFunctions.meth_caller)
+ process_class_id pcid
+ process_member
(ClassName cid)
(remove_apostrophes_from_function_eval mid)
~is_method:true
~is_const:false
| Aast.ValCollection (kind, _, _) ->
let type_name = Aast.show_vc_kind kind in
process_class_id (pos, "\\HH\\" ^ type_name)
| Aast.KeyValCollection (kind, _, _) ->
let type_name = Aast.show_kvc_kind kind in
process_class_id (pos, "\\HH\\" ^ type_name)
| Aast.EnumClassLabel (enum_name, label_name) ->
begin
match enum_name with
| None ->
let (ety, _, _) = expr in
let ty = Typing_defs_core.get_node ety in
(match ty with
| Tnewtype (_, [ty_enum_class; _], _) ->
(match get_node ty_enum_class with
| Tclass ((_, enum_class_name), _, _)
| Tgeneric (enum_class_name, _) ->
Result_set.singleton
{
name = Utils.strip_ns enum_class_name ^ "#" ^ label_name;
type_ = EnumClassLabel (enum_class_name, label_name);
is_declaration = false;
pos;
}
| _ -> self#zero)
| _ -> self#zero)
| Some (_, enum_name) ->
Result_set.singleton
{
name = Utils.strip_ns enum_name ^ "#" ^ label_name;
type_ = EnumClassLabel (enum_name, label_name);
is_declaration = false;
pos;
}
end
| _ -> self#zero
in
acc + super#on_expr env expr
method! on_expression_tree
env
Aast.
{
et_hint;
et_virtualized_expr;
et_splices;
et_function_pointers;
et_runtime_expr = _;
et_dollardollar_pos = _;
} =
(* We only want to consider completion from the hint and the
virtualized expression, not the visitor expression. The
visitor expression is unityped, so we can't do much.*)
let acc = self#on_hint env et_hint in
let acc = self#plus acc (self#on_Block env et_splices) in
(* We're overriding super#on_expression_tree, so we need to
update the environment. *)
let env = Tast_env.set_in_expr_tree env true in
let acc = self#plus acc (self#on_Block env et_function_pointers) in
let (_, _, virtualized_expr_) = et_virtualized_expr in
let e =
match virtualized_expr_ with
| Aast.Call
( ( _,
_,
Aast.Efun
( {
Aast.f_body =
{ Aast.fb_ast = [(_, Aast.Return (Some e))]; _ };
_;
},
_ ) ),
_,
_,
_ ) ->
(* The virtualized expression is wrapped in an invoked
lambda to help check unbound variables, which leads to
unwanted closure info in hovers. Use the inner
expression directly. *)
e
| _ -> et_virtualized_expr
in
self#plus acc (self#on_expr env e)
method! on_class_id env (ty, p, cid) =
match cid with
| Aast.CIexpr expr ->
(* We want to special case this because we want to get the type of the
inner expression, which will have a type like `classname<Foo>`, rather
than the resolved type of the class ID, which will have a type like
`Foo`. Since the class ID and the inner expression have the same span,
it is not easy to distinguish them later. *)
self#on_expr env expr
| Aast.CIparent
| Aast.CIself
| Aast.CIstatic ->
(* We want to special case these because we want to keep track of the
original class id type. This information is useful in some cases, for
instance when refactoring class names, because we want to avoid
refactoring `self`, `static`, and `parent` class ids. *)
typed_class_id ~class_id_type:Other env ty p
| Aast.CI _ -> typed_class_id env ty p
method! on_Call env ((_, _, expr_) as e) tal el unpacked_element =
(* For Id, Obj_get (with an Id member), and Class_const, we don't want to
* use the result of `self#on_expr env e`, since it would record a
* property, class const, or global const access instead of a method call.
* So instead of invoking super#on_Call, we reimplement it here, omitting
* `self#on_expr env e` when necessary. *)
let ( + ) = self#plus in
let ea =
match expr_ with
| Aast.Call
((_, _, Aast.Class_const (_, (_, methName))), _, [(_, arg)], _)
when Tast_env.is_in_expr_tree env
&& String.equal methName SN.ExpressionTrees.symbolType ->
(* Treat MyVisitor::symbolType(foo<>) as just foo(). *)
self#on_expr env arg
| Aast.Id id -> process_fun_id id
| Aast.Obj_get (((ty, _, _) as obj), (_, _, Aast.Id mid), _, _) ->
self#on_expr env obj + typed_method env ty mid
| Aast.Class_const (((ty, _, _) as cid), mid) ->
self#on_class_id env cid + typed_method env ty mid
| _ -> self#on_expr env e
in
let tala = self#on_list self#on_targ env tal in
let ela = self#on_list self#on_expr env (List.map ~f:snd el) in
let uea =
Option.value_map
~default:Result_set.empty
~f:(self#on_expr env)
unpacked_element
in
ea + tala + ela + uea
method! on_Haccess env root ids =
let acc =
Tast_env.referenced_typeconsts env root ids
|> List.map ~f:process_typeconst
|> List.fold ~init:self#zero ~f:self#plus
in
self#plus acc (super#on_Haccess env root ids)
method! on_tparam env tp =
let (pos, name) = tp.Aast.tp_name in
let acc =
Result_set.singleton
{ name; type_ = TypeVar; is_declaration = true; pos }
in
self#plus acc (super#on_tparam env tp)
method! on_Lvar env (pos, id) =
let acc =
if Local_id.is_user_denotable id then
process_lvar_id (pos, Local_id.get_name id)
else
Result_set.empty
in
self#plus acc (super#on_Lvar env (pos, id))
method! on_hint env h =
let acc =
match h with
| (pos, Aast.Habstr (name, _)) ->
Result_set.singleton
{ name; type_ = TypeVar; is_declaration = false; pos }
| (pos, Aast.Hprim prim) ->
let name = Aast_defs.string_of_tprim prim in
Result_set.singleton
{
name;
type_ = BuiltInType (BIprimitive prim);
is_declaration = false;
pos;
}
| (pos, Aast.Hnothing) ->
Result_set.singleton
{
name = "nothing";
type_ = BuiltInType BInothing;
is_declaration = false;
pos;
}
| (pos, Aast.Hmixed) ->
Result_set.singleton
{
name = "mixed";
type_ = BuiltInType BImixed;
is_declaration = false;
pos;
}
| (pos, Aast.Hnonnull) ->
Result_set.singleton
{
name = "nonnull";
type_ = BuiltInType BInonnull;
is_declaration = false;
pos;
}
| (pos, Aast.Hdynamic) ->
Result_set.singleton
{
name = "dynamic";
type_ = BuiltInType BIdynamic;
is_declaration = false;
pos;
}
| (pos, Aast.Hshape _) ->
Result_set.singleton
{
name = "shape";
type_ = BuiltInType BIshape;
is_declaration = false;
pos;
}
| (pos, Aast.Hthis) ->
Result_set.singleton
{
name = "this";
type_ = BuiltInType BIthis;
is_declaration = false;
pos;
}
| (pos, Aast.Hoption _) ->
Result_set.singleton
{
name = "?";
type_ = BuiltInType BIoption;
is_declaration = false;
pos;
}
| _ -> Result_set.empty
in
self#plus acc (super#on_hint env h)
method! on_fun_param env param =
let acc = process_lvar_id (param.Aast.param_pos, param.Aast.param_name) in
self#plus acc (super#on_fun_param env param)
method! on_Happly env sid hl =
let acc = process_class_id sid in
self#plus acc (super#on_Happly env sid hl)
method! on_catch env (sid, lid, block) =
let acc = process_class_id sid in
self#plus acc (super#on_catch env (sid, lid, block))
method! on_class_ env class_ =
class_name := Some class_.Aast.c_name;
let acc = process_class class_ in
(*
Enums implicitly extend BuiltinEnum. However, BuiltinEnums also extend
the same Enum as a type parameter.
Ex: enum Size extends BuiltinEnum<Size> { ... }
This will return the definition of the enum twice when finding references
on it. As a result, we set the extends property of an enum's tast to an empty list.
The same situation applies to Enum classes that extends
BuiltinEnumClass. However in this case we just want to filter out
this one extends, and keep the other unchanged.
*)
let class_ =
let open Aast in
let c_name = snd class_.c_name in
(* Checks if the hint is matching the pattern
* `HH\BuiltinEnumClass<HH\MemberOf<c_name, _>>`
*)
let is_generated_builtin_enum_class = function
| ( _,
Happly
( (_, builtin_enum_class),
[
( _,
Happly
( (_, memberof),
[(_, Happly ((_, name), [])); _interface] ) );
] ) ) ->
String.equal builtin_enum_class SN.Classes.cHH_BuiltinEnumClass
&& String.equal memberof SN.Classes.cMemberOf
&& String.equal name c_name
| (_, Happly ((_, builtin_abstract_enum_class), [])) ->
String.equal
builtin_abstract_enum_class
SN.Classes.cHH_BuiltinAbstractEnumClass
| _ -> false
in
(* Checks if the hint is matching the pattern
* `HH\BuiltinEnum<c_name>`
*)
let is_generated_builtin_enum = function
| (_, Happly ((_, builtin_enum), [(_, Happly ((_, name), []))])) ->
String.equal builtin_enum SN.Classes.cHH_BuiltinEnum
&& String.equal name c_name
| _ -> false
in
(* If the class is an enum or enum class, remove the generated
* occurrences.
*)
if Ast_defs.is_c_enum_class class_.c_kind then
(* Enum classes might extend other classes, so we filter
* the list and we don't depend on their order.
*)
let c_extends =
List.filter_map
~f:(fun h ->
if is_generated_builtin_enum_class h then
(* don't take this occurrence into account *)
None
else
Some h)
class_.c_extends
in
(* We also have to take care of the type of constants that
* are rewritten from Foo to MemberOf<EnumName, Foo>
*)
let c_consts =
List.map
~f:(fun cc ->
let cc_type =
Option.map
~f:(fun h ->
match snd h with
| Happly ((_, name), [_; h])
when String.equal name SN.Classes.cMemberOf ->
h
| _ -> h)
cc.cc_type
in
{ cc with cc_type })
class_.c_consts
in
{ class_ with c_extends; c_consts }
else if Ast_defs.is_c_enum class_.c_kind then
(* For enums, we could remove everything as they don't extends
* other classes, but let's filter anyway, just to be resilient
* to future evolutions
*)
let c_extends =
List.filter_map
~f:(fun h ->
if is_generated_builtin_enum h then
(* don't take this occurrence into account *)
None
else
Some h)
class_.c_extends
in
{ class_ with c_extends }
else
class_
in
let acc = self#plus acc (super#on_class_ env class_) in
class_name := None;
acc
method! on_fun_ env fun_ =
let acc = process_fun_id ~is_declaration:true fun_.Aast.f_name in
self#plus acc (super#on_fun_ env { fun_ with Aast.f_unsafe_ctxs = None })
method! on_typedef env typedef =
let acc = process_class_id ~is_declaration:true typedef.Aast.t_name in
self#plus acc (super#on_typedef env typedef)
method! on_gconst env cst =
let acc = process_global_const ~is_declaration:true cst.Aast.cst_name in
self#plus acc (super#on_gconst env cst)
method! on_Id env id =
let acc = process_global_const id in
self#plus acc (super#on_Id env id)
method! on_Obj_get env obj ((_, _, expr_) as member) ognf prop_or_method =
match expr_ with
| Aast.Id _ ->
(* Don't visit this Id, since we would record it as a gconst access. *)
let obja = self#on_expr env obj in
let ognfa = self#on_og_null_flavor env ognf in
self#plus obja ognfa
| _ -> super#on_Obj_get env obj member ognf prop_or_method
method! on_SFclass_const env cid mid =
let ( + ) = Result_set.union in
process_class_id cid
+ process_member (ClassName (snd cid)) mid ~is_method:false ~is_const:true
+ super#on_SFclass_const env cid mid
method! on_method_ env m =
method_name := Some (m.Aast.m_name, m.Aast.m_static);
let acc = super#on_method_ env { m with Aast.m_unsafe_ctxs = None } in
method_name := None;
acc
method! on_user_attribute env ua =
let acc = process_attribute ua.Aast.ua_name !class_name !method_name in
self#plus acc (super#on_user_attribute env ua)
end
(* Types of decls used in keyword extraction.*)
type decl_kind =
| DKclass
| DKinterface
type keyword_context =
| Decl of decl_kind
| Method
| Parameter
| ReturnType
| AsyncBlockHeader
(** Get keyword positions from the FFP for every keyword that has hover
documentation. **)
let keywords ctx entry : Result_set.elt list =
let cst = Ast_provider.compute_cst ~ctx ~entry in
let tree = Provider_context.PositionedSyntaxTree.root cst in
let open Full_fidelity_positioned_syntax in
let token_pos (t : Token.t) =
let offset = t.Token.offset + t.Token.leading_width in
Full_fidelity_source_text.relative_pos
entry.Provider_context.path
t.Token.source_text
offset
(offset + t.Token.width)
in
let syntax_pos s =
let offset = start_offset s + leading_width s in
Full_fidelity_source_text.relative_pos
entry.Provider_context.path
(source_text s)
offset
(offset + width s)
in
let rec aux ctx acc s =
match s.syntax with
| ClassishDeclaration cd ->
let decl_kind =
match cd.classish_keyword.syntax with
| Token t ->
(match t.Token.kind with
| Token.TokenKind.Class -> DKclass
| Token.TokenKind.Interface -> DKinterface
| _ -> DKclass)
| _ -> DKclass
in
let ctx = Some (Decl decl_kind) in
List.fold (children s) ~init:acc ~f:(aux ctx)
| MethodishDeclaration _ ->
List.fold (children s) ~init:acc ~f:(aux (Some Method))
| FunctionDeclarationHeader fdh ->
let acc = aux ctx acc fdh.function_modifiers in
let acc = aux ctx acc fdh.function_parameter_list in
let acc = aux ctx acc fdh.function_contexts in
aux (Some ReturnType) acc fdh.function_readonly_return
| ParameterDeclaration pd -> aux (Some Parameter) acc pd.parameter_readonly
| AwaitableCreationExpression ace ->
let acc = aux ctx acc ace.awaitable_attribute_spec in
let acc = aux (Some AsyncBlockHeader) acc ace.awaitable_async in
aux ctx acc ace.awaitable_compound_statement
| Contexts c ->
let is_empty =
match c.contexts_types.syntax with
| Missing -> true
| _ -> false
in
if is_empty then
{
name = "pure function";
type_ = PureFunctionContext;
is_declaration = false;
pos = syntax_pos s;
}
:: acc
else
acc
| Token t ->
(match t.Token.kind with
| Token.TokenKind.Extends ->
{
name = "extends";
type_ =
Keyword
(match ctx with
| Some (Decl DKclass) -> ExtendsOnClass
| Some (Decl DKinterface) -> ExtendsOnInterface
| _ -> ExtendsOnClass);
is_declaration = false;
pos = token_pos t;
}
:: acc
| Token.TokenKind.Abstract ->
{
name = "abstract";
type_ =
Keyword
(match ctx with
| Some Method -> AbstractOnMethod
| _ -> AbstractOnClass);
is_declaration = false;
pos = token_pos t;
}
:: acc
| Token.TokenKind.Final ->
{
name = "final";
type_ =
Keyword
(match ctx with
| Some Method -> FinalOnMethod
| _ -> FinalOnClass);
is_declaration = false;
pos = token_pos t;
}
:: acc
| Token.TokenKind.Async ->
{
name = "async";
type_ =
Keyword
(match ctx with
| Some AsyncBlockHeader -> AsyncBlock
| _ -> Async);
is_declaration = false;
pos = token_pos t;
}
:: acc
| Token.TokenKind.Await ->
{
name = "await";
type_ = Keyword Await;
is_declaration = false;
pos = token_pos t;
}
:: acc
| Token.TokenKind.Concurrent ->
{
name = "concurrent";
type_ = Keyword Concurrent;
is_declaration = false;
pos = token_pos t;
}
:: acc
| Token.TokenKind.Readonly ->
{
name = "readonly";
type_ =
Keyword
(match ctx with
| Some Method -> ReadonlyOnMethod
| Some Parameter -> ReadonlyOnParameter
| Some ReturnType -> ReadonlyOnReturnType
| _ -> ReadonlyOnExpression);
is_declaration = false;
pos = token_pos t;
}
:: acc
| _ -> acc)
| _ -> List.fold (children s) ~init:acc ~f:(aux ctx)
in
aux None [] tree
let all_symbols ctx tast =
Errors.ignore_ (fun () -> visitor#go ctx tast |> Result_set.elements)
let all_symbols_ctx
~(ctx : Provider_context.t) ~(entry : Provider_context.entry) :
Result_set.elt list =
match entry.Provider_context.symbols with
| None ->
let keyword_symbols = keywords ctx entry in
let { Tast_provider.Compute_tast.tast; _ } =
Tast_provider.compute_tast_quarantined ~ctx ~entry
in
let symbols = keyword_symbols @ all_symbols ctx tast in
entry.Provider_context.symbols <- Some symbols;
symbols
| Some symbols -> symbols
let go_quarantined
~(ctx : Provider_context.t)
~(entry : Provider_context.entry)
~(line : int)
~(column : int) : Relative_path.t SymbolOccurrence.t list =
all_symbols_ctx ~ctx ~entry |> List.filter ~f:(is_target line column)