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
38 let get_class_exn ctx name
=
39 let not_found_msg = Printf.sprintf
"Class %s" name
in
40 value_or_not_found not_found_msg @@ Decl_provider.get_class ctx name
42 let get_fun_pos ctx name
=
43 Decl_provider.get_fun ctx name
|> Option.map ~f
:(fun decl
-> decl
.fe_pos
)
45 let get_fun_pos_exn ctx name
= value_or_not_found name
(get_fun_pos ctx name
)
47 let get_class_pos ctx name
=
48 Decl_provider.get_class ctx name
49 |> Option.map ~f
:(fun decl
-> Class.pos decl
)
51 let get_class_pos_exn ctx name
=
52 value_or_not_found name
(get_class_pos ctx name
)
54 let get_typedef_pos ctx name
=
55 Decl_provider.get_typedef ctx name
56 |> Option.map ~f
:(fun decl
-> decl
.td_pos
)
58 let get_gconst_pos ctx name
=
59 Decl_provider.get_gconst ctx name
60 |> Option.map ~f
:(fun ty
-> Typing_defs.get_pos ty
)
62 let get_class_or_typedef_pos ctx name
=
63 Option.first_some
(get_class_pos ctx name
) (get_typedef_pos ctx name
)
66 module Nast_helper
= struct
67 let make_nast_getter ~get_pos ~find_in_file ~naming
=
68 let nasts = ref SMap.empty
in
70 if SMap.mem name
!nasts then
71 Some
(SMap.find name
!nasts)
74 get_pos ctx name
>>= fun pos
->
75 find_in_file ctx
(Pos.filename pos
) name
>>= fun nast
->
76 let nast = naming ctx
nast in
77 nasts := SMap.add name
nast !nasts;
82 ~get_pos
:Decl.get_fun_pos
83 ~find_in_file
:Ast_provider.find_fun_in_file
86 let get_fun_nast_exn ctx name
=
87 value_or_not_found name
(get_fun_nast ctx name
)
91 ~get_pos
:Decl.get_class_pos
92 ~find_in_file
:Ast_provider.find_class_in_file
95 let get_typedef_nast =
97 ~get_pos
:Decl.get_typedef_pos
98 ~find_in_file
:Ast_provider.find_typedef_in_file
99 ~naming
:Naming.typedef
101 let get_typedef_nast_exn ctx name
=
102 value_or_not_found name
(get_typedef_nast ctx name
)
104 let get_gconst_nast =
106 ~get_pos
:Decl.get_gconst_pos
107 ~find_in_file
:Ast_provider.find_gconst_in_file
108 ~naming
:Naming.global_const
110 let get_gconst_nast_exn ctx name
=
111 value_or_not_found name
(get_gconst_nast ctx name
)
113 let make_class_element_nast_getter ~get_elements ~get_element_name
=
114 let elements_by_class_name = ref SMap.empty
in
115 fun ctx class_name element_name
->
116 if SMap.mem class_name
!elements_by_class_name then
119 (SMap.find class_name
!elements_by_class_name)
122 get_class_nast ctx class_name
>>= fun class_
->
123 let elements_by_element_name =
125 (get_elements class_
)
126 ~f
:(fun elements element
->
127 SMap.add
(get_element_name element
) element elements
)
130 elements_by_class_name :=
131 SMap.add class_name
elements_by_element_name !elements_by_class_name;
132 SMap.find_opt element_name
elements_by_element_name
134 let get_method_nast =
135 make_class_element_nast_getter
136 ~get_elements
:(fun class_
-> class_
.c_methods
)
137 ~get_element_name
:(fun method_
-> snd method_
.m_name
)
139 let get_method_nast_exn ctx class_name method_name
=
141 (class_name ^
"::" ^ method_name
)
142 (get_method_nast ctx class_name method_name
)
145 make_class_element_nast_getter
146 ~get_elements
:(fun class_
-> class_
.c_consts
)
147 ~get_element_name
:(fun const
-> snd const
.cc_id
)
149 let get_typeconst_nast =
150 make_class_element_nast_getter
151 ~get_elements
:(fun class_
-> class_
.c_typeconsts
)
152 ~get_element_name
:(fun typeconst
-> snd typeconst
.c_tconst_name
)
155 make_class_element_nast_getter
156 ~get_elements
:(fun class_
-> class_
.c_vars
)
157 ~get_element_name
:(fun class_var
-> snd class_var
.cv_id
)
159 let get_prop_nast_exn ctx class_name prop_name
=
161 (class_name ^
"::" ^ prop_name
)
162 (get_prop_nast ctx class_name prop_name
)
166 let get_class_name : type a
. a
Typing_deps.Dep.variant
-> string option =
167 (* the OCaml compiler is not smart enough to let us use an or-pattern for all of these
168 * because of how it's matching on a GADT *)
172 | Const
(cls
, _
) -> Some cls
173 | Method
(cls
, _
) -> Some cls
174 | SMethod
(cls
, _
) -> Some cls
175 | Prop
(cls
, _
) -> Some cls
176 | SProp
(cls
, _
) -> Some cls
177 | Class cls
-> Some cls
178 | Cstr cls
-> Some cls
179 | AllMembers cls
-> Some cls
180 | Extends cls
-> Some cls
186 | RecordDef _
-> records_not_supported ())
188 let global_dep_name dep
=
205 raise UnexpectedDependency
206 | RecordDef _
-> records_not_supported ())
208 let get_dep_pos ctx dep
=
209 let open Typing_deps.Dep
in
213 Decl.get_fun_pos ctx name
223 Decl.get_class_or_typedef_pos ctx name
226 Decl.get_gconst_pos ctx name
227 | RecordDef _
-> records_not_supported ()
229 let get_fun_mode ctx name
=
230 Nast_helper.get_fun_nast ctx name
|> Option.map ~f
:(fun fun_
-> fun_
.f_mode
)
232 let get_class_mode ctx name
=
233 Nast_helper.get_class_nast ctx name
234 |> Option.map ~f
:(fun class_
-> class_
.c_mode
)
236 let get_typedef_mode ctx name
=
237 Nast_helper.get_typedef_nast ctx name
238 |> Option.map ~f
:(fun typedef
-> typedef
.t_mode
)
240 let get_gconst_mode ctx name
=
241 Nast_helper.get_gconst_nast ctx name
242 |> Option.map ~f
:(fun gconst
-> gconst
.cst_mode
)
244 let get_class_or_typedef_mode ctx name
=
245 Option.first_some
(get_class_mode ctx name
) (get_typedef_mode ctx name
)
247 let get_dep_mode ctx dep
=
248 let open Typing_deps.Dep
in
252 get_fun_mode ctx name
262 get_class_or_typedef_mode ctx name
265 get_gconst_mode ctx name
266 | RecordDef _
-> records_not_supported ()
268 let is_strict_dep ctx dep
=
269 match get_dep_mode ctx dep
with
270 | Some
FileInfo.Mstrict
-> true
273 let is_strict_fun ctx name
= is_strict_dep ctx
(Typing_deps.Dep.Fun name
)
275 let is_strict_class ctx name
= is_strict_dep ctx
(Typing_deps.Dep.Class name
)
277 let is_builtin_dep ctx dep
=
278 let msg = Typing_deps.Dep.variant_to_string dep
in
279 let pos = value_or_not_found msg (get_dep_pos ctx dep
) in
280 Relative_path.(is_hhi
(prefix
(Pos.filename
pos)))
282 let is_relevant_dependency
284 (dep
: Typing_deps.Dep.dependent
Typing_deps.Dep.variant
) =
288 | Typing_deps.Dep.Fun g
289 | Typing_deps.Dep.FunName g
->
292 (* We have to collect dependencies of the entire class because dependency collection is
293 coarse-grained: if cls's member depends on D, we get a dependency edge cls --> D,
294 not (cls, member) --> D *)
296 Option.equal
String.equal
(get_class_name dep
) (Some cls
)
299 module Target
= struct
300 let get_filename ctx target
=
303 | Function name
-> Decl.get_fun_pos_exn ctx name
304 | Method
(name
, _
) -> Decl.get_class_pos_exn ctx name
308 let extract_target ctx target
=
309 let filename = get_filename ctx target
in
310 let abs_filename = Relative_path.to_absolute
filename in
311 let file_content = In_channel.read_all
abs_filename in
315 let fun_ = Nast_helper.get_fun_nast_exn ctx name
in
317 | Method
(class_name
, method_name
) ->
319 Nast_helper.get_method_nast_exn ctx class_name method_name
323 Pos.get_text_from_pos
file_content pos
326 module Pretty
= struct
327 let print_error source_text error
=
329 SyntaxError.to_positioned_string
331 (SourceText.offset_to_position source_text
)
333 Hh_logger.log
"%s\n" text
335 let tree_from_string s
=
336 let source_text = SourceText.make
Relative_path.default s
in
337 let mode = Full_fidelity_parser.parse_mode
source_text in
338 let env = Full_fidelity_parser_env.make ?
mode () in
339 let tree = SyntaxTree.make ~
env source_text in
340 if List.is_empty
(SyntaxTree.all_errors
tree) then
343 List.iter
(SyntaxTree.all_errors
tree) (print_error source_text);
344 raise
Hackfmt_error.InvalidSyntax
348 let re = Str.regexp
"\\\\:" in
349 Str.global_replace
re ":"
352 try Libhackfmt.format_tree
(tree_from_string (fixup_xhp text))
353 with Hackfmt_error.InvalidSyntax
-> text
355 let strip_ns obj_name
=
356 match String.rsplit2 obj_name '
\\'
with
357 | Some
(_
, name
) -> name
360 let concat_map ~sep ~f list
= String.concat ~sep
(List.map ~f list
)
362 let function_make_default = "extract_standalone_make_default"
364 let call_make_default = Printf.sprintf
"\\%s()" function_make_default
366 let extract_standalone_any = "EXTRACT_STANDALONE_ANY"
368 let string_of_tprim = function
373 | Tstring
-> "string"
374 | Tarraykey
-> "arraykey"
377 | Tresource
-> "resource"
378 | Tnoreturn
-> "noreturn"
380 let string_of_shape_field_name = function
381 | Ast_defs.SFlit_int
(_
, s
) -> s
382 | Ast_defs.SFlit_str
(_
, s
) -> Printf.sprintf
"'%s'" s
383 | Ast_defs.SFclass_const
((_
, c
), (_
, s
)) -> Printf.sprintf
"%s::%s" c s
385 let string_of_xhp_attr_info xhp_attr_info
=
386 match xhp_attr_info
.xai_tag
with
387 | Some Required
-> "@required"
388 | Some LateInit
-> "@lateinit"
391 let rec string_of_hint hint
=
393 | Hoption hint
-> "?" ^
string_of_hint hint
394 | Hlike hint
-> "~" ^
string_of_hint hint
397 hf_reactive_kind
= _
;
400 hf_param_mutability
= _
;
403 (* TODO(vmladenov) support capability types here *)
405 hf_is_mutable_return
= _
;
407 let param_hints = List.map hf_param_tys ~f
:string_of_hint in
409 List.map hf_param_kinds ~f
:(function
410 | Some
Ast_defs.Pinout
-> "inout "
413 let params = List.map2_exn
param_kinds param_hints ~f
:( ^
) in
415 match hf_variadic_ty
with
416 | Some hint
-> [string_of_hint hint ^
"..."]
420 "(function(%s) : %s)"
421 (String.concat ~sep
:", " (params @ variadic))
422 (string_of_hint hf_return_ty
)
424 Printf.sprintf
"(%s)" (concat_map ~sep
:", " ~f
:string_of_hint hints
)
425 | Habstr
(name
, hints
)
426 | Happly
((_
, name
), hints
) ->
431 Printf.sprintf
"<%s>" (concat_map ~sep
:", " ~f
:string_of_hint hints
)
434 | Hshape
{ nsi_allows_unknown_fields
; nsi_field_map
} ->
435 let string_of_shape_field { sfi_optional
; sfi_name
; sfi_hint
} =
436 let optional_prefix =
445 (string_of_shape_field_name sfi_name
)
446 (string_of_hint sfi_hint
)
448 let shape_fields = List.map nsi_field_map ~f
:string_of_shape_field in
450 if nsi_allows_unknown_fields
then
455 let shape_entries = shape_fields @ shape_suffix in
456 Printf.sprintf
"shape(%s)" (String.concat ~sep
:", " shape_entries)
457 | Haccess
(root
, ids
) ->
458 String.concat ~sep
:"::" (string_of_hint root
:: List.map ids ~f
:snd
)
459 | Hsoft hint
-> "@" ^
string_of_hint hint
461 | Hnonnull
-> "nonnull"
462 | Hdarray
(khint
, vhint
) ->
465 (string_of_hint khint
)
466 (string_of_hint vhint
)
467 | Hvarray hint
-> Printf.sprintf
"varray<%s>" (string_of_hint hint
)
468 | Hvarray_or_darray
(None
, vhint
) ->
469 Printf.sprintf
"varray_or_darray<%s>" (string_of_hint vhint
)
470 | Hvarray_or_darray
(Some khint
, vhint
) ->
472 "varray_or_darray<%s, %s>"
473 (string_of_hint khint
)
474 (string_of_hint vhint
)
475 | Hprim prim
-> string_of_tprim prim
477 | Hdynamic
-> "dynamic"
478 | Hnothing
-> "nothing"
480 Printf.sprintf
"(%s)" (concat_map ~sep
:" | " ~f
:string_of_hint hints
)
481 | Hintersection hints
->
482 Printf.sprintf
"(%s)" (concat_map ~sep
:" & " ~f
:string_of_hint hints
)
483 | Hany
-> extract_standalone_any
484 | Herr
-> extract_standalone_any
485 | Hfun_context name
-> "ctx " ^ name
488 let maybe_string_of_user_attribute { ua_name
; ua_params
} =
489 let name = snd ua_name
in
491 | [] when SMap.mem
name SN.UserAttributes.as_map
-> Some
name
494 let string_of_user_attributes user_attributes
=
495 let user_attributes =
496 List.filter_map ~f
:maybe_string_of_user_attribute user_attributes
498 match user_attributes with
500 | _
-> Printf.sprintf
"<<%s>>" (String.concat ~sep
:", " user_attributes)
502 let string_of_variance = function
503 | Ast_defs.Covariant
-> "+"
504 | Ast_defs.Contravariant
-> "-"
505 | Ast_defs.Invariant
-> ""
507 let string_of_constraint (kind
, hint
) =
510 | Ast_defs.Constraint_as
-> "as"
511 | Ast_defs.Constraint_eq
-> "="
512 | Ast_defs.Constraint_super
-> "super"
514 keyword ^
" " ^
string_of_hint hint
516 let rec string_of_tparam
526 let variance = string_of_variance tp_variance
in
527 let name = snd tp_name
in
528 let parameters = string_of_tparams tp_parameters
in
529 let constraints = List.map tp_constraints ~f
:string_of_constraint in
530 let user_attributes = string_of_user_attributes tp_user_attributes
in
532 match tp_reified
with
546 and string_of_tparams tparams
=
550 Printf.sprintf
"<%s>" (concat_map ~sep
:", " ~f
:string_of_tparam tparams
)
552 let string_of_fun_param
559 param_user_attributes
;
562 let user_attributes = string_of_user_attributes param_user_attributes
in
564 match param_callconv
with
565 | Some
Ast_defs.Pinout
-> "inout"
569 match param_type_hint
with
570 | (_
, Some hint
) -> string_of_hint hint
574 if param_is_variadic
then
580 match param_expr
with
581 | Some _
-> " = " ^
call_make_default
593 let get_fun_declaration ctx
name =
594 let fun_ = Nast_helper.get_fun_nast_exn ctx
name in
595 let user_attributes = string_of_user_attributes fun_.f_user_attributes
in
596 let tparams = string_of_tparams
fun_.f_tparams
in
598 match fun_.f_variadic
with
599 | FVvariadicArg fp
-> [string_of_fun_param fp
]
600 | FVellipsis _
-> ["..."]
601 | FVnonVariadic
-> []
606 (List.map
fun_.f_params ~f
:string_of_fun_param @ variadic)
609 match fun_.f_ret
with
610 | (_
, Some hint
) -> ": " ^
string_of_hint hint
614 "%s function %s%s(%s)%s {throw new \\Exception();}"
621 let get_init_for_prim = function
622 | Aast_defs.Tnull
-> "null"
626 | Aast_defs.Tbool
-> "false"
627 | Aast_defs.Tfloat
-> "0.0"
629 | Aast_defs.Tarraykey
->
632 | Aast_defs.Tresource
633 | Aast_defs.Tnoreturn
->
636 let rec get_init_from_hint ctx tparams_stack hint
=
637 let unsupported_hint () =
639 "%s: get_init_from_hint: unsupported hint: %s"
640 (Pos.string (Pos.to_absolute
(fst hint
)))
641 (Aast_defs.show_hint hint
);
645 | Hprim prim
-> get_init_for_prim prim
646 | Hoption _
-> "null"
647 | Hlike hint
-> get_init_from_hint ctx tparams_stack hint
648 | Hdarray _
-> "darray[]"
649 | Hvarray_or_darray _
655 (concat_map ~sep
:", " ~f
:(get_init_from_hint ctx tparams_stack
) hints
)
656 | Happly
((_
, name), hints
) ->
659 when String.equal
name SN.Collections.cVec
660 || String.equal
name SN.Collections.cKeyset
661 || String.equal
name SN.Collections.cDict
->
662 Printf.sprintf
"%s[]" (strip_ns name)
664 when String.equal
name SN.Collections.cVector
665 || String.equal
name SN.Collections.cImmVector
666 || String.equal
name SN.Collections.cMap
667 || String.equal
name SN.Collections.cImmMap
668 || String.equal
name SN.Collections.cSet
669 || String.equal
name SN.Collections.cImmSet
->
670 Printf.sprintf
"%s {}" (strip_ns name)
671 | _
when String.equal
name SN.Collections.cPair
->
676 (get_init_from_hint ctx tparams_stack first
)
677 (get_init_from_hint ctx tparams_stack second
)
678 | _
-> failwith
"malformed hint")
679 | _
when String.equal
name SN.Classes.cClassname
->
681 | [(_
, Happly
((_
, class_name
), _
))] ->
682 Printf.sprintf
"%s::class" class_name
683 | _
-> raise UnexpectedDependency
)
685 (match Nast_helper.get_class_nast ctx
name with
687 (match class_
.c_kind
with
690 match class_
.c_consts
with
691 | [] -> failwith
"empty enum"
692 | const
:: _
-> snd const
.cc_id
694 Printf.sprintf
"%s::%s" name const_name
695 | _
-> unsupported_hint ())
697 let typedef = Nast_helper.get_typedef_nast_exn ctx
name in
703 ~f
:(fun tparams tparam hint
->
704 SMap.add
(snd tparam
.tp_name
) hint
tparams)
706 get_init_from_hint ctx
(tparams :: tparams_stack
) typedef.t_kind
))
707 | Hshape
{ nsi_field_map
; _
} ->
708 let non_optional_fields =
709 List.filter nsi_field_map ~f
:(fun shape_field_info
->
710 not shape_field_info
.sfi_optional
)
712 let get_init_shape_field { sfi_hint
; sfi_name
; _
} =
715 (string_of_shape_field_name sfi_name
)
716 (get_init_from_hint ctx tparams_stack sfi_hint
)
720 (concat_map ~sep
:", " ~f
:get_init_shape_field non_optional_fields)
721 | Habstr
(name, []) ->
722 (* FIXME: support non-empty type arguments of Habstr here? *)
723 let rec loop tparams_stack
=
724 match tparams_stack
with
725 | tparams :: tparams_stack'
->
726 (match SMap.find_opt
name tparams with
727 | Some hint
-> get_init_from_hint ctx tparams_stack' hint
728 | None
-> loop tparams_stack'
)
729 | [] -> unsupported_hint ()
732 | _
-> unsupported_hint ()
734 let get_init_from_hint ctx hint
= get_init_from_hint ctx
[] hint
736 let get_gconst_declaration ctx
name =
737 let gconst = Nast_helper.get_gconst_nast_exn ctx
name in
738 let hint = value_or_not_found ("type of " ^
name) gconst.cst_type
in
739 let init = get_init_from_hint ctx
hint in
742 (string_of_hint hint)
746 let get_const_declaration ctx const
=
747 let name = snd const
.cc_id
in
749 match const
.cc_expr
with
754 match (const
.cc_type
, const
.cc_expr
) with
756 (string_of_hint hint, " = " ^
get_init_from_hint ctx
hint)
758 (match Decl_utils.infer_const e
with
760 let hint = (fst e
, Hprim tprim
) in
761 ("", " = " ^
get_init_from_hint ctx
hint)
762 | None
-> raise Unsupported
)
763 | (None
, None
) -> ("", "")
765 Printf.sprintf
"%s const %s %s%s;" abstract type_
name init
767 let get_global_object_declaration ctx obj
=
772 get_fun_declaration ctx f
775 get_gconst_declaration ctx c
776 (* No other global declarations *)
777 | _
-> raise UnexpectedDependency
)
779 let get_class_declaration class_
=
780 let name = snd class_
.c_name
in
781 let user_attributes = string_of_user_attributes class_
.c_user_attributes
in
783 if class_
.c_final
then
789 match class_
.c_kind
with
790 | Ast_defs.Cabstract
-> "abstract class"
791 | Ast_defs.Cnormal
-> "class"
792 | Ast_defs.Cinterface
-> "interface"
793 | Ast_defs.Ctrait
-> "trait"
794 | Ast_defs.Cenum
-> "enum"
796 let tparams = string_of_tparams class_
.c_tparams
in
798 match class_
.c_extends
with
803 (concat_map ~sep
:", " ~f
:string_of_hint class_
.c_extends
)
806 match class_
.c_implements
with
811 (concat_map ~sep
:", " ~f
:string_of_hint class_
.c_implements
)
814 "%s %s %s %s%s %s %s"
823 let get_method_declaration method_ ~from_interface
=
825 if method_.m_abstract
&& not from_interface
then
831 if method_.m_final
then
836 let visibility = string_of_visibility
method_.m_visibility
in
838 if method_.m_static
then
843 let user_attributes = string_of_user_attributes method_.m_user_attributes
in
844 let name = strip_ns (snd
method_.m_name
) in
845 let tparams = string_of_tparams
method_.m_tparams
in
847 match method_.m_variadic
with
848 | FVvariadicArg fp
-> [string_of_fun_param fp
]
849 | FVellipsis _
-> ["..."]
850 | FVnonVariadic
-> []
855 (List.map
method_.m_params ~f
:string_of_fun_param @ variadic)
858 match method_.m_ret
with
859 | (_
, Some
hint) -> ": " ^
string_of_hint hint
863 if method_.m_abstract
|| from_interface
then
866 "{throw new \\Exception();}"
869 "%s %s %s %s %s function %s%s(%s)%s%s"
881 let get_prop_declaration ctx prop
=
882 let name = snd prop
.cv_id
in
883 let user_attributes = string_of_user_attributes prop
.cv_user_attributes
in
885 match (hint_of_type_hint prop
.cv_type
, prop
.cv_expr
) with
886 | (Some
hint, Some _
) ->
887 ( string_of_hint hint,
888 Printf.sprintf
" = %s" (get_init_from_hint ctx
hint) )
889 | (Some
hint, None
) -> (string_of_hint hint, "")
890 | (None
, None
) -> ("", "")
891 (* Untyped prop, not supported for now *)
892 | (None
, Some _
) -> raise Unsupported
894 match prop
.cv_xhp_attr
with
896 (* Ordinary property *)
897 let visibility = string_of_visibility prop
.cv_visibility
in
899 if prop
.cv_is_static
then
912 | Some xhp_attr_info
->
915 "%s attribute %s %s %s %s;"
918 (String.lstrip ~drop
:(fun c
-> Char.equal c '
:'
) name)
920 (string_of_xhp_attr_info xhp_attr_info
)
922 let get_typeconst_declaration typeconst
=
924 match typeconst
.c_tconst_abstract
with
925 | TCAbstract _
-> "abstract"
926 | TCPartiallyAbstract
930 let name = snd typeconst
.c_tconst_name
in
932 match typeconst
.c_tconst_type
with
933 | Some
hint -> " = " ^
string_of_hint hint
937 match typeconst
.c_tconst_constraint
with
938 | Some
hint -> " as " ^
string_of_hint hint
941 Printf.sprintf
"%s const type %s%s%s;" abstract name constraint_ type_
943 let get_method_declaration ctx target class_name method_name
=
945 | ServerCommandTypes.Extract_standalone.Method
946 (target_class_name
, target_method_name
)
947 when String.equal class_name target_class_name
948 && String.equal method_name target_method_name
->
952 Nast_helper.get_class_nast ctx class_name
>>= fun class_
->
953 let from_interface = Ast_defs.is_c_interface class_
.c_kind
in
954 Nast_helper.get_method_nast ctx class_name method_name
>>= fun method_ ->
955 Some
(get_method_declaration method_ ~
from_interface)
957 let get_class_elt_declaration
958 ctx target
(class_elt
: 'a
Typing_deps.Dep.variant
) =
959 let open Typing_deps.Dep
in
961 | Const
(class_name
, const_name) ->
962 (match Nast_helper.get_typeconst_nast ctx class_name
const_name with
963 | Some typeconst
-> Some
(get_typeconst_declaration typeconst
)
965 (match Nast_helper.get_const_nast ctx class_name
const_name with
966 | Some const
-> Some
(get_const_declaration ctx const
)
967 | None
-> raise
(DependencyNotFound
(class_name ^
"::" ^
const_name))))
968 | Method
(class_name
, method_name
)
969 | SMethod
(class_name
, method_name
) ->
970 get_method_declaration ctx target class_name method_name
972 get_method_declaration ctx target class_name
"__construct"
973 | Prop
(class_name
, prop_name
) ->
974 let prop = Nast_helper.get_prop_nast_exn ctx class_name prop_name
in
975 Some
(get_prop_declaration ctx
prop)
976 | SProp
(class_name
, sprop_name
) ->
978 String.lstrip ~drop
:(fun c
-> Char.equal c '$'
) sprop_name
980 let prop = Nast_helper.get_prop_nast_exn ctx class_name
sprop_name in
981 Some
(get_prop_declaration ctx
prop)
982 (* Constructor should've been tackled earlier, and all other dependencies aren't class elements *)
990 raise UnexpectedDependency
991 | RecordDef _
-> records_not_supported ()
993 let construct_enum ctx class_
=
994 let name = snd class_
.c_name
in
996 match class_
.c_enum
with
998 | None
-> failwith
("not an enum: " ^ snd class_
.c_name
)
1001 match enum.e_constraint
with
1002 | Some
hint -> " as " ^
string_of_hint hint
1005 let string_of_enum_const const
=
1009 (get_init_from_hint ctx
enum.e_base
)
1012 "enum %s: %s%s {%s}"
1014 (string_of_hint enum.e_base
)
1016 (concat_map ~sep
:"\n" ~f
:string_of_enum_const class_
.c_consts
)
1018 let get_class_body ctx class_ target class_elts
=
1019 let name = snd class_
.c_name
in
1021 List.map class_
.c_uses ~f
:(fun s
->
1022 Printf.sprintf
"use %s;" (string_of_hint s
))
1024 let (req_extends
, req_implements
) =
1025 List.partition_map class_
.c_reqs ~f
:(fun (s
, extends) ->
1027 `Fst
(Printf.sprintf
"require extends %s;" (string_of_hint s
))
1029 `Snd
(Printf.sprintf
"require implements %s;" (string_of_hint s
)))
1031 let open Typing_deps
in
1033 List.filter_map class_elts ~f
:(function
1036 raise UnexpectedDependency
1037 | Dep.Const
(_
, "class") -> None
1038 | class_elt
-> get_class_elt_declaration ctx target class_elt
)
1040 (* If we are extracting a method of this class, we should declare it
1041 here, with stubs of other class elements. *)
1042 let extracted_method =
1044 | Method
(cls_name
, _
) when String.equal cls_name
name ->
1045 [Target.extract_target ctx target
]
1050 (req_extends
@ req_implements
@ uses @ body @ extracted_method)
1052 let construct_class ctx class_ target fields
=
1053 let decl = get_class_declaration class_
in
1054 let body = get_class_body ctx class_ target fields
in
1055 Printf.sprintf
"%s {%s}" decl body
1057 let construct_enum_or_class ctx class_ target fields
=
1058 match class_
.c_kind
with
1059 | Ast_defs.Cabstract
1061 | Ast_defs.Cinterface
1062 | Ast_defs.Ctrait
->
1063 construct_class ctx class_ target fields
1064 | Ast_defs.Cenum
-> construct_enum ctx class_
1066 let construct_typedef typedef =
1067 let name = snd
typedef.t_name
in
1069 match typedef.t_vis
with
1070 | Aast_defs.Transparent
-> "type"
1071 | Aast_defs.Opaque
-> "newtype"
1073 let tparams = string_of_tparams
typedef.t_tparams
in
1075 match typedef.t_constraint
with
1076 | Some
hint -> " as " ^
string_of_hint hint
1079 let pos = fst
typedef.t_name
in
1083 ~f
:(fun code
-> Printf.sprintf
"/* HH_FIXME[%d] */\n" code
)
1084 (ISet.elements
(Fixme_provider.get_fixme_codes_for_pos
pos)))
1093 (string_of_hint typedef.t_kind
)
1095 let construct_type_declaration ctx t target fields
=
1096 match Nast_helper.get_class_nast ctx t
with
1097 | Some class_
-> construct_enum_or_class ctx class_ target fields
1099 let typedef = Nast_helper.get_typedef_nast_exn ctx t
in
1100 construct_typedef typedef
1103 type extraction_env
= {
1104 dependencies
: Typing_deps.Dep.dependency
Typing_deps.Dep.variant
HashSet.t
;
1105 depends_on_make_default
: bool ref;
1106 depends_on_any
: bool ref;
1109 let rec do_add_dep ctx
env dep
=
1112 | Typing_deps.Dep.Class h
-> String.equal h
SN.Typehints.wildcard
1117 && (not
(HashSet.mem
env.dependencies dep
))
1118 && not
(Dep.is_builtin_dep ctx dep
)
1120 HashSet.add
env.dependencies dep
;
1121 add_signature_dependencies ctx
env dep
1124 and add_dep ctx
env ~this ty
: unit =
1127 inherit [unit] Type_visitor.decl_type_visitor
as super
1129 method! on_tany _ _
= env.depends_on_any
:= true
1131 method! on_tfun
() r ft
=
1132 if List.exists ~f
:Typing_defs.get_fp_has_default ft
.ft_params
then
1133 env.depends_on_make_default
:= true;
1134 super#on_tfun
() r ft
1136 method! on_tapply _ _
(_
, name) tyl
=
1137 let dep = Typing_deps.Dep.Class
name in
1138 do_add_dep ctx
env dep;
1140 (* If we have a constant of a generic type, it can only be an
1141 array type, e.g., vec<A>, for which don't need values of A
1142 to generate an initializer. *)
1143 List.iter tyl ~f
:(add_dep ctx
env ~this
)
1145 method! on_tshape _ _ _ fdm
=
1147 (fun name { sft_ty
; _
} ->
1149 | Ast_defs.SFlit_int _
1150 | Ast_defs.SFlit_str _
->
1152 | Ast_defs.SFclass_const
((_
, c
), (_
, s
)) ->
1153 do_add_dep ctx
env (Typing_deps.Dep.Class c
);
1154 do_add_dep ctx
env (Typing_deps.Dep.Const
(c
, s
)));
1155 add_dep ctx
env ~this sft_ty
)
1158 (* We un-nest (((this::T1)::T2)::T3) into (this, [T1;T2;T3]) and then re-nest
1159 * because legacy representation of Taccess was using lists. TODO: implement
1160 * this more directly instead.
1162 method! on_taccess
() r
(root
, tconst
) =
1163 let rec split_taccess root ids
=
1164 match Typing_defs.get_node root
with
1165 | Taccess
(root
, id
) -> split_taccess root
(id
:: ids
)
1168 let rec make_taccess r root ids
=
1174 (mk
(r
, Typing_defs.Taccess
(root
, id
)))
1177 let (root
, tconsts
) = split_taccess root
[tconst
] in
1178 let expand_type_access class_name tconsts
=
1180 | [] -> raise UnexpectedDependency
1181 (* Expand Class::TConst1::TConst2[::...]: get TConst1 in
1182 Class, get its type or upper bound T, continue adding
1183 dependencies of T::TConst2[::...] *)
1184 | (_
, tconst
) :: tconsts
->
1185 do_add_dep ctx
env (Typing_deps.Dep.Const
(class_name
, tconst
));
1186 let cls = Decl.get_class_exn ctx class_name
in
1187 (match Decl_provider.Class.get_typeconst
cls tconst
with
1191 ~f
:(add_dep ctx ~this
:(Some class_name
) env);
1192 if not
(List.is_empty tconsts
) then (
1193 match (typeconst
.ttc_type
, typeconst
.ttc_constraint
) with
1195 | (None
, Some tc_type
) ->
1196 (* What does 'this' refer to inside of T? *)
1198 match Typing_defs.get_node tc_type
with
1199 | Tapply
((_
, name), _
) -> Some
name
1202 let taccess = make_taccess r tc_type tconsts
in
1203 add_dep ctx ~
this env taccess
1204 | (None
, None
) -> ()
1208 match Typing_defs.get_node root
with
1209 | Taccess
(root'
, tconst
) ->
1210 add_dep ctx ~
this env (make_taccess r root'
(tconst
:: tconsts
))
1211 | Tapply
((_
, name), _
) -> expand_type_access name tconsts
1212 | Tthis
-> expand_type_access (Option.value_exn this) tconsts
1213 | _
-> raise UnexpectedDependency
1216 visitor#on_type
() ty
1218 and add_signature_dependencies ctx
env obj
=
1219 let open Typing_deps.Dep
in
1220 let description = variant_to_string obj
in
1221 match Dep.get_class_name obj
with
1223 do_add_dep ctx
env (Typing_deps.Dep.Class cls_name
);
1224 (match Decl_provider.get_class ctx cls_name
with
1227 value_or_not_found description @@ Decl_provider.get_typedef ctx cls_name
1229 add_dep ctx ~
this:None
env td.td_type
;
1230 Option.iter
td.td_constraint ~f
:(add_dep ctx ~
this:None
env)
1232 let add_dep = add_dep ctx
env ~
this:(Some cls_name
) in
1235 let p = value_or_not_found description @@ Class.get_prop
cls name in
1236 add_dep @@ Lazy.force
p.ce_type
;
1238 (* We need to initialize properties in the constructor, add a dependency on it *)
1239 do_add_dep ctx
env (Cstr cls_name
)
1240 | SProp
(_
, name) ->
1241 let sp = value_or_not_found description @@ Class.get_sprop
cls name in
1242 add_dep @@ Lazy.force
sp.ce_type
1243 | Method
(_
, name) ->
1244 let m = value_or_not_found description @@ Class.get_method
cls name in
1245 add_dep @@ Lazy.force
m.ce_type
;
1246 Class.all_ancestor_names
cls
1247 |> List.iter ~f
:(fun ancestor_name
->
1248 match Decl_provider.get_class ctx ancestor_name
with
1249 | Some ancestor
when Class.has_method ancestor
name ->
1250 do_add_dep ctx
env (Method
(ancestor_name
, name))
1252 | SMethod
(_
, name) ->
1253 (match Class.get_smethod
cls name with
1255 add_dep @@ Lazy.force sm
.ce_type
;
1256 Class.all_ancestor_names
cls
1257 |> List.iter ~f
:(fun ancestor_name
->
1258 match Decl_provider.get_class ctx ancestor_name
with
1259 | Some ancestor
when Class.has_smethod ancestor
name ->
1260 do_add_dep ctx
env (SMethod
(ancestor_name
, name))
1263 (match Class.get_method
cls name with
1265 HashSet.remove
env.dependencies obj
;
1266 do_add_dep ctx
env (Method
(cls_name
, name))
1267 | None
-> raise
(DependencyNotFound
description)))
1268 | Const
(_
, name) ->
1269 (match Class.get_typeconst
cls name with
1271 if not
(String.equal cls_name tc
.ttc_origin
) then
1272 do_add_dep ctx
env (Const
(tc
.ttc_origin
, name));
1273 Option.iter tc
.ttc_type ~f
:add_dep;
1274 Option.iter tc
.ttc_constraint ~f
:add_dep
1276 let c = value_or_not_found description @@ Class.get_const
cls name in
1279 (match Class.construct
cls with
1280 | (Some constr
, _
) -> add_dep @@ Lazy.force constr
.ce_type
1283 List.iter
(Class.all_ancestors
cls) (fun (_
, ty
) -> add_dep ty
);
1284 List.iter
(Class.all_ancestor_reqs
cls) (fun (_
, ty
) -> add_dep ty
);
1286 (Class.enum_type
cls)
1287 ~f
:(fun { te_base
; te_constraint
; te_includes
; te_enum_class
= _
} ->
1289 Option.iter te_constraint ~f
:add_dep;
1290 List.iter te_includes ~f
:add_dep)
1292 (* AllMembers is used for dependencies on enums, so we should depend on all constants *)
1293 List.iter
(Class.consts
cls) (fun (name, c) ->
1294 if not
(String.equal
name "class") then add_dep c.cc_type
)
1295 (* Ignore, we fetch class hierarchy when we call add_signature_dependencies on a class dep *)
1297 | _
-> raise UnexpectedDependency
))
1303 value_or_not_found description @@ Decl_provider.get_fun ctx f
1305 add_dep ctx ~
this:None
env @@ func.fe_type
1309 value_or_not_found description @@ Decl_provider.get_gconst ctx
c
1311 add_dep ctx ~
this:None
env ty
1312 | _
-> raise UnexpectedDependency
)
1314 let get_implementation_dependencies ctx
env cls_name
=
1315 let open Decl_provider
in
1316 match get_class ctx cls_name
with
1319 let open Typing_deps.Dep
in
1320 let add_smethod_impl acc smethod_name
=
1321 match Class.get_smethod
cls smethod_name
with
1322 | Some elt
-> SMethod
(elt
.ce_origin
, smethod_name
) :: acc
1325 let add_method_impl acc method_name
=
1326 match Class.get_method
cls method_name
with
1327 | Some elt
-> Method
(elt
.ce_origin
, method_name
) :: acc
1330 let add_typeconst_impl acc typeconst_name
=
1331 match Class.get_typeconst
cls typeconst_name
with
1332 | Some tc
-> Const
(tc
.ttc_origin
, typeconst_name
) :: acc
1335 let add_const_impl acc
const_name =
1336 match Class.get_const
cls const_name with
1337 | Some
c -> Const
(c.cc_origin
, const_name) :: acc
1340 let add_impls acc ancestor_name
=
1341 let ancestor = Decl.get_class_exn ctx ancestor_name
in
1342 if Dep.is_builtin_dep ctx
(Class ancestor_name
) then
1345 (Class.smethods
ancestor)
1347 ~f
:(fun acc (smethod_name
, _
) -> add_smethod_impl acc smethod_name
)
1351 (Class.methods
ancestor)
1353 ~f
:(fun acc (method_name
, _
) -> add_method_impl acc method_name
)
1357 (Class.typeconsts
ancestor)
1359 ~f
:(fun acc (typeconst_name
, _
) ->
1360 add_typeconst_impl acc typeconst_name
)
1364 (Class.consts
ancestor)
1366 ~f
:(fun acc (const_name, _
) -> add_const_impl acc const_name)
1370 HashSet.fold
env.dependencies ~
init:acc ~f
:(fun dep acc ->
1372 | SMethod
(class_name
, method_name
)
1373 when String.equal class_name ancestor_name
->
1374 add_smethod_impl acc method_name
1375 | Method
(class_name
, method_name
)
1376 when String.equal class_name ancestor_name
->
1377 add_method_impl acc method_name
1378 | Const
(class_name
, name)
1379 when String.equal class_name ancestor_name
->
1380 if Option.is_some
(Class.get_typeconst
ancestor name) then
1381 add_typeconst_impl acc name
1382 else if Option.is_some
(Class.get_const
ancestor name) then
1383 add_const_impl acc name
1389 List.fold ~
init:[] ~f
:add_impls (Class.all_ancestor_names
cls)
1392 List.fold ~
init:result ~f
:add_impls (Class.all_ancestor_req_names
cls)
1396 let rec add_implementation_dependencies ctx
env =
1397 let open Typing_deps.Dep
in
1398 let size = HashSet.length
env.dependencies
in
1399 HashSet.fold
env.dependencies ~
init:[] ~f
:(fun dep acc ->
1401 | Class cls_name
-> cls_name
:: acc
1403 |> List.concat_map ~f
:(get_implementation_dependencies ctx
env)
1404 |> List.iter ~f
:(do_add_dep ctx
env);
1405 if HashSet.length
env.dependencies
<> size then
1406 add_implementation_dependencies ctx
env
1408 let get_dependency_origin ctx
cls (dep : 'a
Typing_deps.Dep.variant
) =
1411 let description = variant_to_string
dep in
1412 let cls = value_or_not_found description @@ get_class ctx
cls in
1415 let p = value_or_not_found description @@ Class.get_prop
cls name in
1417 | SProp
(_
, name) ->
1418 let sp = value_or_not_found description @@ Class.get_sprop
cls name in
1420 | Method
(_
, name) ->
1421 let m = value_or_not_found description @@ Class.get_method
cls name in
1423 | SMethod
(_
, name) ->
1424 let sm = value_or_not_found description @@ Class.get_smethod
cls name in
1426 | Const
(_
, name) ->
1427 let c = value_or_not_found description @@ Class.get_const
cls name in
1430 | _
-> raise UnexpectedDependency
))
1432 let collect_dependencies ctx target
=
1433 let filename = Target.get_filename ctx target
in
1436 dependencies
= HashSet.create
();
1437 depends_on_make_default
= ref false;
1438 depends_on_any
= ref false;
1442 (root
: Typing_deps.Dep.dependent
Typing_deps.Dep.variant
)
1443 (obj
: Typing_deps.Dep.dependency
Typing_deps.Dep.variant
) : unit =
1444 if Dep.is_relevant_dependency target root
then do_add_dep ctx
env obj
1446 Typing_deps.add_dependency_callback
"add_dependency" add_dependency;
1447 (* Collect dependencies through side effects of typechecking and remove
1448 * the target function/method from the set of dependencies to avoid
1449 * declaring it twice.
1455 let (_
: (Tast.def
* Typing_inference_env.t_global_with_pos
) option) =
1456 Typing_check_service.type_fun ctx
filename func
1458 add_implementation_dependencies ctx
env;
1459 HashSet.remove
env.dependencies
(Fun
func);
1460 HashSet.remove
env.dependencies
(FunName
func)
1461 | Method
(cls, m) ->
1463 : (Tast.def
* Typing_inference_env.t_global_with_pos list
) option)
1465 Typing_check_service.type_class ctx
filename cls
1467 HashSet.add
env.dependencies
(Method
(cls, m));
1468 HashSet.add
env.dependencies
(SMethod
(cls, m));
1469 add_implementation_dependencies ctx
env;
1470 HashSet.remove
env.dependencies
(Method
(cls, m));
1471 HashSet.remove
env.dependencies
(SMethod
(cls, m)))
1475 let group_class_dependencies_by_class ctx dependencies
=
1482 if SMap.mem
cls acc then
1486 | AllMembers _
-> acc
1489 let cls = value_exn UnexpectedDependency
(Dep.get_class_name obj
) in
1490 let origin = get_dependency_origin ctx
cls obj
in
1491 (* Consider the following example:
1494 * public static function do(): void {}
1496 * class Derived extends Base {}
1497 * function f(): void {
1501 * We will pull both SMethod(Base, do) and SMethod(Derived, do) as
1502 * dependencies, but we should not generate method do() in Derived.
1503 * Therefore, we should ignore dependencies whose origin differs
1506 if String.equal
origin cls then
1507 SMap.add
cls [obj
] acc ~combine
:(fun x y
-> y
@ x
)
1512 (* Every namespace can contain declarations of classes, functions, constants
1513 as well as nested namespaces *)
1514 type hack_namespace
= {
1515 namespaces
: (string, hack_namespace
) Caml.Hashtbl.t
;
1516 decls
: string HashSet.t
;
1519 let subnamespace index
name =
1520 let (nspaces
, _
) = String.rsplit2_exn ~on
:'
\\'
name in
1521 if String.equal nspaces
"" then
1524 let nspaces = String.strip ~drop
:(fun c -> Char.equal
c '
\\'
) nspaces in
1525 let nspaces = String.split ~on
:'
\\'
nspaces in
1526 List.nth
nspaces index
1528 (* Build the recursive hack_namespace data structure for given declarations *)
1529 let sort_by_namespace declarations
=
1530 let rec add_decl nspace
decl index
=
1531 match subnamespace index
decl with
1533 ( if Option.is_none
(Caml.Hashtbl.find_opt nspace
.namespaces
name) then
1534 let nested = Caml.Hashtbl.create
0 in
1535 let declarations = HashSet.create
() in
1539 { namespaces
= nested; decls
= declarations } );
1540 add_decl (Caml.Hashtbl.find nspace
.namespaces
name) decl (index
+ 1)
1541 | None
-> HashSet.add nspace
.decls
decl
1544 { namespaces = Caml.Hashtbl.create
0; decls
= HashSet.create
() }
1546 List.iter
declarations ~f
:(fun decl -> add_decl namespaces decl 0);
1549 (* Takes declarations of Hack classes, functions, constants (map name -> code)
1550 and produces file(s) with Hack code:
1551 1) Groups declarations by namespaces, creating hack_namespace data structure
1552 2) Recursively prints the code in every namespace.
1553 Special case: since Hack files cannot contain both namespaces and toplevel
1554 declarations, we "generate" a separate file for toplevel declarations, using
1555 hh_single_type_check multifile syntax.
1558 ~depends_on_make_default
1561 partial_declarations
=
1562 let get_code declarations =
1563 let decl_names = SMap.keys
declarations in
1564 let global_namespace = sort_by_namespace decl_names in
1565 let code_from_namespace_decls name acc =
1566 Option.value (SMap.find_opt
name declarations) ~
default:[] @ acc
1569 HashSet.fold
global_namespace.decls ~
init:[] ~f
:code_from_namespace_decls
1571 let rec code_from_namespace nspace_name nspace_content code
=
1572 let code = "}" :: code in
1574 Caml.Hashtbl.fold
code_from_namespace nspace_content
.namespaces code
1578 nspace_content
.decls
1580 ~f
:code_from_namespace_decls
1582 Printf.sprintf
"namespace %s {" nspace_name
:: code
1585 Caml.Hashtbl.fold
code_from_namespace global_namespace.namespaces []
1587 (toplevel, namespaces)
1589 let (strict_toplevel
, strict_namespaces
) = get_code strict_declarations
in
1590 let (partial_toplevel
, partial_namespaces
) = get_code partial_declarations
in
1592 ( if depends_on_make_default
then
1595 "<<__Rx>> function %s(): nothing {throw new \\Exception();}"
1596 Pretty.function_make_default;
1601 if depends_on_any
then
1603 "/* HH_FIXME[4101] */";
1606 Pretty.extract_standalone_any
1607 Pretty.extract_standalone_any;
1608 Printf.sprintf
"type %s_<T> = T;" Pretty.extract_standalone_any;
1613 let strict_hh_prefix = "<?hh" in
1614 let partial_hh_prefix = "<?hh // partial" in
1617 ("//// strict_toplevel.php", (strict_hh_prefix, strict_toplevel
@ helpers));
1618 ("//// partial_toplevel.php", (partial_hh_prefix, partial_toplevel
));
1619 ("//// strict_namespaces.php", (strict_hh_prefix, strict_namespaces
));
1620 ("//// partial_namespaces.php", (partial_hh_prefix, partial_namespaces
));
1623 let non_empty_sections =
1624 List.filter
sections ~f
:(fun (_
, (_
, decls
)) -> not
(List.is_empty decls
))
1626 let format_section (prefix
, decls
) =
1627 prefix ^
"\n" ^
Pretty.format (String.concat ~sep
:"\n" decls
)
1629 match non_empty_sections with
1630 | [(_
, section
)] -> format_section section
1634 ~f
:(fun (comment
, section
) -> comment ^
"\n" ^
format_section section
)
1637 let get_declarations ctx target class_dependencies global_dependencies
=
1638 let (strict_class_dependencies
, partial_class_dependencies
) =
1639 SMap.partition
(fun cls _
-> Dep.is_strict_class ctx
cls) class_dependencies
1641 let (strict_global_dependencies
, partial_global_dependencies
) =
1642 List.partition_tf global_dependencies ~f
:(Dep.is_strict_dep ctx
)
1644 let add_declaration declarations name declaration
=
1645 SMap.add
name [declaration
] declarations ~combine
:(fun x y
-> y
@ x
)
1647 let add_global_declaration declarations dep =
1650 (Dep.global_dep_name dep)
1651 (Pretty.get_global_object_declaration ctx
dep)
1653 let add_class_declaration cls fields
declarations =
1657 (Pretty.construct_type_declaration ctx
cls target fields
)
1659 let strict_declarations =
1661 strict_global_dependencies
1662 ~f
:add_global_declaration
1664 |> SMap.fold
add_class_declaration strict_class_dependencies
1666 let partial_declarations =
1668 partial_global_dependencies
1669 ~f
:add_global_declaration
1671 |> SMap.fold
add_class_declaration partial_class_dependencies
1675 let decl = Target.extract_target ctx target
in
1676 if Dep.is_strict_fun ctx
name then
1677 (add_declaration strict_declarations name decl, partial_declarations)
1679 (strict_declarations, add_declaration partial_declarations name decl)
1680 | Method _
-> (strict_declarations, partial_declarations)
1684 let env = collect_dependencies ctx target
in
1685 let dependencies = HashSet.fold
env.dependencies ~
init:[] ~f
:List.cons
in
1686 let (class_dependencies
, global_dependencies
) =
1687 List.partition_tf
dependencies ~f
:(fun dep ->
1688 Option.is_some
(Dep.get_class_name dep))
1690 let (strict_declarations, partial_declarations) =
1694 (group_class_dependencies_by_class ctx class_dependencies
)
1698 ~depends_on_make_default
:!(env.depends_on_make_default
)
1699 ~depends_on_any
:!(env.depends_on_any
)
1701 partial_declarations
1703 | DependencyNotFound d
-> Printf.sprintf
"Dependency not found: %s" d
1705 | UnexpectedDependency
->
1706 Printexc.get_backtrace
()