From c8eac760b5dde665761d61e83a9a74160d209775 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 7 Jul 2026 18:13:57 +0000 Subject: [PATCH] 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 --- .../WhiteSpace/FnKeywordSpacingStandard.xml | 23 ++++++ .../WhiteSpace/FnKeywordSpacingSniff.php | 73 +++++++++++++++++++ .../WhiteSpace/FnKeywordSpacingUnitTest.1.inc | 31 ++++++++ .../FnKeywordSpacingUnitTest.1.inc.fixed | 30 ++++++++ .../WhiteSpace/FnKeywordSpacingUnitTest.php | 52 +++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 Universal/Docs/WhiteSpace/FnKeywordSpacingStandard.xml create mode 100644 Universal/Sniffs/WhiteSpace/FnKeywordSpacingSniff.php create mode 100644 Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc create mode 100644 Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc.fixed create mode 100644 Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.php diff --git a/Universal/Docs/WhiteSpace/FnKeywordSpacingStandard.xml b/Universal/Docs/WhiteSpace/FnKeywordSpacingStandard.xml new file mode 100644 index 0000000..6427176 --- /dev/null +++ b/Universal/Docs/WhiteSpace/FnKeywordSpacingStandard.xml @@ -0,0 +1,23 @@ + + + + + + + + ($x) => $x + 1; + ]]> + + + ($x) => $x + 1; + ]]> + + + diff --git a/Universal/Sniffs/WhiteSpace/FnKeywordSpacingSniff.php b/Universal/Sniffs/WhiteSpace/FnKeywordSpacingSniff.php new file mode 100644 index 0000000..035bd65 --- /dev/null +++ b/Universal/Sniffs/WhiteSpace/FnKeywordSpacingSniff.php @@ -0,0 +1,73 @@ + + */ + public function register() + { + return [\T_FN]; + } + + /** + * Processes this sniff, when one of its tokens is encountered. + * + * @since 1.6.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + SpacesFixer::checkAndFix( + $phpcsFile, + $stackPtr, + $nextNonEmpty, + (int) $this->spacingAfterKeyword, + 'Expected %s after the "fn" keyword. Found: %s.', + 'Incorrect', + 'error', + 0, + 'Spacing after the arrow function "fn" keyword' + ); + } +} diff --git a/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc b/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc new file mode 100644 index 0000000..81e1964 --- /dev/null +++ b/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc @@ -0,0 +1,31 @@ + $x + 1; +$fn = fn&() => doSomething(); +$fn = static fn($x) => fn($y) => $x + $y; + +/* + * Invalid spacing. + */ + +$fn = fn ($x) => $x + 1; +$fn = fn &($x) => $x + 1; +$fn = static fn + ($x) => $x + 1; +$fn = fn () => doSomething(); + +$fn = fn ($x) => fn ($y) => $x + $y; + +$fn = fn + /* comment */ + ($x) => $x + 1; + +// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 1 +$fn = fn($x) => $x + 1; // Error. +$fn = fn ($x) => $x + 1; // OK. +// Resetting to the default value. +// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 0 diff --git a/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc.fixed b/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc.fixed new file mode 100644 index 0000000..cb6a55d --- /dev/null +++ b/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.1.inc.fixed @@ -0,0 +1,30 @@ + $x + 1; +$fn = fn&() => doSomething(); +$fn = static fn($x) => fn($y) => $x + $y; + +/* + * Invalid spacing. + */ + +$fn = fn($x) => $x + 1; +$fn = fn&($x) => $x + 1; +$fn = static fn($x) => $x + 1; +$fn = fn() => doSomething(); + +$fn = fn($x) => fn($y) => $x + $y; + +$fn = fn + /* comment */ + ($x) => $x + 1; + +// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 1 +$fn = fn ($x) => $x + 1; // Error. +$fn = fn ($x) => $x + 1; // OK. +// Resetting to the default value. +// phpcs:set Universal.WhiteSpace.FnKeywordSpacing spacingAfterKeyword 0 diff --git a/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.php b/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.php new file mode 100644 index 0000000..37cfee3 --- /dev/null +++ b/Universal/Tests/WhiteSpace/FnKeywordSpacingUnitTest.php @@ -0,0 +1,52 @@ + Key is the line number, value is the number of expected errors. + */ + public function getErrorList() + { + return [ + 15 => 1, + 16 => 1, + 17 => 1, + 19 => 1, + 21 => 2, + 23 => 1, + 28 => 1, + ]; + } + + /** + * Returns the lines where warnings should occur. + * + * @return array Key is the line number, value is the number of expected warnings. + */ + public function getWarningList() + { + return []; + } +}