Skip to content

Commit 1b8ee46

Browse files
authored
Add allowUnusedParametersBeforeUsed option (#58)
* Tests: add test for ignoreUnusedArgsBeforeUsed * Add ignoreUnusedArgsBeforeUsed property * Dev: Add ImportDetection to phpcs sniffs * Clean up imports in VariableAnalysisTest * Remove old TODOs * Dev: Ignore LineLength.TooLong * Fix typo in validUndefinedVariableNames declaration * Rename property to allowUnusedParametersBeforeUsed
1 parent 1d89f26 commit 1b8ee46

File tree

7 files changed

+74
-13
lines changed

7 files changed

+74
-13
lines changed

VariableAnalysis/Lib/Helpers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace VariableAnalysis\Lib;
44

55
use PHP_CodeSniffer\Files\File;
6+
use VariableAnalysis\Lib\VariableInfo;
67

78
class Helpers {
89
public static function findContainingOpeningSquareBracket(File $phpcsFile, $stackPtr) {
@@ -86,7 +87,6 @@ public static function findFunctionCallArguments(File $phpcsFile, $stackPtr) {
8687
$tokens = $phpcsFile->getTokens();
8788

8889
// Slight hack: also allow this to find args for array constructor.
89-
// TODO: probably should refactor into three functions: arg-finding and bracket-finding
9090
if (($tokens[$stackPtr]['code'] !== T_STRING) && ($tokens[$stackPtr]['code'] !== T_ARRAY)) {
9191
// Assume $stackPtr is something within the brackets, find our function call
9292
$stackPtr = Helpers::findFunctionCall($phpcsFile, $stackPtr);

VariableAnalysis/Lib/ScopeInfo.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,5 @@ class ScopeInfo {
1313

1414
public function __construct($currScope) {
1515
$this->owner = $currScope;
16-
// TODO: extract opener/closer
1716
}
1817
}

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,13 @@ class VariableAnalysisSniff implements Sniff {
5959
* ignore from undefined variable warnings. For example, to ignore the variables
6060
* `$post` and `$undefined`, this could be set to `'post undefined'`.
6161
*/
62-
public $validUdefinedVariableNames = null;
62+
public $validUndefinedVariableNames = null;
63+
64+
/**
65+
* Allows unused arguments in a function definition if they are
66+
* followed by an argument which is used.
67+
*/
68+
public $allowUnusedParametersBeforeUsed = false;
6369

6470
public function register() {
6571
return [
@@ -153,6 +159,23 @@ protected function getOrCreateVariableInfo($varName, $currScope) {
153159
return $scopeInfo->variables[$varName];
154160
}
155161

162+
protected function areFollowingArgumentsUsed($varInfo, $scopeInfo) {
163+
$foundVarPosition = false;
164+
foreach ($scopeInfo->variables as $variable) {
165+
if ($variable === $varInfo) {
166+
$foundVarPosition = true;
167+
continue;
168+
}
169+
if (! $foundVarPosition) {
170+
continue;
171+
}
172+
if ($variable->firstRead) {
173+
return true;
174+
}
175+
}
176+
return false;
177+
}
178+
156179
protected function markVariableAssignment($varName, $stackPtr, $currScope) {
157180
$varInfo = $this->getOrCreateVariableInfo($varName, $currScope);
158181
if (!isset($varInfo->scopeType)) {
@@ -223,7 +246,6 @@ protected function isVariableUndefined($varName, $stackPtr, $currScope) {
223246
return false;
224247
}
225248
if (isset($varInfo->firstDeclared) && $varInfo->firstDeclared <= $stackPtr) {
226-
// TODO: do we want to check scopeType here?
227249
return false;
228250
}
229251
if (isset($varInfo->firstInitialized) && $varInfo->firstInitialized <= $stackPtr) {
@@ -261,7 +283,6 @@ protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varNam
261283
if (($functionPtr !== false)
262284
&& (($tokens[$functionPtr]['code'] === T_FUNCTION)
263285
|| ($tokens[$functionPtr]['code'] === T_CLOSURE))) {
264-
// TODO: typeHint
265286
$this->markVariableDeclaration($varName, 'param', null, $stackPtr, $functionPtr);
266287
// Are we pass-by-reference?
267288
$referencePtr = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true);
@@ -287,7 +308,6 @@ protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varNam
287308
// $functionPtr is at the use, we need the function keyword for start of scope.
288309
$functionPtr = $phpcsFile->findPrevious(T_CLOSURE, $functionPtr - 1, $currScope + 1, false, null, true);
289310
if ($functionPtr !== false) {
290-
// TODO: typeHints in use?
291311
$this->markVariableDeclaration($varName, 'bound', null, $stackPtr, $functionPtr);
292312
$this->markVariableAssignment($varName, $stackPtr, $functionPtr);
293313

@@ -317,7 +337,6 @@ protected function checkForCatchBlock(File $phpcsFile, $stackPtr, $varName, $cur
317337
$catchPtr = $phpcsFile->findPrevious(T_WHITESPACE, $openPtr - 1, null, true, null, true);
318338
if (($catchPtr !== false) && ($tokens[$catchPtr]['code'] === T_CATCH)) {
319339
// Scope of the exception var is actually the function, not just the catch block.
320-
// TODO: typeHint
321340
$this->markVariableDeclaration($varName, 'local', null, $stackPtr, $currScope, true);
322341
$this->markVariableAssignment($varName, $stackPtr, $currScope);
323342
if ($this->allowUnusedCaughtExceptions) {
@@ -405,7 +424,6 @@ protected function checkForStaticMember(File $phpcsFile, $stackPtr, $varName, $c
405424

406425
protected function checkForStaticOutsideClass(File $phpcsFile, $stackPtr, $varName, $currScope) {
407426
// Are we refering to self:: outside a class?
408-
// TODO: not sure this is our business or should be some other sniff.
409427
410428
$tokens = $phpcsFile->getTokens();
411429
$token = $tokens[$stackPtr];
@@ -925,17 +943,20 @@ protected function processScopeClose(File $phpcsFile, $stackPtr) {
925943
return;
926944
}
927945
foreach ($scopeInfo->variables as $varInfo) {
928-
$this->processScopeCloseForVariable($phpcsFile, $varInfo);
946+
$this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo);
929947
}
930948
}
931949

932-
protected function processScopeCloseForVariable($phpcsFile, $varInfo) {
950+
protected function processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo) {
933951
if ($varInfo->ignoreUnused || isset($varInfo->firstRead)) {
934952
return;
935953
}
936954
if ($this->allowUnusedFunctionParameters && $varInfo->scopeType === 'param') {
937955
return;
938956
}
957+
if ($this->allowUnusedParametersBeforeUsed && $varInfo->scopeType === 'param' && $this->areFollowingArgumentsUsed($varInfo, $scopeInfo)) {
958+
return;
959+
}
939960
if ($varInfo->passByReference && isset($varInfo->firstInitialized)) {
940961
// If we're pass-by-reference then it's a common pattern to
941962
// use the variable to return data to the caller, so any

VariableAnalysis/Tests/CodeAnalysis/VariableAnalysisTest.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
<?php
22
namespace VariableAnalysis\Tests;
33

4-
use PHP_CodeSniffer\Files\LocalFile;
5-
use PHP_CodeSniffer\Ruleset;
6-
use PHP_CodeSniffer\Config;
4+
use VariableAnalysis\Tests\BaseTestCase;
75

86
class VariableAnalysisTest extends BaseTestCase {
97
public function testFunctionWithoutParamsErrors() {
@@ -599,4 +597,32 @@ public function testValidUndefinedVariableNamesIgnoresUndefinedProperties() {
599597
];
600598
$this->assertEquals($expectedWarnings, $lines);
601599
}
600+
601+
public function testUnusedArgumentsBeforeUsedArgumentsAreFound() {
602+
$fixtureFile = $this->getFixture('UnusedAfterUsedFixture.php');
603+
$phpcsFile = $this->prepareLocalFileForSniffs($this->getSniffFiles(), $fixtureFile);
604+
$phpcsFile->process();
605+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
606+
$expectedWarnings = [
607+
5,
608+
8,
609+
];
610+
$this->assertEquals($expectedWarnings, $lines);
611+
}
612+
613+
public function testUnusedArgumentsBeforeUsedArgumentsAreIgnoredIfSet() {
614+
$fixtureFile = $this->getFixture('UnusedAfterUsedFixture.php');
615+
$phpcsFile = $this->prepareLocalFileForSniffs($this->getSniffFiles(), $fixtureFile);
616+
$phpcsFile->ruleset->setSniffProperty(
617+
'VariableAnalysis\Sniffs\CodeAnalysis\VariableAnalysisSniff',
618+
'allowUnusedParametersBeforeUsed',
619+
'true'
620+
);
621+
$phpcsFile->process();
622+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
623+
$expectedWarnings = [
624+
8,
625+
];
626+
$this->assertEquals($expectedWarnings, $lines);
627+
}
602628
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
add_action( 'delete_post_meta', 'check_thumbnail_updated_post_meta', -1000, 3 );
4+
function check_thumbnail_updated_post_meta(
5+
$meta_id,
6+
$post_id,
7+
$meta_key,
8+
$foobar
9+
) {
10+
echo $post_id;
11+
echo $meta_key;
12+
}

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"require-dev": {
3939
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
4040
"phpunit/phpunit": "^6.5",
41+
"sirbrillig/phpcs-import-detection": "^1.1",
4142
"squizlabs/php_codesniffer": "^3.1",
4243
"limedeck/phpunit-detailed-printer": "^3.1"
4344
}

phpcs.xml.dist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine" />
1111
<exclude name="PSR2.Classes.ClassDeclaration.OpenBraceNewLine" />
1212
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
13+
<exclude name="Generic.Files.LineLength.TooLong" />
1314
</rule>
1415
<arg name="tab-width" value="2"/>
1516
<rule ref="Generic.WhiteSpace.ScopeIndent">
@@ -29,4 +30,5 @@
2930
</rule>
3031
<rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie" />
3132
<rule ref="Generic.Classes.OpeningBraceSameLine"/>
33+
<rule ref="ImportDetection" />
3234
</ruleset>

0 commit comments

Comments
 (0)