Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
61fc1f9
[WIP] Add PostRectorInterface instances to list of applied rectors
samsonasik Sep 20, 2025
b204d73
make RectorWithLineChange allow PostRectorInterface class-string type
samsonasik Sep 20, 2025
cbc2f2f
Rename getRectorClasses() to getMainRectorClasses() to differentiate …
samsonasik Sep 20, 2025
3893d19
Fix phpstan
samsonasik Sep 20, 2025
f6cc117
[ci-review] Rector Rectify
actions-user Sep 20, 2025
63fa81e
grammar fix
samsonasik Sep 20, 2025
72dd1d3
grammar fix
samsonasik Sep 20, 2025
7b1d065
part 1: apply line with change on ClassRenamingPostRector
samsonasik Sep 20, 2025
4072c4b
part 2: apply line with change on DocblockNameImportingPostRector
samsonasik Sep 20, 2025
5733137
[ci-review] Rector Rectify
actions-user Sep 20, 2025
a3e2248
Fix phpstan
samsonasik Sep 20, 2025
274f01e
part 3: apply line with change on UnusedImportRemovingPostRector
samsonasik Sep 20, 2025
b2f5e3b
part 4: ensure partial remove use detected
samsonasik Sep 20, 2025
2c4f72f
part 4: ensure partial remove use detected
samsonasik Sep 20, 2025
2f87746
part 5: add rector clas with line on NameImportingPostRector
samsonasik Sep 20, 2025
e819d8a
Add OriginalNameImportSkipVoter for auto import
samsonasik Sep 20, 2025
93dfade
Merge branch 'main' into apply-list-post-rector
samsonasik Sep 20, 2025
1dbced4
Fix no change handling
samsonasik Sep 20, 2025
0e842d8
Fix cs
samsonasik Sep 20, 2025
a1e91ba
Merge branch 'main' into apply-list-post-rector
samsonasik Sep 20, 2025
946764c
Add e2e test
samsonasik Sep 20, 2025
6d32f25
Final touch: add UseAddingPostRector that NameImportingPostRector is …
samsonasik Sep 20, 2025
62aad25
Final touch: add UseAddingPostRector that NameImportingPostRector is …
samsonasik Sep 20, 2025
56cb8e3
final touch: fix grammar
samsonasik Sep 20, 2025
d3979d0
Really final touch: ensure UseAddingPostRector correctly added when e…
samsonasik Sep 20, 2025
b85dd22
Really final touch: clean up
samsonasik Sep 20, 2025
1206bbc
Really Really final touch: use call addRectorClassWithLine() itself o…
samsonasik Sep 20, 2025
c245f80
Really final touch: clean up logic
samsonasik Sep 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
matrix:
php_version: ['8.2']
directory:
- 'e2e/applied-auto-import'
- 'e2e/applied-polyfill-php80'
- 'e2e/applied-rule-change-docblock'
- 'e2e/applied-rule-removed-node'
Expand Down
1 change: 1 addition & 0 deletions e2e/applied-auto-import/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/vendor
7 changes: 7 additions & 0 deletions e2e/applied-auto-import/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"require": {
"php": "^8.1"
},
"minimum-stability": "dev",
"prefer-stable": true
}
32 changes: 32 additions & 0 deletions e2e/applied-auto-import/expected-output.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
1 file with changes
===================

1) src/RenameDocblock.php:2

---------- begin diff ----------
@@ @@

namespace App;

-use SomeUnusedClass;
+use DateTimeInterface;

/**
- * @param \DateTime $someOldClass
+ * @param DateTimeInterface $someOldClass
*/
-function someFunction(\DateTime $someOldClass)
+function someFunction(DateTimeInterface $someOldClass)
{
}
----------- end diff -----------

Applied rules:
* RenameClassRector
* DocblockNameImportingPostRector
* NameImportingPostRector
* UnusedImportRemovingPostRector
* UseAddingPostRector


[OK] 1 file would have been changed (dry-run) by Rector
19 changes: 19 additions & 0 deletions e2e/applied-auto-import/rector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\Name\RenameClassRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
]);

$rectorConfig->ruleWithConfiguration(RenameClassRector::class, [
'DateTime' => 'DateTimeInterface'
]);

$rectorConfig->importNames();
$rectorConfig->removeUnusedImports();
};
12 changes: 12 additions & 0 deletions e2e/applied-auto-import/src/RenameDocblock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace App;

use SomeUnusedClass;

/**
* @param \DateTime $someOldClass
*/
function someFunction(\DateTime $someOldClass)
{
}
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ parameters:
paths:
# caching and message of specific child Rector rules
- src/Rector/AbstractRector.php
- src/PostRector/Rector/AbstractPostRector.php
# for cache
- src/Testing/PHPUnit/AbstractRectorTestCase.php

