diff --git a/CHANGELOG.md b/CHANGELOG.md index e35372c8..7a804693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,17 @@ This projects adheres to [Keep a CHANGELOG](https://keepachangelog.com/) and use _Nothing yet._ +## [1.0.5] - 2023-04-17 + +### Fixed + +#### Utils + +* The `Lists::getAssignments()` method could previously get confused over exotic list keys. Fixed now. [#459] + +[#459]: https://github.com/PHPCSStandards/PHPCSUtils/pull/459 + + ## [1.0.4] - 2023-04-15 ### Changed @@ -839,6 +850,7 @@ This initial alpha release contains the following utility classes: [Unreleased]: https://github.com/PHPCSStandards/PHPCSUtils/compare/stable...HEAD +[1.0.5]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.4...1.0.5 [1.0.4]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.3...1.0.4 [1.0.3]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.2...1.0.3 [1.0.2]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.1...1.0.2 diff --git a/PHPCSUtils/Utils/Lists.php b/PHPCSUtils/Utils/Lists.php index 6213bab1..baf2c02a 100644 --- a/PHPCSUtils/Utils/Lists.php +++ b/PHPCSUtils/Utils/Lists.php @@ -237,6 +237,7 @@ public static function getAssignments(File $phpcsFile, $stackPtr) // Partial reset. $start = null; $lastNonEmpty = null; + $list = null; // Prevent confusion when short array was used as the key. $reference = null; break; @@ -245,8 +246,14 @@ public static function getAssignments(File $phpcsFile, $stackPtr) // Check if this is the end of the list or only a token with the same type as the list closer. if ($tokens[$i]['code'] === $tokens[$closer]['code']) { if ($i !== $closer) { + /* + * Shouldn't be possible anymore now nested brackets are being skipped over, + * but keep it just in case. + */ + // @codeCoverageIgnoreStart $lastNonEmpty = $i; break; + // @codeCoverageIgnoreEnd } elseif ($start === null && $lastComma === $opener) { // This is an empty list. break 2; @@ -302,7 +309,7 @@ public static function getAssignments(File $phpcsFile, $stackPtr) /* * As the top level list has an open/close, we know we don't have a parse error and - * any nested lists will be tokenized correctly, so no need for extra checks here. + * any nested (short) arrays/lists will be tokenized correctly, so no need for extra checks here. */ $nestedOpenClose = self::getOpenClose($phpcsFile, $i, true); $list = $i; @@ -321,6 +328,25 @@ public static function getAssignments(File $phpcsFile, $stackPtr) $start = $i; } + // Skip over everything within all types of brackets which may be used in keys. + if (isset($tokens[$i]['bracket_opener'], $tokens[$i]['bracket_closer']) + && $i === $tokens[$i]['bracket_opener'] + ) { + $i = $tokens[$i]['bracket_closer']; + } elseif ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_closer']) + ) { + $i = $tokens[$i]['parenthesis_closer']; + } elseif (isset($tokens[$i]['scope_condition'], $tokens[$i]['scope_closer']) + && $tokens[$i]['scope_condition'] === $i + ) { + $i = $tokens[$i]['scope_closer']; + } elseif ($tokens[$i]['code'] === \T_ATTRIBUTE + && isset($tokens[$i]['attribute_closer']) + ) { + $i = $tokens[$i]['attribute_closer']; + } + $lastNonEmpty = $i; break; } diff --git a/Tests/Utils/Lists/GetAssignmentsTest.inc b/Tests/Utils/Lists/GetAssignmentsTest.inc index da8cfbfa..3d9e9561 100644 --- a/Tests/Utils/Lists/GetAssignmentsTest.inc +++ b/Tests/Utils/Lists/GetAssignmentsTest.inc @@ -81,6 +81,22 @@ list( list("x" => $x2, "y" => $y2) ) = $points; +/* testKeyedLongListWithCommasInKey */ +list( + $map->getKey($type, $urls) => $x, + array( $tab, $tabs ) => &$y['key'][$tab], + get($year, $day) => $z[$year], + #[MyAttribute] + function($a, $b) { return 'key'; } => $a +) = $array; + +/* testKeyedShortListWithCommasInKeyAndTrailingComma */ +[ + $map->getKey($type, $urls) => $x, + [$tab, $tabs] => &$y['key'][$tab], + get($year, $day) => $z[$year], +] = $array; + /* testLongListMixedKeyedUnkeyed */ // Parse error, but not our concern. list($unkeyed, "key" => $keyed) = $array; diff --git a/Tests/Utils/Lists/GetAssignmentsTest.php b/Tests/Utils/Lists/GetAssignmentsTest.php index 7ab9cbaa..3bbf364c 100644 --- a/Tests/Utils/Lists/GetAssignmentsTest.php +++ b/Tests/Utils/Lists/GetAssignmentsTest.php @@ -838,6 +838,136 @@ public function dataGetAssignments() ], ], ], + 'long-list-keyed-with-commas-in-key' => [ + 'testMarker' => '/* testKeyedLongListWithCommasInKey */', + 'targetToken' => \T_LIST, + 'expected' => [ + 0 => [ + 'raw' => '$map->getKey($type, $urls) => $x', + 'assignment' => '$x', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$x', + 'assignment_token' => 16, + 'assignment_end_token' => 16, + 'assign_by_reference' => false, + 'reference_token' => false, + 'key' => '$map->getKey($type, $urls)', + 'key_token' => 4, + 'key_end_token' => 12, + 'double_arrow_token' => 14, + ], + 1 => [ + 'raw' => 'array( $tab, $tabs ) => &$y[\'key\'][$tab]', + 'assignment' => '$y[\'key\'][$tab]', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$y', + 'assignment_token' => 33, + 'assignment_end_token' => 39, + 'assign_by_reference' => true, + 'reference_token' => 32, + 'key' => 'array( $tab, $tabs )', + 'key_token' => 20, + 'key_end_token' => 28, + 'double_arrow_token' => 30, + ], + 2 => [ + 'raw' => 'get($year, $day) => $z[$year]', + 'assignment' => '$z[$year]', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$z', + 'assignment_token' => 53, + 'assignment_end_token' => 56, + 'assign_by_reference' => false, + 'reference_token' => false, + 'key' => 'get($year, $day)', + 'key_token' => 43, + 'key_end_token' => 49, + 'double_arrow_token' => 51, + ], + 3 => [ + 'raw' => '#[MyAttribute] + function($a, $b) { return \'key\'; } => $a', + 'assignment' => '$a', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$a', + 'assignment_token' => 84, + 'assignment_end_token' => 84, + 'assign_by_reference' => false, + 'reference_token' => false, + 'key' => '#[MyAttribute] function($a, $b) { return \'key\'; }', + 'key_token' => 60, + 'key_end_token' => 80, + 'double_arrow_token' => 82, + ], + ], + ], + 'short-list-keyed-with-commas-in-key' => [ + 'testMarker' => '/* testKeyedShortListWithCommasInKeyAndTrailingComma */', + 'targetToken' => \T_OPEN_SHORT_ARRAY, + 'expected' => [ + 0 => [ + 'raw' => '$map->getKey($type, $urls) => $x', + 'assignment' => '$x', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$x', + 'assignment_token' => 15, + 'assignment_end_token' => 15, + 'assign_by_reference' => false, + 'reference_token' => false, + 'key' => '$map->getKey($type, $urls)', + 'key_token' => 3, + 'key_end_token' => 11, + 'double_arrow_token' => 13, + ], + 1 => [ + 'raw' => '[$tab, $tabs] => &$y[\'key\'][$tab]', + 'assignment' => '$y[\'key\'][$tab]', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$y', + 'assignment_token' => 29, + 'assignment_end_token' => 35, + 'assign_by_reference' => true, + 'reference_token' => 28, + 'key' => '[$tab, $tabs]', + 'key_token' => 19, + 'key_end_token' => 24, + 'double_arrow_token' => 26, + ], + 2 => [ + 'raw' => 'get($year, $day) => $z[$year]', + 'assignment' => '$z[$year]', + 'is_empty' => false, + 'is_nested_list' => false, + 'variable' => '$z', + 'assignment_token' => 49, + 'assignment_end_token' => 52, + 'assign_by_reference' => false, + 'reference_token' => false, + 'key' => 'get($year, $day)', + 'key_token' => 39, + 'key_end_token' => 45, + 'double_arrow_token' => 47, + ], + 3 => [ + 'raw' => '', + 'assignment' => '', + 'is_empty' => true, + 'is_nested_list' => false, + 'variable' => false, + 'assignment_token' => false, + 'assignment_end_token' => false, + 'assign_by_reference' => false, + 'reference_token' => false, + ], + ], + ], + 'parse-error-long-list-mixed-keyed-unkeyed' => [ 'testMarker' => '/* testLongListMixedKeyedUnkeyed */', 'targetToken' => \T_LIST,