Skip to content

Commit

Permalink
[TypeDeclaration] Removing NodeRepository from ParamTypeDeclarationRe…
Browse files Browse the repository at this point in the history
…ctor (#597)

Co-authored-by: GitHub Action <action@github.com>
  • Loading branch information
TomasVotruba and actions-user committed Aug 5, 2021
1 parent 3eb5036 commit ad15c5f
Show file tree
Hide file tree
Showing 21 changed files with 155 additions and 281 deletions.
40 changes: 0 additions & 40 deletions packages/NodeCollector/NodeCollector/NodeRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PHPStan\Reflection\ReflectionProvider;
use Rector\NodeNameResolver\NodeNameResolver;
Expand Down Expand Up @@ -58,15 +57,6 @@ public function findUsedTraitsInClass(ClassLike $classLike): array
return $traits;
}

/**
* @return Class_[]|Interface_[]
* @deprecated Use static reflection instead
*/
public function findClassesAndInterfacesByType(string $type): array
{
return array_merge($this->findChildrenOfClass($type), $this->findImplementersOfInterface($type));
}

/**
* @deprecated Use static reflection instead
*
Expand All @@ -89,16 +79,6 @@ public function findChildrenOfClass(string $class): array
return $childrenClasses;
}

/**
* @deprecated Use static reflection instead
*
* @param class-string $class
*/
public function findInterface(string $class): ?Interface_
{
return $this->parsedNodeCollector->findInterface($class);
}

/**
* @deprecated Use static reflection instead
*
Expand Down Expand Up @@ -132,24 +112,4 @@ private function isChildOrEqualClassLike(string $desiredClass, ?string $currentC

return $currentClassName !== $desiredClass;
}

/**
* @return Interface_[]
*/
private function findImplementersOfInterface(string $interface): array
{
$implementerInterfaces = [];

foreach ($this->parsedNodeCollector->getInterfaces() as $interfaceNode) {
$className = $interfaceNode->getAttribute(AttributeKey::CLASS_NAME);

if (! $this->isChildOrEqualClassLike($interface, $className)) {
continue;
}

$implementerInterfaces[] = $interfaceNode;
}

return $implementerInterfaces;
}
}
5 changes: 0 additions & 5 deletions packages/NodeCollector/NodeCollector/ParsedNodeCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ public function findClass(string $name): ?Class_
return $this->classes[$name] ?? null;
}

public function findInterface(string $name): ?Interface_
{
return $this->interfaces[$name] ?? null;
}

public function findTrait(string $name): ?Trait_
{
return $this->traits[$name] ?? null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@

use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\BetterReflection\Reflection\ReflectionClass;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
use Symplify\SmartFileSystem\Normalizer\PathNormalizer;

final class ClassMethodParamVendorLockResolver
{
public function __construct(
private NodeNameResolver $nodeNameResolver
private NodeNameResolver $nodeNameResolver,
private PathNormalizer $pathNormalizer,
private ReflectionProvider $reflectionProvider,
private PrivatesAccessor $privatesAccessor
) {
}

Expand All @@ -23,14 +30,21 @@ public function isVendorLocked(ClassMethod $classMethod): bool
return true;
}

$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
$classReflection = $this->resolveClassReflection($classMethod);
if (! $classReflection instanceof ClassReflection) {
return false;
}

$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return false;
/** @var string $methodName */
$methodName = $this->nodeNameResolver->getName($classMethod);

if ($this->hasTraitMethodVendorLock($classReflection, $methodName)) {
return true;
}

// has interface vendor lock? → better skip it, as PHPStan has access only to just analyzed classes
if ($this->hasParentInterfaceMethod($classReflection, $methodName)) {
return true;
}

$methodName = $this->nodeNameResolver->getName($classMethod);
Expand All @@ -41,7 +55,86 @@ public function isVendorLocked(ClassMethod $classMethod): bool
}

