Skip to content

Commit abba0f3

Browse files
committed
Remove superfluous phpdocs for typed properties (PHP 7.4)
1 parent 6325744 commit abba0f3

File tree

3 files changed

+476
-39
lines changed

3 files changed

+476
-39
lines changed

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,8 +1112,8 @@ Choose from the list of available rules:
11121112

11131113
* **no_superfluous_phpdoc_tags** [@Symfony, @PhpCsFixer]
11141114

1115-
Removes ``@param`` and ``@return`` tags that don't provide any useful
1116-
information.
1115+
Removes ``@param``, ``@return`` and ``@var`` tags that don't provide any
1116+
useful information.
11171117

11181118
Configuration options:
11191119

src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php

Lines changed: 123 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ final class NoSuperfluousPhpdocTagsFixer extends AbstractFixer implements Config
3636
public function getDefinition()
3737
{
3838
return new FixerDefinition(
39-
'Removes `@param` and `@return` tags that don\'t provide any useful information.',
39+
'Removes `@param`, `@return` and `@var` tags that don\'t provide any useful information.',
4040
[
4141
new CodeSample('<?php
4242
class Foo {
@@ -67,6 +67,14 @@ class Foo {
6767
public function doFoo(Bar $bar, $baz): Baz {}
6868
}
6969
', new VersionSpecification(70000)),
70+
new VersionSpecificCodeSample('<?php
71+
class Foo {
72+
/**
73+
* @var Bar
74+
*/
75+
private Bar $bar;
76+
}
77+
', new VersionSpecification(70400)),
7078
]
7179
);
7280
}
@@ -106,46 +114,25 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
106114
continue;
107115
}
108116

109-
$functionIndex = $this->findDocumentedFunction($tokens, $index);
110-
if (null === $functionIndex) {
111-
continue;
112-
}
113-
114-
$docBlock = new DocBlock($token->getContent());
117+
$content = $initialContent = $token->getContent();
115118

116-
$openingParenthesisIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
117-
$closingParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesisIndex);
119+
$documentedElementIndex = $this->findDocumentedElement($tokens, $index);
118120

119-
$argumentsInfo = $this->getArgumentsInfo(
120-
$tokens,
121-
$openingParenthesisIndex + 1,
122-
$closingParenthesisIndex - 1
123-
);
124-
125-
foreach ($docBlock->getAnnotationsOfType('param') as $annotation) {
126-
if (0 === Preg::match('/@param(?:\s+[^\$]\S+)?\s+(\$\S+)/', $annotation->getContent(), $matches)) {
127-
continue;
128-
}
129-
130-
$argumentName = $matches[1];
131-
132-
if (
133-
!isset($argumentsInfo[$argumentName])
134-
|| $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $shortNames)
135-
) {
136-
$annotation->remove();
137-
}
121+
if (null === $documentedElementIndex) {
122+
continue;
138123
}
139124

140-
$returnTypeInfo = $this->getReturnTypeInfo($tokens, $closingParenthesisIndex);
125+
$token = $tokens[$documentedElementIndex];
141126

142-
foreach ($docBlock->getAnnotationsOfType('return') as $annotation) {
143-
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $shortNames)) {
144-
$annotation->remove();
145-
}
127+
if ($token->isGivenKind(T_FUNCTION)) {
128+
$content = $this->fixFunctionDocComment($content, $tokens, $index, $shortNames);
129+
} elseif ($token->isGivenKind(T_VARIABLE)) {
130+
$content = $this->fixPropertyDocComment($content, $tokens, $index, $shortNames);
146131
}
147132

148-
$tokens[$index] = new Token([T_DOC_COMMENT, $docBlock->getContent()]);
133+
if ($content !== $initialContent) {
134+
$tokens[$index] = new Token([T_DOC_COMMENT, $content]);
135+
}
149136
}
150137
}
151138

@@ -162,19 +149,116 @@ protected function createConfigurationDefinition()
162149
]);
163150
}
164151

