Skip to content

Commit 3864ca6

Browse files
committed
fix(ScopeIndent): Fix test fails on attributes on constructor promotion properties
1 parent dfbd808 commit 3864ca6

File tree

2 files changed

+147
-49
lines changed

2 files changed

+147
-49
lines changed

coder_sniffer/Drupal/Sniffs/WhiteSpace/ScopeIndentSniff.php

Lines changed: 129 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class ScopeIndentSniff implements Sniff
7474
* or PHP open/close tags can escape from here and have their own
7575
* rules elsewhere.
7676
*
77-
* @var array<int, int|string>
77+
* @var int[]
7878
*/
7979
public $ignoreIndentationTokens = [];
8080

@@ -126,7 +126,7 @@ public function register()
126126
* @param int $stackPtr The position of the current token
127127
* in the stack passed in $tokens.
128128
*
129-
* @return void|int
129+
* @return int
130130
*/
131131
public function process(File $phpcsFile, $stackPtr)
132132
{
@@ -146,13 +146,13 @@ public function process(File $phpcsFile, $stackPtr)
146146
}
147147
}
148148

149-
$lastOpenTag = $stackPtr;
150-
$lastCloseTag = null;
151-
$openScopes = [];
152-
$adjustments = [];
153-
$setIndents = [];
154-
$disableExactEnd = 0;
155-
$tokenIndent = 0;
149+
$lastOpenTag = $stackPtr;
150+
$lastCloseTag = null;
151+
$openScopes = [];
152+
$adjustments = [];
153+
$setIndents = [];
154+
$disableExactStack = [];
155+
$disableExactEnd = 0;
156156

157157
$tokens = $phpcsFile->getTokens();
158158
$first = $phpcsFile->findFirstOnLine(T_INLINE_HTML, $stackPtr);
@@ -189,6 +189,11 @@ public function process(File $phpcsFile, $stackPtr)
189189
$checkAnnotations = $phpcsFile->config->annotations;
190190

191191
for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
192+
if ($i === false) {
193+
// Something has gone very wrong; maybe a parse error.
194+
break;
195+
}
196+
192197
if ($checkAnnotations === true
193198
&& $tokens[$i]['code'] === T_PHPCS_SET
194199
&& isset($tokens[$i]['sniffCode']) === true
@@ -232,6 +237,7 @@ public function process(File $phpcsFile, $stackPtr)
232237
if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS
233238
&& isset($tokens[$i]['parenthesis_closer']) === true
234239
) {
240+
$disableExactStack[$tokens[$i]['parenthesis_closer']] = $tokens[$i]['parenthesis_closer'];
235241
$disableExactEnd = max($disableExactEnd, $tokens[$i]['parenthesis_closer']);
236242
if ($this->debug === true) {
237243
$line = $tokens[$i]['line'];
@@ -337,7 +343,14 @@ public function process(File $phpcsFile, $stackPtr)
337343
echo "\t* open tag is inside condition; using open tag *".PHP_EOL;
338344
}
339345

340-
$checkIndent = ($tokens[$lastOpenTag]['column'] - 1);
346+
$first = $phpcsFile->findFirstOnLine([T_WHITESPACE, T_INLINE_HTML], $lastOpenTag, true);
347+
if ($this->debug === true) {
348+
$line = $tokens[$first]['line'];
349+
$type = $tokens[$first]['type'];
350+
echo "\t* first token on line $line is $first ($type) *".PHP_EOL;
351+
}
352+
353+
$checkIndent = ($tokens[$first]['column'] - 1);
341354
if (isset($adjustments[$condition]) === true) {
342355
$checkIndent += $adjustments[$condition];
343356
}
@@ -483,12 +496,7 @@ public function process(File $phpcsFile, $stackPtr)
483496

484497
$arrayOpener = $tokens[$arrayCloser]['bracket_opener'];
485498
if ($tokens[$arrayCloser]['line'] !== $tokens[$arrayOpener]['line']) {
486-
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $arrayOpener, true);
487-
$checkIndent = ($tokens[$first]['column'] - 1);
488-
if (isset($adjustments[$first]) === true) {
489-
$checkIndent += $adjustments[$first];
490-
}
491-
499+
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $arrayOpener, true);
492500
$exact = false;
493501

494502
if ($this->debug === true) {
@@ -524,6 +532,11 @@ public function process(File $phpcsFile, $stackPtr)
524532
$first = $phpcsFile->findNext(T_WHITESPACE, ($first + 1), null, true);
525533
}
526534

