Updated Rector to commit d9374ddfa6c8a1ae41c1ac271d102980c6eba8f5

d9374ddfa6 [Dep] Move phpstan-phpunit to require-dev (#3734)
This commit is contained in:
Tomas Votruba 2023-05-05 09:34:50 +00:00
parent 1e2a0a6217
commit 61359ad57c
41 changed files with 13 additions and 1941 deletions

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '8b48059c347d98d191478504da236c4c2ba1e667';
public const PACKAGE_VERSION = 'd9374ddfa6c8a1ae41c1ac271d102980c6eba8f5';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-05-05 09:12:04';
public const RELEASE_DATE = '2023-05-05 16:30:49';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita3d85d0220e4ded852f7ff0a91ee1f48::getLoader();
return ComposerAutoloaderInitdef1a712419aaa37ea20f1fb4d4e81bb::getLoader();

View File

@ -80,29 +80,6 @@ return array(
'PHPStan\\PhpDocParser\\Parser\\StringUnescaper' => $vendorDir . '/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php',
'PHPStan\\PhpDocParser\\Parser\\TokenIterator' => $vendorDir . '/phpstan/phpdoc-parser/src/Parser/TokenIterator.php',
'PHPStan\\PhpDocParser\\Parser\\TypeParser' => $vendorDir . '/phpstan/phpdoc-parser/src/Parser/TypeParser.php',
'PHPStan\\PhpDoc\\PHPUnit\\MockObjectTypeNodeResolverExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/PhpDoc/PHPUnit/MockObjectTypeNodeResolverExtension.php',
'PHPStan\\Rules\\PHPUnit\\AnnotationHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AnnotationHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertRuleHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertRuleHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameBooleanExpectedRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameBooleanExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameNullExpectedRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameNullExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameWithCountRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameWithCountRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassCoversExistsRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassMethodCoversExistsRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassMethodCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\CoversHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/CoversHelper.php',
'PHPStan\\Rules\\PHPUnit\\DataProviderDeclarationRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderDeclarationRule.php',
'PHPStan\\Rules\\PHPUnit\\DataProviderHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderHelper.php',
'PHPStan\\Rules\\PHPUnit\\DataProviderHelperFactory' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderHelperFactory.php',
'PHPStan\\Rules\\PHPUnit\\MockMethodCallRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/MockMethodCallRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInClassAnnotationRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInClassAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInMethodAnnotationRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInMethodAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\ShouldCallParentMethodsRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ShouldCallParentMethodsRule.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertFunctionTypeSpecifyingExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertFunctionTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertMethodTypeSpecifyingExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertMethodTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertStaticMethodTypeSpecifyingExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertStaticMethodTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertTypeSpecifyingExtensionHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertTypeSpecifyingExtensionHelper.php',
'PHPStan\\Type\\PHPUnit\\InvocationMockerDynamicReturnTypeExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/InvocationMockerDynamicReturnTypeExtension.php',
'PHPStan\\Type\\PHPUnit\\MockBuilderDynamicReturnTypeExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php',
'PHPStan\\Type\\PHPUnit\\MockObjectDynamicReturnTypeExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/MockObjectDynamicReturnTypeExtension.php',
'PhpParser\\Builder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder.php',
'PhpParser\\BuilderFactory' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php',
'PhpParser\\BuilderHelpers' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php',

View File

@ -48,5 +48,4 @@ return array(
'RectorPrefix202305\\Clue\\React\\NDJson\\' => array($vendorDir . '/clue/ndjson-react/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PHPStan\\PhpDocParser\\' => array($vendorDir . '/phpstan/phpdoc-parser/src'),
'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-phpunit/src'),
);

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita3d85d0220e4ded852f7ff0a91ee1f48
class ComposerAutoloaderInitdef1a712419aaa37ea20f1fb4d4e81bb
{
private static $loader;
@ -22,17 +22,17 @@ class ComposerAutoloaderInita3d85d0220e4ded852f7ff0a91ee1f48
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInita3d85d0220e4ded852f7ff0a91ee1f48', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitdef1a712419aaa37ea20f1fb4d4e81bb', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInita3d85d0220e4ded852f7ff0a91ee1f48', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitdef1a712419aaa37ea20f1fb4d4e81bb', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitdef1a712419aaa37ea20f1fb4d4e81bb::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInitdef1a712419aaa37ea20f1fb4d4e81bb::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48
class ComposerStaticInitdef1a712419aaa37ea20f1fb4d4e81bb
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -70,7 +70,6 @@ class ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48
array (
'PhpParser\\' => 10,
'PHPStan\\PhpDocParser\\' => 21,
'PHPStan\\' => 8,
),
);
@ -246,10 +245,6 @@ class ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48
array (
0 => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src',
),
'PHPStan\\' =>
array (
0 => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src',
),
);
public static $classMap = array (
@ -327,29 +322,6 @@ class ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48
'PHPStan\\PhpDocParser\\Parser\\StringUnescaper' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php',
'PHPStan\\PhpDocParser\\Parser\\TokenIterator' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Parser/TokenIterator.php',
'PHPStan\\PhpDocParser\\Parser\\TypeParser' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Parser/TypeParser.php',
'PHPStan\\PhpDoc\\PHPUnit\\MockObjectTypeNodeResolverExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/PhpDoc/PHPUnit/MockObjectTypeNodeResolverExtension.php',
'PHPStan\\Rules\\PHPUnit\\AnnotationHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AnnotationHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertRuleHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertRuleHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameBooleanExpectedRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameBooleanExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameNullExpectedRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameNullExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameWithCountRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameWithCountRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassCoversExistsRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassMethodCoversExistsRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassMethodCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\CoversHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/CoversHelper.php',
'PHPStan\\Rules\\PHPUnit\\DataProviderDeclarationRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderDeclarationRule.php',
'PHPStan\\Rules\\PHPUnit\\DataProviderHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderHelper.php',
'PHPStan\\Rules\\PHPUnit\\DataProviderHelperFactory' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderHelperFactory.php',
'PHPStan\\Rules\\PHPUnit\\MockMethodCallRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/MockMethodCallRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInClassAnnotationRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInClassAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInMethodAnnotationRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInMethodAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\ShouldCallParentMethodsRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ShouldCallParentMethodsRule.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertFunctionTypeSpecifyingExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertFunctionTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertMethodTypeSpecifyingExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertMethodTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertStaticMethodTypeSpecifyingExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertStaticMethodTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertTypeSpecifyingExtensionHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertTypeSpecifyingExtensionHelper.php',
'PHPStan\\Type\\PHPUnit\\InvocationMockerDynamicReturnTypeExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/InvocationMockerDynamicReturnTypeExtension.php',
'PHPStan\\Type\\PHPUnit\\MockBuilderDynamicReturnTypeExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/MockBuilderDynamicReturnTypeExtension.php',
'PHPStan\\Type\\PHPUnit\\MockObjectDynamicReturnTypeExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/MockObjectDynamicReturnTypeExtension.php',
'PhpParser\\Builder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder.php',
'PhpParser\\BuilderFactory' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php',
'PhpParser\\BuilderHelpers' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php',
@ -3152,9 +3124,9 @@ class ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita3d85d0220e4ded852f7ff0a91ee1f48::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitdef1a712419aaa37ea20f1fb4d4e81bb::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitdef1a712419aaa37ea20f1fb4d4e81bb::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitdef1a712419aaa37ea20f1fb4d4e81bb::$classMap;
}, null, ClassLoader::class);
}

