Skip to content
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

InvalidCast when working with ::class on generics #8810

Closed
davidbyoung opened this issue Dec 2, 2022 · 9 comments
Closed

InvalidCast when working with ::class on generics #8810

davidbyoung opened this issue Dec 2, 2022 · 9 comments

Comments

@davidbyoung
Copy link

I copy/pasted the relevant parts of my code here: https://psalm.dev/r/13175e171b. When dealing with somewhat complex constrained generics, using ::class gives me an InvalidCast {NameOfClass} cannot be cast to string. This is happening on the master branch of Psalm.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/13175e171b
<?php

class AuthenticationSchemeOptions {}

/**
 * @template T of AuthenticationSchemeOptions
 */
class AuthenticationScheme
{
    /**
     * @param class-string<IAuthenticationSchemeHandler<T>> $handlerClassName The name of the authentication scheme handler class used by this scheme
     * @param T $options The options for this scheme
     */
    public function __construct(
        public readonly string $handlerClassName,
        public readonly AuthenticationSchemeOptions $options = new AuthenticationSchemeOptions()
    ) {
    }
}

/**
 * @template T of AuthenticationSchemeOptions
 */
interface IAuthenticationSchemeHandler {}

interface IAuthenticationSchemeHandlerResolver
{
    /**
     * @template TSchemeOptions of AuthenticationSchemeOptions
     * @template THandler of IAuthenticationSchemeHandler<TSchemeOptions>
     * @param class-string<THandler> $authenticationHandlerClassName
     * @return THandler The resolved authentication handler
     */
    public function resolve(string $authenticationHandlerClassName): IAuthenticationSchemeHandler;
}

class CookieAuthenticationOptions extends AuthenticationSchemeOptions {}

/**
 * @implements IAuthenticationSchemeHandler<CookieAuthenticationOptions>
 */
class CookieAuthenticationHandler implements IAuthenticationSchemeHandler {}


/** @var IAuthenticationSchemeHandlerResolver $resolver */
$resolver->resolve(CookieAuthenticationHandler::class);
Psalm output (using commit c3cc906):

ERROR: InvalidCast - 46:20 - CookieAuthenticationHandler cannot be cast to string

@weirdan
Copy link
Collaborator

weirdan commented Dec 2, 2022

Simplified a bit: https://psalm.dev/r/db793e0df3

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/db793e0df3
<?php

/** @template-covariant T of Throwable */
interface Box {}

/** @implements Box<Exception> */
class ExceptionBox implements Box {}

interface Packer
{
    /**
     * @template TBox of Box
     * @param class-string<TBox> $boxClass
     * @return TBox
     */
    public function pack(string $boxClass): Box;
}

/** @var Packer $packer */
$packer->pack(ExceptionBox::class);
Psalm output (using commit c3cc906):

ERROR: InvalidCast - 20:15 - ExceptionBox cannot be cast to string

@orklah
Copy link
Collaborator

orklah commented Dec 4, 2022

That's really weird, Psalm transforms the TLiteralClassString into a TNamedObject at some point and then complains it can't cast it to string...

@vudaltsov
Copy link
Contributor

Looking forward to the fix. We have many such issues in our project.

@roxblnfk
Copy link

roxblnfk commented Apr 5, 2023

Yet another example https://psalm.dev/r/80771a0b51

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/80771a0b51
<?php

declare(strict_types=1);

/**
 * @template TClass of object
 */
interface InjectorInterface
{
    /**
     * @return TClass
     */
    public function createInjection(): object;
}
class Binder
{
    /**
     * @template TClass of object
     *
     * @param class-string<TClass> $class
     * @param class-string<InjectorInterface<TClass>> $injector
     */
    public function bindInjector(string $class, string $injector): void
    {}
}

interface CacheInterface {}
/**
 * @implements InjectorInterface<CacheInterface>
 */
class CacheInjector implements InjectorInterface {
    public function createInjection(): CacheInterface
    {
        return new class implements CacheInterface {};
    }
}


(new Binder)->bindInjector(CacheInterface::class, CacheInjector::class);
Psalm output (using commit 82b3a7c):

ERROR: InvalidCast - 39:51 - CacheInjector cannot be cast to string

@orklah
Copy link
Collaborator

orklah commented May 4, 2023

Fixed by @klimick on #9738 \o/

@orklah orklah closed this as completed May 4, 2023
@vudaltsov
Copy link
Contributor

Awesome, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants