Skip to content

Commit fd676ed

Browse files
committed
Support multiple tags and improve interface handling in @phpstan-require-extends
1 parent e468be5 commit fd676ed

File tree

2 files changed

+36
-27
lines changed

2 files changed

+36
-27
lines changed

src/Rules/PhpDoc/RequireExtendsCheck.php

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,32 +47,39 @@ public function checkExtendsTags(Node $node, array $extendsTags): array
4747
continue;
4848
}
4949

50-
$class = $type->getObjectClassNames()[0];
51-
$referencedClassReflection = $type->getObjectClassReflections()[0] ?? null;
50+
$referencedClassReflections = array_map(fn ($reflection) => [$reflection, $reflection->getName()], $type->getObjectClassReflections());
51+
$referencedClassReflectionsMap = array_column($referencedClassReflections, 0, 1);
52+
foreach ($type->getObjectClassNames() as $class) {
53+
$referencedClassReflection = $referencedClassReflectionsMap[$class]?? null;
54+
if ($referencedClassReflection === null) {
55+
$errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends contains unknown class %s.', $class))
56+
->discoveringSymbolsTip()
57+
->identifier('class.notFound')
58+
->build();
59+
continue;
60+
}
5261

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-
);
62+
if ($referencedClassReflection->isInterface()) {
63+
$errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain an interface %s, expected a class.', $class))
64+
->tip('If you meant an interface, use @phpstan-require-implements instead.')
65+
->identifier('requireExtends.interface')
66+
->build();
67+
} elseif (!$referencedClassReflection->isClass()) {
68+
$errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain non-class type %s.', $class))
69+
->identifier(sprintf('requireExtends.%s', strtolower($referencedClassReflection->getClassTypeDescription())))
70+
->build();
71+
} elseif ($referencedClassReflection->isFinal()) {
72+
$errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends cannot contain final class %s.', $class))
73+
->identifier('requireExtends.finalClass')
74+
->build();
75+
} else {
76+
$errors = array_merge(
77+
$errors,
78+
$this->classCheck->checkClassNames([
79+
new ClassNameNodePair($class, $node),
80+
], $this->checkClassCaseSensitivity),
81+
);
82+
}
7683
}
7784
}
7885

tests/PHPStan/Rules/PhpDoc/RequireExtendsDefinitionClassRuleTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ public function testRule(): void
4545
8,
4646
],
4747
[
48-
'PHPDoc tag @phpstan-require-extends cannot contain non-class type IncompatibleRequireExtends\SomeInterface.',
48+
'PHPDoc tag @phpstan-require-extends cannot contain an interface IncompatibleRequireExtends\SomeInterface, expected a class.',
4949
13,
50+
'If you meant an interface, use @phpstan-require-implements instead.',
5051
],
5152
[
5253
$enumError,
@@ -75,8 +76,9 @@ public function testRule(): void
7576
121,
7677
],
7778
[
78-
'PHPDoc tag @phpstan-require-extends contains non-object type IncompatibleRequireExtends\UnresolvableExtendsInterface&stdClass.',
79+
'PHPDoc tag @phpstan-require-extends cannot contain an interface IncompatibleRequireExtends\UnresolvableExtendsInterface, expected a class.',
7980
135,
81+
'If you meant an interface, use @phpstan-require-implements instead.',
8082
],
8183
[
8284
'PHPDoc tag @phpstan-require-extends can only be used once.',

0 commit comments

Comments
 (0)