Remove typechecker-only options from GlobalOptions to avoid code duplication
[hiphop-php.git] / hphp / hack / src / decl / shallow_class_fanout.ml
blob3fd6765e088d9df31c033a2a4c9bfa5912872f25
1 (**
2 * Copyright (c) 2019, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Hh_prelude
11 open ClassDiff
12 open Reordered_argument_collections
13 open Typing_deps
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
20 SSet.add acc cid
21 else
22 acc))
24 let get_minor_change_fanout
25 ~(ctx : Provider_context.t)
26 (class_name : string)
27 (minor_change : ClassDiff.minor_change) : AffectedDeps.t =
28 let mode = Provider_context.get_deps_mode ctx in
29 let changed = DepSet.singleton (Dep.make (Dep.Type class_name)) in
30 let acc = AffectedDeps.empty () in
31 let acc = AffectedDeps.mark_changed acc changed in
32 let acc = AffectedDeps.mark_as_needing_recheck acc changed in
33 let {
34 mro_positions_changed;
35 member_diff =
36 { consts; typeconsts; props; sprops; methods; smethods; constructor };
37 } =
38 minor_change
40 let changed_and_descendants =
41 lazy (Typing_deps.add_extend_deps mode changed)
43 let acc =
44 (* If positions affecting the linearization have changed, we need to update
45 positions in the linearization of this class and all its descendants.
46 We mark those linearizations as invalidated here. We don't need to
47 recheck the fanout of the invalidated classes--since only positions
48 changed, there will be no change in the fanout except in the positions
49 in error messages, and we recheck all files with errors anyway. *)
50 if mro_positions_changed then
51 let changed_and_descendants = Lazy.force changed_and_descendants in
52 AffectedDeps.mark_mro_invalidated acc changed_and_descendants
53 else
54 acc
56 (* Recheck any file with a dependency on the provided member
57 in the changed class and in each of its descendants,
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 member =
63 let changed_and_descendants = Lazy.force changed_and_descendants in
64 DepSet.fold changed_and_descendants ~init:acc ~f:(fun dep acc ->
65 let acc =
66 AffectedDeps.mark_as_needing_recheck acc (DepSet.singleton dep)
68 AffectedDeps.mark_all_dependents_as_needing_recheck_from_hash
69 mode
70 acc
71 (Typing_deps.Dep.make_member_dep_from_type_dep dep member))
73 let add_member_fanout ~is_const member change acc =
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
83 consts. *)
85 is_const || ClassDiff.method_or_property_change_affects_descendants change
86 then
87 recheck_descendants_and_their_member_dependents acc member
88 else
89 AffectedDeps.mark_all_dependents_as_needing_recheck_from_hash
90 mode
91 acc
92 (Typing_deps.Dep.make_member_dep_from_type_dep
93 (Typing_deps.Dep.make (Typing_deps.Dep.Type class_name))
94 member)
96 let add_member_fanouts ~is_const changes make_member acc =
97 SMap.fold changes ~init:acc ~f:(fun name ->
98 add_member_fanout ~is_const (make_member name))
100 let acc =
101 SMap.fold consts ~init:acc ~f:(fun name change acc ->
102 let acc =
103 (* If a const has been added or removed in an enum type, we must recheck
104 all switch statements which need to have a case for each variant
105 (exhaustive switch statements add an AllMembers dependency).
106 We don't bother to test whether the class is an enum type because
107 non-enum classes will have no AllMembers dependents anyway. *)
108 match change with
109 | Added
110 | Removed ->
111 recheck_descendants_and_their_member_dependents acc Dep.Member.all
112 | _ -> acc
114 add_member_fanout ~is_const:true (Dep.Member.const name) change acc)
116 let acc =
118 |> add_member_fanouts ~is_const:true typeconsts Dep.Member.const
119 |> add_member_fanouts ~is_const:false props Dep.Member.prop
120 |> add_member_fanouts ~is_const:false sprops Dep.Member.sprop
121 |> add_member_fanouts ~is_const:false methods Dep.Member.method_
122 |> add_member_fanouts ~is_const:false smethods Dep.Member.smethod
124 let acc =
125 Option.value_map constructor ~default:acc ~f:(fun change ->
126 add_member_fanout ~is_const:false Dep.Member.constructor change acc)
130 let get_maximum_fanout (ctx : Provider_context.t) (class_name : string) =
131 let mode = Provider_context.get_deps_mode ctx in
132 AffectedDeps.get_maximum_fanout mode (Dep.make (Dep.Type class_name))
134 let get_fanout ~(ctx : Provider_context.t) (class_name, diff) : AffectedDeps.t =
135 match diff with
136 | Unchanged -> AffectedDeps.empty ()
137 | Major_change _major_change -> get_maximum_fanout ctx class_name
138 | Minor_change minor_change ->
139 get_minor_change_fanout ~ctx class_name minor_change
141 let direct_references_cardinal mode class_name : int =
142 Typing_deps.get_ideps mode (Dep.Type class_name) |> DepSet.cardinal
144 let descendants_cardinal mode class_name : int =
145 (Typing_deps.add_extend_deps
146 mode
147 (DepSet.singleton @@ Dep.make @@ Dep.Type class_name)
148 |> DepSet.cardinal)
151 let children_cardinal mode class_name : int =
152 Typing_deps.get_ideps mode (Dep.Extends class_name) |> DepSet.cardinal
154 let get_fanout_cardinal (fanout : AffectedDeps.t) : int =
155 DepSet.cardinal fanout.AffectedDeps.needs_recheck
157 module Log = struct
158 let do_log ctx ~fanout_cardinal =
159 TypecheckerOptions.log_fanout ~fanout_cardinal
160 @@ Provider_context.get_tcopt ctx
162 let log_class_fanout
164 ((class_name : string), (diff : ClassDiff.t))
165 (fanout : AffectedDeps.t) : unit =
166 let fanout_cardinal = get_fanout_cardinal fanout in
167 if do_log ~fanout_cardinal ctx then
168 let mode = Provider_context.get_deps_mode ctx in
169 HackEventLogger.Fanouts.log_class
170 ~class_name
171 ~class_diff:(ClassDiff.show diff)
172 ~fanout_cardinal
173 ~class_diff_category:(ClassDiff.to_category_json diff)
174 ~direct_references_cardinal:(direct_references_cardinal mode class_name)
175 ~descendants_cardinal:(descendants_cardinal mode class_name)
176 ~children_cardinal:(children_cardinal mode class_name)
178 let log_fanout
180 (changes : _ list)
181 (fanout : AffectedDeps.t)
182 ~max_class_fanout_cardinal : unit =
183 let fanout_cardinal = get_fanout_cardinal fanout in
184 if do_log ~fanout_cardinal ctx then
185 HackEventLogger.Fanouts.log
186 ~changes_cardinal:(List.length changes)
187 ~max_class_fanout_cardinal
188 ~fanout_cardinal
191 let add_fanout ~ctx (fanout_acc, max_class_fanout_cardinal) diff =
192 let fanout = get_fanout ~ctx diff in
193 Log.log_class_fanout ctx diff fanout;
194 let fanout_acc = AffectedDeps.union fanout_acc fanout in
195 let max_class_fanout_cardinal =
196 Int.max max_class_fanout_cardinal (get_fanout_cardinal fanout)
198 (fanout_acc, max_class_fanout_cardinal)
200 let fanout_of_changes
201 ~(ctx : Provider_context.t) (changes : (string * ClassDiff.t) list) :
202 AffectedDeps.t =
203 let (fanout, max_class_fanout_cardinal) =
204 List.fold changes ~init:(AffectedDeps.empty (), 0) ~f:(add_fanout ~ctx)
206 Log.log_fanout ctx changes fanout ~max_class_fanout_cardinal;
207 fanout