@@ -59,7 +59,13 @@ class VariableAnalysisSniff implements Sniff {
59
59
* ignore from undefined variable warnings. For example, to ignore the variables
60
60
* `$post` and `$undefined`, this could be set to `'post undefined'`.
61
61
*/
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 ;
63
69
64
70
public function register () {
65
71
return [
@@ -153,6 +159,23 @@ protected function getOrCreateVariableInfo($varName, $currScope) {
153
159
return $ scopeInfo ->variables [$ varName ];
154
160
}
155
161
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
+
156
179
protected function markVariableAssignment ($ varName , $ stackPtr , $ currScope ) {
157
180
$ varInfo = $ this ->getOrCreateVariableInfo ($ varName , $ currScope );
158
181
if (!isset ($ varInfo ->scopeType )) {
@@ -223,7 +246,6 @@ protected function isVariableUndefined($varName, $stackPtr, $currScope) {
223
246
return false ;
224
247
}
225
248
if (isset ($ varInfo ->firstDeclared ) && $ varInfo ->firstDeclared <= $ stackPtr ) {
226
- // TODO: do we want to check scopeType here?
227
249
return false ;
228
250
}
229
251
if (isset ($ varInfo ->firstInitialized ) && $ varInfo ->firstInitialized <= $ stackPtr ) {
@@ -261,7 +283,6 @@ protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varNam
261
283
if (($ functionPtr !== false )
262
284
&& (($ tokens [$ functionPtr ]['code ' ] === T_FUNCTION )
263
285
|| ($ tokens [$ functionPtr ]['code ' ] === T_CLOSURE ))) {
264
- // TODO: typeHint
265
286
$ this ->markVariableDeclaration ($ varName , 'param ' , null , $ stackPtr , $ functionPtr );
266
287
// Are we pass-by-reference?
267
288
$ referencePtr = $ phpcsFile ->findPrevious (T_WHITESPACE , $ stackPtr - 1 , null , true , null , true );
@@ -287,7 +308,6 @@ protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varNam
287
308
// $functionPtr is at the use, we need the function keyword for start of scope.
288
309
$ functionPtr = $ phpcsFile ->findPrevious (T_CLOSURE , $ functionPtr - 1 , $ currScope + 1 , false , null , true );
289
310
if ($ functionPtr !== false ) {
290
- // TODO: typeHints in use?
291
311
$ this ->markVariableDeclaration ($ varName , 'bound ' , null , $ stackPtr , $ functionPtr );
292
312
$ this ->markVariableAssignment ($ varName , $ stackPtr , $ functionPtr );
293
313
@@ -317,7 +337,6 @@ protected function checkForCatchBlock(File $phpcsFile, $stackPtr, $varName, $cur
317
337
$ catchPtr = $ phpcsFile ->findPrevious (T_WHITESPACE , $ openPtr - 1 , null , true , null , true );
318
338
if (($ catchPtr !== false ) && ($ tokens [$ catchPtr ]['code ' ] === T_CATCH )) {
319
339
// Scope of the exception var is actually the function, not just the catch block.
320
- // TODO: typeHint
321
340
$ this ->markVariableDeclaration ($ varName , 'local ' , null , $ stackPtr , $ currScope , true );
322
341
$ this ->markVariableAssignment ($ varName , $ stackPtr , $ currScope );
323
342
if ($ this ->allowUnusedCaughtExceptions ) {
@@ -405,7 +424,6 @@ protected function checkForStaticMember(File $phpcsFile, $stackPtr, $varName, $c
405
424
406
425
protected function checkForStaticOutsideClass (File $ phpcsFile , $ stackPtr , $ varName , $ currScope ) {
407
426
// Are we refering to self:: outside a class?
408
- // TODO: not sure this is our business or should be some other sniff.
409
427
410
428
$ tokens = $ phpcsFile ->getTokens ();
411
429
$ token = $ tokens [$ stackPtr ];
@@ -925,17 +943,20 @@ protected function processScopeClose(File $phpcsFile, $stackPtr) {
925
943
return ;
926
944
}
927
945
foreach ($ scopeInfo ->variables as $ varInfo ) {
928
- $ this ->processScopeCloseForVariable ($ phpcsFile , $ varInfo );
946
+ $ this ->processScopeCloseForVariable ($ phpcsFile , $ varInfo, $ scopeInfo );
929
947
}
930
948
}
931
949
932
- protected function processScopeCloseForVariable ($ phpcsFile , $ varInfo ) {
950
+ protected function processScopeCloseForVariable ($ phpcsFile , $ varInfo, $ scopeInfo ) {
933
951
if ($ varInfo ->ignoreUnused || isset ($ varInfo ->firstRead )) {
934
952
return ;
935
953
}
936
954
if ($ this ->allowUnusedFunctionParameters && $ varInfo ->scopeType === 'param ' ) {
937
955
return ;
938
956
}
957
+ if ($ this ->allowUnusedParametersBeforeUsed && $ varInfo ->scopeType === 'param ' && $ this ->areFollowingArgumentsUsed ($ varInfo , $ scopeInfo )) {
958
+ return ;
959
+ }
939
960
if ($ varInfo ->passByReference && isset ($ varInfo ->firstInitialized )) {
940
961
// If we're pass-by-reference then it's a common pattern to
941
962
// use the variable to return data to the caller, so any
0 commit comments