diff --git a/README.md b/README.md index c311202b..3f5633d8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Latest stable version](https://img.shields.io/packagist/v/kubawerlos/php-cs-fixer-custom-fixers.svg?label=current%20version)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers) [![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net) [![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE) -![Tests](https://img.shields.io/badge/tests-3802-brightgreen.svg) +![Tests](https://img.shields.io/badge/tests-3805-brightgreen.svg) [![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers) [![CI status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions/workflows/ci.yaml/badge.svg)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions/workflows/ci.yaml) diff --git a/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php b/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php index 05003884..06c95f28 100644 --- a/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php +++ b/src/Fixer/PhpdocTagNoNamedArgumentsFixer.php @@ -22,6 +22,11 @@ use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Preg; +use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis; +use PhpCsFixer\Tokenizer\Analyzer\AttributeAnalyzer; +use PhpCsFixer\Tokenizer\Analyzer\FullyQualifiedNameAnalyzer; +use PhpCsFixer\Tokenizer\CT; +use PhpCsFixer\Tokenizer\FCT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixer\WhitespacesFixerConfig; @@ -131,10 +136,16 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void } $prevIndex = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($prevIndex)); + if ($tokens[$prevIndex]->isGivenKind(\T_NEW)) { continue; } + if (self::isAttributeClass($tokens, $prevIndex)) { + continue; + } + $this->ensureIsDocBlockWithNoNameArgumentsTag($tokens, $index); $docBlockIndex = $tokens->getPrevTokenOfKind($index + 2, [[\T_DOC_COMMENT]]); @@ -150,6 +161,31 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void } } + private static function isAttributeClass(Tokens $tokens, int $index): bool + { + while ($tokens[$index]->isGivenKind([\T_ABSTRACT, \T_FINAL, FCT::T_READONLY])) { + $index = $tokens->getPrevMeaningfulToken($index); + \assert(\is_int($index)); + } + + if (!$tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) { + return false; + } + + $fullyQualifiedNameAnalyzer = new FullyQualifiedNameAnalyzer($tokens); + + foreach (AttributeAnalyzer::collect($tokens, $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $index)) as $attributeAnalysis) { + foreach ($attributeAnalysis->getAttributes() as $attribute) { + $attributeName = \strtolower($fullyQualifiedNameAnalyzer->getFullyQualifiedName($attribute['name'], $attribute['start'], NamespaceUseAnalysis::TYPE_CLASS)); + if ($attributeName === 'attribute') { + return true; + } + } + } + + return false; + } + private function ensureIsDocBlockWithNoNameArgumentsTag(Tokens $tokens, int $index): void { /** @var null|callable(WhitespacesFixerConfig, Tokens, int): void $ensureIsDocBlockWithTagNoNameArguments */ diff --git a/tests/Fixer/PhpdocTagNoNamedArgumentsFixerTest.php b/tests/Fixer/PhpdocTagNoNamedArgumentsFixerTest.php index 314900db..772255e5 100644 --- a/tests/Fixer/PhpdocTagNoNamedArgumentsFixerTest.php +++ b/tests/Fixer/PhpdocTagNoNamedArgumentsFixerTest.php @@ -225,4 +225,90 @@ class Foo {} new WhitespacesFixerConfig("\t", "\r\n"), ]; } + + /** + * @dataProvider provideFix80Cases + * + * @requires PHP >= 8.0 + */ + public function testFix80(string $expected, ?string $input = null): void + { + $this->doTest($expected, $input); + } + + /** + * @return iterable + */ + public static function provideFix80Cases(): iterable + { + yield 'do not add for attribute class' => [ + <<<'PHP' + [ + <<<'PHP' + = 8.2 + */ + public function testFix82(string $expected, ?string $input = null): void + { + $this->doTest($expected, $input); + } + + /** + * @return iterable + */ + public static function provideFix82Cases(): iterable + { + yield 'do not add for attribute (readonly) class' => [ + <<<'PHP' +