2 * Copyright (c) Facebook, Inc. and its affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the "hack" directory of this source tree.
10 * Checks to determine whether names referenced in a file are defined globally.
12 * NOTE: Unlike other nast checks, this one depends on and also
13 * modifies global naming table state. We would rather have this done in typing
14 * but currently there are multiple scenarios when the typechecker
15 * does not comprehensively check expressions. We are then left with
16 * an unrecorded dependency. This should be fixed on some more basic level.
22 droot
: Typing_deps.Dep.dependent
Typing_deps.Dep.variant
;
24 ctx
: Provider_context.t
;
25 type_params
: Aast.reify_kind
SMap.t
;
27 (* Need some context to differentiate global consts and other Id's *)
28 seen_names
: Pos.t
SMap.t
;
29 (* Special context for pocket universes *)
30 pu_case_types
: Aast.reify_kind
SMap.t
;
31 pu_member_types
: Aast.sid list
SMap.t
;
32 (* Contexts where typedefs are valid typenames *)
33 class_id_allow_typedef
: bool;
34 hint_allow_typedef
: bool;
35 hint_context
: Errors.name_context
;
38 let handle_unbound_name env
(pos
, name
) kind
=
39 (* We've already errored in naming if we get *Unknown* class *)
40 if String.equal name
Naming_special_names.Classes.cUnknown
then
48 Errors.unbound_name pos name kind
;
49 (* In addition to reporting errors, we also add to the global dependency table *)
52 | Errors.FunctionNamespace
-> Typing_deps.Dep.Fun name
53 | Errors.TypeNamespace
-> Typing_deps.Dep.Class name
54 | Errors.ConstantNamespace
-> Typing_deps.Dep.GConst name
55 | Errors.TraitContext
-> Typing_deps.Dep.Class name
56 | Errors.RecordContext
-> Typing_deps.Dep.RecordDef name
57 | Errors.ClassContext
-> Typing_deps.Dep.Class name
59 Typing_deps.add_idep env
.droot
dep
61 let has_canon_name env get_name get_pos
(pos
, name
) =
62 match get_name env
.ctx name
with
66 match get_pos env
.ctx canon_name
with
69 Errors.did_you_mean_naming pos name canon_pos canon_name
;
73 let check_fun_name env
((_
, name
) as id
) =
74 if Naming_special_names.SpecialFunctions.is_special_function name
then
76 else if Naming_provider.fun_exists env
.ctx name
then
81 Naming_global.GEnv.fun_canon_name
82 Naming_global.GEnv.fun_pos
87 handle_unbound_name env id
Errors.FunctionNamespace
89 let check_const_name env
((_
, name
) as id
) =
90 if Naming_provider.const_exists env
.ctx name
then
93 handle_unbound_name env id
Errors.ConstantNamespace
96 ?
(kind
= Errors.TypeNamespace
)
101 if String.equal name
Naming_special_names.Classes.cHH_BuiltinEnum
then
104 match SMap.find_opt name env
.type_params
with
106 (* TODO: These throw typing errors instead of naming errors *)
107 if not allow_generics
then Errors.generics_not_allowed pos
;
110 | Aast.Erased
-> Errors.generic_at_runtime pos
"Erased"
111 | Aast.SoftReified
-> Errors.generic_at_runtime pos
"Soft reified"
116 match Naming_provider.get_type_pos_and_kind env
.ctx name
with
117 | Some
(def_pos
, Naming_types.TTypedef
) when not allow_typedef
->
119 Naming_global.GEnv.get_full_pos env
.ctx
(def_pos
, name
)
121 Errors.unexpected_typedef pos full_pos kind
127 Naming_global.GEnv.type_canon_name
128 Naming_global.GEnv.type_pos
133 handle_unbound_name env id kind
137 ?
(kind
= Errors.TypeNamespace
)
142 if String.equal name
Naming_special_names.Typehints.wildcard
then
145 check_type_name ~kind env id ~allow_typedef ~allow_generics
147 let extend_type_params init paraml
=
150 ~f
:(fun { Aast.tp_name
= (_
, name
); tp_reified
; _
} acc
->
151 SMap.add name tp_reified acc
)
156 inherit [env
] Stateful_aast_visitor.default_nast_visitor_with_state
158 (* The following are all setting the environments / context correctly *)
159 method initial_state
=
161 mode
= FileInfo.Mpartial
;
162 droot
= Typing_deps.Dep.Fun
"";
164 type_params
= SMap.empty
;
166 seen_names
= SMap.empty
;
167 pu_case_types
= SMap.empty
;
168 pu_member_types
= SMap.empty
;
169 class_id_allow_typedef
= false;
170 hint_allow_typedef
= true;
171 hint_context
= Errors.TypeNamespace
;
174 method! at_class_ env c
=
176 List.exists c
.Aast.c_user_attributes ~f
:(fun { Aast.ua_name
; _
} ->
179 Naming_special_names.UserAttributes.uaProbabilisticModel
)
184 droot
= Typing_deps.Dep.Class
(snd c
.Aast.c_name
);
185 mode
= c
.Aast.c_mode
;
187 extend_type_params SMap.empty c
.Aast.c_tparams
.Aast.c_tparam_list
;
193 method! at_typedef env td
=
197 droot
= Typing_deps.Dep.Class
(snd td
.Aast.t_name
);
198 mode
= FileInfo.Mstrict
;
199 type_params
= extend_type_params SMap.empty td
.Aast.t_tparams
;
204 method! at_fun_ env f
=
208 droot
= Typing_deps.Dep.Fun
(snd f
.Aast.f_name
);
209 mode
= f
.Aast.f_mode
;
210 type_params
= extend_type_params env
.type_params f
.Aast.f_tparams
;
215 method! at_gconst env gconst
=
219 droot
= Typing_deps.Dep.GConst
(snd gconst
.Aast.cst_name
);
220 mode
= gconst
.Aast.cst_mode
;
225 method! at_file_attribute env _
=
227 { env
with droot
= Typing_deps.Dep.Fun
""; type_params
= SMap.empty
}
231 method! at_method_ env m
=
234 type_params
= extend_type_params env
.type_params m
.Aast.m_tparams
;
237 method! at_method_redeclaration env mt
=
240 type_params
= extend_type_params env
.type_params mt
.Aast.mt_tparams
;
243 method! at_pu_enum env pu_enum
=
247 ~f
:(fun acc
Aast.{ tp_name
= (_
, name
); tp_reified
= reified
; _
} ->
248 SMap.add name reified acc
)
249 pu_enum
.Aast.pu_case_types
251 let pu_member_types =
253 pu_enum
.Aast.pu_members
255 ~f
:(fun acc
Aast.{ pum_atom
= (_
, name
); pum_types
; _
} ->
256 let pum_type_param_names = List.map ~f
:fst pum_types
in
257 match SMap.find_opt name acc
with
258 | None
-> SMap.add name
pum_type_param_names acc
260 SMap.add name
(List.append
pum_type_param_names types
) acc
)
262 { env
with pu_case_types; pu_member_types }
264 method! at_pu_case_value env _
=
265 { env
with type_params
= SMap.union env
.type_params env
.pu_case_types }
267 method! at_pu_member env pu_member
=
268 let pu_name = snd pu_member
.Aast.pum_atom
in
269 let member_types = SMap.find
pu_name env
.pu_member_types in
273 ~init
:env
.type_params
274 ~f
:(fun acc type_param
-> SMap.add
(snd type_param
) Aast.Erased acc
)
276 { env
with type_params }
278 method! at_targ env _
= { env
with hint_allow_typedef
= true }
280 method! at_class_hint env _
=
283 hint_context
= Errors.ClassContext
;
284 hint_allow_typedef
= false;
287 method! at_trait_hint env _
=
290 hint_context
= Errors.TraitContext
;
291 hint_allow_typedef
= false;
294 method! at_record_hint env _
=
297 hint_context
= Errors.RecordContext
;
298 hint_allow_typedef
= false;
301 method! at_xhp_attr_hint env _
= { env
with hint_allow_typedef
= false }
303 (* Below are the methods where we check for unbound names *)
304 method! at_expr env e
=
306 | Aast.Call
(_
, (_
, Aast.Id
(p
, name
)), _
, _
, _
)
307 when env
.in_ppl
&& Naming_special_names.PPLFunctions.is_reserved name
->
308 { env
with seen_names
= SMap.add name p env
.seen_names
}
309 | Aast.FunctionPointer
(Aast.FP_id
((p
, name
) as id
), _
)
310 | Aast.Call
(_
, (_
, Aast.Id
((p
, name
) as id
)), _
, _
, _
) ->
311 let () = check_fun_name env id
in
312 { env
with seen_names
= SMap.add name p env
.seen_names
}
313 | Aast.Id
((p
, name
) as id
) ->
315 match SMap.find_opt name env
.seen_names
with
316 | None
-> check_const_name env id
317 | Some pos
when not
@@ Pos.equal p pos
-> check_const_name env id
322 let () = check_fun_name env id
in
324 | Aast.Method_caller
(id
, _
)
325 | Aast.Smethod_id
(id
, _
)
326 | Aast.Xml
(id
, _
, _
) ->
331 ~allow_generics
:false
332 ~kind
:Errors.ClassContext
336 | Aast.Record
(id
, _
) ->
341 ~allow_generics
:false
342 ~kind
:Errors.RecordContext
346 | Aast.Class_const
((_
, Aast.CI _
), (_
, s
)) when String.equal s
"class" ->
347 { env
with class_id_allow_typedef
= true }
348 | Aast.Obj_get
(_
, (_
, Aast.Id
(p
, name
)), _
) ->
349 { env
with seen_names
= SMap.add name p env
.seen_names
}
352 method! at_shape_field_name env sfn
=
355 | Ast_defs.SFclass_const
(id
, _
) ->
359 ~allow_generics
:false
360 ~kind
:Errors.ClassContext
366 method! at_user_attribute env
{ Aast.ua_name
; _
} =
368 if not
@@ Naming_special_names.UserAttributes.is_reserved
(snd ua_name
)
373 ~allow_generics
:false
374 ~kind
:Errors.ClassContext
379 method! at_class_id env ci
=
385 ~allow_typedef
:env
.class_id_allow_typedef
387 ~kind
:Errors.ClassContext
393 method! at_catch env
(id
, _
, _
) =
398 ~allow_generics
:false
399 ~kind
:Errors.ClassContext
404 method! at_hint env h
=
406 | Aast.Happly
(id
, _
) ->
410 ~allow_typedef
:env
.hint_allow_typedef
411 ~allow_generics
:false
412 ~kind
:env
.hint_context
415 (* Intentionally set allow_typedef to true for a hint's type parameters *
416 * because there are no runtime restrictions *)
417 { env
with hint_allow_typedef
= true }