Skip to content

Commit

Permalink
ArrayType - string offset might exist as integer offset
Browse files Browse the repository at this point in the history
  • Loading branch information
michalbundyra committed Feb 21, 2024
1 parent d0b1cf6 commit b4dbfa4
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 18 deletions.
9 changes: 7 additions & 2 deletions src/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,10 @@ public function isOffsetAccessible(): TrinaryLogic
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
$offsetType = $offsetType->toArrayKey();
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()) {

if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()
&& ($offsetType->isString()->no() || !$offsetType->isConstantScalarValue()->no())
) {
return TrinaryLogic::createNo();
}

Expand All @@ -407,7 +410,9 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
public function getOffsetValueType(Type $offsetType): Type
{
$offsetType = $offsetType->toArrayKey();
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()) {
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()
&& ($offsetType->isString()->no() || !$offsetType->isConstantScalarValue()->no())
) {
return new ErrorType();
}

Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-array-key-type.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3133.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-10577.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-10610.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-2550.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2899.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/preg_split.php');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,10 @@ public function testRule(): void
'Offset \'b\' does not exist on array{a: \'blabla\'}.',
228,
],
[
'Offset string does not exist on array<int, mixed>.',
240,
],
[
'Cannot access offset \'a\' on Closure(): void.',
253,
],
[
'Offset string does not exist on array<int, string>.',
308,
],
[
'Offset null does not exist on array<int, string>.',
310,
Expand Down Expand Up @@ -252,18 +244,10 @@ public function testRuleBleedingEdge(): void
'Offset \'b\' does not exist on array{a: \'blabla\'}.',
228,
],
[
'Offset string does not exist on array<int, mixed>.',
240,
],
[
'Cannot access offset \'a\' on Closure(): void.',
253,
],
[
'Offset string does not exist on array<int, string>.',
308,
],
[
'Offset null does not exist on array<int, string>.',
310,
Expand Down
8 changes: 8 additions & 0 deletions tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,12 @@ public function testBug10577(): void
$this->analyse([__DIR__ . '/data/bug-10577.php'], []);
}

public function testBug10610(): void
{
$this->treatPhpDocTypesAsCertain = true;
$this->strictUnnecessaryNullsafePropertyFetch = true;

$this->analyse([__DIR__ . '/data/bug-10610.php'], []);
}

}
87 changes: 87 additions & 0 deletions tests/PHPStan/Rules/Variables/data/bug-10610.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace Bug10610;

use function PHPStan\Testing\assertType;

class HelloWorld
{
private const MAP = [
'foo' => [
'19' => '582',
'26' => '689',
'56' => '817',
'52' => '1050',
'67' => '2923',
'78' => '4057',
'75' => '4078',
'54' => '4078',
'76' => '4079',
'77' => '4080',
'9' => '4080',
'46' => '4091',
'22' => '4111',
'48' => '4112',
'70' => '4113',
'42' => '4117',
'43' => '4118',
'6' => '4126',
'36' => '4129',
'13' => '4309',
'14' => '4904',
'5' => '5222',
'71' => '5223',
'73' => '5242',
'74' => '5250',
'24' => '5252',
'58' => '5255',
'35' => '5261',
'1' => '5264',
'20' => '5268',
'21' => '5269',
'31' => '5270',
'51' => '5271',
'55' => '5271',
'39' => '5274',
'50' => '5277',
'49' => '5278',
'11' => '5279',
'41' => '5279',
'44' => '5280',
'59' => '5281',
'60' => '5281',
'23' => '5281',
'72' => '5283',
'32' => '5283',
'8' => '5285',
'40' => '5285',
'12' => '5298',
'37' => '5305',
'65' => '5310',
'64' => '5310',
'57' => '5352',
'33' => '5364',
'25' => '5375',
'34' => '5460',
'45' => '7581',
'3' => '7624',
'53' => '7672',
'999' => '7953',
'69' => '7953',
'2' => '8206',
'7' => '9697',
],
'bar' => [
'30' => 'Test3',
],
];

public function validate(string $k, string $value): void
{
$res = self::MAP[$k][$value] ?? '';

assertType("'1050'|'2923'|'4057'|'4078'|'4079'|'4080'|'4091'|'4111'|'4112'|'4113'|'4117'|'4118'|'4126'|'4129'|'4309'|'4904'|'5222'|'5223'|'5242'|'5250'|'5252'|'5255'|'5261'|'5264'|'5268'|'5269'|'5270'|'5271'|'5274'|'5277'|'5278'|'5279'|'5280'|'5281'|'5283'|'5285'|'5298'|'5305'|'5310'|'5352'|'5364'|'5375'|'5460'|'582'|'689'|'7581'|'7624'|'7672'|'7953'|'817'|'8206'|'9697'|'Test3'", self::MAP[$k][$value]);

// ...
}
}

0 comments on commit b4dbfa4

Please sign in to comment.