Skip to content

Commit 5ce46a8

Browse files
authored
Allow array unpacking in ArrayDeclaration multiline Sniff (#3843)
Ignore array unpacking when determining whether an array is a list- or dict-shaped. Includes tests. Fixes #3557
1 parent 653f390 commit 5ce46a8

File tree

6 files changed

+216
-9
lines changed

6 files changed

+216
-9
lines changed

src/Standards/Squiz/Sniffs/Arrays/ArrayDeclarationSniff.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,13 @@ public function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $array
430430
}
431431

432432
if ($keyUsed === true && $tokens[$lastToken]['code'] === T_COMMA) {
433-
$error = 'No key specified for array entry; first entry specifies key';
434-
$phpcsFile->addError($error, $nextToken, 'NoKeySpecified');
435-
return;
433+
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($lastToken + 1), null, true);
434+
// Allow for PHP 7.4+ array unpacking within an array declaration.
435+
if ($tokens[$nextToken]['code'] !== T_ELLIPSIS) {
436+
$error = 'No key specified for array entry; first entry specifies key';
437+
$phpcsFile->addError($error, $nextToken, 'NoKeySpecified');
438+
return;
439+
}
436440
}
437441

438442
if ($keyUsed === false) {
@@ -470,8 +474,17 @@ public function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $array
470474
true
471475
);
472476

473-
$indices[] = ['value' => $valueContent];
474-
$singleUsed = true;
477+
$indices[] = ['value' => $valueContent];
478+
$usesArrayUnpacking = $phpcsFile->findPrevious(
479+
Tokens::$emptyTokens,
480+
($nextToken - 2),
481+
null,
482+
true
483+
);
484+
if ($tokens[$usesArrayUnpacking]['code'] !== T_ELLIPSIS) {
485+
// Don't decide if an array is key => value indexed or not when PHP 7.4+ array unpacking is used.
486+
$singleUsed = true;
487+
}
475488
}//end if
476489

477490
$lastToken = $nextToken;

src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.1.inc

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,14 +475,57 @@ yield array(
475475
static fn () : string => '',
476476
);
477477

478-
$foo = [
478+
$foo = array(
479479
'foo' => match ($anything) {
480480
'foo' => 'bar',
481481
default => null,
482482
},
483-
];
483+
);
484484

485485
// Intentional syntax error.
486486
$a = array(
487487
'a' =>
488488
);
489+
490+
// Safeguard correct errors for key/no key when PHP 7.4+ array unpacking is encountered.
491+
$x = array(
492+
...$a,
493+
'foo' => 'bar',
494+
);
495+
496+
$x = array(
497+
'foo' => 'bar',
498+
...$a,
499+
);
500+
501+
$x = array(
502+
'foo' => 'bar',
503+
...$a,
504+
'baz' => 'bar',
505+
);
506+
507+
$x = array(
508+
...$a,
509+
'foo' => 'bar', // OK.
510+
'bar', // NoKeySpecified Error (based on second entry).
511+
);
512+
513+
$x = array(
514+
...$a,
515+
'bar', // OK.
516+
'foo' => 'bar', // KeySpecified Error (based on second entry).
517+
);
518+
519+
$x = array(
520+
'foo' => 'bar',
521+
...$a,
522+
'baz' => 'bar',
523+
'bar', // NoKeySpecified Error (based on first entry).
524+
);
525+
526+
$x = array(
527+
'bar',
528+
...$a,
529+
'bar',
530+
'baz' => 'bar', // KeySpecified (based on first entry).
531+
);

src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.1.inc.fixed

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,14 +511,57 @@ yield array(
511511
static fn () : string => '',
512512
);
513513

514-
$foo = [
514+
$foo = array(
515515
'foo' => match ($anything) {
516516
'foo' => 'bar',
517517
default => null,
518518
},
519-
];
519+
);
520520

