Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
RedundantVarNameSniff
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 2
156
0.00% covered (danger)
0.00%
0 / 1
 register
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
132
1<?php
2
3namespace MediaWiki\Sniffs\Commenting;
4
5use PHP_CodeSniffer\Files\File;
6use PHP_CodeSniffer\Sniffs\Sniff;
7use PHP_CodeSniffer\Util\Tokens;
8
9/**
10 * Custom sniff that reports and repairs documentations of class properties that repeat the
11 * variable name, which is unnecessary.
12 *
13 * @license GPL-2.0-or-later
14 * @author Thiemo Kreuz
15 */
16class RedundantVarNameSniff implements Sniff {
17
18    /**
19     * @inheritDoc
20     */
21    public function register(): array {
22        return [ T_DOC_COMMENT_TAG ];
23    }
24
25    /**
26     * @inheritDoc
27     */
28    public function process( File $phpcsFile, $stackPtr ) {
29        $tokens = $phpcsFile->getTokens();
30
31        if ( $tokens[$stackPtr]['level'] !== 1 || $tokens[$stackPtr]['content'] !== '@var' ) {
32            return;
33        }
34
35        $docPtr = $phpcsFile->findNext( T_DOC_COMMENT_WHITESPACE, $stackPtr + 1, null, true );
36        if ( !$docPtr || $tokens[$docPtr]['code'] !== T_DOC_COMMENT_STRING ) {
37            return;
38        }
39
40        // This assumes there is always a variable somewhere after a @var, which should be the case
41        $variablePtr = $phpcsFile->findNext( T_VARIABLE, $docPtr + 1 );
42        if ( !$variablePtr ) {
43            return;
44        }
45
46        $visibilityPtr = $phpcsFile->findPrevious(
47            // This is already compatible with `public int $var;` available since PHP 7.4
48            // Skip over `static` in the declaration too, T278471
49            Tokens::$emptyTokens + [ T_NULLABLE, T_STRING, T_STATIC ],
50            $variablePtr - 1,
51            $docPtr + 1,
52            true
53        );
54        if ( !$visibilityPtr || ( $tokens[$visibilityPtr]['code'] !== T_VAR &&
55            !isset( Tokens::$scopeModifiers[ $tokens[$visibilityPtr]['code'] ] ) )
56        ) {
57            return;
58        }
59
60        $variableName = $tokens[$variablePtr]['content'];
61        if ( !preg_match(
62            '{^([^\s$]+\s)?\s*' . preg_quote( $variableName ) . '\b\s*(.*)}is',
63            $tokens[$docPtr]['content'],
64            $matches
65        ) ) {
66            return;
67        }
68
69        $fix = $phpcsFile->addFixableError(
70            'Found redundant variable name %s in @var',
71            $docPtr,
72            'Found',
73            [ $variableName ]
74        );
75        if ( $fix ) {
76            $phpcsFile->fixer->replaceToken( $docPtr, trim( $matches[1] . $matches[2] ) );
77        }
78    }
79
80}