|
9 | 9 | use PHPStan\Rules\IdentifierRuleError;
|
10 | 10 | use PHPStan\Rules\RuleErrorBuilder;
|
11 | 11 | use PHPStan\Type\VerbosityLevel;
|
| 12 | +use function array_column; |
| 13 | +use function array_map; |
12 | 14 | use function array_merge;
|
13 | 15 | use function count;
|
14 | 16 | use function sprintf;
|
@@ -47,32 +49,39 @@ public function checkExtendsTags(Node $node, array $extendsTags): array
|
47 | 49 | continue;
|
48 | 50 | }
|
49 | 51 |
|
50 |
| - $class = $type->getObjectClassNames()[0]; |
51 |
| - $referencedClassReflection = $type->getObjectClassReflections()[0] ?? null; |
| 52 | + $referencedClassReflections = array_map(static fn ($reflection) => [$reflection, $reflection->getName()], $type->getObjectClassReflections()); |
| 53 | + $referencedClassReflectionsMap = array_column($referencedClassReflections, 0, 1); |
| 54 | + foreach ($type->getObjectClassNames() as $class) { |
| 55 | + $referencedClassReflection = $referencedClassReflectionsMap[$class] ?? null; |
| 56 | + if ($referencedClassReflection === null) { |
| 57 | + $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends contains unknown class %s.', $class)) |
| 58 | + ->discoveringSymbolsTip() |
| 59 | + ->identifier('class.notFound') |
| 60 | + ->build(); |
| 61 | + continue; |
| 62 | + } |
52 | 63 |
|
53 |
| - if ($referencedClassReflection === null) { |
54 |
| - $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends contains unknown class %s.', $class)) |
55 |
| - ->discoveringSymbolsTip() |
56 |
| - ->identifier('class.notFound') |
57 |
| - ->build(); |
58 |
| - continue; |
59 |
| - } |
60 |
| - |
61 |
| - if (!$referencedClassReflection->isClass()) { |
62 |
| - $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain non-class type %s.', $class)) |
63 |
| - ->identifier(sprintf('requireExtends.%s', strtolower($referencedClassReflection->getClassTypeDescription()))) |
64 |
| - ->build(); |
65 |
| - } elseif ($referencedClassReflection->isFinal()) { |
66 |
| - $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain final class %s.', $class)) |
67 |
| - ->identifier('requireExtends.finalClass') |
68 |
| - ->build(); |
69 |
| - } else { |
70 |
| - $errors = array_merge( |
71 |
| - $errors, |
72 |
| - $this->classCheck->checkClassNames([ |
73 |
| - new ClassNameNodePair($class, $node), |
74 |
| - ], $this->checkClassCaseSensitivity), |
75 |
| - ); |
| 64 | + if ($referencedClassReflection->isInterface()) { |
| 65 | + $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain an interface %s, expected a class.', $class)) |
| 66 | + ->tip('If you meant an interface, use @phpstan-require-implements instead.') |
| 67 | + ->identifier('requireExtends.interface') |
| 68 | + ->build(); |
| 69 | + } elseif (!$referencedClassReflection->isClass()) { |
| 70 | + $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain non-class type %s.', $class)) |
| 71 | + ->identifier(sprintf('requireExtends.%s', strtolower($referencedClassReflection->getClassTypeDescription()))) |
| 72 | + ->build(); |
| 73 | + } elseif ($referencedClassReflection->isFinal()) { |
| 74 | + $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain final class %s.', $class)) |
| 75 | + ->identifier('requireExtends.finalClass') |
| 76 | + ->build(); |
| 77 | + } else { |
| 78 | + $errors = array_merge( |
| 79 | + $errors, |
| 80 | + $this->classCheck->checkClassNames([ |
| 81 | + new ClassNameNodePair($class, $node), |
| 82 | + ], $this->checkClassCaseSensitivity), |
| 83 | + ); |
| 84 | + } |
76 | 85 | }
|
77 | 86 | }
|
78 | 87 |
|
|
0 commit comments