Skip to content

Commit 137c6be

Browse files
authored
Allow debugging of usages (#165)
1 parent d5ab39a commit 137c6be

37 files changed

+1141
-261
lines changed

.ecrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"Exclude": [
3+
"^tests/Rule/data/debug/expected_output.txt"
4+
]
5+
}

README.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ use PHPStan\Analyser\Scope;
157157
use ReflectionMethod;
158158
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodRef;
159159
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodUsage;
160-
use ShipMonk\PHPStan\DeadCode\Graph\UsageOriginDetector;
160+
use ShipMonk\PHPStan\DeadCode\Graph\UsageOrigin;
161161
use ShipMonk\PHPStan\DeadCode\Provider\MemberUsageProvider;
162162
use Symfony\Component\Serializer\SerializerInterface;
163163

@@ -185,13 +185,13 @@ class DeserializationUsageProvider implements MemberUsageProvider
185185
$secondArgument = $node->getArgs()[1]->value;
186186
$serializedClass = $scope->getType($secondArgument)->getConstantStrings()[0];
187187

188-
// record the method it was called from (needed for proper transitive dead code elimination)
189-
$originRef = $this->originDetector->detectOrigin($scope);
188+
// record the place it was called from (needed for proper transitive dead code elimination)
189+
$usageOrigin = UsageOrigin::createRegular($node, $scope);
190190

191191
// record the hidden constructor call
192192
$constructorRef = new ClassMethodRef($serializedClass->getValue(), '__construct', false);
193193

194-
return [new ClassMethodUsage($originRef, $constructorRef)];
194+
return [new ClassMethodUsage($usageOrigin, $constructorRef)];
195195
}
196196

