Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 3 additions & 0 deletions src/Type/Accessory/HasOffsetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult

public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
{
if ($acceptingType->isArray()->yes() && $acceptingType->isIterableAtLeastOnce()->yes()) {
return AcceptsResult::createYes();
}
return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
}

Expand Down
4 changes: 4 additions & 0 deletions src/Type/Accessory/HasOffsetValueType.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult

public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
{
if ($acceptingType->isArray()->yes() && $acceptingType->isIterableAtLeastOnce()->yes()) {
return AcceptsResult::createYes();
Copy link
Member

Choose a reason for hiding this comment

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

It should check the iterable value type against $this->valueType.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't check the iteratable-value-type and the non-emptiness in a single IF because $acceptingType is invoked once for each element of the intersection.

this means I get the ArrayType which I could check against the value-type, but not against non-emptiness
and on the 2nd call I get the NonEmptyArray type, which I can check only against the non-emptiness, but not the value type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will think more about it.. maybe we need a fix directly on IntersectionType then

}

return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
}

Expand Down
23 changes: 23 additions & 0 deletions tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2053,4 +2053,27 @@ public function testBug7522(): void
$this->analyse([__DIR__ . '/data/bug-7522.php'], []);
}

public function testBug12847(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->checkExplicitMixed = true;
$this->checkImplicitMixed = true;

$this->analyse([__DIR__ . '/data/bug-12847.php'], [
[
'Parameter #1 $array of function Bug12847\doSomething expects non-empty-array<mixed>, mixed given.',
32,
'mixed is empty.',
],
[
'Parameter #1 $array of function Bug12847\doSomething expects non-empty-array<mixed>, mixed given.',
39,
'mixed is empty.',
],
]);
}

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

namespace Bug12847;

function doBar():void {
/**
* @var array<mixed> $array
*/
$array = [
'abc' => 'def'
];

if (isset($array['def'])) {
doSomething($array);
}
}

function doFoo(array $array):void {
if (isset($array['def'])) {
doSomething($array);
}
}

function doFooBar(array $array):void {
if (array_key_exists('foo', $array) && $array['foo'] === 17) {
doSomething($array);
}
}

function doImplicitMixed($mixed):void {
if (isset($mixed['def'])) {
doSomething($mixed);
}
}

function doExplicitMixed(mixed $mixed): void
{
if (isset($mixed['def'])) {
doSomething($mixed);
}
}

/**
* @param non-empty-array<mixed> $array
*/
function doSomething(array $array): void
{

}
Loading