-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Readonly classes cannot be combined with
#[AllowDynamicProperties]
- Loading branch information
Showing
10 changed files
with
296 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\Traits; | ||
|
||
use Attribute; | ||
use PhpParser\Node; | ||
use PHPStan\Analyser\MutatingScope; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Reflection\ReflectionProvider; | ||
use PHPStan\Rules\AttributesCheck; | ||
use PHPStan\Rules\Rule; | ||
use PHPStan\Rules\RuleErrorBuilder; | ||
use PHPStan\ShouldNotHappenException; | ||
use function count; | ||
|
||
/** | ||
* @implements Rule<Node\Stmt\Trait_> | ||
*/ | ||
final class TraitAttributesRule implements Rule | ||
{ | ||
|
||
public function __construct( | ||
private AttributesCheck $attributesCheck, | ||
private ReflectionProvider $reflectionProvider, | ||
) | ||
{ | ||
} | ||
|
||
public function getNodeType(): string | ||
{ | ||
return Node\Stmt\Trait_::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
$traitName = $node->namespacedName; | ||
if ($traitName === null) { | ||
return []; | ||
} | ||
|
||
if (!$this->reflectionProvider->hasClass($traitName->toString())) { | ||
return []; | ||
} | ||
|
||
$errors = $this->attributesCheck->check( | ||
$scope, | ||
$node->attrGroups, | ||
Attribute::TARGET_CLASS, | ||
'class', | ||
); | ||
|
||
$classReflection = $this->reflectionProvider->getClass($traitName->toString()); | ||
if (count($classReflection->getNativeReflection()->getAttributes('AllowDynamicProperties')) > 0) { | ||
$errors[] = RuleErrorBuilder::message('Attribute class AllowDynamicProperties cannot be used with trait.') | ||
->identifier('class.allowDynamicPropertiesTrait') | ||
->nonIgnorable() | ||
->build(); | ||
} | ||
|
||
return $errors; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php // lint >= 8.2 | ||
|
||
namespace Bug12281; | ||
|
||
#[\AllowDynamicProperties] | ||
readonly class BlogData { /* … */ } | ||
|
||
/** @readonly */ | ||
#[\AllowDynamicProperties] | ||
class BlogDataPhpdoc { /* … */ } | ||
|
||
#[\AllowDynamicProperties] | ||
enum BlogDataEnum { /* … */ } | ||
|
||
#[\AllowDynamicProperties] | ||
interface BlogDataInterface { /* … */ } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\Traits; | ||
|
||
use PHPStan\Php\PhpVersion; | ||
use PHPStan\Rules\AttributesCheck; | ||
use PHPStan\Rules\ClassCaseSensitivityCheck; | ||
use PHPStan\Rules\Classes\ClassAttributesRule; | ||
use PHPStan\Rules\ClassForbiddenNameCheck; | ||
use PHPStan\Rules\ClassNameCheck; | ||
use PHPStan\Rules\FunctionCallParametersCheck; | ||
use PHPStan\Rules\NullsafeCheck; | ||
use PHPStan\Rules\PhpDoc\UnresolvableTypeHelper; | ||
use PHPStan\Rules\Properties\PropertyReflectionFinder; | ||
use PHPStan\Rules\Rule; | ||
use PHPStan\Rules\RuleLevelHelper; | ||
use PHPStan\Rules\Traits\TraitAttributesRule; | ||
use PHPStan\Testing\RuleTestCase; | ||
use const PHP_VERSION_ID; | ||
|
||
/** | ||
* @extends RuleTestCase<TraitAttributesRule> | ||
*/ | ||
class TraitAttributesRuleTest extends RuleTestCase | ||
{ | ||
|
||
private bool $checkExplicitMixed = false; | ||
|
||
private bool $checkImplicitMixed = false; | ||
|
||
protected function getRule(): Rule | ||
{ | ||
$reflectionProvider = $this->createReflectionProvider(); | ||
return new TraitAttributesRule( | ||
new AttributesCheck( | ||
$reflectionProvider, | ||
new FunctionCallParametersCheck( | ||
new RuleLevelHelper($reflectionProvider, true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false), | ||
new NullsafeCheck(), | ||
new PhpVersion(80000), | ||
new UnresolvableTypeHelper(), | ||
new PropertyReflectionFinder(), | ||
true, | ||
true, | ||
true, | ||
true, | ||
true, | ||
), | ||
new ClassNameCheck( | ||
new ClassCaseSensitivityCheck($reflectionProvider, false), | ||
new ClassForbiddenNameCheck(self::getContainer()), | ||
), | ||
true, | ||
), | ||
$reflectionProvider, | ||
); | ||
} | ||
|
||
public function testRule(): void | ||
{ | ||
$this->analyse([__DIR__ . '/data/trait-attributes.php'], [ | ||
[ | ||
'Attribute class TraitAttributes\AbstractAttribute is abstract.', | ||
8, | ||
], | ||
[ | ||
'Attribute class TraitAttributes\MyTargettedAttribute does not have the class target.', | ||
20, | ||
], | ||
]); | ||
} | ||
|
||
public function testBug12011(): void | ||
{ | ||
if (PHP_VERSION_ID < 80300) { | ||
$this->markTestSkipped('Test requires PHP 8.3.'); | ||
} | ||
|
||
$this->checkExplicitMixed = true; | ||
$this->checkImplicitMixed = true; | ||
|
||
$this->analyse([__DIR__ . '/data/bug-12011.php'], [ | ||
[ | ||
'Parameter #1 $name of attribute class Bug12011Trait\Table constructor expects string|null, int given.', | ||
8, | ||
], | ||
]); | ||
} | ||
|
||
public function testBug12281(): void | ||
{ | ||
$this->analyse([__DIR__ . '/data/bug-12281.php'], [ | ||
[ | ||
'Attribute class AllowDynamicProperties cannot be used with trait.', | ||
11, | ||
], | ||
]); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php // lint >= 8.3 | ||
|
||
namespace Bug12011Trait; | ||
|
||
use Attribute; | ||
|
||
|
||
#[Table(self::TABLE_NAME)] | ||
trait MyTrait | ||
{ | ||
private const int TABLE_NAME = 'table'; | ||
} | ||
|
||
class X { | ||
use MyTrait; | ||
} | ||
|
||
#[Attribute(Attribute::TARGET_CLASS)] | ||
final class Table | ||
{ | ||
public function __construct( | ||
public readonly string|null $name = null, | ||
public readonly string|null $schema = null, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php // lint >= 8.2 | ||
|
||
namespace Bug12281Traits; | ||
|
||
#[\AllowDynamicProperties] | ||
enum BlogDataEnum { /* … */ } // reported by ClassAttributesRule | ||
|
||
#[\AllowDynamicProperties] | ||
interface BlogDataInterface { /* … */ } // reported by ClassAttributesRule | ||
|
||
#[\AllowDynamicProperties] | ||
trait BlogDataTrait { /* … */ } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
namespace TraitAttributes; | ||
|
||
#[\Attribute] | ||
abstract class AbstractAttribute {} | ||
|
||
#[AbstractAttribute] | ||
trait MyTrait {} | ||
|
||
#[\Attribute] | ||
class MyAttribute {} | ||
|
||
#[MyAttribute] | ||
trait MyTrait2 {} | ||
|
||
#[\Attribute(\Attribute::TARGET_PROPERTY)] | ||
class MyTargettedAttribute {} | ||
|
||
#[MyTargettedAttribute] | ||
trait MyTrait3 {} |