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
LoopAnalyzer
crash on undefined $this->property
key in $loop_scope->loop_parent_context->vars_in_scope
#5848
Comments
I found these snippets: https://psalm.dev/r/3c9f6e100f<?php
declare(strict_types=1);
interface ApiClient {
public function get(string $endpoint, array $criteria): array;
}
abstract class Criteria
{
public ?int $limit = null;
public ?int $page = null;
/**
* @psalm-return array{
* limit?: int,
* page?: int,
* }
*/
public function toArray(): array
{
return array_filter([
'limit' => $this->limit,
'page' => $this->page,
]);
}
}
final class BatchIterator
{
private ApiClient $client;
/** @psalm-var non-empty-string */
private string $endpoint;
private Criteria $requestedCriteria;
/** @psalm-var callable(array): array $collectionParser */
private $collectionParser;
/**
* @psalm-param non-empty-string $endpoint
* @psalm-param callable(array): list<array> $collectionParser
*/
public function __construct(
ApiClient $client,
string $endpoint,
Criteria $criteria,
callable $collectionParser,
) {
$this->client = $client;
$this->endpoint = $endpoint;
$this->requestedCriteria = clone $criteria;
$this->collectionParser = $collectionParser;
}
/**
* @template T
* @psalm-param callable(array): T $itemParser
* @psalm-param positive-int $batchSize
* @psalm-return iterable<T>
*/
public function getIterable(callable $itemParser, int $batchSize = 25): iterable
{
$batchCriteria = clone $this->requestedCriteria;
$currentPage = $batchCriteria->page = $batchCriteria->page ?? 1;
// If the requested limit is smaller than the batch size we can optimize the response by only fetching the requested amount.
$batchCriteria->limit = ($this->requestedCriteria->limit && $this->requestedCriteria->limit < $batchSize)
? $this->requestedCriteria->limit
: $batchSize;
$fetchedItems = 0;
do {
$response = $this->client->get($this->endpoint, $batchCriteria->toArray());
$this->requestedCriteria->limit = $this->requestedCriteria->limit ?? 0;
/** @psalm-var list<array> $items */
$items = ($this->collectionParser)($response);
if ([] === $items) {
break;
}
foreach ($items as $item) {
if ($this->requestedCriteria->limit <= $fetchedItems) {
break;
}
yield $itemParser($item);
++$fetchedItems;
}
$batchCriteria->page = ++$currentPage;
} while ($this->requestedCriteria->limit > $fetchedItems);
}
}
|
Modifying the code as follows seems to make it work: $currentPage = $batchCriteria->page = $batchCriteria->page ?? 1;
+ $limit = $this->requestedCriteria->limit;
+ $collectionParser = $this->collectionParser;
// If the requested limit is smaller than the batch size we can optimize the response by only fetching the requested amount.
$batchCriteria->limit = ($this->requestedCriteria->limit && $this->requestedCriteria->limit < $batchSize)
@@ -73,17 +75,16 @@ final class BatchIterator
do {
$response = $this->client->get($this->endpoint, $batchCriteria->toArray());
- $this->requestedCriteria->limit = $this->requestedCriteria->limit ?? 0;
+ $limit = $limit ?? 0;
/** @psalm-var list<array> $items */
- $items = ($this->collectionParser)($response);
+ $items = $collectionParser($response);
if ([] === $items) {
break;
}
foreach ($items as $item) {
- if ($this->requestedCriteria->limit <= $fetchedItems) {
+ if ($limit <= $fetchedItems) {
break;
}
yield $itemParser($item); In practice, any |
Are you running any plugins that manipulate the |
I'm running with @azjezz' psl plugin, as well as the phpunit plugin: will try removing it and re-running 👍 |
I just verified that this occurs also without plugins |
Closing here: I no longer have access to the codebase that leads to the crash, so this cannot be currently reproduced, sorry :( |
I get a crash in
LoopAnalyzer
when analyzing code like https://psalm.dev/r/3c9f6e100f (some of it simplified for the sake of excluding external factors):lots of code:
Overall, the issue doesn't seem to be reproducible with
psalm.dev
, but the trace looks like this:Looking at the crash in more detail, I inspected the contents of
PhpParser\Node\Stmt\Do_
inpsalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php
Lines 497 to 499 in 38c452a
The actual code in
$stmt
there is:The problem seems to occur because
$this->requestedCriteria
is modified within the loop, when abreak;
occurs, but I'm unsure how to reproduce the issue in isolation 🤔The text was updated successfully, but these errors were encountered: