Skip to content

Commit eb3037b

Browse files
committed
UnusedPrivateElementsSniff: fixed false positive when property used in new
1 parent f77cd97 commit eb3037b

File tree

3 files changed

+41
-12
lines changed

3 files changed

+41
-12
lines changed

SlevomatCodingStandard/Sniffs/Classes/UnusedPrivateElementsSniff.php

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,15 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer):
124124
return false;
125125
};
126126

127-
$checkPropertyUsage = function (int $propertyNameTokenPointer) use ($phpcsFile, $tokens, &$reportedProperties, &$writeOnlyProperties): void {
127+
$checkPropertyUsage = function (int $propertyNameTokenPointer, int $thisOrSelfTokenPointer) use ($phpcsFile, $tokens, &$reportedProperties, &$writeOnlyProperties): void {
128128
$propertyName = $this->getNormalizedPropertyName($tokens[$propertyNameTokenPointer]['content']);
129129

130+
$possibleNewTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $thisOrSelfTokenPointer - 1);
131+
if ($possibleNewTokenPointer !== null && $tokens[$possibleNewTokenPointer]['code'] === T_NEW) {
132+
unset($reportedProperties[$propertyName]);
133+
return;
134+
}
135+
130136
$possibleAssignTokenPointer = TokenHelper::findNextEffective($phpcsFile, $propertyNameTokenPointer + 1);
131137
$possibleAssignToken = $tokens[$possibleAssignTokenPointer];
132138
if (
@@ -157,34 +163,38 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer):
157163
unset($reportedProperties[$propertyName]);
158164
};
159165

160-
$checkObjectOperatorUsage = function (int $objectOperatorTokenPointer) use ($phpcsFile, $tokens, $checkPropertyUsage, &$reportedMethods): int {
166+
$checkObjectOperatorUsage = function (int $objectOperatorTokenPointer, int $thisTokenPointer) use ($phpcsFile, $tokens, $checkPropertyUsage, &$reportedMethods): int {
161167
$nextTokenPointer = TokenHelper::findNextEffective($phpcsFile, $objectOperatorTokenPointer + 1);
162168
$nextToken = $tokens[$nextTokenPointer];
163169
if ($nextToken['code'] !== T_STRING) {
164170
// $variable-> but not accessing a specific property (e. g. $variable->$foo or $variable->{$foo})
165171
return $objectOperatorTokenPointer + 1;
166172
}
173+
167174
$methodCallTokenPointer = TokenHelper::findNextEffective($phpcsFile, $nextTokenPointer + 1);
168175
$methodCallToken = $tokens[$methodCallTokenPointer];
169176
if ($methodCallToken['code'] === T_OPEN_PARENTHESIS) {
170-
// Calling a method on $variable
171-
unset($reportedMethods[$this->getNormalizedMethodName($nextToken['content'])]);
172-
return $methodCallTokenPointer + 1;
177+
$possibleNewTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $thisTokenPointer - 1);
178+
if ($possibleNewTokenPointer === null || $tokens[$possibleNewTokenPointer]['code'] !== T_NEW) {
179+
// Calling a method on $variable
180+
unset($reportedMethods[$this->getNormalizedMethodName($nextToken['content'])]);
181+
return $methodCallTokenPointer + 1;
182+
}
173183
}
174184

175-
$checkPropertyUsage($nextTokenPointer);
185+
$checkPropertyUsage($nextTokenPointer, $thisTokenPointer);
176186

177187
return $nextTokenPointer + 1;
178188
};
179189

