Skip to content

Commit

Permalink
Generatelize EntrypointProviders to UsageProviders; add ConstantUsage…
Browse files Browse the repository at this point in the history
…Provider
  • Loading branch information
janedbal committed Dec 6, 2024
1 parent ee6004c commit 91af662
Show file tree
Hide file tree
Showing 18 changed files with 287 additions and 168 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ includes:
- `#[Required]` attribute
- `#[Route]` attributes
- `onKernelResponse`, `onKernelRequest`, etc
- class constants referenced in `containerXmlPath` via `!php/const:`

#### Doctrine:
- `#[AsEntityListener]` attribute
Expand Down Expand Up @@ -72,24 +73,26 @@ parameters:
```

## Customization:
- If your application does some magic calls unknown to this library, you can implement your own entrypoint provider.
- Just tag it with `shipmonk.deadCode.entrypointProvider` and implement `ShipMonk\PHPStan\DeadCode\Provider\MethodEntrypointProvider`
- You can simplify your implementation by extending `ShipMonk\PHPStan\DeadCode\Provider\SimpleMethodEntrypointProvider`
- If your application does some magic calls unknown to this library, you can implement your own usage provider.
- Just tag it with `shipmonk.deadCode.methodUsageProvider` and implement `ShipMonk\PHPStan\DeadCode\Provider\MethodUsageProvider`
- You can simplify your implementation by extending `ShipMonk\PHPStan\DeadCode\Provider\SimpleMethodUsageProvider`
- Similar classes and interfaces are also available for constant usages

```neon
# phpstan.neon.dist
services:
-
class: App\ApiOutputEntrypointProvider
class: App\ApiOutputMethodUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
```

```php

use ReflectionMethod;
use ShipMonk\PHPStan\DeadCode\Provider\SimpleMethodEntrypointProvider;
use ShipMonk\PHPStan\DeadCode\Provider\SimpleMethodUsageProvider;

