diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 9d1223c..833c61f 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -30,7 +30,7 @@
-
+
diff --git a/rules.neon b/rules.neon
index 439e808..38d510a 100644
--- a/rules.neon
+++ b/rules.neon
@@ -60,6 +60,10 @@ services:
class: ShipMonk\PHPStan\DeadCode\Collector\ClassDefinitionCollector
tags:
- phpstan.collector
+ -
+ class: ShipMonk\PHPStan\DeadCode\Collector\ConstantFetchCollector
+ tags:
+ - phpstan.collector
-
class: ShipMonk\PHPStan\DeadCode\Collector\EntrypointCollector
tags:
diff --git a/src/Collector/ClassDefinitionCollector.php b/src/Collector/ClassDefinitionCollector.php
index d34bef5..19850d3 100644
--- a/src/Collector/ClassDefinitionCollector.php
+++ b/src/Collector/ClassDefinitionCollector.php
@@ -23,6 +23,7 @@
* @implements Collector,
* methods: array}>,
* parents: array,
* traits: array, aliases?: array}>,
@@ -42,6 +43,7 @@ public function getNodeType(): string
* @return array{
* kind: string,
* name: string,
+ * constants: array,
* methods: array}>,
* parents: array,
* traits: array, aliases?: array}>,
@@ -70,10 +72,21 @@ public function processNode(
];
}
+ $constants = [];
+
+ foreach ($node->getConstants() as $constant) {
+ foreach ($constant->consts as $const) {
+ $constants[$const->name->toString()] = [
+ 'line' => $const->getStartLine(),
+ ];
+ }
+ }
+
return [
'kind' => $kind,
'name' => $typeName,
'methods' => $methods,
+ 'constants' => $constants,
'parents' => $this->getParents($node),
'traits' => $this->getTraits($node),
'interfaces' => $this->getInterfaces($node),
diff --git a/src/Collector/ConstantFetchCollector.php b/src/Collector/ConstantFetchCollector.php
new file mode 100644
index 0000000..107eb35
--- /dev/null
+++ b/src/Collector/ConstantFetchCollector.php
@@ -0,0 +1,103 @@
+>
+ */
+class ConstantFetchCollector implements Collector
+{
+
+ /**
+ * @var list
+ */
+ private array $accessBuffer = [];
+
+ public function getNodeType(): string
+ {
+ return Node::class;
+ }
+
+ /**
+ * @return non-empty-list|null
+ */
+ public function processNode(
+ Node $node,
+ Scope $scope
+ ): ?array
+ {
+ if ($node instanceof ClassConstFetch) {
+ $this->registerFetch($node, $scope);
+ }
+
+ if (!$scope->isInClass() || $node instanceof ClassMethodsNode) { // @phpstan-ignore-line ignore BC promise
+ $data = $this->accessBuffer;
+ $this->accessBuffer = [];
+
+ // collect data once per class to save memory & resultCache size
+ return $data === []
+ ? null
+ : array_map(
+ static fn (ClassConstantFetch $fetch): string => $fetch->toString(),
+ $data,
+ );
+ }
+
+ return null;
+ }
+
+ private function registerFetch(ClassConstFetch $node, Scope $scope): void
+ {
+ $ownerType = $node->class instanceof Name
+ ? $scope->resolveTypeByName($node->class)
+ : $scope->getType($node->class);
+
+ $constantNames = $node->name instanceof Expr
+ ? array_map(static fn (ConstantStringType $string): string => $string->getValue(), $scope->getType($node->name)->getConstantStrings())
+ : [$node->name->toString()];
+
+ foreach ($ownerType->getObjectClassReflections() as $classReflection) {
+ foreach ($constantNames as $constantName) {
+ if ($classReflection->hasConstant($constantName)) {
+ $className = $classReflection->getConstant($constantName)->getDeclaringClass()->getName();
+
+ } else { // call of unknown const (might be present on children)
+ $className = $classReflection->getName(); // TODO untested yet
+ }
+
+ $this->accessBuffer[] = new ClassConstantFetch(
+ $this->getCaller($scope),
+ new ClassConstantRef($className, $constantName),
+ );
+ }
+ }
+ }
+
+ private function getCaller(Scope $scope): ?ClassMethodRef
+ {
+ if (!$scope->isInClass()) {
+ return null;
+ }
+
+ if (!$scope->getFunction() instanceof MethodReflection) {
+ return null;
+ }
+
+ return new ClassMethodRef($scope->getClassReflection()->getName(), $scope->getFunction()->getName());
+ }
+
+}
diff --git a/src/Collector/EntrypointCollector.php b/src/Collector/EntrypointCollector.php
index 70b94b7..c3d697c 100644
--- a/src/Collector/EntrypointCollector.php
+++ b/src/Collector/EntrypointCollector.php
@@ -6,8 +6,8 @@
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;
use PHPStan\Node\InClassNode;
-use ShipMonk\PHPStan\DeadCode\Crate\Call;
-use ShipMonk\PHPStan\DeadCode\Crate\Method;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodCall;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodRef;
use ShipMonk\PHPStan\DeadCode\Provider\MethodEntrypointProvider;
/**
@@ -49,9 +49,9 @@ public function processNode(
foreach ($this->entrypointProviders as $entrypointProvider) {
foreach ($entrypointProvider->getEntrypoints($node->getClassReflection()) as $entrypointMethod) {
- $call = new Call(
+ $call = new ClassMethodCall(
null,
- new Method($entrypointMethod->getDeclaringClass()->getName(), $entrypointMethod->getName()),
+ new ClassMethodRef($entrypointMethod->getDeclaringClass()->getName(), $entrypointMethod->getName()),
false,
);
$entrypoints[] = $call->toString();
diff --git a/src/Collector/MethodCallCollector.php b/src/Collector/MethodCallCollector.php
index d8bda2a..ae8005b 100644
--- a/src/Collector/MethodCallCollector.php
+++ b/src/Collector/MethodCallCollector.php
@@ -23,8 +23,8 @@
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
-use ShipMonk\PHPStan\DeadCode\Crate\Call;
-use ShipMonk\PHPStan\DeadCode\Crate\Method;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodCall;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodRef;
use function array_map;
/**
@@ -34,7 +34,7 @@ class MethodCallCollector implements Collector
{
/**
- * @var list
+ * @var list
*/
private array $callsBuffer = [];
@@ -94,7 +94,7 @@ public function processNode(
return $data === []
? null
: array_map(
- static fn (Call $call): string => $call->toString(),
+ static fn (ClassMethodCall $call): string => $call->toString(),
$data,
);
}
@@ -131,9 +131,9 @@ private function registerMethodCall(
foreach ($methodNames as $methodName) {
foreach ($this->getDeclaringTypesWithMethod($scope, $callerType, $methodName, TrinaryLogic::createNo()) as $className) {
- $this->callsBuffer[] = new Call(
+ $this->callsBuffer[] = new ClassMethodCall(
$this->getCaller($scope),
- new Method($className, $methodName),
+ new ClassMethodRef($className, $methodName),
$possibleDescendantCall,
);
}
@@ -158,9 +158,9 @@ private function registerStaticCall(
foreach ($methodNames as $methodName) {
foreach ($this->getDeclaringTypesWithMethod($scope, $callerType, $methodName, TrinaryLogic::createYes()) as $className) {
- $this->callsBuffer[] = new Call(
+ $this->callsBuffer[] = new ClassMethodCall(
$this->getCaller($scope),
- new Method($className, $methodName),
+ new ClassMethodRef($className, $methodName),
$possibleDescendantCall,
);
}
@@ -184,9 +184,9 @@ private function registerArrayCallable(
$possibleDescendantCall = !$caller->isClassString()->yes();
foreach ($this->getDeclaringTypesWithMethod($scope, $caller, $methodName, TrinaryLogic::createMaybe()) as $className) {
- $this->callsBuffer[] = new Call(
+ $this->callsBuffer[] = new ClassMethodCall(
$this->getCaller($scope),
- new Method($className, $methodName),
+ new ClassMethodRef($className, $methodName),
$possibleDescendantCall,
);
}
@@ -197,9 +197,9 @@ private function registerArrayCallable(
private function registerAttribute(Attribute $node, Scope $scope): void
{
- $this->callsBuffer[] = new Call(
+ $this->callsBuffer[] = new ClassMethodCall(
null,
- new Method($scope->resolveName($node->name), '__construct'),
+ new ClassMethodRef($scope->resolveName($node->name), '__construct'),
false,
);
}
@@ -210,9 +210,9 @@ private function registerClone(Clone_ $node, Scope $scope): void
$callerType = $scope->getType($node->expr);
foreach ($this->getDeclaringTypesWithMethod($scope, $callerType, $methodName, TrinaryLogic::createNo()) as $className) {
- $this->callsBuffer[] = new Call(
+ $this->callsBuffer[] = new ClassMethodCall(
$this->getCaller($scope),
- new Method($className, $methodName),
+ new ClassMethodRef($className, $methodName),
true,
);
}
@@ -278,7 +278,7 @@ private function getDeclaringTypesWithMethod(
return $result;
}
- private function getCaller(Scope $scope): ?Method
+ private function getCaller(Scope $scope): ?ClassMethodRef
{
if (!$scope->isInClass()) {
return null;
@@ -288,7 +288,7 @@ private function getCaller(Scope $scope): ?Method
return null;
}
- return new Method($scope->getClassReflection()->getName(), $scope->getFunction()->getName());
+ return new ClassMethodRef($scope->getClassReflection()->getName(), $scope->getFunction()->getName());
}
}
diff --git a/src/Crate/ClassConstantFetch.php b/src/Crate/ClassConstantFetch.php
new file mode 100644
index 0000000..9a5e0ba
--- /dev/null
+++ b/src/Crate/ClassConstantFetch.php
@@ -0,0 +1,45 @@
+origin = $origin;
+ $this->fetch = $fetch;
+ }
+
+ public function toString(): string
+ {
+ return serialize($this);
+ }
+
+ public static function fromString(string $callKey): self
+ {
+ $result = unserialize($callKey, ['allowed_classes' => [self::class, ClassMethodRef::class, ClassConstantRef::class]]);
+
+ if (!$result instanceof self) {
+ $self = self::class;
+ throw new LogicException("Invalid string for $self deserialization: $callKey");
+ }
+
+ return $result;
+ }
+
+}
diff --git a/src/Crate/ClassConstantRef.php b/src/Crate/ClassConstantRef.php
new file mode 100644
index 0000000..47d8f80
--- /dev/null
+++ b/src/Crate/ClassConstantRef.php
@@ -0,0 +1,19 @@
+className = $className;
+ $this->memberName = $memberName;
+ $this->memberType = $memberType;
+ }
+
+ public function toString(): string
+ {
+ $classRef = $this->className ?? self::UNKNOWN_CLASS;
+ return $classRef . '::' . $this->memberName;
+ }
+
+}
diff --git a/src/Crate/Call.php b/src/Crate/ClassMethodCall.php
similarity index 84%
rename from src/Crate/Call.php
rename to src/Crate/ClassMethodCall.php
index 0051562..86bb5ba 100644
--- a/src/Crate/Call.php
+++ b/src/Crate/ClassMethodCall.php
@@ -9,18 +9,18 @@
/**
* @immutable
*/
-class Call
+class ClassMethodCall
{
- public ?Method $caller;
+ public ?ClassMethodRef $caller;
- public Method $callee;
+ public ClassMethodRef $callee;
public bool $possibleDescendantCall;
public function __construct(
- ?Method $caller,
- Method $callee,
+ ?ClassMethodRef $caller,
+ ClassMethodRef $callee,
bool $possibleDescendantCall
)
{
@@ -62,8 +62,8 @@ public static function fromString(string $callKey): self
}
[$calleeClassName, $calleeMethodName] = $calleeSplit;
- $callee = new Method(
- $calleeClassName === Method::UNKNOWN_CLASS ? null : $calleeClassName,
+ $callee = new ClassMethodRef(
+ $calleeClassName === ClassMemberRef::UNKNOWN_CLASS ? null : $calleeClassName,
$calleeMethodName,
);
@@ -77,7 +77,7 @@ public static function fromString(string $callKey): self
}
[$callerClassName, $callerMethodName] = $callerSplit;
- $caller = new Method($callerClassName, $callerMethodName);
+ $caller = new ClassMethodRef($callerClassName, $callerMethodName);
}
return new self(
diff --git a/src/Crate/ClassMethodRef.php b/src/Crate/ClassMethodRef.php
new file mode 100644
index 0000000..5490937
--- /dev/null
+++ b/src/Crate/ClassMethodRef.php
@@ -0,0 +1,19 @@
+className = $className;
- $this->methodName = $methodName;
- }
-
- public function toString(): string
- {
- $classRef = $this->className ?? self::UNKNOWN_CLASS;
- return $classRef . '::' . $this->methodName;
- }
-
-}
diff --git a/src/Rule/DeadMethodRule.php b/src/Rule/DeadMethodRule.php
index 3d81b24..7bf83dc 100644
--- a/src/Rule/DeadMethodRule.php
+++ b/src/Rule/DeadMethodRule.php
@@ -12,11 +12,14 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use ShipMonk\PHPStan\DeadCode\Collector\ClassDefinitionCollector;
+use ShipMonk\PHPStan\DeadCode\Collector\ConstantFetchCollector;
use ShipMonk\PHPStan\DeadCode\Collector\EntrypointCollector;
use ShipMonk\PHPStan\DeadCode\Collector\MethodCallCollector;
-use ShipMonk\PHPStan\DeadCode\Crate\Call;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassConstantFetch;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMemberRef;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodCall;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodRef;
use ShipMonk\PHPStan\DeadCode\Crate\Kind;
-use ShipMonk\PHPStan\DeadCode\Crate\Method;
use ShipMonk\PHPStan\DeadCode\Crate\Visibility;
use ShipMonk\PHPStan\DeadCode\Hierarchy\ClassHierarchy;
use function array_key_exists;
@@ -33,7 +36,7 @@
/**
* @implements Rule
*/
-class DeadMethodRule implements Rule, DiagnoseExtension
+class DeadMethodRule implements Rule, DiagnoseExtension // TODO rename to DeadCodeRule
{
public const ERROR_IDENTIFIER = 'shipmonk.deadMethod';
@@ -64,6 +67,7 @@ class DeadMethodRule implements Rule, DiagnoseExtension
* kind: string,
* name: string,
* file: string,
+ * constants: array,
* methods: array}>,
* parents: array,
* traits: array, aliases?: array}>,
@@ -87,14 +91,24 @@ class DeadMethodRule implements Rule, DiagnoseExtension
private array $blackMethods = [];
/**
- * @var array> methodName => Call[]
+ * @var array constantKey => [file, line]
+ */
+ private array $blackConstants = [];
+
+ /**
+ * @var array> originMethodKey => constantKey[]
+ */
+ private array $constantFetches = [];
+
+ /**
+ * @var array> methodName => Call[]
*/
private array $mixedCalls = [];
/**
* @var array> caller => callee[]
*/
- private array $callGraph = [];
+ private array $callGraph = []; // TODO include also const fetches?
public function __construct(
ClassHierarchy $classHierarchy,
@@ -125,10 +139,14 @@ public function processNode(
return [];
}
- /** @var list $calls */
+ /** @var list $calls */
$calls = [];
+ /** @var list $fetches */
+ $fetches = [];
+
$methodDeclarationData = $node->get(ClassDefinitionCollector::class);
$methodCallData = $node->get(MethodCallCollector::class);
+ $constFetchData = $node->get(ConstantFetchCollector::class);
$entrypointData = $node->get(EntrypointCollector::class);
/** @var array>> $callData */
@@ -138,10 +156,10 @@ public function processNode(
foreach ($callData as $callsPerFile) {
foreach ($callsPerFile as $callStrings) {
foreach ($callStrings as $callString) {
- $call = Call::fromString($callString);
+ $call = ClassMethodCall::fromString($callString);
if ($call->callee->className === null) {
- $this->mixedCalls[$call->callee->methodName][] = $call;
+ $this->mixedCalls[$call->callee->memberName][] = $call;
continue;
}
@@ -150,6 +168,19 @@ public function processNode(
}
}
+ foreach ($constFetchData as $file => $data) {
+ foreach ($data as $constantData) {
+ foreach ($constantData as $constantKey) {
+ $fetch = ClassConstantFetch::fromString($constantKey);
+ $fetches[] = $fetch;
+
+ if ($fetch->origin !== null) {
+ $this->constantFetches[$fetch->origin->toString()][] = $fetch->fetch->toString();
+ }
+ }
+ }
+ }
+
foreach ($methodDeclarationData as $file => $data) {
foreach ($data as $typeData) {
$typeName = $typeData['name'];
@@ -157,6 +188,7 @@ public function processNode(
'kind' => $typeData['kind'],
'name' => $typeName,
'file' => $file,
+ 'constants' => $typeData['constants'],
'methods' => $typeData['methods'],
'parents' => $typeData['parents'],
'traits' => $typeData['traits'],
@@ -169,6 +201,7 @@ public function processNode(
foreach ($this->typeDefinitions as $typeName => $typeDefinition) {
$methods = $typeDefinition['methods'];
+ $constants = $typeDefinition['constants'];
$file = $typeDefinition['file'];
$ancestorNames = $this->getAncestorNames($typeName);
@@ -182,14 +215,19 @@ public function processNode(
if (isset($this->mixedCalls[$methodName])) {
foreach ($this->mixedCalls[$methodName] as $originalCall) {
- $calls[] = new Call(
+ $calls[] = new ClassMethodCall(
$originalCall->caller,
- new Method($typeName, $methodName),
+ new ClassMethodRef($typeName, $methodName),
$originalCall->possibleDescendantCall,
);
}
}
}
+
+ foreach ($constants as $constantName => $constantData) {
+ $definition = $typeName . '::' . $constantName;
+ $this->blackConstants[$definition] = [$file, $constantData['line']];
+ }
}
$whiteCallees = [];
@@ -211,8 +249,15 @@ public function processNode(
}
}
+ foreach ($fetches as $fetch) {
+ if ($fetch->origin === null) {
+ unset($this->blackConstants[$fetch->fetch->toString()]);
+ }
+ // else utilize call-graph traversal via markTransitivesWhite
+ }
+
foreach ($whiteCallees as $whiteCalleeKey) {
- $this->markTransitiveCallsWhite($whiteCalleeKey);
+ $this->markTransitivesWhite($whiteCalleeKey);
}
foreach ($this->blackMethods as $blackMethodKey => $_) {
@@ -228,6 +273,10 @@ public function processNode(
$errors[] = $this->buildError($deadMethodKey, [], $file, $line);
}
+ foreach ($this->blackConstants as $deadConstantKey => [$file, $line]) {
+ $errors[] = $this->buildError($deadConstantKey, [], $file, $line);
+ }
+
return $errors;
}
@@ -305,7 +354,7 @@ private function isAnonymousClass(?string $className): bool
/**
* @return list
*/
- private function getAlternativeMethodKeys(Method $method, bool $possibleDescendant): array
+ private function getAlternativeMethodKeys(ClassMemberRef $method, bool $possibleDescendant): array
{
if ($method->className === null) {
throw new LogicException('Those were eliminated above, should never happen');
@@ -322,7 +371,7 @@ private function getAlternativeMethodKeys(Method $method, bool $possibleDescenda
if ($possibleDescendant) {
foreach ($this->classHierarchy->getClassDescendants($method->className) as $descendantName) {
- $result[] = $this->getMethodKey($descendantName, $method->methodName);
+ $result[] = $this->getMethodKey($descendantName, $method->memberName);
}
}
@@ -343,13 +392,17 @@ private function getAlternativeMethodKeys(Method $method, bool $possibleDescenda
/**
* @param array $visitedKeys
*/
- private function markTransitiveCallsWhite(string $callerKey, array $visitedKeys = []): void
+ private function markTransitivesWhite(string $callerKey, array $visitedKeys = []): void
{
$visitedKeys = $visitedKeys === [] ? [$callerKey => null] : $visitedKeys;
$calleeKeys = $this->callGraph[$callerKey] ?? [];
unset($this->blackMethods[$callerKey]);
+ foreach ($this->constantFetches[$callerKey] ?? [] as $constantKey) {
+ unset($this->blackConstants[$constantKey]);
+ }
+
foreach ($calleeKeys as $calleeKey) {
if (array_key_exists($calleeKey, $visitedKeys)) {
continue;
@@ -359,7 +412,7 @@ private function markTransitiveCallsWhite(string $callerKey, array $visitedKeys
continue;
}
- $this->markTransitiveCallsWhite($calleeKey, array_merge($visitedKeys, [$calleeKey => null]));
+ $this->markTransitivesWhite($calleeKey, array_merge($visitedKeys, [$calleeKey => null]));
}
}
@@ -527,11 +580,11 @@ private function getMethodKey(string $typeName, string $methodName): string
return $typeName . '::' . $methodName;
}
- private function isConsideredWhite(Call $call): bool
+ private function isConsideredWhite(ClassMethodCall $call): bool
{
return $call->caller === null
|| $this->isAnonymousClass($call->caller->className)
- || array_key_exists($call->caller->methodName, self::UNSUPPORTED_MAGIC_METHODS);
+ || array_key_exists($call->caller->memberName, self::UNSUPPORTED_MAGIC_METHODS);
}
private function isNeverReportedAsDead(string $methodKey): bool
@@ -591,7 +644,7 @@ public function print(Output $output): void
}
/**
- * @param list $calls
+ * @param list $calls
*/
private function getExampleCaller(array $calls): ?string
{
diff --git a/tests/AllServicesInConfigTest.php b/tests/AllServicesInConfigTest.php
index 9e58499..03b5371 100644
--- a/tests/AllServicesInConfigTest.php
+++ b/tests/AllServicesInConfigTest.php
@@ -7,9 +7,12 @@
use PHPStan\Testing\PHPStanTestCase;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
-use ShipMonk\PHPStan\DeadCode\Crate\Call;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassConstantFetch;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassConstantRef;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMemberRef;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodCall;
+use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodRef;
use ShipMonk\PHPStan\DeadCode\Crate\Kind;
-use ShipMonk\PHPStan\DeadCode\Crate\Method;
use ShipMonk\PHPStan\DeadCode\Crate\Visibility;
use ShipMonk\PHPStan\DeadCode\Provider\MethodEntrypointProvider;
use ShipMonk\PHPStan\DeadCode\Provider\SimpleMethodEntrypointProvider;
@@ -45,8 +48,11 @@ public function test(): void
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
$missingClassNames = [];
$excluded = [
- Call::class,
- Method::class,
+ ClassMethodCall::class,
+ ClassMethodRef::class,
+ ClassConstantRef::class,
+ ClassConstantFetch::class,
+ ClassMemberRef::class,
Kind::class,
Visibility::class,
MethodEntrypointProvider::class,
diff --git a/tests/Rule/DeadMethodRuleTest.php b/tests/Rule/DeadMethodRuleTest.php
index 31c6bd7..8f74a8f 100644
--- a/tests/Rule/DeadMethodRuleTest.php
+++ b/tests/Rule/DeadMethodRuleTest.php
@@ -16,6 +16,7 @@
use PHPStan\Symfony\ServiceMapFactory;
use ReflectionMethod;
use ShipMonk\PHPStan\DeadCode\Collector\ClassDefinitionCollector;
+use ShipMonk\PHPStan\DeadCode\Collector\ConstantFetchCollector;
use ShipMonk\PHPStan\DeadCode\Collector\EntrypointCollector;
use ShipMonk\PHPStan\DeadCode\Collector\MethodCallCollector;
use ShipMonk\PHPStan\DeadCode\Formatter\RemoveDeadCodeFormatter;
@@ -70,6 +71,7 @@ protected function getCollectors(): array
new EntrypointCollector($this->getEntrypointProviders()),
new ClassDefinitionCollector(),
new MethodCallCollector($this->trackMixedCalls),
+ new ConstantFetchCollector(),
];
}
@@ -232,6 +234,13 @@ public function testGrouping(): void
]);
}
+ public function testConstants(): void
+ {
+ $this->emitErrorsInGroups = false; // TODO test even groups
+ $this->analyseFiles([__DIR__ . '/data/DeadMethodRule/constants/basic.php']);
+ $this->analyseFiles([__DIR__ . '/data/DeadMethodRule/constants/override.php']);
+ }
+
/**
* @return array, 1?: int}>
*/
@@ -240,7 +249,6 @@ public static function provideFiles(): iterable
yield 'anonym' => [__DIR__ . '/data/DeadMethodRule/anonym.php'];
yield 'enum' => [__DIR__ . '/data/DeadMethodRule/enum.php', 8_01_00];
yield 'callables' => [__DIR__ . '/data/DeadMethodRule/callables.php'];
- yield 'code' => [__DIR__ . '/data/DeadMethodRule/basic.php'];
yield 'ctor' => [__DIR__ . '/data/DeadMethodRule/ctor.php'];
yield 'ctor-interface' => [__DIR__ . '/data/DeadMethodRule/ctor-interface.php'];
yield 'ctor-private' => [__DIR__ . '/data/DeadMethodRule/ctor-private.php'];
diff --git a/tests/Rule/data/DeadMethodRule/constants/basic.php b/tests/Rule/data/DeadMethodRule/constants/basic.php
new file mode 100644
index 0000000..c47435c
--- /dev/null
+++ b/tests/Rule/data/DeadMethodRule/constants/basic.php
@@ -0,0 +1,34 @@
+parentMethod();
+ }
+
+}
+
+new TestClass();
diff --git a/tests/Rule/data/DeadMethodRule/constants/override.php b/tests/Rule/data/DeadMethodRule/constants/override.php
new file mode 100644
index 0000000..45e2245
--- /dev/null
+++ b/tests/Rule/data/DeadMethodRule/constants/override.php
@@ -0,0 +1,25 @@
+