55namespace Rector \TypeDeclaration \Rector \FunctionLike ;
66
77use PhpParser \Node ;
8- use PhpParser \Node \Expr ;
9- use PhpParser \Node \Expr \Closure ;
10- use PhpParser \Node \Expr \Yield_ ;
11- use PhpParser \Node \Expr \YieldFrom ;
12- use PhpParser \Node \FunctionLike ;
13- use PhpParser \Node \Identifier ;
14- use PhpParser \Node \Name ;
15- use PhpParser \Node \Stmt ;
16- use PhpParser \Node \Stmt \Class_ ;
178use PhpParser \Node \Stmt \ClassMethod ;
18- use PhpParser \Node \Stmt \Expression ;
199use PhpParser \Node \Stmt \Function_ ;
20- use PhpParser \NodeVisitor ;
21- use PHPStan \Type \MixedType ;
22- use PHPStan \Type \Type ;
23- use Rector \NodeTypeResolver \PHPStan \Type \TypeFactory ;
24- use Rector \PhpDocParser \NodeTraverser \SimpleCallableNodeTraverser ;
2510use Rector \PHPStan \ScopeFetcher ;
2611use Rector \PHPStanStaticTypeMapper \Enum \TypeKind ;
2712use Rector \Rector \AbstractRector ;
2813use Rector \StaticTypeMapper \StaticTypeMapper ;
29- use Rector \StaticTypeMapper \ ValueObject \ Type \ FullyQualifiedGenericObjectType ;
30- use Rector \StaticTypeMapper \ ValueObject \ Type \ FullyQualifiedObjectType ;
14+ use Rector \TypeDeclarationDocblocks \ NodeFinder \ YieldNodeFinder ;
15+ use Rector \TypeDeclarationDocblocks \ TypeResolver \ YieldTypeResolver ;
3116use Rector \ValueObject \PhpVersionFeature ;
3217use Rector \VendorLocker \NodeVendorLocker \ClassMethodReturnTypeOverrideGuard ;
3318use Rector \VersionBonding \Contract \MinPhpVersionInterface ;
4025final class AddReturnTypeDeclarationFromYieldsRector extends AbstractRector implements MinPhpVersionInterface
4126{
4227 public function __construct (
43- private readonly TypeFactory $ typeFactory ,
44- private readonly SimpleCallableNodeTraverser $ simpleCallableNodeTraverser ,
4528 private readonly StaticTypeMapper $ staticTypeMapper ,
46- private readonly ClassMethodReturnTypeOverrideGuard $ classMethodReturnTypeOverrideGuard
29+ private readonly ClassMethodReturnTypeOverrideGuard $ classMethodReturnTypeOverrideGuard ,
30+ private readonly YieldNodeFinder $ yieldNodeFinder ,
31+ private readonly YieldTypeResolver $ yieldTypeResolver ,
4732 ) {
4833 }
4934
@@ -91,7 +76,8 @@ public function getNodeTypes(): array
9176 public function refactor (Node $ node ): ?Node
9277 {
9378 $ scope = ScopeFetcher::fetch ($ node );
94- $ yieldNodes = $ this ->findCurrentScopeYieldNodes ($ node );
79+
80+ $ yieldNodes = $ this ->yieldNodeFinder ->find ($ node );
9581 if ($ yieldNodes === []) {
9682 return null ;
9783 }
@@ -111,7 +97,7 @@ public function refactor(Node $node): ?Node
11197 return null ;
11298 }
11399
114- $ yieldType = $ this ->resolveYieldType ($ yieldNodes , $ node );
100+ $ yieldType = $ this ->yieldTypeResolver -> resolveFromYieldNodes ($ yieldNodes , $ node );
115101 $ returnTypeNode = $ this ->staticTypeMapper ->mapPHPStanTypeToPhpParserNode ($ yieldType , TypeKind::RETURN );
116102 if (! $ returnTypeNode instanceof Node) {
117103 return null ;
@@ -125,109 +111,4 @@ public function provideMinPhpVersion(): int
125111 {
126112 return PhpVersionFeature::SCALAR_TYPES ;
127113 }
128-
129- /**
130- * @return Yield_[]|YieldFrom[]
131- */
132- private function findCurrentScopeYieldNodes (FunctionLike $ functionLike ): array
133- {
134- $ yieldNodes = [];
135-
136- $ this ->simpleCallableNodeTraverser ->traverseNodesWithCallable ((array ) $ functionLike ->getStmts (), static function (
137- Node $ node
138- ) use (&$ yieldNodes ): ?int {
139- // skip anonymous class and inner function
140- if ($ node instanceof Class_) {
141- return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN ;
142- }
143-
144- // skip nested scope
145- if ($ node instanceof FunctionLike) {
146- return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN ;
147- }
148-
149- if ($ node instanceof Stmt && ! $ node instanceof Expression) {
150- $ yieldNodes = [];
151- return NodeVisitor::STOP_TRAVERSAL ;
152- }
153-
154- if (! $ node instanceof Yield_ && ! $ node instanceof YieldFrom) {
155- return null ;
156- }
157-
158- $ yieldNodes [] = $ node ;
159- return null ;
160- });
161-
162- return $ yieldNodes ;
163- }
164-
165- private function resolveYieldValue (Yield_ | YieldFrom $ yield ): ?Expr
166- {
167- if ($ yield instanceof Yield_) {
168- return $ yield ->value ;
169- }
170-
171- return $ yield ->expr ;
172- }
173-
174- /**
175- * @param array<Yield_|YieldFrom> $yieldNodes
176- * @return Type[]
177- */
178- private function resolveYieldedTypes (array $ yieldNodes ): array
179- {
180- $ yieldedTypes = [];
181-
182- foreach ($ yieldNodes as $ yieldNode ) {
183- $ value = $ this ->resolveYieldValue ($ yieldNode );
184- if (! $ value instanceof Expr) {
185- // one of the yields is empty
186- return [];
187- }
188-
189- $ resolvedType = $ this ->nodeTypeResolver ->getType ($ value );
190- if ($ resolvedType instanceof MixedType) {
191- continue ;
192- }
193-
194- $ yieldedTypes [] = $ resolvedType ;
195- }
196-
197- return $ yieldedTypes ;
198- }
199-
200- /**
201- * @param array<Yield_|YieldFrom> $yieldNodes
202- */
203- private function resolveYieldType (
204- array $ yieldNodes ,
205- ClassMethod |Function_ $ functionLike
206- ): FullyQualifiedObjectType |FullyQualifiedGenericObjectType {
207- $ yieldedTypes = $ this ->resolveYieldedTypes ($ yieldNodes );
208-
209- $ className = $ this ->resolveClassName ($ functionLike );
210-
211- if ($ yieldedTypes === []) {
212- return new FullyQualifiedObjectType ($ className );
213- }
214-
215- $ yieldedTypes = $ this ->typeFactory ->createMixedPassedOrUnionType ($ yieldedTypes );
216- return new FullyQualifiedGenericObjectType ($ className , [$ yieldedTypes ]);
217- }
218-
219- private function resolveClassName (Function_ |ClassMethod |Closure $ functionLike ): string
220- {
221- $ returnTypeNode = $ functionLike ->getReturnType ();
222-
223- if ($ returnTypeNode instanceof Identifier && $ returnTypeNode ->name === 'iterable ' ) {
224- return 'Iterator ' ;
225- }
226-
227- if ($ returnTypeNode instanceof Name && ! $ this ->isName ($ returnTypeNode , 'Generator ' )) {
228- return $ this ->getName ($ returnTypeNode );
229- }
230-
231- return 'Generator ' ;
232- }
233114}
0 commit comments