diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9f83d85139e..ec7779bfbed 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -18,6 +18,12 @@ parameters: count: 1 path: src/Analyser/ArgumentsNormalizer.php + - + rawMessage: Casting to string something that's already string. + identifier: cast.useless + count: 3 + path: src/Analyser/ExprHandler/AssignHandler.php + - rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.' identifier: phpstanApi.instanceofType diff --git a/src/Analyser/ExprHandler/AssignHandler.php b/src/Analyser/ExprHandler/AssignHandler.php index 1db8061d9ee..5d2a084ec04 100644 --- a/src/Analyser/ExprHandler/AssignHandler.php +++ b/src/Analyser/ExprHandler/AssignHandler.php @@ -319,7 +319,7 @@ public function processAssignVar( $nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedExpr), $scopeBeforeAssignEval, $storage); $scope = $scope->assignVariable($var->name, $type, $scope->getNativeType($assignedExpr), TrinaryLogic::createYes()); foreach ($conditionalExpressions as $exprString => $holders) { - $scope = $scope->addConditionalExpressions($exprString, $holders); + $scope = $scope->addConditionalExpressions((string) $exprString, $holders); } if ($assignedExpr instanceof Expr\Array_) { @@ -861,6 +861,8 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco continue; } + $exprString = (string) $exprString; + if (!isset($conditionalExpressions[$exprString])) { $conditionalExpressions[$exprString] = []; } @@ -889,6 +891,8 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $ continue; } + $exprString = (string) $exprString; + if (!isset($conditionalExpressions[$exprString])) { $conditionalExpressions[$exprString] = []; } @@ -938,7 +942,7 @@ private function isExprSafeToProjectThroughVariable(Expr $expr, string $variable // narrowing targets at a usage site — skip them so they don't collide with PHP's // numeric-string array-key autocast or leak internal virtual expressions into the // conditional-expression map. - if ($expr instanceof Node\Scalar || $expr instanceof ConstFetch || $expr instanceof VirtualNode) { + if ($expr instanceof Node\Scalar || $expr instanceof ConstFetch || $expr instanceof VirtualNode || $expr instanceof Expr\UnaryMinus && $expr->expr instanceof Node\Scalar) { return false; } diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 4a22a57950c..24903779b3f 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -494,7 +494,7 @@ public function specifyTypesInCondition( } if ($context->true()) { - if (!$expr->left instanceof Node\Scalar) { + if (!$expr->left instanceof Node\Scalar && !($expr->left instanceof Expr\UnaryMinus && $expr->left->expr instanceof Node\Scalar)) { $result = $result->unionWith( $this->create( $expr->left, @@ -504,7 +504,7 @@ public function specifyTypesInCondition( )->setRootExpr($expr), ); } - if (!$expr->right instanceof Node\Scalar) { + if (!$expr->right instanceof Node\Scalar && !($expr->right instanceof Expr\UnaryMinus && $expr->right->expr instanceof Node\Scalar)) { $result = $result->unionWith( $this->create( $expr->right, @@ -515,7 +515,7 @@ public function specifyTypesInCondition( ); } } elseif ($context->false()) { - if (!$expr->left instanceof Node\Scalar) { + if (!$expr->left instanceof Node\Scalar && !($expr->left instanceof Expr\UnaryMinus && $expr->left->expr instanceof Node\Scalar)) { $result = $result->unionWith( $this->create( $expr->left, @@ -525,7 +525,7 @@ public function specifyTypesInCondition( )->setRootExpr($expr), ); } - if (!$expr->right instanceof Node\Scalar) { + if (!$expr->right instanceof Node\Scalar && !($expr->right instanceof Expr\UnaryMinus && $expr->right->expr instanceof Node\Scalar)) { $result = $result->unionWith( $this->create( $expr->right, diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 6826995d4db..135aad26f8b 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1543,6 +1543,13 @@ public function testBug14501(): void $this->assertNoErrors($errors); } + #[RequiresPhp('>= 8.0.0')] + public function testBug14542(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-14542.php'); + $this->assertNoErrors($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return list diff --git a/tests/PHPStan/Analyser/data/bug-14542.php b/tests/PHPStan/Analyser/data/bug-14542.php new file mode 100644 index 00000000000..e24bdfd194f --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-14542.php @@ -0,0 +1,17 @@ + $ids + */ + public function compare(mixed $a, mixed $b, array $ids): int + { + $indexA = ($index = \array_search($a, $ids)) > -1 ? $index : \PHP_INT_MAX; + $indexB = ($index = \array_search($b, $ids)) > -1 ? $index : \PHP_INT_MAX; + + return \strnatcmp((string) $indexA, (string) $indexB); + } +}