2016-12-17 04:16:29 +01:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Tests;
|
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class TraitTest extends TestCase
|
2016-12-17 04:16:29 +01:00
|
|
|
{
|
2018-11-06 03:57:36 +01:00
|
|
|
use Traits\InvalidCodeAnalysisTestTrait;
|
|
|
|
use Traits\ValidCodeAnalysisTestTrait;
|
2016-12-17 04:16:29 +01:00
|
|
|
|
2017-01-13 20:07:23 +01:00
|
|
|
/**
|
2019-03-01 21:55:20 +01:00
|
|
|
* @return iterable<string,array{string,assertions?:array<string,string>,error_levels?:string[]}>
|
2017-01-13 20:07:23 +01:00
|
|
|
*/
|
2018-11-06 03:57:36 +01:00
|
|
|
public function providerValidCodeParse()
|
2016-12-17 04:16:29 +01:00
|
|
|
{
|
2017-04-25 05:45:02 +02:00
|
|
|
return [
|
|
|
|
'accessiblePrivateMethodFromTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
private function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->fooFoo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessibleProtectedMethodFromTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
protected function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->fooFoo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessiblePublicMethodFromTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->fooFoo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessiblePrivatePropertyFromTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/** @var string */
|
|
|
|
private $fooFoo = "";
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
echo $this->fooFoo;
|
2017-07-25 22:11:02 +02:00
|
|
|
$this->fooFoo = "hello";
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessibleProtectedPropertyFromTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/** @var string */
|
|
|
|
protected $fooFoo = "";
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
echo $this->fooFoo;
|
2017-07-25 22:11:02 +02:00
|
|
|
$this->fooFoo = "hello";
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessiblePublicPropertyFromTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/** @var string */
|
|
|
|
public $fooFoo = "";
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
echo $this->fooFoo;
|
2017-07-25 22:11:02 +02:00
|
|
|
$this->fooFoo = "hello";
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessibleProtectedMethodFromInheritedTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
protected function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class C extends B {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->fooFoo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'accessiblePublicMethodFromInheritedTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class C extends B {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->fooFoo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'staticClassMethodFromWithinTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
self::barBar();
|
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public static function barBar(): void {
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'redefinedTraitMethodWithoutAlias' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(string $a): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-05-27 02:05:57 +02:00
|
|
|
(new B)->fooFoo("hello");',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'redefinedTraitMethodWithAlias' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T {
|
|
|
|
fooFoo as barBar;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->barBar();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'traitSelf' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function g(): self
|
|
|
|
{
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
$a = (new A)->g();',
|
|
|
|
'assertions' => [
|
2017-06-29 16:22:49 +02:00
|
|
|
'$a' => 'A',
|
2017-05-27 02:05:57 +02:00
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'parentTraitSelf' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function g(): self
|
|
|
|
{
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B extends A {
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class C {
|
|
|
|
use T;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
$a = (new B)->g();',
|
|
|
|
'assertions' => [
|
2017-06-29 16:22:49 +02:00
|
|
|
'$a' => 'A',
|
2017-05-27 02:05:57 +02:00
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'directStaticCall' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/** @return void */
|
|
|
|
public static function foo() {}
|
|
|
|
}
|
|
|
|
class A {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
/** @return void */
|
|
|
|
public function bar() {
|
|
|
|
T::foo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'abstractTraitMethod' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/** @return void */
|
|
|
|
abstract public function foo();
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
abstract class A {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
/** @return void */
|
|
|
|
public function bar() {
|
|
|
|
$this->foo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
|
|
|
],
|
2017-06-23 06:39:37 +02:00
|
|
|
'instanceOfTraitUser' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function f(): void {
|
2017-06-23 06:39:37 +02:00
|
|
|
if ($this instanceof A) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
2017-10-24 06:01:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'getClassTraitUser' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-07-18 04:50:30 +02:00
|
|
|
public function f(): void {
|
2018-11-29 05:59:43 +01:00
|
|
|
if (get_class($this) === B::class) {
|
2018-07-18 04:50:30 +02:00
|
|
|
$this->foo();
|
|
|
|
}
|
|
|
|
}
|
2017-10-24 06:01:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
2018-07-18 04:50:30 +02:00
|
|
|
use T;
|
2017-10-24 06:01:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
2018-07-18 04:50:30 +02:00
|
|
|
use T;
|
|
|
|
|
|
|
|
public function foo() : void {}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'staticClassTraitUser' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function f(): void {
|
2018-11-29 05:59:43 +01:00
|
|
|
if (static::class === B::class) {
|
2018-07-18 04:50:30 +02:00
|
|
|
$this->foo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
public function foo() : void {}
|
|
|
|
}',
|
|
|
|
],
|
2019-02-26 07:03:33 +01:00
|
|
|
'isAClassTraitUserStringClass' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function f(): void {
|
|
|
|
if (is_a(static::class, "B")) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
public function foo() : void {}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'isAClassTraitUserClassConstant' => [
|
2018-07-18 04:50:30 +02:00
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function f(): void {
|
|
|
|
if (is_a(static::class, "B")) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
public function foo() : void {}
|
2017-06-23 06:39:37 +02:00
|
|
|
}',
|
|
|
|
],
|
2017-07-09 21:54:43 +02:00
|
|
|
'useTraitInClassWithAbstractMethod' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
abstract public function foo(): void;
|
2017-07-09 21:54:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function foo(): void {}
|
2017-07-25 22:11:02 +02:00
|
|
|
}',
|
2017-07-09 21:54:43 +02:00
|
|
|
],
|
|
|
|
'useTraitInSubclassWithAbstractMethod' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
abstract public function foo(): void;
|
2017-07-09 21:54:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
abstract class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function foo(): void {}
|
2017-07-09 21:54:43 +02:00
|
|
|
}
|
|
|
|
|
2017-07-29 05:38:57 +02:00
|
|
|
class B extends A {
|
|
|
|
use T;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'useTraitInSubclassWithAbstractMethodInParent' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function foo(): void {}
|
2017-07-29 05:38:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
abstract class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
abstract public function foo(): void {}
|
2017-07-29 05:38:57 +02:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:54:43 +02:00
|
|
|
class B extends A {
|
|
|
|
use T;
|
2017-07-25 22:11:02 +02:00
|
|
|
}',
|
2017-07-09 21:54:43 +02:00
|
|
|
],
|
2017-12-17 07:06:12 +01:00
|
|
|
'differentMethodReturnTypes' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public static function getSelf(): self {
|
2017-12-17 07:06:12 +01:00
|
|
|
return new self();
|
|
|
|
}
|
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public static function callGetSelf(): self {
|
2017-12-17 07:06:12 +01:00
|
|
|
return self::getSelf();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}',
|
|
|
|
],
|
2018-01-09 15:22:23 +01:00
|
|
|
'parentRefInTraitShouldNotFail' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function foo(): void {
|
2018-01-09 15:22:23 +01:00
|
|
|
parent::foo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function foo(): void {}
|
2018-01-09 15:22:23 +01:00
|
|
|
}
|
|
|
|
class B extends A {
|
|
|
|
use T;
|
|
|
|
}',
|
|
|
|
],
|
2018-01-21 06:32:45 +01:00
|
|
|
'namespacedTraitLookup' => [
|
|
|
|
'<?php
|
|
|
|
namespace Classes {
|
|
|
|
use Traits\T;
|
|
|
|
|
|
|
|
class A {}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Traits {
|
|
|
|
use Classes\A;
|
|
|
|
|
|
|
|
trait T {
|
|
|
|
public function getA() : A {
|
|
|
|
return new A;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
$a = (new Classes\B)->getA();
|
|
|
|
}',
|
|
|
|
],
|
2018-03-13 04:37:21 +01:00
|
|
|
'useAndMap' => [
|
|
|
|
'<?php
|
|
|
|
class C
|
|
|
|
{
|
|
|
|
use T2;
|
|
|
|
use T1 {
|
|
|
|
traitFunc as _func;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function func(): void
|
|
|
|
{
|
|
|
|
static::_func();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trait T1
|
|
|
|
{
|
|
|
|
public static function traitFunc(): void {}
|
|
|
|
}
|
|
|
|
trait T2 { }',
|
|
|
|
],
|
|
|
|
'mapAndUse' => [
|
|
|
|
'<?php
|
|
|
|
class C
|
|
|
|
{
|
|
|
|
use T1 {
|
|
|
|
traitFunc as _func;
|
|
|
|
}
|
|
|
|
use T2;
|
|
|
|
|
|
|
|
public static function func(): void
|
|
|
|
{
|
|
|
|
static::_func();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trait T1
|
|
|
|
{
|
|
|
|
public static function traitFunc(): void {}
|
|
|
|
}
|
|
|
|
trait T2 { }',
|
|
|
|
],
|
2018-03-14 15:51:13 +01:00
|
|
|
'moreArgsInDefined' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
abstract public function foo() : void;
|
|
|
|
|
|
|
|
public function callFoo() : void {
|
|
|
|
$this->foo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
public function foo(string $s = null) : void {
|
|
|
|
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-03-21 21:55:31 +01:00
|
|
|
'aliasedMethodInternalCallNoReplacement' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function foo() : int {
|
|
|
|
return $this->bar();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function bar() : int {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T {
|
|
|
|
bar as bat;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function baz() : int {
|
|
|
|
return $this->bar();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'aliasedMethodInternalCallWithLocalDefinition' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function bar() : int {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T {
|
|
|
|
bar as bat;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function bar() : string {
|
|
|
|
return "hello";
|
|
|
|
}
|
|
|
|
|
|
|
|
public function baz() : string {
|
|
|
|
return $this->bar();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-06-09 02:07:31 +02:00
|
|
|
'allMethodsReplaced' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
protected function foo() : void {}
|
|
|
|
|
|
|
|
public function bat() : void {
|
|
|
|
$this->foo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
protected function foo(string $s) : void {}
|
|
|
|
|
|
|
|
public function bat() : void {
|
|
|
|
$this->foo("bat");
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-03-21 22:39:01 +01:00
|
|
|
'aliasedPrivateMethodInternalCallWithLocalDefinition' => [
|
|
|
|
'<?php
|
|
|
|
trait T1 {
|
|
|
|
use T2;
|
|
|
|
|
|
|
|
private function foo() : int {
|
|
|
|
return $this->bar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait T2 {
|
|
|
|
private function bar() : int {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T1;
|
|
|
|
|
|
|
|
private function baz() : int {
|
|
|
|
return $this->bar();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-05-09 04:32:57 +02:00
|
|
|
'traitClassConst' => [
|
|
|
|
'<?php
|
|
|
|
trait A {
|
|
|
|
public function foo(): string {
|
|
|
|
return B::class;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait B {}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use A;
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2018-05-09 04:32:57 +02:00
|
|
|
],
|
2018-05-30 16:13:55 +02:00
|
|
|
'noRedundantConditionForTraitStatic' => [
|
|
|
|
'<?php
|
|
|
|
trait Foo {
|
|
|
|
public function bar() : array {
|
|
|
|
$type = static::class;
|
|
|
|
$r = new \ReflectionClass($type);
|
|
|
|
$values = $r->getConstants();
|
|
|
|
$callback =
|
|
|
|
/** @param mixed $v */
|
|
|
|
function ($v) : bool {
|
|
|
|
return \is_int($v) || \is_string($v);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (is_a($type, \Bat::class, true)) {
|
|
|
|
$callback =
|
|
|
|
/** @param mixed $v */
|
|
|
|
function ($v) : bool {
|
|
|
|
return \is_int($v) && 0 === ($v & $v - 1) && $v > 0;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_filter($values, $callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Bar {
|
|
|
|
use Foo;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Bat {
|
|
|
|
use Foo;
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2018-05-30 16:13:55 +02:00
|
|
|
],
|
2018-07-03 06:51:57 +02:00
|
|
|
'nonMemoizedAssertions' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function compare(O $other) : void {
|
|
|
|
if ($other instanceof self) {
|
|
|
|
if ($other->value === $this->value) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class O {}
|
|
|
|
|
|
|
|
class A extends O {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $value;
|
|
|
|
|
|
|
|
public function __construct(string $string) {
|
|
|
|
$this->value = $string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class B extends O {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
/** @var bool */
|
|
|
|
private $value;
|
|
|
|
|
|
|
|
public function __construct(bool $bool) {
|
|
|
|
$this->value = $bool;
|
|
|
|
}
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2018-07-03 06:51:57 +02:00
|
|
|
],
|
2018-11-30 19:45:39 +01:00
|
|
|
'manyTraitAliases' => [
|
|
|
|
'<?php
|
|
|
|
trait Foo {
|
|
|
|
public static function staticMethod():void {}
|
|
|
|
public function nonstatic():void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
Class Bar {
|
|
|
|
use Foo {
|
|
|
|
Foo::staticMethod as foo;
|
|
|
|
Foo::staticMethod as foobar;
|
|
|
|
Foo::staticMethod as fine;
|
|
|
|
Foo::nonstatic as bad;
|
|
|
|
Foo::nonstatic as good;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$b = new Bar();
|
|
|
|
|
|
|
|
Bar::fine();
|
|
|
|
$b::fine();
|
|
|
|
$b->fine();
|
|
|
|
|
|
|
|
$b->good();
|
|
|
|
|
|
|
|
Bar::foo();
|
|
|
|
Bar::foobar();
|
|
|
|
|
|
|
|
$b::foo();
|
|
|
|
$b::foobar();
|
|
|
|
|
|
|
|
$b->foo();
|
|
|
|
$b->foobar();
|
|
|
|
|
2019-03-23 19:27:54 +01:00
|
|
|
$b->bad();',
|
2018-11-30 19:45:39 +01:00
|
|
|
],
|
2019-01-07 17:53:22 +01:00
|
|
|
'inheritedProtectedTraitMethodAccess' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
private function bar() : void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T {
|
|
|
|
bar as protected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class AChild extends A {
|
|
|
|
public function foo() : void {
|
|
|
|
$this->bar();
|
|
|
|
}
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2019-01-07 17:53:22 +01:00
|
|
|
],
|
|
|
|
'inheritedPublicTraitMethodAccess' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
private function bar() : void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T {
|
|
|
|
bar as public;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-23 19:27:54 +01:00
|
|
|
(new A)->bar();',
|
2019-01-07 17:53:22 +01:00
|
|
|
],
|
2019-01-16 16:59:06 +01:00
|
|
|
'allowImplementMethodMadePublicInClass' => [
|
|
|
|
'<?php
|
|
|
|
interface I {
|
|
|
|
public function boo();
|
|
|
|
}
|
|
|
|
|
|
|
|
trait T {
|
|
|
|
private function boo() : void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class A implements I {
|
|
|
|
use T { boo as public; }
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'allowImplementMethodMadePublicInParent' => [
|
|
|
|
'<?php
|
|
|
|
interface I {
|
|
|
|
public function boo();
|
|
|
|
}
|
|
|
|
|
|
|
|
trait T {
|
|
|
|
private function boo() : void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T { boo as public; }
|
|
|
|
}
|
|
|
|
|
|
|
|
class BChild extends B implements I {}',
|
|
|
|
],
|
2019-01-18 16:08:16 +01:00
|
|
|
'allowTraitParentDefinition' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
|
|
|
|
class C extends A
|
|
|
|
{
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
|
|
|
|
trait T
|
|
|
|
{
|
|
|
|
public function bar() : ?C
|
|
|
|
{
|
|
|
|
if ($this instanceof A) {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2019-02-10 22:32:30 +01:00
|
|
|
],
|
|
|
|
'noCrashOnUndefinedIgnoredTrait' => [
|
|
|
|
'<?php
|
|
|
|
/** @psalm-suppress UndefinedTrait */
|
|
|
|
class C {
|
|
|
|
use UnknownTrait;
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2019-01-18 16:08:16 +01:00
|
|
|
],
|
2019-02-18 22:52:09 +01:00
|
|
|
'reconcileStaticTraitProperties' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/**
|
|
|
|
* @var string|null
|
|
|
|
*/
|
|
|
|
private static $b;
|
|
|
|
|
|
|
|
private static function booA(): string {
|
|
|
|
if (self::$b === null) {
|
|
|
|
return "hello";
|
|
|
|
}
|
|
|
|
return self::$b;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use T;
|
|
|
|
}',
|
|
|
|
],
|
2019-03-02 20:17:26 +01:00
|
|
|
'covariantAbstractReturn' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
/** @return iterable */
|
|
|
|
abstract public function bar();
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
/** @return array */
|
|
|
|
public function bar() { return []; }
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2019-03-02 20:17:26 +01:00
|
|
|
],
|
2019-03-17 16:31:04 +01:00
|
|
|
'traitSelfParam' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function bar(self $object): self {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Foo {
|
|
|
|
use T;
|
2019-03-17 18:50:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$f1 = new Foo();
|
|
|
|
$f2 = (new Foo())->bar($f1);',
|
2019-03-17 16:31:04 +01:00
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
];
|
2016-12-17 04:16:29 +01:00
|
|
|
}
|
|
|
|
|
2017-01-13 20:07:23 +01:00
|
|
|
/**
|
2019-03-01 21:55:20 +01:00
|
|
|
* @return iterable<string,array{string,error_message:string,2?:string[],3?:bool,4?:string}>
|
2017-01-13 20:07:23 +01:00
|
|
|
*/
|
2018-11-06 03:57:36 +01:00
|
|
|
public function providerInvalidCodeParse()
|
2016-12-17 04:16:29 +01:00
|
|
|
{
|
2017-04-25 05:45:02 +02:00
|
|
|
return [
|
|
|
|
'inaccessiblePrivateMethodFromInheritedTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
private function fooFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class C extends B {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function doFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->fooFoo();
|
|
|
|
}
|
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'InaccessibleMethod',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'undefinedTrait' => [
|
|
|
|
'<?php
|
|
|
|
class B {
|
|
|
|
use A;
|
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'UndefinedTrait',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'missingPropertyType' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public $foo;
|
|
|
|
}
|
|
|
|
class A {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function assignToFoo(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->foo = 5;
|
|
|
|
}
|
|
|
|
}',
|
2019-02-27 22:00:44 +01:00
|
|
|
'error_message' => 'MissingPropertyType - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:32 - Property T::$foo does not have a ' .
|
2018-05-18 17:02:50 +02:00
|
|
|
'declared type - consider int|null',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'missingPropertyTypeWithConstructorInit' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public $foo;
|
|
|
|
}
|
|
|
|
class A {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-04-21 23:20:21 +02:00
|
|
|
public function __construct() {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->foo = 5;
|
|
|
|
}
|
|
|
|
}',
|
2019-02-27 22:00:44 +01:00
|
|
|
'error_message' => 'MissingPropertyType - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:32 - Property T::$foo does not have a ' .
|
2017-05-27 02:05:57 +02:00
|
|
|
'declared type - consider int',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'missingPropertyTypeWithConstructorInitAndNull' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public $foo;
|
|
|
|
}
|
|
|
|
class A {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-04-21 23:20:21 +02:00
|
|
|
public function __construct() {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->foo = 5;
|
|
|
|
}
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
public function makeNull(): void {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->foo = null;
|
|
|
|
}
|
|
|
|
}',
|
2019-02-27 22:00:44 +01:00
|
|
|
'error_message' => 'MissingPropertyType - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:32 - Property T::$foo does not have a ' .
|
2018-05-18 17:02:50 +02:00
|
|
|
'declared type - consider int|null',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'missingPropertyTypeWithConstructorInitAndNullDefault' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public $foo = null;
|
|
|
|
}
|
|
|
|
class A {
|
|
|
|
use T;
|
2017-06-23 06:39:37 +02:00
|
|
|
|
2018-04-21 23:20:21 +02:00
|
|
|
public function __construct() {
|
2017-04-25 05:45:02 +02:00
|
|
|
$this->foo = 5;
|
|
|
|
}
|
|
|
|
}',
|
2019-02-27 22:00:44 +01:00
|
|
|
'error_message' => 'MissingPropertyType - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:32 - Property T::$foo does not have a ' .
|
2018-01-11 23:35:28 +01:00
|
|
|
'declared type - consider int|null',
|
2017-05-27 02:05:57 +02:00
|
|
|
],
|
2017-07-25 22:11:02 +02:00
|
|
|
'redefinedTraitMethodInSubclass' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {
|
2017-07-25 22:11:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class B {
|
|
|
|
use T;
|
|
|
|
}
|
|
|
|
|
|
|
|
class C extends B {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(string $a): void {
|
2017-07-25 22:11:02 +02:00
|
|
|
}
|
|
|
|
}',
|
|
|
|
'error_message' => 'MethodSignatureMismatch',
|
|
|
|
],
|
2018-02-12 16:46:45 +01:00
|
|
|
'missingTraitPropertyType' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public $foo;
|
|
|
|
}
|
|
|
|
|
|
|
|
class A {
|
|
|
|
use T;
|
|
|
|
}',
|
|
|
|
'error_message' => 'MissingPropertyType',
|
|
|
|
],
|
2018-05-30 18:23:53 +02:00
|
|
|
'nestedTraitWithBadReturnType' => [
|
|
|
|
'<?php
|
|
|
|
trait A {
|
|
|
|
public function foo() : string {
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait B {
|
|
|
|
use A;
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use B;
|
|
|
|
}',
|
|
|
|
'error_message' => 'InvalidReturnType',
|
|
|
|
],
|
2018-06-09 02:06:05 +02:00
|
|
|
'replaceTraitMethod' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
protected function foo() : void {}
|
|
|
|
|
|
|
|
public function bat() : void {
|
|
|
|
$this->foo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use T;
|
|
|
|
|
|
|
|
protected function foo(string $s) : void {}
|
|
|
|
}',
|
|
|
|
'error_message' => 'TooFewArguments',
|
|
|
|
],
|
2019-01-13 16:19:27 +01:00
|
|
|
'traitMethodMadePrivate' => [
|
|
|
|
'<?php
|
|
|
|
trait T {
|
|
|
|
public function foo() : void {
|
|
|
|
echo "here";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class C {
|
|
|
|
use T {
|
|
|
|
foo as private traitFoo;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function bar() : void {
|
|
|
|
$this->traitFoo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class D extends C {
|
|
|
|
public function bar() : void {
|
|
|
|
$this->traitFoo(); // should fail
|
|
|
|
}
|
|
|
|
}',
|
2019-03-23 19:27:54 +01:00
|
|
|
'error_message' => 'InaccessibleMethod',
|
2019-01-13 16:19:27 +01:00
|
|
|
],
|
2019-03-16 03:12:35 +01:00
|
|
|
'preventTraitPropertyType' => [
|
|
|
|
'<?php
|
|
|
|
trait T {}
|
|
|
|
|
|
|
|
class X {
|
|
|
|
/** @var T|null */
|
|
|
|
public $hm;
|
|
|
|
}',
|
2019-05-16 00:41:26 +02:00
|
|
|
'error_message' => 'UndefinedDocblockClass',
|
2019-03-16 03:12:35 +01:00
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
];
|
2017-04-15 06:04:03 +02:00
|
|
|
}
|
2016-12-17 04:16:29 +01:00
|
|
|
}
|