Permalink
Browse files

Pass receiver type for static method calls when checking reactivity

Summary:
as in title: check reactivity on a statically known receiver type, now cases like one below are handled properly
```
<?hh // strict

interface Rx {}

class A {
  <<__RxIfImplements(Rx::class)>>
  static function f(): int {
    return 1;
  }
}

class RxA extends A implements Rx {
}

<<__Rx>>
function f(): int {
  // should be ok
  return RxA::f();
}
```
also this diff adjusts error message for conditionally reactive calls to include call position

Reviewed By: KendallHopkins

Differential Revision: D7241274

fbshipit-source-id: 193fac809a47d938920e045a82d34683e7b692e8
  • Loading branch information...
vladima authored and hhvm-bot committed Mar 13, 2018
1 parent 921354c commit 35059810dd528df551e022a129353fefdcc3dd41
Showing with 306 additions and 16 deletions.
  1. +5 −4 hphp/hack/src/typing/typing.ml
  2. +1 −1 hphp/hack/src/typing/typing_reactivity.ml
  3. +6 −5 hphp/hack/src/utils/errors/errors.ml
  4. +1 −1 hphp/hack/src/utils/errors/errors_sig.ml
  5. +17 −0 hphp/hack/test/typecheck/reactive/nonrx_parent_call.php
  6. +4 −0 hphp/hack/test/typecheck/reactive/nonrx_parent_call.php.exp
  7. +3 −1 hphp/hack/test/typecheck/reactive/rx_if_implements16.php.exp
  8. +3 −1 hphp/hack/test/typecheck/reactive/rx_if_implements27.php.exp
  9. +3 −1 hphp/hack/test/typecheck/reactive/rx_if_implements28.php.exp
  10. +3 −1 hphp/hack/test/typecheck/reactive/rx_if_implements29.php.exp
  11. +3 −1 hphp/hack/test/typecheck/reactive/rx_if_implements30.php.exp
  12. +19 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements1.php
  13. +1 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements1.php.exp
  14. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements2.php
  15. +1 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements2.php.exp
  16. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements3.php
  17. +1 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements3.php.exp
  18. +21 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements4.php
  19. +1 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements4.php.exp
  20. +21 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements4_1.php
  21. +1 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements4_1.php.exp
  22. +21 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements4_2.php
  23. +1 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements4_2.php.exp
  24. +19 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements5.php
  25. +4 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements5.php.exp
  26. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements6.php
  27. +4 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements6.php.exp
  28. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements7.php
  29. +4 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements7.php.exp
  30. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements8.php
  31. +4 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements8.php.exp
  32. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements8_1.php
  33. +4 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements8_1.php.exp
  34. +18 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements8_2.php
  35. +4 −0 hphp/hack/test/typecheck/reactive/static_rx_if_implements8_2.php.exp
