Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ArrayAccess destructuring assignment producing mixed. #2903

Draft
wants to merge 2 commits into
base: 1.10.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/Type/ObjectType.php
Expand Up @@ -1129,14 +1129,14 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic

public function getOffsetValueType(Type $offsetType): Type
{
if (!$this->isExtraOffsetAccessibleClass()->no()) {
return new MixedType();
}

if ($this->isInstanceOf(ArrayAccess::class)->yes()) {
return RecursionGuard::run($this, fn (): Type => ParametersAcceptorSelector::selectSingle($this->getMethod('offsetGet', new OutOfClassScope())->getVariants())->getReturnType());
}

if (!$this->isExtraOffsetAccessibleClass()->no()) {
return new MixedType();
}

return new ErrorType();
}

Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -1433,6 +1433,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9123.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10037.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/set-type-type-specifying.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9456.php');
}

/**
Expand Down
67 changes: 67 additions & 0 deletions tests/PHPStan/Analyser/data/bug-9456.php
@@ -0,0 +1,67 @@
<?php declare(strict_types = 1);

namespace Bug9456;

use function PHPStan\Testing\assertType;
use ArrayAccess;

/**
* @template TKey of array-key
* @template TValue of mixed
*
* @implements ArrayAccess<TKey, TValue>
*/
class Collection implements ArrayAccess {
/**
* @param (callable(TValue, TKey): bool)|TValue|string $key
* @param TValue|string|null $operator
* @param TValue|null $value
* @return static<int<0, 1>, static<TKey, TValue>>
*/
public function partition($key, $operator = null, $value = null) {} // @phpstan-ignore-line

/**
* @param TKey $key
* @return TValue
*/
public function offsetGet($key): mixed { return null; } // @phpstan-ignore-line

/**
* @param TKey $key
* @return bool
*/
public function offsetExists($key): bool { return true; }

/**
* @param TKey|null $key
* @param TValue $value
* @return void
*/
public function offsetSet($key, $value): void {}

/**
* @param TKey $key
* @return void
*/
public function offsetUnset($key): void {}

/**
* @param Collection<int, string> $collection
*/
public function sayHello(Collection $collection): void
{
$result = $collection->partition('key');

assertType(
'Bug9456\Collection<int<0, 1>, Bug9456\Collection<int, string>>',
$result
);
assertType('Bug9456\Collection<int, string>', $result[0]);
assertType('Bug9456\Collection<int, string>', $result[1]);

[$one, $two] = $collection->partition('key');

assertType('Bug9456\Collection<int, string>', $one);
assertType('Bug9456\Collection<int, string>', $two);
}
}
Expand Up @@ -168,4 +168,10 @@ public function testRuleWithNullsafeVariant(): void
]);
}


public function testRuleWithSimpleXml(): void
{
$this->analyse([__DIR__ . '/data/bug-9456-xml.php'], []);
}

}
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Operators/data/bug-9456-xml.php
@@ -0,0 +1,19 @@
<?php

namespace Bug9456Xml;

use SimpleXMLElement;

/**
* @param SimpleXMLElement $xml
*/
function simplexml($xml): void
{
if (isset($xml->modules)) {
foreach ($xml->modules->module as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
$install = ($attributes['install'] == 1) ? true : false;
}
}
}