Support ~ types on XHP attributes that are enum {'x', 'y'}
[hiphop-php.git] / hphp / hack / src / decl / shallow_class_fanout.ml
blobe49aadc7fd9df4a0a4a05b720da94cbdc59a2ad5
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 add_minor_change_fanout
25 ~(ctx : Provider_context.t)
26 (acc : AffectedDeps.t)
27 (class_name : string)
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
34 let {
35 mro_positions_changed;
36 member_diff =
37 { consts; typeconsts; props; sprops; methods; smethods; constructor };
38 } =
39 minor_change
41 let changed_and_descendants =
42 lazy (Typing_deps.add_extend_deps mode changed)
44 let acc =
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
54 else
55 acc
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 ->
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 (make_dep dep))
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
83 consts. *)
85 is_const || ClassDiff.method_or_property_change_affects_descendants change
86 then
87 recheck_descendants_and_their_member_dependents acc make_dep
88 else
89 AffectedDeps.mark_all_dependents_as_needing_recheck_from_hash
90 mode
91 acc
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))
98 let acc =
99 SMap.fold consts ~init:acc ~f:(fun name change acc ->
100 let 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. *)
106 match change with
107 | Added
108 | Removed ->
109 AffectedDeps.mark_all_dependents_as_needing_recheck
110 mode
112 (Dep.AllMembers class_name)
113 | _ -> acc
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))
118 let acc =
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)
122 let acc =
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)
126 let acc =
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)
130 let acc =
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)
134 let acc =
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)
138 let acc =
139 Option.value_map constructor ~default:acc ~f:(fun change ->
140 add_member_fanout ~is_const:false acc change (fun type_hash ->
141 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))
150 let add_fanout
151 ~(ctx : Provider_context.t) (acc : AffectedDeps.t) (class_name, diff) :
152 AffectedDeps.t =
153 match diff with
154 | Unchanged -> acc
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) :
161 AffectedDeps.t =
162 List.fold changes ~init:(AffectedDeps.empty ()) ~f:(add_fanout ~ctx)