535+
$checkIndent = ($tokens[$first]['column'] - 1);
536+
if (isset($adjustments[$first]) === true) {
537+
$checkIndent += $adjustments[$first];
538+
}
539+
527540
if (isset($tokens[$first]['scope_closer']) === true
528541
&& $tokens[$first]['scope_closer'] === $first
529542
) {
@@ -609,11 +622,11 @@ public function process(File $phpcsFile, $stackPtr)
609622

610623
// Scope closers reset the required indent to the same level as the opening condition.
611624
if (($checkToken !== null
612-
&& isset($openScopes[$checkToken]) === true
625+
&& (isset($openScopes[$checkToken]) === true
613626
|| (isset($tokens[$checkToken]['scope_condition']) === true
614627
&& isset($tokens[$checkToken]['scope_closer']) === true
615628
&& $tokens[$checkToken]['scope_closer'] === $checkToken
616-
&& $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['scope_opener']]['line']))
629+
&& $tokens[$checkToken]['line'] !== $tokens[$tokens[$checkToken]['scope_opener']]['line'])))
617630
|| ($checkToken === null
618631
&& isset($openScopes[$i]) === true)
619632
) {
@@ -796,6 +809,19 @@ public function process(File $phpcsFile, $stackPtr)
796809
) {
797810
$exact = true;
798811

812+
if ($disableExactEnd > $checkToken) {
813+
foreach ($disableExactStack as $disableExactStackEnd) {
814+
if ($disableExactStackEnd < $checkToken) {
815+
continue;
816+
}
817+
818+
if ($tokens[$checkToken]['conditions'] === $tokens[$disableExactStackEnd]['conditions']) {
819+
$exact = false;
820+
break;
821+
}
822+
}
823+
}
824+
799825
$lastOpener = null;
800826
if (empty($openScopes) === false) {
801827
end($openScopes);
@@ -844,16 +870,34 @@ public function process(File $phpcsFile, $stackPtr)
844870
&& $tokens[($checkToken + 1)]['code'] !== T_DOUBLE_COLON
845871
) {
846872
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($checkToken + 1), null, true);
847-
if ($next === false || $tokens[$next]['code'] !== T_CLOSURE) {
848-
if ($this->debug === true) {
849-
$line = $tokens[$checkToken]['line'];
850-
$type = $tokens[$checkToken]['type'];
851-
echo "\t* method prefix ($type) found on line $line; indent set to exact *".PHP_EOL;
873+
if ($next === false
874+
|| ($tokens[$next]['code'] !== T_CLOSURE
875+
&& $tokens[$next]['code'] !== T_VARIABLE
876+
&& $tokens[$next]['code'] !== T_FN)
877+
) {
878+
$isMethodPrefix = true;
879+
if (isset($tokens[$checkToken]['nested_parenthesis']) === true) {
880+
$parenthesis = array_keys($tokens[$checkToken]['nested_parenthesis']);
881+
$deepestOpen = array_pop($parenthesis);
882+
if (isset($tokens[$deepestOpen]['parenthesis_owner']) === true
883+
&& $tokens[$tokens[$deepestOpen]['parenthesis_owner']]['code'] === T_FUNCTION
884+
) {
885+
// This is constructor property promotion and not a method prefix.
886+
$isMethodPrefix = false;
887+
}
852888
}
853889

854-
$exact = true;
855-
}
856-
}
890+
if ($isMethodPrefix === true) {
891+
if ($this->debug === true) {
892+
$line = $tokens[$checkToken]['line'];
893+
$type = $tokens[$checkToken]['type'];
894+
echo "\t* method prefix ($type) found on line $line; indent set to exact *".PHP_EOL;
895+
}
896+
897+
$exact = true;
898+
}
899+
}//end if
900+
}//end if
857901

858902
// JS property indentation has to be exact or else if will break
859903
// things like function and object indentation.
@@ -890,8 +934,6 @@ public function process(File $phpcsFile, $stackPtr)
890934
}
891935
}
892936
}
893-
894-
$checkIndent = (int) (ceil($checkIndent / $this->indent) * $this->indent);
895937
}//end if
896938

897939
// Close tags needs to be indented to exact column positions.
@@ -911,7 +953,8 @@ public function process(File $phpcsFile, $stackPtr)
911953
// Don't perform strict checking on chained method calls since they
912954
// are often covered by custom rules.
913955
if ($checkToken !== null
914-
&& $tokens[$checkToken]['code'] === T_OBJECT_OPERATOR
956+
&& ($tokens[$checkToken]['code'] === T_OBJECT_OPERATOR
957+
|| $tokens[$checkToken]['code'] === T_NULLSAFE_OBJECT_OPERATOR)
915958
&& $exact === true
916959
) {
917960
$exact = false;
@@ -988,18 +1031,38 @@ public function process(File $phpcsFile, $stackPtr)
9881031
}
9891032

