[PHP 7.4] Skip dependency on native ReflectionProperty->getType() (#1409)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tomas Votruba 2021-12-06 22:29:15 +03:00 committed by GitHub
parent 26cc1817b2
commit 079b20c894
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 55 deletions

View File

@ -9,6 +9,7 @@ use Rector\ChangesReporting\Annotation\RectorsChangelogResolver;
use Rector\ChangesReporting\Contract\Output\OutputFormatterInterface;
use Rector\Core\ValueObject\Configuration;
use Rector\Core\ValueObject\ProcessResult;
use Rector\Parallel\ValueObject\Bridge;
final class JsonOutputFormatter implements OutputFormatterInterface
{
@ -29,7 +30,7 @@ final class JsonOutputFormatter implements OutputFormatterInterface
public function report(ProcessResult $processResult, Configuration $configuration): void
{
$errorsArray = [
$errorsJson = [
'meta' => [
'config' => $configuration->getMainConfigFilePath(),
],
@ -47,7 +48,7 @@ final class JsonOutputFormatter implements OutputFormatterInterface
$appliedRectorsWithChangelog = $this->rectorsChangelogResolver->resolve($fileDiff->getRectorClasses());
$errorsArray['file_diffs'][] = [
$errorsJson[Bridge::FILE_DIFFS][] = [
'file' => $relativeFilePath,
'diff' => $fileDiff->getDiff(),
'applied_rectors' => $fileDiff->getRectorClasses(),
@ -55,18 +56,18 @@ final class JsonOutputFormatter implements OutputFormatterInterface
];
// for Rector CI
$errorsArray['changed_files'][] = $relativeFilePath;
$errorsJson['changed_files'][] = $relativeFilePath;
}
$errors = $processResult->getErrors();
$errorsArray['totals']['errors'] = count($errors);
$errorsJson['totals']['errors'] = count($errors);
$errorsData = $this->createErrorsData($errors);
if ($errorsData !== []) {
$errorsArray['errors'] = $errorsData;
$errorsJson['errors'] = $errorsData;
}
$json = Json::encode($errorsArray, Json::PRETTY);
$json = Json::encode($errorsJson, Json::PRETTY);
echo $json . PHP_EOL;
}
@ -79,20 +80,20 @@ final class JsonOutputFormatter implements OutputFormatterInterface
$errorsData = [];
foreach ($errors as $error) {
$errorData = [
$errorDataJson = [
'message' => $error->getMessage(),
'file' => $error->getRelativeFilePath(),
];
if ($error->getRectorClass()) {
$errorData['caused_by'] = $error->getRectorClass();
$errorDataJson['caused_by'] = $error->getRectorClass();
}
if ($error->getLine() !== null) {
$errorData['line'] = $error->getLine();
$errorDataJson['line'] = $error->getLine();
}
$errorsData[] = $errorData;
$errorsData[] = $errorDataJson;
}
return $errorsData;

View File

@ -0,0 +1,17 @@
<?php
namespace Rector\Tests\DowngradePhp74\Rector\MethodCall\DowngradeReflectionGetTypeRector\Fixture;
use ReflectionNamedType;
final class SkipInstanceOf
{
public function run(\ReflectionProperty $reflectionProperty)
{
if ($reflectionProperty->getType() instanceof ReflectionNamedType) {
return true;
}
return false;
}
}

View File

@ -10,6 +10,7 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigura
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(MethodCallToMethodCallRector::class)
->configure([new MethodCallToMethodCall(FirstDependency::class, 'go', SecondDependency::class, 'away')]);
};

View File

@ -5,9 +5,11 @@ declare(strict_types=1);
namespace Rector\DowngradePhp74\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -73,6 +75,11 @@ CODE_SAMPLE
return null;
}
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($parent instanceof Instanceof_) {
return null;
}
return $this->nodeFactory->createNull();
}
}

View File

@ -95,9 +95,9 @@ final class TypeProvidingExprFromClassResolver
Scope $scope,
ObjectType $objectType
): ?PropertyFetch {
$reflectionClass = $classReflection->getNativeReflection();
$nativeReflectionClass = $classReflection->getNativeReflection();
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
foreach ($nativeReflectionClass->getProperties() as $reflectionProperty) {
/** @var PhpPropertyReflection $phpPropertyReflection */
$phpPropertyReflection = $classReflection->getProperty($reflectionProperty->getName(), $scope);

View File

@ -78,11 +78,7 @@ class SomeClass
}
CODE_SAMPLE
,
[
self::METHOD_CALLS_TO_METHOD_CALLS => [
new MethodCallToMethodCall('FirstDependency', 'go', 'SecondDependency', 'away'),
],
]
[new MethodCallToMethodCall('FirstDependency', 'go', 'SecondDependency', 'away')]
),
]);
}
@ -172,7 +168,7 @@ CODE_SAMPLE
return $newPropertyName;
}
// re-use existing proeprty name
// re-use existing property name
return $this->getName($classContextProperty);
}
}

