diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionSpacingSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionSpacingSniff.php index c0cbed2268..a213a94af1 100644 --- a/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionSpacingSniff.php +++ b/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionSpacingSniff.php @@ -116,14 +116,36 @@ public function process(File $phpcsFile, $stackPtr) $prev = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true); - while ($tokens[$prev]['code'] === T_ATTRIBUTE_END) { - // Skip past function attributes. - $prev = $phpcsFile->findPrevious($ignore, ($tokens[$prev]['attribute_opener'] - 1), null, true); + $startOfDeclarationLine = $phpcsFile->findNext(T_WHITESPACE, ($prev + 1), null, true); + for ($i = $startOfDeclarationLine; $i >= 0; $i--) { + if ($tokens[$i]['line'] === $tokens[$startOfDeclarationLine]['line']) { + $startOfDeclarationLine = $i; + continue; + } + + break; } - if ($tokens[$prev]['code'] === T_DOC_COMMENT_CLOSE_TAG) { - // Skip past function docblocks. - $prev = $phpcsFile->findPrevious($ignore, ($tokens[$prev]['comment_opener'] - 1), null, true); + // Skip past function docblocks and attributes. + $prev = $startOfDeclarationLine; + if ($startOfDeclarationLine > 0) { + for ($prev = ($startOfDeclarationLine - 1); $prev > 0; $prev--) { + if ($tokens[$prev]['code'] === T_WHITESPACE) { + continue; + } + + if ($tokens[$prev]['code'] === T_DOC_COMMENT_CLOSE_TAG) { + $prev = $tokens[$prev]['comment_opener']; + continue; + } + + if ($tokens[$prev]['code'] === T_ATTRIBUTE_END) { + $prev = $tokens[$prev]['attribute_opener']; + continue; + } + + break; + } } if ($tokens[$prev]['code'] === T_OPEN_CURLY_BRACKET) { @@ -224,9 +246,11 @@ public function process(File $phpcsFile, $stackPtr) before the function. */ + $startOfPreamble = $phpcsFile->findNext(T_WHITESPACE, ($prev + 1), null, true); + $prevLineToken = null; - for ($i = $stackPtr; $i >= 0; $i--) { - if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) { + for ($i = $startOfPreamble; $i >= 0; $i--) { + if ($tokens[$i]['line'] === $tokens[$startOfPreamble]['line']) { continue; } @@ -241,33 +265,15 @@ public function process(File $phpcsFile, $stackPtr) $prevContent = 0; $prevLineToken = 0; } else { - $currentLine = $tokens[$stackPtr]['line']; - - $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, $prevLineToken, null, true); - - if ($tokens[$prevContent]['code'] === T_COMMENT - || isset(Tokens::$phpcsCommentTokens[$tokens[$prevContent]['code']]) === true + $firstBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($startOfDeclarationLine - 1), null, true); + if ($tokens[$firstBefore]['code'] === T_COMMENT + || isset(Tokens::$phpcsCommentTokens[$tokens[$firstBefore]['code']]) === true ) { // Ignore comments as they can have different spacing rules, and this // isn't a proper function comment anyway. return; } - while ($tokens[$prevContent]['code'] === T_ATTRIBUTE_END - && $tokens[$prevContent]['line'] === ($currentLine - 1) - ) { - // Account for function attributes. - $currentLine = $tokens[$tokens[$prevContent]['attribute_opener']]['line']; - $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($tokens[$prevContent]['attribute_opener'] - 1), null, true); - } - - if ($tokens[$prevContent]['code'] === T_DOC_COMMENT_CLOSE_TAG - && $tokens[$prevContent]['line'] === ($currentLine - 1) - ) { - // Account for function comments. - $prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($tokens[$prevContent]['comment_opener'] - 1), null, true); - } - // Before we throw an error, check that we are not throwing an error // for another function. We don't want to error for no blank lines after // the previous function and no blank lines before this one as well. @@ -278,38 +284,33 @@ public function process(File $phpcsFile, $stackPtr) $stopAt = array_pop($conditions); } - $prevLineToken = $prevContent; - $prevLine = ($tokens[$prevContent]['line'] - 1); - $i = ($stackPtr - 1); - $foundLines = 0; + $currentLine = $tokens[$startOfPreamble]['line']; + $prevContent = $prev; + $prevLine = ($tokens[$prevContent]['line'] - 1); + $foundLines = ($currentLine - $tokens[$prevContent]['line'] - 1); + + for ($i = $prevContent; $i > $stopAt; $i--) { + if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET) { + if (isset($tokens[$i]['scope_condition']) === true + && $tokens[$tokens[$i]['scope_condition']]['code'] === T_FUNCTION + ) { + // Found a previous function. + return; + } else { + break; + } + } - while ($currentLine !== $prevLine && $currentLine > 1 && $i > $stopAt) { if ($tokens[$i]['code'] === T_FUNCTION) { // Found another interface or abstract function. return; } - if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET - && $tokens[$tokens[$i]['scope_condition']]['code'] === T_FUNCTION - ) { - // Found a previous function. - return; - } - $currentLine = $tokens[$i]['line']; if ($currentLine === $prevLine) { break; } - - if ($tokens[($i - 1)]['line'] < $currentLine && $tokens[($i + 1)]['line'] > $currentLine) { - // This token is on a line by itself. If it is whitespace, the line is empty. - if ($tokens[$i]['code'] === T_WHITESPACE) { - $foundLines++; - } - } - - $i--; - }//end while + }//end for }//end if $requiredSpacing = $this->spacing; diff --git a/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc b/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc index 581235e14c..c516775cc6 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc +++ b/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc @@ -593,3 +593,136 @@ function a() { */ function b() { } + + +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingBeforeFirst 1 +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingAfterLast 0 + +class DocblockFollowedByAttributesCorrectSpacing { + + /** + * No error. + */ + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + function FirstFunction() + { + // Code + } +} + +class DocblockFollowedByAttributesTooMuchSpacing { + + + + /** + * Docblock. + */ + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + function FirstFunction() + { + // Code + } +} + +class DocblockFollowedByAttributesTooLittleSpacing { + /** + * Docblock. + */ + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + function FirstFunction() + { + // Code + } +} + +class DocblockPrecededByAttributesCorrectSpacing { + + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + /** + * No error. + */ + function FirstFunction() + { + // Code + } +} + +class DocblockPrecededByAttributesTooMuchSpacing { + + + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + /** + * Docblock. + */ + function FirstFunction() + { + // Code + } +} + +class DocblockPrecededByAttributesTooLittleSpacing { + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + /** + * Docblock. + */ + function FirstFunction() + { + // Code + } +} + +// Reset properties to their default value. +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingBeforeFirst 2 +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingAfterLast 2 + +class SilenceBeforeErrorIfPreviousThingWasAFunctionBug +{ + /** + * Docblock. + */ + + #[ReturnTypeWillChange] + + + + + + #[ + + AnotherAttribute + + ]#[AndAThirdAsWell] + + public function blankLineDetectionA() + { + + }//end blankLineDetectionA() + + /** + * Docblock. + */ + #[ReturnTypeWillChange] + + public function blankLineDetectionB() + { + + }//end blankLineDetectionB() +}//end class diff --git a/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc.fixed b/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc.fixed index f1b979326a..289b2b6840 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc.fixed +++ b/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.1.inc.fixed @@ -685,3 +685,140 @@ function a() { */ function b() { } + + +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingBeforeFirst 1 +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingAfterLast 0 + +class DocblockFollowedByAttributesCorrectSpacing { + + /** + * No error. + */ + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + function FirstFunction() + { + // Code + } +} + +class DocblockFollowedByAttributesTooMuchSpacing { + + /** + * Docblock. + */ + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + function FirstFunction() + { + // Code + } +} + +class DocblockFollowedByAttributesTooLittleSpacing { + + /** + * Docblock. + */ + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + function FirstFunction() + { + // Code + } +} + +class DocblockPrecededByAttributesCorrectSpacing { + + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + /** + * No error. + */ + function FirstFunction() + { + // Code + } +} + +class DocblockPrecededByAttributesTooMuchSpacing { + + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + /** + * Docblock. + */ + function FirstFunction() + { + // Code + } +} + +class DocblockPrecededByAttributesTooLittleSpacing { + + #[AttributesShouldBeJumpedOver] + #[ + ASecondAttributeShouldBeJumpedOverToo + ]#[AndAThirdAsWell] + /** + * Docblock. + */ + function FirstFunction() + { + // Code + } +} + +// Reset properties to their default value. +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingBeforeFirst 2 +// phpcs:set Squiz.WhiteSpace.FunctionSpacing spacingAfterLast 2 + +class SilenceBeforeErrorIfPreviousThingWasAFunctionBug +{ + + + /** + * Docblock. + */ + + #[ReturnTypeWillChange] + + + + + + #[ + + AnotherAttribute + + ]#[AndAThirdAsWell] + + public function blankLineDetectionA() + { + + }//end blankLineDetectionA() + + + /** + * Docblock. + */ + #[ReturnTypeWillChange] + + public function blankLineDetectionB() + { + + }//end blankLineDetectionB() + + +}//end class diff --git a/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.php b/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.php index 17c431ee4b..7bc264a67b 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.php +++ b/src/Standards/Squiz/Tests/WhiteSpace/FunctionSpacingUnitTest.php @@ -101,8 +101,15 @@ public function getErrorList($testFile='') 560 => 1, 566 => 1, 580 => 2, - 583 => 3, + 583 => 4, 591 => 1, + 627 => 1, + 641 => 1, + 672 => 1, + 686 => 1, + 714 => 1, + 717 => 1, + 727 => 1, ]; case 'FunctionSpacingUnitTest.2.inc':