22
33namespace PHPStan \Rules \PHPUnit ;
44
5+ use PhpParser \Comment \Doc ;
56use PhpParser \Modifiers ;
67use PhpParser \Node \Attribute ;
78use PhpParser \Node \Expr \ClassConstFetch ;
1920use PHPStan \Rules \IdentifierRuleError ;
2021use PHPStan \Rules \RuleErrorBuilder ;
2122use PHPStan \Type \FileTypeMapper ;
23+ use ReflectionMethod ;
2224use function array_merge ;
2325use function count ;
2426use function explode ;
2830class 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