Skip to content

Commit 3b19ddf

Browse files
authored
Add allowUnusedVariablesBeforeRequire option (#196)
* Add tests for unused before require * Move areFollowingArgumentsUsed to Helpers * Add test cases for all forms of require/include * Add isRequireInScopeAfter and use allowUnusedVariablesBeforeRequire * Add test to be sure option does not break other behavior * Add allowUnusedVariablesBeforeRequire to README
1 parent 26bf3d3 commit 3b19ddf

File tree

5 files changed

+171
-27
lines changed

5 files changed

+171
-27
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ The available options are as follows:
6767
- `allowUnusedFunctionParameters` (bool, default `false`): if set to true, function arguments will never be marked as unused.
6868
- `allowUnusedCaughtExceptions` (bool, default `true`): if set to true, caught Exception variables will never be marked as unused.
6969
- `allowUnusedParametersBeforeUsed` (bool, default `true`): if set to true, unused function arguments will be ignored if they are followed by used function arguments.
70+
- `allowUnusedVariablesBeforeRequire` (bool, default `false`): if set to true, variables defined before a `require`, `require_once`, `include`, or `include_once` will not be marked as unused. They may be intended for the required file.
7071
- `allowUndefinedVariablesInFileScope` (bool, default `false`): if set to true, undefined variables in the file's top-level scope will never be marked as undefined.
7172
- `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'`.
7273
- `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 `'/^_/'`.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
namespace VariableAnalysis\Tests\VariableAnalysisSniff;
3+
4+
use VariableAnalysis\Tests\BaseTestCase;
5+
6+
class UnusedFollowedByRequire extends BaseTestCase {
7+
public function testUnusedFollowedByRequireWarnsByDefault() {
8+
$fixtureFile = $this->getFixture('UnusedFollowedByRequireFixture.php');
9+
$phpcsFile = $this->prepareLocalFileForSniffs($fixtureFile);
10+
$phpcsFile->process();
11+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
12+
$expectedWarnings = [
13+
2,
14+
3,
15+
4,
16+
8,
17+
9,
18+
10,
19+
14,
20+
15,
21+
16,
22+
20,
23+
21,
24+
22,
25+
];
26+
$this->assertEquals($expectedWarnings, $lines);
27+
}
28+
29+
public function testUnusedFollowedByRequireDoesNotWarnWhenSet() {
30+
$fixtureFile = $this->getFixture('UnusedFollowedByRequireFixture.php');
31+
$phpcsFile = $this->prepareLocalFileForSniffs($fixtureFile);
32+
$phpcsFile->ruleset->setSniffProperty(
33+
'VariableAnalysis\Sniffs\CodeAnalysis\VariableAnalysisSniff',
34+
'allowUnusedVariablesBeforeRequire',
35+
'true'
36+
);
37+
$phpcsFile->process();
38+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
39+
$expectedWarnings = [
40+
4,
41+
10,
42+
16,
43+
22,
44+
];
45+
$this->assertEquals($expectedWarnings, $lines);
46+
}
47+
48+
public function testUnusedFollowedByRequireDoesNotBreakOtherThingsWhenSet() {
49+
$fixtureFile = $this->getFixture('FunctionWithoutParamFixture.php');
50+
$phpcsFile = $this->prepareLocalFileForSniffs($fixtureFile);
51+
$phpcsFile->ruleset->setSniffProperty(
52+
'VariableAnalysis\Sniffs\CodeAnalysis\VariableAnalysisSniff',
53+
'allowUnusedVariablesBeforeRequire',
54+
'true'
55+
);
56+
$phpcsFile->process();
57+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
58+
$expectedWarnings = [
59+
4,
60+
5,
61+
6,
62+
7,
63+
8,
64+
9,
65+
10,
66+
11,
67+
12,
68+
13,
69+
18,
70+
19,
71+
];
72+
$this->assertEquals($expectedWarnings, $lines);
73+
}
74+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
function require_file_function($param) { // unused variable $param
3+
$var = 'something'; // unused variable $var
4+
activate_code($data); // undefined variable $data
5+
require __DIR__ . '/views/my-view.php';
6+
}
7+
8+
function require_once_file_function($param) { // unused variable $param
9+
$var = 'something'; // unused variable $var
10+
activate_code($data); // undefined variable $data
11+
require_once __DIR__ . '/views/my-view.php';
12+
}
13+
14+
function include_file_function($param) { // unused variable $param
15+
$var = 'something'; // unused variable $var
16+
activate_code($data); // undefined variable $data
17+
include __DIR__ . '/views/my-view.php';
18+
}
19+
20+
function include_once_file_function($param) { // unused variable $param
21+
$var = 'something'; // unused variable $var
22+
activate_code($data); // undefined variable $data
23+
include_once __DIR__ . '/views/my-view.php';
24+
}

VariableAnalysis/Lib/Helpers.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
namespace VariableAnalysis\Lib;
44

55
use PHP_CodeSniffer\Files\File;
6+
use VariableAnalysis\Lib\ScopeInfo;
7+
use VariableAnalysis\Lib\ScopeType;
8+
use VariableAnalysis\Lib\VariableInfo;
69
use PHP_CodeSniffer\Util\Tokens;
710
use PHPCSUtils\Utils\FunctionDeclarations;
811

@@ -652,4 +655,60 @@ public static function getLastNonEmptyTokenIndexInFile(File $phpcsFile) {
652655
self::debug('no non-empty token found for end of file');
653656
return 0;
654657
}
658+
659+
/**
660+
* @param VariableInfo $varInfo
661+
* @param ScopeInfo $scopeInfo
662+
*
663+
* @return bool
664+
*/
665+
public static function areFollowingArgumentsUsed(VariableInfo $varInfo, ScopeInfo $scopeInfo) {
666+
$foundVarPosition = false;
667+
foreach ($scopeInfo->variables as $variable) {
668+
if ($variable === $varInfo) {
669+
$foundVarPosition = true;
670+
continue;
671+
}
672+
if (! $foundVarPosition) {
673+
continue;
674+
}
675+
if ($variable->scopeType !== ScopeType::PARAM) {
676+
continue;
677+
}
678+
if ($variable->firstRead) {
679+
return true;
680+
}
681+
}
682+
return false;
683+
}
684+
685+
/**
686+
* @param File $phpcsFile
687+
* @param VariableInfo $varInfo
688+
* @param ScopeInfo $scopeInfo
689+
*
690+
* @return bool
691+
*/
692+
public static function isRequireInScopeAfter(File $phpcsFile, VariableInfo $varInfo, ScopeInfo $scopeInfo) {
693+
$requireTokens = [
694+
T_REQUIRE,
695+
T_REQUIRE_ONCE,
696+
T_INCLUDE,
697+
T_INCLUDE_ONCE,
698+
];
699+
$indexToStartSearch = $varInfo->firstDeclared;
700+
if (! empty($varInfo->firstInitialized)) {
701+
$indexToStartSearch = $varInfo->firstInitialized;
702+
}
703+
$tokens = $phpcsFile->getTokens();
704+
$indexToStopSearch = isset($tokens[$scopeInfo->owner]['scope_closer']) ? $tokens[$scopeInfo->owner]['scope_closer'] : null;
705+
if (! is_int($indexToStartSearch) || ! is_int($indexToStopSearch)) {
706+
return false;
707+
}
708+
$requireTokenIndex = $phpcsFile->findNext($requireTokens, $indexToStartSearch + 1, $indexToStopSearch);
709+
if (is_int($requireTokenIndex)) {
710+
return true;
711+
}
712+
return false;
713+
}
655714
}

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ class VariableAnalysisSniff implements Sniff {
131131
*/
132132
public $allowUnusedForeachVariables = true;
133133

134+
/**
135+
* If set to true, unused variables in a function before a require or import
136+
* statement will not be marked as unused because they may be used in the
137+
* required file.
138+
*
139+
* @var bool
140+
*/
141+
public $allowUnusedVariablesBeforeRequire = false;
142+
134143
/**
135144
* @return (int|string)[]
136145
*/
@@ -337,32 +346,6 @@ protected function getOrCreateVariableInfo($varName, $currScope) {
337346
return $scopeInfo->variables[$varName];
338347
}
339348

340-
/**
341-
* @param VariableInfo $varInfo
342-
* @param ScopeInfo $scopeInfo
343-
*
344-
* @return bool
345-
*/
346-
protected function areFollowingArgumentsUsed($varInfo, $scopeInfo) {
347-
$foundVarPosition = false;
348-
foreach ($scopeInfo->variables as $variable) {
349-
if ($variable === $varInfo) {
350-
$foundVarPosition = true;
351-
continue;
352-
}
353-
if (! $foundVarPosition) {
354-
continue;
355-
}
356-
if ($variable->scopeType !== ScopeType::PARAM) {
357-
continue;
358-
}
359-
if ($variable->firstRead) {
360-
return true;
361-
}
362-
}
363-
return false;
364-
}
365-
366349
/**
367350
* @param string $varName
368351
* @param int $stackPtr
@@ -1601,7 +1584,7 @@ protected function processScopeCloseForVariable(File $phpcsFile, VariableInfo $v
16011584
if ($this->allowUnusedFunctionParameters && $varInfo->scopeType === ScopeType::PARAM) {
16021585
return;
16031586
}
1604-
if ($this->allowUnusedParametersBeforeUsed && $varInfo->scopeType === ScopeType::PARAM && $this->areFollowingArgumentsUsed($varInfo, $scopeInfo)) {
1587+
if ($this->allowUnusedParametersBeforeUsed && $varInfo->scopeType === ScopeType::PARAM && Helpers::areFollowingArgumentsUsed($varInfo, $scopeInfo)) {
16051588
Helpers::debug("variable {$varInfo->name} at end of scope has unused following args");
16061589
return;
16071590
}
@@ -1624,6 +1607,9 @@ protected function processScopeCloseForVariable(File $phpcsFile, VariableInfo $v
16241607
if (empty($varInfo->firstDeclared) && empty($varInfo->firstInitialized)) {
16251608
return;
16261609
}
1610+
if ($this->allowUnusedVariablesBeforeRequire && Helpers::isRequireInScopeAfter($phpcsFile, $varInfo, $scopeInfo)) {
1611+
return;
1612+
}
16271613
$this->warnAboutUnusedVariable($phpcsFile, $varInfo);
16281614
}
16291615

0 commit comments

Comments
 (0)