521521
// Intentional syntax error.
522522
$a = array(
523523
'a' =>
524524
);
525+
526+
// Safeguard correct errors for key/no key when PHP 7.4+ array unpacking is encountered.
527+
$x = array(
528+
...$a,
529+
'foo' => 'bar',
530+
);
531+
532+
$x = array(
533+
'foo' => 'bar',
534+
...$a,
535+
);
536+
537+
$x = array(
538+
'foo' => 'bar',
539+
...$a,
540+
'baz' => 'bar',
541+
);
542+
543+
$x = array(
544+
...$a,
545+
'foo' => 'bar', // OK.
546+
'bar', // NoKeySpecified Error (based on second entry).
547+
);
548+
549+
$x = array(
550+
...$a,
551+
'bar', // OK.
552+
'foo' => 'bar', // KeySpecified Error (based on second entry).
553+
);
554+
555+
$x = array(
556+
'foo' => 'bar',
557+
...$a,
558+
'baz' => 'bar',
559+
'bar', // NoKeySpecified Error (based on first entry).
560+
);
561+
562+
$x = array(
563+
'bar',
564+
...$a,
565+
'bar',
566+
'baz' => 'bar', // KeySpecified (based on first entry).
567+
);

src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.2.inc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,57 @@ yield [
464464
static fn () : string => '',
465465
];
466466

467+
$foo = [
468+
'foo' => match ($anything) {
469+
'foo' => 'bar',
470+
default => null,
471+
},
472+
];
473+
467474
// Intentional syntax error.
468475
$a = [
469476
'a' =>
470477
];
478+
479+
// Safeguard correct errors for key/no key when PHP 7.4+ array unpacking is encountered.
480+
$x = [
481+
...$a,
482+
'foo' => 'bar',
483+
];
484+
485+
$x = [
486+
'foo' => 'bar',
487+
...$a,
488+
];
489+
490+
$x = [
491+
'foo' => 'bar',
492+
...$a,
493+
'baz' => 'bar',
494+
];
495+
496+
$x = [
497+
...$a,
498+
'foo' => 'bar', // OK.
499+
'bar', // NoKeySpecified Error (based on second entry).
500+
];
501+
502+
$x = [
503+
...$a,
504+
'bar', // OK.
505+
'foo' => 'bar', // KeySpecified Error (based on second entry).
506+
];
507+
508+
$x = [
509+
'foo' => 'bar',
510+
...$a,
511+
'baz' => 'bar',
512+
'bar', // NoKeySpecified Error (based on first entry).
513+
];
514+
515+
$x = [
516+
'bar',
517+
...$a,
518+
'bar',
519+
'baz' => 'bar', // KeySpecified (based on first entry).
520+
];

src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.2.inc.fixed

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,57 @@ yield [
498498
static fn () : string => '',
499499
];
500500

501+
$foo = [
502+
'foo' => match ($anything) {
503+
'foo' => 'bar',
504+
default => null,
505+
},
506+
];
507+
501508
// Intentional syntax error.
502509
$a = [
503510
'a' =>
504511
];
512+
513+
// Safeguard correct errors for key/no key when PHP 7.4+ array unpacking is encountered.
514+
$x = [
515+
...$a,
516+
'foo' => 'bar',
517+
];
518+
519+
$x = [
520+
'foo' => 'bar',
521+
...$a,
522+
];
523+
524+
$x = [
525+
'foo' => 'bar',
526+
...$a,
527+
'baz' => 'bar',
528+
];
529+
530+
$x = [
531+
...$a,
532+
'foo' => 'bar', // OK.
533+
'bar', // NoKeySpecified Error (based on second entry).
534+
];
535+
536+
$x = [
537+
...$a,
538+
'bar', // OK.
539+
'foo' => 'bar', // KeySpecified Error (based on second entry).
540+
];
541+
542+
$x = [
543+
'foo' => 'bar',
544+
...$a,
545+
'baz' => 'bar',
546+
'bar', // NoKeySpecified Error (based on first entry).
547+
];
548+
549+
$x = [
550+
'bar',
551+
...$a,
552+
'bar',
553+
'baz' => 'bar', // KeySpecified (based on first entry).
554+
];

src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ public function getErrorList($testFile='')
124124
467 => 1,
125125
471 => 1,
126126
472 => 1,
127+
510 => 1,
128+
516 => 1,
129+
523 => 1,
130+
530 => 1,
127131
];
128132
case 'ArrayDeclarationUnitTest.2.inc':
129133
return [
@@ -210,6 +214,10 @@ public function getErrorList($testFile='')
210214
456 => 1,
211215
460 => 1,
212216
461 => 1,
217+
499 => 1,
218+
505 => 1,
219+
512 => 1,
220+
519 => 1,
213221
];
214222
default:
215223
return [];

0 commit comments

Comments
 (0)