From a6c9e2fd626ff63341bbaac0098c8ea0ea11129b Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 20 Nov 2018 04:36:03 +0100 Subject: [PATCH] Generic/SpaceAfterCast: make the sniff configurable & deprecates NoSpaceAfterCast This PR makes the `Generic.Formatting.SpaceAfterCast` sniff configurable as discussed in PR 2057 and deprecates the `Generic.Formatting.NoSpaceAfterCast` sniff. Notes: * Adds a public `spacing` property. Defaults to `1` to maintain BC. * Adds a public `ignoreNewLines` property. Defaults to `false` to maintain BC. * Adds a new non-fixable `CommentFound` error for when non-whitespace tokens are found between the type cast and the next non-empty token. * Adds a new fixable `TooLittleSpace` error code for when `$spacing` is set to more than `1` and there is whitespace, but not enough. * Adjusts the error message for the `NoSpace` and `TooMuchSpace` errors to allow for the flexibility now needed, what with the configurable `spacing` property. Note: the error **codes** have not been changed to prevent a BC-break. * Makes the fixer more efficient compared to before. Previously, if a whitespace + new line + whitespace would be found with `spacing` set to `1`, the fixer would need three loops to apply all the fixes, now this is fixed in one go. Includes additional unit tests. --- .../Formatting/NoSpaceAfterCastSniff.php | 3 + .../Sniffs/Formatting/SpaceAfterCastSniff.php | 112 ++++++++++++++++-- .../Formatting/SpaceAfterCastUnitTest.inc | 48 +++++++- .../SpaceAfterCastUnitTest.inc.fixed | 41 +++++++ .../Formatting/SpaceAfterCastUnitTest.php | 15 +++ 5 files changed, 204 insertions(+), 15 deletions(-) diff --git a/src/Standards/Generic/Sniffs/Formatting/NoSpaceAfterCastSniff.php b/src/Standards/Generic/Sniffs/Formatting/NoSpaceAfterCastSniff.php index 1e0e5761c4..850cb9177e 100644 --- a/src/Standards/Generic/Sniffs/Formatting/NoSpaceAfterCastSniff.php +++ b/src/Standards/Generic/Sniffs/Formatting/NoSpaceAfterCastSniff.php @@ -5,6 +5,9 @@ * @author Greg Sherwood * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + * + * @deprecated 3.4.0 Use the Generic.Formatting.SpaceAfterCast sniff with + * the $spacing property set to 0 instead. */ namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting; diff --git a/src/Standards/Generic/Sniffs/Formatting/SpaceAfterCastSniff.php b/src/Standards/Generic/Sniffs/Formatting/SpaceAfterCastSniff.php index ddf2e123bf..c4c7b2e862 100644 --- a/src/Standards/Generic/Sniffs/Formatting/SpaceAfterCastSniff.php +++ b/src/Standards/Generic/Sniffs/Formatting/SpaceAfterCastSniff.php @@ -16,6 +16,20 @@ class SpaceAfterCastSniff implements Sniff { + /** + * The number of spaces desired after a cast token. + * + * @var integer + */ + public $spacing = 1; + + /** + * Allow newlines instead of spaces. + * + * @var boolean + */ + public $ignoreNewlines = false; + /** * Returns an array of tokens this test wants to listen for. @@ -40,7 +54,8 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { - $tokens = $phpcsFile->getTokens(); + $tokens = $phpcsFile->getTokens(); + $this->spacing = (int) $this->spacing; if ($tokens[$stackPtr]['code'] === T_BINARY_CAST && $tokens[$stackPtr]['content'] === 'b' @@ -49,24 +64,95 @@ public function process(File $phpcsFile, $stackPtr) return; } - if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { - $error = 'A cast statement must be followed by a single space'; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpace'); - if ($fix === true) { - $phpcsFile->fixer->addContent($stackPtr, ' '); - } + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($nextNonEmpty === false) { + return; + } + + if ($this->ignoreNewlines === true + && $tokens[$stackPtr]['line'] !== $tokens[$nextNonEmpty]['line'] + ) { + $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', 'newline'); + return; + } + if ($this->spacing === 0 && $nextNonEmpty === ($stackPtr + 1)) { $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', 0); return; } - $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', $tokens[($stackPtr + 1)]['length']); + $maybePlural = ''; + if ($this->spacing !== 1) { + $maybePlural = 's'; + } + + $nextNonWhitespace = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); + if ($nextNonEmpty !== $nextNonWhitespace) { + $error = 'Expected %s space%s after cast statement; comment found'; + $data = [ + $this->spacing, + $maybePlural, + ]; + $phpcsFile->addError($error, $stackPtr, 'CommentFound', $data); + + if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) { + $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', $tokens[($stackPtr + 1)]['length']); + } else { + $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', 0); + } + + return; + } + + $found = 0; + if ($tokens[$stackPtr]['line'] !== $tokens[$nextNonEmpty]['line']) { + $found = 'newline'; + } else if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) { + $found = $tokens[($stackPtr + 1)]['length']; + } + + $phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', $found); + + if ($found === $this->spacing) { + return; + } + + $error = 'Expected %s space%s after cast statement; %s found'; + $data = [ + $this->spacing, + $maybePlural, + $found, + ]; + + $errorCode = 'TooMuchSpace'; + if ($this->spacing !== 0) { + if ($found === 0) { + $errorCode = 'NoSpace'; + } else if ($found !== 'newline' && $found < $this->spacing) { + $errorCode = 'TooLittleSpace'; + } + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data); + + if ($fix === true) { + $padding = str_repeat(' ', $this->spacing); + if ($found === 0) { + $phpcsFile->fixer->addContent($stackPtr, $padding); + } else { + $phpcsFile->fixer->beginChangeset(); + $start = ($stackPtr + 1); + + if ($this->spacing > 0) { + $phpcsFile->fixer->replaceToken($start, $padding); + ++$start; + } + + for ($i = $start; $i < $nextNonWhitespace; $i++) { + $phpcsFile->fixer->replaceToken($i, ''); + } - if ($tokens[($stackPtr + 1)]['length'] !== 1) { - $error = 'A cast statement must be followed by a single space'; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'TooMuchSpace'); - if ($fix === true) { - $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' '); + $phpcsFile->fixer->endChangeset(); } } diff --git a/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc b/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc index f41b9a84e8..70ecd98a12 100644 --- a/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc +++ b/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc @@ -10,7 +10,7 @@ $var = (integer) $var2; $var = (string) $var2; $var = (string)$var2; -$var = (string) $var2; +$var = (string) $var2; $var = (float) $var2; $var = (float)$var2; @@ -42,10 +42,54 @@ $var = (object) $var2; $var = (unset) $var2; $var = (unset)$var2; -$var = (unset) $var2; +$var = (unset) $var2; $var = b"binary $foo"; $var = b"binary string"; $var = b'binary string'; $var = (binary) $string; $var = (binary)$string; + +$var = (boolean) /* comment */ $var2; + +$var = (int) + $var2; + +if ( (string) // phpcs:ignore Standard.Cat.SniffName -- for reasons. + $x === 'test' +) {} + +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines true +$var = (int) + $var1 + (bool) $var2; + +if ( (string) // phpcs:ignore Standard.Cat.SniffName -- for reasons. + $x === 'test' +) {} +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines false + +// phpcs:set Generic.Formatting.SpaceAfterCast spacing 2 +$var = (int) $var2; +$var = (string)$var2; +$var = (array) $var2; +$var = (unset) $var2; +$var = (boolean) /* comment */ $var2; + +$var = (integer) + $var2; + +// phpcs:set Generic.Formatting.SpaceAfterCast spacing 0 +$var = (int) $var2; +$var = (string)$var2; +$var = (array) $var2; +$var = (unset) $var2; +$var = (boolean) /* comment */ $var2; + +$var = (integer) + $var2; + +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines true +$var = (int) + $var1 + (bool) $var2; +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines false +// phpcs:set Generic.Formatting.SpaceAfterCast spacing 1 diff --git a/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc.fixed b/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc.fixed index 21a8657c78..f9b2a592aa 100644 --- a/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc.fixed +++ b/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.inc.fixed @@ -49,3 +49,44 @@ $var = b"binary string"; $var = b'binary string'; $var = (binary) $string; $var = (binary) $string; + +$var = (boolean) /* comment */ $var2; + +$var = (int) $var2; + +if ( (string) // phpcs:ignore Standard.Cat.SniffName -- for reasons. + $x === 'test' +) {} + +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines true +$var = (int) + $var1 + (bool) $var2; + +if ( (string) // phpcs:ignore Standard.Cat.SniffName -- for reasons. + $x === 'test' +) {} +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines false + +// phpcs:set Generic.Formatting.SpaceAfterCast spacing 2 +$var = (int) $var2; +$var = (string) $var2; +$var = (array) $var2; +$var = (unset) $var2; +$var = (boolean) /* comment */ $var2; + +$var = (integer) $var2; + +// phpcs:set Generic.Formatting.SpaceAfterCast spacing 0 +$var = (int)$var2; +$var = (string)$var2; +$var = (array)$var2; +$var = (unset)$var2; +$var = (boolean) /* comment */ $var2; + +$var = (integer)$var2; + +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines true +$var = (int) + $var1 + (bool)$var2; +// phpcs:set Generic.Formatting.SpaceAfterCast ignoreNewlines false +// phpcs:set Generic.Formatting.SpaceAfterCast spacing 1 diff --git a/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.php b/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.php index 03a364ac68..505ded6c51 100644 --- a/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.php +++ b/src/Standards/Generic/Tests/Formatting/SpaceAfterCastUnitTest.php @@ -49,6 +49,21 @@ public function getErrorList() 44 => 1, 45 => 1, 51 => 1, + 53 => 1, + 55 => 1, + 58 => 1, + 64 => 1, + 72 => 1, + 73 => 1, + 75 => 1, + 76 => 1, + 78 => 1, + 82 => 1, + 84 => 1, + 85 => 1, + 86 => 1, + 88 => 1, + 93 => 1, ]; }//end getErrorList()