Skip to content

Commit fcc32c8

Browse files
authored
feature: Support removing superfluous PHPDocs involving self (#6583)
1 parent 92c0600 commit fcc32c8

File tree

3 files changed

+931
-575
lines changed

3 files changed

+931
-575
lines changed

src/Cache/SignatureInterface.php

-3
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,5 @@ public function getLineEnding(): string;
3131

3232
public function getRules(): array;
3333

34-
/**
35-
* @param SignatureInterface $signature
36-
*/
3734
public function equals(self $signature): bool;
3835
}

src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php

+64-15
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use PhpCsFixer\Tokenizer\CT;
3030
use PhpCsFixer\Tokenizer\Token;
3131
use PhpCsFixer\Tokenizer\Tokens;
32+
use PhpCsFixer\Tokenizer\TokensAnalyzer;
3233

3334
final class NoSuperfluousPhpdocTagsFixer extends AbstractFixer implements ConfigurableFixerInterface
3435
{
@@ -106,14 +107,44 @@ public function isCandidate(Tokens $tokens): bool
106107
*/
107108
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
108109
{
110+
$tokensAnalyzer = new TokensAnalyzer($tokens);
111+
109112
$namespaceUseAnalyzer = new NamespaceUsesAnalyzer();
110113
$shortNames = [];
114+
$currentSymbol = null;
115+
$currentSymbolEndIndex = null;
111116

112117
foreach ($namespaceUseAnalyzer->getDeclarationsFromTokens($tokens) as $namespaceUseAnalysis) {
113118
$shortNames[strtolower($namespaceUseAnalysis->getShortName())] = '\\'.strtolower($namespaceUseAnalysis->getFullName());
114119
}
115120

121+
$symbolKinds = [T_CLASS, T_INTERFACE];
122+
if (\defined('T_ENUM')) { // @TODO drop the condition when requiring PHP 8.1+
123+
$symbolKinds[] = T_ENUM;
124+
}
125+
116126
foreach ($tokens as $index => $token) {
127+
if ($index === $currentSymbolEndIndex) {
128+
$currentSymbol = null;
129+
$currentSymbolEndIndex = null;
130+
131+
continue;
132+
}
133+
134+
if ($token->isGivenKind(T_CLASS) && $tokensAnalyzer->isAnonymousClass($index)) {
135+
continue;
136+
}
137+
138+
if ($token->isGivenKind($symbolKinds)) {
139+
$currentSymbol = $tokens[$tokens->getNextMeaningfulToken($index)]->getContent();
140+
$currentSymbolEndIndex = $tokens->findBlockEnd(
141+
Tokens::BLOCK_TYPE_CURLY_BRACE,
142+
$tokens->getNextTokenOfKind($index, ['{']),
143+
);
144+
145+
continue;
146+
}
147+
117148
if (!$token->isGivenKind(T_DOC_COMMENT)) {
118149
continue;
119150
}
@@ -131,9 +162,9 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
131162
}
132163

133164
if ('function' === $documentedElement['type']) {
134-
$content = $this->fixFunctionDocComment($content, $tokens, $documentedElement, $shortNames);
165+
$content = $this->fixFunctionDocComment($content, $tokens, $documentedElement, $currentSymbol, $shortNames);
135166
} elseif ('property' === $documentedElement['type']) {
136-
$content = $this->fixPropertyDocComment($content, $tokens, $documentedElement, $shortNames);
167+
$content = $this->fixPropertyDocComment($content, $tokens, $documentedElement, $currentSymbol, $shortNames);
137168
} elseif ('classy' === $documentedElement['type']) {
138169
$content = $this->fixClassDocComment($content, $documentedElement);
139170
} else {
@@ -250,8 +281,13 @@ private function findDocumentedElement(Tokens $tokens, int $docCommentIndex): ?a
250281
return null;
251282
}
252283

253-
private function fixFunctionDocComment(string $content, Tokens $tokens, array $element, array $shortNames): string
254-
{
284+
private function fixFunctionDocComment(
285+
string $content,
286+
Tokens $tokens,
287+
array $element,
288+
?string $currentSymbol,
289+
array $shortNames
290+
): string {
255291
$docBlock = new DocBlock($content);
256292

257293
$openingParenthesisIndex = $tokens->getNextTokenOfKind($element['index'], ['(']);
@@ -274,15 +310,15 @@ private function fixFunctionDocComment(string $content, Tokens $tokens, array $e
274310
continue;
275311
}
276312

277-
if (!isset($argumentsInfo[$argumentName]) || $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $shortNames)) {
313+
if (!isset($argumentsInfo[$argumentName]) || $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $currentSymbol, $shortNames)) {
278314
$annotation->remove();
279315
}
280316
}
281317

282318
$returnTypeInfo = $this->getReturnTypeInfo($tokens, $closingParenthesisIndex);
283319

284320
foreach ($docBlock->getAnnotationsOfType('return') as $annotation) {
285-
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $shortNames)) {
321+
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $currentSymbol, $shortNames)) {
286322
$annotation->remove();
287323
}
288324
}
@@ -292,8 +328,13 @@ private function fixFunctionDocComment(string $content, Tokens $tokens, array $e
292328
return $docBlock->getContent();
293329
}
294330

