`roave/you-are-using-it-wrong` complains about use of internal methods when token type is tested with `instanceof`
This is probably more of a psalm bug than anything else (or maybe I'm actually using it wrong), but I'm filing here just in case. I'm speculating that testing a token with instanceof
causes psalm to either lose the intersection with the Token
interface or, for whatever reason, the interface methods have a lower precedence after instanceof
. So, calling getValue()
on a token, such as Delimiter
, causes psalm to look at the methods on the BasicToken
class, which is declared as @internal, instead of the Token
interface, which results in psalm emitting errors.
The work around is to create a psalm config file, regardless of whether the project uses psalm or not, which will cause roave/you-are-using-it-wrong
to skip checks during installation.
Steps to reproduce:
- The calling code must reside in a different namespace (i.e. not Girgias\CSSParser)
- The calling code must test the type of the token using
instanceof
on a token that does not override it's base class' methods (most tokens extendingBasicToken
are candidates here) - The project must not have a psalm configuration file, so that
roave/you-are-using-it-wrong
gets invoked - Run composer install/update on the project
Sample code:
<?php
declare(strict_types=1);
namespace Vendor\TestPkg;
use Girgias\CSSParser\CompleteLexer;
use Girgias\CSSParser\SpecificationCompliantInputStream;
use Girgias\CSSParser\SpecificationCompliantLexer;
use Girgias\CSSParser\Tokens\Delimiter;
use Girgias\CSSParser\Tokens\EOF;
class App
{
public function run()
{
$input = new SpecificationCompliantInputStream('#div');
$lexer = new SpecificationCompliantLexer(new CompleteLexer($input));
do {
$token = $lexer->readNext();
if ($token instanceof Delimiter && $token->getValue() === '+') {
// do some stuff...
}
} while (!$token instanceof EOF);
}
}
$ composer update
...
roave/you-are-using-it-wrong: checking strictly type-checked packages...
roave/you-are-using-it-wrong: following package usages will be checked:
- girgias/css3-parser
- - Girgias\CSSParser\
ERROR: InternalMethod - test_pkg/src/App.php:23:56 - The method Girgias\CSSParser\Tokens\BasicToken::getValue is internal to Girgias but called from Vendor\TestPkg\App (see https://psalm.dev/175)
if ($token instanceof Delimiter && $token->getValue() === '+') {
Its not clear to me what should happen when a non-internal interface is implemented by an object that is marked as internal. Should the interface override the internalness of the object that implements it or should the interface inherit the implementing object's internalness?