1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00

Merge pull request #9742 from fluffycondor/allow-phpdoc-dynamic-properties

Allow dynamic properties from PHPDoc
This commit is contained in:
orklah 2023-05-06 15:56:16 +02:00 committed by GitHub
commit 0ea2a6a567
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 1 deletions

View File

@ -1197,7 +1197,8 @@ class AtomicPropertyFetchAnalyzer
?string $var_id, ?string $var_id,
bool &$has_valid_fetch_type bool &$has_valid_fetch_type
): void { ): void {
if ($config->use_phpdoc_property_without_magic_or_parent if (($config->use_phpdoc_property_without_magic_or_parent
|| $class_storage->hasAttributeIncludingParents('AllowDynamicProperties', $codebase))
&& isset($class_storage->pseudo_property_get_types['$' . $prop_name]) && isset($class_storage->pseudo_property_get_types['$' . $prop_name])
) { ) {
$stmt_type = $class_storage->pseudo_property_get_types['$' . $prop_name]; $stmt_type = $class_storage->pseudo_property_get_types['$' . $prop_name];

View File

@ -4,6 +4,7 @@ namespace Psalm\Storage;
use Psalm\Aliases; use Psalm\Aliases;
use Psalm\CodeLocation; use Psalm\CodeLocation;
use Psalm\Codebase;
use Psalm\Config; use Psalm\Config;
use Psalm\Internal\Analyzer\ClassLikeAnalyzer; use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
use Psalm\Internal\MethodIdentifier; use Psalm\Internal\MethodIdentifier;
@ -486,6 +487,24 @@ final class ClassLikeStorage implements HasAttributesInterface
return $this->attributes; return $this->attributes;
} }
public function hasAttributeIncludingParents(
string $fq_class_name,
Codebase $codebase
): bool {
if ($this->hasAttribute($fq_class_name)) {
return true;
}
foreach ($this->parent_classes as $parent_class) {
$parent_class_storage = $codebase->classlike_storage_provider->get($parent_class);
if ($parent_class_storage->hasAttribute($fq_class_name)) {
return true;
}
}
return false;
}
/** /**
* Get the template constraint types for the class. * Get the template constraint types for the class.
* *
@ -511,4 +530,15 @@ final class ClassLikeStorage implements HasAttributesInterface
{ {
return $this->sealed_methods ?? $config->seal_all_methods; return $this->sealed_methods ?? $config->seal_all_methods;
} }
private function hasAttribute(string $fq_class_name): bool
{
foreach ($this->attributes as $attribute) {
if ($fq_class_name === $attribute->fq_class_name) {
return true;
}
}
return false;
}
} }

View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Psalm\Tests\Internal\Analyzer\Statements\Expression\Fetch;
use Psalm\Tests\TestCase;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
final class AtomicPropertyFetchAnalyzerTest extends TestCase
{
use ValidCodeAnalysisTestTrait;
public function providerValidCodeParse(): iterable
{
return [
'allowDynamicProperties' => [
'code' => '<?php
/** @property-read string $foo */
#[\AllowDynamicProperties]
class A {
public function __construct(string $key, string $value)
{
$this->$key = $value;
}
}
echo (new A("foo", "bar"))->foo;
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.2',
],
'allowDynamicProperties for child' => [
'code' => '<?php
/** @property-read string $foo */
#[\AllowDynamicProperties]
class A {
public function __construct(string $key, string $value)
{
$this->$key = $value;
}
}
class B extends A {}
echo (new B("foo", "bar"))->foo;
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.2',
],
'allowDynamicProperties for grandchild' => [
'code' => '<?php
/** @property-read string $foo */
#[\AllowDynamicProperties]
class A {
public function __construct(string $key, string $value)
{
$this->$key = $value;
}
}
class B extends A {}
class C extends B {}
echo (new C("foo", "bar"))->foo;
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.2',
],
];
}
}