Skip to content

Commit

Permalink
[Defluent] Remove often-breaking set (#1062)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Oct 26, 2021
1 parent 87a6907 commit 461cb9f
Show file tree
Hide file tree
Showing 194 changed files with 153 additions and 6,476 deletions.
28 changes: 6 additions & 22 deletions config/set/defluent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,16 @@

declare(strict_types=1);

use Rector\Defluent\Rector\ClassMethod\ReturnThisRemoveRector;
use Rector\Defluent\Rector\MethodCall\FluentChainMethodCallToNormalMethodCallRector;
use Rector\Defluent\Rector\MethodCall\InArgFluentChainMethodCallToStandaloneMethodCallRector;
use Rector\Defluent\Rector\MethodCall\MethodCallOnSetterMethodCallToStandaloneAssignRector;
use Rector\Defluent\Rector\MethodCall\NewFluentChainMethodCallToNonFluentRector;
use Rector\Defluent\Rector\Return_\DefluentReturnMethodCallRector;
use Rector\Defluent\Rector\Return_\ReturnFluentChainMethodCallToNormalMethodCallRector;
use Rector\Defluent\Rector\Return_\ReturnNewFluentChainMethodCallToNonFluentRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

// @see https://ocramius.github.io/blog/fluent-interfaces-are-evil/
// @see https://www.yegor256.com/2018/03/13/fluent-interfaces.html

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();

// variable/property
$services->set(FluentChainMethodCallToNormalMethodCallRector::class);
$services->set(ReturnFluentChainMethodCallToNormalMethodCallRector::class);

// new
$services->set(NewFluentChainMethodCallToNonFluentRector::class);
$services->set(ReturnNewFluentChainMethodCallToNonFluentRector::class);

$services->set(ReturnThisRemoveRector::class);
$services->set(DefluentReturnMethodCallRector::class);
$services->set(MethodCallOnSetterMethodCallToStandaloneAssignRector::class);
$services->set(InArgFluentChainMethodCallToStandaloneMethodCallRector::class);
$deprecatedMessage = sprintf(
'The DEFLUENT set is deprecated for high number of assumptions and reported bugs. Better use PHPStan rule "%s" to warn about these cases and refactor manually.',
'https://github.com/symplify/phpstan-rules/blob/main/docs/rules_overview.md#nochainmethodcallrule'
);
trigger_error($deprecatedMessage);
sleep(3);
};
4 changes: 0 additions & 4 deletions config/set/guzzle50.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

declare(strict_types=1);

use Rector\Defluent\Rector\MethodCall\FluentChainMethodCallToNormalMethodCallRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector;
Expand All @@ -15,9 +14,6 @@
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();

# both uses "%classes_to_defluent%
$services->set(FluentChainMethodCallToNormalMethodCallRector::class);