View File

@ -932,61 +932,6 @@
],
"install-path": "..\/phpstan\/phpstan"
},
{
"name": "phpstan\/phpstan-phpunit",
"version": "1.3.11",
"version_normalized": "1.3.11.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/phpstan\/phpstan-phpunit.git",
"reference": "9e1b9de6d260461f6e99b6a8f2dbb0bbb98b579c"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan-phpunit\/zipball\/9e1b9de6d260461f6e99b6a8f2dbb0bbb98b579c",
"reference": "9e1b9de6d260461f6e99b6a8f2dbb0bbb98b579c",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan\/phpstan": "^1.10"
},
"conflict": {
"phpunit\/phpunit": "<7.0"
},
"require-dev": {
"nikic\/php-parser": "^4.13.0",
"php-parallel-lint\/php-parallel-lint": "^1.2",
"phpstan\/phpstan-strict-rules": "^1.0",
"phpunit\/phpunit": "^9.5"
},
"time": "2023-03-25T19:42:13+00:00",
"type": "phpstan-extension",
"extra": {
"phpstan": {
"includes": [
"extension.neon",
"rules.neon"
]
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPStan\\": "src\/"
}
},
"notification-url": "https:\/\/packagist.org\/downloads\/",
"license": [
"MIT"
],
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https:\/\/github.com\/phpstan\/phpstan-phpunit\/issues",
"source": "https:\/\/github.com\/phpstan\/phpstan-phpunit\/tree\/1.3.11"
},
"install-path": "..\/phpstan\/phpstan-phpunit"
},
{
"name": "psr\/cache",
"version": "3.0.0",

File diff suppressed because one or more lines are too long

View File

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2016 Ondřej Mirtes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,96 +0,0 @@
# PHPStan PHPUnit extensions and rules
[![Build](https://github.com/phpstan/phpstan-phpunit/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-phpunit/actions)
[![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-phpunit/v/stable)](https://packagist.org/packages/phpstan/phpstan-phpunit)
[![License](https://poser.pugx.org/phpstan/phpstan-phpunit/license)](https://packagist.org/packages/phpstan/phpstan-phpunit)
* [PHPStan](https://phpstan.org/)
* [PHPUnit](https://phpunit.de)
This extension provides following features:
* `createMock()`, `getMockForAbstractClass()` and `getMockFromWsdl()` methods return an intersection type (see the [detailed explanation of intersection types](https://phpstan.org/blog/union-types-vs-intersection-types)) of the mock object and the mocked class so that both methods from the mock object (like `expects`) and from the mocked class are available on the object.
* `getMock()` called on `MockBuilder` is also supported.
* Interprets `Foo|PHPUnit_Framework_MockObject_MockObject` in phpDoc so that it results in an intersection type instead of a union type.
* Defines early terminating method calls for the `PHPUnit\Framework\TestCase` class to prevent undefined variable errors.
* Specifies types of expressions passed to various `assert` methods like `assertInstanceOf`, `assertTrue`, `assertInternalType` etc.
* Combined with PHPStan's level 4, it points out always-true and always-false asserts like `assertTrue(true)` etc.
It also contains this strict framework-specific rules (can be enabled separately):
* Check that you are not using `assertSame()` with `true` as expected value. `assertTrue()` should be used instead.
* Check that you are not using `assertSame()` with `false` as expected value. `assertFalse()` should be used instead.
* Check that you are not using `assertSame()` with `null` as expected value. `assertNull()` should be used instead.
* Check that you are not using `assertSame()` with `count($variable)` as second parameter. `assertCount($variable)` should be used instead.
## How to document mock objects in phpDocs?
If you need to configure the mock even after you assign it to a property or return it from a method, you should add `PHPUnit_Framework_MockObject_MockObject` to the phpDoc:
```php
/**
* @return Foo&PHPUnit_Framework_MockObject_MockObject
*/
private function createFooMock()
{
return $this->createMock(Foo::class);
}
public function testSomething()
{
$fooMock = $this->createFooMock();
$fooMock->method('doFoo')->will($this->returnValue('test'));
$fooMock->doFoo();
}
```
Please note that the correct syntax for intersection types is `Foo&PHPUnit_Framework_MockObject_MockObject`. `Foo|PHPUnit_Framework_MockObject_MockObject` is also supported, but only for ecosystem and legacy reasons.
If the mock is fully configured and only the methods of the mocked class are supposed to be called on the value, it's fine to typehint only the mocked class:
```php
/** @var Foo */
private $foo;
protected function setUp()
{
$fooMock = $this->createMock(Foo::class);
$fooMock->method('doFoo')->will($this->returnValue('test'));
$this->foo = $fooMock;
}
public function testSomething()
{
$this->foo->doFoo();
// $this->foo->method() and expects() can no longer be called
}
```
## Installation
To use this extension, require it in [Composer](https://getcomposer.org/):
```
composer require --dev phpstan/phpstan-phpunit
```
If you also install [phpstan/extension-installer](https://github.com/phpstan/extension-installer) then you're all set!
<details>
<summary>Manual installation</summary>
If you don't want to use `phpstan/extension-installer`, include extension.neon in your project's PHPStan config:
```
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
```
To perform framework-specific checks, include also this file:
```
- vendor/phpstan/phpstan-phpunit/rules.neon
```
</details>

View File

@ -1,47 +0,0 @@
{
"name": "phpstan\/phpstan-phpunit",
"type": "phpstan-extension",
"description": "PHPUnit extensions and rules for PHPStan",
"license": [
"MIT"
],
"require": {
"php": "^7.2 || ^8.0",
"phpstan\/phpstan": "^1.10"
},
"conflict": {
"phpunit\/phpunit": "<7.0"
},
"require-dev": {
"nikic\/php-parser": "^4.13.0",
"php-parallel-lint\/php-parallel-lint": "^1.2",
"phpstan\/phpstan-strict-rules": "^1.0",
"phpunit\/phpunit": "^9.5"
},
"config": {
"platform": {
"php": "7.4.6"
},
"sort-packages": true
},
"extra": {
"phpstan": {
"includes": [
"extension.neon",
"rules.neon"
]
}
},
"autoload": {
"psr-4": {
"PHPStan\\": "src\/"
}
},
"autoload-dev": {
"classmap": [
"tests\/"
]
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -1,66 +0,0 @@
parameters:
phpunit:
convertUnionToIntersectionType: true
additionalConstructors:
- PHPUnit\Framework\TestCase::setUp
earlyTerminatingMethodCalls:
PHPUnit\Framework\Assert:
- fail
- markTestIncomplete
- markTestSkipped
stubFiles:
- stubs/InvocationMocker.stub
- stubs/MockBuilder.stub
- stubs/MockObject.stub
- stubs/Stub.stub
- stubs/TestCase.stub
exceptions:
uncheckedExceptionRegexes:
- '#^PHPUnit\\#'
- '#^SebastianBergmann\\#'
parametersSchema:
phpunit: structure([
convertUnionToIntersectionType: bool()
])
services:
-
class: PHPStan\PhpDoc\PHPUnit\MockObjectTypeNodeResolverExtension
-
class: PHPStan\Type\PHPUnit\Assert\AssertFunctionTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
-
class: PHPStan\Type\PHPUnit\Assert\AssertMethodTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.methodTypeSpecifyingExtension
-
class: PHPStan\Type\PHPUnit\Assert\AssertStaticMethodTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension
-
class: PHPStan\Type\PHPUnit\InvocationMockerDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Type\PHPUnit\MockBuilderDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Type\PHPUnit\MockObjectDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Rules\PHPUnit\CoversHelper
-
class: PHPStan\Rules\PHPUnit\AnnotationHelper
-
class: PHPStan\Rules\PHPUnit\DataProviderHelper
factory: @PHPStan\Rules\PHPUnit\DataProviderHelperFactory::create()
-
class: PHPStan\Rules\PHPUnit\DataProviderHelperFactory
conditionalTags:
PHPStan\PhpDoc\PHPUnit\MockObjectTypeNodeResolverExtension:
phpstan.phpDoc.typeNodeResolverExtension: %phpunit.convertUnionToIntersectionType%

View File

@ -1,29 +0,0 @@
rules:
- PHPStan\Rules\PHPUnit\AssertSameBooleanExpectedRule
- PHPStan\Rules\PHPUnit\AssertSameNullExpectedRule
- PHPStan\Rules\PHPUnit\AssertSameWithCountRule
- PHPStan\Rules\PHPUnit\MockMethodCallRule
- PHPStan\Rules\PHPUnit\ShouldCallParentMethodsRule
services:
- class: PHPStan\Rules\PHPUnit\ClassCoversExistsRule
- class: PHPStan\Rules\PHPUnit\ClassMethodCoversExistsRule
-
class: PHPStan\Rules\PHPUnit\DataProviderDeclarationRule
arguments:
checkFunctionNameCase: %checkFunctionNameCase%
deprecationRulesInstalled: %deprecationRulesInstalled%
- class: PHPStan\Rules\PHPUnit\NoMissingSpaceInClassAnnotationRule
- class: PHPStan\Rules\PHPUnit\NoMissingSpaceInMethodAnnotationRule
conditionalTags:
PHPStan\Rules\PHPUnit\ClassCoversExistsRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\ClassMethodCoversExistsRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\DataProviderDeclarationRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\NoMissingSpaceInClassAnnotationRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\NoMissingSpaceInMethodAnnotationRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%

View File

@ -1,51 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\PhpDoc\PHPUnit;
use PHPStan\Analyser\NameScope;
use PHPStan\PhpDoc\TypeNodeResolver;
use PHPStan\PhpDoc\TypeNodeResolverAwareExtension;
use PHPStan\PhpDoc\TypeNodeResolverExtension;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function array_key_exists;
use function count;
class MockObjectTypeNodeResolverExtension implements TypeNodeResolverExtension, TypeNodeResolverAwareExtension
{
/** @var TypeNodeResolver */
private $typeNodeResolver;
public function setTypeNodeResolver(TypeNodeResolver $typeNodeResolver) : void
{
$this->typeNodeResolver = $typeNodeResolver;
}
public function getCacheKey() : string
{
return 'phpunit-v1';
}
public function resolve(TypeNode $typeNode, NameScope $nameScope) : ?Type
{
if (!$typeNode instanceof UnionTypeNode) {
return null;
}
static $mockClassNames = ['PHPUnit_Framework_MockObject_MockObject' => \true, 'RectorPrefix202305\\PHPUnit\\Framework\\MockObject\\MockObject' => \true, 'RectorPrefix202305\\PHPUnit\\Framework\\MockObject\\Stub' => \true];
$types = $this->typeNodeResolver->resolveMultiple($typeNode->types, $nameScope);
foreach ($types as $type) {
$classNames = $type->getObjectClassNames();
if (count($classNames) !== 1) {
continue;
}
if (array_key_exists($classNames[0], $mockClassNames)) {
$resultType = TypeCombinator::intersect(...$types);
if ($resultType instanceof NeverType) {
continue;
}
return $resultType;
}
}
return null;
}
}

View File

@ -1,43 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Comment\Doc;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use function array_key_exists;
use function in_array;
use function preg_match;
use function preg_split;
class AnnotationHelper
{
private const ANNOTATIONS_WITH_PARAMS = ['backupGlobals', 'backupStaticAttributes', 'covers', 'coversDefaultClass', 'dataProvider', 'depends', 'group', 'preserveGlobalState', 'requires', 'testDox', 'testWith', 'ticket', 'uses'];
/**
* @return RuleError[] errors
*/
public function processDocComment(Doc $docComment) : array
{
$errors = [];
$docCommentLines = preg_split("/((\r?\n)|(\r\n?))/", $docComment->getText());
if ($docCommentLines === \false) {
return [];
}
foreach ($docCommentLines as $docCommentLine) {
// These annotations can't be retrieved using the getResolvedPhpDoc method on the FileTypeMapper as they are not present when they are invalid
$annotation = preg_match('/(?<annotation>@(?<property>[a-zA-Z]+)(?<whitespace>\\s*)(?<value>.*))/', $docCommentLine, $matches);
if ($annotation === \false) {
continue;
// Line without annotation
}
if (array_key_exists('property', $matches) === \false || array_key_exists('whitespace', $matches) === \false || array_key_exists('annotation', $matches) === \false) {
continue;
}
if (!in_array($matches['property'], self::ANNOTATIONS_WITH_PARAMS, \true) || $matches['whitespace'] !== '') {
continue;
}
$errors[] = RuleErrorBuilder::message('Annotation "' . $matches['annotation'] . '" is invalid, "@' . $matches['property'] . '" should be followed by a space and a value.')->build();
}
return $errors;
}
}

View File

@ -1,40 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Type\ObjectType;
use function in_array;
use function strtolower;
class AssertRuleHelper
{
/**
* @phpstan-assert-if-true Node\Expr\MethodCall|Node\Expr\StaticCall $node
*/
public static function isMethodOrStaticCallOnAssert(Node $node, Scope $scope) : bool
{
$testCaseType = new ObjectType('RectorPrefix202305\\PHPUnit\\Framework\\Assert');
if ($node instanceof Node\Expr\MethodCall) {
$calledOnType = $scope->getType($node->var);
} elseif ($node instanceof Node\Expr\StaticCall) {
if ($node->class instanceof Node\Name) {
$class = (string) $node->class;
if ($scope->isInClass() && in_array(strtolower($class), ['self', 'static', 'parent'], \true)) {
$calledOnType = new ObjectType($scope->getClassReflection()->getName());
} else {
$calledOnType = new ObjectType($class);
}
} else {
$calledOnType = $scope->getType($node->class);
}
} else {
return \false;
}
if (!$testCaseType->isSuperTypeOf($calledOnType)->yes()) {
return \false;
}
return \true;
}
}

View File

@ -1,44 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\NodeAbstract;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use function count;
/**
* @implements Rule<NodeAbstract>
*/
class AssertSameBooleanExpectedRule implements Rule
{
public function getNodeType() : string
{
return NodeAbstract::class;
}
public function processNode(Node $node, Scope $scope) : array
{
if (!\PHPStan\Rules\PHPUnit\AssertRuleHelper::isMethodOrStaticCallOnAssert($node, $scope)) {
return [];
}
if (count($node->getArgs()) < 2) {
return [];
}
if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== 'assertsame') {
return [];
}
$expectedArgumentValue = $node->getArgs()[0]->value;
if (!$expectedArgumentValue instanceof ConstFetch) {
return [];
}
if ($expectedArgumentValue->name->toLowerString() === 'true') {
return ['You should use assertTrue() instead of assertSame() when expecting "true"'];
}
if ($expectedArgumentValue->name->toLowerString() === 'false') {
return ['You should use assertFalse() instead of assertSame() when expecting "false"'];
}
return [];
}
}

View File

@ -1,41 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\NodeAbstract;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use function count;
/**
* @implements Rule<NodeAbstract>
*/
class AssertSameNullExpectedRule implements Rule
{
public function getNodeType() : string
{
return NodeAbstract::class;
}
public function processNode(Node $node, Scope $scope) : array
{
if (!\PHPStan\Rules\PHPUnit\AssertRuleHelper::isMethodOrStaticCallOnAssert($node, $scope)) {
return [];
}
if (count($node->getArgs()) < 2) {
return [];
}
if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== 'assertsame') {
return [];
}
$expectedArgumentValue = $node->getArgs()[0]->value;
if (!$expectedArgumentValue instanceof ConstFetch) {
return [];
}
if ($expectedArgumentValue->name->toLowerString() === 'null') {
return ['You should use assertNull() instead of assertSame(null, $actual).'];
}
return [];
}
}

View File

@ -1,45 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use Countable;
use PhpParser\Node;
use PhpParser\NodeAbstract;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\ObjectType;
use function count;
/**
* @implements Rule<NodeAbstract>
*/
class AssertSameWithCountRule implements Rule
{
public function getNodeType() : string
{
return NodeAbstract::class;
}
public function processNode(Node $node, Scope $scope) : array
{
if (!\PHPStan\Rules\PHPUnit\AssertRuleHelper::isMethodOrStaticCallOnAssert($node, $scope)) {
return [];
}
if (count($node->getArgs()) < 2) {
return [];
}
if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== 'assertsame') {
return [];
}
$right = $node->getArgs()[1]->value;
if ($right instanceof Node\Expr\FuncCall && $right->name instanceof Node\Name && $right->name->toLowerString() === 'count') {
return ['You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, count($variable)).'];
}
if ($right instanceof Node\Expr\MethodCall && $right->name instanceof Node\Identifier && $right->name->toLowerString() === 'count' && count($right->getArgs()) === 0) {
$type = $scope->getType($right->var);
if ((new ObjectType(Countable::class))->isSuperTypeOf($type)->yes()) {
return ['You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, $variable->count()).'];
}
}
return [];
}
}

View File

@ -1,68 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPUnit\Framework\TestCase;
use function array_merge;
use function array_shift;
use function count;
use function sprintf;
/**
* @implements Rule<InClassNode>
*/
class ClassCoversExistsRule implements Rule
{
/**
* Covers helper.
*
* @var CoversHelper
*/
private $coversHelper;
/**
* Reflection provider.
*
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(\PHPStan\Rules\PHPUnit\CoversHelper $coversHelper, ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
$this->coversHelper = $coversHelper;
}
public function getNodeType() : string
{
return InClassNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $node->getClassReflection();
if (!$classReflection->isSubclassOf(TestCase::class)) {
return [];
}
$errors = [];
$classPhpDoc = $classReflection->getResolvedPhpDoc();
[$classCovers, $classCoversDefaultClasses] = $this->coversHelper->getCoverAnnotations($classPhpDoc);
if (count($classCoversDefaultClasses) >= 2) {
$errors[] = RuleErrorBuilder::message(sprintf('@coversDefaultClass is defined multiple times.'))->build();
return $errors;
}
$coversDefaultClass = array_shift($classCoversDefaultClasses);
if ($coversDefaultClass !== null) {
$className = (string) $coversDefaultClass->value;
if (!$this->reflectionProvider->hasClass($className)) {
$errors[] = RuleErrorBuilder::message(sprintf('@coversDefaultClass references an invalid class %s.', $className))->build();
}
}
foreach ($classCovers as $covers) {
$errors = array_merge($errors, $this->coversHelper->processCovers($node, $covers, null));
}
return $errors;
}
}

View File

@ -1,79 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\FileTypeMapper;
use PHPUnit\Framework\TestCase;
use function array_map;
use function array_merge;
use function array_shift;
use function count;
use function in_array;
use function sprintf;
/**
* @implements Rule<Node\Stmt\ClassMethod>
*/
class ClassMethodCoversExistsRule implements Rule
{
/**
* Covers helper.
*
* @var CoversHelper
*/
private $coversHelper;
/**
* The file type mapper.
*
* @var FileTypeMapper
*/
private $fileTypeMapper;
public function __construct(\PHPStan\Rules\PHPUnit\CoversHelper $coversHelper, FileTypeMapper $fileTypeMapper)
{
$this->coversHelper = $coversHelper;
$this->fileTypeMapper = $fileTypeMapper;
}
public function getNodeType() : string
{
return Node\Stmt\ClassMethod::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
return [];
}
if (!$classReflection->isSubclassOf(TestCase::class)) {
return [];
}
$errors = [];
$classPhpDoc = $classReflection->getResolvedPhpDoc();
[$classCovers, $classCoversDefaultClasses] = $this->coversHelper->getCoverAnnotations($classPhpDoc);
$classCoversStrings = array_map(static function (PhpDocTagNode $covers) : string {
return (string) $covers->value;
}, $classCovers);
$docComment = $node->getDocComment();
if ($docComment === null) {
return [];
}
$coversDefaultClass = count($classCoversDefaultClasses) === 1 ? array_shift($classCoversDefaultClasses) : null;
$methodPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($scope->getFile(), $classReflection->getName(), $scope->isInTrait() ? $scope->getTraitReflection()->getName() : null, $node->name->toString(), $docComment->getText());
[$methodCovers, $methodCoversDefaultClasses] = $this->coversHelper->getCoverAnnotations($methodPhpDoc);
$errors = [];
if (count($methodCoversDefaultClasses) > 0) {
$errors[] = RuleErrorBuilder::message(sprintf('@coversDefaultClass defined on class method %s.', $node->name))->build();
}
foreach ($methodCovers as $covers) {
if (in_array((string) $covers->value, $classCoversStrings, \true)) {
$errors[] = RuleErrorBuilder::message(sprintf('Class already @covers %s so the method @covers is redundant.', $covers->value))->build();
}
$errors = array_merge($errors, $this->coversHelper->processCovers($node, $covers, $coversDefaultClass));
}
return $errors;
}
}

View File

@ -1,91 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PhpParser\Node\Name;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use function array_merge;
use function explode;
use function sprintf;
use function strpos;
class CoversHelper
{
/**
* Reflection provider.
*
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
/**
* Gathers @covers and @coversDefaultClass annotations from phpdocs.
*
* @return array{PhpDocTagNode[], PhpDocTagNode[]}
*/
public function getCoverAnnotations(?ResolvedPhpDocBlock $phpDoc) : array
{
if ($phpDoc === null) {
return [[], []];
}
$phpDocNodes = $phpDoc->getPhpDocNodes();
$covers = [];
$coversDefaultClasses = [];
foreach ($phpDocNodes as $docNode) {
$covers = array_merge($covers, $docNode->getTagsByName('@covers'));
$coversDefaultClasses = array_merge($coversDefaultClasses, $docNode->getTagsByName('@coversDefaultClass'));
}
return [$covers, $coversDefaultClasses];
}
/**
* @return RuleError[] errors
*/
public function processCovers(Node $node, PhpDocTagNode $phpDocTag, ?PhpDocTagNode $coversDefaultClass) : array
{
$errors = [];
$covers = (string) $phpDocTag->value;
if ($covers === '') {
$errors[] = RuleErrorBuilder::message('@covers value does not specify anything.')->build();
return $errors;
}
$isMethod = strpos($covers, '::') !== \false;
$fullName = $covers;
if ($isMethod) {
[$className, $method] = explode('::', $covers);
} else {
$className = $covers;
}
if ($className === '' && $node instanceof Node\Stmt\ClassMethod && $coversDefaultClass !== null) {
$className = (string) $coversDefaultClass->value;
$fullName = $className . $covers;
}
if ($this->reflectionProvider->hasClass($className)) {
$class = $this->reflectionProvider->getClass($className);
if ($class->isInterface()) {
$errors[] = RuleErrorBuilder::message(sprintf('@covers value %s references an interface.', $fullName))->build();
}
if (isset($method) && $method !== '' && !$class->hasMethod($method)) {
$errors[] = RuleErrorBuilder::message(sprintf('@covers value %s references an invalid method.', $fullName))->build();
}
} elseif (isset($method) && $this->reflectionProvider->hasFunction(new Name($method, []), null)) {
return $errors;
} elseif (!isset($method) && $this->reflectionProvider->hasFunction(new Name($className, []), null)) {
return $errors;
} else {
$error = RuleErrorBuilder::message(sprintf('@covers value %s references an invalid %s.', $fullName, $isMethod ? 'method' : 'class or function'));
if (strpos($className, '\\') === \false) {
$error->tip('The @covers annotation requires a fully qualified name.');
}
$errors[] = $error->build();
}
return $errors;
}
}

View File

@ -1,56 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPUnit\Framework\TestCase;
use function array_merge;
/**
* @implements Rule<Node\Stmt\ClassMethod>
*/
class DataProviderDeclarationRule implements Rule
{
/**
* Data provider helper.
*
* @var DataProviderHelper
*/
private $dataProviderHelper;
/**
* When set to true, it reports data provider method with incorrect name case.
*
* @var bool
*/
private $checkFunctionNameCase;
/**
* When phpstan-deprecation-rules is installed, it reports deprecated usages.
*
* @var bool
*/
private $deprecationRulesInstalled;
public function __construct(\PHPStan\Rules\PHPUnit\DataProviderHelper $dataProviderHelper, bool $checkFunctionNameCase, bool $deprecationRulesInstalled)
{
$this->dataProviderHelper = $dataProviderHelper;
$this->checkFunctionNameCase = $checkFunctionNameCase;
$this->deprecationRulesInstalled = $deprecationRulesInstalled;
}
public function getNodeType() : string
{
return Node\Stmt\ClassMethod::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || !$classReflection->isSubclassOf(TestCase::class)) {
return [];
}
$errors = [];
foreach ($this->dataProviderHelper->getDataProviderMethods($scope, $node, $classReflection) as $dataProviderValue => [$dataProviderClassReflection, $dataProviderMethodName, $lineNumber]) {
$errors = array_merge($errors, $this->dataProviderHelper->processDataProvider($dataProviderValue, $dataProviderClassReflection, $dataProviderMethodName, $lineNumber, $this->checkFunctionNameCase, $this->deprecationRulesInstalled));
}
return $errors;
}
}

View File

@ -1,188 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node\Attribute;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MissingMethodFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\FileTypeMapper;
use function array_merge;
use function count;
use function explode;
use function preg_match;
use function sprintf;
class DataProviderHelper
{
/**
* Reflection provider.
*
* @var ReflectionProvider
*/
private $reflectionProvider;
/**
* The file type mapper.
*
* @var FileTypeMapper
*/
private $fileTypeMapper;
/** @var bool */
private $phpunit10OrNewer;
public function __construct(ReflectionProvider $reflectionProvider, FileTypeMapper $fileTypeMapper, bool $phpunit10OrNewer)
{
$this->reflectionProvider = $reflectionProvider;
$this->fileTypeMapper = $fileTypeMapper;
$this->phpunit10OrNewer = $phpunit10OrNewer;
}
/**
* @return iterable<array{ClassReflection|null, string, int}>
*/
public function getDataProviderMethods(Scope $scope, ClassMethod $node, ClassReflection $classReflection) : iterable
{
$docComment = $node->getDocComment();
if ($docComment !== null) {
$methodPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($scope->getFile(), $classReflection->getName(), $scope->isInTrait() ? $scope->getTraitReflection()->getName() : null, $node->name->toString(), $docComment->getText());
foreach ($this->getDataProviderAnnotations($methodPhpDoc) as $annotation) {
$dataProviderValue = $this->getDataProviderAnnotationValue($annotation);
if ($dataProviderValue === null) {
// Missing value is already handled in NoMissingSpaceInMethodAnnotationRule
continue;
}
$dataProviderMethod = $this->parseDataProviderAnnotationValue($scope, $dataProviderValue);
$dataProviderMethod[] = $node->getLine();
(yield $dataProviderValue => $dataProviderMethod);
}
}
if (!$this->phpunit10OrNewer) {
return;
}
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
$dataProviderMethod = null;
if ($attr->name->toLowerString() === 'phpunit\\framework\\attributes\\dataprovider') {
$dataProviderMethod = $this->parseDataProviderAttribute($attr, $classReflection);
} elseif ($attr->name->toLowerString() === 'phpunit\\framework\\attributes\\dataproviderexternal') {
$dataProviderMethod = $this->parseDataProviderExternalAttribute($attr);
}
if ($dataProviderMethod === null) {
continue;
}
yield from $dataProviderMethod;
}
}
}
/**
* @return array<PhpDocTagNode>
*/
private function getDataProviderAnnotations(?ResolvedPhpDocBlock $phpDoc) : array
{
if ($phpDoc === null) {
return [];
}
$phpDocNodes = $phpDoc->getPhpDocNodes();
$annotations = [];
foreach ($phpDocNodes as $docNode) {
$annotations = array_merge($annotations, $docNode->getTagsByName('@dataProvider'));
}
return $annotations;
}
/**
* @return RuleError[] errors
*/
public function processDataProvider(string $dataProviderValue, ?ClassReflection $classReflection, string $methodName, int $lineNumber, bool $checkFunctionNameCase, bool $deprecationRulesInstalled) : array
{
if ($classReflection === null) {
$error = RuleErrorBuilder::message(sprintf('@dataProvider %s related class not found.', $dataProviderValue))->line($lineNumber)->build();
return [$error];
}
try {
$dataProviderMethodReflection = $classReflection->getNativeMethod($methodName);
} catch (MissingMethodFromReflectionException $missingMethodFromReflectionException) {
$error = RuleErrorBuilder::message(sprintf('@dataProvider %s related method not found.', $dataProviderValue))->line($lineNumber)->build();
return [$error];
}
$errors = [];
if ($checkFunctionNameCase && $methodName !== $dataProviderMethodReflection->getName()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method is used with incorrect case: %s.', $dataProviderValue, $dataProviderMethodReflection->getName()))->line($lineNumber)->build();
}
if (!$dataProviderMethodReflection->isPublic()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be public.', $dataProviderValue))->line($lineNumber)->build();
}
if ($deprecationRulesInstalled && $this->phpunit10OrNewer && !$dataProviderMethodReflection->isStatic()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be static in PHPUnit 10 and newer.', $dataProviderValue))->line($lineNumber)->build();
}
return $errors;
}
private function getDataProviderAnnotationValue(PhpDocTagNode $phpDocTag) : ?string
{
if (preg_match('/^[^ \\t]+/', (string) $phpDocTag->value, $matches) !== 1) {
return null;
}
return $matches[0];
}
/**
* @return array{ClassReflection|null, string}
*/
private function parseDataProviderAnnotationValue(Scope $scope, string $dataProviderValue) : array
{
$parts = explode('::', $dataProviderValue, 2);
if (count($parts) <= 1) {
return [$scope->getClassReflection(), $dataProviderValue];
}
if ($this->reflectionProvider->hasClass($parts[0])) {
return [$this->reflectionProvider->getClass($parts[0]), $parts[1]];
}
return [null, $dataProviderValue];
}
/**
* @return array<string, array{(ClassReflection|null), string, int}>|null
*/
private function parseDataProviderExternalAttribute(Attribute $attribute) : ?array
{
if (count($attribute->args) !== 2) {
return null;
}
$methodNameArg = $attribute->args[1]->value;
if (!$methodNameArg instanceof String_) {
return null;
}
$classNameArg = $attribute->args[0]->value;
if ($classNameArg instanceof ClassConstFetch && $classNameArg->class instanceof Name) {
$className = $classNameArg->class->toString();
} elseif ($classNameArg instanceof String_) {
$className = $classNameArg->value;
} else {
return null;
}
$dataProviderClassReflection = null;
if ($this->reflectionProvider->hasClass($className)) {
$dataProviderClassReflection = $this->reflectionProvider->getClass($className);
$className = $dataProviderClassReflection->getName();
}
return [sprintf('%s::%s', $className, $methodNameArg->value) => [$dataProviderClassReflection, $methodNameArg->value, $attribute->getLine()]];
}
/**
* @return array<string, array{(ClassReflection|null), string, int}>|null
*/
private function parseDataProviderAttribute(Attribute $attribute, ClassReflection $classReflection) : ?array
{
if (count($attribute->args) !== 1) {
return null;
}
$methodNameArg = $attribute->args[0]->value;
if (!$methodNameArg instanceof String_) {
return null;
}
return [$methodNameArg->value => [$classReflection, $methodNameArg->value, $attribute->getLine()]];
}
}

View File

@ -1,51 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\FileTypeMapper;
use PHPUnit\Framework\TestCase;
use function dirname;
use function explode;
use function file_get_contents;
use function is_file;
use function json_decode;
class DataProviderHelperFactory
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var FileTypeMapper */
private $fileTypeMapper;
public function __construct(ReflectionProvider $reflectionProvider, FileTypeMapper $fileTypeMapper)
{
$this->reflectionProvider = $reflectionProvider;
$this->fileTypeMapper = $fileTypeMapper;
}
public function create() : \PHPStan\Rules\PHPUnit\DataProviderHelper
{
$phpUnit10OrNewer = \false;
if ($this->reflectionProvider->hasClass(TestCase::class)) {
$testCase = $this->reflectionProvider->getClass(TestCase::class);
$file = $testCase->getFileName();
if ($file !== null) {
$phpUnitRoot = dirname($file, 3);
$phpUnitComposer = $phpUnitRoot . '/composer.json';
if (is_file($phpUnitComposer)) {
$composerJson = @file_get_contents($phpUnitComposer);
if ($composerJson !== \false) {
$json = json_decode($composerJson, \true);
$version = $json['extra']['branch-alias']['dev-main'] ?? null;
if ($version !== null) {
$majorVersion = (int) explode('.', $version)[0];
if ($majorVersion >= 10) {
$phpUnit10OrNewer = \true;
}
}
}
}
}
}
return new \PHPStan\Rules\PHPUnit\DataProviderHelper($this->reflectionProvider, $this->fileTypeMapper, $phpUnit10OrNewer);
}
}

View File

@ -1,67 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use RectorPrefix202305\PHPUnit\Framework\MockObject\Builder\InvocationMocker;
use RectorPrefix202305\PHPUnit\Framework\MockObject\MockObject;
use RectorPrefix202305\PHPUnit\Framework\MockObject\Stub;
use function array_filter;
use function count;
use function implode;
use function in_array;
use function sprintf;
/**
* @implements Rule<MethodCall>
*/
class MockMethodCallRule implements Rule
{
public function getNodeType() : string
{
return Node\Expr\MethodCall::class;
}
public function processNode(Node $node, Scope $scope) : array
{
/** @var Node\Expr\MethodCall $node */
$node = $node;
if (!$node->name instanceof Node\Identifier || $node->name->name !== 'method') {
return [];
}
if (count($node->getArgs()) < 1) {
return [];
}
$argType = $scope->getType($node->getArgs()[0]->value);
if (count($argType->getConstantStrings()) === 0) {
return [];
}
$errors = [];
foreach ($argType->getConstantStrings() as $constantString) {
$method = $constantString->getValue();
$type = $scope->getType($node->var);
if ((in_array(MockObject::class, $type->getObjectClassNames(), \true) || in_array(Stub::class, $type->getObjectClassNames(), \true)) && !$type->hasMethod($method)->yes()) {
$mockClasses = array_filter($type->getObjectClassNames(), static function (string $class) : bool {
return $class !== MockObject::class && $class !== Stub::class;
});
if (count($mockClasses) === 0) {
continue;
}
$errors[] = sprintf('Trying to mock an undefined method %s() on class %s.', $method, implode('&', $mockClasses));
continue;
}
$mockedClassObject = $type->getTemplateType(InvocationMocker::class, 'TMockedClass');
if ($mockedClassObject->hasMethod($method)->yes()) {
continue;
}
$classNames = $mockedClassObject->getObjectClassNames();
if (count($classNames) === 0) {
continue;
}
$errors[] = sprintf('Trying to mock an undefined method %s() on class %s.', $method, implode('|', $classNames));
}
return $errors;
}
}

View File

@ -1,42 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\Rule;
use PHPUnit\Framework\TestCase;
/**
* @implements Rule<InClassNode>
*/
class NoMissingSpaceInClassAnnotationRule implements Rule
{
/**
* Covers helper.
*
* @var AnnotationHelper
*/
private $annotationHelper;
public function __construct(\PHPStan\Rules\PHPUnit\AnnotationHelper $annotationHelper)
{
$this->annotationHelper = $annotationHelper;
}
public function getNodeType() : string
{
return InClassNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || $classReflection->isSubclassOf(TestCase::class) === \false) {
return [];
}
$docComment = $node->getDocComment();
if ($docComment === null) {
return [];
}
return $this->annotationHelper->processDocComment($docComment);
}
}

View File

@ -1,42 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Rules\Rule;
use PHPUnit\Framework\TestCase;
/**
* @implements Rule<InClassMethodNode>
*/
class NoMissingSpaceInMethodAnnotationRule implements Rule
{
/**
* Covers helper.
*
* @var AnnotationHelper
*/
private $annotationHelper;
public function __construct(\PHPStan\Rules\PHPUnit\AnnotationHelper $annotationHelper)
{
$this->annotationHelper = $annotationHelper;
}
public function getNodeType() : string
{
return InClassMethodNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || $classReflection->isSubclassOf(TestCase::class) === \false) {
return [];
}
$docComment = $node->getDocComment();
if ($docComment === null) {
return [];
}
return $this->annotationHelper->processDocComment($docComment);
}
}

View File

@ -1,85 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPUnit\Framework\TestCase;
use function in_array;
use function sprintf;
use function strtolower;
/**
* @implements Rule<InClassMethodNode>
*/
class ShouldCallParentMethodsRule implements Rule
{
public function getNodeType() : string
{
return InClassMethodNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$methodName = $node->getOriginalNode()->name->name;
if (!in_array(strtolower($methodName), ['setup', 'teardown'], \true)) {
return [];
}
if ($scope->getClassReflection() === null) {
return [];
}
if (!$scope->getClassReflection()->isSubclassOf(TestCase::class)) {
return [];
}
$parentClass = $scope->getClassReflection()->getParentClass();
if ($parentClass === null) {
return [];
}
if (!$parentClass->hasNativeMethod($methodName)) {
return [];
}
$parentMethod = $parentClass->getNativeMethod($methodName);
if ($parentMethod->getDeclaringClass()->getName() === TestCase::class) {
return [];
}
$hasParentCall = $this->hasParentClassCall($node->getOriginalNode()->getStmts(), strtolower($methodName));
if (!$hasParentCall) {
return [RuleErrorBuilder::message(sprintf('Missing call to parent::%s() method.', $methodName))->build()];
}
return [];
}
/**
* @param Node\Stmt[]|null $stmts
*
*/
private function hasParentClassCall(?array $stmts, string $methodName) : bool
{
if ($stmts === null) {
return \false;
}
foreach ($stmts as $stmt) {
if (!$stmt instanceof Node\Stmt\Expression) {
continue;
}
if (!$stmt->expr instanceof Node\Expr\StaticCall) {
continue;
}
if (!$stmt->expr->class instanceof Node\Name) {
continue;
}
$class = (string) $stmt->expr->class;
if (strtolower($class) !== 'parent') {
continue;
}
if (!$stmt->expr->name instanceof Node\Identifier) {
continue;
}
if ($stmt->expr->name->toLowerString() === $methodName) {
return \true;
}
}
return \false;
}
}

View File

@ -1,41 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit\Assert;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use function strlen;
use function strpos;
use function substr;
class AssertFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
/** @var TypeSpecifier */
private $typeSpecifier;
public function setTypeSpecifier(TypeSpecifier $typeSpecifier) : void
{
$this->typeSpecifier = $typeSpecifier;
}
public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context) : bool
{
return \PHPStan\Type\PHPUnit\Assert\AssertTypeSpecifyingExtensionHelper::isSupported($this->trimName($functionReflection->getName()), $node->getArgs());
}
public function specifyTypes(FunctionReflection $functionReflection, FuncCall $node, Scope $scope, TypeSpecifierContext $context) : SpecifiedTypes
{
return \PHPStan\Type\PHPUnit\Assert\AssertTypeSpecifyingExtensionHelper::specifyTypes($this->typeSpecifier, $scope, $this->trimName($functionReflection->getName()), $node->getArgs());
}
private function trimName(string $functionName) : string
{
$prefix = 'PHPUnit\\Framework\\';
if (strpos($functionName, $prefix) === 0) {
return substr($functionName, strlen($prefix));
}
return $functionName;
}
}

View File

@ -1,34 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit\Assert;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\MethodTypeSpecifyingExtension;
class AssertMethodTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
/** @var TypeSpecifier */
private $typeSpecifier;
public function setTypeSpecifier(TypeSpecifier $typeSpecifier) : void
{
$this->typeSpecifier = $typeSpecifier;
}
public function getClass() : string
{
return 'RectorPrefix202305\\PHPUnit\\Framework\\Assert';
}
public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context) : bool
{
return \PHPStan\Type\PHPUnit\Assert\AssertTypeSpecifyingExtensionHelper::isSupported($methodReflection->getName(), $node->getArgs());
}
public function specifyTypes(MethodReflection $functionReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context) : SpecifiedTypes
{
return \PHPStan\Type\PHPUnit\Assert\AssertTypeSpecifyingExtensionHelper::specifyTypes($this->typeSpecifier, $scope, $functionReflection->getName(), $node->getArgs());
}
}

View File

@ -1,34 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit\Assert;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\StaticMethodTypeSpecifyingExtension;
class AssertStaticMethodTypeSpecifyingExtension implements StaticMethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
/** @var TypeSpecifier */
private $typeSpecifier;
public function setTypeSpecifier(TypeSpecifier $typeSpecifier) : void
{
$this->typeSpecifier = $typeSpecifier;
}
public function getClass() : string
{
return 'RectorPrefix202305\\PHPUnit\\Framework\\Assert';
}
public function isStaticMethodSupported(MethodReflection $methodReflection, StaticCall $node, TypeSpecifierContext $context) : bool
{
return \PHPStan\Type\PHPUnit\Assert\AssertTypeSpecifyingExtensionHelper::isSupported($methodReflection->getName(), $node->getArgs());
}
public function specifyTypes(MethodReflection $functionReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context) : SpecifiedTypes
{
return \PHPStan\Type\PHPUnit\Assert\AssertTypeSpecifyingExtensionHelper::specifyTypes($this->typeSpecifier, $scope, $functionReflection->getName(), $node->getArgs());
}
}