165-
private function findDocumentedFunction(Tokens $tokens, $index)
152+
/**
153+
* @param int $docCommentIndex
154+
*
155+
* @return null|int
156+
*/
157+
private function findDocumentedElement(Tokens $tokens, $docCommentIndex)
166158
{
159+
$index = $docCommentIndex;
160+
167161
do {
168162
$index = $tokens->getNextMeaningfulToken($index);
169163

170-
if (null === $index || $tokens[$index]->isGivenKind(T_FUNCTION)) {
164+
if (null === $index || $tokens[$index]->isGivenKind([T_FUNCTION, T_CLASS, T_INTERFACE])) {
171165
return $index;
172166
}
173167
} while ($tokens[$index]->isGivenKind([T_ABSTRACT, T_FINAL, T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC]));
174168

169+
$index = $tokens->getNextMeaningfulToken($docCommentIndex);
170+
171+
$kindsBeforeProperty = [T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, T_STRING, T_NS_SEPARATOR];
172+
173+
if (!$tokens[$index]->isGivenKind($kindsBeforeProperty)) {
174+
return null;
175+
}
176+
177+
do {
178+
$index = $tokens->getNextMeaningfulToken($index);
179+
180+
if ($tokens[$index]->isGivenKind(T_VARIABLE)) {
181+
return $index;
182+
}
183+
} while ($tokens[$index]->isGivenKind($kindsBeforeProperty));
184+
175185
return null;
176186
}
177187

188+
/**
189+
* @param string $content
190+
* @param int $functionIndex
191+
*
192+
* @return string
193+
*/
194+
private function fixFunctionDocComment($content, Tokens $tokens, $functionIndex, array $shortNames)
195+
{
196+
$docBlock = new DocBlock($content);
197+
198+
$openingParenthesisIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
199+
$closingParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openingParenthesisIndex);
200+
201+
$argumentsInfo = $this->getArgumentsInfo(
202+
$tokens,
203+
$openingParenthesisIndex + 1,
204+
$closingParenthesisIndex - 1
205+
);
206+
207+
foreach ($docBlock->getAnnotationsOfType('param') as $annotation) {
208+
if (0 === Preg::match('/@param(?:\s+[^\$]\S+)?\s+(\$\S+)/', $annotation->getContent(), $matches)) {
209+
continue;
210+
}
211+
212+
$argumentName = $matches[1];
213+
214+
if (!isset($argumentsInfo[$argumentName]) || $this->annotationIsSuperfluous($annotation, $argumentsInfo[$argumentName], $shortNames)) {
215+
$annotation->remove();
216+
}
217+
}
218+
219+
$returnTypeInfo = $this->getReturnTypeInfo($tokens, $closingParenthesisIndex);
220+
221+
foreach ($docBlock->getAnnotationsOfType('return') as $annotation) {
222+
if ($this->annotationIsSuperfluous($annotation, $returnTypeInfo, $shortNames)) {
223+
$annotation->remove();
224+
}
225+
}
226+
227+
return $docBlock->getContent();
228+
}
229+
230+
/**
231+
* @param string $content
232+
* @param int $docCommentIndex
233+
*
234+
* @return string
235+
*/
236+
private function fixPropertyDocComment($content, Tokens $tokens, $docCommentIndex, array $shortNames)
237+
{
238+
$docBlock = new DocBlock($content);
239+
240+
$index = $tokens->getNextMeaningfulToken($docCommentIndex);
241+
242+
$kindsBeforeProperty = [T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC];
243+
244+
if (!$tokens[$index]->isGivenKind($kindsBeforeProperty)) {
245+
return $content;
246+
}
247+
248+
do {
249+
$index = $tokens->getNextMeaningfulToken($index);
250+
251+
$propertyTypeInfo = $this->parseTypeHint($tokens, $index);
252+
foreach ($docBlock->getAnnotationsOfType('var') as $annotation) {
253+
if ($this->annotationIsSuperfluous($annotation, $propertyTypeInfo, $shortNames)) {
254+
$annotation->remove();
255+
}
256+
}
257+
} while ($tokens[$index]->isGivenKind($kindsBeforeProperty));
258+
259+
return $docBlock->getContent();
260+
}
261+
178262
/**
179263
* @param int $start
180264
* @param int $end
@@ -254,7 +338,7 @@ private function parseTypeHint(Tokens $tokens, $index)
254338
}
255339

256340
return [
257-
'type' => $type,
341+
'type' => '' === $type ? null : $type,
258342
'allows_null' => $allowsNull,
259343
];
260344
}
@@ -268,6 +352,8 @@ private function annotationIsSuperfluous(Annotation $annotation, array $info, ar
268352
{
269353
if ('param' === $annotation->getTag()->getName()) {
270354
$regex = '/@param\s+(?:\S|\s(?!\$))+\s\$\S+\s+\S/';
355+
} elseif ('var' === $annotation->getTag()->getName()) {
356+
$regex = '/@var\s+\S+(\s+\$\S+)?(\s+)([^$\s]+)/';
271357
} else {
272358
$regex = '/@return\s+\S+\s+\S/';
273359
}

0 commit comments

Comments
 (0)