diff --git a/SlevomatCodingStandard/Helpers/AnnotationHelper.php b/SlevomatCodingStandard/Helpers/AnnotationHelper.php index d015f9566..f6081af13 100644 --- a/SlevomatCodingStandard/Helpers/AnnotationHelper.php +++ b/SlevomatCodingStandard/Helpers/AnnotationHelper.php @@ -25,12 +25,9 @@ use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode; use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode; use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; -use function count; use function in_array; use function sprintf; -use function strlen; use function strtolower; -use const T_DOC_COMMENT_STRING; /** * @internal @@ -67,11 +64,11 @@ static function () use ($phpcsFile, $docCommentOpenPointer, $name): array { if ($parsedDocComment !== null) { foreach ($parsedDocComment->getNode()->getTags() as $node) { - $annotationStartPointer = self::getStartPointer($phpcsFile, $parsedDocComment->getOpenPointer(), $node); + $annotationStartPointer = $parsedDocComment->getNodeStartPointer($phpcsFile, $node); $annotations[] = new Annotation( $node, $annotationStartPointer, - self::getEndPointer($phpcsFile, $parsedDocComment, $annotationStartPointer, $node) + $parsedDocComment->getNodeEndPointer($phpcsFile, $node, $annotationStartPointer) ); } } @@ -309,64 +306,6 @@ public static function isAnnotationUseless( ); } - private static function getStartPointer(File $phpcsFile, int $docCommentOpenPointer, PhpDocTagNode $annotationNode): int - { - $tokens = $phpcsFile->getTokens(); - - $tagStartLine = $tokens[$docCommentOpenPointer]['line'] + $annotationNode->getAttribute('startLine') - 1; - - $searchPointer = $docCommentOpenPointer + 1; - for ($i = $docCommentOpenPointer + 1; $i < $tokens[$docCommentOpenPointer]['comment_closer']; $i++) { - if ($tagStartLine === $tokens[$i]['line']) { - $searchPointer = $i; - break; - } - } - - return TokenHelper::findNext($phpcsFile, TokenHelper::$annotationTokenCodes, $searchPointer); - } - - private static function getEndPointer( - File $phpcsFile, - ParsedDocComment $parsedDocComment, - int $annotationStartPointer, - PhpDocTagNode $annotationNode - ): int - { - $tokens = $phpcsFile->getTokens(); - - $annotationContent = $parsedDocComment->getTokens()->getContentBetween( - $annotationNode->getAttribute(Attribute::START_INDEX), - $annotationNode->getAttribute(Attribute::END_INDEX) + 1 - ); - $annotationLength = strlen($annotationContent); - - $searchPointer = $annotationStartPointer; - - $content = ''; - for ($i = $annotationStartPointer; $i < count($tokens); $i++) { - $content .= $tokens[$i]['content']; - - if (strlen($content) >= $annotationLength) { - $searchPointer = $i; - break; - } - } - - $nextAnnotationStartPointer = TokenHelper::findNext( - $phpcsFile, - TokenHelper::$annotationTokenCodes, - $searchPointer + 1, - $parsedDocComment->getClosePointer() - ); - - $pointerAfter = $nextAnnotationStartPointer ?? $parsedDocComment->getClosePointer(); - - $stringPointerBefore = TokenHelper::findPrevious($phpcsFile, T_DOC_COMMENT_STRING, $pointerAfter - 1, $searchPointer); - - return $stringPointerBefore ?? $searchPointer; - } - private static function changeAnnotationNode(PhpDocTagNode $tagNode, Node $nodeToChange, Node $changedNode): PhpDocTagNode { static $visitor; diff --git a/SlevomatCodingStandard/Helpers/ParsedDocComment.php b/SlevomatCodingStandard/Helpers/ParsedDocComment.php index 8eccb3d6f..2c4fa0113 100644 --- a/SlevomatCodingStandard/Helpers/ParsedDocComment.php +++ b/SlevomatCodingStandard/Helpers/ParsedDocComment.php @@ -2,8 +2,16 @@ namespace SlevomatCodingStandard\Helpers; +use PHP_CodeSniffer\Files\File; +use PHPStan\PhpDocParser\Ast\Attribute; +use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use PHPStan\PhpDocParser\Parser\TokenIterator; +use function array_merge; +use function count; +use function strlen; +use function trim; +use const T_DOC_COMMENT_STRING; /** * @internal @@ -51,4 +59,50 @@ public function getTokens(): TokenIterator return $this->tokens; } + public function getNodeStartPointer(File $phpcsFile, Node $node): int + { + $tokens = $phpcsFile->getTokens(); + + $tagStartLine = $tokens[$this->openPointer]['line'] + $node->getAttribute('startLine') - 1; + + $searchPointer = $this->openPointer + 1; + for ($i = $this->openPointer + 1; $i < $this->closePointer; $i++) { + if ($tagStartLine === $tokens[$i]['line']) { + $searchPointer = $i; + break; + } + } + + return TokenHelper::findNext($phpcsFile, array_merge(TokenHelper::$annotationTokenCodes, [T_DOC_COMMENT_STRING]), $searchPointer); + } + + public function getNodeEndPointer(File $phpcsFile, Node $node, int $nodeStartPointer): int + { + $tokens = $phpcsFile->getTokens(); + + $content = trim($this->tokens->getContentBetween( + $node->getAttribute(Attribute::START_INDEX), + $node->getAttribute(Attribute::END_INDEX) + 1 + )); + $length = strlen($content); + + $searchPointer = $nodeStartPointer; + + $content = ''; + for ($i = $nodeStartPointer; $i < count($tokens); $i++) { + $content .= $tokens[$i]['content']; + + if (strlen($content) >= $length) { + $searchPointer = $i; + break; + } + } + + return TokenHelper::findPrevious( + $phpcsFile, + array_merge(TokenHelper::$annotationTokenCodes, [T_DOC_COMMENT_STRING]), + $searchPointer + ); + } + } diff --git a/SlevomatCodingStandard/Helpers/PhpDocParserHelper.php b/SlevomatCodingStandard/Helpers/PhpDocParserHelper.php index 257565c14..6cc06d61a 100644 --- a/SlevomatCodingStandard/Helpers/PhpDocParserHelper.php +++ b/SlevomatCodingStandard/Helpers/PhpDocParserHelper.php @@ -42,6 +42,7 @@ public static function getParser(): PhpDocParser true, true, $usedAttributes, + true, true ); } diff --git a/SlevomatCodingStandard/Sniffs/Commenting/DocCommentSpacingSniff.php b/SlevomatCodingStandard/Sniffs/Commenting/DocCommentSpacingSniff.php index 5f9675c8e..7cee53eda 100644 --- a/SlevomatCodingStandard/Sniffs/Commenting/DocCommentSpacingSniff.php +++ b/SlevomatCodingStandard/Sniffs/Commenting/DocCommentSpacingSniff.php @@ -17,7 +17,6 @@ use function array_key_exists; use function array_keys; use function array_map; -use function array_merge; use function array_values; use function asort; use function count; @@ -35,7 +34,6 @@ use function usort; use const T_DOC_COMMENT_OPEN_TAG; use const T_DOC_COMMENT_STAR; -use const T_DOC_COMMENT_STRING; use const T_DOC_COMMENT_WHITESPACE; class DocCommentSpacingSniff implements Sniff @@ -103,34 +101,23 @@ public function process(File $phpcsFile, $docCommentOpenerPointer): void $tokens = $phpcsFile->getTokens(); - $firstContentStartPointer = TokenHelper::findNextExcluding( + if (TokenHelper::findNextExcluding( $phpcsFile, [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STAR], $docCommentOpenerPointer + 1, $tokens[$docCommentOpenerPointer]['comment_closer'] - ); - - if ($firstContentStartPointer === null) { + ) === null) { return; } - $firstContentEndPointer = $firstContentStartPointer; - $actualPointer = $firstContentStartPointer; - do { - /** @var int $actualPointer */ - $actualPointer = TokenHelper::findNextExcluding( - $phpcsFile, - [T_DOC_COMMENT_STAR, T_DOC_COMMENT_WHITESPACE], - $actualPointer + 1, - $tokens[$docCommentOpenerPointer]['comment_closer'] + 1 - ); - - if ($tokens[$actualPointer]['code'] !== T_DOC_COMMENT_STRING) { - break; - } + $parsedDocComment = DocCommentHelper::parseDocComment($phpcsFile, $docCommentOpenerPointer); - $firstContentEndPointer = $actualPointer; - } while (true); + $firstContentStartPointer = $parsedDocComment->getNodeStartPointer($phpcsFile, $parsedDocComment->getNode()->children[0]); + $firstContentEndPointer = $parsedDocComment->getNodeEndPointer( + $phpcsFile, + $parsedDocComment->getNode()->children[0], + $firstContentStartPointer + ); $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenerPointer); usort($annotations, static function (Annotation $a, Annotation $b): int { @@ -142,11 +129,7 @@ public function process(File $phpcsFile, $docCommentOpenerPointer): void /** @var int $lastContentEndPointer */ $lastContentEndPointer = $annotationsCount > 0 - ? TokenHelper::findPrevious( - $phpcsFile, - array_merge(TokenHelper::$annotationTokenCodes, [T_DOC_COMMENT_STRING]), - $tokens[$docCommentOpenerPointer]['comment_closer'] - 1 - ) + ? $annotations[$annotationsCount - 1]->getEndPointer() : $firstContentEndPointer; $this->checkLinesBeforeFirstContent($phpcsFile, $docCommentOpenerPointer, $firstContentStartPointer); diff --git a/composer.json b/composer.json index 49b672a3d..ec0c19339 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.2 || ^8.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", - "phpstan/phpdoc-parser": "^1.22.0", + "phpstan/phpdoc-parser": "^1.23.0", "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { diff --git a/tests/Sniffs/Commenting/data/docCommentSpacingDefaultSettingsNoErrors.php b/tests/Sniffs/Commenting/data/docCommentSpacingDefaultSettingsNoErrors.php index 0a187fd05..31fdb719d 100644 --- a/tests/Sniffs/Commenting/data/docCommentSpacingDefaultSettingsNoErrors.php +++ b/tests/Sniffs/Commenting/data/docCommentSpacingDefaultSettingsNoErrors.php @@ -6,6 +6,8 @@ /** * Description + * + * phpcs:disable SlevomatCodingStandard.Classes.RequireAbstractOrFinal.ClassNeitherAbstractNorFinal */ class Whatever { diff --git a/tests/Sniffs/Commenting/data/docCommentSpacingModifiedSettingsNoErrors.php b/tests/Sniffs/Commenting/data/docCommentSpacingModifiedSettingsNoErrors.php index 85b3a4d82..e3fa0ef7e 100644 --- a/tests/Sniffs/Commenting/data/docCommentSpacingModifiedSettingsNoErrors.php +++ b/tests/Sniffs/Commenting/data/docCommentSpacingModifiedSettingsNoErrors.php @@ -10,6 +10,8 @@ * * Description * + * phpcs:disable SlevomatCodingStandard.Classes.RequireAbstractOrFinal.ClassNeitherAbstractNorFinal + * */ class Whatever { diff --git a/tests/Sniffs/Commenting/data/uselessFunctionDocCommentSniffNoErrors.php b/tests/Sniffs/Commenting/data/uselessFunctionDocCommentSniffNoErrors.php index 33bd3a7a5..d6c4b3d34 100644 --- a/tests/Sniffs/Commenting/data/uselessFunctionDocCommentSniffNoErrors.php +++ b/tests/Sniffs/Commenting/data/uselessFunctionDocCommentSniffNoErrors.php @@ -191,4 +191,14 @@ public function specificAnnotationInFQN(): void { } + /** + * @param string $parameter + * Some parameter + * @return string + * Some return value + */ + public function descriptionOnNextLine() + { + } + }