Skip to content

Commit 7eff102

Browse files
committed
Support ReflectionMethod|ClassMethod in DataProviderHelper
1 parent c54a269 commit 7eff102

File tree

1 file changed

+87
-46
lines changed

1 file changed

+87
-46
lines changed

src/Rules/PHPUnit/DataProviderHelper.php

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\PHPUnit;
44

5+
use PhpParser\Comment\Doc;
56
use PhpParser\Modifiers;
67
use PhpParser\Node\Attribute;
78
use PhpParser\Node\Expr\ClassConstFetch;
@@ -19,6 +20,7 @@
1920
use PHPStan\Rules\IdentifierRuleError;
2021
use PHPStan\Rules\RuleErrorBuilder;
2122
use PHPStan\Type\FileTypeMapper;
23+
use ReflectionMethod;
2224
use function array_merge;
2325
use function count;
2426
use function explode;
@@ -28,16 +30,8 @@
2830
class DataProviderHelper
2931
{
3032

31-
/**
32-
* Reflection provider.
33-
*
34-
*/
3533
private ReflectionProvider $reflectionProvider;
3634

37-
/**
38-
* The file type mapper.
39-
*
40-
*/
4135
private FileTypeMapper $fileTypeMapper;
4236

4337
private Parser $parser;
@@ -58,56 +52,23 @@ public function __construct(
5852
}
5953

6054
/**
55+
* @param ReflectionMethod|ClassMethod $node
56+
*
6157
* @return iterable<array{ClassReflection|null, string, int}>
6258
*/
6359
public function getDataProviderMethods(
6460
Scope $scope,
65-
ClassMethod $node,
61+
$node,
6662
ClassReflection $classReflection
6763
): iterable
6864
{
69-
$docComment = $node->getDocComment();
70-
if ($docComment !== null) {
71-
$methodPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
72-
$scope->getFile(),
73-
$classReflection->getName(),
74-
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
75-
$node->name->toString(),
76-
$docComment->getText(),
77-
);
78-
foreach ($this->getDataProviderAnnotations($methodPhpDoc) as $annotation) {
79-
$dataProviderValue = $this->getDataProviderAnnotationValue($annotation);
80-
if ($dataProviderValue === null) {
81-
// Missing value is already handled in NoMissingSpaceInMethodAnnotationRule
82-
continue;
83-
}
84-
85-
$dataProviderMethod = $this->parseDataProviderAnnotationValue($scope, $dataProviderValue);
86-
$dataProviderMethod[] = $node->getStartLine();
87-
88-
yield $dataProviderValue => $dataProviderMethod;
89-
}
90-
}
65+
yield from $this->yieldDataProviderAnnotations($node, $scope, $classReflection);
9166

9267
if (!$this->phpunit10OrNewer) {
9368
return;
9469
}
9570

96-
foreach ($node->attrGroups as $attrGroup) {
97-
foreach ($attrGroup->attrs as $attr) {
98-
$dataProviderMethod = null;
99-
if ($attr->name->toLowerString() === 'phpunit\\framework\\attributes\\dataprovider') {
100-
$dataProviderMethod = $this->parseDataProviderAttribute($attr, $classReflection);
101-
} elseif ($attr->name->toLowerString() === 'phpunit\\framework\\attributes\\dataproviderexternal') {
102-
$dataProviderMethod = $this->parseDataProviderExternalAttribute($attr);
103-
}
104-
if ($dataProviderMethod === null) {
105-
continue;
106-
}
107-
108-
yield from $dataProviderMethod;
109-
}
110-
}
71+
yield from $this->yieldDataProviderAttributes($node, $classReflection);
11172
}
11273

11374
/**
@@ -306,4 +267,84 @@ private function parseDataProviderAttribute(Attribute $attribute, ClassReflectio
306267
];
307268
}
308269

270+
/**
271+
* @param ReflectionMethod|ClassMethod $node
272+
*
273+
* @return iterable<array{ClassReflection|null, string, int}>
274+
*/
275+
private function yieldDataProviderAttributes($node, ClassReflection $classReflection): iterable
276+
{
277+
if ($node instanceof ReflectionMethod) {
278+
foreach ($node->getAttributes('PHPUnit\Framework\Attributes\DataProvider') as $attr) {
279+
$args = $attr->getArguments();
280+
if (count($args) !== 1) {
281+
continue;
282+
}
283+
284+
$startLine = $node->getStartLine();
285+
if ($startLine === false) {
286+
$startLine = -1;
287+
}
288+
289+
yield [$classReflection, $args[0], $startLine];
290+
}
291+
292+
return;
293+
}
294+
295+
foreach ($node->attrGroups as $attrGroup) {
296+
foreach ($attrGroup->attrs as $attr) {
297+
$dataProviderMethod = null;
298+
if ($attr->name->toLowerString() === 'phpunit\\framework\\attributes\\dataprovider') {
299+
$dataProviderMethod = $this->parseDataProviderAttribute($attr, $classReflection);
300+
} elseif ($attr->name->toLowerString() === 'phpunit\\framework\\attributes\\dataproviderexternal') {
301+
$dataProviderMethod = $this->parseDataProviderExternalAttribute($attr);
302+
}
303+
if ($dataProviderMethod === null) {
304+
continue;
305+
}
306+
307+
yield from $dataProviderMethod;
308+
}
309+
}
310+
}
311+
312+
/**
313+
* @param ReflectionMethod|ClassMethod $node
314+
*
315+
* @return iterable<array{ClassReflection|null, string, int}>
316+
*/
317+
private function yieldDataProviderAnnotations($node, Scope $scope, ClassReflection $classReflection): iterable
318+
{
319+
$docComment = $node->getDocComment();
320+
if ($docComment === null || $docComment === false) {
321+
return;
322+
}
323+
324+
$methodPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
325+
$scope->getFile(),
326+
$classReflection->getName(),
327+
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
328+
$node instanceof ClassMethod ? $node->name->toString() : $node->getName(),
329+
$docComment instanceof Doc ? $docComment->getText() : $docComment,
330+
);
331+
foreach ($this->getDataProviderAnnotations($methodPhpDoc) as $annotation) {
332+
$dataProviderValue = $this->getDataProviderAnnotationValue($annotation);
333+
if ($dataProviderValue === null) {
334+
// Missing value is already handled in NoMissingSpaceInMethodAnnotationRule
335+
continue;
336+
}
337+
338+
$startLine = $node->getStartLine();
339+
if ($startLine === false) {
340+
$startLine = -1;
341+
}
342+
343+
$dataProviderMethod = $this->parseDataProviderAnnotationValue($scope, $dataProviderValue);
344+
$dataProviderMethod[] = $startLine;
345+
346+
yield $dataProviderValue => $dataProviderMethod;
347+
}
348+
}
349+
309350
}

0 commit comments

Comments
 (0)