Skip to content

Commit

Permalink
narrow down $this/static for property assigns
Browse files Browse the repository at this point in the history
  • Loading branch information
schlndh committed Apr 12, 2024
1 parent f8d0564 commit 1bd62a8
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Rules/Properties/PropertyReflectionFinder.php
Expand Up @@ -49,7 +49,7 @@ public function findPropertyReflectionsFromNode($propertyFetch, Scope $scope): a
}

if ($propertyFetch->class instanceof Node\Name) {
$propertyHolderType = $scope->resolveTypeByName($propertyFetch->class);
$propertyHolderType = $scope->getType(new Expr\ClassConstFetch($propertyFetch->class, 'class'))->getClassStringObjectType();
} else {
$propertyHolderType = $scope->getType($propertyFetch->class);
}
Expand Down
34 changes: 34 additions & 0 deletions tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php
Expand Up @@ -1646,4 +1646,38 @@ public function testArgon2PasswordHash(): void
$this->analyse([__DIR__ . '/data/argon2id-password-hash.php'], []);
}

public function testBugInstanceofStaticVsThis(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$this->analyse([__DIR__ . '/../Properties/data/bug-instanceof-static-vs-this-property-assign.php'], [
[
'Parameter #1 $var is passed by reference so it does not accept readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$nativeReadonlyProp.',
17,
],
[
'Parameter #1 $var of function BugInstanceofStaticVsThisPropertyAssign\set expects int, string given.',
24,
],
[
'Parameter #1 $var of function BugInstanceofStaticVsThisPropertyAssign\set expects int, string given.',
25,
],
[
'Parameter #1 $var is passed by reference so it does not accept readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$nativeReadonlyProp.',
31,
],
[
'Parameter #1 $var of function BugInstanceofStaticVsThisPropertyAssign\set expects int, string given.',
38,
],
[
'Parameter #1 $var of function BugInstanceofStaticVsThisPropertyAssign\set expects int, string given.',
39,
],
]);
}

}
Expand Up @@ -68,4 +68,22 @@ public function testRuleIgnoresNativeReadonly(): void
$this->analyse([__DIR__ . '/data/readonly-assign-ref-phpdoc-and-native.php'], []);
}

public function testBugInstanceofStaticVsThis(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$this->analyse([__DIR__ . '/data/bug-instanceof-static-vs-this-property-assign.php'], [
[
'@readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$phpdocReadonlyProp is assigned by reference.',
20,
],
[
'@readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$phpdocReadonlyProp is assigned by reference.',
34,
],
]);
}

}
Expand Up @@ -148,4 +148,22 @@ public function testFeature7648(): void
$this->analyse([__DIR__ . '/data/feature-7648.php'], []);
}

public function testBugInstanceofStaticVsThis(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$this->analyse([__DIR__ . '/data/bug-instanceof-static-vs-this-property-assign.php'], [
[
'@readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$phpdocReadonlyProp is assigned outside of its declaring class.',
16,
],
[
'@readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$phpdocReadonlyProp is assigned outside of its declaring class.',
30,
],
]);
}

}
Expand Up @@ -39,4 +39,22 @@ public function testRule(): void
]);
}

public function testBugInstanceofStaticVsThis(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$this->analyse([__DIR__ . '/data/bug-instanceof-static-vs-this-property-assign.php'], [
[
'Readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$nativeReadonlyProp is assigned by reference.',
19,
],
[
'Readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$nativeReadonlyProp is assigned by reference.',
33,
],
]);
}

}
18 changes: 18 additions & 0 deletions tests/PHPStan/Rules/Properties/ReadOnlyPropertyAssignRuleTest.php
Expand Up @@ -154,4 +154,22 @@ public function testBug6773(): void
]);
}

public function testBugInstanceofStaticVsThis(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$this->analyse([__DIR__ . '/data/bug-instanceof-static-vs-this-property-assign.php'], [
[
'Readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$nativeReadonlyProp is assigned outside of its declaring class.',
15,
],
[
'Readonly property BugInstanceofStaticVsThisPropertyAssign\FooChild::$nativeReadonlyProp is assigned outside of its declaring class.',
29,
],
]);
}

}
Expand Up @@ -608,4 +608,32 @@ public function testUnset(): void
]);
}

public function testBugInstanceofStaticVsThis(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$this->checkExplicitMixed = true;
$message = 'Static property BugInstanceofStaticVsThisPropertyAssign\FooChild::$staticStringProp (string) does not accept int.';
$this->analyse([__DIR__ . '/data/bug-instanceof-static-vs-this-property-assign.php'], [
[
$message,
22,
],
[
$message,
23,
],
[
$message,
36,
],
[
$message,
37,
],
]);
}

}
@@ -0,0 +1,58 @@
<?php declare(strict_types=1); // lint >= 8.1

namespace BugInstanceofStaticVsThisPropertyAssign;

function set(int &$var): void
{
$var = 5;
}

class FooBase
{
public function run(): void
{
if ($this instanceof FooChild) {
$this->nativeReadonlyProp = 5;
$this->phpdocReadonlyProp = 5;
set($this->nativeReadonlyProp);
set($this->phpdocReadonlyProp);
$a = &$this->nativeReadonlyProp;
$b = &$this->phpdocReadonlyProp;

if (rand()) $this::$staticStringProp = 5;
if (rand()) static::$staticStringProp = 5;
set($this::$staticStringProp);
set(static::$staticStringProp);
}

if (is_a(static::class, FooChild::class, true)) {
$this->nativeReadonlyProp = 5;
$this->phpdocReadonlyProp = 5;
set($this->nativeReadonlyProp);
set($this->phpdocReadonlyProp);
$a = &$this->nativeReadonlyProp;
$b = &$this->phpdocReadonlyProp;

if (rand()) $this::$staticStringProp = 5;
if (rand()) static::$staticStringProp = 5;
set($this::$staticStringProp);
set(static::$staticStringProp);
}
}
}

class FooChild extends FooBase
{
public readonly int $nativeReadonlyProp;

/** @readonly */
public int $phpdocReadonlyProp;

public static string $staticStringProp;

public function __construct()
{
$this->nativeReadonlyProp = 5;
$this->phpdocReadonlyProp = 5;
}
}

0 comments on commit 1bd62a8

Please sign in to comment.