Skip to content

Commit

Permalink
Merge pull request #358 from PHPCSStandards/php8.1/add-support-for-enums
Browse files Browse the repository at this point in the history
PHP 8.1: add support for enums
  • Loading branch information
jrfnl committed Oct 15, 2022
2 parents 0b2f4e1 + f90dd12 commit ebcd67c
Show file tree
Hide file tree
Showing 26 changed files with 203 additions and 26 deletions.
8 changes: 8 additions & 0 deletions PHPCSUtils/Tokens/Collections.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class Collections
* within a namespace scope are still global and not limited to that namespace.
*
* @since 1.0.0-alpha1
* @since 1.0.0-alpha4 Added the PHP 8.1 T_ENUM token.
*
* @deprecated 1.0.0-alpha4 Use the {@see Collections::closedScopes()} method instead.
*
Expand All @@ -194,6 +195,7 @@ class Collections
\T_ANON_CLASS => \T_ANON_CLASS,
\T_INTERFACE => \T_INTERFACE,
\T_TRAIT => \T_TRAIT,
\T_ENUM => \T_ENUM,
\T_FUNCTION => \T_FUNCTION,
\T_CLOSURE => \T_CLOSURE,
];
Expand Down Expand Up @@ -384,6 +386,7 @@ class Collections
* DEPRECATED: OO structures which can use the "implements" keyword.
*
* @since 1.0.0-alpha1
* @since 1.0.0-alpha4 Added the PHP 8.1 T_ENUM token.
*
* @deprecated 1.0.0-alpha4 Use the {@see Collections::ooCanImplement()} method instead.
*
Expand All @@ -392,6 +395,7 @@ class Collections
public static $OOCanImplement = [
\T_CLASS => \T_CLASS,
\T_ANON_CLASS => \T_ANON_CLASS,
\T_ENUM => \T_ENUM,
];