View File

@ -1,188 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit\Assert;
use Closure;
use Countable;
use EmptyIterator;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\LNumber;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierContext;
use ReflectionObject;
use function array_key_exists;
use function count;
use function strlen;
use function strpos;
use function substr;
class AssertTypeSpecifyingExtensionHelper
{
/** @var Closure[] */
private static $resolvers;
/**
* @param Arg[] $args
*/
public static function isSupported(string $name, array $args) : bool
{
$trimmedName = self::trimName($name);
$resolvers = self::getExpressionResolvers();
if (!array_key_exists($trimmedName, $resolvers)) {
return \false;
}
$resolver = $resolvers[$trimmedName];
$resolverReflection = new ReflectionObject($resolver);
return count($args) >= count($resolverReflection->getMethod('__invoke')->getParameters()) - 1;
}
private static function trimName(string $name) : string
{
if (strpos($name, 'assert') !== 0) {
return $name;
}
$name = substr($name, strlen('assert'));
if (strpos($name, 'Not') === 0) {
return substr($name, 3);
}
if (strpos($name, 'IsNot') === 0) {
return 'Is' . substr($name, 5);
}
return $name;
}
/**
* @param Arg[] $args $args
*/
public static function specifyTypes(TypeSpecifier $typeSpecifier, Scope $scope, string $name, array $args) : SpecifiedTypes
{
$expression = self::createExpression($scope, $name, $args);
if ($expression === null) {
return new SpecifiedTypes([], []);
}
return $typeSpecifier->specifyTypesInCondition($scope, $expression, TypeSpecifierContext::createTruthy());
}
/**
* @param Arg[] $args
*/
private static function createExpression(Scope $scope, string $name, array $args) : ?Expr
{
$trimmedName = self::trimName($name);
$resolvers = self::getExpressionResolvers();
$resolver = $resolvers[$trimmedName];
$expression = $resolver($scope, ...$args);
if ($expression === null) {
return null;
}
if (strpos($name, 'Not') !== \false) {
$expression = new BooleanNot($expression);
}
return $expression;
}
/**
* @return Closure[]
*/
private static function getExpressionResolvers() : array
{
if (self::$resolvers === null) {
self::$resolvers = ['InstanceOf' => static function (Scope $scope, Arg $class, Arg $object) : ?Instanceof_ {
$classType = $scope->getType($class->value);
$classNames = $classType->getConstantStrings();
if (count($classNames) !== 1) {
return null;
}
return new Instanceof_($object->value, new Name($classNames[0]->getValue()));
}, 'Same' => static function (Scope $scope, Arg $expected, Arg $actual) : Identical {
return new Identical($expected->value, $actual->value);
}, 'True' => static function (Scope $scope, Arg $actual) : Identical {
return new Identical($actual->value, new ConstFetch(new Name('true')));
}, 'False' => static function (Scope $scope, Arg $actual) : Identical {
return new Identical($actual->value, new ConstFetch(new Name('false')));
}, 'Null' => static function (Scope $scope, Arg $actual) : Identical {
return new Identical($actual->value, new ConstFetch(new Name('null')));
}, 'Empty' => static function (Scope $scope, Arg $actual) : Expr\BinaryOp\BooleanOr {
return new Expr\BinaryOp\BooleanOr(new Instanceof_($actual->value, new Name(EmptyIterator::class)), new Expr\BinaryOp\BooleanOr(new Expr\BinaryOp\BooleanAnd(new Instanceof_($actual->value, new Name(Countable::class)), new Identical(new FuncCall(new Name('count'), [new Arg($actual->value)]), new LNumber(0))), new Expr\Empty_($actual->value)));
}, 'IsArray' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_array'), [$actual]);
}, 'IsBool' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_bool'), [$actual]);
}, 'IsCallable' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_callable'), [$actual]);
}, 'IsFloat' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_float'), [$actual]);
}, 'IsInt' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_int'), [$actual]);
}, 'IsIterable' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_iterable'), [$actual]);
}, 'IsNumeric' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_numeric'), [$actual]);
}, 'IsObject' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_object'), [$actual]);
}, 'IsResource' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_resource'), [$actual]);
}, 'IsString' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_string'), [$actual]);
}, 'IsScalar' => static function (Scope $scope, Arg $actual) : FuncCall {
return new FuncCall(new Name('is_scalar'), [$actual]);
}, 'InternalType' => static function (Scope $scope, Arg $type, Arg $value) : ?FuncCall {
$typeNames = $scope->getType($type->value)->getConstantStrings();
if (count($typeNames) !== 1) {
return null;
}
switch ($typeNames[0]->getValue()) {
case 'numeric':
$functionName = 'is_numeric';
break;
case 'integer':
case 'int':
$functionName = 'is_int';
break;
case 'double':
case 'float':
case 'real':
$functionName = 'is_float';
break;
case 'string':
$functionName = 'is_string';
break;
case 'boolean':
case 'bool':
$functionName = 'is_bool';
break;
case 'scalar':
$functionName = 'is_scalar';
break;
case 'null':
$functionName = 'is_null';
break;
case 'array':
$functionName = 'is_array';
break;
case 'object':
$functionName = 'is_object';
break;
case 'resource':
$functionName = 'is_resource';
break;
case 'callable':
$functionName = 'is_callable';
break;
default:
return null;
}
return new FuncCall(new Name($functionName), [$value]);
}, 'ArrayHasKey' => static function (Scope $scope, Arg $key, Arg $array) : FuncCall {
return new FuncCall(new Name('array_key_exists'), [$key, $array]);
}, 'ObjectHasAttribute' => static function (Scope $scope, Arg $property, Arg $object) : FuncCall {
return new FuncCall(new Name('property_exists'), [$object, $property]);
}];
}
return self::$resolvers;
}
}

