Skip to content

Commit 9ec3de9

Browse files
mheibermeta-codesync[bot]
authored andcommitted
Convert needs concrete override check to warning (was an error)
Summary: For ease of adoption, we're starting with using warnings instead of errors for `__NeedsConcrete` checking. This change is particularly important because without it it's hard to migrate the code base gradually, as we'd have to do whole hierarchies in one go. ## Examples Here are what the warnings look like with pretty-printing and context. These examples are also used in the test suite. {F1982437894} We take care to include the method name for methods defined in traits: {F1982437893} context in fb-only link below Reviewed By: geralt-encore Differential Revision: D83658029 fbshipit-source-id: 2a824c63649433da866be5debbf80922fe517047
1 parent 58858c0 commit 9ec3de9

13 files changed

+131
-53
lines changed

hphp/hack/src/errors/error_codes.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ module Typing = struct
776776
| OptionalParameterNotSupported [@value 4484]
777777
| InvalidRecursiveType [@value 4485]
778778
| StaticCallOnTraitRequireThisAs [@value 4486]
779-
| NeedsConcreteOverride [@value 4487]
780779
| StringToClassPointer [@value 4488]
781780
| SwitchNeedsDefault [@value 4489]
782781
| SimpliHackRunPrompt [@value 4493]
@@ -818,6 +817,7 @@ module Warning = struct
818817
| CallNeedsConcrete [@value 12024]
819818
| AbstractAccessViaStatic [@value 12025]
820819
| UninstantiableClassViaStatic [@value 12026]
820+
| NeedsConcreteOverride [@value 12027]
821821
[@@deriving enum, ord, show { with_path = false }]
822822
end
823823

