[PHP 8.0] Make Downgrade widening union depend on ClassMethod, the narrow scope (#375)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tomas Votruba 2021-07-04 20:11:13 +02:00 committed by GitHub
parent ae71516150
commit a9b1bbba88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 335 additions and 412 deletions

View File

@ -4766,7 +4766,7 @@ Remove the "object" param and return type, add a `@param` and `@return` tags ins
Change param type to match the lowest type in whole family tree
- class: [`Rector\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector`](../rules/DowngradePhp72/Rector/Class_/DowngradeParameterTypeWideningRector.php)
- class: [`Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector`](../rules/DowngradePhp72/Rector/Class_/DowngradeParameterTypeWideningRector.php)
```diff
interface SomeInterface

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\PhpVersion;
use Rector\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector;
use Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector;
use Rector\DowngradePhp72\Rector\FuncCall\DowngradePregUnmatchedAsNullConstantRector;
use Rector\DowngradePhp72\Rector\FuncCall\DowngradeStreamIsattyRector;
use Rector\DowngradePhp72\Rector\FunctionLike\DowngradeObjectTypeDeclarationRector;

View File

@ -77,6 +77,7 @@ final class PhpDocTypeChanger
$newType,
TypeKind::RETURN()
);
$currentReturnTagValueNode = $phpDocInfo->getReturnTagValue();
if ($currentReturnTagValueNode !== null) {

View File

@ -67,8 +67,7 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
if ($classNode instanceof Trait_) {
/** @var string $traitName */
$traitName = $classNode->getAttribute(AttributeKey::CLASS_NAME);
$scope = $this->traitNodeScopeCollector->getScopeForTraitAndNode($traitName, $node);
$scope = $this->traitNodeScopeCollector->getScopeForTrait($traitName);
}
}

View File

@ -90,7 +90,7 @@ final class VariableTypeResolver implements NodeTypeResolverInterface
if ($classLike instanceof Trait_) {
/** @var string $traitName */
$traitName = $variable->getAttribute(AttributeKey::CLASS_NAME);
$traitNodeScope = $this->traitNodeScopeCollector->getScopeForTraitAndNode($traitName, $variable);
$traitNodeScope = $this->traitNodeScopeCollector->getScopeForTrait($traitName);
if ($traitNodeScope !== null) {
return $traitNodeScope;

View File

@ -4,10 +4,7 @@ declare(strict_types=1);
namespace Rector\NodeTypeResolver\PHPStan\Collector;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\VirtualNode;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
final class TraitNodeScopeCollector
{
@ -16,38 +13,18 @@ final class TraitNodeScopeCollector
*/
private array $scopeByTraitNodeHash = [];
public function __construct(
private BetterStandardPrinter $betterStandardPrinter
) {
}
public function addForTraitAndNode(string $traitName, Node $node, Scope $scope): void
public function addForTrait(string $traitName, Scope $scope): void
{
if ($node instanceof VirtualNode) {
return;
}
$traitNodeHash = $this->createHash($traitName, $node);
// probably set from another class
if (isset($this->scopeByTraitNodeHash[$traitNodeHash])) {
if (isset($this->scopeByTraitNodeHash[$traitName])) {
return;
}
$this->scopeByTraitNodeHash[$traitNodeHash] = $scope;
$this->scopeByTraitNodeHash[$traitName] = $scope;
}
public function getScopeForTraitAndNode(string $traitName, Node $node): ?Scope
public function getScopeForTrait(string $traitName): ?Scope
{
$traitNodeHash = $this->createHash($traitName, $node);
return $this->scopeByTraitNodeHash[$traitNodeHash] ?? null;
}
private function createHash(string $traitName, Node $node): string
{
$printedNode = $this->betterStandardPrinter->print($node);
return sha1($traitName . $printedNode);
return $this->scopeByTraitNodeHash[$traitName] ?? null;
}
}

View File

@ -9,6 +9,7 @@ use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeTraverser;
use PHPStan\AnalysedCodeException;
use PHPStan\Analyser\MutatingScope;
@ -71,10 +72,15 @@ final class PHPStanNodeScopeResolver
$nodeCallback = function (Node $node, Scope $scope): void {
// traversing trait inside class that is using it scope (from referenced) - the trait traversed by Rector is different (directly from parsed file)
if ($scope->isInTrait()) {
/** @var ClassReflection $classReflection */
$classReflection = $scope->getTraitReflection();
$traitName = $classReflection->getName();
$this->traitNodeScopeCollector->addForTraitAndNode($traitName, $node, $scope);
// has just entereted trait, to avoid adding it for ever ynode
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof Trait_) {
/** @var ClassReflection $classReflection */
$classReflection = $scope->getTraitReflection();
$traitName = $classReflection->getName();
$this->traitNodeScopeCollector->addForTrait($traitName, $scope);
}
return;
}

View File

@ -543,3 +543,8 @@ parameters:
-
message: '#foreach\(\.\.\.\), while\(\), for\(\) or if\(\.\.\.\) cannot contains a complex expression\. Extract it to a new variable assign on line before#'
path: src/Application/ApplicationFileProcessor.php
- '#Callable callable\(PHPStan\\Type\\Type\)\: PHPStan\\Type\\Type invoked with 2 parameters, 1 required#'
# leave for now
- '#Cognitive complexity for "Rector\\TypeDeclaration\\TypeNormalizer\:\:normalizeArrayTypeAndArrayNever\(\)" is 14, keep it under 9#'

View File

@ -1,41 +0,0 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Another;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_;
final class SameEnd
{
/**
* @param \Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_ $firstTrait
* @param Another\Trait_ $secondTrait
* @param \Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_ $thirdTrait
*/
public function __construct(\Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_ $firstTrait, Another\Trait_ $secondTrait, \Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_ $thirdTrait)
{
}
}
?>
-----
<?php
namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Another;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_;
final class SameEnd
{
/**
* @param Trait_ $firstTrait
* @param Another\Trait_ $secondTrait
* @param Trait_ $thirdTrait
*/
public function __construct(Trait_ $firstTrait, Another\Trait_ $secondTrait, \Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_ $thirdTrait)
{
}
}
?>

View File

@ -2,11 +2,11 @@
namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\SomeTrait;
final class SameEndWithExistingImport
{
public function __construct(\Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_ $firstTrait)
public function __construct(\Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\SomeTrait $firstTrait)
{
}
}
@ -17,11 +17,11 @@ final class SameEndWithExistingImport
namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\Trait_;
use Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some\SomeTrait;
final class SameEndWithExistingImport
{
public function __construct(Trait_ $firstTrait)
public function __construct(SomeTrait $firstTrait)
{
}
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Another;
final class Trait_
final class NestedTrait
{
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Tests\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Some;
final class Trait_
final class SomeTrait
{
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
/**
* @see https://3v4l.org/8B03D
@ -21,7 +21,7 @@ final class Asker implements SomeAskingInterface
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
/**
* @see https://3v4l.org/8B03D

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SomeContainerInterface
{
@ -25,7 +25,7 @@ class SomeUniqueContainer implements SomeContainerInterface
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SomeContainerInterface
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface A
{
@ -25,7 +25,7 @@ final class MostChild implements A
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface A
{

View File

@ -1,6 +1,8 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\AnotherContainerInterface;
final class YetAnotherContainer implements AnotherContainerInterface
{
@ -14,16 +16,13 @@ trait AnotherServiceLocatorTrait
}
}
interface AnotherContainerInterface
{
public function get($name);
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\AnotherContainerInterface;
final class YetAnotherContainer implements AnotherContainerInterface
{
@ -40,9 +39,4 @@ trait AnotherServiceLocatorTrait
}
}
interface AnotherContainerInterface
{
public function get($name);
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface WhateverInterface
{
@ -23,7 +23,7 @@ class SomeChildClass extends AbstractSomeAncestorClass
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface WhateverInterface
{

View File

@ -0,0 +1,12 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\SomeExternalContainerInterface;
class KeepInterfaceDonwgraded implements SomeExternalContainerInterface
{
public function get($id)
{
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
final class KeepTraitAndInterfaceSame implements AnyContainerInterface
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SomeAskingInterfaceWithNullable
{
@ -18,7 +18,7 @@ final class AskForMore implements SomeAskingInterfaceWithNullable
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SomeAskingInterfaceWithNullable
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface ParentWithString
{
@ -18,7 +18,7 @@ class ChildString implements ParentWithString
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface ParentWithString
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SecondPositionInterface
{
@ -18,7 +18,7 @@ final class SomeSecondPosition implements SecondPositionInterface
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SecondPositionInterface
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface SomeInputInterface
{

View File

@ -1,8 +1,8 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\AnyService;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\AnyService;
use Symfony\Component\Console\Command\Command;
final class SkipCommand extends Command

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Psr\Container\ContainerInterface;

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
interface KeepOriginalIfSharedType
{

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\ParentNullableString;
final class SkipNullableStringFromExternal extends ParentNullableString
{
public function load(string $value = null)
{
}
}
?>

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\vendor\NullableStringTrait;
final class SkipNullableStringFromExternalTrait
{
use NullableStringTrait;
public function load(string $value = null)
{
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
use PhpParser\Node\Param;
use PHPStan\Type\Type;

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
trait StreamDecoratorTrait
{
@ -24,7 +24,7 @@ class MultipartStream
----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
trait StreamDecoratorTrait
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
class AncestorClass
{
@ -22,7 +22,7 @@ class ChildClass extends AncestorClass
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Fixture;
class AncestorClass
{

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source;
interface AnotherContainerInterface
{
public function get($name);
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source;
final class AnyService
{
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source;
class ParentNullableString
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source;
interface SomeExternalContainerInterface
{

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source;
final class UniqueType
{
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source;
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\vendor;
trait NullableStringTrait
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
use Rector\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector;
use Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {

View File

@ -1,12 +0,0 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\SomeExternalContainerInterface;
class KeepInterfaceDonwgraded implements SomeExternalContainerInterface
{
public function get($id)
{
}
}

View File

@ -1,14 +0,0 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\ParentNullableString;
final class SkipNullableStringFromExternal extends ParentNullableString
{
public function load(string $value = null)
{
}
}
?>

View File

@ -1,16 +0,0 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\NullableStringTrait;
final class SkipNullableStringFromExternalTrait
{
use NullableStringTrait;
public function load(string $value = null)
{
}
}
?>

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source;
final class AnyService
{
}

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source;
final class UniqueType
{
}

View File

@ -2,7 +2,7 @@
namespace Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Fixture;
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\UniqueType;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\UniqueType;
use Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Source\ILoader;
use Rector\Tests\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector\Source\SingleSomeClass;
use SplFileInfo;

View File

@ -14,11 +14,6 @@ final class ClassWithProperty
*/
private $multiCount;
/**
* @var void
*/
private $shouldBeSkipped;
/**
* @var callable
*/
@ -45,11 +40,6 @@ final class ClassWithProperty
*/
private $multiCount;
/**
* @var void
*/
private $shouldBeSkipped;
/**
* @var callable
*/

View File

@ -0,0 +1,11 @@
<?php
namespace Rector\Tests\Php74\Rector\Property\TypedPropertyRector\Fixture;
final class SkipVoid
{
/**
* @var void
*/
private $shouldBeSkipped;
}

View File

@ -2,7 +2,7 @@
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Fixture;
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Source\Foo;
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Source\vendor\Foo;
final class SkipFromExternal2
{
@ -12,5 +12,3 @@ final class SkipFromExternal2
{
}
}
?>

View File

@ -1,10 +1,10 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Source;
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Source\vendor;
trait Foo
{
public function load($resource, string $type = null)
{
}
}
}

View File

@ -1,35 +0,0 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
class KnownStaticNullableArray
{
public function getMoreItems()
{
if ((bool) rand(0, 100)) {
return null;
}
return [];
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
class KnownStaticNullableArray
{
public function getMoreItems(): ?array
{
if ((bool) rand(0, 100)) {
return null;
}
return [];
}
}
?>

View File

@ -2,11 +2,11 @@
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ExternalForVoid;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\vendor\ExternalForVoid;
final class SkipFromExternalVoid extends ExternalForVoid
{
public function run()
{
}
}
}

View File

@ -2,7 +2,7 @@
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ExternalForVoidTrait;
use Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\vendor\ExternalForVoidTrait;
final class SkipFromExternalVoidTrait
{
@ -11,4 +11,4 @@ final class SkipFromExternalVoidTrait
public function run()
{
}
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source;
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\vendor;
class ExternalForVoid
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source;
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\vendor;
trait ExternalForVoidTrait
{

View File

@ -138,22 +138,17 @@ final class CurrentAndParentClassMethodComparator
$parentMethodReflection = $classReflection->getMethod($methodName, $scope);
// 3rd party code
if ($parentMethodReflection !== null) {
if (! $parentMethodReflection->isPrivate() && ! $parentMethodReflection->isPublic() && $classMethod->isPublic()) {
return true;
}
if ($parentMethodReflection->isInternal()->yes()) {
// we can't know for certain so we assume its a override with purpose
return true;
}
if ($this->areParameterDefaultsDifferent($classMethod, $parentMethodReflection)) {
return true;
}
// if ($parentMethodReflection !== null) {
if (! $parentMethodReflection->isPrivate() && ! $parentMethodReflection->isPublic() && $classMethod->isPublic()) {
return true;
}
return false;
if ($parentMethodReflection->isInternal()->yes()) {
// we can't know for certain so we assume its a override with purpose
return true;
}
return $this->areParameterDefaultsDifferent($classMethod, $parentMethodReflection);
}
private function areParameterDefaultsDifferent(

View File

@ -7,13 +7,13 @@ namespace Rector\DeadCode\Comparator\Parameter;
use PhpParser\Node\Expr;
use PhpParser\Node\Param;
use PHPStan\Reflection\ParameterReflection;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\DowngradePhp80\Reflection\DefaultParameterValueResolver;
final class ParameterDefaultsComparator
{
public function __construct(
private ValueResolver $valueResolver,
private NodeComparator $nodeComparator,
private DefaultParameterValueResolver $defaultParameterValueResolver
) {
}
@ -34,29 +34,10 @@ final class ParameterDefaultsComparator
$firstParameterValue = $this->defaultParameterValueResolver->resolveFromParameterReflection(
$parameterReflection
);
$secondParameterValue = $this->valueResolver->getValue($paramDefault);
return $firstParameterValue !== $secondParameterValue;
return ! $this->nodeComparator->areNodesEqual($paramDefault, $firstParameterValue);
}
// /**
// * @return bool|float|int|string|mixed[]|null
// */
// public function resolveParameterReflectionDefaultValue(ParameterReflection $parameterReflection)
// {
// $defaultValue = $parameterReflection->getDefaultValue();
// if (! $defaultValue instanceof ConstantType) {
// throw new ShouldNotHappenException();
// }
//
// if ($defaultValue instanceof ConstantArrayType) {
// return $defaultValue->getAllArrays();
// }
//
// /** @var ConstantStringType|ConstantIntegerType|ConstantFloatType|ConstantBooleanType|NullType $defaultValue */
// return $defaultValue->getValue();
// }
private function isMutuallyExclusiveNull(ParameterReflection $parameterReflection, Param $param): bool
{
if ($parameterReflection->getDefaultValue() === null && $param->default !== null) {

View File

@ -5,7 +5,9 @@ declare(strict_types=1);
namespace Rector\DeadCode\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
@ -69,15 +71,11 @@ CODE_SAMPLE
if ($this->shouldSkipClass($classLike)) {
return null;
}
if ($node->stmts === null) {
return null;
}
if (count($node->stmts) !== 1) {
return null;
}
$stmtsValues = array_values($node->stmts);
$onlyStmt = $this->unwrapExpression($stmtsValues[0]);
$onlyStmt = $this->matchClassMethodOnlyStmt($node);
if ($onlyStmt === null) {
return null;
}
// are both return?
if ($this->isMethodReturnType($node, 'void') && ! $onlyStmt instanceof Return_) {
@ -143,4 +141,20 @@ CODE_SAMPLE
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
return $phpDocInfo->hasByName('required');
}
private function matchClassMethodOnlyStmt(ClassMethod $classMethod): null | Stmt | Expr
{
if ($classMethod->stmts === null) {
return null;
}
if (count($classMethod->stmts) !== 1) {
return null;
}
// recount empty notes
$stmtsValues = array_values($classMethod->stmts);
return $this->unwrapExpression($stmtsValues[0]);
}
}

View File

@ -8,8 +8,11 @@ use PhpParser\Node;
use PhpParser\Node\Param;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypehintHelper;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
@ -29,6 +32,20 @@ final class NativeTypeClassTreeResolver
): ?Type {
$nativeReflectionClass = $classReflection->getNativeReflection();
if (! $classReflection->hasNativeMethod($methodName)) {
return null;
}
$phpstanParameterReflection = null;
$methodReflection = $classReflection->getNativeMethod($methodName);
foreach ($methodReflection->getVariants() as $parametersAcceptor) {
$phpstanParameterReflection = $parametersAcceptor->getParameters()[$position] ?? null;
}
if (! $phpstanParameterReflection instanceof ParameterReflection) {
return null;
}
$reflectionMethod = $nativeReflectionClass->getMethod($methodName);
$parameterReflection = $reflectionMethod->getParameters()[$position] ?? null;
if (! $parameterReflection instanceof \ReflectionParameter) {
@ -37,7 +54,7 @@ final class NativeTypeClassTreeResolver
}
// "native" reflection from PHPStan removes the type, so we need to check with both reflection and php-paser
$nativeType = $this->resolveNativeType($parameterReflection);
$nativeType = $this->resolveNativeType($parameterReflection, $phpstanParameterReflection);
if (! $nativeType instanceof MixedType) {
return $nativeType;
}
@ -50,8 +67,10 @@ final class NativeTypeClassTreeResolver
);
}
private function resolveNativeType(\ReflectionParameter $reflectionParameter): Type
{
private function resolveNativeType(
\ReflectionParameter $reflectionParameter,
ParameterReflection $parameterReflection
): Type {
if (! $reflectionParameter instanceof ReflectionParameter) {
return new MixedType();
}
@ -70,6 +89,21 @@ final class NativeTypeClassTreeResolver
return new MixedType();
}
return $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
$paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
return $this->joinWithNullTypeIfNullDefaultValue($parameterReflection, $paramType);
}
private function joinWithNullTypeIfNullDefaultValue(ParameterReflection $parameterReflection, Type $paramType): Type
{
// nullable type!
if (! $parameterReflection->getDefaultValue() instanceof NullType) {
return $paramType;
}
if (TypeCombinator::containsNull($paramType)) {
return $paramType;
}
return TypeCombinator::addNull($paramType);
}
}

View File

@ -2,24 +2,23 @@
declare(strict_types=1);
namespace Rector\DowngradePhp72\Rector\Class_;
namespace Rector\DowngradePhp72\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeAnalyzer\ExternalFullyQualifiedAnalyzer;
use Rector\Core\Rector\AbstractRector;
use Rector\DowngradePhp72\NodeAnalyzer\ClassLikeWithTraitsClassMethodResolver;
use Rector\DowngradePhp72\NodeAnalyzer\ParamContravariantDetector;
use Rector\DowngradePhp72\NodeAnalyzer\ParentChildClassMethodTypeResolver;
use Rector\DowngradePhp72\PhpDoc\NativeParamToPhpDocDecorator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Collector\TraitNodeScopeCollector;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -28,7 +27,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
* @changelog https://www.php.net/manual/en/migration72.new-features.php#migration72.new-features.param-type-widening
* @see https://3v4l.org/fOgSE
*
* @see \Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\DowngradeParameterTypeWideningRectorTest
* @see \Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\DowngradeParameterTypeWideningRectorTest
*/
final class DowngradeParameterTypeWideningRector extends AbstractRector
{
@ -38,7 +37,7 @@ final class DowngradeParameterTypeWideningRector extends AbstractRector
private NativeParamToPhpDocDecorator $nativeParamToPhpDocDecorator,
private ParamContravariantDetector $paramContravariantDetector,
private TypeFactory $typeFactory,
private ExternalFullyQualifiedAnalyzer $externalFullyQualifiedAnalyzer
private TraitNodeScopeCollector $traitNodeScopeCollector
) {
}
@ -85,15 +84,15 @@ CODE_SAMPLE
*/
public function getNodeTypes(): array
{
return [Class_::class, Interface_::class];
return [ClassMethod::class];
}
/**
* @param Class_|Interface_ $node
* @param ClassMethod $node
*/
public function refactor(Node $node): ?Node
{
$scope = $node->getAttribute(AttributeKey::SCOPE);
$scope = $this->resolveScope($node);
if (! $scope instanceof Scope) {
return null;
}
@ -107,24 +106,26 @@ CODE_SAMPLE
return null;
}
if ($this->externalFullyQualifiedAnalyzer->hasExternalFullyQualifieds($node)) {
return null;
}
$hasChanged = false;
/** @var ClassReflection[] $ancestors */
$ancestors = $classReflection->getAncestors();
$classMethods = $this->classLikeWithTraitsClassMethodResolver->resolve($ancestors);
$classLikes = $this->nodeRepository->findClassesAndInterfacesByType($classReflection->getName());
$interfaces = $classReflection->getInterfaces();
$interfaceClassReflections = $classReflection->getInterfaces();
foreach ($classMethods as $classMethod) {
if ($this->skipClassMethod($classMethod, $classReflection, $ancestors, $classLikes)) {
continue;
}
// refactor here
$changedClassMethod = $this->refactorClassMethod($classMethod, $classReflection, $ancestors, $interfaces);
$changedClassMethod = $this->refactorClassMethod(
$classMethod,
$classReflection,
$ancestors,
$interfaceClassReflections
);
if ($changedClassMethod !== null) {
$hasChanged = true;
}
@ -235,4 +236,21 @@ CODE_SAMPLE
return count($classReflection->getAncestors()) === 1;
}
private function resolveScope(ClassMethod $classMethod): ?Scope
{
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if ($scope instanceof Scope) {
return $scope;
}
// fallback to a trait method
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
if ($classLike instanceof Trait_) {
$traitName = $this->getName($classLike);
return $this->traitNodeScopeCollector->getScopeForTrait($traitName);
}
return null;
}
}

View File

@ -12,6 +12,7 @@ use PHPStan\Type\UnionType;
final class DetailedTypeAnalyzer
{
/**
* Use this constant to avoid overly detailed long-dragging union types across whole universe
* @var int
*/
private const MAX_NUMBER_OF_TYPES = 3;

View File

@ -178,10 +178,6 @@ CODE_SAMPLE
return true;
}
if ($this->detailedTypeAnalyzer->isTooDetailed($newType)) {
return true;
}
// not an array type
if ($newType instanceof VoidType) {
return true;

View File

@ -230,14 +230,11 @@ CODE_SAMPLE
return false;
}
$hasExternalClassOrInterfaceOrTrait = $this->externalFullyQualifiedAnalyzer->hasExternalFullyQualifieds(
$classLike
);
if (! $this->externalFullyQualifiedAnalyzer->hasVendorLocatedDependency($classLike)) {
return false;
}
return $functionLike->returnType === null && $hasExternalClassOrInterfaceOrTrait && $this->isName(
$inferredReturnNode,
'void'
);
return $functionLike->returnType === null && $this->isName($inferredReturnNode, 'void');
}
private function isNullableTypeSubType(Type $currentType, Type $inferedType): bool

View File

@ -64,7 +64,11 @@ final class PropertyTypeInferer
if ($resolvedTypes !== []) {
$resolvedType = $this->typeFactory->createMixedPassedOrUnionType($resolvedTypes);
} else {
// void type is not allowed in properties
$resolvedType = $this->varDocPropertyTypeInferer->inferProperty($property);
if ($resolvedType instanceof VoidType) {
return new MixedType();
}
}
// default value type must be added to each resolved type if set

View File

@ -48,7 +48,7 @@ final class SilentVoidResolver
return false;
}
if ($classLike instanceof Class_ && $this->externalFullyQualifiedAnalyzer->hasExternalFullyQualifieds(
if ($classLike instanceof Class_ && $this->externalFullyQualifiedAnalyzer->hasVendorLocatedDependency(
$classLike
)) {
return false;

View File

@ -12,10 +12,13 @@ use PHPStan\Type\NeverType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
use Rector\TypeDeclaration\ValueObject\NestedArrayType;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
use Symplify\SimplePhpDocParser\PhpDocNodeTraverser;
/**
* @see \Rector\Tests\TypeDeclaration\TypeNormalizerTest
@ -29,7 +32,9 @@ final class TypeNormalizer
public function __construct(
private TypeFactory $typeFactory,
private UnionTypeFactory $unionTypeFactory
private UnionTypeFactory $unionTypeFactory,
// private PhpDocNodeTraverser $phpDocNodeTraverser,
private PrivatesAccessor $privatesAccessor
) {
}
@ -95,24 +100,39 @@ final class TypeNormalizer
*/
public function normalizeArrayTypeAndArrayNever(Type $type): Type
{
if (! $type instanceof UnionType) {
return $type;
}
return TypeTraverser::map($type, function (Type $traversedType, callable $traverserCallable): Type {
if ($traversedType instanceof ConstantArrayType && $traversedType->getKeyType() instanceof NeverType && $traversedType->getItemType() instanceof NeverType) {
// not sure why, but with direct new node everything gets nulled to MixedType
$this->privatesAccessor->setPrivateProperty($traversedType, 'keyType', new MixedType());
$this->privatesAccessor->setPrivateProperty($traversedType, 'itemType', new MixedType());
$nonNeverTypes = [];
foreach ($type->getTypes() as $unionedType) {
if (! $unionedType instanceof ArrayType) {
return $type;
return $traversedType;
}
if ($unionedType->getItemType() instanceof NeverType) {
continue;
if ($traversedType instanceof UnionType) {
$collectedTypes = [];
foreach ($traversedType->getTypes() as $unionedType) {
// basically an empty array - not useful at all
if ($this->isArrayNeverType($unionedType)) {
continue;
}
$collectedTypes[] = $unionedType;
}
// re-create new union types
if (count($traversedType->getTypes()) !== count($collectedTypes)) {
return $this->typeFactory->createMixedPassedOrUnionType($collectedTypes);
}
}
$nonNeverTypes[] = $unionedType;
}
if ($traversedType instanceof NeverType) {
return new MixedType();
}
return $this->typeFactory->createMixedPassedOrUnionType($nonNeverTypes);
return $traverserCallable($traversedType, $traverserCallable);
});
}
/**
@ -164,4 +184,13 @@ final class TypeNormalizer
return $unionedTypes[0];
}
private function isArrayNeverType(Type $type): bool
{
if (! $type instanceof ArrayType) {
return false;
}
return $type->getKeyType() instanceof NeverType && $type->getItemType() instanceof NeverType;
}
}

View File

@ -4,100 +4,41 @@ declare(strict_types=1);
namespace Rector\Core\NodeAnalyzer;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\TraitUse;
use Rector\NodeCollector\NodeCollector\NodeRepository;
use Rector\NodeNameResolver\NodeNameResolver;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class ExternalFullyQualifiedAnalyzer
{
public function __construct(
private NodeNameResolver $nodeNameResolver,
private NodeRepository $nodeRepository
) {
}
public function hasExternalFullyQualifieds(ClassLike $classLike): bool
{
if ($classLike instanceof Class_ || $classLike instanceof Interface_) {
$extends = $classLike->extends ?? [];
} else {
$extends = [];
}
/** @var FullyQualified[] $extends */
$extends = $extends instanceof FullyQualified
? [$extends]
: $extends;
/** @var FullyQualified[] $implements */
$implements = $classLike instanceof Class_ ? $classLike->implements : [];
$parentClassesAndInterfaces = array_merge($extends, $implements);
$hasExternalClassOrInterface = $this->hasExternalClassOrInterface($parentClassesAndInterfaces);
if ($hasExternalClassOrInterface) {
return true;
}
/** @var TraitUse[] $traitUses */
$traitUses = $classLike->getTraitUses();
return $this->hasExternalTrait($traitUses);
}
/**
* @param FullyQualified[] $fullyQualifiedClassLikes
* Is in a class that depends on a class, interface or trait located in vendor?
*/
private function hasExternalClassOrInterface(array $fullyQualifiedClassLikes): bool
public function hasVendorLocatedDependency(Node $node): bool
{
if ($fullyQualifiedClassLikes === []) {
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return false;
}
foreach ($fullyQualifiedClassLikes as $fullyQualifiedClassLike) {
/** @var string $className */
$className = $this->nodeNameResolver->getName($fullyQualifiedClassLike);
$isClassFound = (bool) $this->nodeRepository->findClass($className);
$isInterfaceFound = (bool) $this->nodeRepository->findInterface($className);
if ($isClassFound) {
continue;
}
if ($isInterfaceFound) {
continue;
}
return true;
}
return false;
}
/**
* @param TraitUse[] $traitUses
*/
private function hasExternalTrait(array $traitUses): bool
{
if ($traitUses === []) {
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return false;
}
foreach ($traitUses as $traitUse) {
$traits = $traitUse->traits;
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
if ($classReflection === $ancestorClassReflection) {
continue;
}
foreach ($traits as $trait) {
if (! $trait instanceof FullyQualified) {
return false;
}
$fileName = $ancestorClassReflection->getFileName();
if ($fileName === false) {
continue;
}
/** @var string $traitName */
$traitName = $this->nodeNameResolver->getName($trait);
$isTraitFound = (bool) $this->nodeRepository->findTrait($traitName);
if (! $isTraitFound) {
return true;
}
// file is located in vendor → out of modifiable scope
if (str_contains($fileName, '/vendor/')) {
return true;
}
}