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
27 changes: 26 additions & 1 deletion src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,17 @@
}
}

return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType));
$result = $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType));

if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) {

Check warning on line 948 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $result = $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType)); - if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) { + if (!$result->yes() && $this->isCallable()->yes() && !$this->isArray()->no()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 948 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $result = $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType)); - if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) { + if (!$result->yes() && !$this->isCallable()->no() && $this->isArray()->yes()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 948 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $result = $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType)); - if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) { + if (!$result->yes() && $this->isCallable()->yes() && !$this->isArray()->no()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 948 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ $result = $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType)); - if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) { + if (!$result->yes() && !$this->isCallable()->no() && $this->isArray()->yes()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
$arrayKeyOffsetType = $offsetType->toArrayKey();
$callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]);
if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 951 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); - if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) { + if (!$callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->no()) { return TrinaryLogic::createYes(); } }

Check warning on line 951 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); - if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) { + if (!$callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->no()) { return TrinaryLogic::createYes(); } }
return TrinaryLogic::createYes();
}
}

return $result;
}

public function getOffsetValueType(Type $offsetType): Type
Expand All @@ -953,6 +963,21 @@
return TypeUtils::toBenevolentUnion($result);
}

if ($this->isCallable()->yes() && $this->isArray()->yes()) {

Check warning on line 966 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ return TypeUtils::toBenevolentUnion($result); } - if ($this->isCallable()->yes() && $this->isArray()->yes()) { + if ($this->isCallable()->yes() && !$this->isArray()->no()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 966 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ return TypeUtils::toBenevolentUnion($result); } - if ($this->isCallable()->yes() && $this->isArray()->yes()) { + if ($this->isCallable()->yes() && !$this->isArray()->no()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
$arrayKeyOffsetType = $offsetType->toArrayKey();
$callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]);
Comment thread
staabm marked this conversation as resolved.
if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 969 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ($this->isCallable()->yes() && $this->isArray()->yes()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); - if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) { + if (!$callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->no()) { if ((new ConstantIntegerType(0))->isSuperTypeOf($arrayKeyOffsetType)->yes()) { $narrowedType = new UnionType([new ClassStringType(), new ObjectWithoutClassType()]); } elseif ((new ConstantIntegerType(1))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {

Check warning on line 969 in src/Type/IntersectionType.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "PHPStan\Infection\TrinaryLogicMutator": @@ @@ if ($this->isCallable()->yes() && $this->isArray()->yes()) { $arrayKeyOffsetType = $offsetType->toArrayKey(); $callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); - if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) { + if (!$callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->no()) { if ((new ConstantIntegerType(0))->isSuperTypeOf($arrayKeyOffsetType)->yes()) { $narrowedType = new UnionType([new ClassStringType(), new ObjectWithoutClassType()]); } elseif ((new ConstantIntegerType(1))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
if ((new ConstantIntegerType(0))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
$narrowedType = new UnionType([new ClassStringType(), new ObjectWithoutClassType()]);
} elseif ((new ConstantIntegerType(1))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
$narrowedType = new StringType();
} else {
$narrowedType = new UnionType([new StringType(), new ObjectWithoutClassType()]);
}
$result = TypeCombinator::intersect($result, $narrowedType);
}
}

return $result;
}

Expand Down
62 changes: 62 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-3842.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php declare(strict_types = 1);

namespace Bug3842;

use function PHPStan\Testing\assertType;

class ClassA
{
public static function callback(): void
{
}
}

class ClassB
{
public function callback(): void
{
}
}

function testIsArrayOnCallable(callable $value): void {
if (is_array($value)) {
assertType('array<mixed, mixed>&callable(): mixed', $value);
assertType('class-string|object', $value[0]);
assertType('string', $value[1]);
}
}

/** @param callable-array $value */
function testCallableArrayPhpDoc(array $value): void {
assertType('array&callable(): mixed', $value);
assertType('class-string|object', $value[0]);
assertType('string', $value[1]);
}

function testIsStringOnCallable(callable $value): void {
if (is_string($value)) {
assertType('callable-string', $value);
}
}

/** @param array{string|object, string} $values */
function check(array $values): void {
}

/** @param array{class-string|object, string} $values */
function checkClassString(array $values): void {
}

/** @param 0|1 $offset */
function testCallableArrayUnionOffset(callable $value, int $offset): void {
if (is_array($value)) {
assertType('object|string', $value[$offset]);
}
}

function testPassCallableArray(callable $value): void {
if (is_array($value)) {
check($value);
checkClassString($value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2866,4 +2866,9 @@ public function testBug13643(): void
$this->analyse([__DIR__ . '/data/bug-13643.php'], []);
}

public function testBug3842(): void
{
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-3842.php'], []);
}

}
Loading