diff --git a/CHANGELOG.md b/CHANGELOG.md index 1599c220..b9f163ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,52 @@ This projects adheres to [Keep a CHANGELOG](https://keepachangelog.com/) and use _Nothing yet._ +## [1.0.12] - 2024-05-20 + +### Added + +#### PHPCS BackCompat + +* `BCFile::getMemberProperties()`: sync with PHPCS 3.10.0 - support for PHP 8.2 DNF types. [#604] +* `BCFile::getMethodProperties()`: sync with PHPCS 3.10.0 - support for PHP 8.2 DNF types. [#604] +* `BCFile::getMethodParameters()`: sync with PHPCS 3.10.0 - support for PHP 8.2 DNF types. [#604] + +#### Utils + +* `FunctionDeclarations::getParameters()`: support for PHP 8.2 DNF types. [#604] +* `FunctionDeclarations::getProperties()`: support for PHP 8.2 DNF types. [#604] +* `Variables::getMemberProperties()`: support for PHP 8.2 DNF types. [#604] + +### Changed + +#### Tokens + +* `Collections::parameterTypeTokens()`, `Collections::propertyTypeTokens()` and `Collections::returnTypeTokens()`: now include the new `T_TYPE_OPEN_PARENTHESIS` and `T_TYPE_CLOSE_PARENTHESIS` tokens for PHP 8.2 DNF type support. [#604] + +#### Utils + +* `ControlStructures::getCaughtExceptions()`: will now silently ignore parse errors in the code under scan which prevent the method from analyzing a `catch` statement. [#594] + The method will now return an empty array instead of throwing a `PHP_CodeSniffer\Exceptions\RuntimeException`. + +#### Other + +* Dropped support for [PHP_CodeSniffer] < 3.10.0. [#603] + Please ensure you run `composer update phpcsstandards/phpcsutils --with-dependencies` to benefit from this. +* Various housekeeping and documentation improvements. + +### Fixed + +#### Utils + +* `UseStatements::splitImportUseStatement()`: the values in the return array will now never include a leading backslash. [#590] + Previously the behaviour around import `use` statements declared with a leading backslash was undefined and the backslash would be included in the return value. + +[#590]: https://github.com/PHPCSStandards/PHPCSUtils/pull/590 +[#594]: https://github.com/PHPCSStandards/PHPCSUtils/pull/594 +[#603]: https://github.com/PHPCSStandards/PHPCSUtils/pull/603 +[#604]: https://github.com/PHPCSStandards/PHPCSUtils/pull/604 + + ## [1.0.11] - 2024-04-24 ### Changed @@ -1005,6 +1051,7 @@ This initial alpha release contains the following utility classes: [Unreleased]: https://github.com/PHPCSStandards/PHPCSUtils/compare/stable...HEAD +[1.0.12]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.11...1.0.12 [1.0.11]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.10...1.0.11 [1.0.10]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.9...1.0.10 [1.0.9]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.8...1.0.9 diff --git a/PHPCSUtils/BackCompat/BCFile.php b/PHPCSUtils/BackCompat/BCFile.php index f64d7da0..8c063439 100644 --- a/PHPCSUtils/BackCompat/BCFile.php +++ b/PHPCSUtils/BackCompat/BCFile.php @@ -76,7 +76,7 @@ final class BCFile * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::getDeclarationName() Original source. * @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version. @@ -228,7 +228,9 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr) // it's likely to be an array which might have arguments in it. This // could cause problems in our parsing below, so lets just skip to the // end of it. - if (isset($tokens[$i]['parenthesis_opener']) === true) { + if ($tokens[$i]['code'] !== T_TYPE_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_opener']) === true + ) { // Don't do this if it's the close parenthesis for the method. if ($i !== $tokens[$i]['parenthesis_closer']) { $i = ($tokens[$i]['parenthesis_closer'] + 1); @@ -324,6 +326,8 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr) case T_NS_SEPARATOR: case T_TYPE_UNION: case T_TYPE_INTERSECTION: + case T_TYPE_OPEN_PARENTHESIS: + case T_TYPE_CLOSE_PARENTHESIS: case T_FALSE: case T_TRUE: case T_NULL: @@ -462,7 +466,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - PHPCS 3.9.2: skip over closure use statements. PHPCS #421. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source. * @see \PHPCSUtils\Utils\FunctionDeclarations::getProperties() PHPCSUtils native improved version. @@ -480,147 +484,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr) */ public static function getMethodProperties(File $phpcsFile, $stackPtr) { - $tokens = $phpcsFile->getTokens(); - - if ($tokens[$stackPtr]['code'] !== T_FUNCTION - && $tokens[$stackPtr]['code'] !== T_CLOSURE - && $tokens[$stackPtr]['code'] !== T_FN - ) { - throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_FN'); - } - - if ($tokens[$stackPtr]['code'] === T_FUNCTION) { - $valid = [ - T_PUBLIC => T_PUBLIC, - T_PRIVATE => T_PRIVATE, - T_PROTECTED => T_PROTECTED, - T_STATIC => T_STATIC, - T_FINAL => T_FINAL, - T_ABSTRACT => T_ABSTRACT, - T_WHITESPACE => T_WHITESPACE, - T_COMMENT => T_COMMENT, - T_DOC_COMMENT => T_DOC_COMMENT, - ]; - } else { - $valid = [ - T_STATIC => T_STATIC, - T_WHITESPACE => T_WHITESPACE, - T_COMMENT => T_COMMENT, - T_DOC_COMMENT => T_DOC_COMMENT, - ]; - } - - $scope = 'public'; - $scopeSpecified = false; - $isAbstract = false; - $isFinal = false; - $isStatic = false; - - for ($i = ($stackPtr - 1); $i > 0; $i--) { - if (isset($valid[$tokens[$i]['code']]) === false) { - break; - } - - switch ($tokens[$i]['code']) { - case T_PUBLIC: - $scope = 'public'; - $scopeSpecified = true; - break; - case T_PRIVATE: - $scope = 'private'; - $scopeSpecified = true; - break; - case T_PROTECTED: - $scope = 'protected'; - $scopeSpecified = true; - break; - case T_ABSTRACT: - $isAbstract = true; - break; - case T_FINAL: - $isFinal = true; - break; - case T_STATIC: - $isStatic = true; - break; - } - } - - $returnType = ''; - $returnTypeToken = false; - $returnTypeEndToken = false; - $nullableReturnType = false; - $hasBody = true; - $returnTypeTokens = Collections::returnTypeTokens(); - - if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) { - $scopeOpener = null; - if (isset($tokens[$stackPtr]['scope_opener']) === true) { - $scopeOpener = $tokens[$stackPtr]['scope_opener']; - } - - for ($i = $tokens[$stackPtr]['parenthesis_closer']; $i < $phpcsFile->numTokens; $i++) { - if (($scopeOpener === null && $tokens[$i]['code'] === T_SEMICOLON) - || ($scopeOpener !== null && $i === $scopeOpener) - ) { - // End of function definition. - break; - } - - if ($tokens[$i]['code'] === T_USE) { - // Skip over closure use statements. - for ($j = ($i + 1); $j < $phpcsFile->numTokens && isset(Tokens::$emptyTokens[$tokens[$j]['code']]) === true; $j++); - if ($tokens[$j]['code'] === T_OPEN_PARENTHESIS) { - if (isset($tokens[$j]['parenthesis_closer']) === false) { - // Live coding/parse error, stop parsing. - break; - } - - $i = $tokens[$j]['parenthesis_closer']; - continue; - } - } - - if ($tokens[$i]['code'] === T_NULLABLE) { - $nullableReturnType = true; - } - - if (isset($returnTypeTokens[$tokens[$i]['code']]) === true) { - if ($returnTypeToken === false) { - $returnTypeToken = $i; - } - - $returnType .= $tokens[$i]['content']; - $returnTypeEndToken = $i; - } - } - - if ($tokens[$stackPtr]['code'] === T_FN) { - $bodyToken = T_FN_ARROW; - } else { - $bodyToken = T_OPEN_CURLY_BRACKET; - } - - $end = $phpcsFile->findNext([$bodyToken, T_SEMICOLON], $tokens[$stackPtr]['parenthesis_closer']); - $hasBody = ($end !== false && $tokens[$end]['code'] === $bodyToken); - } - - if ($returnType !== '' && $nullableReturnType === true) { - $returnType = '?' . $returnType; - } - - return [ - 'scope' => $scope, - 'scope_specified' => $scopeSpecified, - 'return_type' => $returnType, - 'return_type_token' => $returnTypeToken, - 'return_type_end_token' => $returnTypeEndToken, - 'nullable_return_type' => $nullableReturnType, - 'is_abstract' => $isAbstract, - 'is_final' => $isFinal, - 'is_static' => $isStatic, - 'has_body' => $hasBody, - ]; + return $phpcsFile->getMethodProperties($stackPtr); } /** @@ -647,7 +511,7 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::getMemberProperties() Original source. * @see \PHPCSUtils\Utils\Variables::getMemberProperties() PHPCSUtils native improved version. @@ -685,7 +549,7 @@ public static function getMemberProperties(File $phpcsFile, $stackPtr) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 1.3.0. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::getClassProperties() Original source. * @see \PHPCSUtils\Utils\ObjectDeclarations::getClassProperties() PHPCSUtils native improved version. @@ -713,7 +577,7 @@ public static function getClassProperties(File $phpcsFile, $stackPtr) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::isReference() Original source. * @see \PHPCSUtils\Utils\Operators::isReference() PHPCSUtils native improved version. @@ -739,7 +603,7 @@ public static function isReference(File $phpcsFile, $stackPtr) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::getTokensAsString() Original source. * @see \PHPCSUtils\Utils\GetTokensAsString Related set of functions. @@ -768,7 +632,7 @@ public static function getTokensAsString(File $phpcsFile, $start, $length, $orig * * Changelog for the PHPCS native function: * - Introduced in PHPCS 2.1.0. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::findStartOfStatement() Original source. * @@ -792,7 +656,7 @@ public static function findStartOfStatement(File $phpcsFile, $start, $ignore = n * * Changelog for the PHPCS native function: * - Introduced in PHPCS 2.1.0. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::findEndOfStatement() Original source. * @@ -816,7 +680,7 @@ public static function findEndOfStatement(File $phpcsFile, $start, $ignore = nul * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::hasCondition() Original source. * @see \PHPCSUtils\Utils\Conditions::hasCondition() PHPCSUtils native alternative. @@ -841,7 +705,7 @@ public static function hasCondition(File $phpcsFile, $stackPtr, $types) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 1.3.0. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::getCondition() Original source. * @see \PHPCSUtils\Utils\Conditions::getCondition() More versatile alternative. @@ -872,7 +736,7 @@ public static function getCondition(File $phpcsFile, $stackPtr, $type, $first = * * Changelog for the PHPCS native function: * - Introduced in PHPCS 1.2.0. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::findExtendedClassName() Original source. * @see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedClassName() PHPCSUtils native improved version. @@ -897,7 +761,7 @@ public static function findExtendedClassName(File $phpcsFile, $stackPtr) * * Changelog for the PHPCS native function: * - Introduced in PHPCS 2.7.0. - * - The upstream method has received no significant updates since PHPCS 3.9.0. + * - The upstream method has received no significant updates since PHPCS 3.10.0. * * @see \PHP_CodeSniffer\Files\File::findImplementedInterfaceNames() Original source. * @see \PHPCSUtils\Utils\ObjectDeclarations::findImplementedInterfaceNames() PHPCSUtils native improved version. diff --git a/PHPCSUtils/BackCompat/BCTokens.php b/PHPCSUtils/BackCompat/BCTokens.php index dac8feb1..2d6a72fd 100644 --- a/PHPCSUtils/BackCompat/BCTokens.php +++ b/PHPCSUtils/BackCompat/BCTokens.php @@ -74,7 +74,7 @@ final class BCTokens /** * Handle calls to (undeclared) methods for token arrays which haven't received any - * changes since PHPCS 3.9.0. + * changes since PHPCS 3.10.0. * * @since 1.0.0 * diff --git a/PHPCSUtils/TestUtils/UtilityMethodTestCase.php b/PHPCSUtils/TestUtils/UtilityMethodTestCase.php index 55904857..8242002b 100644 --- a/PHPCSUtils/TestUtils/UtilityMethodTestCase.php +++ b/PHPCSUtils/TestUtils/UtilityMethodTestCase.php @@ -84,7 +84,7 @@ * * * * @return array * * / - * public function dataMyMethod() + * public static function dataMyMethod() * { * return array( * array('/* testTestCaseDescription * /', false), diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index a842c9ec..3a40fdcd 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -400,14 +400,16 @@ final class Collections * @var array */ private static $parameterTypeTokens = [ - \T_CALLABLE => \T_CALLABLE, - \T_SELF => \T_SELF, - \T_PARENT => \T_PARENT, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_NULL => \T_NULL, - \T_TYPE_UNION => \T_TYPE_UNION, - \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_CALLABLE => \T_CALLABLE, + \T_SELF => \T_SELF, + \T_PARENT => \T_PARENT, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_NULL => \T_NULL, + \T_TYPE_UNION => \T_TYPE_UNION, + \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS, + \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS, ]; /** @@ -446,14 +448,16 @@ final class Collections * @var array */ private static $propertyTypeTokens = [ - \T_CALLABLE => \T_CALLABLE, // Not allowed in PHP, but in this list to allow for (flagging) code errors. - \T_SELF => \T_SELF, - \T_PARENT => \T_PARENT, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_NULL => \T_NULL, - \T_TYPE_UNION => \T_TYPE_UNION, - \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_CALLABLE => \T_CALLABLE, // Not allowed in PHP, but in this list to allow for flagging code errors. + \T_SELF => \T_SELF, + \T_PARENT => \T_PARENT, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_NULL => \T_NULL, + \T_TYPE_UNION => \T_TYPE_UNION, + \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS, + \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS, ]; /** @@ -464,12 +468,14 @@ final class Collections * @var array */ private static $returnTypeTokens = [ - \T_CALLABLE => \T_CALLABLE, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_NULL => \T_NULL, - \T_TYPE_UNION => \T_TYPE_UNION, - \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_CALLABLE => \T_CALLABLE, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_NULL => \T_NULL, + \T_TYPE_UNION => \T_TYPE_UNION, + \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS, + \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS, ]; /** diff --git a/PHPCSUtils/Utils/Context.php b/PHPCSUtils/Utils/Context.php index c0f1f947..b488060c 100644 --- a/PHPCSUtils/Utils/Context.php +++ b/PHPCSUtils/Utils/Context.php @@ -209,7 +209,7 @@ public static function inForCondition(File $phpcsFile, $stackPtr) if ($tokens[$i]['level'] !== $level || \count($tokens[$i]['nested_parenthesis']) !== $parens ) { - // Disregard semi-colons at lower nesting/condition levels. + // Disregard semicolons at lower nesting/condition levels. continue; } diff --git a/PHPCSUtils/Utils/ControlStructures.php b/PHPCSUtils/Utils/ControlStructures.php index b343c736..02bbf02c 100644 --- a/PHPCSUtils/Utils/ControlStructures.php +++ b/PHPCSUtils/Utils/ControlStructures.php @@ -214,8 +214,6 @@ public static function isElseIf(File $phpcsFile, $stackPtr) * * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified `$stackPtr` is not of * type `T_CATCH` or doesn't exist. - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If no parenthesis opener or closer can be - * determined (parse error). */ public static function getCaughtExceptions(File $phpcsFile, $stackPtr) { @@ -228,7 +226,7 @@ public static function getCaughtExceptions(File $phpcsFile, $stackPtr) } if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) { - throw new RuntimeException('Parentheses opener/closer of the T_CATCH could not be determined'); + return []; } $opener = $tokens[$stackPtr]['parenthesis_opener']; diff --git a/PHPCSUtils/Utils/PassedParameters.php b/PHPCSUtils/Utils/PassedParameters.php index 00c6311d..0d65b8e4 100644 --- a/PHPCSUtils/Utils/PassedParameters.php +++ b/PHPCSUtils/Utils/PassedParameters.php @@ -49,12 +49,11 @@ final class PassedParameters * * - If passed a `T_STRING`, `T_NAME_FULLY_QUALIFIED`, `T_NAME_RELATIVE`, `T_NAME_QUALIFIED`, * or `T_VARIABLE` stack pointer, it will treat it as a function call. - * If a `T_STRING` or `T_VARIABLE` which is *not* a function call is passed, the behaviour is - * undetermined. + * If a token which is *not* a function call is passed, the behaviour is undetermined. * - If passed a `T_ANON_CLASS` stack pointer, it will accept it as a class instantiation. * - If passed a `T_SELF`, `T_STATIC` or `T_PARENT` stack pointer, it will accept it as a - * class instantiation function call when used like `new self()` (with or without parenthesis). - * When these hierarchiecal keywords are not preceded by the `new` keyword, parenthesis + * class instantiation function call when used like `new self()` (with or without parentheses). + * When these hierarchiecal keywords are not preceded by the `new` keyword, parentheses * will be required for the token to be accepted. * - If passed a `T_ARRAY` or `T_OPEN_SHORT_ARRAY` stack pointer, it will detect * whether the array has values or is empty. diff --git a/PHPCSUtils/Utils/UseStatements.php b/PHPCSUtils/Utils/UseStatements.php index 3fdf9d66..6e29f5f8 100644 --- a/PHPCSUtils/Utils/UseStatements.php +++ b/PHPCSUtils/Utils/UseStatements.php @@ -192,10 +192,6 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); - if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_USE) { - throw new RuntimeException('$stackPtr must be of type T_USE'); - } - if (self::isImportUse($phpcsFile, $stackPtr) === false) { throw new RuntimeException('$stackPtr must be an import use statement'); } @@ -301,9 +297,9 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr) case \T_COMMA: if ($name !== '') { if ($useGroup === true) { - $statements[$type][$alias] = $baseName . $name; + $statements[$type][$alias] = \ltrim($baseName, '\\') . $name; } else { - $statements[$type][$alias] = $name; + $statements[$type][$alias] = \ltrim($name, '\\'); } } diff --git a/README.md b/README.md index 60cfe41d..c971fe4e 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Whether you need to split an `array` into the individual items, are trying to de Includes improved versions of the PHPCS native utility functions and plenty of new utility functions. -These functions are compatible with PHPCS 3.9.0 up to PHPCS `master`. +These functions are compatible with PHPCS 3.10.0 up to PHPCS `master`. ### A collection of static properties and methods for often-used token groups @@ -66,7 +66,7 @@ Supports PHPUnit 4.x up to 9.x. Normally to use the latest version of PHP_CodeSniffer native utility functions, you would have to raise the minimum requirements of your external PHPCS standard. -Now you won't have to anymore. This package allows you to use the latest version of those utility functions in all PHP_CodeSniffer versions from PHPCS 3.9.0 and up. +Now you won't have to anymore. This package allows you to use the latest version of those utility functions in all PHP_CodeSniffer versions from PHPCS 3.10.0 and up. ### Fully documented @@ -78,7 +78,7 @@ To see detailed information about all the available abstract sniffs, utility fun ## Minimum Requirements * PHP 5.4 or higher. -* [PHP_CodeSniffer] 3.9.0+. +* [PHP_CodeSniffer] 3.10.0+. * Recommended PHP extensions for optimal functionality: - PCRE with Unicode support (normally enabled by default) diff --git a/Tests/AbstractSniffs/AbstractArrayDeclaration/AbstractArrayDeclarationSniffTest.php b/Tests/AbstractSniffs/AbstractArrayDeclaration/AbstractArrayDeclarationSniffTest.php index 6235f41c..d83bb5c9 100644 --- a/Tests/AbstractSniffs/AbstractArrayDeclaration/AbstractArrayDeclarationSniffTest.php +++ b/Tests/AbstractSniffs/AbstractArrayDeclaration/AbstractArrayDeclarationSniffTest.php @@ -159,11 +159,11 @@ public function testSingleLineShortArrayNoKeysNoTrailingComma() $mockObj->process(self::$phpcsFile, $target); // Verify that the properties have been correctly set. - $this->assertAttributeValueSame($target, 'stackPtr', $mockObj); - $this->assertAttributeValueSame($target, 'arrayOpener', $mockObj); - $this->assertAttributeValueSame(($target + 5), 'arrayCloser', $mockObj); - $this->assertAttributeValueSame(2, 'itemCount', $mockObj); - $this->assertAttributeValueSame(true, 'singleLine', $mockObj); + $this->assertPropertySame($target, 'stackPtr', $mockObj); + $this->assertPropertySame($target, 'arrayOpener', $mockObj); + $this->assertPropertySame(($target + 5), 'arrayCloser', $mockObj); + $this->assertPropertySame(2, 'itemCount', $mockObj); + $this->assertPropertySame(true, 'singleLine', $mockObj); } /** @@ -235,11 +235,11 @@ public function testMultiLineLongArrayKeysTrailingComma() $mockObj->process(self::$phpcsFile, $target); // Verify that the properties have been correctly set. - $this->assertAttributeValueSame($target, 'stackPtr', $mockObj); - $this->assertAttributeValueSame(($target + 1), 'arrayOpener', $mockObj); - $this->assertAttributeValueSame(($target + 35), 'arrayCloser', $mockObj); - $this->assertAttributeValueSame(4, 'itemCount', $mockObj); - $this->assertAttributeValueSame(false, 'singleLine', $mockObj); + $this->assertPropertySame($target, 'stackPtr', $mockObj); + $this->assertPropertySame(($target + 1), 'arrayOpener', $mockObj); + $this->assertPropertySame(($target + 35), 'arrayCloser', $mockObj); + $this->assertPropertySame(4, 'itemCount', $mockObj); + $this->assertPropertySame(false, 'singleLine', $mockObj); } /** @@ -318,11 +318,11 @@ public function testMultiLineShortArrayMixedKeysNoKeys() $mockObj->process(self::$phpcsFile, $target); // Verify that the properties have been correctly set. - $this->assertAttributeValueSame($target, 'stackPtr', $mockObj); - $this->assertAttributeValueSame($target, 'arrayOpener', $mockObj); - $this->assertAttributeValueSame(($target + 22), 'arrayCloser', $mockObj); - $this->assertAttributeValueSame(3, 'itemCount', $mockObj); - $this->assertAttributeValueSame(false, 'singleLine', $mockObj); + $this->assertPropertySame($target, 'stackPtr', $mockObj); + $this->assertPropertySame($target, 'arrayOpener', $mockObj); + $this->assertPropertySame(($target + 22), 'arrayCloser', $mockObj); + $this->assertPropertySame(3, 'itemCount', $mockObj); + $this->assertPropertySame(false, 'singleLine', $mockObj); } /** diff --git a/Tests/AssertAttributeSame.php b/Tests/AssertPropertySame.php similarity index 54% rename from Tests/AssertAttributeSame.php rename to Tests/AssertPropertySame.php index d8f7bfd2..a3ff44b5 100644 --- a/Tests/AssertAttributeSame.php +++ b/Tests/AssertPropertySame.php @@ -18,35 +18,35 @@ * PHPUnit cross-version compatibility helper. * * Backfills the PHPUnit native `assertAttributeSame()` method in PHPUnit 9.x and above in which - * the method was removed. Use `assertAttributeValueSame()` instead of `assertAttributeSame()` + * the method was removed. Use `assertPropertySame()` instead of `assertAttributeSame()` * for cross-version compatibility. * * @since 1.0.0 */ -trait AssertAttributeSame +trait AssertPropertySame { /** * PHPUnit cross-version helper method to test the value of class properties. * - * @param mixed $expected Expected property value. - * @param string $attributeName The name of the property to check. - * @param object $actualObject The object on which to check the property value. - * @param string $message Optional. Custom error message. + * @param mixed $expected Expected property value. + * @param string $propertyName The name of the property to check. + * @param object $actualObject The object on which to check the property value. + * @param string $message Optional. Custom error message. * * @return void */ - public function assertAttributeValueSame($expected, $attributeName, $actualObject, $message = '') + public function assertPropertySame($expected, $propertyName, $actualObject, $message = '') { // Will throw a warning on PHPUnit 8, but will still work. if (\method_exists($this, 'assertAttributeSame')) { - $this->assertAttributeSame($expected, $attributeName, $actualObject, $message); + $this->assertAttributeSame($expected, $propertyName, $actualObject, $message); return; } // PHPUnit 9.0+. try { - $actual = $this->getObjectAttributeValue($actualObject, $attributeName); + $actual = $this->getObjectPropertyValue($actualObject, $propertyName); } catch (Exception $e) { $this->fail($e->getMessage()); } @@ -55,31 +55,32 @@ public function assertAttributeValueSame($expected, $attributeName, $actualObjec } /** - * Retrieve the value of an object's attribute. - * This also works for attributes that are declared protected or private. + * Retrieve the value of an object property. + * + * This also works for properties that are declared protected or private. * * @param object|string $objectUnderTest The object or class on which to check the property value. - * @param string $attributeName The name of the property to check. + * @param string $propertyName The name of the property to check. * * @return mixed Property value. * * @throws \Exception */ - public static function getObjectAttributeValue($objectUnderTest, $attributeName) + public static function getObjectPropertyValue($objectUnderTest, $propertyName) { $reflector = new ReflectionObject($objectUnderTest); do { try { - $attribute = $reflector->getProperty($attributeName); + $property = $reflector->getProperty($propertyName); - if (!$attribute || $attribute->isPublic()) { - return $objectUnderTest->$attributeName; + if (!$property || $property->isPublic()) { + return $objectUnderTest->$propertyName; } - $attribute->setAccessible(true); - $value = $attribute->getValue($objectUnderTest); - $attribute->setAccessible(false); + $property->setAccessible(true); + $value = $property->getValue($objectUnderTest); + $property->setAccessible(false); return $value; } catch (ReflectionException $e) { @@ -88,8 +89,8 @@ public static function getObjectAttributeValue($objectUnderTest, $attributeName) throw new Exception( \sprintf( - 'Attribute "%s" not found in object.', - $attributeName + 'Property "%s" not found in object.', + $propertyName ) ); } diff --git a/Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc b/Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc index e5b6d306..058ba484 100644 --- a/Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc +++ b/Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc @@ -339,3 +339,18 @@ class WhitespaceAndCommentsInTypes { /* testIntersectionTypeWithWhitespaceAndComment */ public \Foo /*comment*/ & Bar $hasWhitespaceAndComment; } + +trait DNFTypes { + /* testPHP82DNFTypeStatic */ + public static (Foo&\Bar)|bool $propA; + + /* testPHP82DNFTypeReadonlyA */ + protected readonly float|(Partially\Qualified&Traversable) $propB; + + /* testPHP82DNFTypeReadonlyB */ + private readonly (namespace\Foo&Bar)|string $propC; + + /* testPHP82DNFTypeIllegalNullable */ + // Intentional fatal error - nullable operator cannot be combined with DNF. + var ?(A&\Pck\B)|bool $propD; +} diff --git a/Tests/BackCompat/BCFile/GetMemberPropertiesTest.php b/Tests/BackCompat/BCFile/GetMemberPropertiesTest.php index a1c397da..60ee29db 100644 --- a/Tests/BackCompat/BCFile/GetMemberPropertiesTest.php +++ b/Tests/BackCompat/BCFile/GetMemberPropertiesTest.php @@ -1101,6 +1101,59 @@ public static function dataGetMemberProperties() 'nullable_type' => false, ], ], + + 'php8.2-dnf-with-static' => [ + 'identifier' => '/* testPHP82DNFTypeStatic */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '(Foo&\Bar)|bool', + 'type_token' => ($php8Names === true) ? -8 : -9, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.2-dnf-with-readonly-1' => [ + 'identifier' => '/* testPHP82DNFTypeReadonlyA */', + 'expected' => [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => 'float|(Partially\Qualified&Traversable)', + 'type_token' => ($php8Names === true) ? -8 : -10, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.2-dnf-with-readonly-2' => [ + 'identifier' => '/* testPHP82DNFTypeReadonlyB */', + 'expected' => [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => '(namespace\Foo&Bar)|string', + 'type_token' => ($php8Names === true) ? -8 : -10, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.2-dnf-with-illegal-nullable' => [ + 'identifier' => '/* testPHP82DNFTypeIllegalNullable */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?(A&\Pck\B)|bool', + 'type_token' => ($php8Names === true) ? -8 : -11, + 'type_end_token' => -2, + 'nullable_type' => true, + ], + ], ]; } diff --git a/Tests/BackCompat/BCFile/GetMethodParametersTest.inc b/Tests/BackCompat/BCFile/GetMethodParametersTest.inc index 17c8417e..b504f928 100644 --- a/Tests/BackCompat/BCFile/GetMethodParametersTest.inc +++ b/Tests/BackCompat/BCFile/GetMethodParametersTest.inc @@ -280,6 +280,23 @@ function newInInitializers( \Package\TypeB $newToo = new \Package\TypeB(10, 'string'), ) {} +/* testPHP82DNFTypes */ +function dnfTypes( + #[MyAttribute] + false|(Foo&Bar)|true $obj1, + (\Boo&\Pck\Bar)|(Boo&Baz) $obj2 = new Boo() +) {} + +/* testPHP82DNFTypesWithSpreadOperatorAndReference */ +function dnfInGlobalFunctionWithSpreadAndReference((Countable&MeMe)|iterable &$paramA, true|(Foo&Bar) ...$paramB) {} + +/* testPHP82DNFTypesIllegalNullable */ +// Intentional fatal error - nullable operator cannot be combined with DNF. +$dnf_closure = function (? ( MyClassA & /*comment*/ \Package\MyClassB & \Package\MyClassC ) $var): void {}; + +/* testPHP82DNFTypesInArrow */ +$dnf_arrow = fn((Hi&Ho)|FALSE &...$range): string => $a; + /* testFunctionCallFnPHPCS353-354 */ $value = $obj->fn(true); diff --git a/Tests/BackCompat/BCFile/GetMethodParametersTest.php b/Tests/BackCompat/BCFile/GetMethodParametersTest.php index 38e5817c..d473653d 100644 --- a/Tests/BackCompat/BCFile/GetMethodParametersTest.php +++ b/Tests/BackCompat/BCFile/GetMethodParametersTest.php @@ -2601,6 +2601,157 @@ public function testPHP81NewInInitializers() $this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected); } + /** + * Verify recognition of 8.2 DNF parameter type declarations. + * + * @return void + */ + public function testPHP82DNFTypes() + { + $php8Names = parent::usesPhp8NameTokens(); + + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 21, + 'name' => '$obj1', + 'content' => '#[MyAttribute] + false|(Foo&Bar)|true $obj1', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'false|(Foo&Bar)|true', + 'type_hint_token' => 11, + 'type_hint_end_token' => 19, + 'nullable_type' => false, + 'comma_token' => 22, + ]; + $expected[1] = [ + 'token' => ($php8Names === true) ? 37 : 41, + 'name' => '$obj2', + 'content' => '(\Boo&\Pck\Bar)|(Boo&Baz) $obj2 = new Boo()', + 'default' => 'new Boo()', + 'default_token' => ($php8Names === true) ? 41 : 45, + 'default_equal_token' => ($php8Names === true) ? 39 : 43, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '(\Boo&\Pck\Bar)|(Boo&Baz)', + 'type_hint_token' => 25, + 'type_hint_end_token' => ($php8Names === true) ? 35 : 39, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF parameter type declarations when the variable + * has either a spread operator or a reference. + * + * @return void + */ + public function testPHP82DNFTypesWithSpreadOperatorAndReference() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 13, + 'name' => '$paramA', + 'content' => '(Countable&MeMe)|iterable &$paramA', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 12, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '(Countable&MeMe)|iterable', + 'type_hint_token' => 4, + 'type_hint_end_token' => 10, + 'nullable_type' => false, + 'comma_token' => 14, + ]; + $expected[1] = [ + 'token' => 25, + 'name' => '$paramB', + 'content' => 'true|(Foo&Bar) ...$paramB', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 24, + 'type_hint' => 'true|(Foo&Bar)', + 'type_hint_token' => 16, + 'type_hint_end_token' => 22, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF parameter type declarations using the nullability operator (not allowed). + * + * @return void + */ + public function testPHP82DNFTypesIllegalNullable() + { + $php8Names = parent::usesPhp8NameTokens(); + + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => ($php8Names === true) ? 21 : 27, + 'name' => '$var', + 'content' => '? ( MyClassA & /*comment*/ \Package\MyClassB & \Package\MyClassC ) $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?(MyClassA&\Package\MyClassB&\Package\MyClassC)', + 'type_hint_token' => 5, + 'type_hint_end_token' => ($php8Names === true) ? 19 : 25, + 'nullable_type' => true, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF parameter type declarations in an arrow function. + * + * @return void + */ + public function testPHP82DNFTypesInArrow() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 12, + 'name' => '$range', + 'content' => '(Hi&Ho)|FALSE &...$range', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 10, + 'variable_length' => true, + 'variadic_token' => 11, + 'type_hint' => '(Hi&Ho)|FALSE', + 'type_hint_token' => 2, + 'type_hint_end_token' => 8, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + /** * Verify handling of a closure. * diff --git a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc index 736a4ee0..cc0322f2 100644 --- a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc +++ b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc @@ -165,6 +165,25 @@ function pseudoTypeTrue(): ?true {} // Intentional fatal error - Type contains both true and false, bool should be used instead, but that's not the concern of the method. function pseudoTypeFalseAndTrue(): true|false {} +/* testPHP82DNFType */ +function hasDNFType() : bool|(Foo&Bar)|string {} + +abstract class AbstractClass { + /* testPHP82DNFTypeAbstractMethod */ + abstract protected function abstractMethodDNFType() : float|(Foo&Bar); +} + +/* testPHP82DNFTypeIllegalNullable */ +// Intentional fatal error - nullable operator cannot be combined with DNF. +function illegalNullableDNF(): ?(A&\Pck\B)|bool {} + +/* testPHP82DNFTypeClosure */ +$closure = function() : object|(namespace\Foo&Countable) {}; + +/* testPHP82DNFTypeFn */ +// Intentional fatal error - void type cannot be combined with DNF. +$arrow = fn() : null|(Partially\Qualified&Traversable)|void => do_something(); + /* testNotAFunction */ return true; diff --git a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php index 15c7dfcc..6d698243 100644 --- a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php +++ b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php @@ -1096,6 +1096,132 @@ public function testPHP82PseudoTypeFalseAndTrue() $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); } + /** + * Verify recognition of PHP 8.2 DNF return type declaration. + * + * @return void + */ + public function testPHP82DNFType() + { + // Offsets are relative to the T_FUNCTION token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'bool|(Foo&Bar)|string', + 'return_type_token' => 8, + 'return_type_end_token' => 16, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF return type declaration on an abstract method. + * + * @return void + */ + public function testPHP82DNFTypeAbstractMethod() + { + // Offsets are relative to the T_FUNCTION token. + $expected = [ + 'scope' => 'protected', + 'scope_specified' => true, + 'return_type' => 'float|(Foo&Bar)', + 'return_type_token' => 8, + 'return_type_end_token' => 14, + 'nullable_return_type' => false, + 'is_abstract' => true, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF return type declaration with illegal nullability. + * + * @return void + */ + public function testPHP82DNFTypeIllegalNullable() + { + $php8Names = parent::usesPhp8NameTokens(); + + // Offsets are relative to the T_FUNCTION token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?(A&\Pck\B)|bool', + 'return_type_token' => 8, + 'return_type_end_token' => ($php8Names === true) ? 14 : 17, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF return type declaration on a closure. + * + * @return void + */ + public function testPHP82DNFTypeClosure() + { + $php8Names = parent::usesPhp8NameTokens(); + + // Offsets are relative to the T_CLOSURE token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'object|(namespace\Foo&Countable)', + 'return_type_token' => 6, + 'return_type_end_token' => ($php8Names === true) ? 12 : 14, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Verify recognition of PHP 8.2 DNF return type declaration on an arrow function. + * + * @return void + */ + public function testPHP82DNFTypeFn() + { + $php8Names = parent::usesPhp8NameTokens(); + + // Offsets are relative to the T_FN token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'null|(Partially\Qualified&Traversable)|void', + 'return_type_token' => 6, + 'return_type_end_token' => ($php8Names === true) ? 14 : 16, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + /** * Test for incorrect tokenization of array return type declarations in PHPCS < 2.8.0. * diff --git a/Tests/BackCompat/BCFile/IsReferenceTest.inc b/Tests/BackCompat/BCFile/IsReferenceTest.inc index d371d6ef..05af8390 100644 --- a/Tests/BackCompat/BCFile/IsReferenceTest.inc +++ b/Tests/BackCompat/BCFile/IsReferenceTest.inc @@ -201,6 +201,12 @@ $closure = function &($param) use ($value) {}; /* testBitwiseAndArrowFunctionInDefault */ $fn = fn( $one = E_NOTICE & E_STRICT) => 1; +/* testIntersectionIsNotReference */ +function intersect(Foo&Bar $param) {} + +/* testDNFTypeIsNotReference */ +$fn = fn((Foo&\Bar)|null /* testParamPassByReference */ &$param) => $param; + /* testTokenizerIssue1284PHPCSlt280A */ if ($foo) {} [&$a, /* testTokenizerIssue1284PHPCSlt280B */ &$b] = $c; diff --git a/Tests/BackCompat/BCFile/IsReferenceTest.php b/Tests/BackCompat/BCFile/IsReferenceTest.php index 2054a1e4..ef92b927 100644 --- a/Tests/BackCompat/BCFile/IsReferenceTest.php +++ b/Tests/BackCompat/BCFile/IsReferenceTest.php @@ -49,16 +49,47 @@ class IsReferenceTest extends UtilityMethodTestCase /** * Test that false is returned when a non-"bitwise and" token is passed. * + * @param string $testMarker Comment which precedes the test case. + * @param array $targetTokens Type of tokens to look for. + * + * @dataProvider dataNotBitwiseAndToken + * * @return void */ - public function testNotBitwiseAndToken() + public function testNotBitwiseAndToken($testMarker, $targetTokens) { - $testClass = static::TEST_CLASS; + $testClass = static::TEST_CLASS; + $targetTokens[] = T_BITWISE_AND; - $target = $this->getTargetToken('/* testBitwiseAndA */', T_STRING); + $target = $this->getTargetToken($testMarker, $targetTokens); $this->assertFalse($testClass::isReference(self::$phpcsFile, $target)); } + /** + * Data provider. + * + * @see testNotBitwiseAndToken() + * + * @return array>> + */ + public static function dataNotBitwiseAndToken() + { + return [ + 'Not ampersand token at all' => [ + 'testMarker' => '/* testBitwiseAndA */', + 'targetTokens' => [T_STRING], + ], + 'ampersand in intersection type' => [ + 'testMarker' => '/* testIntersectionIsNotReference */', + 'targetTokens' => [T_TYPE_INTERSECTION], + ], + 'ampersand in DNF type' => [ + 'testMarker' => '/* testDNFTypeIsNotReference */', + 'targetTokens' => [T_TYPE_INTERSECTION], + ], + ]; + } + /** * Test correctly identifying whether a "bitwise and" token is a reference or not. * @@ -364,6 +395,10 @@ public static function dataIsReference() 'testMarker' => '/* testBitwiseAndArrowFunctionInDefault */', 'expected' => false, ], + 'reference: param pass by ref in arrow function' => [ + 'testMarker' => '/* testParamPassByReference */', + 'expected' => true, + ], 'issue-1284-short-list-directly-after-close-curly-control-structure' => [ 'testMarker' => '/* testTokenizerIssue1284PHPCSlt280A */', 'expected' => true, diff --git a/Tests/BackCompat/Helper/GetVersionTest.php b/Tests/BackCompat/Helper/GetVersionTest.php index 25d7b402..3e21a0d3 100644 --- a/Tests/BackCompat/Helper/GetVersionTest.php +++ b/Tests/BackCompat/Helper/GetVersionTest.php @@ -47,7 +47,7 @@ public function testGetVersion() } if ($expected === 'lowest') { - $expected = '3.9.0'; + $expected = '3.10.0'; } $result = Helper::getVersion(); diff --git a/Tests/PolyfilledTestCase.php b/Tests/PolyfilledTestCase.php index 4cce3293..7bb6b70a 100644 --- a/Tests/PolyfilledTestCase.php +++ b/Tests/PolyfilledTestCase.php @@ -13,7 +13,7 @@ namespace PHPCSUtils\Tests; -use PHPCSUtils\Tests\AssertAttributeSame; +use PHPCSUtils\Tests\AssertPropertySame; use PHPCSUtils\Tests\ExpectWithConsecutiveArgs; use PHPCSUtils\TestUtils\UtilityMethodTestCase; use Yoast\PHPUnitPolyfills\Autoload; @@ -54,7 +54,7 @@ abstract class PolyfilledTestCase extends UtilityMethodTestCase { // PHPCSUtils native helpers. - use AssertAttributeSame; + use AssertPropertySame; use ExpectWithConsecutiveArgs; // PHPUnit Polyfills. @@ -91,7 +91,7 @@ abstract class PolyfilledTestCase extends UtilityMethodTestCase abstract class PolyfilledTestCase extends UtilityMethodTestCase { // PHPCSUtils native helper. - use AssertAttributeSame; + use AssertPropertySame; use ExpectWithConsecutiveArgs; // PHPUnit Polyfills. diff --git a/Tests/TestUtils/UtilityMethodTestCase/SkipJSCSSTestsOnPHPCS4Test.php b/Tests/TestUtils/UtilityMethodTestCase/SkipJSCSSTestsOnPHPCS4Test.php index 0b1096fd..6d71e6b4 100644 --- a/Tests/TestUtils/UtilityMethodTestCase/SkipJSCSSTestsOnPHPCS4Test.php +++ b/Tests/TestUtils/UtilityMethodTestCase/SkipJSCSSTestsOnPHPCS4Test.php @@ -51,7 +51,11 @@ public function testSkipJsCss() if (\version_compare(parent::$phpcsVersion, '3.99.99', '>') === true) { $msg = 'JS and CSS support has been removed in PHPCS 4.'; $exception = 'PHPUnit\Framework\SkippedTestError'; - if (\class_exists('PHPUnit_Framework_SkippedTestError')) { + + if (\class_exists('PHPUnit\Framework\SkippedWithMessageException')) { + // PHPUnit 10+. + $exception = 'PHPUnit\Framework\SkippedWithMessageException'; + } elseif (\class_exists('PHPUnit_Framework_SkippedTestError')) { // PHPUnit < 6. $exception = 'PHPUnit_Framework_SkippedTestError'; } diff --git a/Tests/Tokens/Collections/ParameterTypeTokensTest.php b/Tests/Tokens/Collections/ParameterTypeTokensTest.php index 3f4bd3a3..d5a31389 100644 --- a/Tests/Tokens/Collections/ParameterTypeTokensTest.php +++ b/Tests/Tokens/Collections/ParameterTypeTokensTest.php @@ -31,20 +31,22 @@ final class ParameterTypeTokensTest extends TestCase public function testParameterTypeTokens() { $expected = [ - \T_CALLABLE => \T_CALLABLE, - \T_SELF => \T_SELF, - \T_PARENT => \T_PARENT, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_NULL => \T_NULL, - \T_TYPE_UNION => \T_TYPE_UNION, - \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, - \T_NS_SEPARATOR => \T_NS_SEPARATOR, - \T_NAMESPACE => \T_NAMESPACE, - \T_STRING => \T_STRING, - \T_NAME_QUALIFIED => \T_NAME_QUALIFIED, - \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, - \T_NAME_RELATIVE => \T_NAME_RELATIVE, + \T_CALLABLE => \T_CALLABLE, + \T_SELF => \T_SELF, + \T_PARENT => \T_PARENT, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_NULL => \T_NULL, + \T_TYPE_UNION => \T_TYPE_UNION, + \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS, + \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS, + \T_NS_SEPARATOR => \T_NS_SEPARATOR, + \T_NAMESPACE => \T_NAMESPACE, + \T_STRING => \T_STRING, + \T_NAME_QUALIFIED => \T_NAME_QUALIFIED, + \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, + \T_NAME_RELATIVE => \T_NAME_RELATIVE, ]; $this->assertSame($expected, Collections::parameterTypeTokens()); diff --git a/Tests/Tokens/Collections/PropertyTypeTokensTest.php b/Tests/Tokens/Collections/PropertyTypeTokensTest.php index 65278cba..bb6eb396 100644 --- a/Tests/Tokens/Collections/PropertyTypeTokensTest.php +++ b/Tests/Tokens/Collections/PropertyTypeTokensTest.php @@ -31,20 +31,22 @@ final class PropertyTypeTokensTest extends TestCase public function testPropertyTypeTokens() { $expected = [ - \T_CALLABLE => \T_CALLABLE, - \T_SELF => \T_SELF, - \T_PARENT => \T_PARENT, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_NULL => \T_NULL, - \T_TYPE_UNION => \T_TYPE_UNION, - \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, - \T_NS_SEPARATOR => \T_NS_SEPARATOR, - \T_NAMESPACE => \T_NAMESPACE, - \T_STRING => \T_STRING, - \T_NAME_QUALIFIED => \T_NAME_QUALIFIED, - \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, - \T_NAME_RELATIVE => \T_NAME_RELATIVE, + \T_CALLABLE => \T_CALLABLE, + \T_SELF => \T_SELF, + \T_PARENT => \T_PARENT, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_NULL => \T_NULL, + \T_TYPE_UNION => \T_TYPE_UNION, + \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS, + \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS, + \T_NS_SEPARATOR => \T_NS_SEPARATOR, + \T_NAMESPACE => \T_NAMESPACE, + \T_STRING => \T_STRING, + \T_NAME_QUALIFIED => \T_NAME_QUALIFIED, + \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, + \T_NAME_RELATIVE => \T_NAME_RELATIVE, ]; $this->assertSame($expected, Collections::propertyTypeTokens()); diff --git a/Tests/Tokens/Collections/ReturnTypeTokensTest.php b/Tests/Tokens/Collections/ReturnTypeTokensTest.php index f53d9252..9357ac1c 100644 --- a/Tests/Tokens/Collections/ReturnTypeTokensTest.php +++ b/Tests/Tokens/Collections/ReturnTypeTokensTest.php @@ -31,21 +31,23 @@ final class ReturnTypeTokensTest extends TestCase public function testReturnTypeTokens() { $expected = [ - \T_CALLABLE => \T_CALLABLE, - \T_FALSE => \T_FALSE, - \T_TRUE => \T_TRUE, - \T_NULL => \T_NULL, - \T_TYPE_UNION => \T_TYPE_UNION, - \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, - \T_PARENT => \T_PARENT, - \T_SELF => \T_SELF, - \T_STATIC => \T_STATIC, - \T_NS_SEPARATOR => \T_NS_SEPARATOR, - \T_NAMESPACE => \T_NAMESPACE, - \T_STRING => \T_STRING, - \T_NAME_QUALIFIED => \T_NAME_QUALIFIED, - \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, - \T_NAME_RELATIVE => \T_NAME_RELATIVE, + \T_CALLABLE => \T_CALLABLE, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_NULL => \T_NULL, + \T_TYPE_UNION => \T_TYPE_UNION, + \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION, + \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS, + \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS, + \T_PARENT => \T_PARENT, + \T_SELF => \T_SELF, + \T_STATIC => \T_STATIC, + \T_NS_SEPARATOR => \T_NS_SEPARATOR, + \T_NAMESPACE => \T_NAMESPACE, + \T_STRING => \T_STRING, + \T_NAME_QUALIFIED => \T_NAME_QUALIFIED, + \T_NAME_FULLY_QUALIFIED => \T_NAME_FULLY_QUALIFIED, + \T_NAME_RELATIVE => \T_NAME_RELATIVE, ]; $this->assertSame($expected, Collections::returnTypeTokens()); diff --git a/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php b/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php index c2aefe3a..0377450f 100644 --- a/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php +++ b/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php @@ -47,19 +47,6 @@ public function testNotCatch() ControlStructures::getCaughtExceptions(self::$phpcsFile, $target); } - /** - * Test receiving an expected exception when a parse error is encountered. - * - * @return void - */ - public function testParseError() - { - $this->expectPhpcsException('Parentheses opener/closer of the T_CATCH could not be determined'); - - $target = $this->getTargetToken('/* testLiveCoding */', \T_CATCH); - ControlStructures::getCaughtExceptions(self::$phpcsFile, $target); - } - /** * Test retrieving the exceptions caught in a `catch` condition. * @@ -220,6 +207,10 @@ public static function dataGetCaughtExceptions() 'testMarker' => '/* testMultiMissingExceptionNames */', 'expected' => [], ], + 'live coding / parse error' => [ + 'testMarker' => '/* testLiveCoding */', + 'expected' => [], + ], ]; } } diff --git a/Tests/Utils/Lists/GetAssignmentsTest.php b/Tests/Utils/Lists/GetAssignmentsTest.php index 33b8882d..51ceb068 100644 --- a/Tests/Utils/Lists/GetAssignmentsTest.php +++ b/Tests/Utils/Lists/GetAssignmentsTest.php @@ -42,8 +42,8 @@ public function testNonExistentToken() * * @dataProvider dataNotListToken * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string|array $targetToken The token type(s) to look for. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int|string $targetToken The token type(s) to look for. * * @return void */ @@ -60,7 +60,7 @@ public function testNotListToken($testMarker, $targetToken) * * @see testNotListToken() For the array format. * - * @return array + * @return array> */ public static function dataNotListToken() { @@ -81,9 +81,10 @@ public static function dataNotListToken() * * @dataProvider dataGetAssignments * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string|array $targetToken The token type(s) to look for. - * @param array|false $expected The expected function return value. + * @param string $testMarker The comment which prefaces the target token + * in the test file. + * @param int|string $targetToken The token type(s) to look for. + * @param array> $expected The expected function return value. * * @return void */ @@ -124,7 +125,7 @@ public function testGetAssignments($testMarker, $targetToken, $expected) * * @see testGetAssignments() For the array format. * - * @return array + * @return array>>> */ public static function dataGetAssignments() { diff --git a/Tests/Utils/MessageHelper/AddMessageTest.php b/Tests/Utils/MessageHelper/AddMessageTest.php index d9a4d402..5b2d59ac 100644 --- a/Tests/Utils/MessageHelper/AddMessageTest.php +++ b/Tests/Utils/MessageHelper/AddMessageTest.php @@ -49,9 +49,9 @@ final class AddMessageTest extends UtilityMethodTestCase * @dataProvider dataAddMessage * @covers ::addMessage * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param bool $isError Whether to test adding an error or a warning. - * @param array $expected Expected error details. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param bool $isError Whether to test adding an error or a warning. + * @param array $expected Expected error details. * * @return void */ @@ -82,7 +82,7 @@ public function testAddMessage($testMarker, $isError, $expected) * * @see testAddMessage() For the array format. * - * @return array + * @return array>> */ public static function dataAddMessage() { @@ -114,9 +114,9 @@ public static function dataAddMessage() * @dataProvider dataAddFixableMessage * @covers ::addFixableMessage * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param bool $isError Whether to test adding an error or a warning. - * @param array $expected Expected error details. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param bool $isError Whether to test adding an error or a warning. + * @param array $expected Expected error details. * * @return void */ @@ -148,7 +148,7 @@ public function testAddFixableMessage($testMarker, $isError, $expected) * * @see testAddFixableMessage() For the array format. * - * @return array + * @return array>> */ public static function dataAddFixableMessage() { @@ -177,9 +177,9 @@ public static function dataAddFixableMessage() /** * Helper method to verify the expected message details. * - * @param int $stackPtr The stack pointer on which the error/warning is expected. - * @param bool $isError Whether to test adding an error or a warning. - * @param array $expected Expected error details. + * @param int $stackPtr The stack pointer on which the error/warning is expected. + * @param bool $isError Whether to test adding an error or a warning. + * @param array $expected Expected error details. * * @return void */ diff --git a/Tests/Utils/Parentheses/ParenthesesTest.php b/Tests/Utils/Parentheses/ParenthesesTest.php index 46efa8fa..57e85a69 100644 --- a/Tests/Utils/Parentheses/ParenthesesTest.php +++ b/Tests/Utils/Parentheses/ParenthesesTest.php @@ -425,8 +425,8 @@ public function testFunctionNamedFnKeywordNotParenthesesOwner() * * @dataProvider dataWalkParentheses * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Expected function output for the various functions. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Expected function output for the various functions. * * @return void */ @@ -456,8 +456,8 @@ public function testGetFirstOpener($testName, $expectedResults) * * @dataProvider dataWalkParentheses * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Expected function output for the various functions. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Expected function output for the various functions. * * @return void */ @@ -487,8 +487,8 @@ public function testGetFirstCloser($testName, $expectedResults) * * @dataProvider dataWalkParentheses * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Expected function output for the various functions. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Expected function output for the various functions. * * @return void */ @@ -518,8 +518,8 @@ public function testGetFirstOwner($testName, $expectedResults) * * @dataProvider dataWalkParentheses * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Expected function output for the various functions. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Expected function output for the various functions. * * @return void */ @@ -549,8 +549,8 @@ public function testGetLastOpener($testName, $expectedResults) * * @dataProvider dataWalkParentheses * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Expected function output for the various functions. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Expected function output for the various functions. * * @return void */ @@ -580,8 +580,8 @@ public function testGetLastCloser($testName, $expectedResults) * * @dataProvider dataWalkParentheses * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Expected function output for the various functions. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Expected function output for the various functions. * * @return void */ @@ -616,7 +616,7 @@ public function testGetLastOwner($testName, $expectedResults) * @see testGetLastCloser() For the array format. * @see testGetLastOwner() For the array format. * - * @return array + * @return array>> */ public static function dataWalkParentheses() { @@ -937,9 +937,9 @@ public static function dataWalkParentheses() * * @dataProvider dataHasOwner * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $expectedResults Array with the owner token type to search for as key - * and the expected result as a value. + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $expectedResults Array with the owner token type to search for as key + * and the expected result as a value. * * @return void */ @@ -969,7 +969,7 @@ public function testHasOwner($testName, $expectedResults) * * @see testHasOwner() For the array format. * - * @return array + * @return array>> */ public static function dataHasOwner() { @@ -1191,9 +1191,9 @@ public function testHasOwnerMultipleTypes() * * @dataProvider dataFirstOwnerIn * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $validOwners Valid owners to test against. - * @param int|false $expected Expected function output + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $validOwners Valid owners to test against. + * @param int|false $expected Expected function output * * @return void */ @@ -1213,7 +1213,7 @@ public function testFirstOwnerIn($testName, $validOwners, $expected) * * @see testFirstOwnerIn() For the array format. * - * @return array + * @return array|false>> */ public static function dataFirstOwnerIn() { @@ -1282,9 +1282,9 @@ public static function dataFirstOwnerIn() * * @dataProvider dataLastOwnerIn * - * @param string $testName The name of this test as set in the cached $testTokens array. - * @param array $validOwners Valid owners to test against. - * @param int|false $expected Expected function output + * @param string $testName The name of this test as set in the cached $testTokens array. + * @param array $validOwners Valid owners to test against. + * @param int|false $expected Expected function output * * @return void */ @@ -1304,7 +1304,7 @@ public function testLastOwnerIn($testName, $validOwners, $expected) * * @see testLastOwnerIn() For the array format. * - * @return array + * @return array|false>> */ public static function dataLastOwnerIn() { diff --git a/Tests/Utils/PassedParameters/GetParameterCountTest.inc b/Tests/Utils/PassedParameters/GetParameterCountTest.inc index 490a579e..f69def98 100644 --- a/Tests/Utils/PassedParameters/GetParameterCountTest.inc +++ b/Tests/Utils/PassedParameters/GetParameterCountTest.inc @@ -219,6 +219,14 @@ $anon = new class( $param1, $param2 ) { public function __construct($param1, $param2) {} }; +/* testPHP80ClassInstantiationInAttribute */ +#[MyAttribute(1, self::Foo, 'string')] +function foo() {} + +/* testPHP80ClassInstantiationInMultiAttribute */ +#[AttributeOne, \AttributeTwo(1, self::Foo)] +function bar() {} + /* testArrayWithEmptyItem */ // Intentional parse error. $bar = [0 => $a,, 2 => $b]; diff --git a/Tests/Utils/PassedParameters/GetParameterCountTest.php b/Tests/Utils/PassedParameters/GetParameterCountTest.php index 8800c34e..5ee5e8db 100644 --- a/Tests/Utils/PassedParameters/GetParameterCountTest.php +++ b/Tests/Utils/PassedParameters/GetParameterCountTest.php @@ -353,6 +353,21 @@ public static function dataGetParameterCount() 'expected' => 2, ], + 'class-instantiation-in-attribute' => [ + 'testMarker' => '/* testPHP80ClassInstantiationInAttribute */', + 'expected' => 3, + ], + 'class-instantiation-in-attribute-no-params' => [ + 'testMarker' => '/* testPHP80ClassInstantiationInMultiAttribute */', + 'expected' => 0, + 'targetContent' => 'AttributeOne', + ], + 'class-instantiation-in-attribute-with-params' => [ + 'testMarker' => '/* testPHP80ClassInstantiationInMultiAttribute */', + 'expected' => 2, + 'targetContent' => ($php8Names === true) ? '\AttributeTwo' : 'AttributeTwo', + ], + 'array-with-empty-item' => [ 'testMarker' => '/* testArrayWithEmptyItem */', 'expected' => 3, diff --git a/Tests/Utils/PassedParameters/GetParameterFromStackTest.php b/Tests/Utils/PassedParameters/GetParameterFromStackTest.php index b146603c..8b8e50da 100644 --- a/Tests/Utils/PassedParameters/GetParameterFromStackTest.php +++ b/Tests/Utils/PassedParameters/GetParameterFromStackTest.php @@ -202,10 +202,11 @@ public static function dataGetParameterFunctionCallWithParamName() * * @dataProvider dataGetParameterFromStack * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param array $expectedName The expected result array for the $name parameter. - * @param array|false $expectedExpires The expected result array for the $expires_or_options parameter. - * @param array|false $expectedHttpOnly The expected result array for the $httponly parameter. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param array|false $expectedName The expected result array for the $name parameter. + * @param array|false $expectedExpires The expected result array for the $expires_or_options + * parameter. + * @param array|false $expectedHttpOnly The expected result array for the $httponly parameter. * * @return void */ @@ -280,7 +281,7 @@ public function testGetParameterFromStack($testMarker, $expectedName, $expectedE * * @see testGetParameterFromStack() For the array format. * - * @return array + * @return array|false>> */ public static function dataGetParameterFromStack() { @@ -413,9 +414,9 @@ public static function dataGetParameterFromStack() * * @dataProvider dataGetParameterFromStackNamedAfterVariadic * - * @param int $offset The positional offfset to pass. - * @param array $names The parameter names to pass. - * @param array|false $expected The expected result array for the parameter. + * @param int $offset The positional offfset to pass. + * @param array $names The parameter names to pass. + * @param array|false $expected The expected result array for the parameter. * * @return void */ @@ -446,7 +447,7 @@ public function testGetParameterFromStackNamedAfterVariadic($offset, $names, $ex * * @see testGetParameterFromStackNamedAfterVariadic() For the array format. * - * @return array + * @return array|array|false>> */ public static function dataGetParameterFromStackNamedAfterVariadic() { diff --git a/Tests/Utils/PassedParameters/GetParametersNamedTest.php b/Tests/Utils/PassedParameters/GetParametersNamedTest.php index 2ea5be1c..78916dd8 100644 --- a/Tests/Utils/PassedParameters/GetParametersNamedTest.php +++ b/Tests/Utils/PassedParameters/GetParametersNamedTest.php @@ -33,10 +33,11 @@ final class GetParametersNamedTest extends UtilityMethodTestCase * * @dataProvider dataGetParameters * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string $targetType The type of token to look for. - * @param array $expected The expected parameter array. - * @param mixed $targetContent Optional. The token content to look for. + * @param string $testMarker The comment which prefaces the target token + * in the test file. + * @param int|string $targetType The type of token to look for. + * @param array> $expected The expected parameter array. + * @param string|null $targetContent Optional. The token content to look for. * * @return void */ @@ -73,7 +74,7 @@ public function testGetParameters($testMarker, $targetType, $expected, $targetCo * * @see testGetParameters() For the array format. * - * @return array + * @return array>|null>> */ public static function dataGetParameters() { diff --git a/Tests/Utils/PassedParameters/GetParametersSkipShortArrayCheckTest.php b/Tests/Utils/PassedParameters/GetParametersSkipShortArrayCheckTest.php index b3ae65ac..39ab014e 100644 --- a/Tests/Utils/PassedParameters/GetParametersSkipShortArrayCheckTest.php +++ b/Tests/Utils/PassedParameters/GetParametersSkipShortArrayCheckTest.php @@ -10,7 +10,6 @@ namespace PHPCSUtils\Tests\Utils\PassedParameters; -use PHPCSUtils\BackCompat\Helper; use PHPCSUtils\Tests\PolyfilledTestCase; use PHPCSUtils\Utils\PassedParameters; @@ -61,7 +60,7 @@ public function testHasParametersDontSkipShortArrayCheck($testMarker, $targetTyp * * @see testHasParametersDontSkipShortArrayCheck() For the array format. * - * @return array + * @return array>>> */ public static function dataHasParametersDontSkipShortArrayCheck() { @@ -80,9 +79,9 @@ public static function dataHasParametersDontSkipShortArrayCheck() * * @dataProvider dataGetParametersSkipShortArrayCheck * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string $targetType The type of token to look for. - * @param array $expected The expected return value. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int|string $targetType The type of token to look for. + * @param array> $expected The expected return value. * * @return void */ @@ -93,7 +92,7 @@ public function testGetParametersSkipShortArrayCheck($testMarker, $targetType, $ * Note: this also means that the "$expected" value will not be tested as the exception * will be received before the code reaches that point. */ - if ($targetType === \T_OPEN_SQUARE_BRACKET && \version_compare(Helper::getVersion(), '3.7.1', '>')) { + if ($targetType === \T_OPEN_SQUARE_BRACKET) { $this->expectPhpcsException( 'The hasParameters() method expects a function call, array, isset or unset token to be passed.' ); @@ -125,7 +124,7 @@ public function testGetParametersSkipShortArrayCheck($testMarker, $targetType, $ * * @see testGetParametersSkipShortArrayCheck() For the array format. * - * @return array + * @return array>>> */ public static function dataGetParametersSkipShortArrayCheck() { @@ -144,7 +143,7 @@ public static function dataGetParametersSkipShortArrayCheck() * @see testGetParametersSkipShortArrayCheck() For the array format. * @see testHasParametersDontSkipShortArrayCheck() For the array format. * - * @return array + * @return array>>> */ public static function dataTestCases() { diff --git a/Tests/Utils/PassedParameters/GetParametersTest.inc b/Tests/Utils/PassedParameters/GetParametersTest.inc index 6e291206..3295cf8b 100644 --- a/Tests/Utils/PassedParameters/GetParametersTest.inc +++ b/Tests/Utils/PassedParameters/GetParametersTest.inc @@ -132,10 +132,17 @@ $arr4 = array(...$arr1, ...arrGen(), ...new ArrayIterator(['a', 'b', 'c'])); // Also includes code sample for PHP 8.1 unpacking with string keys. $fruits = ['banana', ...$parts, 'watermelon', ...["a" => 2],]; -/* testPHP80FunctionCallInAttribute */ +/* testPHP80ClassInstantiationInAttribute1 */ #[AttributeAttachedToClosure([1, 2, 3])] $closure = function() {}; +/* testPHP80ClassInstantiationInAttribute2 */ +#[MyAttribute(1, self::Foo, 'string')] +function foo() {} + +#[AttributeOne, /* testPHP80ClassInstantiationInMultiAttribute */ \AttributeTwo(1, self::Foo)] +function bar() {} + /* testPHP80SkippingOverAttributes */ $result = function_call( $value, diff --git a/Tests/Utils/PassedParameters/GetParametersTest.php b/Tests/Utils/PassedParameters/GetParametersTest.php index 245be61c..8e435ead 100644 --- a/Tests/Utils/PassedParameters/GetParametersTest.php +++ b/Tests/Utils/PassedParameters/GetParametersTest.php @@ -49,9 +49,10 @@ public function testGetParametersNoParams() * * @dataProvider dataGetParameters * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string $targetType The type of token to look for. - * @param array $expected The expected parameter array. + * @param string $testMarker The comment which prefaces the target token in + * the test file. + * @param int|string $targetType The type of token to look for. + * @param array> $expected The expected parameter array. * * @return void */ @@ -84,10 +85,12 @@ public function testGetParameters($testMarker, $targetType, $expected) * * @see testGetParameters() For the array format. * - * @return array + * @return array>>> */ public static function dataGetParameters() { + $php8Names = parent::usesPhp8NameTokens(); + return [ 'function-call' => [ 'testMarker' => '/* testFunctionCall */', @@ -586,9 +589,9 @@ public function test( $foo, $bar ) { ], ], - // PHP 8.0: function calls in attributes. - 'function-call-within-an-attribute' => [ - 'testMarker' => '/* testPHP80FunctionCallInAttribute */', + // PHP 8.0: class instantiation in attributes. + 'class-instantiation-within-an-attribute-1' => [ + 'testMarker' => '/* testPHP80ClassInstantiationInAttribute1 */', 'targetType' => \T_STRING, 'expected' => [ 1 => [ @@ -598,6 +601,43 @@ public function test( $foo, $bar ) { ], ], ], + 'class-instantiation-within-an-attribute-2' => [ + 'testMarker' => '/* testPHP80ClassInstantiationInAttribute2 */', + 'targetType' => \T_STRING, + 'expected' => [ + 1 => [ + 'start' => 2, + 'end' => 2, + 'raw' => '1', + ], + 2 => [ + 'start' => 4, + 'end' => 7, + 'raw' => 'self::Foo', + ], + 3 => [ + 'start' => 9, + 'end' => 10, + 'raw' => "'string'", + ], + ], + ], + 'class-instantiation-within-a-multi-attribute' => [ + 'testMarker' => '/* testPHP80ClassInstantiationInMultiAttribute */', + 'targetType' => ($php8Names === true) ? \T_NAME_FULLY_QUALIFIED : \T_STRING, + 'expected' => [ + 1 => [ + 'start' => 2, + 'end' => 2, + 'raw' => '1', + ], + 2 => [ + 'start' => 4, + 'end' => 7, + 'raw' => 'self::Foo', + ], + ], + ], // PHP 8.0: skipping over attributes. 'function-call-with-attributes-attached-to-passed-closure' => [ @@ -689,10 +729,10 @@ public function testGetParametersResultIsCached() * * @dataProvider dataGetParameter * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string $targetType The type of token to look for. - * @param int $paramPosition The position of the parameter we want to retrieve the details for. - * @param array $expected The expected array for the specific parameter. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int|string $targetType The type of token to look for. + * @param int $paramPosition The position of the parameter we want to retrieve the details for. + * @param array $expected The expected array for the specific parameter. * * @return void */ @@ -721,7 +761,7 @@ public function testGetParameter($testMarker, $targetType, $paramPosition, $expe * * @see testGetParameter() For the array format. * - * @return array + * @return array>> */ public static function dataGetParameter() { diff --git a/Tests/Utils/PassedParameters/GetParametersWithLimitTest.php b/Tests/Utils/PassedParameters/GetParametersWithLimitTest.php index e16c242d..0edd22b3 100644 --- a/Tests/Utils/PassedParameters/GetParametersWithLimitTest.php +++ b/Tests/Utils/PassedParameters/GetParametersWithLimitTest.php @@ -69,7 +69,7 @@ public function testGetParametersWithIneffectiveLimit($limit) * * @see testGetParametersWithIneffectiveLimit() For the array format. * - * @return array + * @return array> */ public static function dataGetParametersWithIneffectiveLimit() { @@ -88,11 +88,12 @@ public static function dataGetParametersWithIneffectiveLimit() * * @dataProvider dataGetParametersWithLimit * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string $targetType The type of token to look for. - * @param int $limit The number of parameters to limit this call to. - * Should match the expected count. - * @param array $expected Optional. The expected return value. Only tested when not empty. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int|string $targetType The type of token to look for. + * @param int $limit The number of parameters to limit this call to. + * Should match the expected count. + * @param array> $expected Optional. The expected return value. Only tested when + * not empty. * * @return void */ @@ -129,7 +130,7 @@ public function testGetParametersWithLimit($testMarker, $targetType, $limit, $ex * * @see testGetParametersWithLimit() For the array format. * - * @return array + * @return array>>> */ public static function dataGetParametersWithLimit() { diff --git a/Tests/Utils/PassedParameters/HasParametersTest.inc b/Tests/Utils/PassedParameters/HasParametersTest.inc index 5eca6c33..f02a6ab5 100644 --- a/Tests/Utils/PassedParameters/HasParametersTest.inc +++ b/Tests/Utils/PassedParameters/HasParametersTest.inc @@ -172,6 +172,14 @@ $anon = new class() {}; /* testHasParamsAnonClass */ $anon = new class( $param1, $param2 ) {}; +/* testHasParamsPHP80ClassInstantiationInAttribute */ +#[MyAttribute(1, self::Foo, 'string')] +function foo() {} + +/* testHasParamsPHP80ClassInstantiationInMultiAttribute */ +#[AttributeOne, \AttributeTwo(1, self::Foo)] +function bar() {} + /* testPHP81FirstClassCallableNotFunctionCallGlobalFunction */ $fn = strlen(...); diff --git a/Tests/Utils/PassedParameters/HasParametersTest.php b/Tests/Utils/PassedParameters/HasParametersTest.php index 51cf443b..350cd81c 100644 --- a/Tests/Utils/PassedParameters/HasParametersTest.php +++ b/Tests/Utils/PassedParameters/HasParametersTest.php @@ -55,7 +55,7 @@ public function testNotAnAcceptedTokenException() } /** - * Test receiving an expected exception when T_SELF is passed not preceeded by `new`. + * Test receiving an expected exception when a hierarchy keyword is passed not preceeded by `new`. * * @dataProvider dataNotACallToConstructor * @@ -387,6 +387,25 @@ public static function dataHasParameters() 'expected' => true, ], + // Class instantiations in attribute. + 'has-params-class-instantiation-in-attribute' => [ + 'testMarker' => '/* testHasParamsPHP80ClassInstantiationInAttribute */', + 'targetType' => \T_STRING, + 'expected' => true, + ], + 'no-params-class-instantiation-in-multi-attribute' => [ + 'testMarker' => '/* testHasParamsPHP80ClassInstantiationInMultiAttribute */', + 'targetType' => \T_STRING, + 'expected' => false, + 'targetContent' => 'AttributeOne', + ], + 'has-params-class-instantiation-in-multi-attribute' => [ + 'testMarker' => '/* testHasParamsPHP80ClassInstantiationInMultiAttribute */', + 'targetType' => ($php8Names === true) ? \T_NAME_FULLY_QUALIFIED : \T_STRING, + 'expected' => true, + 'targetContent' => ($php8Names === true) ? '\AttributeTwo' : 'AttributeTwo', + ], + // PHP 8.1 first class callables are callbacks, not function calls. 'no-params-php81-first-class-callable-global-function' => [ 'testMarker' => '/* testPHP81FirstClassCallableNotFunctionCallGlobalFunction */', diff --git a/Tests/Utils/TextStrings/InterpolatedVariablesTest.php b/Tests/Utils/TextStrings/InterpolatedVariablesTest.php index 051c645a..dad4e49b 100644 --- a/Tests/Utils/TextStrings/InterpolatedVariablesTest.php +++ b/Tests/Utils/TextStrings/InterpolatedVariablesTest.php @@ -143,8 +143,8 @@ final class InterpolatedVariablesTest extends TestCase * * @dataProvider dataEmbedsInPhrases * - * @param string $input The input string. - * @param array $expected The expected function output of the respective functions. + * @param string $input The input string. + * @param array> $expected The expected function output of the respective functions. * * @return void */ @@ -160,8 +160,8 @@ public function testGetEmbeds($input, $expected) * @dataProvider dataEscaping * @dataProvider dataSpecificCases * - * @param string $input The input string. - * @param array $expected The expected function output of the respective functions. + * @param string $input The input string. + * @param array> $expected The expected function output of the respective functions. * * @return void */ @@ -177,8 +177,8 @@ public function testGetEmbedsAndCheckOffset($input, $expected) * @dataProvider dataEscaping * @dataProvider dataSpecificCases * - * @param string $input The input string. - * @param array $expected The expected function output of the respective functions. + * @param string $input The input string. + * @param array> $expected The expected function output of the respective functions. * * @return void */ @@ -193,7 +193,7 @@ public function testStripEmbeds($input, $expected) * @see testGetEmbeds() For the array format. * @see testStripEmbeds() For the array format. * - * @return array + * @return array>>> */ public static function dataEmbedsInPhrases() { @@ -286,7 +286,7 @@ public static function dataEmbedsInPhrases() * @see testGetEmbedsAndCheckOffset() For the array format. * @see testStripEmbeds() For the array format. * - * @return array + * @return array>>> */ public static function dataEscaping() { @@ -357,7 +357,7 @@ public static function dataEscaping() * @see testGetEmbedsAndCheckOffset() For the array format. * @see testStripEmbeds() For the array format. * - * @return array + * @return array>>> */ public static function dataSpecificCases() { diff --git a/Tests/Utils/UseStatements/SplitImportUseStatementTest.inc b/Tests/Utils/UseStatements/SplitImportUseStatementTest.inc index e9daecf2..9f5af864 100644 --- a/Tests/Utils/UseStatements/SplitImportUseStatementTest.inc +++ b/Tests/Utils/UseStatements/SplitImportUseStatementTest.inc @@ -14,6 +14,9 @@ use MyNamespace\MyClass; /* testUsePlainAliased */ use MyNamespace \ YourClass as ClassAlias; +/* testUsePlainLeadingBackslash */ +use \MyNamespace\TheirClass; + /* testUseMultipleWithComments */ use Vendor\Foo\ClassA as ClassABC, Vendor \ /*comment*/ Bar \ /*another comment */ InterfaceB, @@ -38,7 +41,7 @@ use CONST MyNamespace\MY_CONST; use const MyNamespace\YOUR_CONST as CONST_ALIAS; /* testUseConstMultiple */ -use const foo\math\PI, foo\math\GOLDEN_RATIO as MATH_GOLDEN; +use const foo\math\PI, \foo\math\GOLDEN_RATIO as MATH_GOLDEN; /* testGroupUse */ use some\namespacing\{ @@ -47,6 +50,12 @@ use some\namespacing\{ another\level\SomeClassC as C }; +/* testGroupUseLeadingBackslash */ +use \world\namespacing\{ + SomeClassA, + deeper\level\SomeClassB +}; + /* testGroupUseFunctionTrailingComma */ use function bar\math\{ Msin, @@ -70,15 +79,15 @@ use Some\NS\ { }; /* testUsePlainReservedKeyword */ -// Intentional parse error - use of reserved keyword in namespace. +// Intentional parse error for PHP < 8.0 - use of reserved keyword in namespace. use Vendor\break\ClassName; /* testUseFunctionPlainReservedKeyword */ -// Intentional parse error - use of reserved keyword in namespace. +// Intentional parse error for PHP < 8.0 - use of reserved keyword in namespace. use function Vendor\YourNamespace\switch\yourFunction; /* testUseConstPlainReservedKeyword */ -// Intentional parse error - use of reserved keyword in namespace. +// Intentional parse error for PHP < 8.0 - use of reserved keyword in namespace. use const Vendor\YourNamespace\function\yourConst; /* testUsePlainAliasReservedKeyword */ @@ -88,7 +97,7 @@ use Vendor\YourNamespace\ClassName as class; /* testUsePlainAliasReservedKeywordFunction */ // Intentional parse error - use of reserved keyword as alias. use Vendor\{ - YourNamespace\ClassName as function + YourNamespace\ClassName as function }; /* testUsePlainAliasReservedKeywordConst */ diff --git a/Tests/Utils/UseStatements/SplitImportUseStatementTest.php b/Tests/Utils/UseStatements/SplitImportUseStatementTest.php index a38e1309..5bc3c34a 100644 --- a/Tests/Utils/UseStatements/SplitImportUseStatementTest.php +++ b/Tests/Utils/UseStatements/SplitImportUseStatementTest.php @@ -124,6 +124,15 @@ public static function dataSplitImportUseStatement() 'const' => [], ], ], + 'plain-with-leading-backslash' => [ + 'testMarker' => '/* testUsePlainLeadingBackslash */', + 'expected' => [ + 'name' => ['TheirClass' => 'MyNamespace\TheirClass'], + 'function' => [], + 'const' => [], + ], + ], + 'multiple-with-comments' => [ 'testMarker' => '/* testUseMultipleWithComments */', 'expected' => [ @@ -203,6 +212,17 @@ public static function dataSplitImportUseStatement() 'const' => [], ], ], + 'group-with-leading-backslash' => [ + 'testMarker' => '/* testGroupUseLeadingBackslash */', + 'expected' => [ + 'name' => [ + 'SomeClassA' => 'world\namespacing\SomeClassA', + 'SomeClassB' => 'world\namespacing\deeper\level\SomeClassB', + ], + 'function' => [], + 'const' => [], + ], + ], 'group-function-trailing-comma' => [ 'testMarker' => '/* testGroupUseFunctionTrailingComma */', 'expected' => [ diff --git a/composer.json b/composer.json index 09405e9f..80167bcb 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ }, "require" : { "php" : ">=5.4", - "squizlabs/php_codesniffer" : "^3.9.0 || 4.0.x-dev@dev", + "squizlabs/php_codesniffer" : "^3.10.0 || 4.0.x-dev@dev", "dealerdirect/phpcodesniffer-composer-installer" : "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0" }, "require-dev" : { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8d42ecc5..447789ff 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -81,21 +81,4 @@ parameters: - Tests/Utils/GetTokensAsString/GetTokensAsStringTest.php - Tests/Utils/NamingConventions/IsValidIdentifierNameTest.php - # Ignoring as this is fine. - - - message: '`^Parameter #1 \$exception of method PHPUnit\\Framework\\TestCase::expectException\(\) expects class-string, string given\.$`' - path: Tests/TestUtils/UtilityMethodTestCase/SkipJSCSSTestsOnPHPCS4Test.php - count: 1 - - # Level 6 - # Test data providers. - - - message: '`^Method PHPCSUtils\\Tests\\[^: ]+Test(Case)?::data\S+\(\) return type has no value type specified in iterable type array\.$`' - path: Tests/* - - # Test methods. - - - message: '`^Method PHPCSUtils\\Tests\\[^: ]+Test(Case)?::\S+\(\) has parameter \$\S* with no value type specified in iterable type array\.$`' - path: Tests/* - - # yamllint enable rule:line-length + # yamllint enable rule:line-length