295-
private function fixPropertyDocComment(string $content, Tokens $tokens, array $element, array $shortNames): string
296-
{
331+
private function fixPropertyDocComment(
332+
string $content,
333+
Tokens $tokens,
334+
array $element,
335+
?string $currentSymbol,
336+
array $shortNames
337+
): string {
297338
if (\count($element['types']) > 0) {
298339
$propertyTypeInfo = $this->parseTypeHint($tokens, array_key_first($element['types']));
299340
} else {
@@ -306,7 +347,7 @@ private function fixPropertyDocComment(string $content, Tokens $tokens, array $e
306347
$docBlock = new DocBlock($content);
307348

308349
foreach ($docBlock->getAnnotationsOfType('var') as $annotation) {
309-
if ($this->annotationIsSuperfluous($annotation, $propertyTypeInfo, $shortNames)) {
350+
if ($this->annotationIsSuperfluous($annotation, $propertyTypeInfo, $currentSymbol, $shortNames)) {
310351
$annotation->remove();
311352
}
312353
}
@@ -432,8 +473,12 @@ private function parseTypeHint(Tokens $tokens, int $index): array
432473
/**
433474
* @param array<string, string> $symbolShortNames
434475
*/
435-
private function annotationIsSuperfluous(Annotation $annotation, array $info, array $symbolShortNames): bool
436-
{
476+
private function annotationIsSuperfluous(
477+
Annotation $annotation,
478+
array $info,
479+
?string $currentSymbol,
480+
array $symbolShortNames
481+
): bool {
437482
if ('param' === $annotation->getTag()->getName()) {
438483
$regex = '/@param\s+[^\$]+\s(?:\&\s*)?(?:\.{3}\s*)?\$\S+\s+\S/';
439484
} elseif ('var' === $annotation->getTag()->getName()) {
@@ -446,7 +491,7 @@ private function annotationIsSuperfluous(Annotation $annotation, array $info, ar
446491
return false;
447492
}
448493

449-
$annotationTypes = $this->toComparableNames($annotation->getTypes(), $symbolShortNames);
494+
$annotationTypes = $this->toComparableNames($annotation->getTypes(), $currentSymbol, $symbolShortNames);
450495

451496
if (['null'] === $annotationTypes) {
452497
return false;
@@ -462,7 +507,7 @@ private function annotationIsSuperfluous(Annotation $annotation, array $info, ar
462507
$actualTypes[] = 'null';
463508
}
464509

465-
return $annotationTypes === $this->toComparableNames($actualTypes, $symbolShortNames);
510+
return $annotationTypes === $this->toComparableNames($actualTypes, $currentSymbol, $symbolShortNames);
466511
}
467512

468513
/**
@@ -476,10 +521,14 @@ private function annotationIsSuperfluous(Annotation $annotation, array $info, ar
476521
*
477522
* @return array The normalized types
478523
*/
479-
private function toComparableNames(array $types, array $symbolShortNames): array
524+
private function toComparableNames(array $types, ?string $currentSymbol, array $symbolShortNames): array
480525
{
481526
$normalized = array_map(
482-
static function (string $type) use ($symbolShortNames): string {
527+
static function (string $type) use ($currentSymbol, $symbolShortNames): string {
528+
if ('self' === $type && null !== $currentSymbol) {
529+
$type = $currentSymbol;
530+
}
531+
483532
$type = strtolower($type);
484533

485534
if (str_contains($type, '&')) {

0 commit comments

Comments
 (0)