// parent type
if ($ancestorClassReflection->hasNativeMethod($methodName)) {
if (! $ancestorClassReflection->hasNativeMethod($methodName)) {
continue;
}

// is file in vendor?
$fileName = $ancestorClassReflection->getFileName();
// probably internal class
if ($fileName === false) {
continue;
}

$normalizedFileName = $this->pathNormalizer->normalizePath($fileName);
return str_contains($normalizedFileName, '/vendor/');
}

return false;
}

/**
* @return ReflectionClass[]
*/
private function findRelatedClassReflections(ClassReflection $classReflection): array
{
// @todo decouple to some reflection family finder?
/** @var ReflectionClass[] $reflectionClasses */
$reflectionClasses = $this->privatesAccessor->getPrivateProperty($this->reflectionProvider, 'classes');

$relatedClassReflections = [];
foreach ($reflectionClasses as $reflectionClass) {
if ($reflectionClass->getName() === $classReflection->getName()) {
continue;
}

// is related?
if (! $reflectionClass->isSubclassOf($classReflection->getName())) {
continue;
}

$relatedClassReflections[] = $reflectionClass;
}

return $relatedClassReflections;
}

private function hasTraitMethodVendorLock(ClassReflection $classReflection, string $methodName): bool
{
$relatedReflectionClasses = $this->findRelatedClassReflections($classReflection);

foreach ($relatedReflectionClasses as $relatedReflectionClass) {
foreach ($relatedReflectionClass->getTraits() as $traitReflectionClass) {
/** @var ClassReflection $traitReflectionClass */
if ($traitReflectionClass->hasMethod($methodName)) {
return true;
}
}
}

return false;
}

private function resolveClassReflection(ClassMethod $classMethod): ClassReflection|null
{
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return null;
}

return $scope->getClassReflection();
}

/**
* Has interface even in our project?
* Better skip it, as PHPStan has access only to just analyzed classes.
* This might change type, that works for current class, but breaks another implementer.
*/
private function hasParentInterfaceMethod(ClassReflection $classReflection, string $methodName): bool
{
foreach ($classReflection->getInterfaces() as $interfaceClassReflection) {
if ($interfaceClassReflection->hasMethod($methodName)) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\Php80\Rector\FunctionLike\UnionTypesRector\Fixture;

use Rector\Tests\Php80\Rector\FunctionLike\UnionTypesRector\Source\ParentClassWithMethod;
use Rector\Tests\Php80\Rector\FunctionLike\UnionTypesRector\Source\vendor\ParentClassWithMethod;

final class Child extends ParentClassWithMethod
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

declare(strict_types=1);

namespace Rector\Tests\Php80\Rector\FunctionLike\UnionTypesRector\Source;
namespace Rector\Tests\Php80\Rector\FunctionLike\UnionTypesRector\Source\vendor;

class ParentClassWithMethod
abstract class ParentClassWithMethod
{
public function enqueueAt($at)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Fixture;

use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Source\AbstractParentClass;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Source\vendor\AbstractParentClass;

final class ChildClass extends AbstractParentClass
{
Expand All @@ -27,7 +27,7 @@ final class ChildClass extends AbstractParentClass

namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Fixture;

use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Source\AbstractParentClass;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Source\vendor\AbstractParentClass;

final class ChildClass extends AbstractParentClass
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ final class LocalChildClass extends AbstractLocalParentClass
{
}

/**
* @param int $number
*/
public function changeToo(int $number)
{
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Fixture;

trait SkipTraitWithInterfaceImplementation
{
public function getCount($items)
{
return 5;
}
}

interface SomeInterface
{
/**
* @param array $items
* @return int
*/
public function getCount($items);
}

class SomeClassWithInterfaceAndTrait implements SomeInterface
{
use SkipTraitWithInterfaceImplementation;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Fixture;

use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Source\SniffInterface;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector\Source\vendor\SniffInterface;

final class SkipVendorLocatedTypes implements SniffInterface
{
Expand Down

This file was deleted.

0 comments on commit ad15c5f

Please sign in to comment.