breakingVariableRenameGuard = $breakingVariableRenameGuard; $this->expectedNameResolver = $expectedNameResolver; $this->namingConventionAnalyzer = $namingConventionAnalyzer; $this->varTagValueNodeRenamer = $varTagValueNodeRenamer; $this->variableAndCallAssignMatcher = $variableAndCallAssignMatcher; $this->variableRenamer = $variableRenamer; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Rename variable to match method return type', [new CodeSample(<<<'CODE_SAMPLE' class SomeClass { public function run() { $a = $this->getRunner(); } public function getRunner(): Runner { return new Runner(); } } CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { public function run() { $runner = $this->getRunner(); } public function getRunner(): Runner { return new Runner(); } } CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [Assign::class]; } /** * @param Assign $node */ public function refactor(Node $node) : ?Node { $variableAndCallAssign = $this->variableAndCallAssignMatcher->match($node); if (!$variableAndCallAssign instanceof VariableAndCallAssign) { return null; } $call = $variableAndCallAssign->getCall(); if ($this->isMultipleCall($call)) { return null; } $expectedName = $this->expectedNameResolver->resolveForCall($call); if ($expectedName === null) { return null; } if ($this->isName($node->var, $expectedName)) { return null; } if ($this->shouldSkip($variableAndCallAssign, $expectedName)) { return null; } $this->renameVariable($variableAndCallAssign, $expectedName); return $node; } /** * @param \PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall $callNode */ private function isMultipleCall($callNode) : bool { $parentNode = $callNode->getAttribute(AttributeKey::PARENT_NODE); $callNodeClass = \get_class($callNode); while ($parentNode instanceof Node) { $usedNodes = $this->betterNodeFinder->find($parentNode, function (Node $node) use($callNodeClass, $callNode) : bool { $nodeClass = \get_class($node); if ($callNodeClass !== $nodeClass) { return \false; } $usedNodeOriginalNode = $callNode->getAttribute(AttributeKey::ORIGINAL_NODE); if (!$usedNodeOriginalNode instanceof Node) { return \false; } if (\get_class($usedNodeOriginalNode) !== \get_class($callNode)) { return \false; } /** @var FuncCall|StaticCall|MethodCall $node */ $passedNode = clone $node; /** @var FuncCall|StaticCall|MethodCall $usedNodeOriginalNode */ $usedNode = clone $usedNodeOriginalNode; /** @var FuncCall|StaticCall|MethodCall $passedNode */ $passedNode->args = []; /** @var FuncCall|StaticCall|MethodCall $usedNode */ $usedNode->args = []; return $this->nodeComparator->areNodesEqual($passedNode, $usedNode); }); if (\count($usedNodes) > 1) { return \true; } $parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE); } return \false; } private function shouldSkip(VariableAndCallAssign $variableAndCallAssign, string $expectedName) : bool { if ($this->namingConventionAnalyzer->isCallMatchingVariableName($variableAndCallAssign->getCall(), $variableAndCallAssign->getVariableName(), $expectedName)) { return \true; } $isUnionName = Strings::match($variableAndCallAssign->getVariableName(), self::OR_BETWEEN_WORDS_REGEX); if ($isUnionName !== null) { return \true; } return $this->breakingVariableRenameGuard->shouldSkipVariable($variableAndCallAssign->getVariableName(), $expectedName, $variableAndCallAssign->getFunctionLike(), $variableAndCallAssign->getVariable()); } private function renameVariable(VariableAndCallAssign $variableAndCallAssign, string $expectedName) : void { $assign = $variableAndCallAssign->getAssign(); $assignPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($assign); $this->varTagValueNodeRenamer->renameAssignVarTagVariableName($assignPhpDocInfo, $variableAndCallAssign->getVariableName(), $expectedName); $this->variableRenamer->renameVariableInFunctionLike($variableAndCallAssign->getFunctionLike(), $variableAndCallAssign->getVariableName(), $expectedName, $variableAndCallAssign->getAssign()); } }