Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -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
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
7 changes: 7 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,13 @@ public function testBug14501(): void
$this->assertNoErrors($errors);
}

#[RequiresPhp('>= 8.0.0')]
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