diff --git a/config/sets/level/deprecated-level-set.php b/config/sets/level/deprecated-level-set.php deleted file mode 100644 index 4155f094..00000000 --- a/config/sets/level/deprecated-level-set.php +++ /dev/null @@ -1,11 +0,0 @@ -sets([PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES]); $rectorConfig->rules([ + AssertIssetToAssertObjectHasPropertyRector::class, StaticDataProviderClassMethodRector::class, PublicDataProviderClassMethodRector::class, - PropertyExistsWithoutAssertRector::class, AddProphecyTraitRector::class, WithConsecutiveRector::class, RemoveSetMethodsMethodCallRector::class, @@ -29,6 +29,10 @@ // https://github.com/sebastianbergmann/phpunit/issues/4087 new MethodCallRename('PHPUnit\Framework\Assert', 'assertRegExp', 'assertMatchesRegularExpression'), + // https://github.com/sebastianbergmann/phpunit/issues/5220 + new MethodCallRename('PHPUnit\Framework\Assert', 'assertObjectHasAttribute', 'assertObjectHasProperty'), + new MethodCallRename('PHPUnit\Framework\Assert', 'assertObjectNotHasAttribute', 'assertObjectHasNotProperty'), + new MethodCallRename( 'PHPUnit\Framework\MockObject\Rule\InvocationOrder', 'getInvocationCount', diff --git a/config/sets/phpunit110.php b/config/sets/phpunit110.php new file mode 100644 index 00000000..6b580784 --- /dev/null +++ b/config/sets/phpunit110.php @@ -0,0 +1,10 @@ +rules([NamedArgumentForDataProviderRector::class]); +}; diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index e6b90ea8..64e457a3 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -270,22 +270,37 @@ Turns instanceof comparisons to their method name alternatives in PHPUnit TestCa
-## AssertIssetToSpecificMethodRector +## AssertIssetToAssertObjectHasPropertyRector -Turns isset comparisons to their method name alternatives in PHPUnit TestCase +Change `"isset()"` property check, to `assertObjectHasProperty()` method -- class: [`Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertIssetToSpecificMethodRector`](../rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php) +- class: [`Rector\PHPUnit\PHPUnit100\Rector\MethodCall\AssertIssetToAssertObjectHasPropertyRector`](../rules/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector.php) ```diff --$this->assertTrue(isset($anything->foo)); -+$this->assertObjectHasAttribute("foo", $anything); + use PHPUnit\Framework\TestCase; + + final class SomeTest extends TestCase + { + public function test() + { + $object = new stdClass(); +- $this->assertTrue(isset($object->someProperty)); ++ $this->assertObjectHasProperty('someProperty', $object); + } + } ```
+## AssertIssetToSpecificMethodRector + +Turns `assertTrue()` + `isset()` comparisons to more precise `assertArrayHasKey()` method + +- class: [`Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertIssetToSpecificMethodRector`](../rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php) + ```diff --$this->assertFalse(isset($anything["foo"]), "message"); -+$this->assertArrayNotHasKey("foo", $anything, "message"); +-$this->assertTrue(isset($anything["foo"]), "message"); ++$this->assertArrayHasKey("foo", $anything, "message"); ```
@@ -751,21 +766,6 @@ Changes PHPUnit calls from self::assert*() to `$this->assert*()`
-## PropertyExistsWithoutAssertRector - -Turns PHPUnit TestCase assertObjectHasAttribute into `property_exists` comparisons - -- class: [`Rector\PHPUnit\PHPUnit100\Rector\MethodCall\PropertyExistsWithoutAssertRector`](../rules/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector.php) - -```diff --$this->assertClassHasAttribute("property", "Class"); --$this->assertClassNotHasAttribute("property", "Class"); -+$this->assertFalse(property_exists(new Class, "property")); -+$this->assertTrue(property_exists(new Class, "property")); -``` - -
- ## PublicDataProviderClassMethodRector Change data provider methods to public diff --git a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/fixture.php.inc index ba678bd3..a5855b2e 100644 --- a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/fixture.php.inc +++ b/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/fixture.php.inc @@ -6,8 +6,6 @@ final class MyIssetTest extends \PHPUnit\Framework\TestCase { public function test() { - $this->assertTrue(isset($node->value1)); - $this->assertFalse(isset($node->value2), 'message'); $this->assertTrue(isset($node['value1']), 'message'); $this->assertFalse(isset($node['value2'])); } @@ -23,8 +21,6 @@ final class MyIssetTest extends \PHPUnit\Framework\TestCase { public function test() { - $this->assertObjectHasAttribute('value1', $node); - $this->assertObjectNotHasAttribute('value2', $node, 'message'); $this->assertArrayHasKey('value1', $node, 'message'); $this->assertArrayNotHasKey('value2', $node); } diff --git a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/magic_method_set_is_ignored.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/magic_method_set_is_ignored.php.inc deleted file mode 100644 index 057d33f6..00000000 --- a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/magic_method_set_is_ignored.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -assertTrue(isset($foo->bar)); - } -} -?> ------ -assertObjectHasAttribute('bar', $foo); - } -} -?> diff --git a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_if_magic_method_isset_exists_in_parent.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_if_magic_method_isset_exists_in_parent.php.inc deleted file mode 100644 index 1ab766d3..00000000 --- a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_if_magic_method_isset_exists_in_parent.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -assertTrue(isset($foo->bar)); - } -} diff --git a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_property.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_property.php.inc new file mode 100644 index 00000000..b8abcb75 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_property.php.inc @@ -0,0 +1,12 @@ +assertTrue(isset($node->value1)); + $this->assertFalse(isset($node->value2), 'message'); + } +} diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/PropertyExistsWithoutAssertRectorTest.php b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/AssertIssetToAssertObjectHasPropertyRectorTest.php similarity index 73% rename from rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/PropertyExistsWithoutAssertRectorTest.php rename to rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/AssertIssetToAssertObjectHasPropertyRectorTest.php index ac544b8c..c86b9d73 100644 --- a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/PropertyExistsWithoutAssertRectorTest.php +++ b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/AssertIssetToAssertObjectHasPropertyRectorTest.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Rector\PHPUnit\Tests\PHPUnit100\Rector\MethodCall\PropertyExistsWithoutAssertRector; +namespace Rector\PHPUnit\Tests\PHPUnit100\Rector\MethodCall\AssertIssetToAssertObjectHasPropertyRector; use Iterator; use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class PropertyExistsWithoutAssertRectorTest extends AbstractRectorTestCase +final class AssertIssetToAssertObjectHasPropertyRectorTest extends AbstractRectorTestCase { #[DataProvider('provideData')] public function test(string $filePath): void diff --git a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_if_magic_method_isset_exists.php.inc b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/Fixture/skip_magic_isset.php.inc similarity index 50% rename from rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_if_magic_method_isset_exists.php.inc rename to rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/Fixture/skip_magic_isset.php.inc index b81ea622..14b0f1a3 100644 --- a/rules-tests/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector/Fixture/skip_if_magic_method_isset_exists.php.inc +++ b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/Fixture/skip_magic_isset.php.inc @@ -1,14 +1,15 @@ assertTrue(isset($foo->bar)); } } - -?> diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/Fixture/some_isset_to_property.php.inc b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/Fixture/some_isset_to_property.php.inc new file mode 100644 index 00000000..d2abcf40 --- /dev/null +++ b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/Fixture/some_isset_to_property.php.inc @@ -0,0 +1,33 @@ +assertTrue(isset($object->someProperty)); + } +} + +?> +----- +assertObjectHasAttribute('someProperty', $object); + } +} + +?> diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/config/configured_rule.php b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/config/configured_rule.php new file mode 100644 index 00000000..dae2a100 --- /dev/null +++ b/rules-tests/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AssertIssetToAssertObjectHasPropertyRector::class]); diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture.php.inc b/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture.php.inc deleted file mode 100644 index b00ea55a..00000000 --- a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture.php.inc +++ /dev/null @@ -1,33 +0,0 @@ -assertClassHasAttribute('property', 'stdClass'); - $this->assertClassNotHasAttribute('property', 'Namespaced\stdClass', 'message'); - } -} - -?> ------ -assertTrue(property_exists(new \stdClass(), 'property')); - $this->assertFalse(property_exists(new \Namespaced\stdClass(), 'property'), 'message'); - } -} - -?> diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture2.php.inc b/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture2.php.inc deleted file mode 100644 index 2452c90b..00000000 --- a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture2.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -assertObjectNotHasAttribute('property', $response); - } -} - -?> ------ -assertFalse(property_exists($response, 'property')); - } -} - -?> diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture3.php.inc b/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture3.php.inc deleted file mode 100644 index fcbd82da..00000000 --- a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture3.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -assertObjectHasAttribute('property', $object->data); - $this->assertObjectNotHasAttribute('property', $object->data); - } -} - -?> ------ -assertTrue(property_exists($object->data, 'property')); - $this->assertFalse(property_exists($object->data, 'property')); - } -} - -?> diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture4.php.inc b/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture4.php.inc deleted file mode 100644 index d999cd39..00000000 --- a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/Fixture/fixture4.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -assertObjectHasAttribute($property, $object); - $this->assertObjectNotHasAttribute($property, $object); - $this->assertObjectHasAttribute($property[0], $object); - $this->assertObjectNotHasAttribute($property[1], $object); - $this->assertObjectHasAttribute($property->name, $object); - $this->assertObjectNotHasAttribute($property[1]->name, $object); - } -} - -?> ------ -assertTrue(property_exists($object, $property)); - $this->assertFalse(property_exists($object, $property)); - $this->assertTrue(property_exists($object, $property[0])); - $this->assertFalse(property_exists($object, $property[1])); - $this->assertTrue(property_exists($object, $property->name)); - $this->assertFalse(property_exists($object, $property[1]->name)); - } -} - -?> diff --git a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/config/configured_rule.php b/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/config/configured_rule.php deleted file mode 100644 index b002cf4d..00000000 --- a/rules-tests/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector/config/configured_rule.php +++ /dev/null @@ -1,10 +0,0 @@ -rule(PropertyExistsWithoutAssertRector::class); -}; diff --git a/rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php b/rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php index 9fb8da35..01ca9077 100644 --- a/rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php +++ b/rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php @@ -5,20 +5,14 @@ namespace Rector\PHPUnit\CodeQuality\Rector\MethodCall; use PhpParser\Node; -use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Scalar\String_; -use PHPStan\Reflection\ClassReflection; -use PHPStan\Type\ObjectWithoutClassType; -use PHPStan\Type\TypeWithClassName; +use Rector\PHPUnit\Enum\AssertMethod; use Rector\PHPUnit\NodeAnalyzer\IdentifierManipulator; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; use Rector\Rector\AbstractRector; -use Rector\Reflection\ClassReflectionAnalyzer; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -27,35 +21,20 @@ */ final class AssertIssetToSpecificMethodRector extends AbstractRector { - /** - * @var string - */ - private const ASSERT_TRUE = 'assertTrue'; - - /** - * @var string - */ - private const ASSERT_FALSE = 'assertFalse'; - public function __construct( private readonly IdentifierManipulator $identifierManipulator, private readonly TestsNodeAnalyzer $testsNodeAnalyzer, - private readonly ClassReflectionAnalyzer $classReflectionAnalyzer ) { } public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( - 'Turns isset comparisons to their method name alternatives in PHPUnit TestCase', + 'Turns assertTrue() + isset() comparisons to more precise assertArrayHasKey() method', [ new CodeSample( - '$this->assertTrue(isset($anything->foo));', - '$this->assertObjectHasAttribute("foo", $anything);' - ), - new CodeSample( - '$this->assertFalse(isset($anything["foo"]), "message");', - '$this->assertArrayNotHasKey("foo", $anything, "message");' + '$this->assertTrue(isset($anything["foo"]), "message");', + '$this->assertArrayHasKey("foo", $anything, "message");' ), ] ); @@ -74,7 +53,10 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, [self::ASSERT_TRUE, self::ASSERT_FALSE])) { + if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames( + $node, + [AssertMethod::ASSERT_TRUE, AssertMethod::ASSERT_FALSE] + )) { return null; } @@ -82,89 +64,27 @@ public function refactor(Node $node): ?Node return null; } - $firstArgumentValue = $node->getArgs()[0] -->value; + $firstArg = $node->getArgs()[0]; + $firstArgumentValue = $firstArg->value; + // is property access if (! $firstArgumentValue instanceof Isset_) { return null; } - $variableNodeClass = $firstArgumentValue->vars[0]::class; - if (! in_array($variableNodeClass, [ArrayDimFetch::class, PropertyFetch::class], true)) { - return null; - } - - /** @var Isset_ $issetNode */ - $issetNode = $node->getArgs()[0] -->value; - - $issetNodeArg = $issetNode->vars[0]; - - if ($issetNodeArg instanceof PropertyFetch) { - if ($this->hasMagicIsset($issetNodeArg->var)) { - return null; - } - - return $this->refactorPropertyFetchNode($node, $issetNodeArg); - } - - if ($issetNodeArg instanceof ArrayDimFetch) { - return $this->refactorArrayDimFetchNode($node, $issetNodeArg); - } - - return $node; - } - - private function hasMagicIsset(Expr $expr): bool - { - $type = $this->nodeTypeResolver->getType($expr); - - if (! $type instanceof TypeWithClassName) { - // object not found, skip - return $type instanceof ObjectWithoutClassType; - } - - $classReflection = $type->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - if ($classReflection->hasMethod('__isset')) { - return true; - } - - if (! $classReflection->isClass()) { - return false; - } - - return $this->classReflectionAnalyzer->resolveParentClassName($classReflection) !== null; - } - - private function refactorPropertyFetchNode(MethodCall|StaticCall $node, PropertyFetch $propertyFetch): ?Node - { - $name = $this->getName($propertyFetch); - if ($name === null) { + $issetVariable = $firstArgumentValue->vars[0]; + if (! $issetVariable instanceof ArrayDimFetch) { return null; } - $this->identifierManipulator->renameNodeWithMap($node, [ - self::ASSERT_TRUE => 'assertObjectHasAttribute', - self::ASSERT_FALSE => 'assertObjectNotHasAttribute', - ]); - - $oldArgs = $node->getArgs(); - unset($oldArgs[0]); - - $newArgs = $this->nodeFactory->createArgs([new String_($name), $propertyFetch->var]); - $node->args = [...$newArgs, ...$oldArgs]; - return $node; + return $this->refactorArrayDimFetchNode($node, $issetVariable); } private function refactorArrayDimFetchNode(MethodCall|StaticCall $node, ArrayDimFetch $arrayDimFetch): Node { $this->identifierManipulator->renameNodeWithMap($node, [ - self::ASSERT_TRUE => 'assertArrayHasKey', - self::ASSERT_FALSE => 'assertArrayNotHasKey', + AssertMethod::ASSERT_TRUE => 'assertArrayHasKey', + AssertMethod::ASSERT_FALSE => 'assertArrayNotHasKey', ]); $oldArgs = $node->getArgs(); diff --git a/rules/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector.php b/rules/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector.php new file mode 100644 index 00000000..e42e7a6c --- /dev/null +++ b/rules/PHPUnit100/Rector/MethodCall/AssertIssetToAssertObjectHasPropertyRector.php @@ -0,0 +1,153 @@ +assertTrue(isset($object->someProperty)); + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + public function test() + { + $object = new stdClass(); + $this->assertObjectHasProperty('someProperty', $object); + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class]; + } + + /** + * @param MethodCall|StaticCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames( + $node, + [AssertMethod::ASSERT_TRUE, AssertMethod::ASSERT_FALSE] + )) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $firstArg = $node->getArgs()[0]; + $firstArgValue = $firstArg->value; + if (! $firstArgValue instanceof Isset_) { + return null; + } + + $issetExpr = $firstArgValue->vars[0]; + if (! $issetExpr instanceof PropertyFetch) { + return null; + } + + if ($this->hasMagicIsset($issetExpr->var)) { + return null; + } + + $name = $this->getName($issetExpr); + if ($name === null) { + return null; + } + + $this->identifierManipulator->renameNodeWithMap($node, [ + AssertMethod::ASSERT_TRUE => 'assertObjectHasAttribute', + AssertMethod::ASSERT_FALSE => 'assertObjectNotHasAttribute', + ]); + + $oldArgs = $node->getArgs(); + unset($oldArgs[0]); + + $newArgs = $this->nodeFactory->createArgs([new String_($name), $issetExpr->var]); + $node->args = [...$newArgs, ...$oldArgs]; + return $node; + } + + private function hasMagicIsset(Expr $expr): bool + { + $type = $this->nodeTypeResolver->getType($expr); + if (! $type instanceof TypeWithClassName) { + // object not found, skip + return $type instanceof ObjectWithoutClassType; + } + + $classReflection = $type->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + if ($classReflection->hasMethod('__isset')) { + return true; + } + + if (! $classReflection->isClass()) { + return false; + } + + return $this->classReflectionAnalyzer->resolveParentClassName($classReflection) !== null; + } +} diff --git a/rules/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector.php b/rules/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector.php deleted file mode 100644 index 4e194f11..00000000 --- a/rules/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector.php +++ /dev/null @@ -1,134 +0,0 @@ - - */ - private const RENAME_METHODS_WITH_OBJECT_MAP = [ - 'assertObjectHasAttribute' => 'assertTrue', - 'assertObjectNotHasAttribute' => 'assertFalse', - ]; - - /** - * @var array - */ - private const RENAME_METHODS_WITH_CLASS_MAP = [ - 'assertClassHasAttribute' => 'assertTrue', - 'assertClassNotHasAttribute' => 'assertFalse', - ]; - - public function __construct( - private readonly IdentifierManipulator $identifierManipulator, - private readonly TestsNodeAnalyzer $testsNodeAnalyzer - ) { - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Turns PHPUnit TestCase assertObjectHasAttribute into `property_exists` comparisons', - [ - new CodeSample( - <<<'CODE_SAMPLE' -$this->assertClassHasAttribute("property", "Class"); -$this->assertClassNotHasAttribute("property", "Class"); -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -$this->assertFalse(property_exists(new Class, "property")); -$this->assertTrue(property_exists(new Class, "property")); -CODE_SAMPLE - ), - ] - ); - } - - /** - * @return array> - */ - public function getNodeTypes(): array - { - return [MethodCall::class, StaticCall::class]; - } - - /** - * @param MethodCall|StaticCall $node - */ - public function refactor(Node $node): ?Node - { - if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, [ - 'assertClassHasAttribute', - 'assertClassNotHasAttribute', - 'assertObjectNotHasAttribute', - 'assertObjectHasAttribute', - ])) { - return null; - } - - $arguments = array_column($node->args, 'value'); - if ( - $arguments[0] instanceof String_ || - $arguments[0] instanceof Variable || - $arguments[0] instanceof ArrayDimFetch || - $arguments[0] instanceof PropertyFetch - ) { - $secondArg = $arguments[0]; - } else { - return null; - } - - if ($arguments[1] instanceof Variable) { - $firstArg = new Variable($arguments[1]->name); - $map = self::RENAME_METHODS_WITH_OBJECT_MAP; - } elseif ($arguments[1] instanceof String_) { - $firstArg = new New_(new FullyQualified($arguments[1]->value)); - $map = self::RENAME_METHODS_WITH_CLASS_MAP; - } elseif ($arguments[1] instanceof PropertyFetch || $arguments[1] instanceof ArrayDimFetch) { - $firstArg = $arguments[1]; - $map = self::RENAME_METHODS_WITH_OBJECT_MAP; - } else { - return null; - } - - unset($node->args[0]); - unset($node->args[1]); - - $propertyExistsFuncCall = new FuncCall(new Name('property_exists'), [ - new Arg($firstArg), - new Arg($secondArg), - ]); - - $newArgs = $this->nodeFactory->createArgs([$propertyExistsFuncCall]); - - $node->args = array_merge($newArgs, $node->getArgs()); - $this->identifierManipulator->renameNodeWithMap($node, $map); - - return $node; - } -} diff --git a/src/Enum/AssertMethod.php b/src/Enum/AssertMethod.php new file mode 100644 index 00000000..496d749d --- /dev/null +++ b/src/Enum/AssertMethod.php @@ -0,0 +1,18 @@ +