Skip to content

Commit

Permalink
BCFile::getMethodParameters(): sync with PHPCS / constructor property…
Browse files Browse the repository at this point in the history
… promotion support

PR 169 added support for PHP 8.0 constructor property promotion to the `FunctionDeclarations::getParameters()` method.

Upstream PR 3152 added the same to the PHPCS native `File::getMethodParameters()` method.

As that PR has now been merged, this commit syncs the upstream changes into the `BCFile::getMethodParameters()` method and moves the unit tests from the "Diff" tests to the BCFile test class.
  • Loading branch information
jrfnl committed Nov 3, 2020
1 parent 846c93b commit 8a86e13
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 325 deletions.
21 changes: 21 additions & 0 deletions PHPCSUtils/BackCompat/BCFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
* 'default_equal_token' => integer, // The stack pointer to the equals sign.
* ```
*
* Parameters declared using PHP 8 constructor property promotion, have these additional array indexes:
* ```php
* 'property_visibility' => string, // The property visibility as declared.
* 'visibility_token' => integer, // The stack pointer to the visibility modifier token.
* ```
*
* PHPCS cross-version compatible version of the `File::getMethodParameters()` method.
*
* Changelog for the PHPCS native function:
Expand Down Expand Up @@ -258,6 +264,7 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
* - PHPCS 3.5.3: Added support for PHP 7.4 `T_FN` arrow functions.
* - PHPCS 3.5.7: Added support for namespace operators in type declarations. PHPCS#3066.
* - PHPCS 3.6.0: Added support for PHP 8.0 union types. PHPCS#3032.
* - PHPCS 3.6.0: Added support for PHP 8.0 constructor property promotion. PHPCS#3152.
*
* @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
* @see \PHPCSUtils\Utils\FunctionDeclarations::getParameters() PHPCSUtils native improved version.
Expand Down Expand Up @@ -327,6 +334,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$typeHintToken = false;
$typeHintEndToken = false;
$nullableType = false;
$visibilityToken = null;

for ($i = $paramStart; $i <= $closer; $i++) {
// Check to see if this token has a parenthesis or bracket opener. If it does
Expand Down Expand Up @@ -445,6 +453,13 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$typeHintEndToken = $i;
}
break;
case 'T_PUBLIC':
case 'T_PROTECTED':
case 'T_PRIVATE':
if ($defaultStart === null) {
$visibilityToken = $i;
}
break;
case 'T_CLOSE_PARENTHESIS':
case 'T_COMMA':
// If it's null, then there must be no parameters for this
Expand Down Expand Up @@ -473,6 +488,11 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken;
$vars[$paramCount]['nullable_type'] = $nullableType;

if ($visibilityToken !== null) {
$vars[$paramCount]['property_visibility'] = $tokens[$visibilityToken]['content'];
$vars[$paramCount]['visibility_token'] = $visibilityToken;
}

if ($tokens[$i]['code'] === T_COMMA) {
$vars[$paramCount]['comma_token'] = $i;
} else {
Expand All @@ -492,6 +512,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$typeHintToken = false;
$typeHintEndToken = false;
$nullableType = false;
$visibilityToken = null;

$paramCount++;
break;
Expand Down
1 change: 0 additions & 1 deletion PHPCSUtils/Utils/FunctionDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ public static function getProperties(File $phpcsFile, $stackPtr)
* - Clearer exception message when a non-closure use token was passed to the function.
* - To allow for backward compatible handling of arrow functions, this method will also accept
* `T_STRING` tokens and examine them to check if these are arrow functions.
* - Support for PHP 8.0 constructor property promotion.
* - Support for PHP 8.0 identifier name tokens in parameter types, cross-version PHP & PHPCS.
*
* @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
Expand Down
32 changes: 32 additions & 0 deletions Tests/BackCompat/BCFile/GetMethodParametersTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,38 @@ function pseudoTypeIterableAndArray(iterable|array|Traversable $var) {}
// Intentional fatal error - duplicate types are not allowed in union types, but that's not the concern of the method.
function duplicateTypeInUnion( int | string /*comment*/ | INT $var) {}

class ConstructorPropertyPromotionNoTypes {
/* testPHP8ConstructorPropertyPromotionNoTypes */
public function __construct(
public $x = 0.0,
protected $y = '',
private $z = null,
) {}
}

class ConstructorPropertyPromotionWithTypes {
/* testPHP8ConstructorPropertyPromotionWithTypes */
public function __construct(protected float|int $x, public ?string &$y = 'test', private mixed $z) {}
}

class ConstructorPropertyPromotionAndNormalParams {
/* testPHP8ConstructorPropertyPromotionAndNormalParam */
public function __construct(public int $promotedProp, ?int $normalArg) {}
}

/* testPHP8ConstructorPropertyPromotionGlobalFunction */
// Intentional fatal error. Property promotion not allowed in non-constructor, but that's not the concern of this method.
function globalFunction(private $x) {}

abstract class ConstructorPropertyPromotionAbstractMethod {
/* testPHP8ConstructorPropertyPromotionAbstractMethod */
// Intentional fatal error.
// 1. Property promotion not allowed in abstract method, but that's not the concern of this method.
// 2. Variadic arguments not allowed in property promotion, but that's not the concern of this method.
// 3. The callable type is not supported for properties, but that's not the concern of this method.
abstract public function __construct(public callable $y, private ...$x);
}

/* testFunctionCallFnPHPCS353-354 */
$value = $obj->fn(true);

Expand Down
249 changes: 249 additions & 0 deletions Tests/BackCompat/BCFile/GetMethodParametersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,252 @@ public function testPHP8DuplicateTypeInUnionWhitespaceAndComment()
$this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected);
}

/**
* Verify recognition of PHP8 constructor property promotion without type declaration, with defaults.
*
* @return void
*/
public function testPHP8ConstructorPropertyPromotionNoTypes()
{
$expected = [];
$expected[0] = [
'token' => 8, // Offset from the T_FUNCTION token.
'name' => '$x',
'content' => 'public $x = 0.0',
'default' => '0.0',
'default_token' => 12, // Offset from the T_FUNCTION token.
'default_equal_token' => 10, // Offset from the T_FUNCTION token.
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'public',
'visibility_token' => 6, // Offset from the T_FUNCTION token.
'comma_token' => 13,
];
$expected[1] = [
'token' => 18, // Offset from the T_FUNCTION token.
'name' => '$y',
'content' => 'protected $y = \'\'',
'default' => "''",
'default_token' => 22, // Offset from the T_FUNCTION token.
'default_equal_token' => 20, // Offset from the T_FUNCTION token.
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'protected',
'visibility_token' => 16, // Offset from the T_FUNCTION token.
'comma_token' => 23,
];
$expected[2] = [
'token' => 28, // Offset from the T_FUNCTION token.
'name' => '$z',
'content' => 'private $z = null',
'default' => 'null',
'default_token' => 32, // Offset from the T_FUNCTION token.
'default_equal_token' => 30, // Offset from the T_FUNCTION token.
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'private',
'visibility_token' => 26, // Offset from the T_FUNCTION token.
'comma_token' => 33,
];

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

/**
* Verify recognition of PHP8 constructor property promotion with type declarations.
*
* @return void
*/
public function testPHP8ConstructorPropertyPromotionWithTypes()
{
$expected = [];
$expected[0] = [
'token' => 10, // Offset from the T_FUNCTION token.
'name' => '$x',
'content' => 'protected float|int $x',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => 'float|int',
'type_hint_token' => 6, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 8, // Offset from the T_FUNCTION token.
'nullable_type' => false,
'property_visibility' => 'protected',
'visibility_token' => 4, // Offset from the T_FUNCTION token.
'comma_token' => 11,
];
$expected[1] = [
'token' => 19, // Offset from the T_FUNCTION token.
'name' => '$y',
'content' => 'public ?string &$y = \'test\'',
'default' => "'test'",
'default_token' => 23, // Offset from the T_FUNCTION token.
'default_equal_token' => 21, // Offset from the T_FUNCTION token.
'pass_by_reference' => true,
'reference_token' => 18, // Offset from the T_FUNCTION token.
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '?string',
'type_hint_token' => 16, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 16, // Offset from the T_FUNCTION token.
'nullable_type' => true,
'property_visibility' => 'public',
'visibility_token' => 13, // Offset from the T_FUNCTION token.
'comma_token' => 24,
];
$expected[2] = [
'token' => 30, // Offset from the T_FUNCTION token.
'name' => '$z',
'content' => 'private mixed $z',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => 'mixed',
'type_hint_token' => 28, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 28, // Offset from the T_FUNCTION token.
'nullable_type' => false,
'property_visibility' => 'private',
'visibility_token' => 26, // Offset from the T_FUNCTION token.
'comma_token' => false,
];

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

/**
* Verify recognition of PHP8 constructor with both property promotion as well as normal parameters.
*
* @return void
*/
public function testPHP8ConstructorPropertyPromotionAndNormalParam()
{
$expected = [];
$expected[0] = [
'token' => 8, // Offset from the T_FUNCTION token.
'name' => '$promotedProp',
'content' => 'public int $promotedProp',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => 'int',
'type_hint_token' => 6, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 6, // Offset from the T_FUNCTION token.
'nullable_type' => false,
'property_visibility' => 'public',
'visibility_token' => 4, // Offset from the T_FUNCTION token.
'comma_token' => 9,
];
$expected[1] = [
'token' => 14, // Offset from the T_FUNCTION token.
'name' => '$normalArg',
'content' => '?int $normalArg',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '?int',
'type_hint_token' => 12, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 12, // Offset from the T_FUNCTION token.
'nullable_type' => true,
'comma_token' => false,
];

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

/**
* Verify behaviour when a non-constructor function uses PHP 8 property promotion syntax.
*
* @return void
*/
public function testPHP8ConstructorPropertyPromotionGlobalFunction()
{
$expected = [];
$expected[0] = [
'token' => 6, // Offset from the T_FUNCTION token.
'name' => '$x',
'content' => 'private $x',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'private',
'visibility_token' => 4, // Offset from the T_FUNCTION token.
'comma_token' => false,
];

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

/**
* Verify behaviour when an abstract constructor uses PHP 8 property promotion syntax.
*
* @return void
*/
public function testPHP8ConstructorPropertyPromotionAbstractMethod()
{
$expected = [];
$expected[0] = [
'token' => 8, // Offset from the T_FUNCTION token.
'name' => '$y',
'content' => 'public callable $y',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => 'callable',
'type_hint_token' => 6, // Offset from the T_FUNCTION token.
'type_hint_end_token' => 6, // Offset from the T_FUNCTION token.
'nullable_type' => false,
'property_visibility' => 'public',
'visibility_token' => 4, // Offset from the T_FUNCTION token.
'comma_token' => 9,
];
$expected[1] = [
'token' => 14, // Offset from the T_FUNCTION token.
'name' => '$x',
'content' => 'private ...$x',
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => true,
'variadic_token' => 13, // Offset from the T_FUNCTION token.
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'private',
'visibility_token' => 11, // Offset from the T_FUNCTION token.
'comma_token' => false,
];

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

/**
* Verify handling of a closure.
*
Expand Down Expand Up @@ -1729,6 +1975,9 @@ protected function getMethodParametersTestHelper($commentString, $expected, $tar
if (isset($param['default_equal_token'])) {
$expected[$key]['default_equal_token'] += $target;
}
if (isset($param['visibility_token'])) {
$expected[$key]['visibility_token'] += $target;
}
}

$this->assertSame($expected, $found);
Expand Down
Loading

0 comments on commit 8a86e13

Please sign in to comment.