Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Make traits, interfaces, and enums unsealable
Reviewed By: DavidSnider

Differential Revision: D8066088

fbshipit-source-id: 58d744839efda2d3692da385d6934600e68c33c8
  • Loading branch information
kmeht authored and hhvm-bot committed May 19, 2018
1 parent 7aee0a7 commit 202e6ab
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 22 deletions.
28 changes: 18 additions & 10 deletions hphp/hack/src/decl/decl.ml
Expand Up @@ -620,16 +620,24 @@ and get_sealed_whitelist env c =
else match Attrs.find SN.UserAttributes.uaSealed c.c_user_attributes with
| None -> None
| Some {ua_params = params; _} ->
if c.c_final then begin
let p, name = c.c_name in Errors.sealed_final p name
end;
let add_class_name names param =
match param with
| _, Class_const ((_, CI cls), (_, name))
when name = SN.Members.mClass ->
SSet.add (get_instantiated_sid_name cls) names
| _ -> names in
Some (List.fold_left params ~f:add_class_name ~init:SSet.empty)
begin match c.c_kind with
| Ast.Cinterface | Ast.Ctrait | Ast.Cenum ->
let pos = fst c.c_name in
let kind = String.capitalize_ascii (Ast.string_of_class_kind c.c_kind) in
Errors.unsealable pos kind;
None
| Ast.Cabstract | Ast.Cnormal ->
if c.c_final then begin
let p, name = c.c_name in Errors.sealed_final p name
end;
let add_class_name names param =
match param with
| _, Class_const ((_, CI cls), (_, name))
when name = SN.Members.mClass ->
SSet.add (get_instantiated_sid_name cls) names
| _ -> names in
Some (List.fold_left params ~f:add_class_name ~init:SSet.empty)
end

and get_implements env ht =
let _r, (_p, c), paraml = Decl_utils.unwrap_class_type ht in
Expand Down
4 changes: 4 additions & 0 deletions hphp/hack/src/errors/errors.ml
Expand Up @@ -1081,6 +1081,7 @@ module Typing = struct
| MutableInNonreactiveContext
| InvalidArgumentOfRxMutableFunction
| LetVarImmutabilityViolation
| Unsealable
(* EXTEND HERE WITH NEW VALUES IF NEEDED *)
[@@ deriving enum, show { with_path = false } ]
let err_code = to_enum
Expand Down Expand Up @@ -2560,6 +2561,9 @@ let sealed_final pos name =
let name = (strip_ns name) in
add (Typing.err_code Typing.SealedFinal) pos ("Sealed class "^name^" cannot be marked final")

let unsealable pos kind =
add (Typing.err_code Typing.Unsealable) pos (kind^" cannot be sealed")

let read_before_write (pos, v) =
add (Typing.err_code Typing.ReadBeforeWrite) pos (
sl[
Expand Down
1 change: 1 addition & 0 deletions hphp/hack/src/errors/errors_sig.ml
Expand Up @@ -219,6 +219,7 @@ module type S = sig
val extend_sealed : Pos.t -> Pos.t -> string -> string -> string -> unit
val trait_implement_sealed : Pos.t -> Pos.t -> string -> unit
val sealed_final : Pos.t -> string -> unit
val unsealable : Pos.t -> string -> unit
val read_before_write : Pos.t * string -> unit
val interface_final : Pos.t -> unit
val trait_final : Pos.t -> unit
Expand Down
1 change: 1 addition & 0 deletions hphp/hack/test/errors/error_map.ml
Expand Up @@ -448,4 +448,5 @@ let%expect_test "error_map" =
MutableInNonreactiveContext = 4242
InvalidArgumentOfRxMutableFunction = 4243
LetVarImmutabilityViolation = 4244
Unsealable = 4245
|}]
8 changes: 8 additions & 0 deletions hphp/hack/test/typecheck/sealed/enum.php
@@ -0,0 +1,8 @@
<?hh // strict

<<__Sealed(B::class)>>
enum A: int {
FOO = 1;
}

abstract class B {}
2 changes: 2 additions & 0 deletions hphp/hack/test/typecheck/sealed/enum.php.exp
@@ -0,0 +1,2 @@
File "enum.php", line 4, characters 6-6:
An enum cannot be sealed (Typing[4245])
4 changes: 1 addition & 3 deletions hphp/hack/test/typecheck/sealed/interface.php.exp
@@ -1,4 +1,2 @@
File "interface.php", line 8, characters 11-11:
You cannot extend sealed interface A (Typing[4238])
File "interface.php", line 5, characters 11-11:
Declaration is here
An interface cannot be sealed (Typing[4245])
4 changes: 1 addition & 3 deletions hphp/hack/test/typecheck/sealed/interface2.php.exp
@@ -1,4 +1,2 @@
File "interface2.php", line 8, characters 13-13:
You cannot implement sealed interface A (Typing[4238])
File "interface2.php", line 5, characters 11-11:
Declaration is here
An interface cannot be sealed (Typing[4245])
4 changes: 1 addition & 3 deletions hphp/hack/test/typecheck/sealed/trait.php.exp
@@ -1,4 +1,2 @@
File "trait.php", line 7, characters 7-7:
A trait cannot implement sealed interface A. Use `require implements` instead (Typing[4238])
File "trait.php", line 5, characters 11-11:
Declaration is here
An interface cannot be sealed (Typing[4245])
4 changes: 1 addition & 3 deletions hphp/hack/test/typecheck/sealed/trait2.php.exp
@@ -1,4 +1,2 @@
File "trait2.php", line 14, characters 13-13:
You cannot implement sealed interface A (Typing[4238])
File "trait2.php", line 5, characters 11-11:
Declaration is here
An interface cannot be sealed (Typing[4245])
8 changes: 8 additions & 0 deletions hphp/hack/test/typecheck/sealed/trait3.php
@@ -0,0 +1,8 @@
<?hh // strict

<<__Sealed(A::class)>>
trait T {}

abstract class A {
use T;
}
2 changes: 2 additions & 0 deletions hphp/hack/test/typecheck/sealed/trait3.php.exp
@@ -0,0 +1,2 @@
File "trait3.php", line 4, characters 7-7:
A trait cannot be sealed (Typing[4245])

0 comments on commit 202e6ab

Please sign in to comment.