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

ReflectionParameter::hasType() should assert return type of ReflectionParameter::getType() #5258

Closed
dbrekelmans opened this issue Feb 20, 2021 · 9 comments

Comments

@dbrekelmans
Copy link

If ReflectionParameter::hasType() returns true, then ReflectionParameter::getType() will always return ReflectionType.

If ReflectionParameter::hasType() returns false, then ReflectionParameter::getType() will always return null.

This is not currently understood by psalm.

https://psalm.dev/r/257f4e884d

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/257f4e884d
<?php

static function () : void {
    $method = new ReflectionMethod(stdClass::class);
    $parameters = $method->getParameters();
    
    foreach ($parameters as $parameter) {
        if (!$parameter->hasType()) {
            throw new RuntimeException('No type found.');
        }
        
        echo($parameter->getType()->__toString());
    }
};
Psalm output (using commit 3106635):

ERROR: PossiblyNullReference - 12:37 - Cannot call method __toString on possibly null value

@dbrekelmans
Copy link
Author

@weirdan If you have some pointers for where to look.. I can attempt a PR myself ;)

@orklah
Copy link
Collaborator

orklah commented Mar 4, 2021

Well, I don't think of any other way other than creating a new ReturnTypeProvider for this.

The complexity here is that the return type depends on the result of another method rather than the parameters of the method.

I looked in Psalm and in Psalm's plugin and it doesn't appear to have already been done anywhere.

However, I think we could exploit method memoization setting (MethodCallAnalyzer line 204) that store the result of previous methods in the context.

By retrieving $event->getContext()->vars_in_scope in the ReturnTypeProvider, you should be able to know the type returned by hasType when calling getType.

As I said, I don't think it has ever been done and it's exploiting a not totally relevant setting and feature to work. I would have no remorse doing that in one of my plugins, but I'm not sure it's good enough to be added in core.

Maybe instead of using memoization setting, Psalm could have a list of methods that should be memoized anyway for this kind of feature

@dbrekelmans
Copy link
Author

dbrekelmans commented Mar 4, 2021

Is this not do-able with a ReflectionParameter stub with an assertion annotation?

For example:

class ReflectionParameter {
    /*
     * @var ReflectionType|null
     */
    private $type;

    /*
     * @psalm-assert-if-true !null $type
     */
    public function hasType() : bool
    {
        return $this->type !== null;
    }

    public function getType() : ?ReflectionType
    {
        return $this->type;
    }
}

@orklah
Copy link
Collaborator

orklah commented Mar 4, 2021

Nope, Psalm currently has no knowledge of the internal state of an object (except for it's type, and potential templates EDIT: also except array-like objects). That means you can't convey datas between calls on the same object. (it has been suggested and refused before)

That's why I suggested using data from the context that has called the methods.

@dbrekelmans
Copy link
Author

Interesting. I will take a look later and see if I can produce a result. Thank you for your input @orklah

@weirdan
Copy link
Collaborator

weirdan commented Mar 4, 2021

Maybe instead of using memoization setting, Psalm could have a list of methods that should be memoized anyway for this kind of feature

Doesn't Psalm already do this for mutation-free methods? Actually ReflectionParameter as a whole looks immutable, and declaring it as such should enable memoization for all of its methods.

@orklah
Copy link
Collaborator

orklah commented Mar 4, 2021

Doesn't Psalm already do this for mutation-free methods? Actually ReflectionParameter as a whole looks immutable, and declaring it as such should enable memoization for all of its methods.

I don't have much practice with purity in Psalm, but it looks right. There's some code that looks like that in MethodCallPurityAnalyzer line 144

@muglug
Copy link
Collaborator

muglug commented Mar 6, 2021

It turns out Psalm already had pretty much everything necessary – the only blocker was type-insensitivity of assertions on $this->getType(), which is now fixed.

This was referenced Mar 15, 2021
This was referenced Mar 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants