-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
427 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace ShipMonk\PHPStan\DeadCode\Collector; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Expr; | ||
use PhpParser\Node\Expr\ClassConstFetch; | ||
use PhpParser\Node\Name; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Collectors\Collector; | ||
use PHPStan\Node\ClassMethodsNode; | ||
use PHPStan\Reflection\MethodReflection; | ||
use PHPStan\Type\Constant\ConstantStringType; | ||
use ShipMonk\PHPStan\DeadCode\Crate\ClassConstantFetch; | ||
use ShipMonk\PHPStan\DeadCode\Crate\ClassConstantRef; | ||
use ShipMonk\PHPStan\DeadCode\Crate\ClassMethodRef; | ||
use function array_map; | ||
|
||
/** | ||
* @implements Collector<Node, list<string>> | ||
*/ | ||
class ConstantFetchCollector implements Collector | ||
{ | ||
|
||
/** | ||
* @var list<ClassConstantFetch> | ||
*/ | ||
private array $accessBuffer = []; | ||
|
||
public function getNodeType(): string | ||
{ | ||
return Node::class; | ||
} | ||
|
||
/** | ||
* @return non-empty-list<string>|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()); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace ShipMonk\PHPStan\DeadCode\Crate; | ||
|
||
use LogicException; | ||
use function serialize; | ||
use function unserialize; | ||
|
||
/** | ||
* @immutable | ||
*/ | ||
class ClassConstantFetch | ||
{ | ||
|
||
public ?ClassMethodRef $origin; // TODO always known class, introduce new type? | ||
public ClassConstantRef $fetch; | ||
|
||
public function __construct( | ||
?ClassMethodRef $origin, | ||
ClassConstantRef $fetch | ||
) | ||
{ | ||
$this->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; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace ShipMonk\PHPStan\DeadCode\Crate; | ||
|
||
/** | ||
* @immutable | ||
*/ | ||
class ClassConstantRef extends ClassMemberRef | ||
{ | ||
|
||
public function __construct( | ||
?string $className, | ||
string $constantName | ||
) | ||
{ | ||
parent::__construct($className, $constantName, ClassMemberRef::TYPE_CONSTANT); | ||
} | ||
|
||
} |
Oops, something went wrong.