Skip to content

Commit 036df99

Browse files
committed
Universal/BackedEnumColonSpacing: new sniff to enforce backed enum colon spacing
Add the `Universal.WhiteSpace.BackedEnumColonSpacing` sniff which enforces the PER Coding Style 2.0 (section 9) spacing around the colon of a backed enum declaration: no space between the enum name and the colon, and a single space between the colon and the backing type. The amount of spacing on either side is configurable via the `spacingBefore` (default 0) and `spacingAfter` (default 1) properties. Fixes 443. Ref: https://github.com/php-fig/per-coding-style/blob/2.0.0/spec.md#9-enumerations
1 parent cfdda7d commit 036df99

11 files changed

Lines changed: 371 additions & 0 deletions
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0"?>
2+
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
4+
title="Backed Enum Colon Spacing"
5+
>
6+
<standard>
7+
<![CDATA[
8+
In a backed enum declaration, there should be no space between the enum name and the colon, and a single space between the colon and the backing type.
9+
]]>
10+
</standard>
11+
<code_comparison>
12+
<code title="Valid: No space before the colon and a single space after it.">
13+
<![CDATA[
14+
enum Suit<em></em>:<em> </em>string {}
15+
]]>
16+
</code>
17+
<code title="Invalid: Space before the colon and no space after it.">
18+
<![CDATA[
19+
enum Suit<em> </em>:<em></em>string {}
20+
]]>
21+
</code>
22+
</code_comparison>
23+
</documentation>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
/**
3+
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
4+
*
5+
* @package PHPCSExtra
6+
* @copyright 2020 PHPCSExtra Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSExtra
9+
*/
10+
11+
namespace PHPCSExtra\Universal\Sniffs\WhiteSpace;
12+
13+
use PHP_CodeSniffer\Files\File;
14+
use PHP_CodeSniffer\Sniffs\Sniff;
15+
use PHP_CodeSniffer\Util\Tokens;
16+
use PHPCSUtils\Fixers\SpacesFixer;
17+
18+
/**
19+
* Checks the spacing around the colon in a backed enum declaration.
20+
*
21+
* @since 1.6.0
22+
*/
23+
final class BackedEnumColonSpacingSniff implements Sniff
24+
{
25+
26+
/**
27+
* The number of spaces to demand between the enum name and the colon.
28+
*
29+
* @since 1.6.0
30+
*
31+
* @var int
32+
*/
33+
public $spacingBefore = 0;
34+
35+
/**
36+
* The number of spaces to demand between the colon and the backing type.
37+
*
38+
* @since 1.6.0
39+
*
40+
* @var int
41+
*/
42+
public $spacingAfter = 1;
43+
44+
/**
45+
* Registers the tokens that this sniff wants to listen for.
46+
*
47+
* @since 1.6.0
48+
*
49+
* @return array<int|string>
50+
*/
51+
public function register()
52+
{
53+
return [\T_ENUM];
54+
}
55+
56+
/**
57+
* Processes this sniff, when one of its tokens is encountered.
58+
*
59+
* @since 1.6.0
60+
*
61+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
62+
* @param int $stackPtr The position of the current token in the token stack.
63+
*
64+
* @return void
65+
*/
66+
public function process(File $phpcsFile, $stackPtr)
67+
{
68+
$tokens = $phpcsFile->getTokens();
69+
70+
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
71+
// Parse error/live coding.
72+
return;
73+
}
74+
75+
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
76+
77+
$colon = $phpcsFile->findNext(\T_COLON, ($stackPtr + 1), $scopeOpener);
78+
if ($colon === false) {
79+
// Bail as this is a non-backed enum.
80+
return;
81+
}
82+
83+
$enumName = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
84+
85+
$spacingBefore = (int) $this->spacingBefore;
86+
$spacingAfter = (int) $this->spacingAfter;
87+
88+
// Check the spacing between the enum name and the colon.
89+
SpacesFixer::checkAndFix(
90+
$phpcsFile,
91+
$enumName,
92+
$colon,
93+
$spacingBefore,
94+
'Expected %s between the enum name and the colon. Found: %s.',
95+
'SpacingBefore',
96+
'error',
97+
0,
98+
'Backed enum: space before colon'
99+
);
100+
101+
$backingType = $phpcsFile->findNext(Tokens::$emptyTokens, ($colon + 1), null, true);
102+
if ($backingType === false || $tokens[$backingType]['code'] !== \T_STRING) {
103+
// Parse error/live coding: no backing type.
104+
return;
105+
}
106+
107+
// Check the spacing between the colon and the backing type.
108+
SpacesFixer::checkAndFix(
109+
$phpcsFile,
110+
$colon,
111+
$backingType,
112+
$spacingAfter,
113+
'Expected %s between the colon and the backing type. Found: %s.',
114+
'SpacingAfter',
115+
'error',
116+
0,
117+
'Backed enum: space after colon'
118+
);
119+
}
120+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* Non-backed enums have no colon and are ignored.
5+
*/
6+
enum NonBacked {}
7+
enum NonBackedImplements implements Bar {}
8+
enum NonBackedWithBody {
9+
public function label() :string { // Colons unrelated to the backed enum declaration colon are ignored.
10+
return 'label';
11+
}
12+
}
13+
14+
/*
15+
* Valid spacing (using the default property values).
16+
*/
17+
enum ValidInt: int {}
18+
enum ValidImplements: string implements HasLabel {}
19+
enum
20+
/* comment */
21+
ValidName: string
22+
{}
23+
24+
/*
25+
* Invalid spacing (using the default property values).
26+
*/
27+
enum SpaceBefore : string {}
28+
enum NoSpaceAfter:string {}
29+
enum MultipleSpacesAfter: string {}
30+
enum SpaceBeforeNoneAfter :string {}
31+
enum MultipleSpacesBeforeAndAfter : string {} // Error x 2.
32+
enum BadImplements :string implements Foo {} // Error x 2.
33+
enum NewlineBefore
34+
: string {}
35+
enum NewlineAfter:
36+
37+
string {}
38+
39+
// No auto-fixing when there are comments around the colon.
40+
enum CommentBefore /* comment */: string {}
41+
enum CommentAfter: /* comment */string {}
42+
43+
/*
44+
* Test non-default property values.
45+
*/
46+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 1
47+
enum CustomBeforeOk : string {} // OK.
48+
enum CustomBeforeError: string {} // Error.
49+
// Resetting to the default value.
50+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 0
51+
52+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingAfter 0
53+
enum CustomAfterOk:string {} // OK.
54+
enum CustomAfterError: string {} // Error.
55+
56+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 2
57+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingAfter 3
58+
enum CustomBothOk : string {} // OK.
59+
enum CustomBothError: string {} // Error x 2.
60+
// Resetting to the default value.
61+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 0
62+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingAfter 1
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
/*
4+
* Non-backed enums have no colon and are ignored.
5+
*/
6+
enum NonBacked {}
7+
enum NonBackedImplements implements Bar {}
8+
enum NonBackedWithBody {
9+
public function label() :string { // Colons unrelated to the backed enum declaration colon are ignored.
10+
return 'label';
11+
}
12+
}
13+
14+
/*
15+
* Valid spacing (using the default property values).
16+
*/
17+
enum ValidInt: int {}
18+
enum ValidImplements: string implements HasLabel {}
19+
enum
20+
/* comment */
21+
ValidName: string
22+
{}
23+
24+
/*
25+
* Invalid spacing (using the default property values).
26+
*/
27+
enum SpaceBefore: string {}
28+
enum NoSpaceAfter: string {}
29+
enum MultipleSpacesAfter: string {}
30+
enum SpaceBeforeNoneAfter: string {}
31+
enum MultipleSpacesBeforeAndAfter: string {} // Error x 2.
32+
enum BadImplements: string implements Foo {} // Error x 2.
33+
enum NewlineBefore: string {}
34+
enum NewlineAfter: string {}
35+
36+
// No auto-fixing when there are comments around the colon.
37+
enum CommentBefore /* comment */: string {}
38+
enum CommentAfter: /* comment */string {}
39+
40+
/*
41+
* Test non-default property values.
42+
*/
43+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 1
44+
enum CustomBeforeOk : string {} // OK.
45+
enum CustomBeforeError : string {} // Error.
46+
// Resetting to the default value.
47+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 0
48+
49+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingAfter 0
50+
enum CustomAfterOk:string {} // OK.
51+
enum CustomAfterError:string {} // Error.
52+
53+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 2
54+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingAfter 3
55+
enum CustomBothOk : string {} // OK.
56+
enum CustomBothError : string {} // Error x 2.
57+
// Resetting to the default value.
58+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingBefore 0
59+
// phpcs:set Universal.WhiteSpace.BackedEnumColonSpacing spacingAfter 1
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// Intentional parse error (nothing after the enum name).
4+
// This should be the only test in the file.
5+
6+
enum LiveCoding
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// Intentional parse error (nothing after the enum backing type).
4+
// This should be the only test in the file.
5+
6+
enum LiveCoding: string
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// Intentional parse error (a backed enum without a backing type with a space before the colon and no space after it).
4+
// This should be the only test in the file.
5+
6+
enum SpaceBeforeNoBackingType :{}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// Intentional parse error (a backed enum without a backing type with a space before the colon and no space after it).
4+
// This should be the only test in the file.
5+
6+
enum SpaceBeforeNoBackingType:{}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// Intentional parse error (a backed enum without a backing type with two spaces after the colon).
4+
// This should be the only test in the file.
5+
6+
enum SpaceAfterNoBackingType: {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// Intentional parse error (a backed enum missing a backing type).
4+
// This should be the only test in the file.
5+
6+
enum NoBackingTypeWithImplements: implements Foo {}

0 commit comments

Comments
 (0)