Type error only occurring locally when analysing entire codebase
We have this method in cucumber/common: https://github.com/cucumber/common/blob/9945048fe4668dce3b3118670488b4c2ca79a884/gherkin/php/src/AstNode.php#L58
Builds in CI using the cucumber/cucumber-build container are passing: https://app.circleci.com/pipelines/github/cucumber/common/12814/workflows/6e3f569e-8a0c-4492-904e-de6284d25109/jobs/488558
When I run psalm just on that file (psalm --no-cache src/AstNode.php, there are no errors. Nor do I get an error when trying to reproduce on psalm.dev
However when running psalm on the whole codebase, we get this error:
ERROR: InvalidReturnType - src/AstNode.php:56:22 - The declared return type '(S:fn-cucumber\gherkin\astnode::getsingle as object)|null' for Cucumber\Gherkin\AstNode::getSingle is incorrect, got '(S:fn-cucumber\gherkin\astnode::getsingle as object)|(TGeneratedFromParam2:fn-cucumber\gherkin\astnode::getsingle as null|object)' (see https://psalm.dev/011)
* @psalm-return ($defaultValue is null ? S|null : S )
This seems to be triggered by $defaultValue being optional - trace shows it as TGeneratedFromParam2:fn-cucumber\gherkin\astnode::getsingle as null|object but if I take the = null away it correctly is typed as S:fn-cucumber\gherkin\astnode::getsingle as object which fixes the return type issue.
I have tried:
--clear-cache- bisecting across cucumber versions. Very old checkouts show this error too
- trying different psalm versions. I've reproduced with 4.29.0, 4.28.0 and back to 4.26.0
It's very mysterious that the CI build in the same container doesn't show this error, nor the single-file analysis
Couldn't reproduce either on psalm.dev: https://psalm.dev/r/8402a9a656
Could you maybe try to analyze with Psalm 5?
I found these snippets:
https://psalm.dev/r/8402a9a656
<?php
class A{
/**
* @template S of object
*
* @param class-string<S> $expectedType
* @param S|null $defaultValue
*
* @psalm-return ($defaultValue is null ? S|null : S )
*/
function getSingle(string $expectedType, ?object $defaultValue = null): mixed
{
$items = $this->getItems($expectedType);
return $items[0] ?? $defaultValue;
}
/**
* @template T of object
*
* @param class-string<T> $expectedType
*
* @psalm-return list<T>
*/
function getItems(string $expectedType): array
{
return [];
}
}
Psalm output (using commit eb6a347):
No issues!
For reference, this is solved when using Psalm 6.13.1