180-
$checkDoubleColonUsage = function (int $doubleColonTokenPointer) use ($phpcsFile, $tokens, $checkPropertyUsage, &$reportedMethods, &$reportedConstants): int {
190+
$checkDoubleColonUsage = function (int $doubleColonTokenPointer, int $selfTokenPointer) use ($phpcsFile, $tokens, $checkPropertyUsage, &$reportedMethods, &$reportedConstants): int {
181191
$nextTokenPointer = TokenHelper::findNextEffective($phpcsFile, $doubleColonTokenPointer + 1);
182192
$nextToken = $tokens[$nextTokenPointer];
183193
if ($nextToken['code'] !== T_STRING) {
184194
// self:: or static:: not followed by a string - possible static property access
185195

186196
if ($nextToken['code'] === T_VARIABLE) {
187-
$checkPropertyUsage($nextTokenPointer);
197+
$checkPropertyUsage($nextTokenPointer, $selfTokenPointer);
188198
}
189199
return $nextTokenPointer + 1;
190200
}
@@ -238,7 +248,7 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer):
238248
} elseif ($token['code'] === T_OBJECT_OPERATOR) {
239249
$variableTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokenPointer - 1);
240250
if ($tokens[$variableTokenPointer]['code'] === T_VARIABLE && $tokens[$variableTokenPointer]['content'] === '$this') {
241-
$findUsagesStartTokenPointer = $checkObjectOperatorUsage($tokenPointer);
251+
$findUsagesStartTokenPointer = $checkObjectOperatorUsage($tokenPointer, $variableTokenPointer);
242252
} else {
243253
$possibleThisTokenPointer = $tokenPointer - 1;
244254
do {
@@ -265,7 +275,7 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer):
265275
} elseif ($token['code'] === T_DOUBLE_COLON) {
266276
$previousTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokenPointer - 1);
267277
if ($isCurrentClass($previousTokenPointer)) {
268-
$findUsagesStartTokenPointer = $checkDoubleColonUsage($tokenPointer);
278+
$findUsagesStartTokenPointer = $checkDoubleColonUsage($tokenPointer, $previousTokenPointer);
269279
} else {
270280
$findUsagesStartTokenPointer = $tokenPointer + 1;
271281
}
@@ -281,9 +291,9 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer):
281291

282292
$afterActualTokenPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
283293
if ($tokens[$afterActualTokenPointer]['code'] === T_OBJECT_OPERATOR) {
284-
$checkObjectOperatorUsage($afterActualTokenPointer);
294+
$checkObjectOperatorUsage($afterActualTokenPointer, $i);
285295
} elseif ($tokens[$afterActualTokenPointer]['code'] === T_DOUBLE_COLON) {
286-
$checkDoubleColonUsage($afterActualTokenPointer);
296+
$checkDoubleColonUsage($afterActualTokenPointer, $i);
287297
}
288298
}
289299
}

tests/Sniffs/Classes/UnusedPrivateElementsSniffTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ public function testErrors(): void
105105
self::assertNoSniffError($resultFile, 130);
106106
self::assertNoSniffError($resultFile, 135);
107107
self::assertNoSniffError($resultFile, 159);
108+
109+
self::assertNoSniffError($resultFile, 161);
110+
self::assertNoSniffError($resultFile, 169);
108111
}
109112

110113
public function testOnlyPublicElements(): void

tests/Sniffs/Classes/data/classWithSomeUnusedElements.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,20 @@ public function methodWithHeredoc()
158158
*/
159159
private $propertyWithPrefixAnnotation;
160160

161+
private $propertyUsedToCreateNewInstanceWithoutParentheses;
162+
163+
public function createNewInstancesWithoutParentheses()
164+
{
165+
$this->propertyUsedToCreateNewInstanceWithoutParentheses = \stdClass::class;
166+
new $this->propertyUsedToCreateNewInstanceWithoutParentheses;
167+
}
168+
169+
private $propertyUsedToCreateNewInstanceWithParentheses;
170+
171+
public function createNewInstancesWithParentheses()
172+
{
173+
$this->propertyUsedToCreateNewInstanceWithParentheses = \stdClass::class;
174+
new $this->propertyUsedToCreateNewInstanceWithParentheses();
175+
}
176+
161177
}

0 commit comments

Comments
 (0)