returnTypeInferer = $returnTypeInferer; $this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard; $this->unionTypeMapper = $unionTypeMapper; $this->phpDocInfoFactory = $phpDocInfoFactory; $this->docBlockUpdater = $docBlockUpdater; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Add union return type', [new CodeSample(<<<'CODE_SAMPLE' final class SomeClass { public function getData() { if (rand(0, 1)) { return null; } if (rand(0, 1)) { return new DateTime('now'); } return new stdClass; } } CODE_SAMPLE , <<<'CODE_SAMPLE' final class SomeClass { public function getData(): null|\DateTime|\stdClass { if (rand(0, 1)) { return null; } if (rand(0, 1)) { return new DateTime('now'); } return new stdClass; } } CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [ClassMethod::class, Function_::class, Closure::class]; } public function provideMinPhpVersion() : int { return PhpVersionFeature::NULLABLE_TYPE; } /** * @param ClassMethod|Function_|Closure $node */ public function refactorWithScope(Node $node, Scope $scope) : ?Node { if ($node->stmts === null) { return null; } if ($node->returnType instanceof Node) { return null; } if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) { return null; } $inferReturnType = $this->returnTypeInferer->inferFunctionLike($node); if (!$inferReturnType instanceof UnionType) { return null; } $returnType = $this->unionTypeMapper->mapToPhpParserNode($inferReturnType, TypeKind::RETURN); if (!$returnType instanceof Node) { return null; } $this->mapStandaloneSubType($node, $inferReturnType); $node->returnType = $returnType; return $node; } /** * @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $node */ private function mapStandaloneSubType($node, UnionType $unionType) : void { $value = null; foreach ($unionType->getTypes() as $type) { if ($type instanceof ConstantBooleanType) { $value = $type->getValue() ? 'true' : 'false'; break; } } if ($value === null) { return; } $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); $returnType = $phpDocInfo->getReturnTagValue(); if (!$returnType instanceof ReturnTagValueNode) { return; } if (!$returnType->type instanceof BracketsAwareUnionTypeNode) { return; } foreach ($returnType->type->types as $key => $type) { if ($type instanceof IdentifierTypeNode && $type->__toString() === 'bool') { $returnType->type->types[$key] = new IdentifierTypeNode($value); $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); break; } } } }