Cover properties promotion cases for Ely\align_multiple_parameters fixer

This commit is contained in:
ErickSkrauch 2023-03-23 17:09:01 +01:00
parent 5ae374f6cd
commit b85d0d5c01
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
2 changed files with 192 additions and 15 deletions

View File

@ -14,14 +14,35 @@ use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer; use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer; use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer; use PhpCsFixer\Tokenizer\TokensAnalyzer;
use SplFileInfo; use SplFileInfo;
final class AlignMultilineParametersFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface { final class AlignMultilineParametersFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface {
private const C_VARIABLES = 'variables'; /**
private const C_DEFAULTS = 'defaults'; * @internal
*/
public const C_VARIABLES = 'variables';
/**
* @internal
*/
public const C_DEFAULTS = 'defaults';
private array $parameterModifiers;
public function __construct() {
parent::__construct();
$this->parameterModifiers = [
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
];
if (defined('T_READONLY')) {
$this->parameterModifiers[] = T_READONLY;
}
}
public function getDefinition(): FixerDefinitionInterface { public function getDefinition(): FixerDefinitionInterface {
return new FixerDefinition( return new FixerDefinition(
@ -105,7 +126,7 @@ function test(
$typeAnalysis = $argument->getTypeAnalysis(); $typeAnalysis = $argument->getTypeAnalysis();
if ($typeAnalysis) { if ($typeAnalysis) {
$hasAtLeastOneTypedArgument = true; $hasAtLeastOneTypedArgument = true;
$typeLength = strlen($typeAnalysis->getName()); $typeLength = $this->getFullTypeLength($tokens, $typeAnalysis->getStartIndex());
if ($typeLength > $longestType) { if ($typeLength > $longestType) {
$longestType = $typeLength; $longestType = $typeLength;
} }
@ -124,24 +145,24 @@ function test(
if ($this->configuration[self::C_VARIABLES] === true) { if ($this->configuration[self::C_VARIABLES] === true) {
$typeLen = 0; $typeLen = 0;
if ($argument->getTypeAnalysis() !== null) { if ($argument->getTypeAnalysis() !== null) {
$typeLen = strlen($argument->getTypeAnalysis()->getName()); $typeLen = $this->getFullTypeLength($tokens, $argument->getTypeAnalysis()->getStartIndex());
} }
$appendix = str_repeat(' ', $longestType - $typeLen + (int)$hasAtLeastOneTypedArgument); $appendix = str_repeat(' ', $longestType - $typeLen + (int)$hasAtLeastOneTypedArgument);
if ($argument->hasTypeAnalysis()) { if ($argument->hasTypeAnalysis()) {
$whitespace = $appendix; $whitespaceToken = $appendix;
} else { } else {
$whitespace = $this->whitespacesConfig->getLineEnding() . $argsIndent . $appendix; $whitespaceToken = $this->whitespacesConfig->getLineEnding() . $argsIndent . $appendix;
} }
} else { } else {
if ($argument->hasTypeAnalysis()) { if ($argument->hasTypeAnalysis()) {
$whitespace = ' '; $whitespaceToken = ' ';
} else { } else {
$whitespace = $this->whitespacesConfig->getLineEnding() . $argsIndent; $whitespaceToken = $this->whitespacesConfig->getLineEnding() . $argsIndent;
} }
} }
$tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, $whitespace); $tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, $whitespaceToken);
} }
if ($this->configuration[self::C_DEFAULTS] !== null) { if ($this->configuration[self::C_DEFAULTS] !== null) {
@ -162,4 +183,28 @@ function test(
} }
} }
private function getFullTypeLength(Tokens $tokens, int $typeIndex): int {
/** @var \PhpCsFixer\Tokenizer\Token $typeToken */
$typeToken = $tokens[$typeIndex];
$typeLength = strlen($typeToken->getContent());
/** @var \PhpCsFixer\Tokenizer\Token $possiblyReadonlyToken */
$possiblyReadonlyToken = $tokens[$typeIndex - 2];
if ($possiblyReadonlyToken->isGivenKind($this->parameterModifiers)) {
/** @var \PhpCsFixer\Tokenizer\Token $whitespaceToken */
$whitespaceToken = $tokens[$typeIndex - 1];
$typeLength += strlen($possiblyReadonlyToken->getContent() . $whitespaceToken->getContent());
}
/** @var \PhpCsFixer\Tokenizer\Token $possiblyPromotionToken */
$possiblyPromotionToken = $tokens[$typeIndex - 4];
if ($possiblyPromotionToken->isGivenKind($this->parameterModifiers)) {
/** @var \PhpCsFixer\Tokenizer\Token $whitespaceToken */
$whitespaceToken = $tokens[$typeIndex - 3];
$typeLength += strlen($possiblyPromotionToken->getContent() . $whitespaceToken->getContent());
}
return $typeLength;
}
} }

View File

@ -17,8 +17,8 @@ final class AlignMultilineParametersFixerTest extends AbstractFixerTestCase {
*/ */
public function testBothTrue(string $expected, ?string $input = null): void { public function testBothTrue(string $expected, ?string $input = null): void {
$this->fixer->configure([ $this->fixer->configure([
'variables' => true, AlignMultilineParametersFixer::C_VARIABLES => true,
'defaults' => true, AlignMultilineParametersFixer::C_DEFAULTS => true,
]); ]);
$this->doTest($expected, $input); $this->doTest($expected, $input);
} }
@ -179,8 +179,8 @@ final class AlignMultilineParametersFixerTest extends AbstractFixerTestCase {
*/ */
public function testBothFalse(string $expected, ?string $input = null): void { public function testBothFalse(string $expected, ?string $input = null): void {
$this->fixer->configure([ $this->fixer->configure([
'variables' => false, AlignMultilineParametersFixer::C_VARIABLES => false,
'defaults' => false, AlignMultilineParametersFixer::C_DEFAULTS => false,
]); ]);
$this->doTest($expected, $input); $this->doTest($expected, $input);
} }
@ -200,8 +200,8 @@ final class AlignMultilineParametersFixerTest extends AbstractFixerTestCase {
*/ */
public function testBothNull(string $expected, ?string $input = null): void { public function testBothNull(string $expected, ?string $input = null): void {
$this->fixer->configure([ $this->fixer->configure([
'variables' => null, AlignMultilineParametersFixer::C_VARIABLES => null,
'defaults' => null, AlignMultilineParametersFixer::C_DEFAULTS => null,
]); ]);
$this->doTest($expected, $input); $this->doTest($expected, $input);
} }
@ -212,6 +212,138 @@ final class AlignMultilineParametersFixerTest extends AbstractFixerTestCase {
} }
} }
/**
* @dataProvider provide80TrueCases
* @requires PHP 8.0
*/
public function test80BothTrue(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => true,
AlignMultilineParametersFixer::C_DEFAULTS => true,
]);
$this->doTest($expected, $input);
}
public function provide80TrueCases(): iterable {
yield 'constructor promotion, defaults' => [
'<?php
class Test {
public function __construct(
public string $string = "string",
protected bool $bool = true
) {}
}
',
'<?php
class Test {
public function __construct(
public string $string = "string",
protected bool $bool = true
) {}
}
',
];
}
/**
* @dataProvider provideFalse80Cases
* @requires PHP 8.0
*/
public function test80BothFalse(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => false,
AlignMultilineParametersFixer::C_DEFAULTS => false,
]);
$this->doTest($expected, $input);
}
public function provideFalse80Cases(): iterable {
foreach ($this->provide80TrueCases() as $key => $case) {
if (isset($case[1])) {
yield $key => [$case[1], $case[0]];
} else {
yield $key => $case;
}
}
}
/**
* @dataProvider provide81TrueCases
* @requires PHP 8.1
*/
public function test81BothTrue(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => true,
AlignMultilineParametersFixer::C_DEFAULTS => true,
]);
$this->doTest($expected, $input);
}
public function provide81TrueCases(): iterable {
yield 'constructor promotion, readonly, defaults' => [
'<?php
class Test {
public function __construct(
public readonly string $string = "string",
protected readonly bool $bool = true
) {}
}
',
'<?php
class Test {
public function __construct(
public readonly string $string = "string",
protected readonly bool $bool = true
) {}
}
',
];
yield 'partial constructor promotion, readonly, defaults' => [
'<?php
class Test {
public function __construct(
readonly string $string = "string",
int $int = 0,
protected bool $bool = true,
$float = 0.0,
) {}
}
',
'<?php
class Test {
public function __construct(
readonly string $string = "string",
int $int = 0,
protected bool $bool = true,
$float = 0.0,
) {}
}
',
];
}
/**
* @dataProvider provideFalse81Cases
* @requires PHP 8.1
*/
public function test81BothFalse(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => false,
AlignMultilineParametersFixer::C_DEFAULTS => false,
]);
$this->doTest($expected, $input);
}
public function provideFalse81Cases(): iterable {
foreach ($this->provide81TrueCases() as $key => $case) {
if (isset($case[1])) {
yield $key => [$case[1], $case[0]];
} else {
yield $key => $case;
}
}
}
protected function createFixer(): AbstractFixer { protected function createFixer(): AbstractFixer {
return new AlignMultilineParametersFixer(); return new AlignMultilineParametersFixer();
} }