Skip to content

Commit d282a5f

Browse files
VincentLangletphpstan-bot
authored andcommitted
Fix phpstan/phpstan#14393: Warn about ?? on non-nullable native-typed instance properties
- Removed bail-out in IssetCheck for native-typed instance properties accessed on non-$this objects - Static properties still bail out (valid lazy initialization pattern with ??=) - $this->prop still respects initialization tracking as before - Updated NullCoalesceRuleTest to always expect warning for ReflectionClass::$name - Updated IssetRuleTest to expect warnings for all native-typed instance properties - Removed unnecessary ?? null in ArrayFindFunctionReturnTypeExtension - New regression test in tests/PHPStan/Rules/Variables/data/bug-14393.php
1 parent 43fca98 commit d282a5f

5 files changed

Lines changed: 45 additions & 19 deletions

File tree

src/Rules/IssetCheck.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,21 @@ static function (Type $type) use ($typeMessageCallback): ?string {
182182
}
183183

184184
if (!$scope->hasExpressionType($expr)->yes()) {
185-
if ($expr instanceof Node\Expr\PropertyFetch) {
185+
if (
186+
$expr instanceof Node\Expr\PropertyFetch
187+
&& $expr->var instanceof Expr\Variable
188+
&& $expr->var->name === 'this'
189+
) {
186190
return $this->checkUndefined($expr->var, $scope, $operatorDescription, $identifier);
187191
}
188192

189-
if ($expr->class instanceof Expr) {
190-
return $this->checkUndefined($expr->class, $scope, $operatorDescription, $identifier);
191-
}
193+
if ($expr instanceof Node\Expr\StaticPropertyFetch) {
194+
if ($expr->class instanceof Expr) {
195+
return $this->checkUndefined($expr->class, $scope, $operatorDescription, $identifier);
196+
}
192197

193-
return null;
198+
return null;
199+
}
194200
}
195201
}
196202

src/Type/Php/ArrayFindFunctionReturnTypeExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3737
return null;
3838
}
3939

40-
$arrayArg = $args[0]->value ?? null;
41-
$callbackArg = $args[1]->value ?? null;
40+
$arrayArg = $args[0]->value;
41+
$callbackArg = $args[1]->value;
4242

4343
$resultTypes = $this->arrayFilterFunctionReturnTypeHelper->getType($scope, $arrayArg, $callbackArg, null);
4444
$resultType = TypeCombinator::union(...array_map(static fn ($type) => $type->getIterableValueType(), $resultTypes->getArrays()));

tests/PHPStan/Rules/Variables/IssetRuleTest.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,18 @@ public function testNativePropertyTypes(): void
202202
{
203203
$this->treatPhpDocTypesAsCertain = true;
204204
$this->analyse([__DIR__ . '/data/isset-native-property-types.php'], [
205-
/*[
206-
// no way to achieve this with current PHP Reflection API
207-
// There's ReflectionClass::getDefaultProperties()
208-
// but it cannot differentiate between `public int $foo` and `public int $foo = null`;
205+
[
209206
'Property IssetNativePropertyTypes\Foo::$hasDefaultValue (int) in isset() is not nullable.',
210207
17,
211-
],*/
208+
],
212209
[
213210
'Property IssetNativePropertyTypes\Foo::$isAssignedBefore (int) in isset() is not nullable.',
214211
20,
215212
],
213+
[
214+
'Property IssetNativePropertyTypes\Foo::$canBeUninitialized (int) in isset() is not nullable.',
215+
22,
216+
],
216217
]);
217218
}
218219

tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PHPStan\Rules\Rule;
99
use PHPStan\Testing\RuleTestCase;
1010
use PHPUnit\Framework\Attributes\RequiresPhp;
11-
use const PHP_VERSION_ID;
1211

1312
/**
1413
* @extends RuleTestCase<NullCoalesceRule>
@@ -114,12 +113,10 @@ public function testCoalesceRule(): void
114113
131,
115114
],
116115
];
117-
if (PHP_VERSION_ID < 80100) {
118-
$errors[] = [
119-
'Property ReflectionClass<object>::$name (class-string<object>) on left side of ?? is not nullable.',
120-
136,
121-
];
122-
}
116+
$errors[] = [
117+
'Property ReflectionClass<object>::$name (class-string<object>) on left side of ?? is not nullable.',
118+
136,
119+
];
123120
$errors[] = [
124121
'Variable $foo on left side of ?? is never defined.',
125122
141,
@@ -377,4 +374,14 @@ public function testBug13921(): void
377374
]);
378375
}
379376

377+
public function testBug14393(): void
378+
{
379+
$this->analyse([__DIR__ . '/data/bug-14393.php'], [
380+
[
381+
'Property Bug14393\MyClass::$i (int) on left side of ?? is not nullable.',
382+
12,
383+
],
384+
]);
385+
}
386+
380387
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14393;
4+
5+
class MyClass
6+
{
7+
public int $i = 10;
8+
}
9+
10+
$o = new MyClass();
11+
12+
var_dump($o->i ?? -1);

0 commit comments

Comments
 (0)