From d1630863ad64e6beb915ac255c41c744b56f3b4a Mon Sep 17 00:00:00 2001 From: Brown Date: Fri, 14 Jun 2019 16:53:40 -0400 Subject: [PATCH] =?UTF-8?q?Fix=20#1787=20-=20prevent=20initialisation=20wh?= =?UTF-8?q?en=20nullable=20isn=E2=80=99t=20set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 12 +-- .../Internal/Visitor/ReflectorVisitor.php | 7 +- tests/PropertyTypeTest.php | 84 +++++++++++++++++++ 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index b0328a841..84b196210 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index c21837b77..58c6f89d3 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -657,7 +657,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer if (!$property_type->isMixed() && !$property_storage->has_default && - !$property_type->isNullable() + !($property_type->isNullable() && $property_type->from_docblock) ) { $property_type->initialized = false; } @@ -1026,7 +1026,9 @@ class ClassAnalyzer extends ClassLikeAnalyzer continue; } - if ($property->type->isMixed() || $property->type->isNullable()) { + if ($property->type->isMixed() + || ($property->type->isNullable() && $property->type->from_docblock) + ) { continue; } @@ -1077,8 +1079,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer $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 */ @@ -1091,8 +1092,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer ); $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 diff --git a/src/Psalm/Internal/Visitor/ReflectorVisitor.php b/src/Psalm/Internal/Visitor/ReflectorVisitor.php index 65c77f5b6..7987e8f76 100644 --- a/src/Psalm/Internal/Visitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/Visitor/ReflectorVisitor.php @@ -2729,12 +2729,11 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse $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; diff --git a/tests/PropertyTypeTest.php b/tests/PropertyTypeTest.php index 45d9f3463..4b63f84de 100644 --- a/tests/PropertyTypeTest.php +++ b/tests/PropertyTypeTest.php @@ -1609,6 +1609,33 @@ class PropertyTypeTest extends TestCase } }', ], + 'nullableDocblockTypedPropertyNoConstructor' => [ + ' [ + ' [ + 'foo; + } + }', + ], ]; } @@ -2460,6 +2487,63 @@ class PropertyTypeTest extends TestCase }', 'error_message' => 'UndefinedInterfaceMethod' ], + 'nullableTypedPropertyNoConstructor' => [ + ' 'MissingConstructor' + ], + 'nullableTypedPropertyEmptyConstructor' => [ + ' 'PropertyNotSetInConstructor' + ], + 'nullableTypedPropertyUseBeforeInitialised' => [ + 'foo; + } + }', + 'error_message' => 'UninitializedProperty' + ], + 'nullableTypedPropertyNoConstructorWithDocblock' => [ + ' 'MissingConstructor' + ], + 'nullableTypedPropertyEmptyConstructorWithDocblock' => [ + ' 'PropertyNotSetInConstructor' + ], + 'nullableTypedPropertyUseBeforeInitialisedWithDocblock' => [ + 'foo; + } + }', + 'error_message' => 'UninitializedProperty' + ], ]; } }