maker-bundle icon indicating copy to clipboard operation
maker-bundle copied to clipboard

Psalm LessSpecificImplementedReturnType error on Repository's `findAll()` docblock

Open ThomasLandauer opened this issue 4 years ago • 9 comments

Psalm is reporting this:

ERROR: LessSpecificImplementedReturnType - src/Repository/LinkRepository.php:8:12 - The inherited return type 'list' for Doctrine\ORM\EntityRepository::findAll is more specific than the implemented return type for Doctrine\ORM\EntityRepository::findall 'array<array-key, App\Entity\Link>' (see https://psalm.dev/166) /**

  • @method Link|null find($id, $lockMode = null, $lockVersion = null)
  • @method Link|null findOneBy(array $criteria, array $orderBy = null)
  • @method Link[] findAll()
  • @method Link[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
  • @extends ServiceEntityRepository<Link> */

ThomasLandauer avatar Feb 15 '21 21:02 ThomasLandauer

Psalm is emitting this because Doctrine\ORM\EntityRepository::findAll() has a return type of list<T> which ultimately is translated by psalm to array<int, Foo>. Where as the @method Link[] findAll() return type is ultimately translated by psalm to @psalm-return array<array-key, ValueType>

I believe changing @method return signatures to array<int, Link> (I cant remember if this syntax is legitimate) would squash this squawk from psalm. It does however look less appealing when you end up with:

/**
 * @method Link|null find($id, $lockMode = null, $lockVersion = null)
 * @method Link|null findOneBy(array $criteria, array $orderBy = null)
 * @method array<int, Link>[]  findAll()
 * @method Link[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class LinkRepository extends ServiceEntityRepository

Then, depending on your rule set, php-cs-fixer would complain about the column alignment in that docBlock and adjust accordingly..

/**
 * @method Link|null         find($id, $lockMode = null, $lockVersion = null)
 * @method Link|null         findOneBy(array $criteria, array $orderBy = null)
 * @method array<int, Link>  findAll()
 * @method Link[]            findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class LinkRepository extends ServiceEntityRepository

Personally, I ignore this particular error in my psalm.xml or baseline file. That approach is not ideal but I'm not sure what the best approach to keeping psalm silent is. Open to ideas!

Perhaps a stub in https://github.com/psalm/psalm-plugin-symfony?

jrushlow avatar Feb 15 '21 21:02 jrushlow

@ThomasLandauer @jrushlow Is there a way to define T in list<T> in our own repositories? That would solve the problem and would lead to us not needing to define @method in the first place.

christian-kolb avatar Feb 16 '21 15:02 christian-kolb

@christian-kolb I'm sure there is - https://psalm.dev/docs/annotating_code/templated_annotations/ has more information on template annotations. If it is possible for us to do that in a clean manner without any adverse effects - PR's welcome..

jrushlow avatar Feb 24 '21 09:02 jrushlow

I guess there would be changes in the ServiceEntityRepository which is part of Doctrine, isn't it?

christian-kolb avatar Feb 24 '21 20:02 christian-kolb

Here is the syntax I use to be compatible with most cases:

Specify the template with by entity with @extends

Specify a signature dedicated to Psalm with @psalm-method

/**
 * @extends ServiceEntityRepository<Link>
 * @method Link|null find($id, $lockMode = null, $lockVersion = null)
 * @method Link|null findOneBy(array $criteria, array $orderBy = null)
 * @psalm-method list<Link> findAll()
 * @method Link[] findAll()
 * @psalm-method list<Link> findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 * @method Link[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class LinkRepository extends ServiceEntityRepository

jeckel avatar Apr 12 '21 08:04 jeckel

@weaverryan can we just remove these comments from the generation ?

There is no psalm error without these.

abdounikarim avatar Nov 15 '21 16:11 abdounikarim

Or we can (optionally) add psalm-ones to the generation, no?

kissifrot avatar May 27 '22 16:05 kissifrot

Here is the syntax I use to be compatible with most cases:

Specify the template with by entity with @extends

Specify a signature dedicated to Psalm with @psalm-method

/**
 * @extends ServiceEntityRepository<Link>
 * @method Link|null find($id, $lockMode = null, $lockVersion = null)
 * @method Link|null findOneBy(array $criteria, array $orderBy = null)
 * @psalm-method list<Link> findAll()
 * @method Link[] findAll()
 * @psalm-method list<Link> findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 * @method Link[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class LinkRepository extends ServiceEntityRepository

This work on level 4+, but cause InvalidDocblock error on level 3

badmansan avatar Sep 11 '22 20:09 badmansan

The only method I could get down to level 1 was to add @psalm-suppress

/**
 * @psalm-suppress MethodSignatureMustProvideReturnType
 * @psalm-suppress LessSpecificImplementedReturnType
 *
 * @extends ServiceEntityRepository<Round>
 *
 * @method Round|null find($id, $lockMode = null, $lockVersion = null)
 * @method Round|null findOneBy(array $criteria, array $orderBy = null)
 * @method Round[]    findAll()
 * @method Round[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class RoundRepository extends ServiceEntityRepository

Ofcourse its not perfect, but it gets the job done, it will properly also be a problem when you add your own find methods, which will not be static analyzed either.

lsv avatar Dec 30 '22 15:12 lsv