197197
return [];
@@ -350,6 +350,34 @@ class IgnoreDeadInterfaceUsageProvider extends ReflectionBasedMemberUsageProvide
350350
}
351351
```
352352

353+
## Debugging:
354+
- If you want to see how dead code detector evaluated usages of certain method, you do the following:
355+
356+
```neon
357+
parameters:
358+
shipmonkDeadCode:
359+
debug:
360+
usagesOf:
361+
- App\User\Entity\Address::__construct
362+
```
363+
364+
Then, run PHPStan with `-vvv` CLI option and you will see the output like this:
365+
366+
```txt
367+
App\User\Entity\Address::__construct
368+
|
369+
| Marked as alive by:
370+
| entry virtual usage from ShipMonk\PHPStan\DeadCode\Provider\SymfonyUsageProvider
371+
| calls App\User\RegisterUserController::__invoke:36
372+
| calls App\User\UserFacade::registerUser:142
373+
| calls App\User\Entity\Address::__construct
374+
|
375+
| Found 2 usages:
376+
| • src/User/UserFacade.php:142
377+
| • tests/User/Entity/AddressTest.php:64 - excluded by tests excluder
378+
```
379+
380+
If you set up `editorUrl` [parameter](https://phpstan.org/user-guide/output-format#opening-file-in-an-editor), you can click on the usages to open it in your IDE.
353381

354382
## Future scope:
355383
- Dead class property detection

phpcs.xml.dist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@
196196
<exclude name="Squiz.Operators.IncrementDecrementUsage.NoBrackets"/><!-- there is no need for brackets -->
197197
</rule>
198198
<rule ref="Squiz.Operators.ValidLogicalOperators"/>
199-
<rule ref="Squiz.PHP.CommentedOutCode"/>
200199
<rule ref="Squiz.PHP.GlobalKeyword"/>
201200
<rule ref="Squiz.PHP.InnerFunctions"/>
202201
<rule ref="Squiz.PHP.LowercasePHPFunctions"/>

rules.neon

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ services:
88
class: ShipMonk\PHPStan\DeadCode\Transformer\FileSystem
99

1010
-
11-
class: ShipMonk\PHPStan\DeadCode\Graph\UsageOriginDetector
11+
class: ShipMonk\PHPStan\DeadCode\Debug\DebugUsagePrinter
12+
arguments:
13+
trackMixedAccess: %shipmonkDeadCode.trackMixedAccess%
14+
editorUrl: %editorUrl%
1215

1316
-
1417
class: ShipMonk\PHPStan\DeadCode\Provider\VendorUsageProvider
@@ -105,7 +108,6 @@ services:
105108
- phpstan.diagnoseExtension
106109
arguments:
107110
reportTransitivelyDeadMethodAsSeparateError: %shipmonkDeadCode.reportTransitivelyDeadMethodAsSeparateError%
108-
trackMixedAccess: %shipmonkDeadCode.trackMixedAccess%
109111

110112
-
111113
class: ShipMonk\PHPStan\DeadCode\Compatibility\BackwardCompatibilityChecker
@@ -137,6 +139,8 @@ parameters:
137139
tests:
138140
enabled: false
139141
devPaths: null
142+
debug:
143+
usagesOf: []
140144

141145
parametersSchema:
142146
shipmonkDeadCode: structure([
@@ -172,4 +176,7 @@ parametersSchema:
172176
devPaths: schema(listOf(string()), nullable())
173177
])
174178
])
179+
debug: structure([
180+
usagesOf: listOf(string())
181+
])
175182
])

src/Collector/ConstantFetchCollector.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantRef;
1919
use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantUsage;
2020
use ShipMonk\PHPStan\DeadCode\Graph\CollectedUsage;
21-
use ShipMonk\PHPStan\DeadCode\Graph\UsageOriginDetector;
21+
use ShipMonk\PHPStan\DeadCode\Graph\UsageOrigin;
2222
use function array_map;
2323
use function count;
2424
use function current;
@@ -33,8 +33,6 @@ class ConstantFetchCollector implements Collector
3333

3434
use BufferedUsageCollector;
3535

36-
private UsageOriginDetector $usageOriginDetector;
37-
3836
private ReflectionProvider $reflectionProvider;
3937

4038
private bool $trackMixedAccess;
@@ -48,15 +46,13 @@ class ConstantFetchCollector implements Collector
4846
* @param list<MemberUsageExcluder> $memberUsageExcluders
4947
*/
5048
public function __construct(
51-
UsageOriginDetector $usageOriginDetector,
5249
ReflectionProvider $reflectionProvider,
5350
bool $trackMixedAccess,
5451
array $memberUsageExcluders
5552
)
5653
{
5754
$this->reflectionProvider = $reflectionProvider;
5855
$this->trackMixedAccess = $trackMixedAccess;
59-
$this->usageOriginDetector = $usageOriginDetector;
6056
$this->memberUsageExcluders = $memberUsageExcluders;
6157
}
6258

@@ -125,7 +121,7 @@ private function registerFunctionCall(FuncCall $node, Scope $scope): void
125121

126122
$this->registerUsage(
127123
new ClassConstantUsage(
128-
$this->usageOriginDetector->detectOrigin($scope),
124+
UsageOrigin::createRegular($node, $scope),
129125
new ClassConstantRef($className, $constantName, true),
130126
),
131127
$node,
@@ -157,7 +153,7 @@ private function registerFetch(ClassConstFetch $node, Scope $scope): void
157153
foreach ($this->getDeclaringTypesWithConstant($ownerType, $constantName) as $className) {
158154
$this->registerUsage(
159155
new ClassConstantUsage(
160-
$this->usageOriginDetector->detectOrigin($scope),
156+
UsageOrigin::createRegular($node, $scope),
161157
new ClassConstantRef($className, $constantName, $possibleDescendantFetch),
162158
),
163159
$node,

src/Collector/MethodCallCollector.php

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodRef;
2626
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodUsage;
2727
use ShipMonk\PHPStan\DeadCode\Graph\CollectedUsage;
28-
use ShipMonk\PHPStan\DeadCode\Graph\UsageOriginDetector;
28+
use ShipMonk\PHPStan\DeadCode\Graph\UsageOrigin;
2929

3030
/**
3131
* @implements Collector<Node, list<string>>
@@ -35,8 +35,6 @@ class MethodCallCollector implements Collector
3535

3636
use BufferedUsageCollector;
3737

38-
private UsageOriginDetector $usageOriginDetector;
39-
4038
private bool $trackMixedAccess;
4139

4240
/**
@@ -48,12 +46,10 @@ class MethodCallCollector implements Collector
4846
* @param list<MemberUsageExcluder> $memberUsageExcluders
4947
*/
5048
public function __construct(
51-
UsageOriginDetector $usageOriginDetector,
5249
bool $trackMixedAccess,
5350
array $memberUsageExcluders
5451
)
5552
{
56-
$this->usageOriginDetector = $usageOriginDetector;
5753
$this->trackMixedAccess = $trackMixedAccess;
5854
$this->memberUsageExcluders = $memberUsageExcluders;
5955
}
@@ -133,7 +129,7 @@ private function registerMethodCall(
133129
foreach ($this->getDeclaringTypesWithMethod($methodName, $callerType, TrinaryLogic::createNo(), $possibleDescendantCall) as $methodRef) {
134130
$this->registerUsage(
135131
new ClassMethodUsage(
136-
$this->usageOriginDetector->detectOrigin($scope),
132+
UsageOrigin::createRegular($methodCall, $scope),
137133
$methodRef,
138134
),
139135
$methodCall,
@@ -163,7 +159,7 @@ private function registerStaticCall(
163159
foreach ($this->getDeclaringTypesWithMethod($methodName, $callerType, TrinaryLogic::createYes(), $possibleDescendantCall) as $methodRef) {
164160
$this->registerUsage(
165161
new ClassMethodUsage(
166-
$this->usageOriginDetector->detectOrigin($scope),
162+
UsageOrigin::createRegular($staticCall, $scope),
167163
$methodRef,
168164
),
169165
$staticCall,
@@ -189,7 +185,7 @@ private function registerArrayCallable(
189185
foreach ($this->getDeclaringTypesWithMethod($methodName, $caller, TrinaryLogic::createMaybe()) as $methodRef) {
190186
$this->registerUsage(
191187
new ClassMethodUsage(
192-
$this->usageOriginDetector->detectOrigin($scope),
188+
UsageOrigin::createRegular($array, $scope),
193189
$methodRef,
194190
),
195191
$array,
@@ -205,7 +201,7 @@ private function registerAttribute(Attribute $node, Scope $scope): void
205201
{
206202
$this->registerUsage(
207203
new ClassMethodUsage(
208-
null,
204+
UsageOrigin::createRegular($node, $scope),
209205
new ClassMethodRef($scope->resolveName($node->name), '__construct', false),
210206
),
211207
$node,
@@ -221,7 +217,7 @@ private function registerClone(Clone_ $node, Scope $scope): void
221217
foreach ($this->getDeclaringTypesWithMethod($methodName, $callerType, TrinaryLogic::createNo()) as $methodRef) {
222218
$this->registerUsage(
223219
new ClassMethodUsage(
224-
$this->usageOriginDetector->detectOrigin($scope),
220+
UsageOrigin::createRegular($node, $scope),
225221
$methodRef,
226222
),
227223
$node,

src/Collector/ProvidedUsagesCollector.php

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,13 @@ private function validateUsage(
8686
$memberRef = $usage->getMemberRef();
8787
$memberRefClass = $memberRef->getClassName();
8888

89-
$originRef = $usage->getOrigin();
90-
$originRefClass = $originRef === null ? null : $originRef->getClassName();
89+
$origin = $usage->getOrigin();
90+
$originClass = $origin->getClassName();
91+
$originMethod = $origin->getMethodName();
9192

9293
$context = sprintf(
93-
"It was emitted as %s by %s for node '%s' in '%s' on line %s",
94-
$usage->toHumanString(),
94+
"It emitted usage of %s by %s for node '%s' in '%s' on line %s",
95+
$usage->getMemberRef()->toHumanString(),
9596
get_class($provider),
9697
get_class($node),
9798
$scope->getFile(),
@@ -102,16 +103,13 @@ private function validateUsage(
102103
throw new LogicException("Class '$memberRefClass' does not exist. $context");
103104
}
104105

105-
if (
106-
$originRef !== null
107-
&& $originRefClass !== null
108-
) {
109-
if (!$this->reflectionProvider->hasClass($originRefClass)) {
110-
throw new LogicException("Class '{$originRefClass}' does not exist. $context");
106+
if ($originClass !== null) {
107+
if (!$this->reflectionProvider->hasClass($originClass)) {
108+
throw new LogicException("Class '{$originClass}' does not exist. $context");
111109
}
112110

113-
if (!$this->reflectionProvider->getClass($originRefClass)->hasMethod($originRef->getMemberName())) {
114-
throw new LogicException("Method '{$originRef->getMemberName()}' does not exist in class '$originRefClass'. $context");
111+
if ($originMethod !== null && !$this->reflectionProvider->getClass($originClass)->hasMethod($originMethod)) {
112+
throw new LogicException("Method '{$originMethod}' does not exist in class '$originClass'. $context");
115113
}
116114
}
117115
}

0 commit comments

Comments
 (0)