Skip to content

Commit

Permalink
Fix #1787 - prevent initialisation when nullable isn’t set
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Jun 14, 2019
1 parent edf3307 commit d163086
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"require": {
"php": "^7.1",
"nikic/php-parser": "^4.0.2 || ^4.1",
"nikic/php-parser": "^4.2",
"openlss/lib-array2xml": "^1.0",
"ocramius/package-versions": "^1.2",
"composer/xdebug-handler": "^1.1",
Expand Down
12 changes: 6 additions & 6 deletions src/Psalm/Internal/Analyzer/ClassAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ public function analyze(

if (!$property_type->isMixed() &&
!$property_storage->has_default &&
!$property_type->isNullable()
!($property_type->isNullable() && $property_type->from_docblock)
) {
$property_type->initialized = false;
}
Expand Down Expand Up @@ -1026,7 +1026,9 @@ private function checkPropertyInitialization(
continue;
}

if ($property->type->isMixed() || $property->type->isNullable()) {
if ($property->type->isMixed()
|| ($property->type->isNullable() && $property->type->from_docblock)
) {
continue;
}

Expand Down Expand Up @@ -1077,8 +1079,7 @@ private function checkPropertyInitialization(
$constructor_storage = $constructor_class_storage->methods['__construct'];

$fake_constructor_params = array_map(
/** @return PhpParser\Node\Param */
function (FunctionLikeParameter $param) {
function (FunctionLikeParameter $param) : PhpParser\Node\Param {
$fake_param = (new PhpParser\Builder\Param($param->name));
if ($param->signature_type) {
/** @psalm-suppress DeprecatedMethod */
Expand All @@ -1091,8 +1092,7 @@ function (FunctionLikeParameter $param) {
);

$fake_constructor_stmt_args = array_map(
/** @return PhpParser\Node\Arg */
function (FunctionLikeParameter $param) {
function (FunctionLikeParameter $param) : PhpParser\Node\Arg {
return new PhpParser\Node\Arg(new PhpParser\Node\Expr\Variable($param->name));
},
$constructor_storage->params
Expand Down
7 changes: 3 additions & 4 deletions src/Psalm/Internal/Visitor/ReflectorVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2729,12 +2729,11 @@ private function visitPropertyDeclaration(
$signature_type = null;
$signature_type_location = null;

/** @var null|PhpParser\Node\Identifier|PhpParser\Node\Name|PhpParser\Node\NullableType */
$parser_property_type = isset($stmt->type) ? $stmt->type : null;

if ($parser_property_type) {
if ($stmt->type) {
$suffix = '';

$parser_property_type = $stmt->type;

if ($parser_property_type instanceof PhpParser\Node\NullableType) {
$suffix = '|null';
$parser_property_type = $parser_property_type->type;
Expand Down
84 changes: 84 additions & 0 deletions tests/PropertyTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,33 @@ public function getBar() : string {
}
}',
],
'nullableDocblockTypedPropertyNoConstructor' => [
'<?php
class A {
/** @var ?bool */
private $foo;
}',
],
'nullableDocblockTypedPropertyEmptyConstructor' => [
'<?php
class A {
/** @var ?bool */
private $foo;
public function __construct() {}
}',
],
'nullableDocblockTypedPropertyUseBeforeInitialised' => [
'<?php
class A {
/** @var ?bool */
private $foo;
public function __construct() {
echo $this->foo;
}
}',
],
];
}

Expand Down Expand Up @@ -2460,6 +2487,63 @@ public function __construct(FooMore $foo) {
}',
'error_message' => 'UndefinedInterfaceMethod'
],
'nullableTypedPropertyNoConstructor' => [
'<?php
class A {
private ?bool $foo;
}',
'error_message' => 'MissingConstructor'
],
'nullableTypedPropertyEmptyConstructor' => [
'<?php
class A {
private ?bool $foo;
public function __construct() {}
}',
'error_message' => 'PropertyNotSetInConstructor'
],
'nullableTypedPropertyUseBeforeInitialised' => [
'<?php
class A {
private ?bool $foo;
public function __construct() {
echo $this->foo;
}
}',
'error_message' => 'UninitializedProperty'
],
'nullableTypedPropertyNoConstructorWithDocblock' => [
'<?php
class A {
/** @var ?bool */
private ?bool $foo;
}',
'error_message' => 'MissingConstructor'
],
'nullableTypedPropertyEmptyConstructorWithDocblock' => [
'<?php
class A {
/** @var ?bool */
private ?bool $foo;
public function __construct() {}
}',
'error_message' => 'PropertyNotSetInConstructor'
],
'nullableTypedPropertyUseBeforeInitialisedWithDocblock' => [
'<?php
class A {
/** @var ?bool */
private ?bool $foo;
public function __construct() {
echo $this->foo;
}
}',
'error_message' => 'UninitializedProperty'
],
];
}
}

0 comments on commit d163086

Please sign in to comment.