View File

@ -1,26 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Type;
use RectorPrefix202305\PHPUnit\Framework\MockObject\Builder\InvocationMocker;
class InvocationMockerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
public function getClass() : string
{
return InvocationMocker::class;
}
public function isMethodSupported(MethodReflection $methodReflection) : bool
{
return $methodReflection->getName() !== 'getMatcher';
}
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope) : Type
{
return $scope->getType($methodCall->var);
}
}

View File

@ -1,27 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Type;
use RectorPrefix202305\PHPUnit\Framework\MockObject\MockBuilder;
use function in_array;
class MockBuilderDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
public function getClass() : string
{
return MockBuilder::class;
}
public function isMethodSupported(MethodReflection $methodReflection) : bool
{
return !in_array($methodReflection->getName(), ['getMock', 'getMockForAbstractClass', 'getMockForTrait'], \true);
}
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope) : Type
{
return $scope->getType($methodCall->var);
}
}

View File

@ -1,39 +0,0 @@
<?php
declare (strict_types=1);
namespace PHPStan\Type\PHPUnit;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use RectorPrefix202305\PHPUnit\Framework\MockObject\Builder\InvocationMocker;
use RectorPrefix202305\PHPUnit\Framework\MockObject\MockObject;
use function array_filter;
use function array_values;
use function count;
class MockObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
public function getClass() : string
{
return MockObject::class;
}
public function isMethodSupported(MethodReflection $methodReflection) : bool
{
return $methodReflection->getName() === 'expects';
}
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope) : Type
{
$type = $scope->getType($methodCall->var);
$mockClasses = array_values(array_filter($type->getObjectClassNames(), static function (string $class) : bool {
return $class !== MockObject::class;
}));
if (count($mockClasses) !== 1) {
return new ObjectType(InvocationMocker::class);
}
return new GenericObjectType(InvocationMocker::class, [new ObjectType($mockClasses[0])]);
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace PHPUnit\Framework\MockObject\Builder;
use PHPUnit\Framework\MockObject\Stub;
/**
* @template TMockedClass
*/
class InvocationMocker
{
}

View File

@ -1,29 +0,0 @@
<?php
namespace PHPUnit\Framework\MockObject;
use PHPUnit\Framework\TestCase;
/**
* @template TMockedClass
*/
class MockBuilder
{
/**
* @phpstan-param TestCase $testCase
* @phpstan-param class-string<TMockedClass> $type
*/
public function __construct(TestCase $testCase, $type) {}
/**
* @phpstan-return MockObject&TMockedClass
*/
public function getMock() {}
/**
* @phpstan-return MockObject&TMockedClass
*/
public function getMockForAbstractClass() {}
}

View File

@ -1,8 +0,0 @@
<?php
namespace PHPUnit\Framework\MockObject;
interface MockObject
{
}

View File

@ -1,8 +0,0 @@
<?php
namespace PHPUnit\Framework\MockObject;
interface Stub
{
}

View File

@ -1,81 +0,0 @@
<?php
namespace PHPUnit\Framework;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\MockBuilder;
use PHPUnit\Framework\MockObject\Stub;
class TestCase
{
/**
* @template T
* @phpstan-param class-string<T> $originalClassName
* @phpstan-return Stub&T
*/
public function createStub($originalClassName) {}
/**
* @template T
* @phpstan-param class-string<T> $originalClassName
* @phpstan-return MockObject&T
*/
public function createMock($originalClassName) {}
/**
* @template T
* @phpstan-param class-string<T> $className
* @phpstan-return MockBuilder<T>
*/
public function getMockBuilder(string $className) {}
/**
* @template T
* @phpstan-param class-string<T> $originalClassName
* @phpstan-return MockObject&T
*/
public function createConfiguredMock($originalClassName) {}
/**
* @template T
* @phpstan-param class-string<T> $originalClassName
* @phpstan-param string[] $methods
* @phpstan-return MockObject&T
*/
public function createPartialMock($originalClassName, array $methods) {}
/**
* @template T
* @phpstan-param class-string<T> $originalClassName
* @phpstan-return MockObject&T
*/
public function createTestProxy($originalClassName) {}
/**
* @template T
* @phpstan-param class-string<T> $originalClassName
* @phpstan-param mixed[] $arguments
* @phpstan-param string $mockClassName
* @phpstan-param bool $callOriginalConstructor
* @phpstan-param bool $callOriginalClone
* @phpstan-param bool $callAutoload
* @phpstan-param string[] $mockedMethods
* @phpstan-param bool $cloneArguments
* @phpstan-return MockObject&T
*/
protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false) {}
/**
* @template T
* @phpstan-param string $wsdlFile
* @phpstan-param class-string<T> $originalClassName
* @phpstan-param string $mockClassName
* @phpstan-param string[] $methods
* @phpstan-param bool $callOriginalConstructor
* @phpstan-param mixed[] $options
* @phpstan-return MockObject&T
*/
protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []) {}
}