1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 13:51:54 +01:00

Fix #4475 - verify that used attributes actual use the Attribute attribute

This commit is contained in:
Matt Brown 2020-11-22 00:52:56 -05:00 committed by Daniil Gentili
parent 763eff2e8b
commit 14807326fe
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
3 changed files with 65 additions and 40 deletions

View File

@ -129,55 +129,69 @@ class AttributeAnalyzer
$attribute_class_storage = $codebase->classlike_storage_provider->get($attribute->fq_class_name); $attribute_class_storage = $codebase->classlike_storage_provider->get($attribute->fq_class_name);
if ($attribute_class_storage->attributes) { $has_attribute_attribute = $attribute->fq_class_name === 'Attribute';
foreach ($attribute_class_storage->attributes as $attribute_attribute) {
if ($attribute_attribute->fq_class_name === 'Attribute') {
if (!$attribute_attribute->args) {
return;
}
$first_arg = reset($attribute_attribute->args); foreach ($attribute_class_storage->attributes as $attribute_attribute) {
if ($attribute_attribute->fq_class_name === 'Attribute') {
$has_attribute_attribute = true;
$first_arg_type = $first_arg->type; if (!$attribute_attribute->args) {
return;
}
if ($first_arg_type instanceof UnresolvedConstantComponent) { $first_arg = reset($attribute_attribute->args);
$first_arg_type = new Union([
\Psalm\Internal\Codebase\ConstantTypeResolver::resolve(
$codebase->classlikes,
$first_arg_type,
$source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer ? $source : null
)
]);
}
if (!$first_arg_type->isSingleIntLiteral()) { $first_arg_type = $first_arg->type;
return;
}
$acceptable_mask = $first_arg_type->getSingleIntLiteral()->value; if ($first_arg_type instanceof UnresolvedConstantComponent) {
$first_arg_type = new Union([
\Psalm\Internal\Codebase\ConstantTypeResolver::resolve(
$codebase->classlikes,
$first_arg_type,
$source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer ? $source : null
)
]);
}
if (($acceptable_mask & $target) !== $target) { if (!$first_arg_type->isSingleIntLiteral()) {
$target_map = [ return;
1 => 'class', }
2 => 'function',
4 => 'method',
8 => 'property',
16 => 'class constant',
32 => 'function/method parameter'
];
if (\Psalm\IssueBuffer::accepts( $acceptable_mask = $first_arg_type->getSingleIntLiteral()->value;
new InvalidAttribute(
'This attribute can not be used on a ' . $target_map[$target], if (($acceptable_mask & $target) !== $target) {
$attribute->name_location $target_map = [
), 1 => 'class',
$source->getSuppressedIssues() 2 => 'function',
)) { 4 => 'method',
// fall through 8 => 'property',
} 16 => 'class constant',
32 => 'function/method parameter'
];
if (\Psalm\IssueBuffer::accepts(
new InvalidAttribute(
'This attribute can not be used on a ' . $target_map[$target],
$attribute->name_location
),
$source->getSuppressedIssues()
)) {
// fall through
} }
} }
} }
} }
if (!$has_attribute_attribute) {
if (\Psalm\IssueBuffer::accepts(
new InvalidAttribute(
'The class ' . $attribute->fq_class_name . ' doesnt have the Attribute attribute',
$attribute->name_location
),
$source->getSuppressedIssues()
)) {
// fall through
}
}
} }
} }

View File

@ -90,7 +90,7 @@ abstract class Atomic implements TypeNode
/** /**
* @param array{int,int}|null $php_version * @param array{int,int}|null $php_version
* @param array<string, array<string, array{Union}>> $template_type_map * @param array<string, array<string, array{Union}>> $template_type_map
* @param array<string, TypeAlias\LinkableTypeAlias> $type_aliases * @param array<string, TypeAlias> $type_aliases
*/ */
public static function create( public static function create(
string $value, string $value,

View File

@ -111,6 +111,17 @@ class AttributeTest extends TestCase
public function providerInvalidCodeParse(): iterable public function providerInvalidCodeParse(): iterable
{ {
return [ return [
'attributeClassHasNoAttributeAnnotation' => [
'<?php
class A {}
#[A]
class B {}',
'error_message' => 'InvalidAttribute',
[],
false,
'8.0'
],
'missingAttributeOnClass' => [ 'missingAttributeOnClass' => [
'<?php '<?php
use Foo\Bar\Pure; use Foo\Bar\Pure;