mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Check for not-set-in-constructor errors across traits too
This commit is contained in:
parent
98d4ced24f
commit
c485a3d056
@ -501,23 +501,33 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
$config = Config::getInstance();
|
||||
|
||||
if (!$config->excludeIssueInFile('PropertyNotSetInConstructor', $this->getFilePath())) {
|
||||
$uninitialized_variable = null;
|
||||
$uninitialized_property = null;
|
||||
$uninitialized_variables = [];
|
||||
$uninitialized_properties = [];
|
||||
|
||||
foreach ($storage->appearing_property_ids as $property_name => $appearing_property_id) {
|
||||
if (explode('::', $appearing_property_id)[0] !== $fq_class_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property_class_name = self::getDeclaringClassForProperty($appearing_property_id);
|
||||
$property_class_storage = self::$storage[strtolower((string)$property_class_name)];
|
||||
$property_class_name = self::getDeclaringClassForProperty($appearing_property_id);
|
||||
|
||||
$property = $property_class_storage->properties[$property_name];
|
||||
|
||||
foreach ($storage->properties as $property_name => $property) {
|
||||
if (!$property->has_default &&
|
||||
$property->type &&
|
||||
!$property->type->isMixed() &&
|
||||
!$property->type->isNullable() &&
|
||||
!$property->is_static
|
||||
) {
|
||||
$uninitialized_variable = '$this->' . $property_name;
|
||||
$uninitialized_property = $property;
|
||||
$uninitialized_variables[] = '$this->' . $property_name;
|
||||
$uninitialized_properties[$property_name] = $property;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($uninitialized_variable && $uninitialized_property) {
|
||||
if ($uninitialized_properties) {
|
||||
if (isset($storage->methods['__construct']) && $constructor_checker) {
|
||||
$method_context = clone $class_context;
|
||||
$method_context->collect_initializations = true;
|
||||
@ -525,24 +535,14 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
|
||||
$constructor_checker->analyze($method_context, null, true);
|
||||
|
||||
foreach ($storage->properties as $property_name => $property) {
|
||||
if ($property->has_default ||
|
||||
!$property->type ||
|
||||
$property->type->isMixed() ||
|
||||
$property->type->isNullable() ||
|
||||
$property->is_static ||
|
||||
!$property->location
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($uninitialized_properties as $property_name => $property) {
|
||||
if (!isset($method_context->vars_in_scope['$this->' . $property_name])) {
|
||||
throw new \UnexpectedValueException('$this->' . $property_name . ' should be in scope');
|
||||
}
|
||||
|
||||
$end_type = $method_context->vars_in_scope['$this->' . $property_name];
|
||||
|
||||
if (!$end_type->initialized) {
|
||||
if (!$end_type->initialized && $property->location) {
|
||||
$property_id = $this->fq_class_name . '::$' . $property_name;
|
||||
|
||||
if (IssueBuffer::accepts(
|
||||
@ -557,16 +557,20 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($uninitialized_property->location) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MissingConstructor(
|
||||
$fq_class_name . ' has an unitiialized variable ' . $uninitialized_variable .
|
||||
', but no constructor',
|
||||
$uninitialized_property->location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
} elseif (!$this instanceof TraitChecker) {
|
||||
$first_uninitialized_property = array_shift($uninitialized_properties);
|
||||
|
||||
if ($first_uninitialized_property->location) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MissingConstructor(
|
||||
$fq_class_name . ' has an unitiialized variable ' . $uninitialized_variables[0] .
|
||||
', but no constructor',
|
||||
$first_uninitialized_property->location
|
||||
),
|
||||
$this->source->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -753,6 +753,59 @@ class PropertyTypeTest extends PHPUnit_Framework_TestCase
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage PropertyNotSetInConstructor
|
||||
* @return void
|
||||
*/
|
||||
public function testDefinedInTraitNotSetInEmptyConstructor()
|
||||
{
|
||||
$this->project_checker->registerFile(
|
||||
getcwd() . '/somefile.php',
|
||||
'<?php
|
||||
trait A {
|
||||
/** @var string **/
|
||||
public $a;
|
||||
}
|
||||
class B {
|
||||
use A;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
}'
|
||||
);
|
||||
|
||||
$file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testDefinedInTraitSetInConstructor()
|
||||
{
|
||||
$this->project_checker->registerFile(
|
||||
getcwd() . '/somefile.php',
|
||||
'<?php
|
||||
trait A {
|
||||
/** @var string **/
|
||||
public $a;
|
||||
}
|
||||
class B {
|
||||
use A;
|
||||
|
||||
public function __construct() {
|
||||
$this->a = "hello";
|
||||
}
|
||||
}'
|
||||
);
|
||||
|
||||
$file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
@ -113,7 +113,7 @@ class TraitTest extends PHPUnit_Framework_TestCase
|
||||
$stmts = self::$parser->parse('<?php
|
||||
trait T {
|
||||
/** @var string */
|
||||
private $fooFoo;
|
||||
private $fooFoo = "";
|
||||
}
|
||||
|
||||
class B {
|
||||
@ -137,7 +137,7 @@ class TraitTest extends PHPUnit_Framework_TestCase
|
||||
$stmts = self::$parser->parse('<?php
|
||||
trait T {
|
||||
/** @var string */
|
||||
protected $fooFoo;
|
||||
protected $fooFoo = "";
|
||||
}
|
||||
|
||||
class B {
|
||||
@ -161,7 +161,7 @@ class TraitTest extends PHPUnit_Framework_TestCase
|
||||
$stmts = self::$parser->parse('<?php
|
||||
trait T {
|
||||
/** @var string */
|
||||
public $fooFoo;
|
||||
public $fooFoo = "";
|
||||
}
|
||||
|
||||
class B {
|
||||
|
Loading…
Reference in New Issue
Block a user