Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ parameters:
count: 1
path: src/Analyser/Ignore/IgnoredErrorHelperResult.php

-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
count: 3
path: src/Analyser/ExprHandler/AssignHandler.php

-
rawMessage: Casting to string something that's already string.
identifier: cast.useless
Expand Down
8 changes: 6 additions & 2 deletions src/Analyser/ExprHandler/AssignHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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_) {
Expand Down Expand Up @@ -861,6 +861,8 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco
continue;
}

$exprString = (string) $exprString;

if (!isset($conditionalExpressions[$exprString])) {
$conditionalExpressions[$exprString] = [];
}
Expand Down Expand Up @@ -889,6 +891,8 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $
continue;
}

$exprString = (string) $exprString;

if (!isset($conditionalExpressions[$exprString])) {
$conditionalExpressions[$exprString] = [];
}
Expand Down Expand Up @@ -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;
}

Expand Down
8 changes: 4 additions & 4 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,12 @@ public function testBug14501(): void
$this->assertNoErrors($errors);
}

public function testBug14542(): void

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add RequiresPhp >= 8.0.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the mixed type

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Both review comments are addressed:

  1. Added #[RequiresPhp('>= 8.0.0')] to testBug14542 because the test data file uses the mixed type which requires PHP 8.0+.
  2. Regenerated the baseline with make phpstan-generate-baseline — the AssignHandler.php entry moved to its correct alphabetical position.

Tests (11936 passing) and static analysis (no errors) both verified clean.

{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-14542.php');
$this->assertNoErrors($errors);
}

/**
* @param string[]|null $allAnalysedFiles
* @return list<Error>
Expand Down
17 changes: 17 additions & 0 deletions tests/PHPStan/Analyser/data/bug-14542.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Bug14542;

class IndexComparator
{
/**
* @param array<mixed> $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);
}
}
Loading