hphp/hack/src/oxidized/gen/error_codes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// This source code is licensed under the MIT license found in the
44
// LICENSE file in the "hack" directory of this source tree.
55
//
6-
// @generated SignedSource<<64a9c66d309a4a2bfcd0a768ac221b26>>
6+
// @generated SignedSource<<2abf2d7b419a65fae9c5b1d22e059609>>
77
//
88
// To regenerate this file, run:
99
// buck run @fbcode//mode/dev-nosan-lg fbcode//hphp/hack/src:oxidized_regen
@@ -598,7 +598,6 @@ pub enum Typing {
598598
OptionalParameterNotSupported = 4484,
599599
InvalidRecursiveType = 4485,
600600
StaticCallOnTraitRequireThisAs = 4486,
601-
NeedsConcreteOverride = 4487,
602601
StringToClassPointer = 4488,
603602
SwitchNeedsDefault = 4489,
604603
SimpliHackRunPrompt = 4493,
@@ -656,6 +655,7 @@ pub enum Warning {
656655
CallNeedsConcrete = 12024,
657656
AbstractAccessViaStatic = 12025,
658657
UninstantiableClassViaStatic = 12026,
658+
NeedsConcreteOverride = 12027,
659659
}
660660
impl TrivialDrop for Warning {}
661661
arena_deserializer::impl_deserialize_in_arena!(Warning);

hphp/hack/src/typing/typing_error.ml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,10 +1889,6 @@ and Secondary : sig
18891889
decl_pos: Pos_or_decl.t;
18901890
most_similar: (string * Pos_or_decl.t) option;
18911891
}
1892-
| Needs_concrete_override of {
1893-
pos: Pos_or_decl.t;
1894-
parent_pos: Pos_or_decl.t;
1895-
}
18961892
| Higher_rank_tparam_escape of {
18971893
tvar_pos: Pos_or_decl.t;
18981894
pos_with_generic: Pos_or_decl.t;
@@ -2212,10 +2208,6 @@ end = struct
22122208
decl_pos: Pos_or_decl.t;
22132209
most_similar: (string * Pos_or_decl.t) option;
22142210
}
2215-
| Needs_concrete_override of {
2216-
pos: Pos_or_decl.t;
2217-
parent_pos: Pos_or_decl.t;
2218-
}
22192211
| Higher_rank_tparam_escape of {
22202212
tvar_pos: Pos_or_decl.t;
22212213
pos_with_generic: Pos_or_decl.t;

hphp/hack/src/typing/typing_error.mli

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,10 +1728,6 @@ and Secondary : sig
17281728
decl_pos: Pos_or_decl.t;
17291729
most_similar: (string * Pos_or_decl.t) option;
17301730
}
1731-
| Needs_concrete_override of {
1732-
pos: Pos_or_decl.t;
1733-
parent_pos: Pos_or_decl.t;
1734-
}
17351731
| Higher_rank_tparam_escape of {
17361732
tvar_pos: Pos_or_decl.t;
17371733
pos_with_generic: Pos_or_decl.t;

hphp/hack/src/typing/typing_error_utils.ml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6593,18 +6593,6 @@ end = struct
65936593
in
65946594
create ~code:Error_code.EnumClassLabelUnknown ~reasons ()
65956595

6596-
let needs_concrete_override pos parent_pos =
6597-
let reasons =
6598-
lazy
6599-
[
6600-
( pos,
6601-
"Cannot declare this method as __NeedsConcrete, since it overrides a non-__NeedsConcrete method"
6602-
);
6603-
(parent_pos, "Previously defined here");
6604-
]
6605-
in
6606-
create ~code:Error_code.NeedsConcreteOverride ~reasons ()
6607-
66086596
let higher_rank_tparam_escape
66096597
tvar_pos pos_with_generic generic_reason generic_name =
66106598
let reasons =
@@ -6841,8 +6829,6 @@ end = struct
68416829
Eval_result.single (violated_refinement_constraint cstr)
68426830
| Unknown_label { enum_name; decl_pos; most_similar } ->
68436831
Eval_result.single (label_unknown enum_name decl_pos most_similar)
6844-
| Needs_concrete_override { pos; parent_pos } ->
6845-
Eval_result.single (needs_concrete_override pos parent_pos)
68466832
| Higher_rank_tparam_escape
68476833
{ tvar_pos; pos_with_generic; generic_reason; generic_name } ->
68486834
Eval_result.single

hphp/hack/src/typing/typing_extends.ml

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -737,22 +737,36 @@ let check_abstract_overrides_concrete
737737
`property);
738738
})
739739

740-
let check_needs_concrete_override env parent_class_elt class_elt on_error =
740+
(** Why this check: It would be unsound for a class that needs concrete (`<<__NeedsConcrete>>`) to override one that does not,
741+
* since a __NeedsConcrete method imposes stricter requirements on its input (`static` must point to a concrete class) *)
742+
let check_needs_concrete_override
743+
env ~parent_class_elt class_ class_elt ~class_pos ~member_name =
741744
if
742745
(not (get_ce_readonly_prop_or_needs_concrete parent_class_elt))
743746
&& get_ce_readonly_prop_or_needs_concrete class_elt
744747
then
745-
(* It would be unsound for a class that needs concrete (`<<__NeedsConcrete>>`) to override one that does not, because
746-
* then `<<__NeedsConcrete>>` methods have stricter requirements (`static` must point to a concrete class). *)
747-
Typing_error_utils.add_typing_error
748-
~env
749-
Typing_error.(
750-
apply_reasons ~on_error
751-
@@ Secondary.Needs_concrete_override
752-
{
753-
pos = Lazy.force class_elt.ce_pos;
754-
parent_pos = Lazy.force parent_class_elt.ce_pos;
755-
})
748+
let defined_in_same_class =
749+
String.equal class_elt.ce_origin (Cls.name class_)
750+
in
751+
let (error_pos, method_name_for_method_defined_outside_class) =
752+
match
753+
Pos_or_decl.get_raw_pos_or_decl_reference (Lazy.force class_elt.ce_pos)
754+
with
755+
| `Raw pos when defined_in_same_class -> (pos, None)
756+
| `Raw _
757+
| `Decl_ref _ ->
758+
(class_pos, Some member_name)
759+
in
760+
Typing_warning_utils.add
761+
env
762+
( error_pos,
763+
Typing_warning.Needs_concrete_override,
764+
{
765+
Typing_warning.Needs_concrete_override.pos =
766+
Lazy.force class_elt.ce_pos;
767+
method_name_for_method_defined_outside_class;
768+
parent_pos = Lazy.force parent_class_elt.ce_pos;
769+
} )
756770

757771
let detect_multiple_concrete_defs
758772
(class_elt, class_) (parent_class_elt, parent_class) =
@@ -1002,6 +1016,7 @@ let check_override
10021016
~parent_class
10031017
~(parent_type : decl_ty)
10041018
~parent_pos
1019+
~class_pos
10051020
~(class_elt : class_elt)
10061021
~parent_class_elt
10071022
on_error =
@@ -1059,7 +1074,13 @@ let check_override
10591074
parent_class_elt
10601075
class_elt
10611076
on_error;
1062-
check_needs_concrete_override env parent_class_elt class_elt on_error;
1077+
check_needs_concrete_override
1078+
env
1079+
~parent_class_elt
1080+
class_
1081+
class_elt
1082+
~class_pos
1083+
~member_name;
10631084

10641085
let (lazy pos) = class_elt.ce_pos in
10651086

@@ -1582,6 +1603,7 @@ let check_class_against_parent_class_elt
15821603
~parent_type
15831604
~parent_pos
15841605
~class_elt
1606+
~class_pos
15851607
(on_error (parent_pos, Cls.name parent_class)) )
15861608
| None ->
15871609
(* The only case when a member belongs to a parent but not the child is if the parent is an
@@ -1770,7 +1792,13 @@ let default_constructor_ce class_ =
17701792

17711793
(* When an interface defines a constructor, we check that they are compatible *)
17721794
let check_constructors
1773-
~parent_pos env (parent_class, parent_type) class_ psubst on_error =
1795+
~parent_pos
1796+
env
1797+
(parent_class, parent_type)
1798+
class_
1799+
~class_pos
1800+
psubst
1801+
on_error =
17741802
let parent_is_interface = Ast_defs.is_c_interface (Cls.kind parent_class) in
17751803
let parent_is_consistent =
17761804
constructor_is_consistent (snd (Cls.construct parent_class))
@@ -1808,6 +1836,7 @@ let check_constructors
18081836
~parent_type
18091837
~parent_pos
18101838
~parent_class_elt:parent_cstr
1839+
~class_pos
18111840
~class_elt:cstr
18121841
on_error
18131842
else
@@ -2166,6 +2195,7 @@ let check_class_extends_parent_constructors
21662195
env
21672196
(parent_class : (Pos.t * string) * decl_ty * decl_ty list * Cls.t)
21682197
class_
2198+
~class_pos
21692199
on_error =
21702200
let (_, parent_type, parent_tparaml, parent_class) = parent_class in
21712201
let psubst = Inst.make_subst (Cls.tparams parent_class) parent_tparaml in
@@ -2175,6 +2205,7 @@ let check_class_extends_parent_constructors
21752205
env
21762206
(parent_class, parent_type)
21772207
class_
2208+
~class_pos
21782209
psubst
21792210
on_error
21802211
in
@@ -2935,6 +2966,7 @@ let check_implements_extends_uses
29352966
~parent_pos:(fst parent_name)
29362967
env
29372968
parent
2969+
~class_pos:name_pos
29382970
class_
29392971
(on_error parent_name))
29402972
in

