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:
commit
0ea2a6a567
@ -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];
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user