Skip to content

Commit c8eac76

Browse files
committed
Universal/FnKeywordSpacing: new sniff to enforce no space after the fn keyword
Add the `Universal.WhiteSpace.FnKeywordSpacing` sniff which enforces that the `fn` keyword of an arrow function is not succeeded by a space, as required by PER Coding Style 2.0 (section 7.1). The amount of spacing is configurable via the `spacingAfterKeyword` property (default 0). Fixes 441. Ref: https://github.com/php-fig/per-coding-style/blob/2.0.0/spec.md#71-short-closures
1 parent cfdda7d commit c8eac76

5 files changed

Lines changed: 209 additions & 0 deletions

File tree

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="Fn Keyword Spacing"
5+
>
6+
<standard>
7+
<![CDATA[
8+
The `fn` keyword of an arrow function should not be succeeded by a space.
9+
]]>
10+
</standard>
11+
<code_comparison>
12+
<code title="Valid: No space after the `fn` keyword.">
13+
<![CDATA[
14+
$fn = fn<em></em>($x) => $x + 1;
15+
]]>
16+
</code>
17+
<code title="Invalid: Space after the `fn` keyword.">
18+
<![CDATA[
19+
$fn = fn<em> </em>($x) => $x + 1;
20+
]]>
21+
</code>
22+
</code_comparison>
23+
</documentation>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
* Enforce the spacing after the "fn" keyword of arrow functions.
20+
*
21+
* @since 1.6.0
22+
*/
23+
final class FnKeywordSpacingSniff implements Sniff
24+
{
25+
26+
/**
27+
* The number of spaces to demand after the "fn" keyword.
28+
*
29+
* @since 1.6.0
30+
*
31+
* @var int
32+
*/
33+
public $spacingAfterKeyword = 0;
34+
35+
/**
36+
* Registers the tokens that this sniff wants to listen for.
37+
*
38+
* @since 1.6.0
39+
*
40+
* @return array<int|string>
41+
*/
42+
public function register()
43+
{
44+
return [\T_FN];
45+
}
46+
47+
/**
48+
* Processes this sniff, when one of its tokens is encountered.
49+
*
50+
* @since 1.6.0
51+
*
52+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
53+
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
54+
*
55+
* @return void
56+
*/
57+
public function process(File $phpcsFile, $stackPtr)
58+
{
59+
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
60+
61+
SpacesFixer::checkAndFix(
62+
$phpcsFile,
63+
$stackPtr,
64+
$nextNonEmpty,
65+
(int) $this->spacingAfterKeyword,
66+
'Expected %s after the "fn" keyword. Found: %s.',
67+
'Incorrect',
68+
'error',
69+
0,
70+
'Spacing after the arrow function "fn" keyword'
71+
);
72+
}
73+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* Valid spacing.
5+
*/
6+
7+
$fn = fn($x) => $x + 1;
8+
$fn = fn&() => doSomething();
9+
$fn = static fn($x) => fn($y) => $x + $y;
10+
11+
/*
12+
* Invalid spacing.
13+
*/
14+
15+
$fn = fn ($x) => $x + 1;
16+
$fn = fn &($x) => $x + 1;
17+
$fn = static fn
18+
($x) => $x + 1;
19+
$fn = fn () => doSomething();
20+
21+
$fn = fn ($x) => fn ($y) => $x + $y;
22+
23+
$fn = fn
24+
/* comment */
25+
($x) => $x + 1;
26+
27+
// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 1
28+
$fn = fn($x) => $x + 1; // Error.
29+
$fn = fn ($x) => $x + 1; // OK.
30+
// Resetting to the default value.
31+
// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 0
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* Valid spacing.
5+
*/
6+
7+
$fn = fn($x) => $x + 1;
8+
$fn = fn&() => doSomething();
9+
$fn = static fn($x) => fn($y) => $x + $y;
10+
11+
/*
12+
* Invalid spacing.
13+
*/
14+
15+
$fn = fn($x) => $x + 1;
16+
$fn = fn&($x) => $x + 1;
17+
$fn = static fn($x) => $x + 1;
18+
$fn = fn() => doSomething();
19+
20+
$fn = fn($x) => fn($y) => $x + $y;
21+
22+
$fn = fn
23+
/* comment */
24+
($x) => $x + 1;
25+
26+
// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 1
27+
$fn = fn ($x) => $x + 1; // Error.
28+
$fn = fn ($x) => $x + 1; // OK.
29+
// Resetting to the default value.
30+
// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 0
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\Tests\WhiteSpace;
12+
13+
use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;
14+
15+
/**
16+
* Unit test class for the FnKeywordSpacing sniff.
17+
*
18+
* @covers PHPCSExtra\Universal\Sniffs\WhiteSpace\FnKeywordSpacingSniff
19+
*
20+
* @since 1.6.0
21+
*/
22+
final class FnKeywordSpacingUnitTest extends AbstractSniffTestCase
23+
{
24+
25+
/**
26+
* Returns the lines where errors should occur.
27+
*
28+
* @return array<int, int> Key is the line number, value is the number of expected errors.
29+
*/
30+
public function getErrorList()
31+
{
32+
return [
33+
15 => 1,
34+
16 => 1,
35+
17 => 1,
36+
19 => 1,
37+
21 => 2,
38+
23 => 1,
39+
28 => 1,
40+
];
41+
}
42+
43+
/**
44+
* Returns the lines where warnings should occur.
45+
*
46+
* @return array<int, int> Key is the line number, value is the number of expected warnings.
47+
*/
48+
public function getWarningList()
49+
{
50+
return [];
51+
}
52+
}

0 commit comments

Comments
 (0)