hphp/hack/src/typing/typing_warning.ml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,21 @@ module Uninstantiable_class_via_static = struct
200200
}
201201
end
202202

203+
module Needs_concrete_override = struct
204+
type t = {
205+
pos: Pos_or_decl.t;
206+
parent_pos: Pos_or_decl.t;
207+
method_name_for_method_defined_outside_class: string option;
208+
(** `Some m` iff `m` is a trait method of a trait that is used by the class.
209+
* In such cases, the location for the warning will be a class name, so we need
210+
* to preserve the method name to give a meaningful error message.
211+
* Example: `class Child extends Parent { use Tr; }`
212+
* ~~~~~
213+
* where trait `Tr` defines the method with the incorrect override of `Parent::m`
214+
*)
215+
}
216+
end
217+
203218
type (_, _) kind =
204219
| Sketchy_equality : (Sketchy_equality.t, warn) kind
205220
| Is_as_always : (Is_as_always.t, migrated) kind
@@ -220,5 +235,6 @@ type (_, _) kind =
220235
| Abstract_access_via_static : (Abstract_access_via_static.t, warn) kind
221236
| Uninstantiable_class_via_static
222237
: (Uninstantiable_class_via_static.t, warn) kind
238+
| Needs_concrete_override : (Needs_concrete_override.t, warn) kind
223239

