Skip to content

Commit

Permalink
[Renaming] New Rule: RenameFunctionLikeParamWithinCallLikeArgRector (#…
Browse files Browse the repository at this point in the history
…5554)

* working

* Code quality refactor

* Docs

* Fixes PHPDoc @see tag

* Rename of Rule

* Reset hasChanged property

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

* Handle first class callable

* stop transversal upon finding a use

* Update rule description

* Refactored to use built in services

* Fixes

* fixes

* replace isset with null coalescing

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

---------

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
  • Loading branch information
peterfox and samsonasik committed Feb 22, 2024
1 parent f9b3312 commit 4951acc
Show file tree
Hide file tree
Showing 12 changed files with 530 additions and 8 deletions.
19 changes: 17 additions & 2 deletions build/target-repository/docs/rector_rules_overview.md
@@ -1,4 +1,4 @@
# 360 Rules Overview
# 361 Rules Overview

<br>

Expand Down Expand Up @@ -50,7 +50,7 @@

- [Removing](#removing) (5)

- [Renaming](#renaming) (9)
- [Renaming](#renaming) (10)

- [Strict](#strict) (5)

Expand Down Expand Up @@ -5601,6 +5601,21 @@ Replace constant by new ones

<br>

### RenameFunctionLikeParamWithinCallLikeArgRector

Rename param within closures and arrow functions based on use with specified method calls

:wrench: **configure it!**

- class: [`Rector\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector`](../rules/Renaming/Rector/FunctionLike/RenameFunctionLikeParamWithinCallLikeArgRector.php)

```diff
-(new SomeClass)->process(function ($param) {});
+(new SomeClass)->process(function ($parameter) {});
```

<br>

### RenameFunctionRector

Turns defined function call new one.
Expand Down
@@ -0,0 +1,57 @@
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

use SomeNamespace\SomeClass;
use SomeNamespace\SomeClassForNamed;

function fixture()
{
SomeClass::someCall(function ($qryOne) {
return $qryOne;
});

$varOne = new SomeClass();
$varOne->someCall(function ($qryTwo) {
return $qryTwo;
});

(new SomeClass())->someCall(function ($qryThree) {
return $qryThree;
});

$varOne->someCall(fn($qryFour) => $qryFour);

SomeClassForNamed::someCall('a', 'b', callback: fn($varTwo) => $varTwo);
}

?>
-----
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

use SomeNamespace\SomeClass;
use SomeNamespace\SomeClassForNamed;

function fixture()
{
SomeClass::someCall(function ($query) {
return $query;
});

$varOne = new SomeClass();
$varOne->someCall(function ($query) {
return $query;
});

(new SomeClass())->someCall(function ($query) {
return $query;
});

$varOne->someCall(fn($query) => $query);

SomeClassForNamed::someCall('a', 'b', callback: fn($query) => $query);
}

?>
@@ -0,0 +1,39 @@
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

use SomeNamespace\SomeClass;
use SomeNamespace\SomeClassForNamed;

function skip_conflicting_parameter_names()
{
SomeClass::someCall(function ($qry, $query) {
return $qry;
});

SomeClass::someCall(fn ($qry, $query) => $qry);

SomeClass::someCall(function ($qry, $query) {
echo $query;
return $qry;
});

$query = 'a';

SomeClass::someCall(function ($qry) use ($query) {
return $query;
});

SomeClass::someCall(function ($qry) use ($query) {
return $qry;
});

SomeClass::someCall(function ($qryFive) {
echo $qryFive;
return function ($query) {
return $query;
};
});
}

?>
@@ -0,0 +1,10 @@
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

use SomeNamespace\SomeClass;

SomeClass::someOtherCall('a', function () {
});

?>
@@ -0,0 +1,11 @@
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

use SomeNamespace\SomeClass;

SomeClass::someOtherCall('a', function ($qry) {
return $qry;
});

?>
@@ -0,0 +1,9 @@
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

SomeOtherClass::someCall(function ($qry) {
return $qry;
});

?>
@@ -0,0 +1,11 @@
<?php

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector\Fixture;

use SomeNamespace\SomeClass;

SomeClass::someOtherCall(function ($qry) {
return $qry;
});

?>
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class RenameFunctionLikeParamWithinCallLikeArgRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector;
use Rector\Renaming\ValueObject\RenameFunctionLikeParamWithinCallLikeArg;
use Rector\ValueObject\PhpVersionFeature;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig
->ruleWithConfiguration(RenameFunctionLikeParamWithinCallLikeArgRector::class, [
new RenameFunctionLikeParamWithinCallLikeArg('SomeNamespace\SomeClass', 'someCall', 0, 0, 'query'),
new RenameFunctionLikeParamWithinCallLikeArg(
'SomeNamespace\SomeClassForNamed',
'someCall',
'callback',
0,
'query',
),
]);

$rectorConfig->phpVersion(PhpVersionFeature::MIXED_TYPE);
};
29 changes: 23 additions & 6 deletions rules/Naming/Guard/BreakingVariableRenameGuard.php
Expand Up @@ -49,7 +49,7 @@ public function __construct(
public function shouldSkipVariable(
string $currentName,
string $expectedName,
ClassMethod | Function_ | Closure $functionLike,
ClassMethod | Function_ | Closure | ArrowFunction $functionLike,
Variable $variable
): bool {
// is the suffix? → also accepted
Expand All @@ -62,7 +62,10 @@ public function shouldSkipVariable(
return true;
}

if ($this->overridenExistingNamesResolver->hasNameInClassMethodForNew($currentName, $functionLike)) {
if (! $functionLike instanceof ArrowFunction && $this->overridenExistingNamesResolver->hasNameInClassMethodForNew(
$currentName,
$functionLike
)) {
return true;
}

Expand All @@ -74,7 +77,7 @@ public function shouldSkipVariable(
return true;
}

return $this->isUsedInClosureUsesName($expectedName, $functionLike);
return $functionLike instanceof Closure && $this->isUsedInClosureUsesName($expectedName, $functionLike);
}

public function shouldSkipParam(
Expand Down Expand Up @@ -148,9 +151,23 @@ private function isVariableAlreadyDefined(Variable $variable, string $currentVar
return $trinaryLogic->maybe();
}

private function hasConflictVariable(ClassMethod | Function_ | Closure $functionLike, string $newName): bool
{
return $this->betterNodeFinder->hasInstanceOfName((array) $functionLike->stmts, Variable::class, $newName);
private function hasConflictVariable(
ClassMethod | Function_ | Closure | ArrowFunction $functionLike,
string $newName
): bool {
if ($functionLike instanceof ArrowFunction) {
return $this->betterNodeFinder->hasInstanceOfName(
[$functionLike->expr, ...$functionLike->params],
Variable::class,
$newName
);
}

return $this->betterNodeFinder->hasInstanceOfName(
[...(array) $functionLike->stmts, ...$functionLike->params],
Variable::class,
$newName
);
}

private function isUsedInClosureUsesName(
Expand Down

0 comments on commit 4951acc

Please sign in to comment.