[CS] Fix dynamic and broken indent detection, allow to configure spacing via RectorConfig::indent() method (#2442)

This commit is contained in:
Tomas Votruba 2022-06-06 11:51:58 +02:00 committed by GitHub
parent 1c64bb1fb0
commit 38a9718b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 171 additions and 157 deletions

View File

@ -64,6 +64,7 @@ return static function (RectorConfig $rectorConfig): void {
$rectorConfig->disableImportNames();
$rectorConfig->importShortClasses();
$rectorConfig->indent(' ', 4);
$rectorConfig->fileExtensions(['php']);
$rectorConfig->nestedChainMethodCallLimit(60);

View File

@ -72,8 +72,12 @@ final class ClassAnnotationMatcher
/**
* @param Use_[]|GroupUse[] $uses
*/
private function resolveFullyQualifiedClass(array $uses, Node $node, string $tag, bool $returnNullOnUnknownClass): ?string
{
private function resolveFullyQualifiedClass(
array $uses,
Node $node,
string $tag,
bool $returnNullOnUnknownClass
): ?string {
$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($scope instanceof Scope) {
@ -88,7 +92,7 @@ final class ClassAnnotationMatcher
return $this->resolveAsAliased($uses, $tag, $returnNullOnUnknownClass);
}
if (str_starts_with($tag, '\\') && $this->reflectionProvider->hasClass($tag)) {
if ($this->isPreslashedExistingClass($tag)) {
// Global or absolute Class
return $tag;
}
@ -127,10 +131,20 @@ final class ClassAnnotationMatcher
private function resolveClass(?string $class, bool $returnNullOnUnknownClass): ?string
{
if (null === $class) {
if ($class === null) {
return null;
}
$resolvedClass = $this->reflectionProvider->hasClass($class) ? $class : null;
return $returnNullOnUnknownClass ? $resolvedClass : $class;
}
private function isPreslashedExistingClass(string $tag): bool
{
if (! str_starts_with($tag, '\\')) {
return false;
}
return $this->reflectionProvider->hasClass($tag);
}
}

View File

@ -230,4 +230,14 @@ final class RectorConfig extends ContainerConfigurator
$parameters = $this->parameters();
$parameters->set(Option::CACHE_CLASS, $cacheClass);
}
/**
* @see https://github.com/nikic/PHP-Parser/issues/723#issuecomment-712401963
*/
public function indent(string $character, int $count): void
{
$parameters = $this->parameters();
$parameters->set(Option::INDENT_CHAR, $character);
$parameters->set(Option::INDENT_SIZE, $count);
}
}

View File

@ -4,14 +4,14 @@ namespace Rector\Tests\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector\Fi
final class FixtureRemoveMethodCallOnThis
{
public function __construct()
{
$this->validateLineLengths();
}
public function __construct()
{
$this->validateLineLengths();
}
protected function validateLineLengths(): void
{
}
protected function validateLineLengths(): void
{
}
}
?>
@ -22,13 +22,13 @@ namespace Rector\Tests\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector\Fi
final class FixtureRemoveMethodCallOnThis
{
public function __construct()
{
}
public function __construct()
{
}
protected function validateLineLengths(): void
{
}
protected function validateLineLengths(): void
{
}
}
?>

View File

@ -4,17 +4,17 @@ namespace Rector\Tests\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector\Fi
abstract class Validator
{
protected function validateLineLengths(): void
{
}
protected function validateLineLengths(): void
{
}
}
final class GeneratorStubUseAbstract extends Validator
{
public function __construct()
{
$this->validateLineLengths();
}
public function __construct()
{
$this->validateLineLengths();
}
}
?>
@ -25,16 +25,16 @@ namespace Rector\Tests\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector\Fi
abstract class Validator
{
protected function validateLineLengths(): void
{
}
protected function validateLineLengths(): void
{
}
}
final class GeneratorStubUseAbstract extends Validator
{
public function __construct()
{
}
public function __construct()
{
}
}
?>

View File

@ -9,23 +9,23 @@ class Z {}
final class CopyDoc
{
public Y $y;
public Y $y;
/**
* @var Z[]
* @Assert\Valid()
* @Assert\NotBlank()
*/
public array $z = [];
/**
* @var Z[]
* @Assert\Valid()
* @Assert\NotBlank()
*/
public array $z = [];
/**
* @param Z[] $z
*/
public function __construct(Y $y, array $z = [])
{
$this->y = $y;
$this->z = $z;
}
/**
* @param Z[] $z
*/
public function __construct(Y $y, array $z = [])
{
$this->y = $y;
$this->z = $z;
}
}
?>
-----
@ -40,18 +40,18 @@ class Z {}
final class CopyDoc
{
/**
* @param Z[] $z
*/
public function __construct(
public Y $y,
/**
* @Assert\Valid()
* @Assert\NotBlank()
*/
public array $z = []
)
{
}
/**
* @param Z[] $z
*/
public function __construct(
public Y $y,
/**
* @Assert\Valid()
* @Assert\NotBlank()
*/
public array $z = []
)
{
}
}
?>

View File

@ -7,18 +7,18 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo
*/
final class Generic
{
/**
* @var T
*/
public mixed $value;
/**
* @var T
*/
public mixed $value;
/**
* @param T $value
*/
public function __construct(mixed $value)
{
$this->value = $value;
}
/**
* @param T $value
*/
public function __construct(mixed $value)
{
$this->value = $value;
}
}
?>
-----
@ -31,11 +31,11 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo
*/
final class Generic
{
/**
* @param T $value
*/
public function __construct(public mixed $value)
{
}
/**
* @param T $value
*/
public function __construct(public mixed $value)
{
}
}
?>

View File

@ -4,18 +4,18 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo
class IterableTyped
{
/**
* @var iterable<string>
*/
private iterable $property;
/**
* @var iterable<string>
*/
private iterable $property;
/**
* @param iterable<string> $property
*/
public function __construct(iterable $property)
{
$this->property = $property;
}
/**
* @param iterable<string> $property
*/
public function __construct(iterable $property)
{
$this->property = $property;
}
}
?>
-----
@ -25,11 +25,11 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo
class IterableTyped
{
/**
* @param iterable<string> $property
*/
public function __construct(private iterable $property)
{
}
/**
* @param iterable<string> $property
*/
public function __construct(private iterable $property)
{
}
}
?>

View File

@ -10,20 +10,20 @@ class Z {}
final class UnionFullyQualified
{
public Y $y;
public Y $y;
public Z $z;
public Z $z;
public function __construct(Y $y, Z $z)
{
$this->y = $y;
$this->z = $z;
}
public function __construct(Y $y, Z $z)
{
$this->y = $y;
$this->z = $z;
}
public function getX(): X
{
return $this->y;
}
public function getX(): X
{
return $this->y;
}
}
?>
-----
@ -39,13 +39,13 @@ class Z {}
final class UnionFullyQualified
{
public function __construct(public Y $y, public Z $z)
{
}
public function __construct(public Y $y, public Z $z)
{
}
public function getX(): X
{
return $this->y;
}
public function getX(): X
{
return $this->y;
}
}
?>

View File

@ -7,12 +7,12 @@ use DateTime;
final class UnionTyped
{
public stdClass|DateTime $property;
public stdClass|DateTime $property;
public function __construct(stdClass|DateTime $property)
{
$this->property = $property;
}
public function __construct(stdClass|DateTime $property)
{
$this->property = $property;
}
}
?>
@ -26,9 +26,9 @@ use DateTime;
final class UnionTyped
{
public function __construct(public stdClass|DateTime $property)
{
}
public function __construct(public stdClass|DateTime $property)
{
}
}
?>

View File

@ -222,4 +222,16 @@ final class Option
* @var string
*/
public const MEMORY_LIMIT = 'memory-limit';
/**
* @deprecated Use @see \Rector\Config\RectorConfig::indent() method
* @var string
*/
public const INDENT_CHAR = 'indent-char';
/**
* @deprecated Use @see \Rector\Config\RectorConfig::indent() method
* @var string
*/
public const INDENT_SIZE = 'indent-size';
}

View File

@ -31,4 +31,14 @@ final class RectorConfigProvider
{
return $this->parameterProvider->provideStringParameter(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER);
}
public function getIndentChar(): string
{
return $this->parameterProvider->provideStringParameter(Option::INDENT_CHAR);
}
public function getIndentSize(): int
{
return $this->parameterProvider->provideIntParameter(Option::INDENT_SIZE);
}
}

View File

@ -27,9 +27,9 @@ use PhpParser\Node\Stmt\TraitUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\PrettyPrinter\Standard;
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
use Rector\Core\Configuration\RectorConfigProvider;
use Rector\Core\Contract\PhpParser\NodePrinterInterface;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Core\PhpParser\Printer\Whitespace\IndentCharacterDetector;
use Rector\Core\Util\StringUtils;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -80,8 +80,8 @@ final class BetterStandardPrinter extends Standard implements NodePrinterInterfa
* @param mixed[] $options
*/
public function __construct(
private readonly IndentCharacterDetector $indentCharacterDetector,
private readonly DocBlockUpdater $docBlockUpdater,
private readonly RectorConfigProvider $rectorConfigProvider,
array $options = []
) {
parent::__construct($options);
@ -103,8 +103,7 @@ final class BetterStandardPrinter extends Standard implements NodePrinterInterfa
{
$newStmts = $this->resolveNewStmts($stmts);
// detect per print
$this->tabOrSpaceIndentCharacter = $this->indentCharacterDetector->detect($origTokens);
$this->tabOrSpaceIndentCharacter = $this->rectorConfigProvider->getIndentChar();
$content = parent::printFormatPreserving($newStmts, $origStmts, $origTokens);

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\PhpParser\Printer\Whitespace;
use Nette\Utils\Strings;
final class IndentCharacterDetector
{
/**
* Solves https://github.com/rectorphp/rector/issues/1964
*
* Some files have spaces, some have tabs. Keep the original indent if possible.
*
* @param mixed[] $tokens
*/
public function detect(array $tokens): string
{
foreach ($tokens as $token) {
if ($token[0] === T_WHITESPACE) {
$tokenContent = $token[1];
$tabMatches = Strings::matchAll($tokenContent, '#^\t#m');
if ($tabMatches !== []) {
return "\t";
}
}
}
// use space by default
return ' ';
}
}

View File

@ -1,12 +1,14 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Tests\Issues\DoNotReplaceUnknownClasses;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
class DoNotReplaceUnknownClassesTest extends AbstractRectorTestCase
final class DoNotReplaceUnknownClassesTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()