/**
Expand All @@ -400,6 +404,7 @@ class Collections
* Note: traits can not declare constants.
*
* @since 1.0.0-alpha1
* @since 1.0.0-alpha4 Added the PHP 8.1 T_ENUM token.
*
* @deprecated 1.0.0-alpha4 Use the {@see Collections::ooConstantScopes()} method instead.
*
Expand All @@ -409,6 +414,7 @@ class Collections
\T_CLASS => \T_CLASS,
\T_ANON_CLASS => \T_ANON_CLASS,
\T_INTERFACE => \T_INTERFACE,
\T_ENUM => \T_ENUM,
];

/**
Expand Down Expand Up @@ -820,6 +826,7 @@ public static function ooCanExtend()
* OO structures which can use the "implements" keyword.
*
* @since 1.0.0-alpha4 This method replaces the {@see Collections::$OOCanImplement} property.
* @since 1.0.0-alpha4 Added the PHP 8.1 T_ENUM token.
*
* @return array <int|string> => <int|string>
*/
Expand All @@ -834,6 +841,7 @@ public static function ooCanImplement()
* Note: traits can not declare constants.
*
* @since 1.0.0-alpha4 This method replaces the {@see Collections::$OOConstantScopes} property.
* @since 1.0.0-alpha4 Added the PHP 8.1 T_ENUM token.
*
* @return array <int|string> => <int|string>
*/
Expand Down
20 changes: 13 additions & 7 deletions PHPCSUtils/Utils/ObjectDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ObjectDeclarations
{

/**
* Retrieves the declaration name for classes, interfaces, traits, and functions.
* Retrieves the declaration name for classes, interfaces, traits, enums and functions.
*
* Main differences with the PHPCS version:
* - Defensive coding against incorrect calls to this method.
Expand All @@ -48,18 +48,20 @@ class ObjectDeclarations
* @see \PHPCSUtils\BackCompat\BCFile::getDeclarationName() Cross-version compatible version of the original.
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.1 enums.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the declaration token
* which declared the class, interface,
* trait, or function.
* trait, enum or function.
*
* @return string|null The name of the class, interface, trait, or function;
* @return string|null The name of the class, interface, trait, enum, or function;
* or `NULL` if the passed token doesn't exist, the function or
* class is anonymous or in case of a parse error/live coding.
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
* `T_FUNCTION`, `T_CLASS`, `T_TRAIT`, or `T_INTERFACE`.
* `T_FUNCTION`, `T_CLASS`, `T_ANON_CLASS`,
* `T_CLOSURE`, `T_TRAIT`, `T_ENUM` or `T_INTERFACE`.
*/
public static function getName(File $phpcsFile, $stackPtr)
{
Expand All @@ -77,9 +79,11 @@ public static function getName(File $phpcsFile, $stackPtr)
&& $tokenCode !== \T_CLASS
&& $tokenCode !== \T_INTERFACE
&& $tokenCode !== \T_TRAIT
&& $tokenCode !== \T_ENUM
) {
throw new RuntimeException(
'Token type "' . $tokens[$stackPtr]['type'] . '" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT'
'Token type "' . $tokens[$stackPtr]['type']
. '" is not T_FUNCTION, T_CLASS, T_INTERFACE, T_TRAIT or T_ENUM'
);
}

Expand Down Expand Up @@ -108,6 +112,7 @@ public static function getName(File $phpcsFile, $stackPtr)
$exclude[] = \T_OPEN_PARENTHESIS;
$exclude[] = \T_OPEN_CURLY_BRACKET;
$exclude[] = \T_BITWISE_AND;
$exclude[] = \T_COLON; // Backed enums.

$nameStart = $phpcsFile->findNext($exclude, ($stackPtr + 1), $stopPoint, true);
if ($nameStart === false) {
Expand Down Expand Up @@ -230,7 +235,7 @@ public static function findExtendedClassName(File $phpcsFile, $stackPtr)
}

/**
* Retrieves the names of the interfaces that the specified class implements.
* Retrieves the names of the interfaces that the specified class or enum implements.
*
* Main differences with the PHPCS version:
* - Bugs fixed:
Expand All @@ -246,9 +251,10 @@ public static function findExtendedClassName(File $phpcsFile, $stackPtr)
* the original.
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.1 enums.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The stack position of the class.
* @param int $stackPtr The stack position of the class or enum token.
*
* @return array|false Array with names of the implemented interfaces or `FALSE` on
* error or if there are no implemented interface names.
Expand Down
6 changes: 4 additions & 2 deletions PHPCSUtils/Utils/Scopes.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ public static function validDirectScope(File $phpcsFile, $stackPtr, $validScopes
}

/**
* Check whether a T_CONST token is a class/interface constant declaration.
* Check whether a T_CONST token is a class/interface/enum constant declaration.
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.1 enums.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack of the
Expand Down Expand Up @@ -116,9 +117,10 @@ public static function isOOProperty(File $phpcsFile, $stackPtr)
}

/**
* Check whether a T_FUNCTION token is a class/interface/trait method declaration.
* Check whether a T_FUNCTION token is a class/interface/trait/enum method declaration.
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.1 enums.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack of the
Expand Down
2 changes: 1 addition & 1 deletion PHPCSUtils/Utils/UseStatements.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public static function getType(File $phpcsFile, $stackPtr)
}

$traitScopes = Tokens::$ooScopeTokens;
// Only classes and traits can import traits.
// Only classes, traits and enums can import traits.
unset($traitScopes[\T_INTERFACE]);

if (isset($traitScopes[$tokens[$lastCondition]['code']]) === true) {
Expand Down
9 changes: 9 additions & 0 deletions Tests/BackCompat/BCFile/FindImplementedInterfaceNamesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ class testFECNClassThatExtendsAndImplements extends testFECNClass implements Int
/* testClassThatImplementsAndExtends */
class testFECNClassThatImplementsAndExtends implements \InterfaceA, InterfaceB extends testFECNClass {}

/* testBackedEnumWithoutImplements */
enum Suit:string {}

/* testEnumImplements */
enum Suit implements Colorful {}

/* testBackedEnumImplements */
enum Suit: string implements Colorful, \Deck {}

/* testAnonClassImplements */
$anon = class() implements testFIINInterface {}

Expand Down
17 changes: 16 additions & 1 deletion Tests/BackCompat/BCFile/FindImplementedInterfaceNamesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function testFindImplementedInterfaceNames($identifier, $expected)
{
$testClass = static::TEST_CLASS;

$OOToken = $this->getTargetToken($identifier, [T_CLASS, T_ANON_CLASS, T_INTERFACE]);
$OOToken = $this->getTargetToken($identifier, [T_CLASS, T_ANON_CLASS, T_INTERFACE, T_ENUM]);
$result = $testClass::findImplementedInterfaceNames(self::$phpcsFile, $OOToken);
$this->assertSame($expected, $result);
}
Expand Down Expand Up @@ -144,6 +144,21 @@ public function dataImplementedInterface()
'InterfaceB',
],
],
[
'/* testBackedEnumWithoutImplements */',
false,
],
[
'/* testEnumImplements */',
['Colorful'],
],
[
'/* testBackedEnumImplements */',
[
'Colorful',
'\Deck',
],
],
[
'/* testAnonClassImplements */',
['testFIINInterface'],
Expand Down
3 changes: 3 additions & 0 deletions Tests/BackCompat/BCFile/GetClassPropertiesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ interface NotAClass {}
/* testAnonClass */
$anon = new class() {};

/* testEnum */
enum NotAClassEither {}

/* testClassWithoutProperties */
class ClassWithoutProperties {}

Expand Down
4 changes: 4 additions & 0 deletions Tests/BackCompat/BCFile/GetClassPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public function dataNotAClassException()
'/* testAnonClass */',
\T_ANON_CLASS,
],
'enum' => [
'/* testEnum */',
\T_ENUM,
],
];
}