class ApiOutputEntrypointProvider extends SimpleMethodEntrypointProvider
class ApiOutputMethodUsageProvider extends SimpleMethodUsageProvider
{

public function isEntrypointMethod(ReflectionMethod $method): bool
Expand Down
3 changes: 2 additions & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ parameters:
PHPStan\Rules\Rule: Rule
PHPStan\Collectors\Collector: Collector
ShipMonk\PHPStan\DeadCode\Rule\RuleTestCase: RuleTest
ShipMonk\PHPStan\DeadCode\Provider\MethodEntrypointProvider: EntrypointProvider
ShipMonk\PHPStan\DeadCode\Provider\ConstantUsageProvider: UsageProvider
ShipMonk\PHPStan\DeadCode\Provider\MethodUsageProvider: UsageProvider
enforceReadonlyPublicProperty:
enabled: false # we support even PHP 7.4

Expand Down
45 changes: 23 additions & 22 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,46 @@ services:
class: ShipMonk\PHPStan\DeadCode\Transformer\FileSystem

-
class: ShipMonk\PHPStan\DeadCode\Provider\VendorEntrypointProvider
class: ShipMonk\PHPStan\DeadCode\Provider\VendorUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
arguments:
enabled: %shipmonkDeadCode.entrypoints.vendor.enabled%
enabled: %shipmonkDeadCode.usageProviders.vendor.enabled%

-
class: ShipMonk\PHPStan\DeadCode\Provider\PhpUnitEntrypointProvider
class: ShipMonk\PHPStan\DeadCode\Provider\PhpUnitUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
arguments:
enabled: %shipmonkDeadCode.entrypoints.phpunit.enabled%
enabled: %shipmonkDeadCode.usageProviders.phpunit.enabled%

-
class: ShipMonk\PHPStan\DeadCode\Provider\SymfonyEntrypointProvider
class: ShipMonk\PHPStan\DeadCode\Provider\SymfonyUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
arguments:
enabled: %shipmonkDeadCode.entrypoints.symfony.enabled%
enabled: %shipmonkDeadCode.usageProviders.symfony.enabled%

-
class: ShipMonk\PHPStan\DeadCode\Provider\DoctrineEntrypointProvider
class: ShipMonk\PHPStan\DeadCode\Provider\DoctrineUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
arguments:
enabled: %shipmonkDeadCode.entrypoints.doctrine.enabled%
enabled: %shipmonkDeadCode.usageProviders.doctrine.enabled%

-
class: ShipMonk\PHPStan\DeadCode\Provider\PhpStanEntrypointProvider
class: ShipMonk\PHPStan\DeadCode\Provider\PhpStanUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
arguments:
enabled: %shipmonkDeadCode.entrypoints.phpstan.enabled%
enabled: %shipmonkDeadCode.usageProviders.phpstan.enabled%

-
class: ShipMonk\PHPStan\DeadCode\Provider\NetteEntrypointProvider
class: ShipMonk\PHPStan\DeadCode\Provider\NetteUsageProvider
tags:
- shipmonk.deadCode.entrypointProvider
- shipmonk.deadCode.methodUsageProvider
arguments:
enabled: %shipmonkDeadCode.entrypoints.nette.enabled%
enabled: %shipmonkDeadCode.usageProviders.nette.enabled%

-
class: ShipMonk\PHPStan\DeadCode\Collector\MethodCallCollector
Expand All @@ -69,11 +69,12 @@ services:
- phpstan.collector

-
class: ShipMonk\PHPStan\DeadCode\Collector\EntrypointCollector
class: ShipMonk\PHPStan\DeadCode\Collector\ProvidedUsagesCollector
tags:
- phpstan.collector
arguments:
entrypointProviders: tagged(shipmonk.deadCode.entrypointProvider)
methodUsageProviders: tagged(shipmonk.deadCode.methodUsageProvider)
constantUsageProviders: tagged(shipmonk.deadCode.constantUsageProvider)

-
class: ShipMonk\PHPStan\DeadCode\Rule\DeadCodeRule
Expand All @@ -89,7 +90,7 @@ parameters:
shipmonkDeadCode:
trackMixedAccess: true
reportTransitivelyDeadMethodAsSeparateError: false
entrypoints:
usageProviders:
vendor:
enabled: true
phpstan:
Expand All @@ -107,7 +108,7 @@ parametersSchema:
shipmonkDeadCode: structure([
trackMixedAccess: bool()
reportTransitivelyDeadMethodAsSeparateError: bool()
entrypoints: structure([
usageProviders: structure([
vendor: structure([
enabled: bool()
])
Expand Down
67 changes: 0 additions & 67 deletions src/Collector/EntrypointCollector.php

This file was deleted.

92 changes: 92 additions & 0 deletions src/Collector/ProvidedUsagesCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php declare(strict_types = 1);

namespace ShipMonk\PHPStan\DeadCode\Collector;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;
use PHPStan\Node\InClassNode;
use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantRef;
use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantUsage;
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodRef;
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodUsage;
use ShipMonk\PHPStan\DeadCode\Provider\ConstantUsageProvider;
use ShipMonk\PHPStan\DeadCode\Provider\MethodUsageProvider;

/**
* @implements Collector<InClassNode, list<string>>
*/
class ProvidedUsagesCollector implements Collector
{

/**
* @var list<MethodUsageProvider>
*/
private array $methodUsageProviders;

/**
* @var list<ConstantUsageProvider>
*/
private array $constantUsageProviders;

/**
* @param list<MethodUsageProvider> $methodUsageProviders
* @param list<ConstantUsageProvider> $constantUsageProviders
*/
public function __construct(
array $methodUsageProviders,
array $constantUsageProviders
)
{
$this->methodUsageProviders = $methodUsageProviders;
$this->constantUsageProviders = $constantUsageProviders;
}

public function getNodeType(): string
{
return InClassNode::class;
}

/**
* @param InClassNode $node
* @return non-empty-list<string>|null
*/
public function processNode(
Node $node,
Scope $scope
): ?array
{
$usages = [];

foreach ($this->methodUsageProviders as $methodUsageProvider) {
foreach ($methodUsageProvider->getMethodUsages($node->getClassReflection()) as $usedMethod) {
$methodUsage = new ClassMethodUsage(
null,
new ClassMethodRef(
$usedMethod->getDeclaringClass()->getName(),
$usedMethod->getName(),
false,
),
);
$usages[] = $methodUsage->serialize();
}
}

foreach ($this->constantUsageProviders as $constantUsageProvider) {
foreach ($constantUsageProvider->getConstantUsages($node->getClassReflection()) as $usedConstant) {
$constantUsage = new ClassConstantUsage(
null,
new ClassConstantRef(
$usedConstant->getDeclaringClass()->getName(),
$usedConstant->getName(),
false,
),
);
$usages[] = $constantUsage->serialize();
}
}

return $usages === [] ? null : $usages;
}

}
27 changes: 27 additions & 0 deletions src/Provider/ConstantUsageProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace ShipMonk\PHPStan\DeadCode\Provider;

use PHPStan\Reflection\ClassReflection;
use ReflectionClassConstant;

/**
* Extension point for marking class constants used based on custom reflection logic.
*
* Register in your phpstan.neon.dist:
*
* services:
* -
* class: App\MyAppConstantUsageProvider
* tags:
* - shipmonk.deadCode.constantUsageProvider
*/
interface ConstantUsageProvider
{

/**
* @return list<ReflectionClassConstant>
*/
public function getConstantUsages(ClassReflection $classReflection): array;

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use ReflectionMethod;
use const PHP_VERSION_ID;

class DoctrineEntrypointProvider extends SimpleMethodEntrypointProvider
class DoctrineUsageProvider extends SimpleMethodUsageProvider
{

private bool $enabled;
Expand All @@ -17,7 +17,7 @@ public function __construct(?bool $enabled)
$this->enabled = $enabled ?? $this->isDoctrineInstalled();
}

public function isEntrypointMethod(ReflectionMethod $method): bool
public function shouldMarkMethodAsUsed(ReflectionMethod $method): bool
{
if (!$this->enabled) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@
use PHPStan\Reflection\MethodReflection;

/**
* Extension point for marking methods as entrypoints (not dead) based on custom reflection logic.
* Extension point for marking methods as used based on custom reflection logic.
*
* Register in your phpstan.neon.dist:
*
* services:
* -
* class: App\MyEntrypointProvider
* class: App\MyAppUsageProvider
* tags:
* - shipmonk.deadCode.entrypointProvider
* - shipmonk.deadCode.methodUsageProvider
*/
interface MethodEntrypointProvider
interface MethodUsageProvider
{

/**
* @return list<MethodReflection>
*/
public function getEntrypoints(ClassReflection $classReflection): array;
public function getMethodUsages(ClassReflection $classReflection): array;

}
Loading

0 comments on commit 91af662

Please sign in to comment.