Skip to content

Commit

Permalink
PHP 8.2 | FunctionDeclarations::getParameters(): handle true pseudotype
Browse files Browse the repository at this point in the history
As pulled in upstream PR 3662 (not yet merged)

Ref:
* squizlabs/PHP_CodeSniffer 3662
  • Loading branch information
jrfnl committed Oct 15, 2022
1 parent e4b5676 commit aef1a01
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 10 deletions.
3 changes: 3 additions & 0 deletions PHPCSUtils/Tokens/Collections.php
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ class Collections
* @since 1.0.0-alpha1
* @since 1.0.0-alpha4 Added the T_TYPE_UNION, T_FALSE, T_NULL tokens for PHP 8.0 union type support.
* @since 1.0.0-alpha4 Added the T_TYPE_INTERSECTION token for PHP 8.1 intersection type support.
* @since 1.0.0-alpha4 Added the T_TRUE token for PHP 8.2 true type support.
*
* @deprecated 1.0.0-alpha4 Use the {@see Collections::parameterTypeTokens()} method instead.
*
Expand All @@ -482,6 +483,7 @@ class Collections
\T_SELF => \T_SELF,
\T_PARENT => \T_PARENT,
\T_FALSE => \T_FALSE,
\T_TRUE => \T_TRUE,
\T_NULL => \T_NULL,
\T_STRING => \T_STRING,
\T_NS_SEPARATOR => \T_NS_SEPARATOR,
Expand Down Expand Up @@ -921,6 +923,7 @@ public static function parameterPassingTokens()
* @since 1.0.0-alpha4 Added the T_TYPE_UNION, T_FALSE, T_NULL tokens for PHP 8.0 union type support.
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens.
* @since 1.0.0-alpha4 Added the T_TYPE_INTERSECTION token for PHP 8.1 intersection type support.
* @since 1.0.0-alpha4 Added the T_TRUE token for PHP 8.2 true type support.
*
* @return array <int|string> => <int|string>
*/
Expand Down
12 changes: 11 additions & 1 deletion PHPCSUtils/Utils/FunctionDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ public static function getProperties(File $phpcsFile, $stackPtr)
* - More efficient and more stable looping of the default value.
* - Clearer exception message when a non-closure use token was passed to the function.
* - Support for PHP 8.0 identifier name tokens in parameter types, cross-version PHP & PHPCS.
* - Support for the PHP 8.2 `true` type.
*
* @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
* @see \PHPCSUtils\BackCompat\BCFile::getMethodParameters() Cross-version compatible version of the original.
Expand All @@ -374,6 +375,7 @@ public static function getProperties(File $phpcsFile, $stackPtr)
* @since 1.0.0-alpha4 Added support for PHP 8.0 parameter attributes.
* @since 1.0.0-alpha4 Added support for PHP 8.1 readonly keyword for constructor property promotion.
* @since 1.0.0-alpha4 Added support for PHP 8.1 intersection types.
* @since 1.0.0-alpha4 Added support for PHP 8.2 true type.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position in the stack of the function token
Expand Down Expand Up @@ -441,8 +443,16 @@ public static function getParameters(File $phpcsFile, $stackPtr)
$visibilityToken = null;
$readonlyToken = null;

$parameterTypeTokens = Collections::parameterTypeTokens();

/*
* BC PHPCS < 3.x.x: The union type separator is not (yet) retokenized correctly
* for union types containing the `true` type.
*/
$parameterTypeTokens[\T_BITWISE_OR] = \T_BITWISE_OR;