9901033
if ($this->tabIndent === true) {
991-
$error .= '%s tabs, found %s';
992-
$data = [
993-
floor($checkIndent / $this->tabWidth),
994-
floor($tokenIndent / $this->tabWidth),
995-
];
1034+
$expectedTabs = floor($checkIndent / $this->tabWidth);
1035+
$foundTabs = floor($tokenIndent / $this->tabWidth);
1036+
$foundSpaces = ($tokenIndent - ($foundTabs * $this->tabWidth));
1037+
if ($foundSpaces > 0) {
1038+
if ($foundTabs > 0) {
1039+
$error .= '%s tabs, found %s tabs and %s spaces';
1040+
$data = [
1041+
$expectedTabs,
1042+
$foundTabs,
1043+
$foundSpaces,
1044+
];
1045+
} else {
1046+
$error .= '%s tabs, found %s spaces';
1047+
$data = [
1048+
$expectedTabs,
1049+
$foundSpaces,
1050+
];
1051+
}
1052+
} else {
1053+
$error .= '%s tabs, found %s';
1054+
$data = [
1055+
$expectedTabs,
1056+
$foundTabs,
1057+
];
1058+
}//end if
9961059
} else {
9971060
$error .= '%s spaces, found %s';
9981061
$data = [
9991062
$checkIndent,
10001063
$tokenIndent,
10011064
];
1002-
}
1065+
}//end if
10031066

10041067
if ($this->debug === true) {
10051068
$line = $tokens[$checkToken]['line'];
@@ -1030,15 +1093,17 @@ public function process(File $phpcsFile, $stackPtr)
10301093

10311094
// Don't check indents exactly between arrays as they tend to have custom rules.
10321095
if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
1096+
$disableExactStack[$tokens[$i]['bracket_closer']] = $tokens[$i]['bracket_closer'];
10331097
$disableExactEnd = max($disableExactEnd, $tokens[$i]['bracket_closer']);
10341098
if ($this->debug === true) {
1035-
$line = $tokens[$i]['line'];
1036-
$type = $tokens[$disableExactEnd]['type'];
1099+
$line = $tokens[$i]['line'];
1100+
$type = $tokens[$disableExactEnd]['type'];
1101+
$endLine = $tokens[$disableExactEnd]['line'];
10371102
echo "Opening short array bracket found on line $line".PHP_EOL;
10381103
if ($disableExactEnd === $tokens[$i]['bracket_closer']) {
1039-
echo "\t=> disabling exact indent checking until $disableExactEnd ($type)".PHP_EOL;
1104+
echo "\t=> disabling exact indent checking until $disableExactEnd ($type) on line $endLine".PHP_EOL;
10401105
} else {
1041-
echo "\t=> continuing to disable exact indent checking until $disableExactEnd ($type)".PHP_EOL;
1106+
echo "\t=> continuing to disable exact indent checking until $disableExactEnd ($type) on line $endLine".PHP_EOL;
10421107
}
10431108
}
10441109
}
@@ -1050,7 +1115,6 @@ public function process(File $phpcsFile, $stackPtr)
10501115
) {
10511116
if ($this->debug === true) {
10521117
$line = $tokens[$i]['line'];
1053-
$type = $tokens[$disableExactEnd]['type'];
10541118
echo "Here/nowdoc found on line $line".PHP_EOL;
10551119
}
10561120

@@ -1074,8 +1138,11 @@ public function process(File $phpcsFile, $stackPtr)
10741138
if ($tokens[$i]['code'] === T_CONSTANT_ENCAPSED_STRING
10751139
|| $tokens[$i]['code'] === T_DOUBLE_QUOTED_STRING
10761140
) {
1077-
$i = $phpcsFile->findNext($tokens[$i]['code'], ($i + 1), null, true);
1078-
$i--;
1141+
$nextNonTextString = $phpcsFile->findNext($tokens[$i]['code'], ($i + 1), null, true);
1142+
if ($nextNonTextString !== false) {
1143+
$i = ($nextNonTextString - 1);
1144+
}
1145+
10791146
continue;
10801147
}
10811148

@@ -1162,7 +1229,7 @@ public function process(File $phpcsFile, $stackPtr)
11621229
if ($this->debug === true) {
11631230
$type = str_replace('_', ' ', strtolower(substr($tokens[$i]['type'], 2)));
11641231
$line = $tokens[$i]['line'];
1165-
echo "* ignoring single-line $type on line $line".PHP_EOL;
1232+
echo "* ignoring single-line $type on line $line *".PHP_EOL;
11661233
}
11671234

11681235
$i = $closer;
@@ -1232,14 +1299,24 @@ public function process(File $phpcsFile, $stackPtr)
12321299
if ($this->debug === true) {
12331300
$line = $tokens[$i]['line'];
12341301
$type = $tokens[$i]['type'];
1235-
echo "* ignoring single-line $type on line $line".PHP_EOL;
1302+
echo "* ignoring single-line $type on line $line *".PHP_EOL;
12361303
}
12371304

12381305
$i = $closer;
12391306
continue;
12401307
}
12411308