$configuration = [
new FuncCallToMethodCall('GuzzleHttp\json_decode', 'GuzzleHttp\Utils', 'jsonDecode'),
new FuncCallToMethodCall('GuzzleHttp\get_path', 'GuzzleHttp\Utils', 'getPath'),
Expand Down
143 changes: 143 additions & 0 deletions packages/Defluent/NodeAnalyzer/FluentChainMethodCallNodeAnalyzer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

declare(strict_types=1);

namespace Rector\Defluent\NodeAnalyzer;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;

/**
* Utils for chain of MethodCall Node:
* "$this->methodCall()->chainedMethodCall()"
*/
final class FluentChainMethodCallNodeAnalyzer
{
public function __construct(
private NodeNameResolver $nodeNameResolver,
private NodeTypeResolver $nodeTypeResolver,
) {
}

/**
* @return string[]
*/
public function collectMethodCallNamesInChain(MethodCall $desiredMethodCall): array
{
$methodCalls = $this->collectAllMethodCallsInChain($desiredMethodCall);

$methodNames = [];
foreach ($methodCalls as $methodCall) {
$methodName = $this->nodeNameResolver->getName($methodCall->name);
if ($methodName === null) {
continue;
}

$methodNames[] = $methodName;
}

return $methodNames;
}

/**
* @return MethodCall[]
*/
public function collectAllMethodCallsInChain(MethodCall $methodCall): array
{
$chainMethodCalls = [$methodCall];

// traverse up
$currentNode = $methodCall->var;
while ($currentNode instanceof MethodCall) {
$chainMethodCalls[] = $currentNode;
$currentNode = $currentNode->var;
}

// traverse down
if (count($chainMethodCalls) === 1) {
$currentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE);
while ($currentNode instanceof MethodCall) {
$chainMethodCalls[] = $currentNode;
$currentNode = $currentNode->getAttribute(AttributeKey::PARENT_NODE);
}
}

return $chainMethodCalls;
}

/**
* Checks "$this->someMethod()->anotherMethod()"
*
* @param string[] $methods
*/
public function isTypeAndChainCalls(Node $node, Type $type, array $methods): bool
{
if (! $node instanceof MethodCall) {
return false;
}

$rootMethodCall = $this->resolveRootMethodCall($node);
if (! $rootMethodCall instanceof MethodCall) {
return false;
}

$rootMethodCallVarType = $this->nodeTypeResolver->getType($rootMethodCall->var);
if (! $rootMethodCallVarType instanceof FullyQualifiedObjectType) {
return false;
}

// node chaining is in reverse order than code
$methods = array_reverse($methods);

foreach ($methods as $method) {
if (! $this->nodeNameResolver->isName($node->name, $method)) {
return false;
}

$node = $node->var;
}

$variableType = $this->nodeTypeResolver->getType($node);
if ($variableType instanceof MixedType) {
return false;
}

return $variableType->isSuperTypeOf($type)
->yes();
}

public function resolveRootExpr(MethodCall $methodCall): Expr | Name
{
$callerNode = $methodCall->var;

while ($callerNode instanceof MethodCall || $callerNode instanceof StaticCall) {
$callerNode = $callerNode instanceof StaticCall ? $callerNode->class : $callerNode->var;
}

return $callerNode;
}

public function resolveRootMethodCall(MethodCall $methodCall): ?MethodCall
{
$callerNode = $methodCall->var;

while ($callerNode instanceof MethodCall && $callerNode->var instanceof MethodCall) {
$callerNode = $callerNode->var;
}

if ($callerNode instanceof MethodCall) {
return $callerNode;
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Reflection\MethodReflection;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\Defluent\Contract\ValueObject\FirstCallFactoryAwareInterface;

final class SameClassMethodCallAnalyzer
{
Expand Down Expand Up @@ -37,23 +36,4 @@ public function haveSingleClass(array $chainMethodCalls): bool
$uniqueClasses = array_unique($classOfClassMethod);
return count($uniqueClasses) < 2;
}

/**
* @param string[] $calleeUniqueTypes
*/
public function isCorrectTypeCount(
array $calleeUniqueTypes,
FirstCallFactoryAwareInterface $firstCallFactoryAware
): bool {
if ($calleeUniqueTypes === []) {
return false;
}

// in case of factory method, 2 methods are allowed
if ($firstCallFactoryAware->isFirstCallFactory()) {
return count($calleeUniqueTypes) === 2;
}

return count($calleeUniqueTypes) === 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

declare(strict_types=1);

namespace Rector\Defluent\ConflictGuard;
namespace Rector\VendorLocker;

use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Type;
use Rector\CodingStyle\Reflection\VendorLocationDetector;
use Rector\Core\PhpParser\AstResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
Expand All @@ -20,44 +19,12 @@ final class ParentClassMethodTypeOverrideGuard
{
public function __construct(
private NodeNameResolver $nodeNameResolver,
private VendorLocationDetector $vendorLocationDetector,
private PathNormalizer $pathNormalizer,
private AstResolver $astResolver,
private ParamTypeInferer $paramTypeInferer
) {
}

public function hasParentMethodOutsideVendor(ClassMethod $classMethod): bool
{
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return false;
}

$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return false;
}

$methodName = $classMethod->name->toString();

foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
if ($classReflection === $ancestorClassReflection) {
continue;
}

if (! $ancestorClassReflection->hasMethod($methodName)) {
continue;
}

if ($this->vendorLocationDetector->detectFunctionLikeReflection($ancestorClassReflection)) {
return true;
}
}

return false;
}

public function isReturnTypeChangeAllowed(ClassMethod $classMethod): bool
{
// make sure return type is not protected by parent contract
Expand Down
22 changes: 0 additions & 22 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ parameters:
# PHP 7.4 1_000 support
- '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\) does not accept string#'

# mixed
- '#Offset int\|string\|null does not exist on array<PhpParser\\Node\\Stmt>\|null#'

- '#Parameter \#1 \$node of method Rector\\PostRector\\Collector\\NodesToAddCollector\:\:wrapToExpression\(\) expects PhpParser\\Node\\Expr\|PhpParser\\Node\\Stmt, PhpParser\\Node given#'

- '#Cognitive complexity for "Rector\\Php80\\NodeResolver\\SwitchExprsResolver\:\:resolve\(\)" is (.*?), keep it under 9#'
Expand All @@ -99,8 +96,6 @@ parameters:
- '#Cognitive complexity for "Rector\\DeadCode\\NodeManipulator\\LivingCodeManipulator\:\:keepLivingCodeFromExpr\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\Transform\\Rector\\Class_\\AddInterfaceByParentRector\:\:refactor\(\)" is \d+, keep it under 9#'


- '#Parameter \#1 \$objectType of method Rector\\Naming\\Naming\\PropertyNaming\:\:fqnToVariableName\(\) expects PHPStan\\Type\\ObjectType\|string, PHPStan\\Type\\Type given#'
# known value
- '#Property PhpParser\\Node\\Stmt\\Foreach_\:\:\$valueVar \(PhpParser\\Node\\Expr\) does not accept PhpParser\\Node\\Expr\|null#'

Expand Down Expand Up @@ -203,11 +198,6 @@ parameters:

- '#PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Expr\\Variable given#'

-
message: '#Property with protected modifier is not allowed\. Use interface contract method instead#'
paths:
- rules/Defluent/ValueObject/*

- '#Method Rector\\CodeQuality\\Rector\\Foreach_\\SimplifyForeachToCoalescingRector\:\:matchReturnOrAssignNode\(\) should return PhpParser\\Node\\Expr\\Assign\|PhpParser\\Node\\Stmt\\Return_\|null but returns PhpParser\\Node\|null#'

- '#Instanceof between PhpParser\\Node\\Stmt and Rector\\Core\\PhpParser\\Node\\CustomNode\\FileWithoutNamespace will always evaluate to false#'
Expand Down Expand Up @@ -263,7 +253,6 @@ parameters:
message: '#Function "property_exists\(\)" cannot be used/left in the code#'
paths:
# on PhpParser Nodes
- src/NodeManipulator/ClassMethodAssignManipulator.php
- packages/NodeTypeResolver/NodeVisitor/FunctionMethodAndClassNodeVisitor.php
- packages/NodeTypeResolver/NodeVisitor/StatementNodeVisitor.php
- packages/NodeNameResolver/NodeNameResolver.php
Expand All @@ -276,11 +265,6 @@ parameters:
- '#Parameter \#1 \$stmts of method Rector\\EarlyReturn\\Rector\\Return_\\PreparedValueToEarlyReturnRector\:\:collectIfs\(\) expects array<PhpParser\\Node\\Stmt\\If_\>, array<PhpParser\\Node\\Stmt\> given#'
- '#Access to an undefined property PhpParser\\Node\\FunctionLike\|PhpParser\\Node\\Stmt\\If_\:\:\$stmts#'

-
message: '#Parameter \#1 \$types of method Rector\\Defluent\\NodeAnalyzer\\FluentCallStaticTypeResolver\:\:filterOutAlreadyPresentParentClasses\(\) expects array<class\-string\>, array<int, string\> given#'
paths:
- rules/Defluent/NodeAnalyzer/FluentCallStaticTypeResolver.php

- '#Cognitive complexity for "Rector\\CodeQuality\\Rector\\Isset_\\IssetOnPropertyObjectToPropertyExistsRector\:\:refactor\(\)" is \d+, keep it under 9#'

-
Expand Down Expand Up @@ -417,11 +401,6 @@ parameters:
- '#Method "decorateReturnWithSpecificType\(\)" returns bool type, so the name should start with is/has/was#'
- '#Method "resolveObjectType\(\)" returns bool type, so the name should start with is/has/was#'

-
message: '#Use dependency injection instead of dependency juggling#'
paths:
- packages/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider

# native filesystem calls, required for performance reasons
-
message: '#"@\\unlink\(\$tmpPath\)" is forbidden to use#'
Expand Down Expand Up @@ -459,7 +438,6 @@ parameters:
paths:
- packages/BetterPhpDocParser/ValueObject/PhpDoc/DoctrineAnnotation/AbstractValuesAwareNode.php
- packages/PostRector/Rector/AbstractPostRector.php
- rules/Defluent/ValueObject/AbstractRootExpr.php
- rules/PhpSpecToPHPUnit/Rector/AbstractPhpSpecToPHPUnitRector.php
- src/Rector/AbstractRector.php

Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 461cb9f

Please sign in to comment.