2 * Copyright (c) 2018, 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.
14 let conditionally_reactive_attribute_to_hint env
{ ua_params
= l
; _
} =
16 (* convert class const expression to non-generic type hint *)
17 | [(p
, Class_const
((_
, CI cls
), (_
, name
)))]
18 when String.equal name
SN.Members.mClass
->
19 (* set Extends dependency for between class that contains
20 method and condition type *)
21 Decl_env.add_extends_dependency env
(snd cls
);
22 Decl_hint.hint env
(p
, Happly
(cls
, []))
24 (* error for invalid argument list was already reported during the
25 naming step, do nothing *)
26 mk
(Reason.none
, Typing_defs.make_tany
())
28 let condition_type_from_attributes env user_attributes
=
29 Naming_attributes.find
SN.UserAttributes.uaOnlyRxIfImpl user_attributes
30 |> Option.map ~f
:(conditionally_reactive_attribute_to_hint env
)
32 let get_classname_or_literal_attribute_param = function
33 | [(_
, String s
)] -> Some s
34 | [(_
, Class_const
((_
, CI
(_
, s
)), (_
, name
)))]
35 when String.equal name
SN.Members.mClass
->
39 let fun_reactivity_opt env user_attributes
=
40 let module UA
= SN.UserAttributes
in
41 let rx_condition = condition_type_from_attributes env user_attributes
in
44 | { Aast.ua_name
= (_
, n
); ua_params
} :: tl
->
45 if String.equal n
UA.uaPure
then
46 Some
(Pure
rx_condition)
47 else if String.equal n
UA.uaNonRx
then
49 else if String.equal n
UA.uaCipp
then
50 Some
(Cipp
(get_classname_or_literal_attribute_param ua_params
))
51 else if String.equal n
UA.uaCippLocal
then
52 Some
(CippLocal
(get_classname_or_literal_attribute_param ua_params
))
53 else if String.equal n
UA.uaCippGlobal
then
60 let fun_reactivity env user_attributes
=
61 fun_reactivity_opt env user_attributes
|> Option.value ~default
:Nonreactive
63 let find_policied_attribute user_attributes
: ifc_fun_decl
=
64 match Naming_attributes.find
SN.UserAttributes.uaPolicied user_attributes
with
65 | Some
{ ua_params
; _
} ->
66 (match get_classname_or_literal_attribute_param ua_params
with
67 | Some s
-> FDPolicied
(Some s
)
68 | None
-> FDPolicied None
)
70 when Naming_attributes.mem
SN.UserAttributes.uaInferFlows user_attributes
->
72 | None
-> default_ifc_fun_decl
74 let has_constfun_attribute user_attributes
=
75 Naming_attributes.mem
SN.UserAttributes.uaConstFun user_attributes
77 let has_accept_disposable_attribute user_attributes
=
78 Naming_attributes.mem
SN.UserAttributes.uaAcceptDisposable user_attributes
80 let has_external_attribute user_attributes
=
81 Naming_attributes.mem
SN.UserAttributes.uaExternal user_attributes
83 let has_can_call_attribute user_attributes
=
84 Naming_attributes.mem
SN.UserAttributes.uaCanCall user_attributes
86 let has_atom_attribute user_attributes
=
87 Naming_attributes.mem
SN.UserAttributes.uaAtom user_attributes
89 let has_return_disposable_attribute user_attributes
=
90 Naming_attributes.mem
SN.UserAttributes.uaReturnDisposable user_attributes
92 let fun_returns_mutable user_attributes
=
93 Naming_attributes.mem
SN.UserAttributes.uaMutableReturn user_attributes
95 let fun_returns_void_to_rx user_attributes
=
96 Naming_attributes.mem
SN.UserAttributes.uaReturnsVoidToRx user_attributes
98 let get_param_mutability user_attributes
=
99 if Naming_attributes.mem
SN.UserAttributes.uaOwnedMutable user_attributes
then
100 Some Param_owned_mutable
101 else if Naming_attributes.mem
SN.UserAttributes.uaMutable user_attributes
then
102 Some Param_borrowed_mutable
103 else if Naming_attributes.mem
SN.UserAttributes.uaMaybeMutable user_attributes
105 Some Param_maybe_mutable
109 exception Gi_reinfer_type_not_supported
111 let cut_namespace id
=
112 let ids = String.split id ~on
:'
\\'
in
115 let rec reinfer_type_to_string_exn ty
=
118 | Tnonnull
-> "nonnull"
119 | Tdynamic
-> "dynamic"
120 | Tunion
[] -> "nothing"
129 | Tstring
-> "string"
130 | Tarraykey
-> "arraykey"
131 | Tresource
-> "resource"
132 | Tnoreturn
-> "noreturn"
134 | Tapply
((_p
, id
), _tyl
) -> cut_namespace id
135 | Taccess
(ty
, id
) ->
136 let s = reinfer_type_to_string_exn (get_node ty
) in
137 Printf.sprintf
"%s::%s" s (snd id
)
138 | _
-> raise Gi_reinfer_type_not_supported
140 let reinfer_type_to_string_opt ty
=
141 try Some
(reinfer_type_to_string_exn ty
)
142 with Gi_reinfer_type_not_supported
-> None
144 let must_reinfer_type tcopt
(ty
: decl_phase ty_
) =
145 let reinfer_types = GlobalOptions.tco_gi_reinfer_types tcopt
in
146 match reinfer_type_to_string_opt ty
with
148 | Some ty_str
-> List.mem
reinfer_types ty_str ~equal
:String.equal
150 let hint_to_type_opt ~is_lambda env reason hint
=
151 let ty = Option.map hint ~f
:(Decl_hint.hint env
) in
152 let tcopt = Provider_context.get_tcopt env
.Decl_env.ctx
in
153 let tco_global_inference = GlobalOptions.tco_global_inference tcopt in
154 let tvar = mk
(reason
, Tvar
0) in
155 if tco_global_inference && not is_lambda
then
160 let rec create_vars_for_reinfer_types ty =
162 | (r
, Tapply
(id
, [ty'
]))
163 when String.equal
(snd id
) SN.Classes.cAwaitable
->
164 let ty'
= create_vars_for_reinfer_types ty'
in
165 mk
(r
, Tapply
(id
, [ty'
]))
166 | (r
, Toption
ty'
) ->
167 let ty'
= create_vars_for_reinfer_types ty'
in
169 | (r
, Tapply
((_p
, id
), []))
170 when String.equal
(cut_namespace id
) "PHPism_FIXME_Array" ->
171 if must_reinfer_type tcopt (get_node
ty) then
172 let tvar = mk
(r
, Tvar
0) in
173 mk
(r
, Tvarray_or_darray
(tvar, tvar))
177 let ty = create_vars_for_reinfer_types ty in
179 | (r
, Tdarray
(tyk
, tyv
)) ->
180 let tyk = create_vars_for_reinfer_types tyk in
181 let tyv = create_vars_for_reinfer_types tyv in
182 mk
(r
, Tdarray
(tyk, tyv))
183 | (r
, Tvarray_or_darray
(tyk, tyv)) ->
184 let tyk = create_vars_for_reinfer_types tyk in
185 let tyv = create_vars_for_reinfer_types tyv in
186 mk
(r
, Tvarray_or_darray
(tyk, tyv))
188 if must_reinfer_type tcopt ty_
then
193 create_vars_for_reinfer_types ty
199 let hint_to_type ~is_lambda ~default env reason hint
=
200 Option.value (hint_to_type_opt ~is_lambda env reason hint
) ~default
202 let make_param_ty env ~is_lambda param
=
203 let param_pos = Decl_env.make_decl_pos env param
.param_pos in
205 let r = Reason.Rwitness
param_pos in
208 ~default
:(mk
(r, Typing_defs.make_tany
()))
210 (Reason.Rglobal_fun_param
param_pos)
211 (hint_of_type_hint param
.param_type_hint
)
214 match get_node
ty with
215 | t
when param
.param_is_variadic
->
216 (* When checking a call f($a, $b) to a function f(C ...$args),
217 * both $a and $b must be of type C *)
218 mk
(Reason.Rvar_param
param_pos, t
)
221 let module UA
= SN.UserAttributes
in
222 let has_at_most_rx_as_func =
223 Naming_attributes.mem
UA.uaAtMostRxAsFunc param
.param_user_attributes
226 if has_at_most_rx_as_func then
227 make_function_type_rxvar
ty
231 let mode = get_param_mode param
.param_callconv
in
233 if has_at_most_rx_as_func then
236 Naming_attributes.find
UA.uaOnlyRxIfImpl param
.param_user_attributes
237 |> Option.map ~f
:(fun v
->
238 Param_rx_if_impl
(conditionally_reactive_attribute_to_hint env v
))
242 fp_name
= Some param
.param_name
;
243 fp_type
= { et_type
= ty; et_enforced
= false };
247 ~mutability
:(get_param_mutability param
.param_user_attributes
)
249 (has_accept_disposable_attribute param
.param_user_attributes
)
250 ~has_default
:(Option.is_some param
.param_expr
)
251 ~ifc_external
:(has_external_attribute param
.param_user_attributes
)
252 ~ifc_can_call
:(has_can_call_attribute param
.param_user_attributes
)
253 ~is_atom
:(has_atom_attribute param
.param_user_attributes
)
254 ~readonly
:(Option.is_some param
.param_readonly
)
255 ~const_function
:(has_constfun_attribute param
.param_user_attributes
);
256 fp_rx_annotation
= rx_annotation;
259 (** Make FunParam for the partial-mode ellipsis parameter (unnamed, and untyped) *)
260 let make_ellipsis_param_ty :
261 Decl_env.env
-> Pos.t
-> 'phase
ty Typing_defs_core.fun_param
=
263 let pos = Decl_env.make_decl_pos env
pos in
264 let r = Reason.Rwitness
pos in
265 let ty = mk
(r, Typing_defs.make_tany
()) in
269 fp_type
= { et_type
= ty; et_enforced
= false };
274 ~accept_disposable
:false
280 ~const_function
:false;
281 fp_rx_annotation
= None
;
284 let ret_from_fun_kind ?
(is_constructor
= false) ~is_lambda env
pos kind hint
=
285 let pos = Decl_env.make_decl_pos env
pos in
286 let default = mk
(Reason.Rwitness
pos, Typing_defs.make_tany
()) in
288 if is_constructor
then
289 mk
(Reason.Rwitness
pos, Tprim Tvoid
)
291 hint_to_type ~is_lambda ~
default env
(Reason.Rglobal_fun_ret
pos) hint
296 | Ast_defs.FGenerator
->
297 let r = Reason.Rret_fun_kind
(pos, kind
) in
301 ((pos, SN.Classes.cGenerator
), [ret_ty (); ret_ty (); ret_ty ()]) )
302 | Ast_defs.FAsyncGenerator
->
303 let r = Reason.Rret_fun_kind
(pos, kind
) in
307 ( (pos, SN.Classes.cAsyncGenerator
),
308 [ret_ty (); ret_ty (); ret_ty ()] ) )
310 let r = Reason.Rret_fun_kind
(pos, kind
) in
311 mk
(r, Tapply
((pos, SN.Classes.cAwaitable
), [ret_ty ()]))
312 | Ast_defs.FSync
-> ret_ty ())
313 | Some _
-> ret_ty ()
315 let type_param = Decl_hint.aast_tparam_to_decl_tparam
317 let where_constraint env
(ty1
, ck
, ty2
) =
318 (Decl_hint.hint env ty1
, ck
, Decl_hint.hint env ty2
)
320 (* Functions building the types for the parameters of a function *)
321 (* It's not completely trivial because of optional arguments *)
323 let check_params paraml
=
324 (* We wish to give an error on the first non-default parameter
325 after a default parameter. That is:
326 function foo(int $x, ?int $y = null, int $z)
327 is an error on $z. *)
328 (* TODO: This check doesn't need to be done at type checking time; it is
329 entirely syntactic. When we switch over to the FFP, remove this code. *)
330 let rec loop seen_default paraml
=
334 if param
.param_is_variadic
then
336 (* Assume that a variadic parameter is the last one we need
337 to check. We've already given a parse error if the variadic
338 parameter is not last. *)
339 else if seen_default
&& Option.is_none param
.param_expr
then
340 Errors.previous_default param
.param_pos
341 (* We've seen at least one required parameter, and there's an
342 optional parameter after it. Given an error, and then stop looking
343 for more errors in this parameter list. *)
345 loop (Option.is_some param
.param_expr
) rl
349 let make_params env ~is_lambda paraml
=
350 List.map paraml ~f
:(make_param_ty env ~is_lambda
)