224240
type ('x, 'a) t = Pos.t * ('x, 'a) kind * 'x

hphp/hack/src/typing/typing_warning_utils.ml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,45 @@ module Uninstantiable_class_via_static = struct
707707
let quickfixes _ = []
708708
end
709709

710+
module Needs_concrete_override = struct
711+
type t = Typing_warning.Needs_concrete_override.t
712+
713+
let code = Codes.NeedsConcreteOverride
714+
715+
let codes = [code]
716+
717+
let code _ = code
718+
719+
let claim
720+
{
721+
Typing_warning.Needs_concrete_override
722+
.method_name_for_method_defined_outside_class;
723+
_;
724+
} =
725+
let method_text =
726+
match method_name_for_method_defined_outside_class with
727+
| Some method_name -> Printf.sprintf "Method `%s`" method_name
728+
| None -> "This method"
729+
in
730+
method_text
731+
^ " is declared as `__NeedsConcrete` but overrides a non-`__NeedsConcrete` method. Please add `__NeedsConcrete` to the overridden method or remove it from the current method. (The `__NeedsConcrete` attribute indicates that a method requires `static` to point to a concrete class)"
732+
733+
let reasons
734+
{
735+
Typing_warning.Needs_concrete_override.pos;
736+
parent_pos;
737+
method_name_for_method_defined_outside_class = _;
738+
} =
739+
[
740+
( pos,
741+
"It is unsafe to declare this method as `__NeedsConcrete`, since it overrides a non-`__NeedsConcrete` method"
742+
);
743+
(parent_pos, "Previously defined here");
744+
]
745+
746+
let quickfixes _ = []
747+
end
748+
710749
let module_of (type a x) (kind : (x, a) Typing_warning.kind) :
711750
(module Warning with type t = x) =
712751
match kind with
@@ -730,6 +769,7 @@ let module_of (type a x) (kind : (x, a) Typing_warning.kind) :
730769
(module Abstract_access_via_static)
731770
| Typing_warning.Uninstantiable_class_via_static ->
732771
(module Uninstantiable_class_via_static)
772+
| Typing_warning.Needs_concrete_override -> (module Needs_concrete_override)
733773

734774
let module_of_migrated
735775
(type x) (kind : (x, Typing_warning.migrated) Typing_warning.kind) :
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?hh
2+
3+
class Base {
4+
public function foo(): void {}
5+
}
6+
7+
class Child extends Base {
8+
<<__NeedsConcrete>>
9+
public function foo(): void {}
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
WARN: File "needs_concrete_override_1.php", line 9, characters 19-21:
2+
This method is declared as `__NeedsConcrete` but overrides a non-`__NeedsConcrete` method. Please add `__NeedsConcrete` to the overridden method or remove it from the current method. (The `__NeedsConcrete` attribute indicates that a method requires `static` to point to a concrete class) (Warn[12027])
3+
File "needs_concrete_override_1.php", line 9, characters 19-21:
4+
It is unsafe to declare this method as `__NeedsConcrete`, since it overrides a non-`__NeedsConcrete` method
5+
File "needs_concrete_override_1.php", line 4, characters 19-21:
6+
Previously defined here

0 commit comments

Comments
 (0)