2 * Copyright (c) 2019, 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.
12 open Reordered_argument_collections
15 let class_names_from_deps ~ctx ~get_classes_in_file deps
=
16 let filenames = Naming_provider.get_files ctx deps
in
17 Relative_path.Set.fold
filenames ~init
:SSet.empty ~f
:(fun file acc
->
18 SSet.fold
(get_classes_in_file file
) ~init
:acc ~f
:(fun cid acc
->
19 if DepSet.mem deps
Dep.(make
(Type cid
)) then
24 let add_minor_change_fanout
25 ~
(ctx
: Provider_context.t
)
26 (acc
: AffectedDeps.t
)
28 (minor_change
: ClassDiff.minor_change
) : AffectedDeps.t
=
29 let mode = Provider_context.get_deps_mode ctx
in
30 let dep = Dep.make
(Dep.Type class_name
) in
31 let changed = DepSet.singleton
dep in
32 let acc = AffectedDeps.mark_changed
acc changed in
33 let acc = AffectedDeps.mark_as_needing_recheck
acc changed in
35 mro_positions_changed
;
37 { consts
; typeconsts
; props
; sprops
; methods
; smethods
; constructor
};
41 let changed_and_descendants =
42 lazy (Typing_deps.add_extend_deps
mode changed)
45 (* If positions affecting the linearization have changed, we need to update
46 positions in the linearization of this class and all its descendants.
47 We mark those linearizations as invalidated here. We don't need to
48 recheck the fanout of the invalidated classes--since only positions
49 changed, there will be no change in the fanout except in the positions
50 in error messages, and we recheck all files with errors anyway. *)
51 if mro_positions_changed
then
52 let changed_and_descendants = Lazy.force
changed_and_descendants in
53 AffectedDeps.mark_mro_invalidated
acc changed_and_descendants
57 (* Recheck any files with a reference to the member returned by make_dep,
58 even if the member was overridden in a subclass. This deals with the case
59 where adding, removing, or changing the abstract-ness of a member causes
60 some subclass which inherits a member of that name from multiple parents
61 to resolve the conflict in a different way than it did previously. *)
62 let recheck_descendants_and_their_member_dependents acc make_dep
=
63 let changed_and_descendants = Lazy.force
changed_and_descendants in
64 DepSet.fold
changed_and_descendants ~init
:acc ~f
:(fun dep acc ->
66 AffectedDeps.mark_as_needing_recheck
acc (DepSet.singleton
dep)
68 AffectedDeps.mark_all_dependents_as_needing_recheck_from_hash
73 let add_member_fanout ~is_const
acc change make_dep
=
74 (* Consts and typeconsts have their types copied into descendant classes in
75 folded decl (rather than being stored in a separate heap as methods and
76 properties are). As a result, when using a const, we register a
77 dependency only upon the class type at the use site. In contrast, if we
78 use a method or property B::f which was inherited from A, we register a
79 dependency both upon B::f and A::f, which is what allows us to avoid the
80 more expensive use of recheck_descendants_and_their_member_dependents
81 here. Since we don't register a dependency upon the const at its class of
82 origin in this way, we must always take the more expensive path for
85 is_const
|| ClassDiff.method_or_property_change_affects_descendants change
87 recheck_descendants_and_their_member_dependents acc make_dep
89 AffectedDeps.mark_all_dependents_as_needing_recheck_from_hash
92 (make_dep
(Typing_deps.Dep.make
(Typing_deps.Dep.Type class_name
)))
94 let add_member_fanouts ~is_const
acc changes make_dep
=
95 SMap.fold changes ~init
:acc ~f
:(fun member_id change
acc ->
96 add_member_fanout ~is_const
acc change
(make_dep member_id
))
99 SMap.fold consts ~init
:acc ~f
:(fun name change
acc ->
101 (* If a const has been added or removed in an enum type, we must recheck
102 all switch statements which need to have a case for each variant
103 (exhaustive switch statements add an AllMembers dependency).
104 We don't bother to test whether the class is an enum type because
105 non-enum classes will have no AllMembers dependents anyway. *)
109 AffectedDeps.mark_all_dependents_as_needing_recheck
112 (Dep.AllMembers class_name
)
115 add_member_fanout ~is_const
:true acc change
(fun type_hash
->
116 Typing_deps.Dep.make_dep_with_type_hash type_hash name
Dep.KConst
))
119 add_member_fanouts ~is_const
:true acc typeconsts
(fun mid type_hash
->
120 Typing_deps.Dep.make_dep_with_type_hash type_hash mid
Dep.KConst
)
123 add_member_fanouts ~is_const
:false acc props
(fun mid type_hash
->
124 Typing_deps.Dep.make_dep_with_type_hash type_hash mid
Dep.KProp
)
127 add_member_fanouts ~is_const
:false acc sprops
(fun mid type_hash
->
128 Typing_deps.Dep.make_dep_with_type_hash type_hash mid
Dep.KSProp
)
131 add_member_fanouts ~is_const
:false acc methods
(fun mid type_hash
->
132 Typing_deps.Dep.make_dep_with_type_hash type_hash mid
Dep.KMethod
)
135 add_member_fanouts ~is_const
:false acc smethods
(fun mid type_hash
->
136 Typing_deps.Dep.make_dep_with_type_hash type_hash mid
Dep.KSMethod
)
139 Option.value_map constructor ~default
:acc ~f
:(fun change
->
140 add_member_fanout ~is_const
:false acc change
(fun type_hash
->
145 let add_maximum_fanout
146 (ctx
: Provider_context.t
) (acc : AffectedDeps.t
) (class_name
: string) =
147 let mode = Provider_context.get_deps_mode ctx
in
148 AffectedDeps.add_maximum_fanout mode acc (Dep.make
(Dep.Type class_name
))
151 ~
(ctx
: Provider_context.t
) (acc : AffectedDeps.t
) (class_name
, diff
) :
155 | Major_change
-> add_maximum_fanout ctx
acc class_name
156 | Minor_change minor_change
->
157 add_minor_change_fanout ~ctx
acc class_name minor_change
159 let fanout_of_changes
160 ~
(ctx
: Provider_context.t
) (changes
: (string * ClassDiff.t
) list
) :
162 List.fold changes ~init
:(AffectedDeps.empty
()) ~f
:(add_fanout ~ctx
)