Skip to content

Commit 90338e0

Browse files
authored
PromotedConstructorPropertyFixer - do not remove PHPDocs and attributes (#732)
1 parent aa1bbd9 commit 90338e0

File tree

4 files changed

+135
-74
lines changed

4 files changed

+135
-74
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Latest stable version](https://img.shields.io/packagist/v/kubawerlos/php-cs-fixer-custom-fixers.svg?label=current%20version)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
44
[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net)
55
[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE)
6-
![Tests](https://img.shields.io/badge/tests-3365-brightgreen.svg)
6+
![Tests](https://img.shields.io/badge/tests-3364-brightgreen.svg)
77
[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
88

99
[![CI Status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/workflows/CI/badge.svg?branch=main)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions)

src/Fixer/PromotedConstructorPropertyFixer.php

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function configure(array $configuration): void
7878
}
7979

8080
/**
81-
* Must run before BracesFixer, ClassAttributesSeparationFixer, ConstructorEmptyBracesFixer, MultilinePromotedPropertiesFixer, NoExtraBlankLinesFixer, NoUnusedImportsFixer.
81+
* Must run before BracesFixer, ClassAttributesSeparationFixer, ConstructorEmptyBracesFixer, MultilinePromotedPropertiesFixer, NoExtraBlankLinesFixer.
8282
*/
8383
public function getPriority(): int
8484
{
@@ -154,16 +154,16 @@ private function promoteProperties(Tokens $tokens, int $classIndex, ConstructorA
154154
continue;
155155
}
156156

157-
$propertyVisibility = null;
157+
$tokensToInsert = [new Token([\T_PUBLIC, 'public'])];
158158
if ($propertyIndex !== null) {
159-
$propertyVisibility = $this->removePropertyAndReturnVisibility($tokens, $propertyIndex, $constructorParameterIndex);
159+
$tokensToInsert = $this->removePropertyAndReturnTokensToInsert($tokens, $propertyIndex, $constructorParameterIndex);
160160
}
161161

162162
$this->removeAssignment($tokens, $constructorPromotableAssignments[$constructorParameterName]);
163163
$this->updateParameterSignature(
164164
$tokens,
165165
$constructorParameterIndex,
166-
$propertyVisibility ?? new Token([\T_PUBLIC, 'public']),
166+
$tokensToInsert,
167167
\substr($propertyType, 0, 1) === '?'
168168
);
169169
}
@@ -293,49 +293,46 @@ private function getClassProperties(Tokens $tokens, int $classIndex): array
293293
return $properties;
294294
}
295295

296-
private function removePropertyAndReturnVisibility(Tokens $tokens, int $propertyIndex, int $parameterIndex): ?Token
296+
/**
297+
* @return array<Token>
298+
*/
299+
private function removePropertyAndReturnTokensToInsert(Tokens $tokens, int $propertyIndex, int $parameterIndex): array
297300
{
298301
$tokens[$parameterIndex] = $tokens[$propertyIndex];
299302

300-
$prevPropertyIndex = $this->getTokenOfKindSibling($tokens, -1, $propertyIndex, ['{', '}', ';', ',']);
301-
302-
$propertyStartIndex = $tokens->getNextMeaningfulToken($prevPropertyIndex);
303-
\assert(\is_int($propertyStartIndex));
303+
$visibilityIndex = $tokens->getPrevTokenOfKind($propertyIndex, [[\T_PRIVATE], [\T_PROTECTED], [\T_PUBLIC], [\T_VAR]]);
304+
\assert(\is_int($visibilityIndex));
304305

305-
$propertyEndIndex = $this->getTokenOfKindSibling($tokens, 1, $propertyIndex, [';', ',']);
306-
307-
$prevVisibilityIndex = $this->getTokenOfKindSibling($tokens, -1, $propertyIndex, ['}', ';']);
308-
309-
$visibilityIndex = $this->getTokenOfKindSibling($tokens, 1, $prevVisibilityIndex, [[\T_PRIVATE], [\T_PROTECTED], [\T_VARIABLE]]);
306+
$prevPropertyIndex = $this->getTokenOfKindSibling($tokens, -1, $propertyIndex, ['{', '}', ';', ',']);
307+
$nextPropertyIndex = $this->getTokenOfKindSibling($tokens, 1, $propertyIndex, [';', ',']);
310308

311-
$visibilityToken = null;
312-
if (!$tokens[$visibilityIndex]->isGivenKind(\T_VARIABLE)) {
313-
$visibilityToken = $tokens[$visibilityIndex];
309+
$removeFrom = $tokens->getTokenNotOfKindSibling($prevPropertyIndex, 1, [[\T_WHITESPACE], [\T_COMMENT]]);
310+
\assert(\is_int($removeFrom));
311+
$removeTo = $nextPropertyIndex;
312+
if ($tokens[$prevPropertyIndex]->equals(',')) {
313+
$removeFrom = $prevPropertyIndex;
314+
$removeTo = $propertyIndex;
315+
} elseif ($tokens[$nextPropertyIndex]->equals(',')) {
316+
$removeFrom = $tokens->getPrevMeaningfulToken($propertyIndex);
317+
\assert(\is_int($removeFrom));
318+
$removeFrom++;
314319
}
315320

316-
$prevPropertyStartIndex = $tokens->getPrevNonWhitespace($propertyStartIndex);
317-
\assert(\is_int($prevPropertyStartIndex));
318-
319-
if ($tokens[$prevPropertyStartIndex]->isGivenKind(\T_DOC_COMMENT)) {
320-
$propertyStartIndex = $prevPropertyStartIndex;
321+
$tokensToInsert = [];
322+
for ($index = $removeFrom; $index <= $visibilityIndex - 1; $index++) {
323+
$tokensToInsert[] = $tokens[$index];
321324
}
322325

323-
$removeFrom = $propertyStartIndex;
324-
$removeTo = $propertyEndIndex;
325-
if ($tokens[$prevPropertyIndex]->equals(',')) {
326-
$removeFrom = $tokens->getPrevMeaningfulToken($propertyStartIndex);
327-
\assert(\is_int($removeFrom));
328-
$removeTo = $propertyEndIndex - 1;
329-
} elseif ($tokens[$propertyEndIndex]->equals(',')) {
330-
$removeFrom = $tokens->getNextMeaningfulToken($visibilityIndex);
331-
\assert(\is_int($removeFrom));
332-
$removeTo = $propertyEndIndex + 1;
326+
$visibilityToken = $tokens[$visibilityIndex];
327+
if ($tokens[$visibilityIndex]->isGivenKind(\T_VAR)) {
328+
$visibilityToken = new Token([\T_PUBLIC, 'public']);
333329
}
330+
$tokensToInsert[] = $visibilityToken;
334331

335332
$tokens->clearRange($removeFrom + 1, $removeTo);
336333
TokenRemover::removeWithLinesIfPossible($tokens, $removeFrom);
337334

338-
return $visibilityToken;
335+
return $tokensToInsert;
339336
}
340337

341338
/**
@@ -375,29 +372,32 @@ private function removeAssignment(Tokens $tokens, int $variableAssignmentIndex):
375372
TokenRemover::removeWithLinesIfPossible($tokens, $thisIndex);
376373
}
377374

378-
private function updateParameterSignature(Tokens $tokens, int $index, Token $visibilityToken, bool $makeTypeNullable): void
375+
/**
376+
* @param array<Token> $tokensToInsert
377+
*/
378+
private function updateParameterSignature(Tokens $tokens, int $constructorParameterIndex, array $tokensToInsert, bool $makeTypeNullable): void
379379
{
380-
$prevElementIndex = $tokens->getPrevTokenOfKind($index, ['(', ',', [CT::T_ATTRIBUTE_CLOSE]]);
380+
$prevElementIndex = $tokens->getPrevTokenOfKind($constructorParameterIndex, ['(', ',', [CT::T_ATTRIBUTE_CLOSE]]);
381381
\assert(\is_int($prevElementIndex));
382382

383383
$propertyStartIndex = $tokens->getNextMeaningfulToken($prevElementIndex);
384384
\assert(\is_int($propertyStartIndex));
385385

386-
$insertTokens = [];
387-
388-
if ($visibilityToken->isGivenKind(\T_PRIVATE)) {
389-
$insertTokens[] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $visibilityToken->getContent()]);
390-
} elseif ($visibilityToken->isGivenKind(\T_PROTECTED)) {
391-
$insertTokens[] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $visibilityToken->getContent()]);
392-
} elseif ($visibilityToken->isGivenKind(\T_PUBLIC)) {
393-
$insertTokens[] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $visibilityToken->getContent()]);
386+
foreach ($tokensToInsert as $index => $token) {
387+
if ($token->isGivenKind(\T_PUBLIC)) {
388+
$tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $token->getContent()]);
389+
} elseif ($token->isGivenKind(\T_PROTECTED)) {
390+
$tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $token->getContent()]);
391+
} elseif ($token->isGivenKind(\T_PRIVATE)) {
392+
$tokensToInsert[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $token->getContent()]);
393+
}
394394
}
395-
$insertTokens[] = new Token([\T_WHITESPACE, ' ']);
395+
$tokensToInsert[] = new Token([\T_WHITESPACE, ' ']);
396396

