Skip to content

Commit

Permalink
Ignore double assignment by reference if second is inside condition (#…
Browse files Browse the repository at this point in the history
…235)

* Add test for overwriting assignment by reference inside conditional

* Allow overwriting reference assignments within conditions

* Make sure unused reference reassignments still trigger in conditions

* Include new test case in other tests

* Compare position of IF token and last assignment

* Fix return type of new function
  • Loading branch information
sirbrillig authored Jun 18, 2021
1 parent 72cb4f9 commit 22c83ec
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Tests/VariableAnalysisSniff/VariableAnalysisTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,7 @@ public function testAssignmentByReference() {
34,
35,
43,
70,
];
$this->assertEquals($expectedWarnings, $lines);
}
Expand All @@ -1058,6 +1059,7 @@ public function testAssignmentByReferenceWithIgnoreUnusedMatch() {
26,
34,
35,
70,
];
$this->assertEquals($expectedWarnings, $lines);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,21 @@ function doubleUsedThenUsedAssignmentByReference() {
$var = &$bee;
return $var;
}

function somefunc($choice, &$arr1, &$arr_default) {
$var = &$arr_default;

if ($choice) {
$var = &$arr1;
}

echo $var;
}

function somefunc($choice, &$arr1, &$arr_default) {
if ($choice) {
$var = &$arr_default; // unused variable $var
$var = &$arr1;
echo $var;
}
}
19 changes: 19 additions & 0 deletions VariableAnalysis/Lib/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@ public static function areConditionsWithinFunctionBeforeClass(array $conditions)
return false;
}

/**
* @param (int|string)[] $conditions
*
* @return int|string|null
*/
public static function getClosestIfPositionIfBeforeOtherConditions(array $conditions) {
// Return true if the token conditions are within an if block before
// they are within a class or function.
$conditionsInsideOut = array_reverse($conditions, true);
if (empty($conditions)) {
return null;
}
$scopeCode = reset($conditionsInsideOut);
if ($scopeCode === T_IF) {
return key($conditionsInsideOut);
}
return null;
}

/**
* @param File $phpcsFile
* @param int $stackPtr
Expand Down
13 changes: 11 additions & 2 deletions VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -937,9 +937,18 @@ protected function processVariableAsAssignment(File $phpcsFile, $stackPtr, $varN
Helpers::debug('processVariableAsAssignment: found reference variable');
$varInfo = $this->getOrCreateVariableInfo($varName, $currScope);
// If the variable was already declared, but was not yet read, it is
// unused because we're about to change the binding.
// unused because we're about to change the binding; that is, unless we
// are inside a conditional block because in that case the condition may
// never activate.
$scopeInfo = $this->getOrCreateScopeInfo($currScope);
$this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo);
$ifPtr = Helpers::getClosestIfPositionIfBeforeOtherConditions($tokens[$referencePtr]['conditions']);
$lastAssignmentPtr = $varInfo->firstDeclared;
if (! $ifPtr && $lastAssignmentPtr) {
$this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo);
}
if ($ifPtr && $lastAssignmentPtr && $ifPtr <= $lastAssignmentPtr) {
$this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo);
}
// The referenced variable may have a different name, but we don't
// actually need to mark it as used in this case because the act of this
// assignment will mark it used on the next token.
Expand Down

0 comments on commit 22c83ec

Please sign in to comment.