Skip to content

Commit 30e1278

Browse files
authored
Add ignore unused foreach option (#66)
* Tests: add tests for ignoreUnusedForeachVariables * README: Add ignoreUnusedForeachVariables * Tests: add normal warnings just to be sure * Add ignoreUnusedForeachVariables option * Add tags to gitignore * Add isForeachLoopVar to VariableInfo * Rename variable to allowUnusedForeachVariables This better matches the other bool options. * Tests: Add foreach loop without key
1 parent 6e022ca commit 30e1278

File tree

6 files changed

+101
-1
lines changed

6 files changed

+101
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
phpunit.xml
44
.phpcs.xml
55
phpcs.xml
6+
tags

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ The available options are as follows:
6868
- `validUnusedVariableNames` (string, default `null`): a space-separated list of names of placeholder variables that you want to ignore from unused variable warnings. For example, to ignore the variables `$junk` and `$unused`, this could be set to `'junk unused'`.
6969
- `ignoreUnusedRegexp` (string, default `null`): a PHP regexp string (note that this requires explicit delimiters) for variables that you want to ignore from unused variable warnings. For example, to ignore the variables `$_junk` and `$_unused`, this could be set to `'/^_/'`.
7070
- `validUndefinedVariableNames` (string, default `null`): a space-separated list of names of placeholder variables that you want to ignore from undefined variable warnings. For example, to ignore the variables `$post` and `$undefined`, this could be set to `'post undefined'`.
71+
- `allowUnusedForeachVariables` (bool, default `false`): if set to true, unused keys or values created by the `as` statement in a `foreach` loop will never be marked as unused.
7172

7273
To set these these options, you must use XML in your ruleset. For details, see the [phpcs customizable sniff properties page](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Customisable-Sniff-Properties). Here is an example that ignores all variables that start with an underscore:
7374

VariableAnalysis/Lib/VariableInfo.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class VariableInfo {
1818
public $firstRead;
1919
public $ignoreUnused = false;
2020
public $ignoreUndefined = false;
21+
public $isForeachLoopVar = false;
2122

2223
public static $scopeTypeDescriptions = array(
2324
'local' => 'variable',

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ class VariableAnalysisSniff implements Sniff {
6767
*/
6868
public $allowUnusedParametersBeforeUsed = true;
6969

70+
/**
71+
* If set to true, unused keys or values created by the `as` statement
72+
* in a `foreach` loop will never be marked as unused.
73+
*/
74+
public $allowUnusedForeachVariables = false;
75+
7076
public function register() {
7177
return [
7278
T_VARIABLE,
@@ -644,8 +650,10 @@ protected function checkForForeachLoopVar(File $phpcsFile, $stackPtr, $varName,
644650
if ($phpcsFile->findPrevious(T_AS, $stackPtr - 1, $openParenPtr) === false) {
645651
return false;
646652
}
647-
648653
$this->markVariableAssignment($varName, $stackPtr, $currScope);
654+
$varInfo = $this->getOrCreateVariableInfo($varName, $currScope);
655+
$varInfo->isForeachLoopVar = true;
656+
649657
return true;
650658
}
651659

@@ -974,6 +982,9 @@ protected function processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo
974982
if ($this->allowUnusedParametersBeforeUsed && $varInfo->scopeType === 'param' && $this->areFollowingArgumentsUsed($varInfo, $scopeInfo)) {
975983
return;
976984
}
985+
if ($this->allowUnusedForeachVariables && $varInfo->isForeachLoopVar) {
986+
return;
987+
}
977988
if ($varInfo->passByReference && isset($varInfo->firstInitialized)) {
978989
// If we're pass-by-reference then it's a common pattern to
979990
// use the variable to return data to the caller, so any

VariableAnalysis/Tests/CodeAnalysis/VariableAnalysisTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,4 +700,54 @@ public function testPregReplaceIgnoresNumericVariables() {
700700
];
701701
$this->assertEquals($expectedWarnings, $lines);
702702
}
703+
704+
public function testUnusedForeachVariablesAreNotIgnoredByDefault() {
705+
$fixtureFile = $this->getFixture('UnusedForeachFixture.php');
706+
$phpcsFile = $this->prepareLocalFileForSniffs($this->getSniffFiles(), $fixtureFile);
707+
$phpcsFile->ruleset->setSniffProperty(
708+
'VariableAnalysis\Sniffs\CodeAnalysis\VariableAnalysisSniff',
709+
'allowUnusedForeachVariables',
710+
'false'
711+
);
712+
$phpcsFile->process();
713+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
714+
$expectedWarnings = [
715+
5,
716+
7,
717+
8,
718+
14,
719+
16,
720+
17,
721+
23,
722+
25,
723+
26,
724+
32,
725+
33,
726+
34,
727+
];
728+
$this->assertEquals($expectedWarnings, $lines);
729+
}
730+
731+
public function testUnusedForeachVariablesAreIgnoredIfSet() {
732+
$fixtureFile = $this->getFixture('UnusedForeachFixture.php');
733+
$phpcsFile = $this->prepareLocalFileForSniffs($this->getSniffFiles(), $fixtureFile);
734+
$phpcsFile->ruleset->setSniffProperty(
735+
'VariableAnalysis\Sniffs\CodeAnalysis\VariableAnalysisSniff',
736+
'allowUnusedForeachVariables',
737+
'true'
738+
);
739+
$phpcsFile->process();
740+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
741+
$expectedWarnings = [
742+
7,
743+
8,
744+
16,
745+
17,
746+
25,
747+
26,
748+
33,
749+
34,
750+
];
751+
$this->assertEquals($expectedWarnings, $lines);
752+
}
703753
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
function loopWithUnusedKey() {
4+
$array = [];
5+
foreach ( $array as $key => $value ) { // maybe marked as unused
6+
echo $value;
7+
$unused = 'foobar'; // should always be marked as unused
8+
echo $undefined; // should always be marked as undefined
9+
}
10+
}
11+
12+
function loopWithUnusedValue() {
13+
$array = [];
14+
foreach ( $array as $key => $value ) { // maybe marked as unused
15+
echo $key;
16+
$unused = 'foobar'; // should always be marked as unused
17+
echo $undefined; // should always be marked as undefined
18+
}
19+
}
20+
21+
function loopWithUnusedKeyAndValue() {
22+
$array = [];
23+
foreach ( $array as $key => $value ) { // maybe marked as unused
24+
echo 'hello';
25+
$unused = 'foobar'; // should always be marked as unused
26+
echo $undefined; // should always be marked as undefined
27+
}
28+
}
29+
30+
function loopWithUnusedValueOnly() {
31+
$array = [];
32+
foreach ( $array as $value ) { // maybe marked as unused
33+
$unused = 'foobar'; // should always be marked as unused
34+
echo $undefined; // should always be marked as undefined
35+
}
36+
}

0 commit comments

Comments
 (0)