for ($i = $paramStart; $i <= $closer; $i++) {
if (isset(Collections::parameterTypeTokens()[$tokens[$i]['code']]) === true
if (isset($parameterTypeTokens[$tokens[$i]['code']]) === true
// Self and parent are valid, static invalid, but was probably intended as type declaration.
|| $tokens[$i]['code'] === \T_STATIC
) {
Expand Down
1 change: 1 addition & 0 deletions Tests/Tokens/Collections/ParameterTypeTokensTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function testParameterTypeTokens()
\T_SELF => \T_SELF,
\T_PARENT => \T_PARENT,
\T_FALSE => \T_FALSE,
\T_TRUE => \T_TRUE,
\T_NULL => \T_NULL,
\T_STRING => \T_STRING,
\T_NS_SEPARATOR => \T_NS_SEPARATOR,
Expand Down
8 changes: 8 additions & 0 deletions Tests/Utils/FunctionDeclarations/GetParametersDiffTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

/* testPHP82PseudoTypeTrue */
function pseudoTypeTrue(?true $var = true) {}

/* testPHP82PseudoTypeFalseAndTrue */
// 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 $var = true) {}
115 changes: 106 additions & 9 deletions Tests/Utils/FunctionDeclarations/GetParametersDiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,124 @@ class GetParametersDiffTest extends UtilityMethodTestCase
{

/**
* Initialize PHPCS & tokenize the test case file.
* Test passing a non-existent token pointer.
*
* @beforeClass
* @return void
*/
public function testNonExistentToken()
{
$this->expectPhpcsException('$stackPtr must be of type T_FUNCTION, T_CLOSURE or T_USE or an arrow function');

FunctionDeclarations::getParameters(self::$phpcsFile, 10000);
}

/**
* Verify recognition of PHP 8.2 stand-alone `true` type.
*
* @return void
*/
public static function setUpTestFile()
public function testPHP82PseudoTypeTrue()
{
self::$caseFile = \dirname(\dirname(__DIR__)) . '/DummyFile.inc';
parent::setUpTestFile();
$expected = [];
$expected[0] = [
'token' => 7, // Offset from the T_FUNCTION token.
'name' => '$var',
'content' => '?true $var = true',
'default' => 'true',
'default_token' => 11, // Offset from the T_FUNCTION token.
'default_equal_token' => 9, // Offset from the T_FUNCTION token.
'has_attributes' => false,
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '?true',
'type_hint_token' => 5, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 5, // Offset from the T_FUNCTION token.
'nullable_type' => true,
'comma_token' => false,
];

$this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected);
}

/**
* Test passing a non-existent token pointer.
* Verify recognition of PHP 8.2 type declaration with (illegal) type false combined with type true.
*
* @return void
*/
public function testNonExistentToken()
public function testPHP82PseudoTypeFalseAndTrue()
{
$this->expectPhpcsException('$stackPtr must be of type T_FUNCTION, T_CLOSURE or T_USE or an arrow function');
$expected = [];
$expected[0] = [
'token' => 8, // Offset from the T_FUNCTION token.
'name' => '$var',
'content' => 'true|false $var = true',
'default' => 'true',
'default_token' => 12, // Offset from the T_FUNCTION token.
'default_equal_token' => 10, // Offset from the T_FUNCTION token.
'has_attributes' => false,
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => 'true|false',
'type_hint_token' => 4, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 6, // Offset from the T_FUNCTION token.
'nullable_type' => false,
'comma_token' => false,
];

FunctionDeclarations::getParameters(self::$phpcsFile, 10000);
$this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected);
}

/**
* Test helper.
*
* @param string $marker The comment which preceeds the test.
* @param array $expected The expected function output.
* @param array $targetType Optional. The token type to search for after $marker.
* Defaults to the function/closure/arrow tokens.
*
* @return void
*/
protected function getMethodParametersTestHelper($marker, $expected, $targetType = [\T_FUNCTION, \T_CLOSURE, \T_FN])
{
$target = $this->getTargetToken($marker, $targetType);
$found = FunctionDeclarations::getParameters(self::$phpcsFile, $target);

foreach ($expected as $key => $param) {
$expected[$key]['token'] += $target;

if ($param['reference_token'] !== false) {
$expected[$key]['reference_token'] += $target;
}
if ($param['variadic_token'] !== false) {
$expected[$key]['variadic_token'] += $target;
}
if ($param['type_hint_token'] !== false) {
$expected[$key]['type_hint_token'] += $target;
}
if ($param['type_hint_end_token'] !== false) {
$expected[$key]['type_hint_end_token'] += $target;
}
if ($param['comma_token'] !== false) {
$expected[$key]['comma_token'] += $target;
}
if (isset($param['default_token'])) {
$expected[$key]['default_token'] += $target;
}
if (isset($param['default_equal_token'])) {
$expected[$key]['default_equal_token'] += $target;
}
if (isset($param['visibility_token'])) {
$expected[$key]['visibility_token'] += $target;
}
if (isset($param['readonly_token'])) {
$expected[$key]['readonly_token'] += $target;
}
}

$this->assertSame($expected, $found);
}
}

0 comments on commit aef1a01

Please sign in to comment.