@@ -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
4242class 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