Skip to content

Commit 7e789fa

Browse files
gnutixclaude
andauthored
StandaloneLinePromotedPropertyFixer: properly deal with attributes. (#78)
* StandaloneLinePromotedPropertyFixer: properly deal with attributes. Co-Authored-By: Claude Code 🤖 <[email protected]> * CI: fix PHPStan and Rector issues - Sort named parameters in rector.php - Update PHPStan ignored errors to match new PHP version range - Add new PHPStan ignores for offset access and array filter issues - Remove obsolete PHPStan ignored error patterns Co-Authored-By: Claude Code 🤖 <[email protected]> --------- Co-authored-by: Claude Code 🤖 <[email protected]>
1 parent cd26aac commit 7e789fa

File tree

5 files changed

+145
-9
lines changed

5 files changed

+145
-9
lines changed

phpstan.neon

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,30 @@ parameters:
1313
- '*/tests/**/Fixture/*'
1414

1515
ignoreErrors:
16-
# partial enum
17-
- '#Method Symplify\\CodingStandard\\TokenRunner\\Analyzer\\FixerAnalyzer\\BlockFinder\:\:(getBlockTypeByContent|getBlockTypeByToken)\(\) never returns \d+ so it can be removed from the return type#'
18-
1916
-
2017
path: tests/bootstrap.php
2118
message: '#Instantiated class PHP_CodeSniffer\\Util\\Tokens not found#'
2219

2320
- '#Constant T_OPEN_CURLY_BRACKET|T_START_NOWDOC not found#'
24-
- '#Method Symplify\\CodingStandard\\TokenRunner\\Traverser\\ArrayBlockInfoFinder\:\:reverseTokens\(\) should return array<PhpCsFixer\\Tokenizer\\Token> but returns array<int, PhpCsFixer\\Tokenizer\\Token\|null>#'
2521

2622
# unused generics
2723
- '#Class (.*?) implements generic interface PhpCsFixer\\Fixer\\ConfigurableFixerInterface but does not specify its types\: TFixerInputConfig, TFixerComputedConfig#'
2824

2925
# conditional check to allow various php versions
3026
-
31-
message: '#Comparison operation ">\=" between int<80200, 80499> and (.*?) is always true#'
27+
message: '#Comparison operation ">\=" between int<80200, 80599> and (.*?) is always true#'
3228
path: src/TokenAnalyzer/DocblockRelatedParamNamesResolver.php
29+
30+
# offset access issues
31+
-
32+
message: '#Offset int\|null might not exist on PhpCsFixer\\Tokenizer\\Tokens#'
33+
path: src/Fixer/Naming/ClassNameResolver.php
34+
35+
-
36+
message: '#Offset int\|null might not exist on PhpCsFixer\\Tokenizer\\Tokens#'
37+
path: src/Fixer/Naming/MethodNameResolver.php
38+
39+
# array filter issue
40+
-
41+
message: '#Parameter \#1 \$array \(array<int, PhpCsFixer\\Tokenizer\\Token>\) to function array_filter does not contain falsy values, the array will always stay the same#'
42+
path: src/TokenRunner/Traverser/TokenReverser.php

rector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
->withPaths([__DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/tests'])
99
->withRootFiles()
1010
->withPhpSets()
11-
->withPreparedSets(codeQuality: true, codingStyle: true, naming: true, earlyReturn: true, privatization: true)
11+
->withPreparedSets(codeQuality: true, codingStyle: true, privatization: true, naming: true, earlyReturn: true)
1212
->withImportNames()
1313
->withSkip([
1414
'*/Source/*',

src/TokenRunner/Analyzer/FixerAnalyzer/TokenSkipper.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,45 @@ public function skipBlocksReversed(Tokens $tokens, int $position): int
5757
{
5858
/** @var Token $token */
5959
$token = $tokens[$position];
60-
if (! $token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_CLOSE) && ! $token->equals(')')) {
60+
if (! $token->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_CLOSE) && ! $token->equals(')') && ! $token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
6161
return $position;
6262
}
6363

64+
// Check if this is an attribute closing bracket
65+
if ($token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
66+
$attributeStartPosition = $this->findAttributeStart($tokens, $position);
67+
if ($attributeStartPosition !== null) {
68+
return $attributeStartPosition;
69+
}
70+
}
71+
6472
$blockInfo = $this->blockFinder->findInTokensByEdge($tokens, $position);
6573
if (! $blockInfo instanceof BlockInfo) {
6674
throw new ShouldNotHappenException();
6775
}
6876

6977
return $blockInfo->getStart();
7078
}
79+
80+
/**
81+
* @param Tokens<Token> $tokens
82+
*/
83+
private function findAttributeStart(Tokens $tokens, int $closingBracketPosition): ?int
84+
{
85+
// Search backwards for T_ATTRIBUTE token (#[)
86+
for ($i = $closingBracketPosition - 1; $i >= 0; --$i) {
87+
$currentToken = $tokens[$i];
88+
89+
if ($currentToken->isGivenKind(T_ATTRIBUTE)) {
90+
return $i;
91+
}
92+
93+
// If we hit another ] or reach a statement boundary, stop searching
94+
if ($currentToken->equals(']') || $currentToken->equals(';') || $currentToken->equals('{') || $currentToken->equals('}')) {
95+
break;
96+
}
97+
}
98+
99+
return null;
100+
}
71101
}

src/TokenRunner/Transformer/FixerTransformer/TokensNewliner.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ public function breakItems(BlockInfo $blockInfo, Tokens $tokens, int $kind): voi
5151

5252
// again, from the bottom to the top
5353
for ($i = $blockInfo->getEnd() - 1; $i >= $blockInfo->getStart(); --$i) {
54+
$i = $this->tokenSkipper->skipBlocksReversed($tokens, $i);
55+
5456
/** @var Token $currentToken */
5557
$currentToken = $tokens[$i];
5658

57-
$i = $this->tokenSkipper->skipBlocksReversed($tokens, $i);
58-
5959
// 2. new line after each comma ",", instead of just space
6060
if ($currentToken->getContent() === ',') {
6161
if ($this->isLastItem($tokens, $i)) {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace Symplify\CodingStandard\Tests\Fixer\Spacing\StandaloneLinePromotedPropertyFixer\Fixture;
4+
5+
final class SkipAttributes
6+
{
7+
public function __construct(#[JMS\Expose, JMS\Type('integer')] private int $id, private string $name) {}
8+
}
9+
10+
final class SkipAttributes2
11+
{
12+
public function __construct(
13+
#[JMS\Expose, JMS\Type('integer')]
14+
private int $id,
15+
private string $name,
16+
) {}
17+
}
18+
19+
final class SkipAttributes3
20+
{
21+
public function __construct(
22+
#[JMS\Expose, JMS\Type('integer')] private int $id,
23+
private string $name,
24+
) {}
25+
}
26+
27+
final class SkipAttributes4
28+
{
29+
public function __construct(
30+
#[JMS\Expose]
31+
#[JMS\Type('integer')] private int $id,
32+
private string $name,
33+
) {}
34+
}
35+
36+
final class SkipAttributes5
37+
{
38+
public function __construct(
39+
#[JMS\Expose]
40+
#[JMS\Type('integer')]
41+
private int $id,
42+
private string $name,
43+
) {}
44+
}
45+
46+
?>
47+
-----
48+
<?php
49+
50+
namespace Symplify\CodingStandard\Tests\Fixer\Spacing\StandaloneLinePromotedPropertyFixer\Fixture;
51+
52+
final class SkipAttributes
53+
{
54+
public function __construct(
55+
#[JMS\Expose, JMS\Type('integer')] private int $id,
56+
private string $name
57+
) {}
58+
}
59+
60+
final class SkipAttributes2
61+
{
62+
public function __construct(
63+
#[JMS\Expose, JMS\Type('integer')]
64+
private int $id,
65+
private string $name,
66+
) {}
67+
}
68+
69+
final class SkipAttributes3
70+
{
71+
public function __construct(
72+
#[JMS\Expose, JMS\Type('integer')] private int $id,
73+
private string $name,
74+
) {}
75+
}
76+
77+
final class SkipAttributes4
78+
{
79+
public function __construct(
80+
#[JMS\Expose]
81+
#[JMS\Type('integer')] private int $id,
82+
private string $name,
83+
) {}
84+
}
85+
86+
final class SkipAttributes5
87+
{
88+
public function __construct(
89+
#[JMS\Expose]
90+
#[JMS\Type('integer')]
91+
private int $id,
92+
private string $name,
93+
) {}
94+
}
95+
96+
?>

0 commit comments

Comments
 (0)