397397
if ($makeTypeNullable && !$tokens[$propertyStartIndex]->isGivenKind(CT::T_NULLABLE_TYPE)) {
398-
$insertTokens[] = new Token([CT::T_NULLABLE_TYPE, '?']);
398+
$tokensToInsert[] = new Token([CT::T_NULLABLE_TYPE, '?']);
399399
}
400400

401-
$this->tokensToInsert[$propertyStartIndex] = $insertTokens;
401+
$this->tokensToInsert[$propertyStartIndex] = $tokensToInsert;
402402
}
403403
}

tests/Fixer/PromotedConstructorPropertyFixerTest.php

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,13 @@ public function __construct(
464464
];
465465
}
466466

467-
yield 'remove property PHPDoc when promoting' => [
467+
yield 'carry over property PHPDoc when promoting' => [
468468
'<?php class Point {
469469
public function __construct(
470-
public float $x = 0.0,
471-
public float $y = 0.0,
470+
/** @var float */
471+
public float $x = 0.0,
472+
/** @var float */
473+
public float $y = 0.0,
472474
) {
473475
}
474476
}',
@@ -537,8 +539,10 @@ public function __construct(
537539
'<?php
538540
class Foo
539541
{
540-
public function __construct(private string $x)
541-
{
542+
public function __construct(
543+
#[Attribute]
544+
private string $x
545+
) {
542546
}
543547
}
544548
',
@@ -547,9 +551,82 @@ class Foo
547551
{
548552
#[Attribute]
549553
private $x;
550-
public function __construct(string $x)
551-
{
554+
public function __construct(
555+
string $x
556+
) {
557+
$this->x = $x;
558+
}
559+
}
560+
',
561+
];
562+
563+
yield 'promote with multiple attributes on property' => [
564+
'<?php
565+
class Foo
566+
{
567+
public function __construct(
568+
#[Attribute1]
569+
#[Attribute2]
570+
#[Attribute3]
571+
private string $x
572+
) {
573+
}
574+
}
575+
',
576+
'<?php
577+
class Foo
578+
{
579+
#[Attribute1]
580+
#[Attribute2]
581+
#[Attribute3]
582+
private $x;
583+
public function __construct(
584+
string $x
585+
) {
586+
$this->x = $x;
587+
}
588+
}
589+
',
590+
];
591+
592+
yield 'promote with multiple attributes on multiple properties' => [
593+
'<?php
594+
class Foo
595+
{
596+
public function __construct(
597+
#[Attribute1]
598+
public string $x,
599+
#[Attribute2]
600+
#[Attribute3]
601+
#[Attribute4]
602+
protected string $y,
603+
#[Attribute5]
604+
#[Attribute6]
605+
private string $z,
606+
) {
607+
}
608+
}
609+
',
610+
'<?php
611+
class Foo
612+
{
613+
#[Attribute1]
614+
public $x;
615+
#[Attribute2]
616+
#[Attribute3]
617+
#[Attribute4]
618+
protected string $y;
619+
#[Attribute5]
620+
#[Attribute6]
621+
private string $z;
622+
public function __construct(
623+
string $x,
624+
string $y,
625+
string $z,
626+
) {
552627
$this->x = $x;
628+
$this->y = $y;
629+
$this->z = $z;
553630
}
554631
}
555632
',
@@ -715,7 +792,10 @@ public function __construct(
715792
* @internal
716793
*/
717794
class Foo {
718-
public function __construct(private int $x) {
795+
public function __construct(/**
796+
* @var int
797+
*/
798+
private int $x) {
719799
}
720800
}
721801
',

tests/priority_fixtures/Custom_promoted_constructor_property,no_unused_imports.test

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)