Skip to content

Commit f0245d1

Browse files
Firehedclaude
andcommitted
Add mutation testing coverage for GmpOperatorTypeSpecifyingExtension
- Use ObjectWithoutClassType for generic object type (not ObjectType('object')) - Add test cases for object+GMP to catch IsSuperTypeOfCalleeAndArgumentMutator on line 37 - Add test case for GMP|int+int to catch TrinaryLogicMutator on line 37 - Add test cases for GMP+int|stdClass to catch TrinaryLogicMutator on line 52 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 83dbf7e commit f0245d1

1 file changed

Lines changed: 22 additions & 1 deletion

File tree

tests/PHPStan/Type/Php/GmpOperatorTypeSpecifyingExtensionTest.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPStan\Type\FloatType;
1010
use PHPStan\Type\IntegerType;
1111
use PHPStan\Type\ObjectType;
12+
use PHPStan\Type\ObjectWithoutClassType;
1213
use PHPStan\Type\Type;
1314
use PHPStan\Type\UnionType;
1415
use PHPUnit\Framework\Attributes\DataProvider;
@@ -93,6 +94,21 @@ public static function dataSpecifyTypeReturnsError(): iterable
9394
yield 'GMP + stdClass' => ['+', 'GMP', 'stdClass'];
9495
yield 'stdClass + GMP' => ['+', 'stdClass', 'GMP'];
9596
yield 'GMP + float' => ['+', 'GMP', 'float'];
97+
98+
// object is a supertype of GMP - these catch line 37 IsSuperTypeOfCalleeAndArgumentMutator
99+
// When mutation swaps callee/argument, $otherSide incorrectly becomes GMP instead of object
100+
yield 'object + GMP' => ['+', 'object', 'GMP'];
101+
yield 'GMP + object' => ['+', 'GMP', 'object'];
102+
103+
// GMP|int is Maybe-GMP - catches line 37 TrinaryLogicMutator
104+
// When mutation changes .yes() to !.no(), $otherSide incorrectly becomes int instead of GMP|int
105+
// Note: int + GMP|int returns GMP (other=int which is valid), only GMP|int + int returns error
106+
yield 'GMP|int + int (specifyType)' => ['+', 'GMP|int', 'int'];
107+
108+
// int|stdClass has isInteger()=Maybe - catches line 52 TrinaryLogicMutator
109+
// When mutation changes .yes() to !.no(), isInteger() incorrectly returns true
110+
yield 'GMP + int|stdClass' => ['+', 'GMP', 'int|stdClass'];
111+
yield 'int|stdClass + GMP' => ['+', 'int|stdClass', 'GMP'];
96112
}
97113

98114
#[DataProvider('dataSpecifyTypeReturnsGmp')]
@@ -111,6 +127,9 @@ public static function dataSpecifyTypeReturnsGmp(): iterable
111127
yield 'GMP + GMP' => ['+', 'GMP', 'GMP'];
112128
yield 'GMP + int' => ['+', 'GMP', 'int'];
113129
yield 'int + GMP' => ['+', 'int', 'GMP'];
130+
131+
// When left is int and right is GMP|int, other=int which is valid
132+
yield 'int + GMP|int' => ['+', 'int', 'GMP|int'];
114133
}
115134

116135
private function createType(string $type): Type
@@ -123,11 +142,13 @@ private function createType(string $type): Type
123142
case 'float':
124143
return new FloatType();
125144
case 'object':
126-
return new ObjectType('object');
145+
return new ObjectWithoutClassType();
127146
case 'stdClass':
128147
return new ObjectType('stdClass');
129148
case 'GMP|int':
130149
return new UnionType([new ObjectType('GMP'), new IntegerType()]);
150+
case 'int|stdClass':
151+
return new UnionType([new IntegerType(), new ObjectType('stdClass')]);
131152
default:
132153
throw new InvalidArgumentException(sprintf('Unknown type: %s', $type));
133154
}

0 commit comments

Comments
 (0)