Skip to content

Commit 070b0b2

Browse files
authored
Make isset() and null-coalesce (??) operators consistent with each other
* add regression tests for property access with dynamic properties related issues * allow undefined expression for `ObjectWithoutClassType` property fetch * fix test for solved issue * make isset and coalesce consistent * allow overwriting allowedUndefinedExpression because not needed anymore * add regression tests for fixed issues * cs fix * fix levels test for coalesce
1 parent c12cf94 commit 070b0b2

File tree

10 files changed

+122
-45
lines changed

10 files changed

+122
-45
lines changed

src/Analyser/MutatingScope.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3909,9 +3909,6 @@ public function isInExpressionAssign(Expr $expr): bool
39093909
public function setAllowedUndefinedExpression(Expr $expr, bool $isAllowed): self
39103910
{
39113911
$exprString = $this->getNodeKey($expr);
3912-
if (array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions)) {
3913-
return $this;
3914-
}
39153912
$currentlyAllowedUndefinedExpressions = $this->currentlyAllowedUndefinedExpressions;
39163913
$currentlyAllowedUndefinedExpressions[$exprString] = $isAllowed;
39173914

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2320,14 +2320,8 @@ static function (?Type $offsetType, Type $valueType) use (&$arrayType): void {
23202320
static fn (): MutatingScope => $rightResult->getScope()->filterByFalseyValue($expr),
23212321
);
23222322
} elseif ($expr instanceof Coalesce) {
2323-
// backwards compatibility: coalesce doesn't allow dynamic properties
23242323
$nonNullabilityResult = $this->ensureNonNullability($scope, $expr->left, false);
2325-
if ($expr->left instanceof PropertyFetch || $expr->left instanceof Expr\NullsafePropertyFetch || $expr->left instanceof StaticPropertyFetch) {
2326-
$condScope = $nonNullabilityResult->getScope()->setAllowedUndefinedExpression($expr->left, false);
2327-
} else {
2328-
$condScope = $nonNullabilityResult->getScope();
2329-
}
2330-
$condScope = $this->lookForEnterAllowedUndefinedVariable($condScope, $expr->left, true);
2324+
$condScope = $this->lookForEnterAllowedUndefinedVariable($nonNullabilityResult->getScope(), $expr->left, true);
23312325
$condResult = $this->processExprNode($expr->left, $condScope, $nodeCallback, $context->enterDeep());
23322326
$scope = $this->revertNonNullability($condResult->getScope(), $nonNullabilityResult->getSpecifiedExpressions());
23332327
$scope = $this->lookForExitAllowedUndefinedVariable($scope, $expr->left);

tests/PHPStan/Levels/data/coalesce-2.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,6 @@ public function testAccessProperties(): void
129129
'Access to an undefined property TestAccessProperties\FooAccessProperties::$dolor.',
130130
250,
131131
],
132-
[
133-
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
134-
264,
135-
],
136-
[
137-
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
138-
266,
139-
],
140-
[
141-
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
142-
270,
143-
],
144132
[
145133
'Cannot access property $bar on TestAccessProperties\NullCoalesce|null.',
146134
272,
@@ -271,18 +259,6 @@ public function testAccessPropertiesWithoutUnionTypes(): void
271259
'Access to an undefined property TestAccessProperties\FooAccessProperties::$dolor.',
272260
250,
273261
],
274-
[
275-
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
276-
264,
277-
],
278-
[
279-
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
280-
266,
281-
],
282-
[
283-
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
284-
270,
285-
],
286262
[
287263
'Cannot access property $bar on TestAccessProperties\NullCoalesce|null.',
288264
272,
@@ -570,11 +546,36 @@ public function testBug6899(): void
570546
'Cannot access property $prop on string.',
571547
15,
572548
],
573-
[
574-
'Cannot access property $prop on object|string.', // open issue https://github.com/phpstan/phpstan/issues/3659, https://github.com/phpstan/phpstan/issues/6026
575-
25,
576-
],
577549
]);
578550
}
579551

552+
public function testBug6026(): void
553+
{
554+
$this->checkThisOnly = false;
555+
$this->checkUnionTypes = true;
556+
$this->analyse([__DIR__ . '/data/bug-6026.php'], []);
557+
}
558+
559+
public function testBug3659(): void
560+
{
561+
$this->checkThisOnly = false;
562+
$this->checkUnionTypes = true;
563+
$this->analyse([__DIR__ . '/data/bug-3659.php'], []);
564+
}
565+
566+
public function testDynamicProperties(): void
567+
{
568+
$this->checkThisOnly = false;
569+
$this->checkUnionTypes = true;
570+
$this->analyse([__DIR__ . '/data/dynamic-properties.php'], []);
571+
}
572+
573+
574+
public function testBug4559(): void
575+
{
576+
$this->checkThisOnly = false;
577+
$this->checkUnionTypes = true;
578+
$this->analyse([__DIR__ . '/data/bug-4559.php'], []);
579+
}
580+
580581
}

tests/PHPStan/Rules/Properties/AccessStaticPropertiesRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,9 @@ public function testBug5143(): void
207207
$this->analyse([__DIR__ . '/data/bug-5143.php'], []);
208208
}
209209

210+
public function testBug6809(): void
211+
{
212+
$this->analyse([__DIR__ . '/data/bug-6809.php'], []);
213+
}
214+
210215
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Bug3659;
4+
5+
class Foo
6+
{
7+
public function func1(object $obj): void
8+
{
9+
$this->func2($obj->someProperty ?? null);
10+
}
11+
12+
public function func2(?string $param): void
13+
{
14+
echo $param ?? 'test';
15+
}
16+
}
17+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug4559;
4+
5+
class HelloWorld
6+
{
7+
public function doBar()
8+
{
9+
$response = json_decode('');
10+
if (isset($response->error->code)) {
11+
echo $response->error->message ?? '';
12+
}
13+
}
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Bug6026;
4+
5+
class Foo {
6+
/**
7+
* @param resource $theResource
8+
* @return bool
9+
*/
10+
public function Process($theResource) : bool
11+
{
12+
$bucket = \stream_bucket_make_writeable($theResource);
13+
if ( $bucket === null )
14+
{
15+
return false;
16+
}
17+
$bucketLen = $bucket->datalen ?? 0;
18+
$bucketLen = isset($bucket->datalen) ? $bucket->datalen : 0;
19+
20+
return true;
21+
}
22+
}
23+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug6809;
4+
5+
trait SomeTrait {
6+
public function __construct() {
7+
$isClassCool = static::$coolClass ?? true;
8+
}
9+
}
10+
11+
class HelloWorld
12+
{
13+
use SomeTrait;
14+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace DynamicProperties;
4+
5+
class Bar {}
6+
7+
class Foo {
8+
public function doBar() {
9+
isset($this->dynamicProperty);
10+
empty($this->dynamicProperty);
11+
$this->dynamicProperty ?? 'test';
12+
13+
$bar = new Bar();
14+
isset($bar->dynamicProperty);
15+
empty($bar->dynamicProperty);
16+
$bar->dynamicProperty ?? 'test';
17+
}
18+
}
19+

0 commit comments

Comments
 (0)