diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index bbc738718b..1b12c10fc1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -31,6 +31,11 @@ parameters: count: 1 path: src/Analyser/MutatingScope.php + - + message: "#^Casting to string something that's already string\\.$#" + count: 2 + path: src/Analyser/MutatingScope.php + - message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantBooleanType is error\\-prone and deprecated\\. Use Type\\:\\:isTrue\\(\\) or Type\\:\\:isFalse\\(\\) instead\\.$#" count: 4 diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 50154629f0..5823dda61d 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -3659,7 +3659,7 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self } $typeSpecifications[] = [ 'sure' => true, - 'exprString' => $exprString, + 'exprString' => (string) $exprString, 'expr' => $expr, 'type' => $type, ]; @@ -3670,7 +3670,7 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self } $typeSpecifications[] = [ 'sure' => false, - 'exprString' => $exprString, + 'exprString' => (string) $exprString, 'expr' => $expr, 'type' => $type, ]; diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 6fc19d0d7e..1a725bcd70 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -225,8 +225,7 @@ public function specifyTypesInCondition( $exprLeftType = $scope->getType($expr->left); $exprRightType = $scope->getType($expr->right); if ( - count($exprLeftType->getConstantScalarValues()) === 1 - || count($exprLeftType->getEnumCases()) === 1 + count($exprLeftType->getFiniteTypes()) === 1 || ($exprLeftType->isConstantValue()->yes() && !$exprRightType->equals($exprLeftType) && $exprRightType->isSuperTypeOf($exprLeftType)->yes()) ) { $types = $this->create( @@ -239,8 +238,7 @@ public function specifyTypesInCondition( ); } if ( - count($exprRightType->getConstantScalarValues()) === 1 - || count($exprRightType->getEnumCases()) === 1 + count($exprRightType->getFiniteTypes()) === 1 || ($exprRightType->isConstantValue()->yes() && !$exprLeftType->equals($exprRightType) && $exprLeftType->isSuperTypeOf($exprRightType)->yes()) ) { $leftType = $this->create( diff --git a/src/Type/TypeCombinator.php b/src/Type/TypeCombinator.php index b5927e3d21..8d641cbfe5 100644 --- a/src/Type/TypeCombinator.php +++ b/src/Type/TypeCombinator.php @@ -74,7 +74,41 @@ public static function remove(Type $fromType, Type $typeToRemove): Type } } - return $fromType->tryRemove($typeToRemove) ?? $fromType; + $removed = $fromType->tryRemove($typeToRemove); + if ($removed !== null) { + return $removed; + } + + $fromFiniteTypes = $fromType->getFiniteTypes(); + if (count($fromFiniteTypes) > 0) { + $finiteTypesToRemove = $typeToRemove->getFiniteTypes(); + if (count($finiteTypesToRemove) === 1) { + $result = []; + foreach ($fromFiniteTypes as $finiteType) { + if ($finiteType->equals($finiteTypesToRemove[0])) { + continue; + } + + $result[] = $finiteType; + } + + if (count($result) === count($fromFiniteTypes)) { + return $fromType; + } + + if (count($result) === 0) { + return new NeverType(); + } + + if (count($result) === 1) { + return $result[0]; + } + + return new UnionType($result); + } + } + + return $fromType; } public static function removeNull(Type $type): Type diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index d471dfe3d8..eb136244ae 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1261,6 +1261,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9404.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/globals.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9208.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/finite-types.php'); } /** diff --git a/tests/PHPStan/Analyser/data/finite-types.php b/tests/PHPStan/Analyser/data/finite-types.php new file mode 100644 index 0000000000..c3e7e719a4 --- /dev/null +++ b/tests/PHPStan/Analyser/data/finite-types.php @@ -0,0 +1,33 @@ +