Skip to content

Commit 79e14df

Browse files
authored
PhpdocTagNoNamedArgumentsFixer - do not add @no-named-arguments to attribute classes (#1059)
1 parent 12a36d3 commit 79e14df

File tree

3 files changed

+123
-1
lines changed

3 files changed

+123
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![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)
66
[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net)
77
[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE)
8-
![Tests](https://img.shields.io/badge/tests-3802-brightgreen.svg)
8+
![Tests](https://img.shields.io/badge/tests-3805-brightgreen.svg)
99
[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
1010

1111
[![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)

src/Fixer/PhpdocTagNoNamedArgumentsFixer.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
use PhpCsFixer\FixerDefinition\FixerDefinition;
2323
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
2424
use PhpCsFixer\Preg;
25+
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
26+
use PhpCsFixer\Tokenizer\Analyzer\AttributeAnalyzer;
27+
use PhpCsFixer\Tokenizer\Analyzer\FullyQualifiedNameAnalyzer;
28+
use PhpCsFixer\Tokenizer\CT;
29+
use PhpCsFixer\Tokenizer\FCT;
2530
use PhpCsFixer\Tokenizer\Token;
2631
use PhpCsFixer\Tokenizer\Tokens;
2732
use PhpCsFixer\WhitespacesFixerConfig;
@@ -131,10 +136,16 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
131136
}
132137

133138
$prevIndex = $tokens->getPrevMeaningfulToken($index);
139+
\assert(\is_int($prevIndex));
140+
134141
if ($tokens[$prevIndex]->isGivenKind(\T_NEW)) {
135142
continue;
136143
}
137144

145+
if (self::isAttributeClass($tokens, $prevIndex)) {
146+
continue;
147+
}
148+
138149
$this->ensureIsDocBlockWithNoNameArgumentsTag($tokens, $index);
139150

140151
$docBlockIndex = $tokens->getPrevTokenOfKind($index + 2, [[\T_DOC_COMMENT]]);
@@ -150,6 +161,31 @@ public function fix(\SplFileInfo $file, Tokens $tokens): void
150161
}
151162
}
152163

164+
private static function isAttributeClass(Tokens $tokens, int $index): bool
165+
{
166+
while ($tokens[$index]->isGivenKind([\T_ABSTRACT, \T_FINAL, FCT::T_READONLY])) {
167+
$index = $tokens->getPrevMeaningfulToken($index);
168+
\assert(\is_int($index));
169+
}
170+
171+
if (!$tokens[$index]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
172+
return false;
173+
}
174+
175+
$fullyQualifiedNameAnalyzer = new FullyQualifiedNameAnalyzer($tokens);
176+
177+
foreach (AttributeAnalyzer::collect($tokens, $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $index)) as $attributeAnalysis) {
178+
foreach ($attributeAnalysis->getAttributes() as $attribute) {
179+
$attributeName = \strtolower($fullyQualifiedNameAnalyzer->getFullyQualifiedName($attribute['name'], $attribute['start'], NamespaceUseAnalysis::TYPE_CLASS));
180+
if ($attributeName === 'attribute') {
181+
return true;
182+
}
183+
}
184+
}
185+
186+
return false;
187+
}
188+
153189
private function ensureIsDocBlockWithNoNameArgumentsTag(Tokens $tokens, int $index): void
154190
{
155191
/** @var null|callable(WhitespacesFixerConfig, Tokens, int): void $ensureIsDocBlockWithTagNoNameArguments */

tests/Fixer/PhpdocTagNoNamedArgumentsFixerTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,90 @@ class Foo {}
225225
new WhitespacesFixerConfig("\t", "\r\n"),
226226
];
227227
}
228+
229+
/**
230+
* @dataProvider provideFix80Cases
231+
*
232+
* @requires PHP >= 8.0
233+
*/
234+
public function testFix80(string $expected, ?string $input = null): void
235+
{
236+
$this->doTest($expected, $input);
237+
}
238+
239+
/**
240+
* @return iterable<array{0: string, 1?: string}>
241+
*/
242+
public static function provideFix80Cases(): iterable
243+
{
244+
yield 'do not add for attribute class' => [
245+
<<<'PHP'
246+
<?php
247+
#[Attribute(flags: Attribute::TARGET_METHOD)]
248+
final class MyAttributeClass {}
249+
PHP,
250+
];
251+
252+
yield 'do not add for attribute class (with alias)' => [
253+
<<<'PHP'
254+
<?php
255+
namespace Foo;
256+
use Attribute as TheAttributeClass;
257+
#[TheAttributeClass(flags: TheAttributeClass::TARGET_METHOD)]
258+
abstract class MyAttributeClass {}
259+
PHP,
260+
];
261+
}
262+
263+
/**
264+
* @dataProvider provideFix82Cases
265+
*
266+
* @requires PHP >= 8.2
267+
*/
268+
public function testFix82(string $expected, ?string $input = null): void
269+
{
270+
$this->doTest($expected, $input);
271+
}
272+
273+
/**
274+
* @return iterable<array{0: string, 1?: string}>
275+
*/
276+
public static function provideFix82Cases(): iterable
277+
{
278+
yield 'do not add for attribute (readonly) class' => [
279+
<<<'PHP'
280+
<?php
281+
282+
/**
283+
* @no-named-arguments
284+
*/
285+
#[FooAttribute]
286+
final readonly class NotAttributeClass1 {}
287+
288+
#[Attribute(flags: Attribute::TARGET_METHOD)]
289+
abstract readonly class MyAttributeClass {}
290+
291+
/**
292+
* @no-named-arguments
293+
*/
294+
#[FooAttribute]
295+
#[BarAttribute]
296+
#[BazAttribute]
297+
final readonly class NotAttributeClass2 {}
298+
PHP,
299+
<<<'PHP'
300+
<?php
301+
#[FooAttribute]
302+
final readonly class NotAttributeClass1 {}
303+
304+
#[Attribute(flags: Attribute::TARGET_METHOD)]
305+
abstract readonly class MyAttributeClass {}
306+
307+
#[FooAttribute]
308+
#[BarAttribute]
309+
#[BazAttribute]
310+
final readonly class NotAttributeClass2 {}
311+
PHP,
312+
];
313+
}
228314
}

0 commit comments

Comments
 (0)