You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The reason will be displayed to describe this comment to others. Learn more.
I checked the fixes against what assertEmpty() does at runtime, at head e7037550f. make tests and make phpstan pass. In five cases the fixed assertion behaves differently from the original. I ran each original and fixed line with real PHPUnit 9.6:
Case
Original
Fixed
Original result
Fixed result
@param int $x, null at runtime
assertNotEmpty($x)
assertNotSame(0, $x)
fails
passes
@param non-falsy-string $x, '0' at runtime
assertNotEmpty($x)
assertNotSame('', $x)
fails
passes
?\SimpleXMLElement $x, <a/>
assertEmpty($x)
assertNull($x)
passes
fails
bool $b
assertEmpty(actual: $b)
assertFalse(actual: $b)
passes
Error: Unknown named parameter $actual
int $i, 0
assertEmpty(message: 'm', actual: $i)
assertSame('', message: 'm', actual: $i)
passes
fails
The type comes from $scope->getType(), so a PHPDoc type decides the fix. In a test, the assertion is often what checks that the PHPDoc is right. The negated fixes then accept values that the original rejected (the first two rows). $scope->getNativeType() would avoid that. The non-falsy-string case would then not be fixed at all, but assertEmpty() on a non-falsy string can never pass anyway.
An empty SimpleXMLElement is empty for empty(), so assertNull() is not the same. ?object has the same problem, because the object can be a SimpleXMLElement at runtime. Fixing only when (new ObjectType(\SimpleXMLElement::class))->isSuperTypeOf(...) is no() for the type without null would keep ?\stdClass and skip both.
The rule reads getArgs()[0] as the asserted value and keeps the arguments as they are:
With actual: as a named argument, assertFalse(), assertTrue(), assertCount() and assertNotCount() throw an Error, because their parameter is $condition or $haystack. assertSame(), assertNotSame(), assertNull() and assertNotNull() have $actual, so they work.
With message: first, the message string is treated as the asserted value, and the fix becomes assertSame('', ...).
Skipping the fix when any argument is named would cover both.
The other fixes are equivalent for their types, including the extra message argument (assertCount(0, $array, 'message')). float, plain string and unions without null stay unfixed, as the description says.
The three Mutation Testing reds come from this PR. Infection reports 4 escaped mutants, which change ->yes() to !->no() on the isObject(), isBoolean(), isArray() and isInteger() checks. So the tests have no value for which one of these checks is a "maybe" and the fix must not apply. The other CI checks pass.
I only checked the parameter names in PHPUnit 9.6, not in 10 to 12.
The reason will be displayed to describe this comment to others. Learn more.
I checked b207aedaf again. The PHPDoc, SimpleXMLElement, named-argument and mutation points from my first review are fixed, and the tests, PHPStan and CI pass.
The nullable-object fix still changes results. assertEmpty() also treats an EmptyIterator and a Countable with a count of 0 as empty. isBuiltin() only looks at the declared class, and a subclass or an implementation can add either. I ran the rule's fix on these parameter types and then ran each original and fixed line with PHPUnit 9.6:
Parameter
Value at runtime
Original
Fixed
?Collection (user class, Countable)
count() is 0
assertEmpty() passes
assertNull() fails
?MyIterator (extends EmptyIterator)
new instance
passes
fails
?MyArrayObject (extends ArrayObject)
new, empty
passes
fails
?OpenParent (non-final class)
a Countable subclass, count 0
passes
fails
?Thing (interface)
a Countable implementation, count 0
passes
fails
?Foo (non-final class)
a Countable subclass, count 0
assertNotEmpty() fails
assertNotNull() passes
?FinalFoo (final user class)
new instance
assertEmpty() fails
assertNull() fails
Only the final class gives the same result. So the null fix could apply only when each class is final, does not implement Countable, and has no builtin parent. That covers EmptyIterator, ArrayObject and SimpleXMLElement subclasses too.
Copilot's UserDefinedObject|int|null case does not reproduce. The rule leaves Foo|int|null as it is, because getObjectClassReflections() returns nothing when a union member is not an object.
I ran the cases with PHPUnit 9.6 only. The IsEmpty constraint has the same EmptyIterator and Countable checks in 10.5, 11.5 and 12.0.
isFinal() accepts PHPDoc @final, which does not prevent runtime subclasses. For a nullable class annotated this way, a subclass implementing Countable with count zero passes assertEmpty() but fails the generated assertNull(); the negated rewrite also changes behavior. Use isFinalByKeyword() to require native finality, consistent with this fixer's use of native types.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #338
support scalar types. we skip all regular strings which are not non-falsey, because fixing these would yield ugly code like
$s == '' || $s == '0'.we only support the easiest cases for now which have a unambiguous fix and lead more readable code.