Skip to content
Closed
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
38 changes: 37 additions & 1 deletion src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -2470,7 +2470,7 @@
) {
if (
($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no())
|| ($context->false() && $type->isSuperTypeOf($scope->getType($expr->right))->yes())
|| ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no()))

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || !$type->isNull()->yes())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->false() && (!$type->isSuperTypeOf($scope->getType($expr->right))->no() || $type->isNull()->no())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->false() && ($scope->getType($expr->right)->isSuperTypeOf($type)->yes() || $type->isNull()->no())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->falsey() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || !$type->isNull()->yes())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->false() && (!$type->isSuperTypeOf($scope->getType($expr->right))->no() || $type->isNull()->no())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->false() && ($scope->getType($expr->right)->isSuperTypeOf($type)->yes() || $type->isNull()->no())) ) { $expr = $expr->left; }

Check warning on line 2473 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) + || ($context->falsey() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) ) { $expr = $expr->left; }
) {
$expr = $expr->left;
}
Expand Down Expand Up @@ -2586,6 +2586,42 @@
}
}

if (
$expr instanceof ArrayDimFetch
&& $expr->dim !== null
&& !$context->null()
) {
$dimType = $scope->getType($expr->dim);
if ($dimType instanceof ConstantIntegerType || $dimType->getConstantStrings() !== []) {
$varType = $scope->getType($expr->var);
$constantArrays = $varType->getConstantArrays();
if ($constantArrays !== []) {
$typesToRemove = [];
foreach ($constantArrays as $constantArray) {
if (!$constantArray->hasOffsetValueType($dimType)->yes()) {

Check warning on line 2601 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ($constantArrays !== []) { $typesToRemove = []; foreach ($constantArrays as $constantArray) { - if (!$constantArray->hasOffsetValueType($dimType)->yes()) { + if ($constantArray->hasOffsetValueType($dimType)->no()) { continue; } $offsetValueType = $constantArray->getOffsetValueType($dimType);

Check warning on line 2601 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ($constantArrays !== []) { $typesToRemove = []; foreach ($constantArrays as $constantArray) { - if (!$constantArray->hasOffsetValueType($dimType)->yes()) { + if ($constantArray->hasOffsetValueType($dimType)->no()) { continue; } $offsetValueType = $constantArray->getOffsetValueType($dimType);
continue;
}
$offsetValueType = $constantArray->getOffsetValueType($dimType);
if ($context->false()) {

Check warning on line 2605 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ continue; } $offsetValueType = $constantArray->getOffsetValueType($dimType); - if ($context->false()) { + if ($context->falsey()) { if ($type->isSuperTypeOf($offsetValueType)->yes()) { $typesToRemove[] = $constantArray; }

Check warning on line 2605 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ continue; } $offsetValueType = $constantArray->getOffsetValueType($dimType); - if ($context->false()) { + if ($context->falsey()) { if ($type->isSuperTypeOf($offsetValueType)->yes()) { $typesToRemove[] = $constantArray; }
if ($type->isSuperTypeOf($offsetValueType)->yes()) {
$typesToRemove[] = $constantArray;
}
} elseif ($context->true()) {

Check warning on line 2609 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ if ($type->isSuperTypeOf($offsetValueType)->yes()) { $typesToRemove[] = $constantArray; } - } elseif ($context->true()) { + } elseif ($context->truthy()) { if ($type->isSuperTypeOf($offsetValueType)->no()) { $typesToRemove[] = $constantArray; }

Check warning on line 2609 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrueTruthyFalseFalseyTypeSpecifierContextMutator": @@ @@ if ($type->isSuperTypeOf($offsetValueType)->yes()) { $typesToRemove[] = $constantArray; } - } elseif ($context->true()) { + } elseif ($context->truthy()) { if ($type->isSuperTypeOf($offsetValueType)->no()) { $typesToRemove[] = $constantArray; }
if ($type->isSuperTypeOf($offsetValueType)->no()) {

Check warning on line 2610 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $typesToRemove[] = $constantArray; } } elseif ($context->true()) { - if ($type->isSuperTypeOf($offsetValueType)->no()) { + if (!$type->isSuperTypeOf($offsetValueType)->yes()) { $typesToRemove[] = $constantArray; } }

Check warning on line 2610 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.3, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ $typesToRemove[] = $constantArray; } } elseif ($context->true()) { - if ($type->isSuperTypeOf($offsetValueType)->no()) { + if ($offsetValueType->isSuperTypeOf($type)->no()) { $typesToRemove[] = $constantArray; } }

Check warning on line 2610 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $typesToRemove[] = $constantArray; } } elseif ($context->true()) { - if ($type->isSuperTypeOf($offsetValueType)->no()) { + if (!$type->isSuperTypeOf($offsetValueType)->yes()) { $typesToRemove[] = $constantArray; } }

Check warning on line 2610 in src/Analyser/TypeSpecifier.php

View workflow job for this annotation

GitHub Actions / Mutation Testing (8.4, ubuntu-latest)

Escaped Mutant for Mutator "PHPStan\Infection\IsSuperTypeOfCalleeAndArgumentMutator": @@ @@ $typesToRemove[] = $constantArray; } } elseif ($context->true()) { - if ($type->isSuperTypeOf($offsetValueType)->no()) { + if ($offsetValueType->isSuperTypeOf($type)->no()) { $typesToRemove[] = $constantArray; } }
$typesToRemove[] = $constantArray;
}
}
}

if ($typesToRemove !== [] && count($typesToRemove) < count($constantArrays)) {
$typeToRemove = TypeCombinator::union(...$typesToRemove);
$varExprString = $this->exprPrinter->printExpr($expr->var);
$sureNotTypes[$varExprString] = [$expr->var, $typeToRemove];
}
}
}
}

$types = new SpecifiedTypes($sureTypes, $sureNotTypes);
if (isset($containsNull) && !$containsNull) {
return $this->createNullsafeTypes($originalExpr, $scope, $context, $type)->unionWith($types);
Expand Down
12 changes: 12 additions & 0 deletions tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,16 @@ public function testAppendToArrayWithPhpIntMaxKey(): void
]);
}

public function testBug9004(): void
{
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-9004.php'], []);
}

public function testBug14566(): void
{
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-14566.php'], []);
}

}
46 changes: 46 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/bug-14566.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);

namespace Bug14566;

/**
* @param array{}|array{hi: 'hello'}|array{hi: array{0: 42, 1?: 42}} $test
*/
function foo(array $test): void {
if (isset($test['hi']) && is_string($test['hi'])) {
return;
}
$test['hi'][] = 42;
}

/**
* @param array{}|array{hi: 'hello'}|array{hi: array{0: 42, 1?: 42}} $test
*/
function foo2(array $test): void {
if (\is_string($test['hi'] ?? null)) {
return;
}
$test['hi'][] = 42;
}

/**
* @param array{}|array{hi: 'hello'}|array{hi: array{0: 42, 1?: 42}} $test
*/
function foo3(array $test): void {
if (!isset($test['hi'])) {
return;
}
if (\is_string($test['hi'])) {
return;
}
$test['hi'][] = 42;
}

/**
* @param array{}|array{hi: 'hello'}|array{hi: array{0: 42, 1?: 42}} $test
*/
function foo4(array $test): void {
if (!\is_array($test['hi'] ?? null)) {
return;
}
$test['hi'][] = 42;
}
Loading