Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow undefined property fetch of ObjectWithoutClassType in Coalesce #1223

Merged
merged 8 commits into from Apr 20, 2022
Merged
3 changes: 0 additions & 3 deletions src/Analyser/MutatingScope.php
Expand Up @@ -3909,9 +3909,6 @@ public function isInExpressionAssign(Expr $expr): bool
public function setAllowedUndefinedExpression(Expr $expr, bool $isAllowed): self
{
$exprString = $this->getNodeKey($expr);
if (array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions)) {
return $this;
}
$currentlyAllowedUndefinedExpressions = $this->currentlyAllowedUndefinedExpressions;
$currentlyAllowedUndefinedExpressions[$exprString] = $isAllowed;

Expand Down
8 changes: 1 addition & 7 deletions src/Analyser/NodeScopeResolver.php
Expand Up @@ -2309,14 +2309,8 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
static fn (): MutatingScope => $rightResult->getScope()->filterByFalseyValue($expr),
);
} elseif ($expr instanceof Coalesce) {
// backwards compatibility: coalesce doesn't allow dynamic properties
$nonNullabilityResult = $this->ensureNonNullability($scope, $expr->left, false);
if ($expr->left instanceof PropertyFetch || $expr->left instanceof Expr\NullsafePropertyFetch || $expr->left instanceof StaticPropertyFetch) {
$condScope = $nonNullabilityResult->getScope()->setAllowedUndefinedExpression($expr->left, false);
} else {
$condScope = $nonNullabilityResult->getScope();
}
$condScope = $this->lookForEnterAllowedUndefinedVariable($condScope, $expr->left, true);
$condScope = $this->lookForEnterAllowedUndefinedVariable($nonNullabilityResult->getScope(), $expr->left, true);
$condResult = $this->processExprNode($expr->left, $condScope, $nodeCallback, $context->enterDeep());
$scope = $this->revertNonNullability($condResult->getScope(), $nonNullabilityResult->getSpecifiedExpressions());
$scope = $this->lookForExitAllowedUndefinedVariable($scope, $expr->left);
Expand Down
7 changes: 0 additions & 7 deletions tests/PHPStan/Levels/data/coalesce-2.json

This file was deleted.

57 changes: 29 additions & 28 deletions tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php
Expand Up @@ -129,18 +129,6 @@ public function testAccessProperties(): void
'Access to an undefined property TestAccessProperties\FooAccessProperties::$dolor.',
250,
],
[
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
264,
],
[
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
266,
],
[
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
270,
],
[
'Cannot access property $bar on TestAccessProperties\NullCoalesce|null.',
272,
Expand Down Expand Up @@ -271,18 +259,6 @@ public function testAccessPropertiesWithoutUnionTypes(): void
'Access to an undefined property TestAccessProperties\FooAccessProperties::$dolor.',
250,
],
[
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
264,
],
[
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
266,
],
[
'Access to an undefined property TestAccessProperties\NullCoalesce::$bar.',
270,
],
[
'Cannot access property $bar on TestAccessProperties\NullCoalesce|null.',
272,
Expand Down Expand Up @@ -570,11 +546,36 @@ public function testBug6899(): void
'Cannot access property $prop on string.',
15,
],
[
'Cannot access property $prop on object|string.', // open issue https://github.com/phpstan/phpstan/issues/3659, https://github.com/phpstan/phpstan/issues/6026
25,
],
]);
}

public function testBug6026(): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-6026.php'], []);
}

public function testBug3659(): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-3659.php'], []);
}

public function testDynamicProperties(): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/dynamic-properties.php'], []);
}


public function testBug4559(): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-4559.php'], []);
}

}
Expand Up @@ -207,4 +207,9 @@ public function testBug5143(): void
$this->analyse([__DIR__ . '/data/bug-5143.php'], []);
}

public function testBug6809(): void
{
$this->analyse([__DIR__ . '/data/bug-6809.php'], []);
}

}
17 changes: 17 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-3659.php
@@ -0,0 +1,17 @@
<?php

namespace Bug3659;

class Foo
{
public function func1(object $obj): void
{
$this->func2($obj->someProperty ?? null);
}

public function func2(?string $param): void
{
echo $param ?? 'test';
}
}

14 changes: 14 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-4559.php
@@ -0,0 +1,14 @@
<?php declare(strict_types = 1);

namespace Bug4559;

class HelloWorld
{
public function doBar()
{
$response = json_decode('');
if (isset($response->error->code)) {
echo $response->error->message ?? '';
}
}
}
23 changes: 23 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-6026.php
@@ -0,0 +1,23 @@
<?php

namespace Bug6026;

class Foo {
/**
* @param resource $theResource
* @return bool
*/
public function Process($theResource) : bool
{
$bucket = \stream_bucket_make_writeable($theResource);
if ( $bucket === null )
{
return false;
}
$bucketLen = $bucket->datalen ?? 0;
$bucketLen = isset($bucket->datalen) ? $bucket->datalen : 0;

return true;
}
}

14 changes: 14 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-6809.php
@@ -0,0 +1,14 @@
<?php declare(strict_types = 1);

namespace Bug6809;

trait SomeTrait {
public function __construct() {
$isClassCool = static::$coolClass ?? true;
}
}

class HelloWorld
{
use SomeTrait;
}
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Properties/data/dynamic-properties.php
@@ -0,0 +1,19 @@
<?php

namespace DynamicProperties;

class Bar {}

class Foo {
public function doBar() {
isset($this->dynamicProperty);
empty($this->dynamicProperty);
$this->dynamicProperty ?? 'test';

$bar = new Bar();
isset($bar->dynamicProperty);
empty($bar->dynamicProperty);
$bar->dynamicProperty ?? 'test';
}
}