Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP 8.2: add support for true type #368

Merged
merged 3 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 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 @@ -526,6 +528,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::propertyTypeTokens()} method instead.
*
Expand All @@ -536,6 +539,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 All @@ -549,6 +553,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::returnTypeTokens()} method instead.
*
Expand All @@ -560,6 +565,7 @@ class Collections
\T_PARENT => \T_PARENT,
\T_STATIC => \T_STATIC,
\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 @@ -917,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 Expand Up @@ -957,6 +964,7 @@ public static function parameterTypeTokensBC()
* @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 Expand Up @@ -997,6 +1005,7 @@ public static function propertyTypeTokensBC()
* @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
20 changes: 19 additions & 1 deletion PHPCSUtils/Utils/FunctionDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ public static function getName(File $phpcsFile, $stackPtr)
* - Defensive coding against incorrect calls to this method.
* - More efficient checking whether a function has a body.
* - Support for PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS.
* - Support for the PHP 8.2 `true` type.
*
* @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
* @see \PHPCSUtils\BackCompat\BCFile::getMethodProperties() Cross-version compatible version of the original.
Expand All @@ -157,6 +158,7 @@ public static function getName(File $phpcsFile, $stackPtr)
* @since 1.0.0-alpha3 Added support for PHP 8.0 static return type.
* @since 1.0.0-alpha4 Added support for PHP 8.0 union types.
* @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 to
Expand Down Expand Up @@ -246,6 +248,12 @@ public static function getProperties(File $phpcsFile, $stackPtr)
$hasBody = false;
$returnTypeTokens = Collections::returnTypeTokens();

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

$parenthesisCloser = null;
if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
$parenthesisCloser = $tokens[$stackPtr]['parenthesis_closer'];
Expand Down Expand Up @@ -354,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 @@ -366,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 @@ -433,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
8 changes: 8 additions & 0 deletions PHPCSUtils/Utils/Variables.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class Variables
* other non-property variables passed to the method.
* - Defensive coding against incorrect calls to this method.
* - Support PHP 8.0 identifier name tokens in property types, cross-version PHP & PHPCS.
* - Support for the PHP 8.2 `true` type.
*
* @see \PHP_CodeSniffer\Files\File::getMemberProperties() Original source.
* @see \PHPCSUtils\BackCompat\BCFile::getMemberProperties() Cross-version compatible version of the original.
Expand All @@ -91,6 +92,7 @@ class Variables
* @since 1.0.0-alpha4 No longer gets confused by PHP 8.0 property attributes.
* @since 1.0.0-alpha4 Added support for PHP 8.1 readonly properties.
* @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 `T_VARIABLE` token
Expand Down Expand Up @@ -181,6 +183,12 @@ public static function getMemberProperties(File $phpcsFile, $stackPtr)
$nullableType = false;
$propertyTypeTokens = Collections::propertyTypeTokens();

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

if ($i < $stackPtr) {
// We've found a type.
for ($i; $i < $stackPtr; $i++) {
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
1 change: 1 addition & 0 deletions Tests/Tokens/Collections/PropertyTypeTokensTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function testPropertyTypeTokens()
\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
1 change: 1 addition & 0 deletions Tests/Tokens/Collections/ReturnTypeTokensTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function testReturnTypeTokens()
\T_PARENT => \T_PARENT,
\T_STATIC => \T_STATIC,
\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);
}
}
7 changes: 7 additions & 0 deletions Tests/Utils/FunctionDeclarations/GetPropertiesDiffTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ trait FooTrait {
$func();
}
}

/* testPHP82PseudoTypeTrue */
function pseudoTypeTrue(): ?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 {}
46 changes: 46 additions & 0 deletions Tests/Utils/FunctionDeclarations/GetPropertiesDiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,52 @@ public function testMessyPhpcsAnnotationsStaticClosure()
$this->getPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected);
}

/**
* Verify recognition of PHP 8.2 stand-alone `true` type.
*
* @return void
*/
public function testPHP82PseudoTypeTrue()
{
$expected = [
'scope' => 'public',
'scope_specified' => false,
'return_type' => '?true',
'return_type_token' => 8, // Offset from the T_FUNCTION token.
'return_type_end_token' => 8, // Offset from the T_FUNCTION token.
'nullable_return_type' => true,
'is_abstract' => false,
'is_final' => false,
'is_static' => false,
'has_body' => true,
];

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

/**
* Verify recognition of PHP 8.2 type declaration with (illegal) type false combined with type true.
*
* @return void
*/
public function testPHP82PseudoTypeFalseAndTrue()
{
$expected = [
'scope' => 'public',
'scope_specified' => false,
'return_type' => 'true|false',
'return_type_token' => 7, // Offset from the T_FUNCTION token.
'return_type_end_token' => 9, // Offset from the T_FUNCTION token.
'nullable_return_type' => false,
'is_abstract' => false,
'is_final' => false,
'is_static' => false,
'has_body' => true,
];

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

/**
* Test helper.
*
Expand Down
Loading