From 2a672121c74c7ce366db7e2e72d8e5528519bf3d Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 9 Dec 2025 16:12:42 +0100 Subject: [PATCH] [fix] skip AddInstanceofAssertForNullableArgumentRector on assert call --- .../Fixture/skip_assert_pass.php.inc | 26 +++++++++++ ...tanceofAssertForNullableArgumentRector.php | 7 +++ src/NodeAnalyzer/AssertCallAnalyzer.php | 44 +++++++++---------- 3 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector/Fixture/skip_assert_pass.php.inc diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector/Fixture/skip_assert_pass.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector/Fixture/skip_assert_pass.php.inc new file mode 100644 index 00000000..c05118b5 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector/Fixture/skip_assert_pass.php.inc @@ -0,0 +1,26 @@ +getSomeObject(); + $this->assertInstanceOf(\stdClass::class, $someObject); + } + + private function getSomeObject(): ?SomeClassUsedInTests + { + if (mt_rand(0, 1)) { + return new SomeClassUsedInTests(); + } + + return null; + } +} diff --git a/rules/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector.php b/rules/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector.php index 0eef1610..1e089e76 100644 --- a/rules/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector.php @@ -18,6 +18,7 @@ use Rector\PHPUnit\CodeQuality\TypeAnalyzer\SimpleTypeAnalyzer; use Rector\PHPUnit\CodeQuality\ValueObject\VariableNameToType; use Rector\PHPUnit\CodeQuality\ValueObject\VariableNameToTypeCollection; +use Rector\PHPUnit\NodeAnalyzer\AssertCallAnalyzer; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -33,6 +34,7 @@ public function __construct( private readonly NullableObjectAssignCollector $nullableObjectAssignCollector, private readonly AssertMethodCallFactory $assertMethodCallFactory, private readonly MethodCallParameterTypeResolver $methodCallParameterTypeResolver, + private readonly AssertCallAnalyzer $assertCallAnalyzer, ) { } @@ -180,6 +182,11 @@ private function matchedNullableArgumentNameToType( return null; } + // avoid double null on assert + if ($this->assertCallAnalyzer->isAssertMethodCall($node)) { + return null; + } + $classMethodParameterTypes = $this->methodCallParameterTypeResolver->resolve($node); foreach ($node->getArgs() as $position => $arg) { diff --git a/src/NodeAnalyzer/AssertCallAnalyzer.php b/src/NodeAnalyzer/AssertCallAnalyzer.php index aa202a6c..95337fbc 100644 --- a/src/NodeAnalyzer/AssertCallAnalyzer.php +++ b/src/NodeAnalyzer/AssertCallAnalyzer.php @@ -89,6 +89,26 @@ public function containsAssertCall(ClassMethod $classMethod): bool return $hasNestedAssertOrMockCall; } + public function isAssertMethodCall(MethodCall|StaticCall $call): bool + { + if (! $call->name instanceof Identifier) { + return false; + } + + $callName = $this->nodeNameResolver->getName($call->name); + if (! is_string($callName)) { + return false; + } + + foreach (self::ASSERT_METHOD_NAME_PREFIXES as $assertMethodNamePrefix) { + if (str_starts_with($callName, $assertMethodNamePrefix)) { + return true; + } + } + + return false; + } + private function hasDirectAssertOrMockCall(ClassMethod $classMethod): bool { return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node): bool { @@ -108,11 +128,11 @@ private function hasDirectAssertOrMockCall(ClassMethod $classMethod): bool return true; } - return $this->isAssertMethodName($node); + return $this->isAssertMethodCall($node); } if ($node instanceof StaticCall) { - return $this->isAssertMethodName($node); + return $this->isAssertMethodCall($node); } return false; @@ -171,24 +191,4 @@ private function resolveClassMethodFromCall(StaticCall | MethodCall $call): ?Cla return $this->astResolver->resolveClassMethod($objectType->getClassName(), $methodName); } - - private function isAssertMethodName(MethodCall|StaticCall $call): bool - { - if (! $call->name instanceof Identifier) { - return false; - } - - $callName = $this->nodeNameResolver->getName($call->name); - if (! is_string($callName)) { - return false; - } - - foreach (self::ASSERT_METHOD_NAME_PREFIXES as $assertMethodNamePrefix) { - if (str_starts_with($callName, $assertMethodNamePrefix)) { - return true; - } - } - - return false; - } }