2
2
3
3
namespace ShipMonk \PHPStan \DeadCode \Provider ;
4
4
5
+ use PHPStan \PhpDocParser \Lexer \Lexer ;
6
+ use PHPStan \PhpDocParser \Parser \PhpDocParser ;
7
+ use PHPStan \PhpDocParser \Parser \TokenIterator ;
5
8
use PHPUnit \Framework \Attributes \DataProvider ;
6
9
use PHPUnit \Framework \TestCase ;
7
10
use ReflectionMethod ;
@@ -17,9 +20,15 @@ class PhpUnitEntrypointProvider implements EntrypointProvider
17
20
18
21
private bool $ enabled ;
19
22
20
- public function __construct (bool $ enabled )
23
+ private PhpDocParser $ phpDocParser ;
24
+
25
+ private Lexer $ lexer ;
26
+
27
+ public function __construct (bool $ enabled , PhpDocParser $ phpDocParser , Lexer $ lexer )
21
28
{
22
29
$ this ->enabled = $ enabled ;
30
+ $ this ->lexer = $ lexer ;
31
+ $ this ->phpDocParser = $ phpDocParser ;
23
32
}
24
33
25
34
public function isEntrypoint (ReflectionMethod $ method ): bool
@@ -28,6 +37,8 @@ public function isEntrypoint(ReflectionMethod $method): bool
28
37
return false ;
29
38
}
30
39
40
+ $ this ->gatherDataProviders ($ method );
41
+
31
42
return $ this ->isTestCaseMethod ($ method )
32
43
|| $ this ->isDataProviderMethod ($ method );
33
44
}
@@ -40,21 +51,73 @@ private function isTestCaseMethod(ReflectionMethod $method): bool
40
51
41
52
private function isDataProviderMethod (ReflectionMethod $ originalMethod ): bool
42
53
{
54
+ if (!$ originalMethod ->getDeclaringClass ()->isSubclassOf (TestCase::class)) {
55
+ return false ;
56
+ }
57
+
58
+ $ declaringClass = $ originalMethod ->getDeclaringClass ();
59
+ $ declaringClassName = $ declaringClass ->getName ();
60
+
61
+ return $ this ->dataProviders [$ declaringClassName ][$ originalMethod ->getName ()] ?? false ;
62
+ }
63
+
64
+ private function gatherDataProviders (ReflectionMethod $ originalMethod ): void
65
+ {
66
+ if (!$ originalMethod ->getDeclaringClass ()->isSubclassOf (TestCase::class)) {
67
+ return ;
68
+ }
69
+
43
70
$ declaringClass = $ originalMethod ->getDeclaringClass ();
44
71
$ declaringClassName = $ declaringClass ->getName ();
45
72
46
- if (!isset ($ this ->dataProviders [$ declaringClassName ])) {
47
- foreach ($ declaringClass ->getMethods () as $ method ) {
48
- foreach ($ method ->getAttributes (DataProvider::class) as $ providerAttributeReflection ) {
49
- /** @var DataProvider $providerAttribute */
50
- $ providerAttribute = $ providerAttributeReflection ->newInstance ();
73
+ if (isset ($ this ->dataProviders [$ declaringClassName ])) {
74
+ return ;
75
+ }
76
+
77
+ foreach ($ declaringClass ->getMethods () as $ method ) {
78
+ if ($ method ->getDeclaringClass ()->getName () !== $ declaringClassName ) {
79
+ continue ; // dont iterate parents
80
+ }
81
+
82
+ foreach ($ this ->getDataProvidersFromAnnotations ($ method ->getDocComment ()) as $ dataProvider ) {
83
+ $ this ->dataProviders [$ declaringClassName ][$ dataProvider ] = true ;
84
+ }
51
85
52
- $ this ->dataProviders [ $ declaringClassName ][ $ providerAttribute -> methodName ()] = true ;
53
- }
86
+ foreach ( $ this ->getDataProvidersFromAttributes ( $ method ) as $ dataProvider ) {
87
+ $ this -> dataProviders [ $ declaringClassName ][ $ dataProvider ] = true ;
54
88
}
55
89
}
90
+ }
56
91
57
- return $ this ->dataProviders [$ declaringClassName ][$ originalMethod ->getName ()] ?? false ;
92
+ /**
93
+ * @param false|string $rawPhpDoc
94
+ * @return iterable<string>
95
+ */
96
+ private function getDataProvidersFromAnnotations ($ rawPhpDoc ): iterable
97
+ {
98
+ if ($ rawPhpDoc === false ) {
99
+ return ;
100
+ }
101
+
102
+ $ tokens = new TokenIterator ($ this ->lexer ->tokenize ($ rawPhpDoc ));
103
+ $ phpDoc = $ this ->phpDocParser ->parse ($ tokens );
104
+
105
+ foreach ($ phpDoc ->getTagsByName ('@dataProvider ' ) as $ tag ) {
106
+ yield (string ) $ tag ->value ;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @return iterable<string>
112
+ */
113
+ private function getDataProvidersFromAttributes (ReflectionMethod $ method ): iterable
114
+ {
115
+ foreach ($ method ->getAttributes (DataProvider::class) as $ providerAttributeReflection ) {
116
+ /** @var DataProvider $providerAttribute */
117
+ $ providerAttribute = $ providerAttributeReflection ->newInstance ();
118
+
119
+ yield $ providerAttribute ->methodName ();
120
+ }
58
121
}
59
122
60
123
}
0 commit comments