12421309
$condition = $tokens[$tokens[$i]['scope_condition']]['code'];
1310+
if ($condition === T_FN) {
1311+
if ($this->debug === true) {
1312+
$line = $tokens[$tokens[$i]['scope_condition']]['line'];
1313+
echo "* ignoring arrow function on line $line *".PHP_EOL;
1314+
}
1315+
1316+
$i = $closer;
1317+
continue;
1318+
}
1319+
12431320
if (isset(Tokens::$scopeOpeners[$condition]) === true
12441321
&& in_array($condition, $this->nonIndentingScopes, true) === false
12451322
) {
@@ -1279,7 +1356,7 @@ public function process(File $phpcsFile, $stackPtr)
12791356
if ($tokens[$i]['line'] === $tokens[$closer]['line']) {
12801357
if ($this->debug === true) {
12811358
$line = $tokens[$i]['line'];
1282-
echo "* ignoring single-line JS object on line $line".PHP_EOL;
1359+
echo "* ignoring single-line JS object on line $line *".PHP_EOL;
12831360
}
12841361

12851362
$i = $closer;
@@ -1309,11 +1386,14 @@ public function process(File $phpcsFile, $stackPtr)
13091386
continue;
13101387
}//end if
13111388

1312-
// Closing an anon class or function.
1389+
// Closing an anon class, closure, or match.
1390+
// Each may be returned, which can confuse control structures that
1391+
// use return as a closer, like CASE statements.
13131392
if (isset($tokens[$i]['scope_condition']) === true
13141393
&& $tokens[$i]['scope_closer'] === $i
13151394
&& ($tokens[$tokens[$i]['scope_condition']]['code'] === T_CLOSURE
1316-
|| $tokens[$tokens[$i]['scope_condition']]['code'] === T_ANON_CLASS)
1395+
|| $tokens[$tokens[$i]['scope_condition']]['code'] === T_ANON_CLASS
1396+
|| $tokens[$tokens[$i]['scope_condition']]['code'] === T_MATCH)
13171397
) {
13181398
if ($this->debug === true) {
13191399
$type = str_replace('_', ' ', strtolower(substr($tokens[$tokens[$i]['scope_condition']]['type'], 2)));
@@ -1493,7 +1573,7 @@ protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
14931573
$padding = '';
14941574
if ($length > 0) {
14951575
if ($this->tabIndent === true) {
1496-
$numTabs = (int) floor($length / $this->tabWidth);
1576+
$numTabs = floor($length / $this->tabWidth);
14971577
if ($numTabs > 0) {
14981578
$numSpaces = ($length - ($numTabs * $this->tabWidth));
14991579
$padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces);
@@ -1531,7 +1611,7 @@ protected function adjustIndent(File $phpcsFile, $stackPtr, $length, $change)
15311611
$padding = ($length + $change);
15321612
if ($padding > 0) {
15331613
if ($this->tabIndent === true) {
1534-
$numTabs = (int) floor($padding / $this->tabWidth);
1614+
$numTabs = floor($padding / $this->tabWidth);
15351615
$numSpaces = ($padding - ($numTabs * $this->tabWidth));
15361616
$padding = str_repeat("\t", $numTabs).str_repeat(' ', $numSpaces);
15371617
} else {

tests/Drupal/good/good.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,3 +1887,21 @@ public function foo() {
18871887
'#empty' => $this->t('No strings available.'),
18881888
'#attributes' => ['class' => ['locale-translate-edit-table']],
18891889
];
1890+
1891+
/**
1892+
* Implements hook_cron().
1893+
*/
1894+
#[Hook('cron')]
1895+
class CronHook {
1896+
1897+
public function __construct(
1898+
private readonly EntityTypeManagerInterface $entityTypeManager,
1899+
private readonly StreamWrapperManagerInterface $streamWrapperManager,
1900+
private readonly ConfigFactoryInterface $configFactory,
1901+
private readonly FileUsageInterface $fileUsage,
1902+
private readonly TimeInterface $time,
1903+
#[Autowire('@logger.channel.file')]
1904+
private readonly LoggerInterface $logger,
1905+
) {}
1906+
1907+
}

0 commit comments

Comments
 (0)