diff --git a/src/Type/Constant/ConstantFloatType.php b/src/Type/Constant/ConstantFloatType.php index 229bf6696f..3468ab5de8 100644 --- a/src/Type/Constant/ConstantFloatType.php +++ b/src/Type/Constant/ConstantFloatType.php @@ -5,8 +5,6 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode; use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; -use PHPStan\TrinaryLogic; -use PHPStan\Type\CompoundType; use PHPStan\Type\ConstantScalarType; use PHPStan\Type\FloatType; use PHPStan\Type\GeneralizePrecision; @@ -14,12 +12,11 @@ use PHPStan\Type\Traits\ConstantScalarTypeTrait; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; -use function abs; use function ini_get; use function ini_set; use function is_finite; +use function is_nan; use function str_contains; -use const PHP_FLOAT_EPSILON; /** @api */ class ConstantFloatType extends FloatType implements ConstantScalarType @@ -42,7 +39,7 @@ public function getValue(): float public function equals(Type $type): bool { - return $type instanceof self && abs($this->value - $type->value) < PHP_FLOAT_EPSILON; + return $type instanceof self && ($this->value === $type->value || is_nan($this->value) && is_nan($type->value)); } private function castFloatToString(float $value): string @@ -69,31 +66,6 @@ public function describe(VerbosityLevel $level): string ); } - public function isSuperTypeOf(Type $type): TrinaryLogic - { - if ($type instanceof self) { - if (!$this->equals($type)) { - if (abs($this->value - $type->value) < PHP_FLOAT_EPSILON) { - return TrinaryLogic::createMaybe(); - } - - return TrinaryLogic::createNo(); - } - - return TrinaryLogic::createYes(); - } - - if ($type instanceof parent) { - return TrinaryLogic::createMaybe(); - } - - if ($type instanceof CompoundType) { - return $type->isSubTypeOf($this); - } - - return TrinaryLogic::createNo(); - } - public function toString(): Type { return new ConstantStringType((string) $this->value); diff --git a/src/Type/Traits/ConstantScalarTypeTrait.php b/src/Type/Traits/ConstantScalarTypeTrait.php index cca9b12008..5a8d6dcfc5 100644 --- a/src/Type/Traits/ConstantScalarTypeTrait.php +++ b/src/Type/Traits/ConstantScalarTypeTrait.php @@ -24,7 +24,7 @@ public function accepts(Type $type, bool $strictTypes): TrinaryLogic public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult { if ($type instanceof self) { - return AcceptsResult::createFromBoolean($this->value === $type->value); + return AcceptsResult::createFromBoolean($this->equals($type)); } if ($type instanceof CompoundType) { @@ -37,7 +37,7 @@ public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult public function isSuperTypeOf(Type $type): TrinaryLogic { if ($type instanceof self) { - return $this->value === $type->value ? TrinaryLogic::createYes() : TrinaryLogic::createNo(); + return TrinaryLogic::createFromBoolean($this->equals($type)); } if ($type instanceof parent) { diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 28cd5caa9d..1a8770dd05 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1664,4 +1664,9 @@ public function testParamClosureThis(): void ]); } + public function testBug10297(): void + { + $this->analyse([__DIR__ . '/data/bug-10297.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-10297.php b/tests/PHPStan/Rules/Functions/data/bug-10297.php new file mode 100644 index 0000000000..ed3a8cf3dd --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-10297.php @@ -0,0 +1,86 @@ + $stream + * @param callable(T, K): iterable $fn + * + * @return Generator + */ +function scollect(iterable $stream, callable $fn): Generator +{ + foreach ($stream as $key => $value) { + yield from $fn($value, $key); + } +} + +/** + * @template K of array-key + * @template T + * @template L of array-key + * @template U + * + * @param array $array + * @param callable(T, K): iterable $fn + * + * @return array + */ +function collectWithKeys(array $array, callable $fn): array +{ + $map = []; + $counter = 0; + + try { + foreach (scollect($array, $fn) as $key => $value) { + $map[$key] = $value; + ++$counter; + } + } catch (TypeError) { + throw new UnexpectedValueException('The key yielded in the callable is not compatible with the type "array-key".'); + } + + if ($counter !== count($map)) { + throw new UnexpectedValueException( + 'Data loss occurred because of duplicated keys. Use `collect()` if you do not care about ' . + 'the yielded keys, or use `scollect()` if you need to support duplicated keys (as arrays cannot).', + ); + } + + return $map; +} + +class SomeUnitTest +{ + /** + * @return iterable + */ + public static function someProvider(): iterable + { + $unsupportedTypes = [ + // this one does not work: + 'Not a Number' => NAN, + // these work: + 'Infinity' => INF, + stdClass::class => new stdClass(), + self::class => self::class, + 'hello there' => 'hello there', + 'array' => [[42]], + ]; + + yield from collectWithKeys($unsupportedTypes, static function (mixed $value, string $type): iterable { + $error = sprintf('Some %s error message', $type); + + yield sprintf('"%s" something something', $type) => [$value, [$error, $error, $error]]; + }); + } +} diff --git a/tests/PHPStan/Rules/Methods/IncompatibleDefaultParameterTypeRuleTest.php b/tests/PHPStan/Rules/Methods/IncompatibleDefaultParameterTypeRuleTest.php index 0061758036..e55eb6eede 100644 --- a/tests/PHPStan/Rules/Methods/IncompatibleDefaultParameterTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/IncompatibleDefaultParameterTypeRuleTest.php @@ -73,4 +73,9 @@ public function testDefaultValueForPromotedProperty(): void ]); } + public function testBug10956(): void + { + $this->analyse([__DIR__ . '/data/bug-10956.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-10956.php b/tests/PHPStan/Rules/Methods/data/bug-10956.php new file mode 100644 index 0000000000..459d5bd6c2 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-10956.php @@ -0,0 +1,19 @@ +