Skip to content
Closed
17 changes: 5 additions & 12 deletions src/Rules/Arrays/NonexistentOffsetInArrayDimFetchRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\RegisteredRule;
use PHPStan\Internal\SprintfHelper;
use PHPStan\Node\Printer\ExprPrinter;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
Expand All @@ -33,6 +34,7 @@ final class NonexistentOffsetInArrayDimFetchRule implements Rule
public function __construct(
private RuleLevelHelper $ruleLevelHelper,
private NonexistentOffsetInArrayDimFetchCheck $nonexistentOffsetInArrayDimFetchCheck,
private ExprPrinter $exprPrinter,
#[AutowiredParameter]
private bool $reportMaybes,
)
Expand Down Expand Up @@ -120,10 +122,7 @@ public function processNode(Node $node, Scope $scope): array
$arrayArg = $node->dim->getArgs()[0]->value;
$arrayType = $scope->getType($arrayArg);
if (
$arrayArg instanceof Node\Expr\Variable
&& $node->var instanceof Node\Expr\Variable
&& is_string($arrayArg->name)
&& $arrayArg->name === $node->var->name
$this->exprPrinter->printExpr($arrayArg) === $this->exprPrinter->printExpr($node->var)
&& $arrayType->isArray()->yes()
&& $arrayType->isIterableAtLeastOnce()->yes()
) {
Expand All @@ -146,10 +145,7 @@ public function processNode(Node $node, Scope $scope): array
$arrayType = $scope->getType($arrayArg);

if (
$arrayArg instanceof Node\Expr\Variable
&& $node->var instanceof Node\Expr\Variable
&& is_string($arrayArg->name)
&& $arrayArg->name === $node->var->name
$this->exprPrinter->printExpr($arrayArg) === $this->exprPrinter->printExpr($node->var)
Comment thread
staabm marked this conversation as resolved.
Outdated
&& $arrayType->isArray()->yes()
&& $arrayType->isIterableAtLeastOnce()->yes()
&& ($numArg === null || $one->isSuperTypeOf($scope->getType($numArg))->yes())
Expand All @@ -170,10 +166,7 @@ public function processNode(Node $node, Scope $scope): array
$arrayArg = $node->dim->left->getArgs()[0]->value;
$arrayType = $scope->getType($arrayArg);
if (
$arrayArg instanceof Node\Expr\Variable
&& $node->var instanceof Node\Expr\Variable
&& is_string($arrayArg->name)
&& $arrayArg->name === $node->var->name
$this->exprPrinter->printExpr($arrayArg) === $this->exprPrinter->printExpr($node->var)
Comment thread
staabm marked this conversation as resolved.
Outdated
&& $arrayType->isList()->yes()
&& $arrayType->isIterableAtLeastOnce()->yes()
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace PHPStan\Rules\Arrays;

use PHPStan\Node\Printer\ExprPrinter;
use PHPStan\Node\Printer\Printer;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
Expand Down Expand Up @@ -44,6 +46,7 @@ protected function getRule(): Rule
reportPossiblyNonexistentGeneralArrayOffset: $this->reportPossiblyNonexistentGeneralArrayOffset,
reportPossiblyNonexistentConstantArrayOffset: $this->reportPossiblyNonexistentConstantArrayOffset,
),
new ExprPrinter(new Printer()),
true,
);
}
Expand Down Expand Up @@ -1277,4 +1280,12 @@ public function testBug14308(): void
$this->analyse([__DIR__ . '/data/bug-14308.php'], []);
}

#[RequiresPhp('>= 8.2')]
public function testBug14390(): void
{
$this->reportPossiblyNonexistentGeneralArrayOffset = true;

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

}
131 changes: 131 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/bug-14390.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php // lint >= 8.2

declare(strict_types = 1);

namespace Bug14390;

readonly class Sample
{
/**
* @param array<string, string> $fields
*/
public function __construct(
public array $fields = [],
) {
}
}

class Foo
{
public function bar(
Sample $sample,
): void {
if ($sample->fields !== []) {
echo $sample->fields[array_key_first($sample->fields)];
Comment thread
staabm marked this conversation as resolved.
}
}

/**
* @param array<string, string> $fields
*/
public function zoo(
array $fields,
): void {
if ($fields !== []) {
echo $fields[array_key_first($fields)];
}
}

public function withKey(
Sample $sample,
): void {
if ($sample->fields !== []) {
$key = array_key_first($sample->fields);
echo $sample->fields[$key];
}
}

public function arrayKeyLast(
Sample $sample,
): void {
if ($sample->fields !== []) {
echo $sample->fields[array_key_last($sample->fields)];
}
}

public function arrayRand(
Sample $sample,
): void {
if ($sample->fields !== []) {
echo $sample->fields[array_rand($sample->fields)];
}
}

/**
* @param non-empty-list<string> $list
*/
public function countMinus1(
array $list,
): void {
echo $list[count($list) - 1];
}
}

readonly class SampleList
{
/**
* @param list<string> $items
*/
public function __construct(
public array $items = [],
) {
}
}

class Bar
{
public function countMinus1Property(
SampleList $sample,
): void {
if ($sample->items !== []) {
echo $sample->items[count($sample->items) - 1];
}
}
}

class StaticProps
{
/** @var array<string, string> */
public static array $fields = [];

/** @var list<string> */
public static array $items = [];

public function arrayKeyFirstStatic(): void
{
if (self::$fields !== []) {
echo self::$fields[array_key_first(self::$fields)];
}
}

public function arrayKeyLastStatic(): void
{
if (self::$fields !== []) {
echo self::$fields[array_key_last(self::$fields)];
}
}

public function arrayRandStatic(): void
{
if (self::$fields !== []) {
echo self::$fields[array_rand(self::$fields)];
}
}

public function countMinus1Static(): void
{
if (self::$items !== []) {
echo self::$items[count(self::$items) - 1];
}
}
}
Loading