Skip to content

Commit

Permalink
Do not prefix "parent", "self", "static" and native constants (#6159)
Browse files Browse the repository at this point in the history
Co-authored-by: kaizen-ci <info@kaizen-ci.org>
  • Loading branch information
TomasVotruba and kaizen-ci authored Apr 17, 2021
1 parent 95b31d0 commit e8a76e8
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class NamespaceLessClass
{
public function someMethod()
{
$parentClass = parent::someMethod();

$someClass = new Rector\Tests\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector\Source\SomeClass();
}
}
Expand All @@ -22,6 +24,8 @@ class NamespaceLessClass
{
public function someMethod()
{
$parentClass = parent::someMethod();

$someClass = new \Rector\Tests\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector\Source\SomeClass();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

final class NoPrefixConsts
{
public function someMethod()
{
$parentClass = parent::someMethod();

$value = true;
$value = FALSE;
$value = __DIR__;

$native = PREG_GREP_INVERT;

$localConstant = LOCAL_CONSTANT;
}
}

?>
-----
<?php

declare(strict_types=1);

namespace Rector\Tests\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector\Fixture;

final class NoPrefixConsts
{
public function someMethod()
{
$parentClass = parent::someMethod();

$value = true;
$value = FALSE;
$value = __DIR__;

$native = PREG_GREP_INVERT;

$localConstant = \LOCAL_CONSTANT;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

final class SkipClassConsts
{
const KEY = 'value';

public function someMethod()
{
return self::KEY;
}
}

?>
-----
<?php

namespace Rector\Tests\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector\Fixture;

final class SkipClassConsts
{
const KEY = 'value';

public function someMethod()
{
return self::KEY;
}
}

?>
39 changes: 39 additions & 0 deletions rules/DeadCode/NodeAnalyzer/UsedVariableNameAnalyzer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Rector\DeadCode\NodeAnalyzer;

use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use Rector\NodeNameResolver\NodeNameResolver;

final class UsedVariableNameAnalyzer
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

public function __construct(NodeNameResolver $nodeNameResolver)
{
$this->nodeNameResolver = $nodeNameResolver;
}

public function isVariableNamed(Node $node, Variable $variable): bool
{
if (($node instanceof MethodCall || $node instanceof PropertyFetch) && ($node->name instanceof Variable && is_string(
$node->name->name
))) {
return $this->nodeNameResolver->isName($variable, $node->name->name);
}

if (! $node instanceof Variable) {
return false;
}

return $this->nodeNameResolver->areNamesEqual($variable, $node);
}
}
77 changes: 41 additions & 36 deletions rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\NullsafeMethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
Expand All @@ -22,6 +21,7 @@
use Rector\Core\Php\ReservedKeywordAnalyzer;
use Rector\Core\PhpParser\Comparing\ConditionSearcher;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\NodeAnalyzer\UsedVariableNameAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
Expand All @@ -46,13 +46,20 @@ final class RemoveUnusedVariableAssignRector extends AbstractRector
*/
private $conditionSearcher;

/**
* @var UsedVariableNameAnalyzer
*/
private $usedVariableNameAnalyzer;

public function __construct(
ReservedKeywordAnalyzer $reservedKeywordAnalyzer,
CompactFuncCallAnalyzer $compactFuncCallAnalyzer
CompactFuncCallAnalyzer $compactFuncCallAnalyzer,
UsedVariableNameAnalyzer $usedVariableNameAnalyzer
) {
$this->reservedKeywordAnalyzer = $reservedKeywordAnalyzer;
$this->compactFuncCallAnalyzer = $compactFuncCallAnalyzer;
$this->conditionSearcher = new ConditionSearcher();
$this->usedVariableNameAnalyzer = $usedVariableNameAnalyzer;
}

public function getRuleDefinition(): RuleDefinition
Expand Down Expand Up @@ -98,29 +105,19 @@ public function refactor(Node $node): ?Node
return null;
}

/** @var Variable $variable */
$variable = $node->var;
if (is_string($variable->name) && $this->reservedKeywordAnalyzer->isNativeVariable($variable->name)) {
if (! $variable instanceof Variable) {
return null;
}

$variableName = $this->getName($variable);
if ($variableName !== null && $this->reservedKeywordAnalyzer->isNativeVariable($variableName)) {
return null;
}

// variable is used
if ($this->isUsed($node, $variable)) {
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
$ifNode = $parentNode->getAttribute(AttributeKey::NEXT_NODE);

// check if next node is if
if (! $ifNode instanceof If_) {
return null;
}

$nodeFound = $this->conditionSearcher->searchIfAndElseForVariableRedeclaration($node, $ifNode);
if ($nodeFound) {
$this->removeNode($node);
return $node;
}

return null;
return $this->refactorUsedVariable($node);
}

$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
Expand Down Expand Up @@ -149,17 +146,20 @@ private function shouldSkip(Assign $assign): bool
return true;
}

return $variable->name instanceof Variable && (bool) $this->betterNodeFinder->findFirstNext($assign, function (Node $node): bool {
return $node instanceof Variable;
});
return $variable->name instanceof Variable && $this->betterNodeFinder->findFirstNext(
$assign,
function (Node $node): bool {
return $node instanceof Variable;
}
);
}

private function isUsed(Assign $assign, Variable $variable): bool
{
$isUsedPrev = (bool) $this->betterNodeFinder->findFirstPreviousOfNode($variable, function (Node $node) use (
$variable
): bool {
return $this->isVariableNamed($node, $variable);
return $this->usedVariableNameAnalyzer->isVariableNamed($node, $variable);
});

if ($isUsedPrev) {
Expand All @@ -184,7 +184,7 @@ private function isUsedNext(Variable $variable): bool
return (bool) $this->betterNodeFinder->findFirstNext($variable, function (Node $node) use (
$variable
): bool {
if ($this->isVariableNamed($node, $variable)) {
if ($this->usedVariableNameAnalyzer->isVariableNamed($node, $variable)) {
return true;
}

Expand All @@ -201,8 +201,7 @@ private function isUsedNext(Variable $variable): bool
*/
private function isUsedInAssignExpr(Expr $expr, Assign $assign): bool
{
$args = $expr->args;
foreach ($args as $arg) {
foreach ($expr->args as $arg) {
$variable = $arg->value;
if (! $variable instanceof Variable) {
continue;
Expand All @@ -211,8 +210,12 @@ private function isUsedInAssignExpr(Expr $expr, Assign $assign): bool
$previousAssign = $this->betterNodeFinder->findFirstPreviousOfNode($assign, function (Node $node) use (
$variable
): bool {
return $node instanceof Assign && $this->isVariableNamed($node->var, $variable);
return $node instanceof Assign && $this->usedVariableNameAnalyzer->isVariableNamed(
$node->var,
$variable
);
});

if ($previousAssign instanceof Assign) {
return $this->isUsed($assign, $variable);
}
Expand All @@ -226,20 +229,22 @@ private function isCall(Expr $expr): bool
return $expr instanceof FuncCall || $expr instanceof MethodCall || $expr instanceof New_ || $expr instanceof NullsafeMethodCall || $expr instanceof StaticCall;
}

private function isVariableNamed(Node $node, Variable $variable): bool
private function refactorUsedVariable(Assign $assign): ?Assign
{
if ($node instanceof MethodCall && $node->name instanceof Variable && is_string($node->name->name)) {
return $this->isName($variable, $node->name->name);
}
$parentNode = $assign->getAttribute(AttributeKey::PARENT_NODE);

$if = $parentNode->getAttribute(AttributeKey::NEXT_NODE);

if ($node instanceof PropertyFetch && $node->name instanceof Variable && is_string($node->name->name)) {
return $this->isName($variable, $node->name->name);
// check if next node is if
if (! $if instanceof If_) {
return null;
}

if (! $node instanceof Variable) {
return false;
if ($this->conditionSearcher->searchIfAndElseForVariableRedeclaration($assign, $if)) {
$this->removeNode($assign);
return $assign;
}

return $this->isName($variable, (string) $this->getName($node));
return null;
}
}
98 changes: 98 additions & 0 deletions rules/PSR4/NodeManipulator/FullyQualifyStmtsAnalyzer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Rector\PSR4\NodeManipulator;

use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
use PHPStan\Reflection\Constant\RuntimeConstantReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Configuration\Option;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
use Symplify\PackageBuilder\Parameter\ParameterProvider;

final class FullyQualifyStmtsAnalyzer
{
/**
* @var ParameterProvider
*/
private $parameterProvider;

/**
* @var SimpleCallableNodeTraverser
*/
private $simpleCallableNodeTraverser;

/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

/**
* @var ReflectionProvider
*/
private $reflectionProvider;

public function __construct(
ParameterProvider $parameterProvider,
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
NodeNameResolver $nodeNameResolver,
ReflectionProvider $reflectionProvider
) {
$this->parameterProvider = $parameterProvider;
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->nodeNameResolver = $nodeNameResolver;
$this->reflectionProvider = $reflectionProvider;
}

/**
* @param Stmt[] $nodes
*/
public function process(array $nodes): void
{
// no need to
if ($this->parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES)) {
return;
}

// FQNize all class names
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node): ?FullyQualified {
if (! $node instanceof Name) {
return null;
}

$fullyQualifiedName = $this->nodeNameResolver->getName($node);
if (in_array($fullyQualifiedName, ['self', 'parent', 'static'], true)) {
return null;
}

if ($this->isNativeConstant($node)) {
return null;
}

return new FullyQualified($fullyQualifiedName);
});
}

private function isNativeConstant(Name $name): bool
{
$parent = $name->getAttribute(AttributeKey::PARENT_NODE);
if (! $parent instanceof ConstFetch) {
return false;
}

$scope = $name->getAttribute(AttributeKey::SCOPE);
if (! $this->reflectionProvider->hasConstant($name, $scope)) {
return false;
}

$constantReflection = $this->reflectionProvider->getConstant($name, $scope);
return $constantReflection instanceof RuntimeConstantReflection;
}
}
Loading

0 comments on commit e8a76e8

Please sign in to comment.