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.
12 open ServerCommandTypes.Extract_standalone
13 module SourceText
= Full_fidelity_source_text
14 module Syntax
= Full_fidelity_positioned_syntax
15 module SyntaxTree
= Full_fidelity_syntax_tree.WithSyntax
(Syntax
)
16 module SyntaxError
= Full_fidelity_syntax_error
17 module SN
= Naming_special_names
18 module Class
= Decl_provider.Class
20 (* Internal error: for example, we are generating code for a dependency on an enum,
21 but the passed dependency is not an enum *)
22 exception UnexpectedDependency
24 exception DependencyNotFound
of string
28 let records_not_supported () = failwith
"Records are not supported"
30 let value_exn ex opt
=
35 let value_or_not_found err_msg opt
= value_exn (DependencyNotFound err_msg
) opt
37 let get_class_exn ctx name
=
38 let not_found_msg = Printf.sprintf
"Class %s" name
in
39 value_or_not_found not_found_msg @@ Decl_provider.get_class ctx name
41 let get_class_name : type a
. a
Typing_deps.Dep.variant
-> string option =
42 (* the OCaml compiler is not smart enough to let us use an or-pattern for all of these
43 * because of how it's matching on a GADT *)
47 | Const
(cls
, _
) -> Some cls
48 | Method
(cls
, _
) -> Some cls
49 | SMethod
(cls
, _
) -> Some cls
50 | Prop
(cls
, _
) -> Some cls
51 | SProp
(cls
, _
) -> Some cls
52 | Class cls
-> Some cls
53 | Cstr cls
-> Some cls
54 | AllMembers cls
-> Some cls
55 | Extends cls
-> Some cls
61 | RecordDef _
-> records_not_supported ())
63 let global_dep_name dep
=
80 raise UnexpectedDependency
81 | RecordDef _
-> records_not_supported ())
83 let get_fun_pos ctx name
=
84 Decl_provider.get_fun ctx name
|> Option.map ~f
:(fun decl
-> decl
.fe_pos
)
86 let get_fun_pos_exn ctx name
= value_or_not_found name
(get_fun_pos ctx name
)
88 let get_class_pos ctx name
=
89 Decl_provider.get_class ctx name
|> Option.map ~f
:(fun decl
-> Class.pos decl
)
91 let get_class_pos_exn ctx name
=
92 value_or_not_found name
(get_class_pos ctx name
)
94 let get_typedef_pos ctx name
=
95 Decl_provider.get_typedef ctx name
|> Option.map ~f
:(fun decl
-> decl
.td_pos
)
97 let get_gconst_pos ctx name
=
98 Decl_provider.get_gconst ctx name
99 |> Option.map ~f
:(fun ty
-> Typing_defs.get_pos ty
)
101 let get_class_or_typedef_pos ctx name
=
102 Option.first_some
(get_class_pos ctx name
) (get_typedef_pos ctx name
)
104 let get_dep_pos ctx dep
=
105 let open Typing_deps.Dep
in
119 get_class_or_typedef_pos ctx name
122 get_gconst_pos ctx name
123 | RecordDef _
-> records_not_supported ()
125 let make_nast_getter ~get_pos ~find_in_file ~naming
=
126 let nasts = ref SMap.empty
in
128 if SMap.mem name
!nasts then
129 Some
(SMap.find name
!nasts)
132 get_pos ctx name
>>= fun pos
->
133 find_in_file ctx
(Pos.filename pos
) name
>>= fun nast
->
134 let nast = naming ctx
nast in
135 nasts := SMap.add name
nast !nasts;
141 ~find_in_file
:Ast_provider.find_fun_in_file
144 let get_fun_nast_exn ctx name
= value_or_not_found name
(get_fun_nast ctx name
)
148 ~get_pos
:get_class_pos
149 ~find_in_file
:Ast_provider.find_class_in_file
150 ~naming
:Naming.class_
152 let get_typedef_nast =
154 ~get_pos
:get_typedef_pos
155 ~find_in_file
:Ast_provider.find_typedef_in_file
156 ~naming
:Naming.typedef
158 let get_typedef_nast_exn ctx name
=
159 value_or_not_found name
(get_typedef_nast ctx name
)
161 let get_gconst_nast =
163 ~get_pos
:get_gconst_pos
164 ~find_in_file
:Ast_provider.find_gconst_in_file
165 ~naming
:Naming.global_const
167 let get_gconst_nast_exn ctx name
=
168 value_or_not_found name
(get_gconst_nast ctx name
)
170 let make_class_element_nast_getter ~get_elements ~get_element_name
=
171 let elements_by_class_name = ref SMap.empty
in
172 fun ctx class_name element_name
->
173 if SMap.mem class_name
!elements_by_class_name then
174 SMap.find_opt element_name
(SMap.find class_name
!elements_by_class_name)
177 get_class_nast ctx class_name
>>= fun class_
->
178 let elements_by_element_name =
180 (get_elements class_
)
181 ~f
:(fun elements element
->
182 SMap.add
(get_element_name element
) element elements
)
185 elements_by_class_name :=
186 SMap.add class_name
elements_by_element_name !elements_by_class_name;
187 SMap.find_opt element_name
elements_by_element_name
189 let get_method_nast =
190 make_class_element_nast_getter
191 ~get_elements
:(fun class_
-> class_
.c_methods
)
192 ~get_element_name
:(fun method_
-> snd method_
.m_name
)
194 let get_method_nast_exn ctx class_name method_name
=
196 (class_name ^
"::" ^ method_name
)
197 (get_method_nast ctx class_name method_name
)
200 make_class_element_nast_getter
201 ~get_elements
:(fun class_
-> class_
.c_consts
)
202 ~get_element_name
:(fun const
-> snd const
.cc_id
)
204 let get_typeconst_nast =
205 make_class_element_nast_getter
206 ~get_elements
:(fun class_
-> class_
.c_typeconsts
)
207 ~get_element_name
:(fun typeconst
-> snd typeconst
.c_tconst_name
)
210 make_class_element_nast_getter
211 ~get_elements
:(fun class_
-> class_
.c_vars
)
212 ~get_element_name
:(fun class_var
-> snd class_var
.cv_id
)
214 let get_prop_nast_exn ctx class_name prop_name
=
216 (class_name ^
"::" ^ prop_name
)
217 (get_prop_nast ctx class_name prop_name
)
219 let get_fun_mode ctx name
=
220 get_fun_nast ctx name
|> Option.map ~f
:(fun fun_
-> fun_
.f_mode
)
222 let get_class_mode ctx name
=
223 get_class_nast ctx name
|> Option.map ~f
:(fun class_
-> class_
.c_mode
)
225 let get_typedef_mode ctx name
=
226 get_typedef_nast ctx name
|> Option.map ~f
:(fun typedef
-> typedef
.t_mode
)
228 let get_gconst_mode ctx name
=
229 get_gconst_nast ctx name
|> Option.map ~f
:(fun gconst
-> gconst
.cst_mode
)
231 let get_class_or_typedef_mode ctx name
=
232 Option.first_some
(get_class_mode ctx name
) (get_typedef_mode ctx name
)
234 let get_dep_mode ctx dep
=
235 let open Typing_deps.Dep
in
239 get_fun_mode ctx name
249 get_class_or_typedef_mode ctx name
252 get_gconst_mode ctx name
253 | RecordDef _
-> records_not_supported ()
255 let is_strict_dep ctx dep
=
256 match get_dep_mode ctx dep
with
257 | Some
FileInfo.Mstrict
-> true
260 let is_strict_fun ctx name
= is_strict_dep ctx
(Typing_deps.Dep.Fun name
)
262 let is_strict_class ctx name
= is_strict_dep ctx
(Typing_deps.Dep.Class name
)
264 let is_builtin_dep ctx dep
=
265 let msg = Typing_deps.Dep.variant_to_string dep
in
266 let pos = value_or_not_found msg (get_dep_pos ctx dep
) in
267 Relative_path.(is_hhi
(prefix
(Pos.filename
pos)))
269 let is_relevant_dependency
270 (target
: target
) (dep
: Typing_deps.Dep.dependent
Typing_deps.Dep.variant
)
275 | Typing_deps.Dep.Fun g
276 | Typing_deps.Dep.FunName g
->
279 (* We have to collect dependencies of the entire class because dependency collection is
280 coarse-grained: if cls's member depends on D, we get a dependency edge cls --> D,
281 not (cls, member) --> D *)
282 | Method
(cls
, _
) -> Option.equal
String.equal
(get_class_name dep
) (Some cls
)
284 let get_filename ctx target
=
287 | Function name
-> get_fun_pos_exn ctx name
288 | Method
(name
, _
) -> get_class_pos_exn ctx name
292 let extract_target ctx target
=
293 let filename = get_filename ctx target
in
294 let abs_filename = Relative_path.to_absolute
filename in
295 let file_content = In_channel.read_all
abs_filename in
299 let fun_ = get_fun_nast_exn ctx name
in
301 | Method
(class_name
, method_name
) ->
302 let method_ = get_method_nast_exn ctx class_name method_name
in
305 Pos.get_text_from_pos
file_content pos
307 let print_error source_text error
=
309 SyntaxError.to_positioned_string
311 (SourceText.offset_to_position source_text
)
313 Hh_logger.log
"%s\n" text
315 let tree_from_string s
=
316 let source_text = SourceText.make
Relative_path.default s
in
317 let mode = Full_fidelity_parser.parse_mode
source_text in
318 let env = Full_fidelity_parser_env.make ?
mode () in
319 let tree = SyntaxTree.make ~
env source_text in
320 if List.is_empty
(SyntaxTree.all_errors
tree) then
323 List.iter
(SyntaxTree.all_errors
tree) (print_error source_text);
324 raise
Hackfmt_error.InvalidSyntax
328 let re = Str.regexp
"\\\\:" in
329 Str.global_replace
re ":"
332 try Libhackfmt.format_tree
(tree_from_string (fixup_xhp text))
333 with Hackfmt_error.InvalidSyntax
-> text
335 let strip_ns obj_name
=
336 match String.rsplit2 obj_name '
\\'
with
337 | Some
(_
, name
) -> name
340 let concat_map ~sep ~f list
= String.concat ~sep
(List.map ~f list
)
342 let function_make_default = "extract_standalone_make_default"
344 let call_make_default = Printf.sprintf
"\\%s()" function_make_default
346 let extract_standalone_any = "EXTRACT_STANDALONE_ANY"
348 let string_of_tprim = function
353 | Tstring
-> "string"
354 | Tarraykey
-> "arraykey"
357 | Tresource
-> "resource"
358 | Tnoreturn
-> "noreturn"
360 let string_of_shape_field_name = function
361 | Ast_defs.SFlit_int
(_
, s
) -> s
362 | Ast_defs.SFlit_str
(_
, s
) -> Printf.sprintf
"'%s'" s
363 | Ast_defs.SFclass_const
((_
, c
), (_
, s
)) -> Printf.sprintf
"%s::%s" c s
365 let string_of_xhp_attr_info xhp_attr_info
=
366 match xhp_attr_info
.xai_tag
with
367 | Some Required
-> "@required"
368 | Some LateInit
-> "@lateinit"
371 let rec string_of_hint hint
=
373 | Hoption hint
-> "?" ^
string_of_hint hint
374 | Hlike hint
-> "~" ^
string_of_hint hint
377 hf_reactive_kind
= _
;
380 hf_param_mutability
= _
;
383 (* TODO(vmladenov) support capability types here *)
385 hf_is_mutable_return
= _
;
387 let param_hints = List.map hf_param_tys ~f
:string_of_hint in
389 List.map hf_param_kinds ~f
:(function
390 | Some
Ast_defs.Pinout
-> "inout "
393 let params = List.map2_exn
param_kinds param_hints ~f
:( ^
) in
395 match hf_variadic_ty
with
396 | Some hint
-> [string_of_hint hint ^
"..."]
400 "(function(%s) : %s)"
401 (String.concat ~sep
:", " (params @ variadic))
402 (string_of_hint hf_return_ty
)
404 Printf.sprintf
"(%s)" (concat_map ~sep
:", " ~f
:string_of_hint hints
)
405 | Habstr
(name
, hints
)
406 | Happly
((_
, name
), hints
) ->
411 Printf.sprintf
"<%s>" (concat_map ~sep
:", " ~f
:string_of_hint hints
)
414 | Hshape
{ nsi_allows_unknown_fields
; nsi_field_map
} ->
415 let string_of_shape_field { sfi_optional
; sfi_name
; sfi_hint
} =
416 let optional_prefix =
425 (string_of_shape_field_name sfi_name
)
426 (string_of_hint sfi_hint
)
428 let shape_fields = List.map nsi_field_map ~f
:string_of_shape_field in
430 if nsi_allows_unknown_fields
then
435 let shape_entries = shape_fields @ shape_suffix in
436 Printf.sprintf
"shape(%s)" (String.concat ~sep
:", " shape_entries)
437 | Haccess
(root
, ids
) ->
438 String.concat ~sep
:"::" (string_of_hint root
:: List.map ids ~f
:snd
)
439 | Hsoft hint
-> "@" ^
string_of_hint hint
441 | Hnonnull
-> "nonnull"
442 | Hdarray
(khint
, vhint
) ->
445 (string_of_hint khint
)
446 (string_of_hint vhint
)
447 | Hvarray hint
-> Printf.sprintf
"varray<%s>" (string_of_hint hint
)
448 | Hvarray_or_darray
(None
, vhint
) ->
449 Printf.sprintf
"varray_or_darray<%s>" (string_of_hint vhint
)
450 | Hvarray_or_darray
(Some khint
, vhint
) ->
452 "varray_or_darray<%s, %s>"
453 (string_of_hint khint
)
454 (string_of_hint vhint
)
455 | Hprim prim
-> string_of_tprim prim
457 | Hdynamic
-> "dynamic"
458 | Hnothing
-> "nothing"
460 Printf.sprintf
"(%s)" (concat_map ~sep
:" | " ~f
:string_of_hint hints
)
461 | Hintersection hints
->
462 Printf.sprintf
"(%s)" (concat_map ~sep
:" & " ~f
:string_of_hint hints
)
463 | Hany
-> extract_standalone_any
464 | Herr
-> extract_standalone_any
465 | Hfun_context name
-> "ctx " ^ name
468 let maybe_string_of_user_attribute { ua_name
; ua_params
} =
469 let name = snd ua_name
in
471 | [] when SMap.mem
name SN.UserAttributes.as_map
-> Some
name
474 let string_of_user_attributes user_attributes
=
475 let user_attributes =
476 List.filter_map ~f
:maybe_string_of_user_attribute user_attributes
478 match user_attributes with
480 | _
-> Printf.sprintf
"<<%s>>" (String.concat ~sep
:", " user_attributes)
482 let string_of_variance = function
483 | Ast_defs.Covariant
-> "+"
484 | Ast_defs.Contravariant
-> "-"
485 | Ast_defs.Invariant
-> ""
487 let string_of_constraint (kind
, hint
) =
490 | Ast_defs.Constraint_as
-> "as"
491 | Ast_defs.Constraint_eq
-> "="
492 | Ast_defs.Constraint_super
-> "super"
494 keyword ^
" " ^
string_of_hint hint
496 let rec string_of_tparam
506 let variance = string_of_variance tp_variance
in
507 let name = snd tp_name
in
508 let parameters = string_of_tparams tp_parameters
in
509 let constraints = List.map tp_constraints ~f
:string_of_constraint in
510 let user_attributes = string_of_user_attributes tp_user_attributes
in
512 match tp_reified
with
526 and string_of_tparams tparams
=
530 Printf.sprintf
"<%s>" (concat_map ~sep
:", " ~f
:string_of_tparam tparams
)
532 let string_of_fun_param
539 param_user_attributes
;
542 let user_attributes = string_of_user_attributes param_user_attributes
in
544 match param_callconv
with
545 | Some
Ast_defs.Pinout
-> "inout"
549 match param_type_hint
with
550 | (_
, Some hint
) -> string_of_hint hint
554 if param_is_variadic
then
560 match param_expr
with
561 | Some _
-> " = " ^
call_make_default
573 let get_fun_declaration ctx
name =
574 let fun_ = get_fun_nast_exn ctx
name in
575 let user_attributes = string_of_user_attributes fun_.f_user_attributes
in
576 let tparams = string_of_tparams
fun_.f_tparams
in
578 match fun_.f_variadic
with
579 | FVvariadicArg fp
-> [string_of_fun_param fp
]
580 | FVellipsis _
-> ["..."]
581 | FVnonVariadic
-> []
586 (List.map
fun_.f_params ~f
:string_of_fun_param @ variadic)
589 match fun_.f_ret
with
590 | (_
, Some hint
) -> ": " ^
string_of_hint hint
594 "%s function %s%s(%s)%s {throw new \\Exception();}"
601 let get_init_for_prim = function
602 | Aast_defs.Tnull
-> "null"
606 | Aast_defs.Tbool
-> "false"
607 | Aast_defs.Tfloat
-> "0.0"
609 | Aast_defs.Tarraykey
->
612 | Aast_defs.Tresource
613 | Aast_defs.Tnoreturn
->
616 let rec get_init_from_hint ctx tparams_stack hint
=
617 let unsupported_hint () =
619 "%s: get_init_from_hint: unsupported hint: %s"
620 (Pos.string (Pos.to_absolute
(fst hint
)))
621 (Aast_defs.show_hint hint
);
625 | Hprim prim
-> get_init_for_prim prim
626 | Hoption _
-> "null"
627 | Hlike hint
-> get_init_from_hint ctx tparams_stack hint
628 | Hdarray _
-> "darray[]"
629 | Hvarray_or_darray _
635 (concat_map ~sep
:", " ~f
:(get_init_from_hint ctx tparams_stack
) hints
)
636 | Happly
((_
, name), hints
) ->
639 when String.equal
name SN.Collections.cVec
640 || String.equal
name SN.Collections.cKeyset
641 || String.equal
name SN.Collections.cDict
->
642 Printf.sprintf
"%s[]" (strip_ns name)
644 when String.equal
name SN.Collections.cVector
645 || String.equal
name SN.Collections.cImmVector
646 || String.equal
name SN.Collections.cMap
647 || String.equal
name SN.Collections.cImmMap
648 || String.equal
name SN.Collections.cSet
649 || String.equal
name SN.Collections.cImmSet
->
650 Printf.sprintf
"%s {}" (strip_ns name)
651 | _
when String.equal
name SN.Collections.cPair
->
656 (get_init_from_hint ctx tparams_stack first
)
657 (get_init_from_hint ctx tparams_stack second
)
658 | _
-> failwith
"malformed hint")
659 | _
when String.equal
name SN.Classes.cClassname
->
661 | [(_
, Happly
((_
, class_name
), _
))] ->
662 Printf.sprintf
"%s::class" class_name
663 | _
-> raise UnexpectedDependency
)
665 (match get_class_nast ctx
name with
667 (match class_
.c_kind
with
670 match class_
.c_consts
with
671 | [] -> failwith
"empty enum"
672 | const
:: _
-> snd const
.cc_id
674 Printf.sprintf
"%s::%s" name const_name
675 | _
-> unsupported_hint ())
677 let typedef = get_typedef_nast_exn ctx
name in
683 ~f
:(fun tparams tparam hint
->
684 SMap.add
(snd tparam
.tp_name
) hint
tparams)
686 get_init_from_hint ctx
(tparams :: tparams_stack
) typedef.t_kind
))
687 | Hshape
{ nsi_field_map
; _
} ->
688 let non_optional_fields =
689 List.filter nsi_field_map ~f
:(fun shape_field_info
->
690 not shape_field_info
.sfi_optional
)
692 let get_init_shape_field { sfi_hint
; sfi_name
; _
} =
695 (string_of_shape_field_name sfi_name
)
696 (get_init_from_hint ctx tparams_stack sfi_hint
)
700 (concat_map ~sep
:", " ~f
:get_init_shape_field non_optional_fields)
701 | Habstr
(name, []) ->
702 (* FIXME: support non-empty type arguments of Habstr here? *)
703 let rec loop tparams_stack
=
704 match tparams_stack
with
705 | tparams :: tparams_stack'
->
706 (match SMap.find_opt
name tparams with
707 | Some hint
-> get_init_from_hint ctx tparams_stack' hint
708 | None
-> loop tparams_stack'
)
709 | [] -> unsupported_hint ()
712 | _
-> unsupported_hint ()
714 let get_init_from_hint ctx hint
= get_init_from_hint ctx
[] hint
716 let get_gconst_declaration ctx
name =
717 let gconst = get_gconst_nast_exn ctx
name in
718 let hint = value_or_not_found ("type of " ^
name) gconst.cst_type
in
719 let init = get_init_from_hint ctx
hint in
720 Printf.sprintf
"const %s %s = %s;" (string_of_hint hint) (strip_ns name) init
722 let get_const_declaration ctx const
=
723 let name = snd const
.cc_id
in
725 match const
.cc_expr
with
730 match (const
.cc_type
, const
.cc_expr
) with
732 (string_of_hint hint, " = " ^
get_init_from_hint ctx
hint)
734 (match Decl_utils.infer_const e
with
736 let hint = (fst e
, Hprim tprim
) in
737 ("", " = " ^
get_init_from_hint ctx
hint)
738 | None
-> raise Unsupported
)
739 | (None
, None
) -> ("", "")
741 Printf.sprintf
"%s const %s %s%s;" abstract type_
name init
743 let get_global_object_declaration ctx obj
=
748 get_fun_declaration ctx f
751 get_gconst_declaration ctx c
752 (* No other global declarations *)
753 | _
-> raise UnexpectedDependency
)
755 let get_class_declaration class_
=
756 let name = snd class_
.c_name
in
757 let user_attributes = string_of_user_attributes class_
.c_user_attributes
in
759 if class_
.c_final
then
765 match class_
.c_kind
with
766 | Ast_defs.Cabstract
-> "abstract class"
767 | Ast_defs.Cnormal
-> "class"
768 | Ast_defs.Cinterface
-> "interface"
769 | Ast_defs.Ctrait
-> "trait"
770 | Ast_defs.Cenum
-> "enum"
772 let tparams = string_of_tparams class_
.c_tparams
in
774 match class_
.c_extends
with
779 (concat_map ~sep
:", " ~f
:string_of_hint class_
.c_extends
)
782 match class_
.c_implements
with
787 (concat_map ~sep
:", " ~f
:string_of_hint class_
.c_implements
)
790 "%s %s %s %s%s %s %s"
799 let get_method_declaration method_ ~from_interface
=
801 if method_.m_abstract
&& not from_interface
then
807 if method_.m_final
then
812 let visibility = string_of_visibility
method_.m_visibility
in
814 if method_.m_static
then
819 let user_attributes = string_of_user_attributes method_.m_user_attributes
in
820 let name = strip_ns (snd
method_.m_name
) in
821 let tparams = string_of_tparams
method_.m_tparams
in
823 match method_.m_variadic
with
824 | FVvariadicArg fp
-> [string_of_fun_param fp
]
825 | FVellipsis _
-> ["..."]
826 | FVnonVariadic
-> []
831 (List.map
method_.m_params ~f
:string_of_fun_param @ variadic)
834 match method_.m_ret
with
835 | (_
, Some
hint) -> ": " ^
string_of_hint hint
839 if method_.m_abstract
|| from_interface
then
842 "{throw new \\Exception();}"
845 "%s %s %s %s %s function %s%s(%s)%s%s"
857 let get_prop_declaration ctx prop
=
858 let name = snd prop
.cv_id
in
859 let user_attributes = string_of_user_attributes prop
.cv_user_attributes
in
861 match (hint_of_type_hint prop
.cv_type
, prop
.cv_expr
) with
862 | (Some
hint, Some _
) ->
863 (string_of_hint hint, Printf.sprintf
" = %s" (get_init_from_hint ctx
hint))
864 | (Some
hint, None
) -> (string_of_hint hint, "")
865 | (None
, None
) -> ("", "")
866 (* Untyped prop, not supported for now *)
867 | (None
, Some _
) -> raise Unsupported
869 match prop
.cv_xhp_attr
with
871 (* Ordinary property *)
872 let visibility = string_of_visibility prop
.cv_visibility
in
874 if prop
.cv_is_static
then
887 | Some xhp_attr_info
->
890 "%s attribute %s %s %s %s;"
893 (String.lstrip ~drop
:(fun c
-> Char.equal c '
:'
) name)
895 (string_of_xhp_attr_info xhp_attr_info
)
897 let get_typeconst_declaration typeconst
=
899 match typeconst
.c_tconst_abstract
with
900 | TCAbstract _
-> "abstract"
901 | TCPartiallyAbstract
905 let name = snd typeconst
.c_tconst_name
in
907 match typeconst
.c_tconst_type
with
908 | Some
hint -> " = " ^
string_of_hint hint
912 match typeconst
.c_tconst_constraint
with
913 | Some
hint -> " as " ^
string_of_hint hint
916 Printf.sprintf
"%s const type %s%s%s;" abstract name constraint_ type_
918 let get_method_declaration ctx target class_name method_name
=
920 | ServerCommandTypes.Extract_standalone.Method
921 (target_class_name
, target_method_name
)
922 when String.equal class_name target_class_name
923 && String.equal method_name target_method_name
->
927 get_class_nast ctx class_name
>>= fun class_
->
928 let from_interface = Ast_defs.is_c_interface class_
.c_kind
in
929 get_method_nast ctx class_name method_name
>>= fun method_ ->
930 Some
(get_method_declaration method_ ~
from_interface)
932 let get_class_elt_declaration
933 ctx target
(class_elt
: 'a
Typing_deps.Dep.variant
) =
934 let open Typing_deps.Dep
in
936 | Const
(class_name
, const_name) ->
937 (match get_typeconst_nast ctx class_name
const_name with
938 | Some typeconst
-> Some
(get_typeconst_declaration typeconst
)
940 (match get_const_nast ctx class_name
const_name with
941 | Some const
-> Some
(get_const_declaration ctx const
)
942 | None
-> raise
(DependencyNotFound
(class_name ^
"::" ^
const_name))))
943 | Method
(class_name
, method_name
)
944 | SMethod
(class_name
, method_name
) ->
945 get_method_declaration ctx target class_name method_name
947 get_method_declaration ctx target class_name
"__construct"
948 | Prop
(class_name
, prop_name
) ->
949 let prop = get_prop_nast_exn ctx class_name prop_name
in
950 Some
(get_prop_declaration ctx
prop)
951 | SProp
(class_name
, sprop_name
) ->
953 String.lstrip ~drop
:(fun c
-> Char.equal c '$'
) sprop_name
955 let prop = get_prop_nast_exn ctx class_name
sprop_name in
956 Some
(get_prop_declaration ctx
prop)
957 (* Constructor should've been tackled earlier, and all other dependencies aren't class elements *)
965 raise UnexpectedDependency
966 | RecordDef _
-> records_not_supported ()
968 let construct_enum ctx class_
=
969 let name = snd class_
.c_name
in
971 match class_
.c_enum
with
973 | None
-> failwith
("not an enum: " ^ snd class_
.c_name
)
976 match enum.e_constraint
with
977 | Some
hint -> " as " ^
string_of_hint hint
980 let string_of_enum_const const
=
984 (get_init_from_hint ctx
enum.e_base
)
989 (string_of_hint enum.e_base
)
991 (concat_map ~sep
:"\n" ~f
:string_of_enum_const class_
.c_consts
)
993 let get_class_body ctx class_ target class_elts
=
994 let name = snd class_
.c_name
in
996 List.map class_
.c_uses ~f
:(fun s
->
997 Printf.sprintf
"use %s;" (string_of_hint s
))
999 let (req_extends
, req_implements
) =
1000 List.partition_map class_
.c_reqs ~f
:(fun (s
, extends) ->
1002 `Fst
(Printf.sprintf
"require extends %s;" (string_of_hint s
))
1004 `Snd
(Printf.sprintf
"require implements %s;" (string_of_hint s
)))
1006 let open Typing_deps
in
1008 List.filter_map class_elts ~f
:(function
1011 raise UnexpectedDependency
1012 | Dep.Const
(_
, "class") -> None
1013 | class_elt
-> get_class_elt_declaration ctx target class_elt
)
1015 (* If we are extracting a method of this class, we should declare it
1016 here, with stubs of other class elements. *)
1017 let extracted_method =
1019 | Method
(cls_name
, _
) when String.equal cls_name
name ->
1020 [extract_target ctx target
]
1025 (req_extends
@ req_implements
@ uses @ body @ extracted_method)
1027 let construct_class ctx class_ target fields
=
1028 let decl = get_class_declaration class_
in
1029 let body = get_class_body ctx class_ target fields
in
1030 Printf.sprintf
"%s {%s}" decl body
1032 let construct_enum_or_class ctx class_ target fields
=
1033 match class_
.c_kind
with
1034 | Ast_defs.Cabstract
1036 | Ast_defs.Cinterface
1037 | Ast_defs.Ctrait
->
1038 construct_class ctx class_ target fields
1039 | Ast_defs.Cenum
-> construct_enum ctx class_
1041 let construct_typedef typedef =
1042 let name = snd
typedef.t_name
in
1044 match typedef.t_vis
with
1045 | Aast_defs.Transparent
-> "type"
1046 | Aast_defs.Opaque
-> "newtype"
1048 let tparams = string_of_tparams
typedef.t_tparams
in
1050 match typedef.t_constraint
with
1051 | Some
hint -> " as " ^
string_of_hint hint
1054 let pos = fst
typedef.t_name
in
1058 ~f
:(fun code
-> Printf.sprintf
"/* HH_FIXME[%d] */\n" code
)
1059 (ISet.elements
(Fixme_provider.get_fixme_codes_for_pos
pos)))
1068 (string_of_hint typedef.t_kind
)
1070 let construct_type_declaration ctx t target fields
=
1071 match get_class_nast ctx t
with
1072 | Some class_
-> construct_enum_or_class ctx class_ target fields
1074 let typedef = get_typedef_nast_exn ctx t
in
1075 construct_typedef typedef
1077 type extraction_env
= {
1078 dependencies
: Typing_deps.Dep.dependency
Typing_deps.Dep.variant
HashSet.t
;
1079 depends_on_make_default
: bool ref;
1080 depends_on_any
: bool ref;
1083 let rec do_add_dep ctx
env dep
=
1086 | Typing_deps.Dep.Class h
-> String.equal h
SN.Typehints.wildcard
1091 && (not
(HashSet.mem
env.dependencies dep
))
1092 && not
(is_builtin_dep ctx dep
)
1094 HashSet.add
env.dependencies dep
;
1095 add_signature_dependencies ctx
env dep
1098 and add_dep ctx
env ~this ty
: unit =
1101 inherit [unit] Type_visitor.decl_type_visitor
as super
1103 method! on_tany _ _
= env.depends_on_any
:= true
1105 method! on_tfun
() r ft
=
1106 if List.exists ~f
:Typing_defs.get_fp_has_default ft
.ft_params
then
1107 env.depends_on_make_default
:= true;
1108 super#on_tfun
() r ft
1110 method! on_tapply _ _
(_
, name) tyl
=
1111 let dep = Typing_deps.Dep.Class
name in
1112 do_add_dep ctx
env dep;
1114 (* If we have a constant of a generic type, it can only be an
1115 array type, e.g., vec<A>, for which don't need values of A
1116 to generate an initializer. *)
1117 List.iter tyl ~f
:(add_dep ctx
env ~this
)
1119 method! on_tshape _ _ _ fdm
=
1121 (fun name { sft_ty
; _
} ->
1123 | Ast_defs.SFlit_int _
1124 | Ast_defs.SFlit_str _
->
1126 | Ast_defs.SFclass_const
((_
, c
), (_
, s
)) ->
1127 do_add_dep ctx
env (Typing_deps.Dep.Class c
);
1128 do_add_dep ctx
env (Typing_deps.Dep.Const
(c
, s
)));
1129 add_dep ctx
env ~this sft_ty
)
1132 (* We un-nest (((this::T1)::T2)::T3) into (this, [T1;T2;T3]) and then re-nest
1133 * because legacy representation of Taccess was using lists. TODO: implement
1134 * this more directly instead.
1136 method! on_taccess
() r
(root
, tconst
) =
1137 let rec split_taccess root ids
=
1138 match Typing_defs.get_node root
with
1139 | Taccess
(root
, id
) -> split_taccess root
(id
:: ids
)
1142 let rec make_taccess r root ids
=
1148 (mk
(r
, Typing_defs.Taccess
(root
, id
)))
1151 let (root
, tconsts
) = split_taccess root
[tconst
] in
1152 let expand_type_access class_name tconsts
=
1154 | [] -> raise UnexpectedDependency
1155 (* Expand Class::TConst1::TConst2[::...]: get TConst1 in
1156 Class, get its type or upper bound T, continue adding
1157 dependencies of T::TConst2[::...] *)
1158 | (_
, tconst
) :: tconsts
->
1159 do_add_dep ctx
env (Typing_deps.Dep.Const
(class_name
, tconst
));
1160 let cls = get_class_exn ctx class_name
in
1161 (match Decl_provider.Class.get_typeconst
cls tconst
with
1165 ~f
:(add_dep ctx ~this
:(Some class_name
) env);
1166 if not
(List.is_empty tconsts
) then (
1167 match (typeconst
.ttc_type
, typeconst
.ttc_constraint
) with
1169 | (None
, Some tc_type
) ->
1170 (* What does 'this' refer to inside of T? *)
1172 match Typing_defs.get_node tc_type
with
1173 | Tapply
((_
, name), _
) -> Some
name
1176 let taccess = make_taccess r tc_type tconsts
in
1177 add_dep ctx ~
this env taccess
1178 | (None
, None
) -> ()
1182 match Typing_defs.get_node root
with
1183 | Taccess
(root'
, tconst
) ->
1184 add_dep ctx ~
this env (make_taccess r root'
(tconst
:: tconsts
))
1185 | Tapply
((_
, name), _
) -> expand_type_access name tconsts
1186 | Tthis
-> expand_type_access (Option.value_exn this) tconsts
1187 | _
-> raise UnexpectedDependency
1190 visitor#on_type
() ty
1192 and add_signature_dependencies ctx
env obj
=
1193 let open Typing_deps.Dep
in
1194 let description = variant_to_string obj
in
1195 match get_class_name obj
with
1197 do_add_dep ctx
env (Typing_deps.Dep.Class cls_name
);
1198 (match Decl_provider.get_class ctx cls_name
with
1201 value_or_not_found description @@ Decl_provider.get_typedef ctx cls_name
1203 add_dep ctx ~
this:None
env td.td_type
;
1204 Option.iter
td.td_constraint ~f
:(add_dep ctx ~
this:None
env)
1206 let add_dep = add_dep ctx
env ~
this:(Some cls_name
) in
1209 let p = value_or_not_found description @@ Class.get_prop
cls name in
1210 add_dep @@ Lazy.force
p.ce_type
;
1212 (* We need to initialize properties in the constructor, add a dependency on it *)
1213 do_add_dep ctx
env (Cstr cls_name
)
1214 | SProp
(_
, name) ->
1215 let sp = value_or_not_found description @@ Class.get_sprop
cls name in
1216 add_dep @@ Lazy.force
sp.ce_type
1217 | Method
(_
, name) ->
1218 let m = value_or_not_found description @@ Class.get_method
cls name in
1219 add_dep @@ Lazy.force
m.ce_type
;
1220 Class.all_ancestor_names
cls
1221 |> List.iter ~f
:(fun ancestor_name
->
1222 match Decl_provider.get_class ctx ancestor_name
with
1223 | Some ancestor
when Class.has_method ancestor
name ->
1224 do_add_dep ctx
env (Method
(ancestor_name
, name))
1226 | SMethod
(_
, name) ->
1227 (match Class.get_smethod
cls name with
1229 add_dep @@ Lazy.force sm
.ce_type
;
1230 Class.all_ancestor_names
cls
1231 |> List.iter ~f
:(fun ancestor_name
->
1232 match Decl_provider.get_class ctx ancestor_name
with
1233 | Some ancestor
when Class.has_smethod ancestor
name ->
1234 do_add_dep ctx
env (SMethod
(ancestor_name
, name))
1237 (match Class.get_method
cls name with
1239 HashSet.remove
env.dependencies obj
;
1240 do_add_dep ctx
env (Method
(cls_name
, name))
1241 | None
-> raise
(DependencyNotFound
description)))
1242 | Const
(_
, name) ->
1243 (match Class.get_typeconst
cls name with
1245 if not
(String.equal cls_name tc
.ttc_origin
) then
1246 do_add_dep ctx
env (Const
(tc
.ttc_origin
, name));
1247 Option.iter tc
.ttc_type ~f
:add_dep;
1248 Option.iter tc
.ttc_constraint ~f
:add_dep
1250 let c = value_or_not_found description @@ Class.get_const
cls name in
1253 (match Class.construct
cls with
1254 | (Some constr
, _
) -> add_dep @@ Lazy.force constr
.ce_type
1257 List.iter
(Class.all_ancestors
cls) (fun (_
, ty
) -> add_dep ty
);
1258 List.iter
(Class.all_ancestor_reqs
cls) (fun (_
, ty
) -> add_dep ty
);
1260 (Class.enum_type
cls)
1261 ~f
:(fun { te_base
; te_constraint
; te_includes
; te_enum_class
= _
} ->
1263 Option.iter te_constraint ~f
:add_dep;
1264 List.iter te_includes ~f
:add_dep)
1266 (* AllMembers is used for dependencies on enums, so we should depend on all constants *)
1267 List.iter
(Class.consts
cls) (fun (name, c) ->
1268 if not
(String.equal
name "class") then add_dep c.cc_type
)
1269 (* Ignore, we fetch class hierarchy when we call add_signature_dependencies on a class dep *)
1271 | _
-> raise UnexpectedDependency
))
1277 value_or_not_found description @@ Decl_provider.get_fun ctx f
1279 add_dep ctx ~
this:None
env @@ func.fe_type
1283 value_or_not_found description @@ Decl_provider.get_gconst ctx
c
1285 add_dep ctx ~
this:None
env ty
1286 | _
-> raise UnexpectedDependency
)
1288 let get_implementation_dependencies ctx
env cls_name
=
1289 let open Decl_provider
in
1290 match get_class ctx cls_name
with
1293 let open Typing_deps.Dep
in
1294 let add_smethod_impl acc smethod_name
=
1295 match Class.get_smethod
cls smethod_name
with
1296 | Some elt
-> SMethod
(elt
.ce_origin
, smethod_name
) :: acc
1299 let add_method_impl acc method_name
=
1300 match Class.get_method
cls method_name
with
1301 | Some elt
-> Method
(elt
.ce_origin
, method_name
) :: acc
1304 let add_typeconst_impl acc typeconst_name
=
1305 match Class.get_typeconst
cls typeconst_name
with
1306 | Some tc
-> Const
(tc
.ttc_origin
, typeconst_name
) :: acc
1309 let add_const_impl acc
const_name =
1310 match Class.get_const
cls const_name with
1311 | Some
c -> Const
(c.cc_origin
, const_name) :: acc
1314 let add_impls acc ancestor_name
=
1315 let ancestor = get_class_exn ctx ancestor_name
in
1316 if is_builtin_dep ctx
(Class ancestor_name
) then
1319 (Class.smethods
ancestor)
1321 ~f
:(fun acc (smethod_name
, _
) -> add_smethod_impl acc smethod_name
)
1325 (Class.methods
ancestor)
1327 ~f
:(fun acc (method_name
, _
) -> add_method_impl acc method_name
)
1331 (Class.typeconsts
ancestor)
1333 ~f
:(fun acc (typeconst_name
, _
) ->
1334 add_typeconst_impl acc typeconst_name
)
1338 (Class.consts
ancestor)
1340 ~f
:(fun acc (const_name, _
) -> add_const_impl acc const_name)
1344 HashSet.fold
env.dependencies ~
init:acc ~f
:(fun dep acc ->
1346 | SMethod
(class_name
, method_name
)
1347 when String.equal class_name ancestor_name
->
1348 add_smethod_impl acc method_name
1349 | Method
(class_name
, method_name
)
1350 when String.equal class_name ancestor_name
->
1351 add_method_impl acc method_name
1352 | Const
(class_name
, name)
1353 when String.equal class_name ancestor_name
->
1354 if Option.is_some
(Class.get_typeconst
ancestor name) then
1355 add_typeconst_impl acc name
1356 else if Option.is_some
(Class.get_const
ancestor name) then
1357 add_const_impl acc name
1363 List.fold ~
init:[] ~f
:add_impls (Class.all_ancestor_names
cls)
1366 List.fold ~
init:result ~f
:add_impls (Class.all_ancestor_req_names
cls)
1370 let rec add_implementation_dependencies ctx
env =
1371 let open Typing_deps.Dep
in
1372 let size = HashSet.length
env.dependencies
in
1373 HashSet.fold
env.dependencies ~
init:[] ~f
:(fun dep acc ->
1375 | Class cls_name
-> cls_name
:: acc
1377 |> List.concat_map ~f
:(get_implementation_dependencies ctx
env)
1378 |> List.iter ~f
:(do_add_dep ctx
env);
1379 if HashSet.length
env.dependencies
<> size then
1380 add_implementation_dependencies ctx
env
1382 let get_dependency_origin ctx
cls (dep : 'a
Typing_deps.Dep.variant
) =
1385 let description = variant_to_string
dep in
1386 let cls = value_or_not_found description @@ get_class ctx
cls in
1389 let p = value_or_not_found description @@ Class.get_prop
cls name in
1391 | SProp
(_
, name) ->
1392 let sp = value_or_not_found description @@ Class.get_sprop
cls name in
1394 | Method
(_
, name) ->
1395 let m = value_or_not_found description @@ Class.get_method
cls name in
1397 | SMethod
(_
, name) ->
1398 let sm = value_or_not_found description @@ Class.get_smethod
cls name in
1400 | Const
(_
, name) ->
1401 let c = value_or_not_found description @@ Class.get_const
cls name in
1404 | _
-> raise UnexpectedDependency
))
1406 let collect_dependencies ctx target
=
1407 let filename = get_filename ctx target
in
1410 dependencies
= HashSet.create
();
1411 depends_on_make_default
= ref false;
1412 depends_on_any
= ref false;
1416 (root
: Typing_deps.Dep.dependent
Typing_deps.Dep.variant
)
1417 (obj
: Typing_deps.Dep.dependency
Typing_deps.Dep.variant
) : unit =
1418 if is_relevant_dependency target root
then do_add_dep ctx
env obj
1420 Typing_deps.add_dependency_callback
"add_dependency" add_dependency;
1421 (* Collect dependencies through side effects of typechecking and remove
1422 * the target function/method from the set of dependencies to avoid
1423 * declaring it twice.
1429 let (_
: (Tast.def
* Typing_inference_env.t_global_with_pos
) option) =
1430 Typing_check_service.type_fun ctx
filename func
1432 add_implementation_dependencies ctx
env;
1433 HashSet.remove
env.dependencies
(Fun
func);
1434 HashSet.remove
env.dependencies
(FunName
func)
1435 | Method
(cls, m) ->
1437 : (Tast.def
* Typing_inference_env.t_global_with_pos list
) option)
1439 Typing_check_service.type_class ctx
filename cls
1441 HashSet.add
env.dependencies
(Method
(cls, m));
1442 HashSet.add
env.dependencies
(SMethod
(cls, m));
1443 add_implementation_dependencies ctx
env;
1444 HashSet.remove
env.dependencies
(Method
(cls, m));
1445 HashSet.remove
env.dependencies
(SMethod
(cls, m)))
1449 let group_class_dependencies_by_class ctx dependencies
=
1456 if SMap.mem
cls acc then
1460 | AllMembers _
-> acc
1463 let cls = value_exn UnexpectedDependency
(get_class_name obj
) in
1464 let origin = get_dependency_origin ctx
cls obj
in
1465 (* Consider the following example:
1468 * public static function do(): void {}
1470 * class Derived extends Base {}
1471 * function f(): void {
1475 * We will pull both SMethod(Base, do) and SMethod(Derived, do) as
1476 * dependencies, but we should not generate method do() in Derived.
1477 * Therefore, we should ignore dependencies whose origin differs
1480 if String.equal
origin cls then
1481 SMap.add
cls [obj
] acc ~combine
:(fun x y
-> y
@ x
)
1486 (* Every namespace can contain declarations of classes, functions, constants
1487 as well as nested namespaces *)
1488 type hack_namespace
= {
1489 namespaces
: (string, hack_namespace
) Caml.Hashtbl.t
;
1490 decls
: string HashSet.t
;
1493 let subnamespace index
name =
1494 let (nspaces
, _
) = String.rsplit2_exn ~on
:'
\\'
name in
1495 if String.equal nspaces
"" then
1498 let nspaces = String.strip ~drop
:(fun c -> Char.equal
c '
\\'
) nspaces in
1499 let nspaces = String.split ~on
:'
\\'
nspaces in
1500 List.nth
nspaces index
1502 (* Build the recursive hack_namespace data structure for given declarations *)
1503 let sort_by_namespace declarations
=
1504 let rec add_decl nspace
decl index
=
1505 match subnamespace index
decl with
1507 ( if Option.is_none
(Caml.Hashtbl.find_opt nspace
.namespaces
name) then
1508 let nested = Caml.Hashtbl.create
0 in
1509 let declarations = HashSet.create
() in
1513 { namespaces
= nested; decls
= declarations } );
1514 add_decl (Caml.Hashtbl.find nspace
.namespaces
name) decl (index
+ 1)
1515 | None
-> HashSet.add nspace
.decls
decl
1518 { namespaces = Caml.Hashtbl.create
0; decls
= HashSet.create
() }
1520 List.iter
declarations ~f
:(fun decl -> add_decl namespaces decl 0);
1523 (* Takes declarations of Hack classes, functions, constants (map name -> code)
1524 and produces file(s) with Hack code:
1525 1) Groups declarations by namespaces, creating hack_namespace data structure
1526 2) Recursively prints the code in every namespace.
1527 Special case: since Hack files cannot contain both namespaces and toplevel
1528 declarations, we "generate" a separate file for toplevel declarations, using
1529 hh_single_type_check multifile syntax.
1532 ~depends_on_make_default
1535 partial_declarations
=
1536 let get_code declarations =
1537 let decl_names = SMap.keys
declarations in
1538 let global_namespace = sort_by_namespace decl_names in
1539 let code_from_namespace_decls name acc =
1540 Option.value (SMap.find_opt
name declarations) ~
default:[] @ acc
1543 HashSet.fold
global_namespace.decls ~
init:[] ~f
:code_from_namespace_decls
1545 let rec code_from_namespace nspace_name nspace_content code
=
1546 let code = "}" :: code in
1548 Caml.Hashtbl.fold
code_from_namespace nspace_content
.namespaces code
1552 nspace_content
.decls
1554 ~f
:code_from_namespace_decls
1556 Printf.sprintf
"namespace %s {" nspace_name
:: code
1559 Caml.Hashtbl.fold
code_from_namespace global_namespace.namespaces []
1561 (toplevel, namespaces)
1563 let (strict_toplevel
, strict_namespaces
) = get_code strict_declarations
in
1564 let (partial_toplevel
, partial_namespaces
) = get_code partial_declarations
in
1566 ( if depends_on_make_default
then
1569 "<<__Rx>> function %s(): nothing {throw new \\Exception();}"
1570 function_make_default;
1575 if depends_on_any
then
1577 "/* HH_FIXME[4101] */";
1580 extract_standalone_any
1581 extract_standalone_any;
1582 Printf.sprintf
"type %s_<T> = T;" extract_standalone_any;
1587 let strict_hh_prefix = "<?hh" in
1588 let partial_hh_prefix = "<?hh // partial" in
1591 ("//// strict_toplevel.php", (strict_hh_prefix, strict_toplevel
@ helpers));
1592 ("//// partial_toplevel.php", (partial_hh_prefix, partial_toplevel
));
1593 ("//// strict_namespaces.php", (strict_hh_prefix, strict_namespaces
));
1594 ("//// partial_namespaces.php", (partial_hh_prefix, partial_namespaces
));
1597 let non_empty_sections =
1598 List.filter
sections ~f
:(fun (_
, (_
, decls
)) -> not
(List.is_empty decls
))
1600 let format_section (prefix
, decls
) =
1601 prefix ^
"\n" ^
format (String.concat ~sep
:"\n" decls
)
1603 match non_empty_sections with
1604 | [(_
, section
)] -> format_section section
1608 ~f
:(fun (comment
, section
) -> comment ^
"\n" ^
format_section section
)
1611 let get_declarations ctx target class_dependencies global_dependencies
=
1612 let (strict_class_dependencies
, partial_class_dependencies
) =
1613 SMap.partition
(fun cls _
-> is_strict_class ctx
cls) class_dependencies
1615 let (strict_global_dependencies
, partial_global_dependencies
) =
1616 List.partition_tf global_dependencies ~f
:(is_strict_dep ctx
)
1618 let add_declaration declarations name declaration
=
1619 SMap.add
name [declaration
] declarations ~combine
:(fun x y
-> y
@ x
)
1621 let add_global_declaration declarations dep =
1624 (global_dep_name dep)
1625 (get_global_object_declaration ctx
dep)
1627 let add_class_declaration cls fields
declarations =
1631 (construct_type_declaration ctx
cls target fields
)
1633 let strict_declarations =
1635 strict_global_dependencies
1636 ~f
:add_global_declaration
1638 |> SMap.fold
add_class_declaration strict_class_dependencies
1640 let partial_declarations =
1642 partial_global_dependencies
1643 ~f
:add_global_declaration
1645 |> SMap.fold
add_class_declaration partial_class_dependencies
1649 let decl = extract_target ctx target
in
1650 if is_strict_fun ctx
name then
1651 (add_declaration strict_declarations name decl, partial_declarations)
1653 (strict_declarations, add_declaration partial_declarations name decl)
1654 | Method _
-> (strict_declarations, partial_declarations)
1658 let env = collect_dependencies ctx target
in
1659 let dependencies = HashSet.fold
env.dependencies ~
init:[] ~f
:List.cons
in
1660 let (class_dependencies
, global_dependencies
) =
1661 List.partition_tf
dependencies ~f
:(fun dep ->
1662 Option.is_some
(get_class_name dep))
1664 let (strict_declarations, partial_declarations) =
1668 (group_class_dependencies_by_class ctx class_dependencies
)
1672 ~depends_on_make_default
:!(env.depends_on_make_default
)
1673 ~depends_on_any
:!(env.depends_on_any
)
1675 partial_declarations
1677 | DependencyNotFound d
-> Printf.sprintf
"Dependency not found: %s" d
1679 | UnexpectedDependency
->
1680 Printexc.get_backtrace
()