Skip to content

Commit a20058d

Browse files
committed
Fixed false positive in UnusedPrivateElementsSniff
1 parent d1cb540 commit a20058d

File tree

6 files changed

+130
-94
lines changed

6 files changed

+130
-94
lines changed

SlevomatCodingStandard/Sniffs/Classes/UnusedPrivateElementsSniff.php

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer)
9494
$writeOnlyProperties = [];
9595
$findUsagesStartTokenPointer = $classToken['scope_opener'] + 1;
9696

97-
$checkVariable = function ($tokenPointer) use ($phpcsFile, $tokens, &$reportedMethods, &$reportedProperties, &$writeOnlyProperties) {
97+
$checkObjectOperatorUsage = function ($tokenPointer) use ($phpcsFile, $tokens, &$reportedMethods, &$reportedProperties, &$writeOnlyProperties) {
9898
$objectOperatorTokenPointer = TokenHelper::findNextEffective($phpcsFile, $tokenPointer + 1);
9999
$objectOperatorToken = $tokens[$objectOperatorTokenPointer];
100100
if ($objectOperatorToken['code'] !== T_OBJECT_OPERATOR) {
@@ -132,17 +132,52 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer)
132132
return $propertyNameTokenPointer + 1;
133133
};
134134

135+
$checkDoubleColonUsage = function (int $tokenPointer) use ($phpcsFile, $tokens, &$reportedMethods, &$reportedConstants): int {
136+
$doubleColonTokenPointer = TokenHelper::findNextEffective($phpcsFile, $tokenPointer + 1);
137+
$doubleColonToken = $tokens[$doubleColonTokenPointer];
138+
if ($doubleColonToken['code'] !== T_DOUBLE_COLON) {
139+
// self or static not followed by ::
140+
return $doubleColonTokenPointer + 1;
141+
}
142+
143+
$methodNameTokenPointer = TokenHelper::findNextEffective($phpcsFile, $doubleColonTokenPointer + 1);
144+
$methodNameToken = $tokens[$methodNameTokenPointer];
145+
if ($methodNameToken['code'] !== T_STRING) {
146+
// self:: or static:: not followed by a string - possible static property access
147+
return $methodNameTokenPointer + 1;
148+
}
149+
150+
$methodCallTokenPointer = TokenHelper::findNextEffective($phpcsFile, $methodNameTokenPointer + 1);
151+
$methodCallToken = $tokens[$methodCallTokenPointer];
152+
if ($methodCallToken['code'] !== T_OPEN_PARENTHESIS) {
153+
$name = $methodNameToken['content'];
154+
if (isset($reportedConstants[$name])) {
155+
unset($reportedConstants[$name]);
156+
}
157+
158+
// self::string or static::string not followed by ( - possible constant access
159+
return $methodCallTokenPointer + 1;
160+
}
161+
162+
$name = $methodNameToken['content'];
163+
if (isset($reportedMethods[$name])) {
164+
unset($reportedMethods[$name]);
165+
}
166+
167+
return $methodCallTokenPointer + 1;
168+
};
169+
135170
while (($tokenPointer = TokenHelper::findNext($phpcsFile, [T_VARIABLE, T_SELF, T_STATIC, T_DOUBLE_QUOTED_STRING], $findUsagesStartTokenPointer, $classToken['scope_closer'])) !== null) {
136-
$propertyAccessToken = $tokens[$tokenPointer];
137-
if ($propertyAccessToken['code'] === T_DOUBLE_QUOTED_STRING) {
138-
if (preg_match_all('~(?<!\\\\)\$this->(.+?\b)(?!\()~', $propertyAccessToken['content'], $matches, PREG_PATTERN_ORDER)) {
171+
$token = $tokens[$tokenPointer];
172+
if ($token['code'] === T_DOUBLE_QUOTED_STRING) {
173+
if (preg_match_all('~(?<!\\\\)\$this->(.+?\b)(?!\()~', $token['content'], $matches, PREG_PATTERN_ORDER)) {
139174
foreach ($matches[1] as $propertyInString) {
140175
if (isset($reportedProperties[$propertyInString])) {
141176
unset($reportedProperties[$propertyInString]);
142177
}
143178
}
144179
}
145-
if (preg_match_all('~(?<=\{)\$this->(.+?\b)(?=\()~', $propertyAccessToken['content'], $matches, PREG_PATTERN_ORDER)) {
180+
if (preg_match_all('~(?<=\{)\$this->(.+?\b)(?=\()~', $token['content'], $matches, PREG_PATTERN_ORDER)) {
146181
foreach ($matches[1] as $methodInString) {
147182
if (isset($reportedMethods[$methodInString])) {
148183
unset($reportedMethods[$methodInString]);
@@ -151,58 +186,29 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $classPointer)
151186
}
152187

153188
$findUsagesStartTokenPointer = $tokenPointer + 1;
154-
} elseif ($propertyAccessToken['content'] === '$this') {
155-
$findUsagesStartTokenPointer = $checkVariable($tokenPointer);
156-
} elseif (in_array($propertyAccessToken['code'], [T_SELF, T_STATIC], true)) {
189+
} elseif ($token['content'] === '$this') {
190+
$findUsagesStartTokenPointer = $checkObjectOperatorUsage($tokenPointer);
191+
} elseif (in_array($token['code'], [T_SELF, T_STATIC], true)) {
157192
$newTokenPointer = TokenHelper::findPreviousEffective($phpcsFile, $tokenPointer - 1);
158193
if ($tokens[$newTokenPointer]['code'] === T_NEW) {
159194
$variableTokenPointer = TokenHelper::findPreviousLocal($phpcsFile, T_VARIABLE, $newTokenPointer - 1);
160195
if ($variableTokenPointer !== null) {
161196
$scopeMethodPointer = TokenHelper::findPrevious($phpcsFile, T_FUNCTION, $variableTokenPointer - 1);
162197
for ($i = $tokens[$scopeMethodPointer]['scope_opener']; $i < $tokens[$scopeMethodPointer]['scope_closer']; $i++) {
163198
if ($tokens[$i]['content'] === $tokens[$variableTokenPointer]['content']) {
164-
$findUsagesStartTokenPointer = $checkVariable($i);
199+
$afterActualTokenPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
200+
if ($tokens[$afterActualTokenPointer]['code'] === T_OBJECT_OPERATOR) {
201+
$findUsagesStartTokenPointer = $checkObjectOperatorUsage($i);
202+
} elseif ($tokens[$afterActualTokenPointer]['code'] === T_DOUBLE_COLON) {
203+
$findUsagesStartTokenPointer = $checkDoubleColonUsage($i);
204+
}
165205
}
166206
}
167207
} else {
168208
$findUsagesStartTokenPointer = $tokenPointer + 1;
169209
}
170210
} else {
171-
$doubleColonTokenPointer = TokenHelper::findNextEffective($phpcsFile, $tokenPointer + 1);
172-
$doubleColonToken = $tokens[$doubleColonTokenPointer];
173-
if ($doubleColonToken['code'] !== T_DOUBLE_COLON) {
174-
// self or static not followed by ::
175-
$findUsagesStartTokenPointer = $doubleColonTokenPointer + 1;
176-
continue;
177-
}
178-
179-
$methodNameTokenPointer = TokenHelper::findNextEffective($phpcsFile, $doubleColonTokenPointer + 1);
180-
$methodNameToken = $tokens[$methodNameTokenPointer];
181-
if ($methodNameToken['code'] !== T_STRING) {
182-
// self:: or static:: not followed by a string - possible static property access
183-
$findUsagesStartTokenPointer = $methodNameTokenPointer + 1;
184-
continue;
185-
}
186-
187-
$methodCallTokenPointer = TokenHelper::findNextEffective($phpcsFile, $methodNameTokenPointer + 1);
188-
$methodCallToken = $tokens[$methodCallTokenPointer];
189-
if ($methodCallToken['code'] !== T_OPEN_PARENTHESIS) {
190-
$name = $methodNameToken['content'];
191-
if (isset($reportedConstants[$name])) {
192-
unset($reportedConstants[$name]);
193-
}
194-
195-
// self::string or static::string not followed by ( - possible constant access
196-
$findUsagesStartTokenPointer = $methodCallTokenPointer + 1;
197-
continue;
198-
}
199-
200-
$name = $methodNameToken['content'];
201-
if (isset($reportedMethods[$name])) {
202-
unset($reportedMethods[$name]);
203-
}
204-
205-
$findUsagesStartTokenPointer = $methodCallTokenPointer + 1;
211+
$findUsagesStartTokenPointer = $checkDoubleColonUsage($tokenPointer);
206212
}
207213
} else {
208214
$findUsagesStartTokenPointer = $tokenPointer + 1;

tests/Sniffs/Classes/UnusedPrivateElementsSniffTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ public function testClassWithSpecialSelf()
101101
$this->assertNoSniffErrorInFile($this->checkFile(__DIR__ . '/data/classWithSpecialSelf.php'));
102102
}
103103

104-
public function testClassWithPrivateMethodCalledOnSelfInstance()
104+
public function testClassWithPrivateElementsUsedOnSelfInstance()
105105
{
106-
$report = $this->checkFile(__DIR__ . '/data/classWithPrivateMethodCalledOnSelfInstance.php');
106+
$report = $this->checkFile(__DIR__ . '/data/classWithPrivateElementsUsedOnSelfInstance.php');
107107
$this->assertNoSniffErrorInFile($report);
108108
}
109109

110-
public function testClassWithPrivateMethodCalledOnStaticInstance()
110+
public function testClassWithPrivateElementsUsedOnStaticInstance()
111111
{
112-
$report = $this->checkFile(__DIR__ . '/data/classWithPrivateMethodCalledOnStaticInstance.php');
112+
$report = $this->checkFile(__DIR__ . '/data/classWithPrivateElementsUsedOnStaticInstance.php');
113113
$this->assertNoSniffErrorInFile($report);
114114
}
115115

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php // lint >= 7.1
2+
3+
class ClassWithPrivateElementsUsedOnSelfInstance
4+
{
5+
6+
private const CONSTANT = 'constant';
7+
8+
private $property = 'property';
9+
10+
private static $staticProperty = 'staticProperty';
11+
12+
public static function create()
13+
{
14+
$self = new self();
15+
$self->setUp();
16+
$self->property;
17+
18+
$self::staticSetUp();
19+
$self::CONSTANT;
20+
$self::$staticProperty;
21+
22+
return $self;
23+
}
24+
25+
private function setUp()
26+
{
27+
}
28+
29+
private static function staticSetUp()
30+
{
31+
}
32+
33+
public static function foo()
34+
{
35+
return new self();
36+
}
37+
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php // lint >= 7.1
2+
3+
class ClassWithPrivateElementsUsedOnSelfInstance
4+
{
5+
6+
private const CONSTANT = 'constant';
7+
8+
private $property = 'property';
9+
10+
private static $staticProperty = 'staticProperty';
11+
12+
public static function create()
13+
{
14+
$self = new static();
15+
$self->setUp();
16+
$self->property;
17+
18+
$self::staticSetUp();
19+
$self::CONSTANT;
20+
$self::$staticProperty;
21+
22+
return $self;
23+
}
24+
25+
private function setUp()
26+
{
27+
}
28+
29+
private static function staticSetUp()
30+
{
31+
}
32+
33+
public static function foo()
34+
{
35+
return new static();
36+
}
37+
38+
}

tests/Sniffs/Classes/data/classWithPrivateMethodCalledOnSelfInstance.php

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/Sniffs/Classes/data/classWithPrivateMethodCalledOnStaticInstance.php

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)