44
55namespace Rector \TypeDeclaration \Rector \ClassMethod ;
66
7- use Nette \Utils \Strings ;
87use PhpParser \Node ;
98use PhpParser \Node \ArrayItem ;
10- use PhpParser \Node \Attribute ;
11- use PhpParser \Node \AttributeGroup ;
129use PhpParser \Node \Expr \Array_ ;
1310use PhpParser \Node \Expr \Yield_ ;
14- use PhpParser \Node \Scalar \String_ ;
1511use PhpParser \Node \Stmt \Class_ ;
1612use PhpParser \Node \Stmt \ClassMethod ;
1713use PhpParser \Node \Stmt \Return_ ;
18- use PHPStan \PhpDocParser \Ast \PhpDoc \GenericTagValueNode ;
19- use PHPStan \PhpDocParser \Ast \PhpDoc \PhpDocTagNode ;
2014use PHPStan \Type \Constant \ConstantArrayType ;
2115use PHPStan \Type \MixedType ;
2216use PHPStan \Type \Type ;
2317use PHPStan \Type \TypeCombinator ;
24- use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfo ;
25- use Rector \BetterPhpDocParser \PhpDocInfo \PhpDocInfoFactory ;
2618use Rector \NodeTypeResolver \PHPStan \Type \TypeFactory ;
2719use Rector \PhpParser \Node \BetterNodeFinder ;
2820use Rector \PHPStanStaticTypeMapper \Enum \TypeKind ;
2921use Rector \PHPUnit \NodeAnalyzer \TestsNodeAnalyzer ;
3022use Rector \Rector \AbstractRector ;
3123use Rector \StaticTypeMapper \StaticTypeMapper ;
3224use Rector \TypeDeclaration \ValueObject \DataProviderNodes ;
25+ use Rector \TypeDeclarationDocblocks \NodeFinder \DataProviderMethodsFinder ;
3326use Symplify \RuleDocGenerator \ValueObject \CodeSample \CodeSample ;
3427use Symplify \RuleDocGenerator \ValueObject \RuleDefinition ;
3528
@@ -43,16 +36,10 @@ final class AddParamTypeBasedOnPHPUnitDataProviderRector extends AbstractRector
4336 */
4437 private const ERROR_MESSAGE = 'Adds param type declaration based on PHPUnit provider return type declaration ' ;
4538
46- /**
47- * @see https://regex101.com/r/hW09Vt/1
48- * @var string
49- */
50- private const METHOD_NAME_REGEX = '#^(?<method_name>\w+)(\(\))?# ' ;
51-
5239 public function __construct (
5340 private readonly TypeFactory $ typeFactory ,
5441 private readonly TestsNodeAnalyzer $ testsNodeAnalyzer ,
55- private readonly PhpDocInfoFactory $ phpDocInfoFactory ,
42+ private readonly DataProviderMethodsFinder $ dataProviderMethodsFinder ,
5643 private readonly BetterNodeFinder $ betterNodeFinder ,
5744 private readonly StaticTypeMapper $ staticTypeMapper ,
5845 ) {
@@ -131,12 +118,12 @@ public function refactor(Node $node): ?Node
131118 continue ;
132119 }
133120
134- $ dataProviderNodes = $ this ->resolveDataProviderNodes ( $ classMethod );
135- if ($ dataProviderNodes ->isEmpty () ) {
121+ $ dataProviderNodes = $ this ->dataProviderMethodsFinder -> findDataProviderNodes ( $ node , $ classMethod );
122+ if ($ dataProviderNodes ->getClassMethods () === [] ) {
136123 continue ;
137124 }
138125
139- $ hasClassMethodChanged = $ this ->refactorClassMethod ($ classMethod , $ node , $ dataProviderNodes-> nodes );
126+ $ hasClassMethodChanged = $ this ->refactorClassMethod ($ classMethod , $ dataProviderNodes );
140127 if ($ hasClassMethodChanged ) {
141128 $ hasChanged = true ;
142129 }
@@ -149,16 +136,8 @@ public function refactor(Node $node): ?Node
149136 return null ;
150137 }
151138
152- private function inferParam (
153- Class_ $ class ,
154- int $ parameterPosition ,
155- PhpDocTagNode | Attribute $ dataProviderNode
156- ): Type {
157- $ dataProviderClassMethod = $ this ->resolveDataProviderClassMethod ($ class , $ dataProviderNode );
158- if (! $ dataProviderClassMethod instanceof ClassMethod) {
159- return new MixedType ();
160- }
161-
139+ private function inferParam (int $ parameterPosition , ClassMethod $ dataProviderClassMethod ): Type
140+ {
162141 $ returns = $ this ->betterNodeFinder ->findReturnsScoped ($ dataProviderClassMethod );
163142 if ($ returns !== []) {
164143 return $ this ->resolveReturnStaticArrayTypeByParameterPosition ($ returns , $ parameterPosition );
@@ -169,33 +148,6 @@ private function inferParam(
169148 return $ this ->resolveYieldStaticArrayTypeByParameterPosition ($ yields , $ parameterPosition );
170149 }
171150
172- private function resolveDataProviderClassMethod (
173- Class_ $ class ,
174- Attribute | PhpDocTagNode $ dataProviderNode
175- ): ?ClassMethod {
176- if ($ dataProviderNode instanceof Attribute) {
177- $ value = $ dataProviderNode ->args [0 ]->value ;
178-
179- if (! $ value instanceof String_) {
180- return null ;
181- }
182-
183- $ content = $ value ->value ;
184- } elseif ($ dataProviderNode ->value instanceof GenericTagValueNode) {
185- $ content = $ dataProviderNode ->value ->value ;
186- } else {
187- return null ;
188- }
189-
190- $ match = Strings::match ($ content , self ::METHOD_NAME_REGEX );
191- if ($ match === null ) {
192- return null ;
193- }
194-
195- $ methodName = $ match ['method_name ' ];
196- return $ class ->getMethod ($ methodName );
197- }
198-
199151 /**
200152 * @param Return_[] $returns
201153 */
@@ -289,47 +241,7 @@ private function resolveParamOnPositionTypes(Array_ $array, int $parameterPositi
289241 return $ paramOnPositionTypes ;
290242 }
291243
292- private function resolveDataProviderNodes (ClassMethod $ classMethod ): DataProviderNodes
293- {
294- $ attributes = $ this ->getPhpDataProviderAttributes ($ classMethod );
295-
296- $ classMethodPhpDocInfo = $ this ->phpDocInfoFactory ->createFromNode ($ classMethod );
297-
298- $ phpdocNodes = $ classMethodPhpDocInfo instanceof PhpDocInfo ?
299- $ classMethodPhpDocInfo ->getTagsByName ('@dataProvider ' ) : [];
300-
301- return new DataProviderNodes ([...$ attributes , ...$ phpdocNodes ]);
302- }
303-
304- /**
305- * @return array<array-key, Attribute>
306- */
307- private function getPhpDataProviderAttributes (ClassMethod $ classMethod ): array
308- {
309- $ attributeName = 'PHPUnit\Framework\Attributes\DataProvider ' ;
310-
311- /** @var AttributeGroup[] $attrGroups */
312- $ attrGroups = $ classMethod ->attrGroups ;
313-
314- $ dataProviders = [];
315-
316- foreach ($ attrGroups as $ attrGroup ) {
317- foreach ($ attrGroup ->attrs as $ attribute ) {
318- if (! $ this ->isName ($ attribute ->name , $ attributeName )) {
319- continue ;
320- }
321-
322- $ dataProviders [] = $ attribute ;
323- }
324- }
325-
326- return $ dataProviders ;
327- }
328-
329- /**
330- * @param array<Attribute|PhpDocTagNode> $dataProviderNodes
331- */
332- private function refactorClassMethod (ClassMethod $ classMethod , Class_ $ class , array $ dataProviderNodes ): bool
244+ private function refactorClassMethod (ClassMethod $ classMethod , DataProviderNodes $ dataProviderNodes ): bool
333245 {
334246 $ hasChanged = false ;
335247
@@ -343,19 +255,18 @@ private function refactorClassMethod(ClassMethod $classMethod, Class_ $class, ar
343255 }
344256
345257 $ paramTypes = [];
346- foreach ($ dataProviderNodes as $ dataProviderNode ) {
347- $ paramTypes [] = $ this ->inferParam ($ class , $ parameterPosition , $ dataProviderNode );
258+ foreach ($ dataProviderNodes-> getClassMethods () as $ dataProviderClassMethod ) {
259+ $ paramTypes [] = $ this ->inferParam ($ parameterPosition , $ dataProviderClassMethod );
348260 }
349261
350262 $ paramTypeDeclaration = TypeCombinator::union (...$ paramTypes );
351-
352263 if ($ paramTypeDeclaration instanceof MixedType) {
353264 continue ;
354265 }
355266
356- $ type = $ this ->staticTypeMapper ->mapPHPStanTypeToPhpParserNode ($ paramTypeDeclaration , TypeKind::PARAM );
357- if ($ type instanceof Node) {
358- $ param ->type = $ type ;
267+ $ typeNode = $ this ->staticTypeMapper ->mapPHPStanTypeToPhpParserNode ($ paramTypeDeclaration , TypeKind::PARAM );
268+ if ($ typeNode instanceof Node) {
269+ $ param ->type = $ typeNode ;
359270 $ hasChanged = true ;
360271 }
361272 }
0 commit comments