View File

@ -7,14 +7,14 @@ namespace Rector\Core\NodeAnalyzer;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\PhpParser\AstResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\Php80\NodeAnalyzer\PromotedPropertyResolver;
use Rector\PostRector\ValueObject\PropertyMetadata;
use ReflectionNamedType;
use ReflectionProperty;
/**
* Can be local property, parent property etc.
@ -25,7 +25,7 @@ final class PropertyPresenceChecker
private readonly PromotedPropertyResolver $promotedPropertyResolver,
private readonly NodeNameResolver $nodeNameResolver,
private readonly ReflectionProvider $reflectionProvider,
private readonly AstResolver $astResolver
private readonly AstResolver $astResolver,
) {
}
@ -40,7 +40,11 @@ final class PropertyPresenceChecker
public function getClassContextProperty(Class_ $class, PropertyMetadata $propertyMetadata): Property | Param | null
{
$className = (string) $this->nodeNameResolver->getName($class);
$className = $this->nodeNameResolver->getName($class);
if ($className === null) {
return null;
}
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}
@ -50,12 +54,13 @@ final class PropertyPresenceChecker
return $property;
}
$property = $this->matchPropertyByParentPublicOrProtectedProperties($className, $propertyMetadata);
$property = $this->matchPropertyByParentNonPrivateProperties($className, $propertyMetadata);
if ($property instanceof Property || $property instanceof Param) {
return $property;
}
$promotedPropertyParams = $this->promotedPropertyResolver->resolveFromClass($class);
foreach ($promotedPropertyParams as $promotedPropertyParam) {
if ($this->nodeNameResolver->isName($promotedPropertyParam, $propertyMetadata->getName())) {
return $promotedPropertyParam;
@ -66,9 +71,9 @@ final class PropertyPresenceChecker
}
/**
* @return ReflectionProperty[]
* @return PhpPropertyReflection[]
*/
private function getParentClassPublicAndProtectedPropertyReflections(string $className): array
private function getParentClassNonPrivatePropertyReflections(string $className): array
{
if (! $this->reflectionProvider->hasClass($className)) {
return [];
@ -77,14 +82,13 @@ final class PropertyPresenceChecker
$classReflection = $this->reflectionProvider->getClass($className);
$propertyReflections = [];
foreach ($classReflection->getParents() as $parentClassReflection) {
$nativeReflectionClass = $parentClassReflection->getNativeReflection();
$propertyNames = $this->resolveNonPrivatePropertyNames($parentClassReflection);
$currentPropertyReflections = $nativeReflectionClass->getProperties(
ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED
);
$propertyReflections = [...$propertyReflections, ...$currentPropertyReflections];
foreach ($propertyNames as $propertyName) {
$propertyReflections[] = $parentClassReflection->getNativeProperty($propertyName);
}
}
return $propertyReflections;
@ -92,57 +96,68 @@ final class PropertyPresenceChecker
private function matchPropertyByType(
PropertyMetadata $propertyMetadata,
ReflectionProperty $reflectionProperty
PhpPropertyReflection $phpPropertyReflection
): Property | Param | null {
if ($propertyMetadata->getType() === null) {
return null;
}
if (! $reflectionProperty->getType() instanceof ReflectionNamedType) {
return null;
}
if (! $propertyMetadata->getType() instanceof TypeWithClassName) {
return null;
}
$propertyObjectType = $propertyMetadata->getType();
$propertyObjectTypeClassName = $propertyObjectType->getClassName();
if ($propertyObjectTypeClassName !== (string) $reflectionProperty->getType()) {
if (! $phpPropertyReflection->getWritableType() instanceof TypeWithClassName) {
return null;
}
$propertyObjectType = $propertyMetadata->getType();
$propertyObjectTypeClassName = $propertyObjectType->getClassName();
if ($propertyObjectTypeClassName !== (string) $reflectionProperty->getType()) {
if (! $propertyObjectType->equals($phpPropertyReflection->getWritableType())) {
return null;
}
return $this->astResolver->resolvePropertyFromPropertyReflection($reflectionProperty);
return $this->astResolver->resolvePropertyFromPropertyReflection($phpPropertyReflection);
}
private function matchPropertyByParentPublicOrProtectedProperties(
private function matchPropertyByParentNonPrivateProperties(
string $className,
PropertyMetadata $propertyMetadata
PropertyMetadata $propertyMetadata,
): Property | Param | null {
$availablePropertyReflections = $this->getParentClassPublicAndProtectedPropertyReflections($className);
$availablePropertyReflections = $this->getParentClassNonPrivatePropertyReflections($className);
foreach ($availablePropertyReflections as $availablePropertyReflection) {
// 1. match type by priority
$property = $this->matchPropertyByType($propertyMetadata, $availablePropertyReflection);
if ($property instanceof Property || $property instanceof Param) {
return $property;
}
$nativePropertyReflection = $availablePropertyReflection->getNativeReflection();
// 2. match by name
if ($availablePropertyReflection->getName() === $propertyMetadata->getName()) {
if ($nativePropertyReflection->getName() === $propertyMetadata->getName()) {
return $this->astResolver->resolvePropertyFromPropertyReflection($availablePropertyReflection);
}
}
return null;
}
/**
* @return string[]
*/
private function resolveNonPrivatePropertyNames(ClassReflection $classReflection): array
{
$propertyNames = [];
$reflectionClass = $classReflection->getNativeReflection();
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
if ($reflectionProperty->isPrivate()) {
continue;
}
$propertyNames[] = $reflectionProperty->getName();
}
return $propertyNames;
}
}

View File

@ -22,6 +22,7 @@ use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
@ -31,7 +32,6 @@ use Rector\Core\ValueObject\MethodName;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;
use Rector\NodeTypeResolver\NodeTypeResolver;
use ReflectionProperty;
use Symplify\Astral\PhpParser\SmartPhpParser;
use Symplify\SmartFileSystem\SmartFileInfo;
use Symplify\SmartFileSystem\SmartFileSystem;
@ -257,12 +257,12 @@ final class AstResolver
}
public function resolvePropertyFromPropertyReflection(
ReflectionProperty $reflectionProperty
PhpPropertyReflection $phpPropertyReflection
): Property | Param | null {
$reflectionClass = $reflectionProperty->getDeclaringClass();
$classReflection = $phpPropertyReflection->getDeclaringClass();
$fileName = $reflectionClass->getFileName();
if ($fileName === false) {
$fileName = $classReflection->getFileName();
if ($fileName === null) {
return null;
}
@ -271,7 +271,8 @@ final class AstResolver
return null;
}
$desiredPropertyName = $reflectionProperty->name;
$nativeReflectionProperty = $phpPropertyReflection->getNativeReflection();
$desiredPropertyName = $nativeReflectionProperty->getName();
/** @var Property[] $properties */
$properties = $this->betterNodeFinder->findInstanceOf($nodes, Property::class);