From 910cf67c1cfa016f38d8b1321847e79e7592dd38 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 26 May 2019 22:09:56 +0200 Subject: [PATCH 01/18] Adding @vimeo/psalm-specific templated type declarations for the mock API --- composer.json | 3 +- src/Framework/MockObject/MockBuilder.php | 13 ++- src/Framework/TestCase.php | 47 ++++++++++ tests/static-analysis/TestUsingMocks.php | 113 +++++++++++++++++++++++ 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 tests/static-analysis/TestUsingMocks.php diff --git a/composer.json b/composer.json index 23cf4951054..66f0d0211a5 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,8 @@ "sebastian/version": "^2.0.1" }, "require-dev": { - "ext-PDO": "*" + "ext-PDO": "*", + "vimeo/psalm": "dev-master@dev" }, "config": { "platform": { diff --git a/src/Framework/MockObject/MockBuilder.php b/src/Framework/MockObject/MockBuilder.php index 46a421858e2..c18db2170ac 100644 --- a/src/Framework/MockObject/MockBuilder.php +++ b/src/Framework/MockObject/MockBuilder.php @@ -11,6 +11,9 @@ use PHPUnit\Framework\TestCase; +/** + * @psalm-template MockedType + */ final class MockBuilder { /** @@ -84,7 +87,9 @@ final class MockBuilder private $generator; /** - * @param array|string $type + * @param string[]|string $type + * + * @psalm-param class-string|string|string[] $type */ public function __construct(TestCase $testCase, $type) { @@ -97,6 +102,8 @@ public function __construct(TestCase $testCase, $type) * Creates a mock object using a fluent interface. * * @throws RuntimeException + * + * @psalm-return MockObject&MockedType */ public function getMock(): MockObject { @@ -125,6 +132,8 @@ public function getMock(): MockObject * * @throws \PHPUnit\Framework\Exception * @throws RuntimeException + * + * @psalm-return MockObject&MockedType */ public function getMockForAbstractClass(): MockObject { @@ -149,6 +158,8 @@ public function getMockForAbstractClass(): MockObject * * @throws \PHPUnit\Framework\Exception * @throws RuntimeException + * + * @psalm-return MockObject&MockedType */ public function getMockForTrait(): MockObject { diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index d931d3a5ad3..da55c933041 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -597,6 +597,9 @@ public function getExpectedExceptionMessageRegExp(): ?string return $this->expectedExceptionMessageRegExp; } + /** + * @psalm-param class-string<|Throwable> $exception + */ public function expectException(string $exception): void { $this->expectedException = $exception; @@ -1080,6 +1083,10 @@ public function registerMockObject(MockObject $mockObject): void * Returns a builder object to create mock objects using a fluent interface. * * @param string|string[] $className + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string[] $className + * @psalm-return MockBuilder */ public function getMockBuilder($className): MockBuilder { @@ -1335,6 +1342,10 @@ protected function setLocale(...$args): void * @param string|string[] $originalClassName * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string[] $originalClassName + * @psalm-return MockObject&RealInstanceType */ protected function createMock($originalClassName): MockObject { @@ -1360,6 +1371,10 @@ protected function createMock($originalClassName): MockObject * @param string|string[] $originalClassName * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string[] $originalClassName + * @psalm-return MockObject&RealInstanceType */ protected function createConfiguredMock($originalClassName, array $configuration): MockObject { @@ -1387,6 +1402,10 @@ protected function createConfiguredMock($originalClassName, array $configuration * @param string[] $methods * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string[] $originalClassName + * @psalm-return MockObject&RealInstanceType */ protected function createPartialMock($originalClassName, array $methods): MockObject { @@ -1411,6 +1430,10 @@ protected function createPartialMock($originalClassName, array $methods): MockOb * Returns a test proxy for the specified class. * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string[] $originalClassName + * @psalm-return MockObject&RealInstanceType */ protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject { @@ -1440,6 +1463,10 @@ protected function createTestProxy(string $originalClassName, array $constructor * @param bool $cloneArguments * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string $originalClassName + * @psalm-return class-string */ protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false): string { @@ -1481,6 +1508,10 @@ protected function getMockClass($originalClassName, $methods = [], array $argume * @param bool $cloneArguments * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string[] $originalClassName + * @psalm-return MockObject&RealInstanceType */ protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject { @@ -1520,6 +1551,10 @@ protected function getMockForAbstractClass($originalClassName, array $arguments * @param array $options An array of options passed to SOAPClient::_construct * * @throws Exception + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|string $originalClassName + * @psalm-return MockObject&RealInstanceType */ protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []): MockObject { @@ -1578,6 +1613,10 @@ protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClas * @param bool $cloneArguments * * @throws Exception + * + * @psalm-template RealTraitType of object + * @psalm-param class-string|string $traitName + * @psalm-return MockObject&RealTraitType */ protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject { @@ -1619,6 +1658,10 @@ protected function getMockForTrait($traitName, array $arguments = [], $mockClass * @throws Exception * * @return object + * + * @psalm-template RealTraitType of object + * @psalm-param class-string|string $traitName + * @psalm-return RealTraitType */ protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)/*: object*/ { @@ -1645,6 +1688,10 @@ protected function getObjectForTrait($traitName, array $arguments = [], $traitCl * @throws \Prophecy\Exception\Doubler\ClassNotFoundException * @throws \Prophecy\Exception\Doubler\DoubleException * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string|null $classOrInterface + * @psalm-return ObjectProphecy */ protected function prophesize($classOrInterface = null): ObjectProphecy { diff --git a/tests/static-analysis/TestUsingMocks.php b/tests/static-analysis/TestUsingMocks.php new file mode 100644 index 00000000000..83724fd56b5 --- /dev/null +++ b/tests/static-analysis/TestUsingMocks.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PHPUnit\StaticAnalysis; + +use PHPUnit\Framework\TestCase; + +class HelloWorldClass +{ + public function sayHello() : string + { + return 'hello world!'; + } +} + +trait HelloWorldTrait +{ + public function sayHello() : string + { + return 'hello world!'; + } +} + +/** + * @small + */ +final class TestUsingMocks extends TestCase +{ + public function testWillSayHelloThroughCreateMock() : void + { + $mock = $this->createMock(HelloWorldClass::class); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + + public function testWillSayHelloThroughCreateConfiguredMock() : void + { + $mock = $this->createConfiguredMock(HelloWorldClass::class, []); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + + public function testWillSayHelloThroughCreatePartialMock() : void + { + $mock = $this->createPartialMock(HelloWorldClass::class, []); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + + public function testWillSayHelloThroughCreateTestProxy() : void + { + $mock = $this->createTestProxy(HelloWorldClass::class, []); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + + public function testWillSayHelloThroughGetMockBuilder() : void + { + $mock = $this + ->getMockBuilder(HelloWorldClass::class) + ->getMock(); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + + public function testWillSayHelloThroughGetMockForTrait() : void + { + $mock = $this->getMockForTrait(HelloWorldTrait::class); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + + public function testWillSayHelloThroughGetObjectForTrait() : void + { + self::assertSame( + 'hello world!', + $this + ->getObjectForTrait(HelloWorldTrait::class) + ->sayHello() + ); + } +} From 764d671ba21a41d5183be6775b7486c0d59844d9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 26 May 2019 22:10:31 +0200 Subject: [PATCH 02/18] Added workaround to allow Psalm to recognize `MockObject#method()` calls --- src/Framework/MockObject/MockObject.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Framework/MockObject/MockObject.php b/src/Framework/MockObject/MockObject.php index 2df6810753e..0a8f30f7162 100644 --- a/src/Framework/MockObject/MockObject.php +++ b/src/Framework/MockObject/MockObject.php @@ -28,4 +28,8 @@ public function __phpunit_hasMatchers(): bool; public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void; public function expects(Invocation $matcher): BuilderInvocationMocker; + + // @TODO get rid of this - required by psalm-only right now - bug in parsing @method? + // @TODO this addition is a BC break and needs to be reverted before merge. + public function method($constraint) : BuilderInvocationMocker; } From ae6da6977048fc48d744b736ec3d36f5eb9ad472 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 26 May 2019 22:41:53 +0200 Subject: [PATCH 03/18] Decorating `Assert` with @vimeo/psalm-specific type annotations --- src/Framework/Assert.php | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index b5dea6e97fb..4e30aea8044 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -819,6 +819,8 @@ public static function assertAttributeNotEquals($expected, string $actualAttribu * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert TEmptyMixed $actual */ public static function assertEmpty($actual, string $message = ''): void { @@ -853,6 +855,8 @@ public static function assertAttributeEmpty(string $haystackAttributeName, $hays * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert TNonEmptyMixed $actual */ public static function assertNotEmpty($actual, string $message = ''): void { @@ -1305,6 +1309,8 @@ public static function assertFileNotIsWritable(string $file, string $message = ' * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert true $condition */ public static function assertTrue($condition, string $message = ''): void { @@ -1316,6 +1322,8 @@ public static function assertTrue($condition, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !true $condition */ public static function assertNotTrue($condition, string $message = ''): void { @@ -1327,6 +1335,8 @@ public static function assertNotTrue($condition, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert false $condition */ public static function assertFalse($condition, string $message = ''): void { @@ -1338,6 +1348,8 @@ public static function assertFalse($condition, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !false $condition */ public static function assertNotFalse($condition, string $message = ''): void { @@ -1349,6 +1361,8 @@ public static function assertNotFalse($condition, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert TNull $actual */ public static function assertNull($actual, string $message = ''): void { @@ -1360,6 +1374,8 @@ public static function assertNull($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !TNull $actual */ public static function assertNotNull($actual, string $message = ''): void { @@ -1556,6 +1572,10 @@ public static function assertObjectNotHasAttribute(string $attributeName, $objec * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-template ExpectedType + * @psalm-param ExpectedType $expected + * @psalm-assert =ExpectedType $actual */ public static function assertSame($expected, $actual, string $message = ''): void { @@ -1597,6 +1617,10 @@ public static function assertAttributeSame($expected, string $actualAttributeNam * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-template ExpectedType + * @psalm-param ExpectedType $expected + * @psalm-assert !=ExpectedType $actual */ public static function assertNotSame($expected, $actual, string $message = ''): void { @@ -1643,6 +1667,10 @@ public static function assertAttributeNotSame($expected, string $actualAttribute * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception + * + * @psalm-template ExpectedType + * @psalm-template-typeof ExpectedType $expected + * @psalm-assert ExpectedType $actual */ public static function assertInstanceOf(string $expected, $actual, string $message = ''): void { @@ -1686,6 +1714,10 @@ public static function assertAttributeInstanceOf(string $expected, string $attri * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception + * + * @psalm-template ExpectedType + * @psalm-template-typeof ExpectedType $expected + * @psalm-assert !ExpectedType $actual */ public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void { @@ -1773,6 +1805,8 @@ public static function assertAttributeInternalType(string $expected, string $att * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert array $actual */ public static function assertIsArray($actual, string $message = ''): void { @@ -1788,6 +1822,8 @@ public static function assertIsArray($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert bool $actual */ public static function assertIsBool($actual, string $message = ''): void { @@ -1803,6 +1839,8 @@ public static function assertIsBool($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert float $actual */ public static function assertIsFloat($actual, string $message = ''): void { @@ -1818,6 +1856,8 @@ public static function assertIsFloat($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert int $actual */ public static function assertIsInt($actual, string $message = ''): void { @@ -1833,6 +1873,8 @@ public static function assertIsInt($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert numeric $actual */ public static function assertIsNumeric($actual, string $message = ''): void { @@ -1848,6 +1890,8 @@ public static function assertIsNumeric($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert object $actual */ public static function assertIsObject($actual, string $message = ''): void { @@ -1863,6 +1907,8 @@ public static function assertIsObject($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert TResource $actual */ public static function assertIsResource($actual, string $message = ''): void { @@ -1878,6 +1924,8 @@ public static function assertIsResource($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert string $actual */ public static function assertIsString($actual, string $message = ''): void { @@ -1893,6 +1941,8 @@ public static function assertIsString($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert TScalar $actual */ public static function assertIsScalar($actual, string $message = ''): void { @@ -1908,6 +1958,8 @@ public static function assertIsScalar($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert callable $actual */ public static function assertIsCallable($actual, string $message = ''): void { @@ -1923,6 +1975,8 @@ public static function assertIsCallable($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert TIterable $actual */ public static function assertIsIterable($actual, string $message = ''): void { @@ -1960,6 +2014,8 @@ public static function assertNotInternalType(string $expected, $actual, string $ * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !array $actual */ public static function assertIsNotArray($actual, string $message = ''): void { @@ -1975,6 +2031,8 @@ public static function assertIsNotArray($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !bool $actual */ public static function assertIsNotBool($actual, string $message = ''): void { @@ -1990,6 +2048,8 @@ public static function assertIsNotBool($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !float $actual */ public static function assertIsNotFloat($actual, string $message = ''): void { @@ -2005,6 +2065,8 @@ public static function assertIsNotFloat($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !int $actual */ public static function assertIsNotInt($actual, string $message = ''): void { @@ -2020,6 +2082,8 @@ public static function assertIsNotInt($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !numeric $actual */ public static function assertIsNotNumeric($actual, string $message = ''): void { @@ -2035,6 +2099,8 @@ public static function assertIsNotNumeric($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !object $actual */ public static function assertIsNotObject($actual, string $message = ''): void { @@ -2050,6 +2116,8 @@ public static function assertIsNotObject($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !TResource $actual */ public static function assertIsNotResource($actual, string $message = ''): void { @@ -2065,6 +2133,8 @@ public static function assertIsNotResource($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !string $actual */ public static function assertIsNotString($actual, string $message = ''): void { @@ -2080,6 +2150,8 @@ public static function assertIsNotString($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !TScalar $actual */ public static function assertIsNotScalar($actual, string $message = ''): void { @@ -2095,6 +2167,8 @@ public static function assertIsNotScalar($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !callable $actual */ public static function assertIsNotCallable($actual, string $message = ''): void { @@ -2110,6 +2184,8 @@ public static function assertIsNotCallable($actual, string $message = ''): void * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @psalm-assert !TIterable $actual */ public static function assertIsNotIterable($actual, string $message = ''): void { @@ -2986,6 +3062,8 @@ public static function countOf(int $count): Count * Fails a test with the given message. * * @throws AssertionFailedError + * + * @psalm-return TNever */ public static function fail(string $message = ''): void { From d50a35365234f08989e005cf557be52f2294d85a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 18:47:30 +0200 Subject: [PATCH 04/18] Split assertion type-check tests into isolated test scenarios Each file verifies that the type can be correctly inferred --- src/Framework/Assert.php | 32 ++++++++----------- .../happy-path/assert-empty.php | 13 ++++++++ .../happy-path/assert-false.php | 17 ++++++++++ .../happy-path/assert-instance-of.php | 12 +++++++ .../happy-path/assert-is-array.php | 13 ++++++++ .../happy-path/assert-is-bool.php | 13 ++++++++ .../happy-path/assert-is-callable.php | 13 ++++++++ .../happy-path/assert-is-float.php | 13 ++++++++ .../happy-path/assert-is-int.php | 13 ++++++++ .../happy-path/assert-is-iterable.php | 13 ++++++++ .../happy-path/assert-is-not-array.php | 13 ++++++++ .../happy-path/assert-is-not-bool.php | 13 ++++++++ .../happy-path/assert-is-not-callable.php | 13 ++++++++ .../happy-path/assert-is-not-float.php | 13 ++++++++ .../happy-path/assert-is-not-int.php | 13 ++++++++ .../happy-path/assert-is-not-iterable.php | 13 ++++++++ .../happy-path/assert-is-not-numeric.php | 13 ++++++++ .../happy-path/assert-is-not-object.php | 13 ++++++++ .../happy-path/assert-is-not-resource.php | 13 ++++++++ .../happy-path/assert-is-not-scalar.php | 13 ++++++++ .../happy-path/assert-is-not-string.php | 13 ++++++++ .../happy-path/assert-is-numeric.php | 17 ++++++++++ .../happy-path/assert-is-object.php | 13 ++++++++ .../happy-path/assert-is-resource.php | 17 ++++++++++ .../happy-path/assert-is-scalar.php | 17 ++++++++++ .../happy-path/assert-is-string.php | 13 ++++++++ .../happy-path/assert-not-empty.php | 13 ++++++++ .../happy-path/assert-not-false.php | 13 ++++++++ .../happy-path/assert-not-instance-of.php | 16 ++++++++++ .../happy-path/assert-not-null.php | 12 +++++++ .../happy-path/assert-not-true.php | 13 ++++++++ .../happy-path/assert-null.php | 17 ++++++++++ .../happy-path/assert-same.php | 12 +++++++ .../happy-path/assert-true.php | 17 ++++++++++ tests/static-analysis/happy-path/fail.php | 15 +++++++++ 35 files changed, 482 insertions(+), 18 deletions(-) create mode 100644 tests/static-analysis/happy-path/assert-empty.php create mode 100644 tests/static-analysis/happy-path/assert-false.php create mode 100644 tests/static-analysis/happy-path/assert-instance-of.php create mode 100644 tests/static-analysis/happy-path/assert-is-array.php create mode 100644 tests/static-analysis/happy-path/assert-is-bool.php create mode 100644 tests/static-analysis/happy-path/assert-is-callable.php create mode 100644 tests/static-analysis/happy-path/assert-is-float.php create mode 100644 tests/static-analysis/happy-path/assert-is-int.php create mode 100644 tests/static-analysis/happy-path/assert-is-iterable.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-array.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-bool.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-callable.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-float.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-int.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-iterable.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-numeric.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-object.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-resource.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-scalar.php create mode 100644 tests/static-analysis/happy-path/assert-is-not-string.php create mode 100644 tests/static-analysis/happy-path/assert-is-numeric.php create mode 100644 tests/static-analysis/happy-path/assert-is-object.php create mode 100644 tests/static-analysis/happy-path/assert-is-resource.php create mode 100644 tests/static-analysis/happy-path/assert-is-scalar.php create mode 100644 tests/static-analysis/happy-path/assert-is-string.php create mode 100644 tests/static-analysis/happy-path/assert-not-empty.php create mode 100644 tests/static-analysis/happy-path/assert-not-false.php create mode 100644 tests/static-analysis/happy-path/assert-not-instance-of.php create mode 100644 tests/static-analysis/happy-path/assert-not-null.php create mode 100644 tests/static-analysis/happy-path/assert-not-true.php create mode 100644 tests/static-analysis/happy-path/assert-null.php create mode 100644 tests/static-analysis/happy-path/assert-same.php create mode 100644 tests/static-analysis/happy-path/assert-true.php create mode 100644 tests/static-analysis/happy-path/fail.php diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index 4e30aea8044..32b6a3c7ede 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -820,7 +820,7 @@ public static function assertAttributeNotEquals($expected, string $actualAttribu * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert TEmptyMixed $actual + * @psalm-assert empty $actual */ public static function assertEmpty($actual, string $message = ''): void { @@ -856,7 +856,7 @@ public static function assertAttributeEmpty(string $haystackAttributeName, $hays * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert TNonEmptyMixed $actual + * @psalm-assert !empty $actual */ public static function assertNotEmpty($actual, string $message = ''): void { @@ -1362,7 +1362,7 @@ public static function assertNotFalse($condition, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert TNull $actual + * @psalm-assert null $actual */ public static function assertNull($actual, string $message = ''): void { @@ -1375,7 +1375,7 @@ public static function assertNull($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert !TNull $actual + * @psalm-assert !null $actual */ public static function assertNotNull($actual, string $message = ''): void { @@ -1617,10 +1617,6 @@ public static function assertAttributeSame($expected, string $actualAttributeNam * * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @psalm-template ExpectedType - * @psalm-param ExpectedType $expected - * @psalm-assert !=ExpectedType $actual */ public static function assertNotSame($expected, $actual, string $message = ''): void { @@ -1668,8 +1664,8 @@ public static function assertAttributeNotSame($expected, string $actualAttribute * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception * - * @psalm-template ExpectedType - * @psalm-template-typeof ExpectedType $expected + * @psalm-template ExpectedType of object + * @psalm-param class-string $expected * @psalm-assert ExpectedType $actual */ public static function assertInstanceOf(string $expected, $actual, string $message = ''): void @@ -1715,8 +1711,8 @@ public static function assertAttributeInstanceOf(string $expected, string $attri * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws Exception * - * @psalm-template ExpectedType - * @psalm-template-typeof ExpectedType $expected + * @psalm-template ExpectedType of object + * @psalm-param class-string $expected * @psalm-assert !ExpectedType $actual */ public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void @@ -1908,7 +1904,7 @@ public static function assertIsObject($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert TResource $actual + * @psalm-assert resource $actual */ public static function assertIsResource($actual, string $message = ''): void { @@ -1942,7 +1938,7 @@ public static function assertIsString($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert TScalar $actual + * @psalm-assert scalar $actual */ public static function assertIsScalar($actual, string $message = ''): void { @@ -1976,7 +1972,7 @@ public static function assertIsCallable($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert TIterable $actual + * @psalm-assert iterable $actual */ public static function assertIsIterable($actual, string $message = ''): void { @@ -2117,7 +2113,7 @@ public static function assertIsNotObject($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert !TResource $actual + * @psalm-assert !resource $actual */ public static function assertIsNotResource($actual, string $message = ''): void { @@ -2185,7 +2181,7 @@ public static function assertIsNotCallable($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert !TIterable $actual + * @psalm-assert !iterable $actual */ public static function assertIsNotIterable($actual, string $message = ''): void { @@ -3063,7 +3059,7 @@ public static function countOf(int $count): Count * * @throws AssertionFailedError * - * @psalm-return TNever + * @psalm-return never-return */ public static function fail(string $message = ''): void { diff --git a/tests/static-analysis/happy-path/assert-empty.php b/tests/static-analysis/happy-path/assert-empty.php new file mode 100644 index 00000000000..ab145b56a77 --- /dev/null +++ b/tests/static-analysis/happy-path/assert-empty.php @@ -0,0 +1,13 @@ + Date: Wed, 5 Jun 2019 18:49:03 +0200 Subject: [PATCH 05/18] Added `psalm.xml` configuration file to check the happy-path static scenarios This is a temporary file location, since the project has its own conventions --- psalm.xml | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 psalm.xml diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000000..444d98929bf --- /dev/null +++ b/psalm.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7edbe46c79850f5ebe78b71d647c1d9af2af6f83 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 18:55:23 +0200 Subject: [PATCH 06/18] Added static-analysis tests to CI runs --- .travis.yml | 1 + psalm.xml => tests/static-analysis/config.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename psalm.xml => tests/static-analysis/config.xml (96%) diff --git a/.travis.yml b/.travis.yml index 943fbe1408d..afa5adf6431 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,4 +64,5 @@ jobs: - phpenv config-rm xdebug.ini script: - travis_retry ./tools/composer update $DEFAULT_COMPOSER_FLAGS + - ./tools/psalm --config=tests/static-analysis/config.xml --no-progress - ./tools/psalm --config=.psalm/config.xml --no-progress --shepherd --stats diff --git a/psalm.xml b/tests/static-analysis/config.xml similarity index 96% rename from psalm.xml rename to tests/static-analysis/config.xml index 444d98929bf..feb606690d5 100644 --- a/psalm.xml +++ b/tests/static-analysis/config.xml @@ -6,7 +6,7 @@ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" > - + From ce564bc1ccf1303a2a4646e329a3d612a9ea3881 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 18:56:20 +0200 Subject: [PATCH 07/18] Reverted BC brak: `MockObject#method()` now correctly detected by Psalm Ref: https://github.com/sebastianbergmann/phpunit/pull/3708#discussion_r287614080 Ref: https://github.com/vimeo/psalm/issues/1692 --- src/Framework/MockObject/MockObject.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Framework/MockObject/MockObject.php b/src/Framework/MockObject/MockObject.php index 0a8f30f7162..2df6810753e 100644 --- a/src/Framework/MockObject/MockObject.php +++ b/src/Framework/MockObject/MockObject.php @@ -28,8 +28,4 @@ public function __phpunit_hasMatchers(): bool; public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration): void; public function expects(Invocation $matcher): BuilderInvocationMocker; - - // @TODO get rid of this - required by psalm-only right now - bug in parsing @method? - // @TODO this addition is a BC break and needs to be reverted before merge. - public function method($constraint) : BuilderInvocationMocker; } From a503a310674eb61f8774029879e32c90441ec0b5 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 18:56:59 +0200 Subject: [PATCH 08/18] Removed `vimeo/psalm` from `require-dev`: PHPUnit already has a scoped phar Ref: https://github.com/sebastianbergmann/phpunit/pull/3708#discussion_r287640768 --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 66f0d0211a5..23cf4951054 100644 --- a/composer.json +++ b/composer.json @@ -49,8 +49,7 @@ "sebastian/version": "^2.0.1" }, "require-dev": { - "ext-PDO": "*", - "vimeo/psalm": "dev-master@dev" + "ext-PDO": "*" }, "config": { "platform": { From 4a47a836b2ee3280fbd2f285f8bf7107537eeaf6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 18:59:42 +0200 Subject: [PATCH 09/18] `PropertyNotSetInConstructor` cannot be avoided in `TestCase` child classes Suppressing the error here. --- tests/static-analysis/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/static-analysis/config.xml b/tests/static-analysis/config.xml index feb606690d5..2b464646ad4 100644 --- a/tests/static-analysis/config.xml +++ b/tests/static-analysis/config.xml @@ -34,7 +34,7 @@ - + From 6b892155468d4a8de58b49985fd70b6a24294979 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 19:05:18 +0200 Subject: [PATCH 10/18] Disabling type declarations for trait mocking `TestCase` methods Traits are no real types, therefore we cannot support their usage in templated types. --- src/Framework/TestCase.php | 8 ------- tests/static-analysis/TestUsingMocks.php | 29 ------------------------ 2 files changed, 37 deletions(-) diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index da55c933041..4874ffb8e77 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -1613,10 +1613,6 @@ protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClas * @param bool $cloneArguments * * @throws Exception - * - * @psalm-template RealTraitType of object - * @psalm-param class-string|string $traitName - * @psalm-return MockObject&RealTraitType */ protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject { @@ -1658,10 +1654,6 @@ protected function getMockForTrait($traitName, array $arguments = [], $mockClass * @throws Exception * * @return object - * - * @psalm-template RealTraitType of object - * @psalm-param class-string|string $traitName - * @psalm-return RealTraitType */ protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)/*: object*/ { diff --git a/tests/static-analysis/TestUsingMocks.php b/tests/static-analysis/TestUsingMocks.php index 83724fd56b5..489f4abcc1c 100644 --- a/tests/static-analysis/TestUsingMocks.php +++ b/tests/static-analysis/TestUsingMocks.php @@ -20,14 +20,6 @@ public function sayHello() : string } } -trait HelloWorldTrait -{ - public function sayHello() : string - { - return 'hello world!'; - } -} - /** * @small */ @@ -89,25 +81,4 @@ public function testWillSayHelloThroughGetMockBuilder() : void self::assertSame('hello mock!', $mock->sayHello()); } - - public function testWillSayHelloThroughGetMockForTrait() : void - { - $mock = $this->getMockForTrait(HelloWorldTrait::class); - - $mock - ->method('sayHello') - ->willReturn('hello mock!'); - - self::assertSame('hello mock!', $mock->sayHello()); - } - - public function testWillSayHelloThroughGetObjectForTrait() : void - { - self::assertSame( - 'hello world!', - $this - ->getObjectForTrait(HelloWorldTrait::class) - ->sayHello() - ); - } } From cde4a44f0938cdc159c70b9329c909877cbd2c22 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 19:06:43 +0200 Subject: [PATCH 11/18] Psalm must run against static analysis tests with `totallyTyped="true"` --- tests/static-analysis/config.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/static-analysis/config.xml b/tests/static-analysis/config.xml index 2b464646ad4..cfe79f53dda 100644 --- a/tests/static-analysis/config.xml +++ b/tests/static-analysis/config.xml @@ -1,6 +1,6 @@ - - From 0d46ceb8d6c08db7bb7c9929de479a7edda9ed90 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 19:11:07 +0200 Subject: [PATCH 12/18] Added PHPStorm type meta-hints, allowing downstream intellisense on mocks --- .phpstorm.meta.php | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .phpstorm.meta.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php new file mode 100644 index 00000000000..2e3086cd361 --- /dev/null +++ b/.phpstorm.meta.php @@ -0,0 +1,39 @@ + Date: Wed, 5 Jun 2019 22:37:59 +0200 Subject: [PATCH 13/18] Corrected `Assert::assertIs(Not)Scalar()` type assertions Incorrect docblock format was used, and incorrect return type was used in the test scenario. --- src/Framework/Assert.php | 2 +- tests/static-analysis/happy-path/assert-is-scalar.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index 32b6a3c7ede..dcabf99f61a 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -2147,7 +2147,7 @@ public static function assertIsNotString($actual, string $message = ''): void * @throws ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * - * @psalm-assert !TScalar $actual + * @psalm-assert !scalar $actual */ public static function assertIsNotScalar($actual, string $message = ''): void { diff --git a/tests/static-analysis/happy-path/assert-is-scalar.php b/tests/static-analysis/happy-path/assert-is-scalar.php index ea5ed7dc24e..3c531cf329d 100644 --- a/tests/static-analysis/happy-path/assert-is-scalar.php +++ b/tests/static-analysis/happy-path/assert-is-scalar.php @@ -9,7 +9,7 @@ * * @psalm-return scalar */ -function consume($value) : string +function consume($value) { Assert::assertIsScalar($value); From ed1cc0d22b9412c76055fcb768583a7b4648c87c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 22:44:35 +0200 Subject: [PATCH 14/18] Using objects/integers to verify `assert(Not)Empty` type assertions This is a workaround to not require https://github.com/vimeo/psalm/issues/1743 to be fixed first --- tests/static-analysis/happy-path/assert-empty.php | 6 +++--- tests/static-analysis/happy-path/assert-not-empty.php | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/static-analysis/happy-path/assert-empty.php b/tests/static-analysis/happy-path/assert-empty.php index ab145b56a77..3ba745d422c 100644 --- a/tests/static-analysis/happy-path/assert-empty.php +++ b/tests/static-analysis/happy-path/assert-empty.php @@ -4,10 +4,10 @@ use PHPUnit\Framework\Assert; -/** @return false */ -function consume(string $value) +/** @return null */ +function consume(?object $value) { Assert::assertEmpty($value); - return $value === 'a non-empty string'; + return $value; } diff --git a/tests/static-analysis/happy-path/assert-not-empty.php b/tests/static-analysis/happy-path/assert-not-empty.php index 857f473a9f8..0c6570ad886 100644 --- a/tests/static-analysis/happy-path/assert-not-empty.php +++ b/tests/static-analysis/happy-path/assert-not-empty.php @@ -4,10 +4,9 @@ use PHPUnit\Framework\Assert; -/** @return false */ -function consume(string $value) +function consume(?int $value) : int { Assert::assertNotEmpty($value); - return $value === ''; + return $value; } From a2514b4da9865df8c4c9f811de6ee2d2cb90d378 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 22:55:26 +0200 Subject: [PATCH 15/18] Failures should be allowed on PHP nightly `master` was being used instead, but no such version is present in the build matrix. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index afa5adf6431..5a32eefd22f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ php: matrix: allow_failures: - - php: master + - php: nightly fast_finish: true env: From a967a09dac7cae5455a07db8cede0ce6bccb7bf9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 23:13:49 +0200 Subject: [PATCH 16/18] Corrected type-check errors detected by Psalm run over the whole codebase --- src/Framework/Assert.php | 4 ++++ src/Framework/TestCase.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index dcabf99f61a..f6c60f1c5b9 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -1692,6 +1692,8 @@ public static function assertInstanceOf(string $expected, $actual, string $messa * * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 * @codeCoverageIgnore + * + * @psalm-param class-string $expected */ public static function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void { @@ -1741,6 +1743,8 @@ public static function assertNotInstanceOf(string $expected, $actual, string $me * * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 * @codeCoverageIgnore + * + * @psalm-param class-string $expected */ public static function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void { diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index 4874ffb8e77..840547ad5eb 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -1432,7 +1432,7 @@ protected function createPartialMock($originalClassName, array $methods): MockOb * @throws Exception * * @psalm-template RealInstanceType of object - * @psalm-param class-string|string[] $originalClassName + * @psalm-param class-string $originalClassName * @psalm-return MockObject&RealInstanceType */ protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject @@ -1510,7 +1510,7 @@ protected function getMockClass($originalClassName, $methods = [], array $argume * @throws Exception * * @psalm-template RealInstanceType of object - * @psalm-param class-string|string[] $originalClassName + * @psalm-param class-string $originalClassName * @psalm-return MockObject&RealInstanceType */ protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject From a330d236168a7f7961a1746baf3d536d2f1e0242 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 23:29:18 +0200 Subject: [PATCH 17/18] Excluded static-analysis directory from CS-fixer paths Annotations such as `@return numeric` are not recognized by this tooling, so for now this test suite is off-limits for `php-cs-fixer`. --- .php_cs.dist | 6 +++++- src/Framework/MockObject/MockBuilder.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.php_cs.dist b/.php_cs.dist index 874977a5001..8bf74690805 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -198,7 +198,11 @@ return PhpCsFixer\Config::create() ->files() ->in(__DIR__ . '/build') ->in(__DIR__ . '/src') - ->in(__DIR__ . '/tests') + ->in(__DIR__ . '/tests/_files') + ->in(__DIR__ . '/tests/basic') + ->in(__DIR__ . '/tests/end-to-end') + ->in(__DIR__ . '/tests/fail') + ->in(__DIR__ . '/tests/unit') ->notName('*.phpt') ->notName('ClassWithAllPossibleReturnTypes.php') ); diff --git a/src/Framework/MockObject/MockBuilder.php b/src/Framework/MockObject/MockBuilder.php index c18db2170ac..5b2c6fcd69c 100644 --- a/src/Framework/MockObject/MockBuilder.php +++ b/src/Framework/MockObject/MockBuilder.php @@ -87,7 +87,7 @@ final class MockBuilder private $generator; /** - * @param string[]|string $type + * @param string|string[] $type * * @psalm-param class-string|string|string[] $type */ From c7cfb860b707fb7b742f195c5cab61b811da6cec Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Wed, 5 Jun 2019 23:36:17 +0200 Subject: [PATCH 18/18] `TestCase#expectException()` requires a `class-string<\Throwable>` Ref: https://github.com/sebastianbergmann/phpunit/pull/3708#discussion_r290944243 --- src/Framework/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index 840547ad5eb..140c964284c 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -598,7 +598,7 @@ public function getExpectedExceptionMessageRegExp(): ?string } /** - * @psalm-param class-string<|Throwable> $exception + * @psalm-param class-string<\Throwable> $exception */ public function expectException(string $exception): void {