Expand Down
15 changes: 8 additions & 7 deletions rules/CodingStyle/Application/UseImportsAdder.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ public function __construct(
* @param array<FullyQualifiedObjectType|AliasedObjectType> $useImportTypes
* @param array<FullyQualifiedObjectType|AliasedObjectType> $constantUseImportTypes
* @param array<FullyQualifiedObjectType|AliasedObjectType> $functionUseImportTypes
* @return Stmt[]
*/
public function addImportsToStmts(
FileWithoutNamespace $fileWithoutNamespace,
array $stmts,
array $useImportTypes,
array $constantUseImportTypes,
array $functionUseImportTypes
): array {
): bool {
$usedImports = $this->usedImportsResolver->resolveForStmts($stmts);
$existingUseImportTypes = $usedImports->getUseImports();
$existingConstantUseImports = $usedImports->getConstantImports();
Expand All @@ -57,7 +56,7 @@ public function addImportsToStmts(

$newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, null);
if ($newUses === []) {
return [$fileWithoutNamespace];
return false;
}

$stmts = array_values(array_filter($stmts, static function (Stmt $stmt): bool {
Expand Down Expand Up @@ -94,7 +93,7 @@ public function addImportsToStmts(
$fileWithoutNamespace->stmts = $stmts;
$fileWithoutNamespace->stmts = array_values($fileWithoutNamespace->stmts);

return [$fileWithoutNamespace];
return true;
}

$this->mirrorUseComments($stmts, $newUses);
Expand All @@ -103,7 +102,7 @@ public function addImportsToStmts(
$fileWithoutNamespace->stmts = array_merge($newUses, $this->resolveInsertNop($fileWithoutNamespace), $stmts);
$fileWithoutNamespace->stmts = array_values($fileWithoutNamespace->stmts);

return [$fileWithoutNamespace];
return true;
}

/**
Expand All @@ -116,7 +115,7 @@ public function addImportsToNamespace(
array $useImportTypes,
array $constantUseImportTypes,
array $functionUseImportTypes
): void {
): bool {
$namespaceName = $this->getNamespaceName($namespace);

$existingUsedImports = $this->usedImportsResolver->resolveForStmts($namespace->stmts);
Expand All @@ -141,13 +140,15 @@ public function addImportsToNamespace(
$newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, $namespaceName);

if ($newUses === []) {
return;
return false;
}

$this->mirrorUseComments($namespace->stmts, $newUses);

$namespace->stmts = array_merge($newUses, $this->resolveInsertNop($namespace), $namespace->stmts);
$namespace->stmts = array_values($namespace->stmts);

return true;
}

/**
Expand Down
33 changes: 22 additions & 11 deletions rules/CodingStyle/Application/UseImportsRemover.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace Rector\CodingStyle\Application;

use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Renaming\Collector\RenamedNameCollector;

final readonly class UseImportsRemover
Expand All @@ -16,35 +17,40 @@ public function __construct(
}

/**
* @param Stmt[] $stmts
* @param string[] $removedUses
* @return Stmt[]
*/
public function removeImportsFromStmts(array $stmts, array $removedUses): array
public function removeImportsFromStmts(FileWithoutNamespace|Namespace_ $node, array $removedUses): bool
{
$hasRemoved = false;
foreach ($stmts as $key => $stmt) {
foreach ($node->stmts as $key => $stmt) {
if (! $stmt instanceof Use_) {
continue;
}

$stmt = $this->removeUseFromUse($removedUses, $stmt);
if ($this->removeUseFromUse($removedUses, $stmt)) {
$node->stmts[$key] = $stmt;
$hasRemoved = true;
}

// remove empty uses
if ($stmt->uses === []) {
unset($stmts[$key]);
$hasRemoved = true;
unset($node->stmts[$key]);
}
}

return $hasRemoved ? array_values($stmts) : $stmts;
if ($hasRemoved) {
$node->stmts = array_values($node->stmts);
}

return $hasRemoved;
}

/**
* @param string[] $removedUses
*/
private function removeUseFromUse(array $removedUses, Use_ $use): Use_
private function removeUseFromUse(array $removedUses, Use_ $use): bool
{
$hasChanged = false;
foreach ($use->uses as $usesKey => $useUse) {
$useName = $useUse->name->toString();
if (! in_array($useName, $removedUses, true)) {
Expand All @@ -56,8 +62,13 @@ private function removeUseFromUse(array $removedUses, Use_ $use): Use_
}

unset($use->uses[$usesKey]);
$hasChanged = true;
}

if ($hasChanged) {
$use->uses = array_values($use->uses);
}

return $use;
return $hasChanged;
}
}
2 changes: 1 addition & 1 deletion src/Bridge/SetRectorsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function resolveFromFilePathIncludingConfiguration(string $configFilePath
{
$rectorConfig = $this->loadRectorConfigFromFilePath($configFilePath);

$rectorClassesWithOptionalConfiguration = $rectorConfig->getRectorClasses();
$rectorClassesWithOptionalConfiguration = $rectorConfig->getMainRectorClasses();

foreach ($rectorConfig->getRuleConfigurations() as $rectorClass => $configuration) {
// remove from non-configurable, if added again with better config
Expand Down
19 changes: 5 additions & 14 deletions src/ChangesReporting/ValueObject/RectorWithLineChange.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Rector\ChangesReporting\ValueObject;

use Rector\Contract\Rector\RectorInterface;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Symplify\EasyParallel\Contract\SerializableInterface;
use Webmozart\Assert\Assert;

Expand All @@ -21,26 +22,16 @@
private const KEY_LINE = 'line';

/**
* @var class-string<RectorInterface>
*/
private string $rectorClass;

/**
* @param class-string<RectorInterface>|RectorInterface $rectorClass
* @param class-string<RectorInterface|PostRectorInterface> $rectorClass
*/
public function __construct(
string|RectorInterface $rectorClass,
private string $rectorClass,
private int $line
) {
if ($rectorClass instanceof RectorInterface) {
$rectorClass = $rectorClass::class;
}

$this->rectorClass = $rectorClass;
}

/**
* @return class-string<RectorInterface>
* @return class-string<RectorInterface|PostRectorInterface>
*/
public function getRectorClass(): string
{
Expand All @@ -63,7 +54,7 @@ public static function decode(array $json): self
}

/**
* @return array{rector_class: class-string<RectorInterface>, line: int}
* @return array{rector_class: class-string<RectorInterface|PostRectorInterface>, line: int}
*/
public function jsonSerialize(): array
{
Expand Down
2 changes: 1 addition & 1 deletion src/Config/RectorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ public function getRuleConfigurations(): array
* @internal Used only for bridge
* @return array<class-string<RectorInterface>>
*/
public function getRectorClasses(): array
public function getMainRectorClasses(): array
{
return $this->tags[RectorInterface::class] ?? [];
}
Expand Down
2 changes: 1 addition & 1 deletion src/Configuration/RectorConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public function __invoke(RectorConfig $rectorConfig): void
}

// log rules from sets and compare them with explicit rules
$setRegisteredRectorClasses = $rectorConfig->getRectorClasses();
$setRegisteredRectorClasses = $rectorConfig->getMainRectorClasses();
SimpleParameterProvider::addParameter(Option::SET_REGISTERED_RULES, $setRegisteredRectorClasses);

if ($this->paths !== []) {
Expand Down
10 changes: 10 additions & 0 deletions src/PostRector/Rector/AbstractPostRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

namespace Rector\PostRector\Rector;

use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitorAbstract;
use Rector\ChangesReporting\ValueObject\RectorWithLineChange;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Rector\ValueObject\Application\File;
use Webmozart\Assert\Assert;
Expand Down Expand Up @@ -33,4 +35,12 @@ public function getFile(): File

return $this->file;
}

protected function addRectorClassWithLine(Node $node): void
{
Assert::isInstanceOf($this->file, File::class);

$rectorWithLineChange = new RectorWithLineChange(static::class, $node->getStartLine());
$this->file->addRectorClassWithLine($rectorWithLineChange);
}
}
4 changes: 3 additions & 1 deletion src/PostRector/Rector/ClassRenamingPostRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ public function beforeTraverse(array $nodes): array
foreach ($nodes as $node) {
if ($node instanceof FileWithoutNamespace || $node instanceof Namespace_) {
$removedUses = $this->renamedClassesDataCollector->getOldClasses();
$node->stmts = $this->useImportsRemover->removeImportsFromStmts($node->stmts, $removedUses);
if ($this->useImportsRemover->removeImportsFromStmts($node, $removedUses)) {
$this->addRectorClassWithLine($node);
}

break;
}
Expand Down
1 change: 1 addition & 0 deletions src/PostRector/Rector/DocblockNameImportingPostRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function enterNode(Node $node): Node|null
return null;
}

$this->addRectorClassWithLine($node);
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
return $node;
}
Expand Down
11 changes: 9 additions & 2 deletions src/PostRector/Rector/NameImportingPostRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Rector\PostRector\Rector;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\GroupUse;
Expand Down Expand Up @@ -36,13 +37,19 @@ public function beforeTraverse(array $nodes): array
return $nodes;
}

public function enterNode(Node $node): Node|null
public function enterNode(Node $node): Name|null
{
if (! $node instanceof FullyQualified) {
return null;
}

return $this->nameImporter->importName($node, $this->getFile(), $this->currentUses);
$name = $this->nameImporter->importName($node, $this->getFile(), $this->currentUses);
if (! $name instanceof Name) {
return null;
}

$this->addRectorClassWithLine($node);
return $name;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/PostRector/Rector/UnusedImportRemovingPostRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public function enterNode(Node $node): ?Node
return null;
}

$this->addRectorClassWithLine($node);
$node->stmts = array_values($node->stmts);
return $node;
}
Expand Down
Loading
Loading