Expand Down
2 changes: 1 addition & 1 deletion Tests/BackCompat/BCFile/GetDeclarationNameJSTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function dataGetDeclarationNameNull()
public function testGetDeclarationName($testMarker, $expected, $targetType = null)
{
if (isset($targetType) === false) {
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_FUNCTION];
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_ENUM, \T_FUNCTION];
}

$target = $this->getTargetToken($testMarker, $targetType);
Expand Down
16 changes: 16 additions & 0 deletions Tests/BackCompat/BCFile/GetDeclarationNameTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ class /* comment */
/* testFunctionFn */
function fn() {}

/* testPureEnum */
enum Foo
{
case SOME_CASE;
}

/* testBackedEnumSpaceBetweenNameAndColon */
enum Hoo : string
{
case ONE = 'one';
case TWO = 'two';
}

/* testBackedEnumNoSpaceBetweenNameAndColon */
enum Suit: int implements Colorful, CardGame {}

/* testLiveCoding */
// Intentional parse error. This has to be the last test in the file.
function // Comment.
14 changes: 13 additions & 1 deletion Tests/BackCompat/BCFile/GetDeclarationNameTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public function dataGetDeclarationNameNull()
public function testGetDeclarationName($testMarker, $expected, $targetType = null)
{
if (isset($targetType) === false) {
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_FUNCTION];
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_ENUM, \T_FUNCTION];
}

$target = $this->getTargetToken($testMarker, $targetType);
Expand Down Expand Up @@ -180,6 +180,18 @@ public function dataGetDeclarationName()
'/* testFunctionFn */',
'fn',
],
'enum-pure' => [
'/* testPureEnum */',
'Foo',
],
'enum-backed-space-between-name-and-colon' => [
'/* testBackedEnumSpaceBetweenNameAndColon */',
'Hoo',
],
'enum-backed-no-space-between-name-and-colon' => [
'/* testBackedEnumNoSpaceBetweenNameAndColon */',
'Suit',
],
];
}
}
15 changes: 15 additions & 0 deletions Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,18 @@ $anon = class {
]
private mixed $baz;
};

