typeFactory = $typeFactory; $this->reflectionProvider = $reflectionProvider; $this->reflectionResolver = $reflectionResolver; $this->strictReturnNewAnalyzer = $strictReturnNewAnalyzer; $this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard; $this->returnTypeInferer = $returnTypeInferer; $this->classAnalyzer = $classAnalyzer; $this->newTypeResolver = $newTypeResolver; $this->betterNodeFinder = $betterNodeFinder; $this->staticTypeMapper = $staticTypeMapper; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Add return type to function like with return new', [new CodeSample(<<<'CODE_SAMPLE' final class SomeClass { public function action() { return new Response(); } } CODE_SAMPLE , <<<'CODE_SAMPLE' final class SomeClass { public function action(): Response { return new Response(); } } CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; } /** * @param ClassMethod|Function_|ArrowFunction $node */ public function refactorWithScope(Node $node, Scope $scope) : ?Node { if ($node->returnType !== null) { return null; } if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { return null; } if (!$node instanceof ArrowFunction) { $returnedNewClassName = $this->strictReturnNewAnalyzer->matchAlwaysReturnVariableNew($node); if (\is_string($returnedNewClassName)) { $node->returnType = new FullyQualified($returnedNewClassName); return $node; } } return $this->refactorDirectReturnNew($node); } public function provideMinPhpVersion() : int { return PhpVersionFeature::SCALAR_TYPES; } /** * @return \PHPStan\Type\ObjectType|\PHPStan\Type\ObjectWithoutClassType|\PHPStan\Type\StaticType|null */ private function createObjectTypeFromNew(New_ $new) { if ($this->classAnalyzer->isAnonymousClass($new->class)) { $newType = $this->newTypeResolver->resolve($new); if (!$newType instanceof ObjectWithoutClassType) { return null; } return $newType; } if (!$new->class instanceof Name) { return null; } $className = $this->getName($new->class); if ($className === ObjectReference::STATIC || $className === ObjectReference::SELF) { $classReflection = $this->reflectionResolver->resolveClassReflection($new); if (!$classReflection instanceof ClassReflection) { throw new ShouldNotHappenException(); } if ($className === ObjectReference::SELF) { return new SelfStaticType($classReflection); } return new StaticType($classReflection); } $classReflection = $this->reflectionProvider->getClass($className); return new ObjectType($className, null, $classReflection); } /** * @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\ArrowFunction|\PhpParser\Node\Expr\Closure $node * @return null|\PhpParser\Node\Expr\ArrowFunction|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure */ private function refactorDirectReturnNew($node) { if ($node instanceof ArrowFunction) { $returns = [new Return_($node->expr)]; } else { /** @var Return_[] $returns */ $returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($node, Return_::class); } if ($returns === []) { return null; } $newTypes = $this->resolveReturnNewType($returns); if ($newTypes === null) { return null; } $returnType = $this->typeFactory->createMixedPassedOrUnionType($newTypes); $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN); if (!$returnTypeNode instanceof Node) { return null; } $returnType = $this->returnTypeInferer->inferFunctionLike($node); if ($returnType instanceof UnionType) { return null; } $node->returnType = $returnTypeNode; return $node; } /** * @param Return_[] $returns * @return Type[]|null */ private function resolveReturnNewType(array $returns) : ?array { $newTypes = []; foreach ($returns as $return) { if (!$return->expr instanceof New_) { return null; } $newType = $this->createObjectTypeFromNew($return->expr); if (!$newType instanceof Type) { return null; } $newTypes[] = $newType; } return $newTypes; } }