@@ -3770,7 +3770,7 @@ and is_abstract_ft fty = match fty with
class_get ~is_method:true ~is_const:false ~explicit_tparams:hl env ty1 m CIparent in
let fty = check_abstract_parent_meth (snd m) p fty in
check_coroutine_call env fty;
let env, tel, tuel, ty = call ~expected p env fty el uel in
let env, tel, tuel, ty = call ~expected ~receiver_type:ty1 p env fty el uel in
make_call env (T.make_typed_expr fpos fty
(T.Class_const ((Some ty1, T.CIparent), m))) hl tel tuel ty
end
@@ -3792,7 +3792,8 @@ and is_abstract_ft fty = match fty with
begin fun (env, fty, _) ->
let fty = check_abstract_parent_meth (snd m) p fty in
check_coroutine_call env fty;
let env, _tel, _tuel, method_ = call ~expected p env fty el uel in
let env, _tel, _tuel, method_ = call ~expected
~receiver_type:ty1 p env fty el uel in
env, method_, None
end
k_lhs
@@ -3804,7 +3805,7 @@ and is_abstract_ft fty = match fty with
class_get ~is_method:true ~is_const:false ~explicit_tparams:hl env ty1 m CIparent in
let fty = check_abstract_parent_meth (snd m) p fty in
check_coroutine_call env fty;
let env, tel, tuel, ty = call ~expected p env fty el uel in
let env, tel, tuel, ty = call ~expected ~receiver_type:ty1 p env fty el uel in
make_call env (T.make_typed_expr fpos fty
(T.Class_const ((Some ty1, T.CIparent), m))) hl tel tuel ty
end
@@ -3845,7 +3846,7 @@ and is_abstract_ft fty = match fty with
())
| _ -> () in
check_coroutine_call env fty;
let env, tel, tuel, ty = call ~expected p env fty el uel in
let env, tel, tuel, ty = call ~expected ~receiver_type:ty1 p env fty el uel in
make_call env (T.make_typed_expr fpos fty
(T.Class_const(te1, m))) hl tel tuel ty
@@ -83,7 +83,7 @@ let check_call env receiver_type pos reason ft =
Option.value_map ~default:"" callee_t ~f:type_to_str in
let receiver_type_str =
Option.value_map receiver_type ~default:"" ~f:type_to_str in
Errors.invalid_conditionally_reactive_call (Reason.to_pos reason)
Errors.invalid_conditionally_reactive_call pos (Reason.to_pos reason)
condition_type_str
receiver_type_str;
| _ -> ()
@@ -2516,13 +2516,14 @@ let untyped_lambda_strict_mode pos =
in
add Typing.untyped_lambda_strict_mode pos msg
let invalid_conditionally_reactive_call pos condition_type receiver_type =
add Typing.invalid_conditionally_reactive_call pos (
"Conditionally reactive method with condition type '" ^ condition_type ^
"' cannot be called. Calling such methods is allowed only from conditionally " ^
let invalid_conditionally_reactive_call pos def_pos condition_type receiver_type =
add_list Typing.invalid_conditionally_reactive_call [
pos, "Conditionally reactive method with condition type '" ^ condition_type ^
"' cannot be called";
def_pos, "Calling such methods is allowed only from conditionally " ^
"reactive methods with the same condition type or if static type of method " ^
"receiver is a subtype of condition type, now '" ^ receiver_type ^ "'."
)
]
let echo_in_reactive_context pos =
add Typing.echo_in_reactive_context pos (
@@ -486,7 +486,7 @@ module type S = sig
val ellipsis_strict_mode : require_param_name:bool -> Pos.t -> unit
val untyped_lambda_strict_mode : Pos.t -> unit
val binding_ref_in_array : Pos.t -> unit
val invalid_conditionally_reactive_call : Pos.t -> string -> string -> unit
val invalid_conditionally_reactive_call : Pos.t -> Pos.t -> string -> string -> unit
val conditionally_reactive_function : Pos.t -> unit
val multiple_conditionally_reactive_annotations : Pos.t -> string -> unit
val conditionally_reactive_annotation_invalid_arguments : Pos.t -> unit
@@ -0,0 +1,17 @@
<?hh // strict
interface Rx {}
class A {
public function f(): int {
return 1;
}
}
class B extends A {
<<__Rx, __Override>>
public function f(): int {
// should be error, A is not reactive
return parent::f();
}
}
@@ -0,0 +1,4 @@
File "nonrx_parent_call.php", line 15, characters 12-22:
Reactive functions can only call other reactive functions. (Typing[4200])
File "nonrx_parent_call.php", line 6, characters 19-19:
This function is not reactive.
@@ -1,2 +1,4 @@
File "rx_if_implements16.php", line 13, characters 12-28:
Conditionally reactive method with condition type '\Rx2' cannot be called (Typing[4225])
File "rx_if_implements16.php", line 17, characters 28-35:
Conditionally reactive method with condition type '\Rx2' cannot be called. Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\A'. (Typing[4225])
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\A'.
@@ -1,2 +1,4 @@
File "rx_if_implements27.php", line 15, characters 12-26:
Conditionally reactive method with condition type '\Rx1' cannot be called (Typing[4225])
File "rx_if_implements27.php", line 7, characters 19-27:
Conditionally reactive method with condition type '\Rx1' cannot be called. Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'. (Typing[4225])
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'.
@@ -1,2 +1,4 @@
File "rx_if_implements28.php", line 16, characters 12-26:
Conditionally reactive method with condition type '\Rx1' cannot be called (Typing[4225])
File "rx_if_implements28.php", line 8, characters 19-27:
Conditionally reactive method with condition type '\Rx1' cannot be called. Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'. (Typing[4225])
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'.
@@ -1,2 +1,4 @@
File "rx_if_implements29.php", line 15, characters 12-26:
Conditionally reactive method with condition type '\Rx1' cannot be called (Typing[4225])
File "rx_if_implements29.php", line 7, characters 19-27:
Conditionally reactive method with condition type '\Rx1' cannot be called. Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'. (Typing[4225])
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'.
@@ -1,2 +1,4 @@
File "rx_if_implements30.php", line 16, characters 12-26:
Conditionally reactive method with condition type '\Rx1' cannot be called (Typing[4225])
File "rx_if_implements30.php", line 8, characters 19-27:
Conditionally reactive method with condition type '\Rx1' cannot be called. Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'. (Typing[4225])
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\I'.
@@ -0,0 +1,19 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class RxA extends A implements Rx {
}
<<__Rx>>
function f(): int {
// should be ok
return RxA::f();
}
@@ -0,0 +1,18 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A implements Rx {
<<__Rx>>
public function g(): int {
// should be OK
return self::f();
}
}
@@ -0,0 +1,18 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A implements Rx {
<<__Rx>>
public function g(): int {
// should be OK
return static::f();
}
}
@@ -0,0 +1,21 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A implements Rx {
}
class C extends B {
<<__Rx>>
public function g(): int {
// should be OK
return parent::f();
}
}
@@ -0,0 +1,21 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A implements Rx {
}
class C extends B {
<<__Rx>>
public static function g(): int {
// should be OK
return parent::f();
}
}
@@ -0,0 +1,21 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
public function f(): int {
return 1;
}
}
class B extends A implements Rx {
}
class C extends B {
<<__Rx>>
public function g(): int {
// should be OK
return parent::f();
}
}
@@ -0,0 +1,19 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class NonRxA extends A {
}
<<__Rx>>
function f(): int {
// should error, NonRxA does not implement Rx
return NonRxA::f();
}
@@ -0,0 +1,4 @@
File "static_rx_if_implements5.php", line 18, characters 10-20:
Conditionally reactive method with condition type '\Rx' cannot be called (Typing[4225])
File "static_rx_if_implements5.php", line 7, characters 19-19:
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\NonRxA'.
@@ -0,0 +1,18 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A {
<<__Rx>>
public function g() {
// should be an error, B does not implement Rx
self::f();
}
}
@@ -0,0 +1,4 @@
File "static_rx_if_implements6.php", line 16, characters 5-13:
Conditionally reactive method with condition type '\Rx' cannot be called (Typing[4225])
File "static_rx_if_implements6.php", line 7, characters 19-19:
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\B'.
@@ -0,0 +1,18 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A {
<<__Rx>>
public function g() {
// should be an error, B does not implement Rx
static::f();
}
}
@@ -0,0 +1,4 @@
File "static_rx_if_implements7.php", line 16, characters 5-15:
Conditionally reactive method with condition type '\Rx' cannot be called (Typing[4225])
File "static_rx_if_implements7.php", line 7, characters 19-19:
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\B'.
@@ -0,0 +1,18 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A {
<<__Rx>>
public function g() {
// should be an error, B does not implement Rx
parent::f();
}
}
@@ -0,0 +1,4 @@
File "static_rx_if_implements8.php", line 16, characters 5-15:
Conditionally reactive method with condition type '\Rx' cannot be called (Typing[4225])
File "static_rx_if_implements8.php", line 7, characters 19-19:
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\A'.
@@ -0,0 +1,18 @@
<?hh // strict
interface Rx {}
class A {
<<__RxIfImplements(Rx::class)>>
static function f(): int {
return 1;
}
}
class B extends A {
<<__Rx>>
public static function g() {
// should be an error, B does not implement Rx
parent::f();
}
}
@@ -0,0 +1,4 @@
File "static_rx_if_implements8_1.php", line 16, characters 5-15:
Conditionally reactive method with condition type '\Rx' cannot be called (Typing[4225])
File "static_rx_if_implements8_1.php", line 7, characters 19-19:
Calling such methods is allowed only from conditionally reactive methods with the same condition type or if static type of method receiver is a subtype of condition type, now '\A'.
Oops, something went wrong.

0 comments on commit 3505981

Please sign in to comment.