enum Suit
{
/* testEnumProperty */
protected $anonymous;
}

enum Direction implements ArrayAccess
{
case Up;
case Down;

/* testEnumMethodParamNotProperty */
public function offsetGet($val) { ... }
}
5 changes: 5 additions & 0 deletions Tests/BackCompat/BCFile/GetMemberPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,10 @@ public function dataGetMemberProperties()
'nullable_type' => false,
],
],
'invalid-property-in-enum' => [
'/* testEnumProperty */',
[],
],
];
}

Expand Down Expand Up @@ -838,6 +842,7 @@ public function dataNotClassProperty()
['/* testGlobalVariable */'],
['/* testNestedMethodParam 1 */'],
['/* testNestedMethodParam 2 */'],
['/* testEnumMethodParamNotProperty */'],
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class FindImplementedInterfaceNamesDiffTest extends UtilityMethodTestCase
*/
public function testFindImplementedInterfaceNames($testMarker, $expected)
{
$OOToken = $this->getTargetToken($testMarker, [\T_CLASS, \T_ANON_CLASS, \T_INTERFACE]);
$OOToken = $this->getTargetToken($testMarker, [\T_CLASS, \T_ANON_CLASS, \T_INTERFACE, \T_ENUM]);
$result = ObjectDeclarations::findImplementedInterfaceNames(self::$phpcsFile, $OOToken);
$this->assertSame($expected, $result);
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/Utils/ObjectDeclarations/GetNameDiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function dataGetNameNull()
public function testGetName($testMarker, $expected, $targetType = null)
{
if (isset($targetType) === false) {
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_FUNCTION];
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_ENUM, \T_FUNCTION];
}

$target = $this->getTargetToken($testMarker, $targetType);
Expand Down
4 changes: 2 additions & 2 deletions Tests/Utils/ObjectDeclarations/GetNameJSTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static function setUpTestFile()
*/
public function testInvalidTokenPassed()
{
$this->expectPhpcsException('Token type "T_STRING" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
$this->expectPhpcsException('Token type "T_STRING" is not T_FUNCTION, T_CLASS, T_INTERFACE, T_TRAIT or T_ENUM');

$target = $this->getTargetToken('/* testInvalidTokenPassed */', \T_STRING);
ObjectDeclarations::getName(self::$phpcsFile, $target);
Expand Down Expand Up @@ -95,7 +95,7 @@ public function testGetDeclarationNameNull($testMarker, $targetType)
public function testGetDeclarationName($testMarker, $expected, $targetType = null)
{
if (isset($targetType) === false) {
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_FUNCTION];
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_ENUM, \T_FUNCTION];
}

$target = $this->getTargetToken($testMarker, $targetType);
Expand Down
4 changes: 2 additions & 2 deletions Tests/Utils/ObjectDeclarations/GetNameTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static function setUpTestFile()
*/
public function testInvalidTokenPassed()
{
$this->expectPhpcsException('Token type "T_STRING" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
$this->expectPhpcsException('Token type "T_STRING" is not T_FUNCTION, T_CLASS, T_INTERFACE, T_TRAIT or T_ENUM');

$target = $this->getTargetToken('/* testInvalidTokenPassed */', \T_STRING);
ObjectDeclarations::getName(self::$phpcsFile, $target);
Expand Down Expand Up @@ -95,7 +95,7 @@ public function testGetDeclarationNameNull($testMarker, $targetType)
public function testGetDeclarationName($testMarker, $expected, $targetType = null)
{
if (isset($targetType) === false) {
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_FUNCTION];
$targetType = [\T_CLASS, \T_INTERFACE, \T_TRAIT, \T_ENUM, \T_FUNCTION];
}

$target = $this->getTargetToken($testMarker, $targetType);
Expand Down
Loading

0 comments on commit ebcd67c

Please sign in to comment.