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

Fix #173 - don’t emit MissingConstructor issues from trait constructor

This commit is contained in:
Matthew Brown 2017-06-21 01:25:41 -04:00
parent e5c25eae97
commit 878696b72c
2 changed files with 99 additions and 80 deletions

View File

@ -625,12 +625,16 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
foreach ($trait_checker->class->stmts as $trait_stmt) {
if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
$this->analyzeClassMethod(
$trait_method_checker = $this->analyzeClassMethod(
$trait_stmt,
$trait_checker,
$class_context,
$global_context
);
if ($trait_stmt->name === '__construct') {
$constructor_checker = $trait_method_checker;
}
}
}
}
@ -666,8 +670,8 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
}
}
if ($uninitialized_properties) {
if (isset($storage->methods['__construct']) && $constructor_checker) {
if ($uninitialized_properties && !($this instanceof TraitChecker)) {
if ($constructor_checker) {
$method_context = clone $class_context;
$method_context->collect_initializations = true;
$method_context->vars_in_scope['$this'] = Type::parseString($fq_class_name);
@ -697,7 +701,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
}
}
}
} elseif (!$this instanceof TraitChecker) {
} else {
$first_uninitialized_property = array_shift($uninitialized_properties);
if ($first_uninitialized_property->location) {

View File

@ -55,14 +55,14 @@ class PropertyTypeTest extends TestCase
* @var mixed
*/
public $foo;
/** @return void */
public function barBar()
{
if (rand(0,10) === 5) {
$this->foo = [];
}
if (!is_array($this->foo)) {
// do something
}
@ -74,7 +74,7 @@ class PropertyTypeTest extends TestCase
class A {
public $foo;
}
$a = (new A)->foo;',
'assertions' => [],
'error_levels' => [
@ -88,7 +88,7 @@ class PropertyTypeTest extends TestCase
/** @return void */
function foo() {
$boop = $this->foo === null && rand(0,1);
echo $this->foo->baz;
}
}',
@ -110,10 +110,10 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $foo = "";
}
$a = rand(0, 10) ? new A() : (rand(0, 10) ? new B() : null);
$b = null;
if ($a instanceof A || $a instanceof B) {
$b = $a->foo;
}',
@ -131,10 +131,10 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $foo = "";
}
$a = rand(0, 10) ? new A() : new B();
$b = null;
if (rand(0, 10) === 4) {
// do nothing
}
@ -151,14 +151,14 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $aa = "";
}
class B {
/** @var A|null */
public $bb;
}
$b = rand(0, 10) ? new A() : new B();
if ($b instanceof B && isset($b->bb) && $b->bb->aa === "aa") {
echo $b->bb->aa;
}',
@ -169,13 +169,13 @@ class PropertyTypeTest extends TestCase
/** @var string|null */
public $aa;
}
$a = new A();
if (!$a->aa) {
$a->aa = "hello";
}
echo substr($a->aa, 1);',
],
'nullableStaticPropertyWithIfCheck' => [
@ -183,12 +183,12 @@ class PropertyTypeTest extends TestCase
class A {
/** @var A|null */
public static $fooFoo;
public static function getFoo() : A {
if (!self::$fooFoo) {
self::$fooFoo = new A();
}
return self::$fooFoo;
}
}',
@ -197,9 +197,9 @@ class PropertyTypeTest extends TestCase
'<?php
class Foo {
}
$a = new \ReflectionMethod("Foo", "__construct");
echo $a->name . " - " . $a->class;',
],
'grandparentReflectedProperties' => [
@ -213,16 +213,16 @@ class PropertyTypeTest extends TestCase
'goodArrayProperties' => [
'<?php
interface I1 {}
class A1 implements I1{}
class B1 implements I1 {}
class C1 {
/** @var array<I1> */
public $is = [];
}
$c = new C1;
$c->is = [new A1];
$c->is = [new A1, new A1];
@ -234,11 +234,11 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
}
$a = new A();
if (isset($a->bar)) {
}',
],
'notSetInConstructorButHasDefault' => [
@ -246,7 +246,7 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a = 0;
public function __construct() { }
}',
],
@ -255,11 +255,11 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() {
$this->foo();
}
private function foo() : void {
$this->a = 5;
}
@ -273,7 +273,7 @@ class PropertyTypeTest extends TestCase
}
class B {
use A;
public function __construct() {
$this->a = "hello";
}
@ -284,15 +284,15 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() {
$this->foo();
}
private function foo() : void {
$this->bar();
}
private function bar() : void {
$this->a = 5;
}
@ -301,11 +301,11 @@ class PropertyTypeTest extends TestCase
'propertyArrayIssetAssertion' => [
'<?php
function bar(string $s) : void { }
class A {
/** @var array<string, string> */
public $a = [];
private function foo() : void {
if (isset($this->a["hello"])) {
bar($this->a["hello"]);
@ -316,18 +316,18 @@ class PropertyTypeTest extends TestCase
'propertyArrayIssetAssertionWithVariableOffset' => [
'<?php
function bar(string $s) : void { }
class A {
/** @var array<string, string> */
public $a = [];
private function foo() : void {
$b = "hello";
if (!isset($this->a[$b])) {
return;
}
bar($this->a[$b]);
}
}',
@ -335,22 +335,37 @@ class PropertyTypeTest extends TestCase
'staticPropertyArrayIssetAssertionWithVariableOffset' => [
'<?php
function bar(string $s) : void { }
class A {
/** @var array<string, string> */
public static $a = [];
}
function foo() : void {
$b = "hello";
if (!isset(A::$a[$b])) {
return;
}
bar(A::$a[$b]);
}',
],
'traitConstructor' => [
'<?php
trait T {
/** @var string **/
public $foo;
public function __construct() {
$this->foo = "hello";
}
}
class A {
use T;
}',
],
];
}
@ -364,7 +379,7 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
}
(new A)->foo = "cool";',
'error_message' => 'UndefinedPropertyAssignment',
],
@ -372,7 +387,7 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
}
echo (new A)->foo;',
'error_message' => 'UndefinedPropertyFetch',
],
@ -398,7 +413,7 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
}
/** @psalm-suppress UndefinedPropertyAssignment */
function fooDo() : void {
(new A)->foo = "cool";
@ -409,7 +424,7 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
public $foo;
public function assignToFoo() : void {
$this->foo = 5;
}
@ -421,7 +436,7 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
public $foo;
public function __construct() : void {
$this->foo = 5;
}
@ -433,11 +448,11 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
public $foo;
public function __construct() : void {
$this->foo = 5;
}
public function makeNull() : void {
$this->foo = null;
}
@ -450,11 +465,11 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
public $foo;
public function __construct() : void {
$this->makeValue();
}
private function makeValue() : void {
$this->foo = 5;
}
@ -466,7 +481,7 @@ class PropertyTypeTest extends TestCase
'<?php
class A {
public $foo = null;
public function __construct() : void {
$this->foo = 5;
}
@ -479,7 +494,7 @@ class PropertyTypeTest extends TestCase
class A {
/** @var string */
public $foo;
public function barBar() : void
{
$this->foo = 5;
@ -505,10 +520,10 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $foo = "";
}
/** @var mixed */
$a = (new Foo());
echo $a->foo;',
'error_message' => 'MixedPropertyFetch',
'error_levels' => [
@ -522,10 +537,10 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $foo = "";
}
/** @var mixed */
$a = (new Foo());
$a->foo = "hello";',
'error_message' => '',
'error_levels' => [
@ -539,16 +554,16 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $foo = "";
}
$a = rand(0, 10) ? new Foo() : null;
$a->foo = "hello";',
'error_message' => 'PossiblyNullPropertyAssignment',
],
'nullablePropertyAssignment' => [
'<?php
$a = null;
$a->foo = "hello";',
'error_message' => 'NullPropertyAssignment',
],
@ -558,30 +573,30 @@ class PropertyTypeTest extends TestCase
/** @var string */
public $foo = "";
}
$a = rand(0, 10) ? new Foo() : null;
echo $a->foo;',
'error_message' => 'PossiblyNullPropertyFetch',
],
'nullablePropertyFetch' => [
'<?php
$a = null;
echo $a->foo;',
'error_message' => 'NullPropertyFetch',
],
'badArrayProperty' => [
'<?php
class A {}
class B {}
class C {
/** @var array<B> */
public $bb;
}
$c = new C;
$c->bb = [new A, new B];',
'error_message' => 'InvalidPropertyAssignment',
@ -592,7 +607,7 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() { }
}',
'error_message' => 'PropertyNotSetInConstructor',
@ -610,7 +625,7 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() {
if (rand(0, 1)) {
$this->a = 5;
@ -624,11 +639,11 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() {
$this->foo();
}
protected function foo() : void {
$this->a = 5;
}
@ -643,7 +658,7 @@ class PropertyTypeTest extends TestCase
}
class B {
use A;
public function __construct() {
}
}',
@ -654,13 +669,13 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() {
if (rand(0, 1)) {
$this->foo();
}
}
private function foo() : void {
$this->a = 5;
}
@ -672,7 +687,7 @@ class PropertyTypeTest extends TestCase
class A {
/** @var int */
public $a;
public function __construct() {
if (rand(0, 1)) {
$this->foo();
@ -680,11 +695,11 @@ class PropertyTypeTest extends TestCase
$this->bar();
}
}
private function foo() : void {
$this->a = 5;
}
private function bar() : void {
$this->a = 5;
}