1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #358, don’t emit PropertyNotSetInConstructor on public final classes/methods

This commit is contained in:
Matthew Brown 2017-11-29 23:46:56 -05:00
parent 0c2b2b69b3
commit 0ea6540018
5 changed files with 116 additions and 35 deletions

View File

@ -1150,7 +1150,7 @@ class CallChecker
$class_checker = $source->getSource();
if ($class_checker instanceof ClassLikeChecker &&
$method_storage->visibility === ClassLikeChecker::VISIBILITY_PRIVATE
($method_storage->visibility === ClassLikeChecker::VISIBILITY_PRIVATE || $method_storage->final)
) {
$local_vars_in_scope = [];
$local_vars_possibly_in_scope = [];

View File

@ -105,6 +105,11 @@ class ClassLikeStorage
*/
public $abstract = false;
/**
* @var bool
*/
public $final = false;
/**
* @var array<string, string>
*/

View File

@ -22,4 +22,9 @@ class MethodStorage extends FunctionLikeStorage
* @var int
*/
public $visibility;
/**
* @var bool
*/
public $final;
}

View File

@ -237,6 +237,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
if ($node instanceof PhpParser\Node\Stmt\Class_) {
$storage->abstract = (bool)$node->isAbstract();
$storage->final = (bool)$node->isFinal();
$this->project_checker->addFullyQualifiedClassName($fq_classlike_name, $this->file_path);
@ -588,6 +589,8 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
/** @var bool */
$storage->abstract = $stmt->isAbstract();
$storage->final = $class_storage->final || $stmt->isFinal();
if ($stmt->isPrivate()) {
$storage->visibility = ClassLikeChecker::VISIBILITY_PRIVATE;
} elseif ($stmt->isProtected()) {

View File

@ -441,49 +441,49 @@ class PropertyTypeTest extends TestCase
],
'extendsClassWithPrivateConstructorSet' => [
'<?php
namespace Q;
namespace Q;
class Base
{
/**
* @var string
*/
private $aString;
class Base
{
/**
* @var string
*/
private $aString;
public function __construct()
{
$this->aString = "aa";
echo($this->aString);
}
}
public function __construct()
{
$this->aString = "aa";
echo($this->aString);
}
}
class Descendant extends Base
{
/**
* @var bool
*/
private $aBool;
class Descendant extends Base
{
/**
* @var bool
*/
private $aBool;
public function __construct()
{
parent::__construct();
$this->aBool = true;
}
}',
public function __construct()
{
parent::__construct();
$this->aBool = true;
}
}',
],
'extendsClassWithPrivateAndException' => [
'<?php
abstract class A extends \Exception {
/** @var string **/
private $p;
abstract class A extends \Exception {
/** @var string **/
private $p;
/** @param string $p **/
final public function __construct($p) {
$this->p = $p;
}
}
/** @param string $p **/
final public function __construct($p) {
$this->p = $p;
}
}
final class B extends A {}',
final class B extends A {}',
],
'setInAbstractMethod' => [
'<?php
@ -511,6 +511,74 @@ final class B extends A {}',
'PropertyNotSetInConstructor' => Config::REPORT_INFO,
],
],
'setInFinalMethod' => [
'<?php
class C
{
/**
* @var string
*/
private $a;
/**
* @var string
*/
private $b;
/**
* @param string[] $opts
* @psalm-param array{a:string,b:string} $opts
*/
public function __construct(array $opts)
{
$this->setOptions($opts);
}
/**
* @param string[] $opts
* @psalm-param array{a:string,b:string} $opts
*/
final public function setOptions(array $opts): void
{
$this->a = $opts["a"] ?? "defaultA";
$this->b = $opts["b"] ?? "defaultB";
}
}',
],
'setInFinalClass' => [
'<?php
final class C
{
/**
* @var string
*/
private $a;
/**
* @var string
*/
private $b;
/**
* @param string[] $opts
* @psalm-param array{a:string,b:string} $opts
*/
public function __construct(array $opts)
{
$this->setOptions($opts);
}
/**
* @param string[] $opts
* @psalm-param array{a:string,b:string} $opts
*/
public function setOptions(array $opts): void
{
$this->a = $opts["a"] ?? "defaultA";
$this->b = $opts["b"] ?? "defaultB";
}
}',
],
];
}