Skip to content

Commit 567221e

Browse files
authored
Ignore unused parameters when function in abstract class is empty (sirbrillig#270)
* Add test for ignoring unused parameters in empty abstract methods * Ignore unused parameters when function in abstract class is empty * Fix lint errors
1 parent 0c1bfa3 commit 567221e

File tree

4 files changed

+94
-7
lines changed

4 files changed

+94
-7
lines changed

Tests/VariableAnalysisSniff/VariableAnalysisTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ public function testClassWithMembersWarnings()
209209
66,
210210
115,
211211
116,
212+
174,
212213
];
213214
$this->assertSame($expectedWarnings, $lines);
214215
}

Tests/VariableAnalysisSniff/fixtures/ClassWithMembersFixture.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,24 @@ abstract class AbstractClassWithStaticProperties {
154154
public static int $static_with_visibility_and_type;
155155
public static ?int $static_with_visibility_and_nullable_type;
156156

157-
public function use_vars();
157+
abstract public function use_vars();
158158

159-
public static function getIntOrNull($value);
159+
abstract public static function getIntOrNull($value);
160160

161-
static function getIntOrNull2($value);
161+
abstract static function getIntOrNull2($value);
162+
}
163+
164+
abstract class AbstractClassWithEmptyMethodBodies {
165+
abstract public function bar($param);
166+
167+
public function baz($param) {
168+
}
169+
170+
public function baz2($param) {
171+
return;
172+
}
173+
174+
public function baz3($param) { // Unused variable $param
175+
return 'foobar';
176+
}
162177
}

VariableAnalysis/Lib/Helpers.php

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ public static function areAnyConditionsAClass(array $conditions)
9191
}
9292

9393
/**
94+
* Return true if the token conditions are within a function before they are
95+
* within a class.
96+
*
9497
* @param (int|string)[] $conditions
9598
*
9699
* @return bool
97100
*/
98101
public static function areConditionsWithinFunctionBeforeClass(array $conditions)
99102
{
100-
// Return true if the token conditions are within a function before
101-
// they are within a class.
102103
$classTypes = [T_CLASS, T_ANON_CLASS, T_TRAIT];
103104
foreach (array_reverse($conditions, true) as $scopeCode) {
104105
if (in_array($scopeCode, $classTypes)) {
@@ -112,14 +113,15 @@ public static function areConditionsWithinFunctionBeforeClass(array $conditions)
112113
}
113114

114115
/**
116+
* Return true if the token conditions are within an if block before they are
117+
* within a class or function.
118+
*
115119
* @param (int|string)[] $conditions
116120
*
117121
* @return int|string|null
118122
*/
119123
public static function getClosestIfPositionIfBeforeOtherConditions(array $conditions)
120124
{
121-
// Return true if the token conditions are within an if block before
122-
// they are within a class or function.
123125
$conditionsInsideOut = array_reverse($conditions, true);
124126
if (empty($conditions)) {
125127
return null;
@@ -1396,4 +1398,55 @@ public static function isConstructorPromotion(File $phpcsFile, $stackPtr)
13961398

13971399
return false;
13981400
}
1401+
1402+
/**
1403+
* Return true if the token is inside an abstract class.
1404+
*
1405+
* @param File $phpcsFile
1406+
* @param int $stackPtr
1407+
*
1408+
* @return bool
1409+
*/
1410+
public static function isInAbstractClass(File $phpcsFile, $stackPtr)
1411+
{
1412+
$classIndex = $phpcsFile->getCondition($stackPtr, T_CLASS);
1413+
if (! is_int($classIndex)) {
1414+
return false;
1415+
}
1416+
$classProperties = $phpcsFile->getClassProperties($classIndex);
1417+
return $classProperties['is_abstract'];
1418+
}
1419+
1420+
/**
1421+
* Return true if the function body is empty or contains only `return;`
1422+
*
1423+
* @param File $phpcsFile
1424+
* @param int $stackPtr The index of the function keyword.
1425+
*
1426+
* @return bool
1427+
*/
1428+
public static function isFunctionBodyEmpty(File $phpcsFile, $stackPtr)
1429+
{
1430+
$tokens = $phpcsFile->getTokens();
1431+
if ($tokens[$stackPtr]['code'] !== T_FUNCTION) {
1432+
return false;
1433+
}
1434+
$functionScopeStart = $tokens[$stackPtr]['scope_opener'];
1435+
$functionScopeEnd = $tokens[$stackPtr]['scope_closer'];
1436+
$tokensToIgnore = array_merge(
1437+
Tokens::$emptyTokens,
1438+
[
1439+
T_RETURN,
1440+
T_SEMICOLON,
1441+
T_OPEN_CURLY_BRACKET,
1442+
T_CLOSE_CURLY_BRACKET,
1443+
]
1444+
);
1445+
for ($i = $functionScopeStart; $i < $functionScopeEnd; $i++) {
1446+
if (! in_array($tokens[$i]['code'], $tokensToIgnore, true)) {
1447+
return false;
1448+
}
1449+
}
1450+
return true;
1451+
}
13991452
}

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1948,6 +1948,24 @@ protected function processScopeCloseForVariable(File $phpcsFile, VariableInfo $v
19481948
if ($scopeInfo->scopeStartIndex === 0 && $this->allowUnusedVariablesInFileScope) {
19491949
return;
19501950
}
1951+
if (
1952+
! empty($varInfo->firstDeclared)
1953+
&& $varInfo->scopeType === ScopeType::PARAM
1954+
&& Helpers::isInAbstractClass(
1955+
$phpcsFile,
1956+
Helpers::getFunctionIndexForFunctionParameter($phpcsFile, $varInfo->firstDeclared) ?: 0
1957+
)
1958+
&& Helpers::isFunctionBodyEmpty(
1959+
$phpcsFile,
1960+
Helpers::getFunctionIndexForFunctionParameter($phpcsFile, $varInfo->firstDeclared) ?: 0
1961+
)
1962+
) {
1963+
// Allow non-abstract methods inside an abstract class to have unused
1964+
// parameters if the method body does nothing. Such methods are
1965+
// effectively optional abstract methods so their unused parameters
1966+
// should be ignored as we do with abstract method parameters.
1967+
return;
1968+
}
19511969

19521970
$this->warnAboutUnusedVariable($phpcsFile, $varInfo);
19531971
}

0 commit comments

Comments
 (0)