From 7465e39f105ee8c52ded4452f72cb8ed6f07e489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0t=C3=ADpek?= Date: Sun, 25 May 2025 17:35:40 +0200 Subject: [PATCH] SlevomatCodingStandard.Classes.ClassMemberSpacing: Prevent deleting unexpected code/comments --- .../Classes/ClassMemberSpacingSniff.php | 33 ++++++++++++------- .../Classes/ClassMemberSpacingSniffTest.php | 22 ++++++++++--- .../data/classMemberSpacingErrors.fixed.php | 10 +++++- .../Classes/data/classMemberSpacingErrors.php | 12 ++++++- .../classMemberSpacingLiveCodingErrors.php | 13 ++++++++ .../data/classMemberSpacingNoErrors.php | 7 +++- 6 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 tests/Sniffs/Classes/data/classMemberSpacingLiveCodingErrors.php diff --git a/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.php b/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.php index 443d45361..e2eebd35c 100644 --- a/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.php +++ b/SlevomatCodingStandard/Sniffs/Classes/ClassMemberSpacingSniff.php @@ -63,7 +63,6 @@ public function process(File $phpcsFile, $classPointer): void $tokens = $phpcsFile->getTokens(); $memberPointer = null; - $previousMemberPointer = null; do { $previousMemberPointer = $memberPointer; @@ -105,7 +104,7 @@ public function process(File $phpcsFile, $classPointer): void } } - $memberStartPointer = $this->getMemberStartPointer($phpcsFile, $memberPointer); + $memberStartPointer = $this->getMemberStartPointer($phpcsFile, $memberPointer, $previousMemberEndPointer); $actualLinesCount = $tokens[$memberStartPointer]['line'] - $tokens[$previousMemberEndPointer]['line'] - 1; @@ -117,11 +116,24 @@ public function process(File $phpcsFile, $classPointer): void ? 'Expected 1 blank line between class members, found %2$d.' : 'Expected %1$d blank lines between class members, found %2$d.'; - $fix = $phpcsFile->addFixableError( + $firstPointerOnMemberLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $memberStartPointer); + $nonWhitespaceBetweenMembersPointer = TokenHelper::findNextNonWhitespace( + $phpcsFile, + $previousMemberEndPointer + 1, + $firstPointerOnMemberLine, + ); + $errorParameters = [ sprintf($errorMessage, $this->linesCountBetweenMembers, $actualLinesCount), $memberPointer, self::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS, - ); + ]; + + if ($nonWhitespaceBetweenMembersPointer !== null) { + $phpcsFile->addError(...$errorParameters); + continue; + } + + $fix = $phpcsFile->addFixableError(...$errorParameters); if (!$fix) { continue; } @@ -131,8 +143,6 @@ public function process(File $phpcsFile, $classPointer): void $this->linesCountBetweenMembers + ($hasCommentWithNewLineAfterPreviousMember ? 0 : 1), ); - $firstPointerOnMemberLine = TokenHelper::findFirstTokenOnLine($phpcsFile, $memberStartPointer); - $phpcsFile->fixer->beginChangeset(); $phpcsFile->fixer->addContent($previousMemberEndPointer, $newLines); @@ -192,13 +202,17 @@ private function findNextMember(File $phpcsFile, int $classPointer, int $previou return $memberPointer; } - private function getMemberStartPointer(File $phpcsFile, int $memberPointer): int + private function getMemberStartPointer(File $phpcsFile, int $memberPointer, int $previousMemberEndPointer): int { $tokens = $phpcsFile->getTokens(); $memberFirstCodePointer = $this->getMemberFirstCodePointer($phpcsFile, $memberPointer); do { + if ($memberFirstCodePointer <= $previousMemberEndPointer) { + return TokenHelper::findNextNonWhitespace($phpcsFile, $memberFirstCodePointer + 1); + } + $pointerBefore = TokenHelper::findPreviousNonWhitespace($phpcsFile, $memberFirstCodePointer - 1); if ($tokens[$pointerBefore]['code'] === T_ATTRIBUTE_END) { @@ -206,10 +220,7 @@ private function getMemberStartPointer(File $phpcsFile, int $memberPointer): int continue; } - if ( - in_array($tokens[$pointerBefore]['code'], Tokens::$commentTokens, true) - && $tokens[$pointerBefore]['line'] + 1 === $tokens[$memberFirstCodePointer]['line'] - ) { + if (in_array($tokens[$pointerBefore]['code'], Tokens::$commentTokens, true)) { $pointerBeforeComment = TokenHelper::findPreviousEffective($phpcsFile, $pointerBefore - 1); if ($tokens[$pointerBeforeComment]['line'] !== $tokens[$pointerBefore]['line']) { $memberFirstCodePointer = array_key_exists('comment_opener', $tokens[$pointerBefore]) diff --git a/tests/Sniffs/Classes/ClassMemberSpacingSniffTest.php b/tests/Sniffs/Classes/ClassMemberSpacingSniffTest.php index 1debdba6a..8361f63c6 100644 --- a/tests/Sniffs/Classes/ClassMemberSpacingSniffTest.php +++ b/tests/Sniffs/Classes/ClassMemberSpacingSniffTest.php @@ -17,7 +17,7 @@ public function testErrors(): void { $report = self::checkFile(__DIR__ . '/data/classMemberSpacingErrors.php'); - self::assertSame(9, $report->getErrorCount()); + self::assertSame(10, $report->getErrorCount()); self::assertSniffError($report, 15, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); self::assertSniffError($report, 21, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); @@ -26,8 +26,9 @@ public function testErrors(): void self::assertSniffError($report, 44, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); self::assertSniffError($report, 47, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); self::assertSniffError($report, 50, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); - self::assertSniffError($report, 60, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); - self::assertSniffError($report, 69, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); + self::assertSniffError($report, 58, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); + self::assertSniffError($report, 70, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); + self::assertSniffError($report, 79, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); self::assertAllFixedInFile($report); } @@ -38,10 +39,21 @@ public function testErrorsWithModifiedLinesCount(): void 'linesCountBetweenMembers' => 2, ]); - self::assertSame(2, $report->getErrorCount()); + self::assertSame(3, $report->getErrorCount()); self::assertSniffError($report, 21, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); - self::assertSniffError($report, 69, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); + self::assertSniffError($report, 58, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); + self::assertSniffError($report, 79, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); + } + + public function testErrorsDuringLiveCoding(): void + { + $report = self::checkFile(__DIR__ . '/data/classMemberSpacingLiveCodingErrors.php'); + + self::assertSame(1, $report->getErrorCount()); + self::assertSame(0, $report->getFixableCount()); + + self::assertSniffError($report, 11, ClassMemberSpacingSniff::CODE_INCORRECT_COUNT_OF_BLANK_LINES_BETWEEN_MEMBERS); } } diff --git a/tests/Sniffs/Classes/data/classMemberSpacingErrors.fixed.php b/tests/Sniffs/Classes/data/classMemberSpacingErrors.fixed.php index 1b4f10f80..2a1947cfb 100644 --- a/tests/Sniffs/Classes/data/classMemberSpacingErrors.fixed.php +++ b/tests/Sniffs/Classes/data/classMemberSpacingErrors.fixed.php @@ -42,7 +42,15 @@ public function third() final const THIRD = 'third'; - readonly int $forth; + readonly int $fourth; + + /** + * @return void + */ + + public function fifth() + { + } } diff --git a/tests/Sniffs/Classes/data/classMemberSpacingErrors.php b/tests/Sniffs/Classes/data/classMemberSpacingErrors.php index 8ccc1032a..a993bba45 100644 --- a/tests/Sniffs/Classes/data/classMemberSpacingErrors.php +++ b/tests/Sniffs/Classes/data/classMemberSpacingErrors.php @@ -47,7 +47,17 @@ public function third() final const THIRD = 'third'; - readonly int $forth; + readonly int $fourth; + + + + /** + * @return void + */ + + public function fifth() + { + } } diff --git a/tests/Sniffs/Classes/data/classMemberSpacingLiveCodingErrors.php b/tests/Sniffs/Classes/data/classMemberSpacingLiveCodingErrors.php new file mode 100644 index 000000000..1608e7d79 --- /dev/null +++ b/tests/Sniffs/Classes/data/classMemberSpacingLiveCodingErrors.php @@ -0,0 +1,13 @@ += 99.0 + +class Test +{ + + public const FOO = 'foo'; + + // Live coding/parse error. + pub + + public $bar; + +} diff --git a/tests/Sniffs/Classes/data/classMemberSpacingNoErrors.php b/tests/Sniffs/Classes/data/classMemberSpacingNoErrors.php index b562e6100..867439a11 100644 --- a/tests/Sniffs/Classes/data/classMemberSpacingNoErrors.php +++ b/tests/Sniffs/Classes/data/classMemberSpacingNoErrors.php @@ -94,7 +94,7 @@ abstract public function thirdWithAttributeAndDocDomment(); * * @var string */ - protected $forth; + protected $fourth; // @codingStandardsIgnoreEnd #[SomeAttribute] @@ -124,6 +124,11 @@ public function withAnonymous($parameter) }; } + /** + * @var string + */ + + protected $fifth; } enum Gender: string