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

Make ParametersAcceptorSelector::selectSingle() return union-ed conditional types #1159

Merged
merged 9 commits into from Apr 2, 2022

Conversation

rvanvelzen
Copy link
Contributor

@rvanvelzen rvanvelzen marked this pull request as draft April 1, 2022 12:31

public function getReturnType(): Type
{
return TypeTraverser::map($this->acceptor->getReturnType(), function (Type $type, callable $traverse) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe cache the result here in a private property? :)

Comment on lines 52 to 55
$parametersAcceptor = $parametersAcceptors[0];
if (TypeUtils::containsConditional($parametersAcceptor->getReturnType())) {
return new SingleParametersAcceptor($parametersAcceptor);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't immediately get tests to pass without guarding here. I could whip up a helper class that can convert a given ParametersAcceptor into the same type but with the return type adjusted? That way the contract still holds.

public function getReturnType(): Type
{
if ($this->returnType === null) {
return $this->returnType = TypeTraverser::map($this->acceptor->getReturnType(), static function (Type $type, callable $traverse) {
Copy link
Contributor

@staabm staabm Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style-nit - the line is already long enough, and will return on the outer block immediately with the same value

Suggested change
return $this->returnType = TypeTraverser::map($this->acceptor->getReturnType(), static function (Type $type, callable $traverse) {
$this->returnType = TypeTraverser::map($this->acceptor->getReturnType(), static function (Type $type, callable $traverse) {

Comment on lines +16 to +18
assertType('bool', $this->get(0));
assertType('bool', $this->get(1));
assertType('bool', $this->get(2));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we expect a more precise type here?

Suggested change
assertType('bool', $this->get(0));
assertType('bool', $this->get(1));
assertType('bool', $this->get(2));
assertType('false', $this->get(0));
assertType('true', $this->get(1));
assertType('false', $this->get(2));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - there is return type extension that just returns ParametersAcceptorSelector::selectSingle() to verify that it provides the resulting union type instead of the conditional type.

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this solution, it's better than what I'd come up with :)

*/
public function flattenConditionalsInReturnType(): self
{
$result = clone $this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike clone, please create new instance explicitly with new self.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's disappointing. Given that these classes are marked @api and the constructor isn't final, constructing an instance of static seems impossible.

Or would you accept a version that returns $this if self::class !== static::class?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Give me a moment, I'll think of something 😊

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, these classes should ideally be final, there's no reason to extend them. I've marked a new todo for 2.0 when the time comes - class has either to be @api or final (it can be both).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can mark them @final in the meantime?

{
$returnType = $this->getReturnType();

$result = clone $this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike clone, please create new instance explicitly with new self.

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good enough, I'm happy to merge it :)

@rvanvelzen rvanvelzen marked this pull request as ready for review April 2, 2022 10:02
@ondrejmirtes ondrejmirtes merged commit 3cdee3a into phpstan:1.6.x Apr 2, 2022
@ondrejmirtes
Copy link
Member

Thank you!

@rvanvelzen rvanvelzen deleted the select-single-conditional branch April 25, 2022 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants