silentVoidResolver = $silentVoidResolver; $this->classMethodReturnVendorLockResolver = $classMethodReturnVendorLockResolver; $this->phpDocTypeChanger = $phpDocTypeChanger; $this->reflectionResolver = $reflectionResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Add return type void to function like without any return', [new ConfiguredCodeSample(<<<'CODE_SAMPLE' final class SomeClass { public function getValues() { $value = 1000; return; } } CODE_SAMPLE , <<<'CODE_SAMPLE' final class SomeClass { public function getValues(): void { $value = 1000; return; } } CODE_SAMPLE , [self::USE_PHPDOC => \false])]); } /** * @return array> */ public function getNodeTypes() : array { return [ClassMethod::class, Function_::class, Closure::class]; } /** * @param ClassMethod|Function_|Closure $node */ public function refactor(Node $node) : ?Node { if ($node->returnType !== null) { return null; } if ($this->shouldSkipClassMethod($node)) { return null; } if (!$this->silentVoidResolver->hasExclusiveVoid($node)) { return null; } if ($this->usePhpdoc) { $hasChanged = $this->changePhpDocToVoidIfNotNever($node); if ($hasChanged) { return $node; } return null; } if ($node instanceof ClassMethod && $this->classMethodReturnVendorLockResolver->isVendorLocked($node)) { return null; } $node->returnType = new Identifier('void'); return $node; } public function provideMinPhpVersion() : int { return PhpVersionFeature::VOID_TYPE; } /** * @param mixed[] $configuration */ public function configure(array $configuration) : void { $usePhpdoc = $configuration[self::USE_PHPDOC] ?? (bool) \current($configuration); Assert::boolean($usePhpdoc); $this->usePhpdoc = $usePhpdoc; } /** * @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure|\PhpParser\Node $node */ private function changePhpDocToVoidIfNotNever($node) : bool { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); if ($phpDocInfo->getReturnType() instanceof NeverType) { return \false; } return $this->phpDocTypeChanger->changeReturnType($phpDocInfo, new VoidType()); } /** * @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $functionLike */ private function shouldSkipClassMethod($functionLike) : bool { if (!$functionLike instanceof ClassMethod) { return \false; } if ($functionLike->isMagic()) { return \true; } if ($functionLike->isAbstract()) { return \true; } if ($functionLike->isProtected()) { return !$this->isInsideFinalClass($functionLike); } return \false; } private function isInsideFinalClass(ClassMethod $classMethod) : bool { $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); if (!$classReflection instanceof ClassReflection) { return \false; } $node = $classMethod->getAttribute(AttributeKey::PARENT_NODE); if (!$node instanceof Class_) { return \false; } return $node->isFinal(); } }