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

PropertyNotSetInConstructor - when trait defined same property in different case #4393

Open
oleg-andreyev opened this issue Oct 21, 2020 · 17 comments

Comments

@oleg-andreyev
Copy link

Example: https://psalm.dev/r/69f9c98ac1

While it looks like a valid case, the error message could be improved and say that property is defined in trait

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/69f9c98ac1
<?php

interface ViewHandlerInterface {}

trait ControllerTrait {
	private ViewHandlerInterface $viewhandler;
}

class Controller {
    use ControllerTrait;
    
	private $viewHandler;
    
    public function __construct(ViewHandlerInterface $viewHandler) {
        $this->viewHandler = $viewHandler;
    }
}
Psalm output (using commit ad5a8c2):

ERROR: PropertyNotSetInConstructor - 9:7 - Property Controller::$viewhandler is not defined in constructor of Controller and in any private or final methods called in the constructor

@oleg-andreyev
Copy link
Author

Another example https://psalm.dev/r/8c91ef717d

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/8c91ef717d
<?php

interface ViewHandlerInterface {}

trait ControllerTrait {
	private ViewHandlerInterface $viewhandler;
    
    public function setViewHandler(ViewHandlerInterface $viewhandler): void {
    	$this->viewhandler = $viewhandler;
    }
}

class Controller {
    use ControllerTrait;
    
    public function __construct(ViewHandlerInterface $viewHandler) {
        $this->setViewHandler($viewHandler);
    }
}
Psalm output (using commit ad5a8c2):

ERROR: PropertyNotSetInConstructor - 13:7 - Property Controller::$viewhandler is not defined in constructor of Controller and in any private or final methods called in the constructor

@weirdan
Copy link
Collaborator

weirdan commented Oct 21, 2020

The latter is valid and can be fixed by making setViewHandler() final or private: https://psalm.dev/r/3dcfb7762c

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/3dcfb7762c
<?php

interface ViewHandlerInterface {}

trait ControllerTrait {
	private ViewHandlerInterface $viewhandler;
    
    public final function setViewHandler(ViewHandlerInterface $viewhandler): void {
    	$this->viewhandler = $viewhandler;
    }
}

class Controller {
    use ControllerTrait;
    
    public function __construct(ViewHandlerInterface $viewHandler) {
        $this->setViewHandler($viewHandler);
    }
}
Psalm output (using commit ad5a8c2):

No issues!

@oleg-andreyev
Copy link
Author

The latter is valid and can be fixed by making setViewHandler() final or private: https://psalm.dev/r/3dcfb7762c

@weirdan It's a vendors code. I cannot do it. It's still weird that final is required.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/3dcfb7762c
<?php

interface ViewHandlerInterface {}

trait ControllerTrait {
	private ViewHandlerInterface $viewhandler;
    
    public final function setViewHandler(ViewHandlerInterface $viewhandler): void {
    	$this->viewhandler = $viewhandler;
    }
}

class Controller {
    use ControllerTrait;
    
    public function __construct(ViewHandlerInterface $viewHandler) {
        $this->setViewHandler($viewHandler);
    }
}
Psalm output (using commit ad5a8c2):

No issues!

@weirdan
Copy link
Collaborator

weirdan commented Oct 21, 2020

It's still weird that final is required.

Not really. If it's neither final nor private, and the class itself is not final then descendants may override the method and leave the property uninitialized.

@oleg-andreyev
Copy link
Author

So by making my class final this issue should go away?

https://psalm.dev/r/e802436284

@psalm-github-bot
Copy link

I found these snippets:

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

interface ViewHandlerInterface {}

trait ControllerTrait {
	private ViewHandlerInterface $viewhandler;
    
    public function setViewHandler(ViewHandlerInterface $viewhandler): void {
    	$this->viewhandler = $viewhandler;
    }
}

final class Controller {
    use ControllerTrait;
    
    public function __construct(ViewHandlerInterface $viewHandler) {
        $this->setViewHandler($viewHandler);
    }
}
Psalm output (using commit ad5a8c2):

ERROR: PropertyNotSetInConstructor - 13:13 - Property Controller::$viewhandler is not defined in constructor of Controller and in any private or final methods called in the constructor

@weirdan
Copy link
Collaborator

weirdan commented Oct 21, 2020

It should, and it does work for inline methods: https://psalm.dev/r/c47bf2af78 , but apparently it fails to consider final when calling a method from trait.

@psalm-github-bot
Copy link

I found these snippets:

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

interface ViewHandlerInterface {}

final class Controller {
    private ViewHandlerInterface $viewhandler;
    
    public function setViewHandler(ViewHandlerInterface $viewhandler): void {
    	$this->viewhandler = $viewhandler;
    }
    
    public function __construct(ViewHandlerInterface $viewHandler) {
        $this->setViewHandler($viewHandler);
    }
}
Psalm output (using commit ad5a8c2):

No issues!

@muglug
Copy link
Collaborator

muglug commented Oct 21, 2020

it's just confused about inherited properties from traits, because that property appears twice. Should be a very simple fix.

@weirdan
Copy link
Collaborator

weirdan commented Oct 21, 2020

because that property appears twice.

It actually doesn't. There are two different properties involved: $viewhandler and $viewHandler (note the case).

Edit: this refers to the original case, not the one discussed later.

@muglug
Copy link
Collaborator

muglug commented Oct 23, 2020

ah ok, sorry

so it's the difference between

<?php

interface A {}

trait ATrait {
    private A $a;
    
    public function setA(A $a): void {
    	$this->a = $a;
    }
}

final class C {
    use ATrait;
    
    public function __construct(A $a) {
        $this->setA($a);
    }
}

and

<?php

interface A {}

trait ATrait {
    private A $a;
    
    public final function setA(A $a): void {
    	$this->a = $a;
    }
}

final class C {
    use ATrait;
    
    public function __construct(A $a) {
        $this->setA($a);
    }
}

@Jean85
Copy link
Contributor

Jean85 commented Jul 14, 2021

Is #6087 the same issue?

@orklah orklah added the traits label Oct 11, 2021
@orklah orklah added the bug label Feb 17, 2022
@AndrolGenhald
Copy link
Collaborator

It looks like this issue is actually just about improving the error message. Psalm is correct here that $viewhandler is not set in the constructor, it's just confusing because there's a very similarly named variable that is set. Maybe saying Controller::ControllerTrait::$viewhandler in the message would make it clear?

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

6 participants