Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DX] Merge getObjectType() and getStaticType() methods to single getType() #973

Merged
merged 14 commits into from
Oct 7, 2021
Merged
83 changes: 34 additions & 49 deletions build/target-repository/docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

- [Carbon](#carbon) (2)

- [CodeQuality](#codequality) (68)
- [CodeQuality](#codequality) (69)

- [CodingStyle](#codingstyle) (39)

Expand Down Expand Up @@ -90,7 +90,7 @@

- [Renaming](#renaming) (11)

- [Restoration](#restoration) (6)
- [Restoration](#restoration) (5)

- [Strict](#strict) (5)

Expand Down Expand Up @@ -845,6 +845,38 @@ Make if conditions more explicit

<br>

### ExplicitMethodCallOverMagicGetSetRector

Replace magic property fetch using `__get()` and `__set()` with existing method get*()/set*() calls

- class: [`Rector\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector`](../rules/CodeQuality/Rector/PropertyFetch/ExplicitMethodCallOverMagicGetSetRector.php)

```diff
class MagicCallsObject
{
// adds magic __get() and __set() methods
use \Nette\SmartObject;

private $name;

public function getName()
{
return $this->name;
}
}

class SomeClass
{
public function run(MagicObject $magicObject)
{
- return $magicObject->name;
+ return $magicObject->getName();
}
}
```

<br>

### FixClassCaseSensitivityNameRector

Change miss-typed case sensitivity name to correct one
Expand Down Expand Up @@ -9651,53 +9683,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {

<br>

### InferParamFromClassMethodReturnRector

Change `@param` doc based on another method return type

:wrench: **configure it!**

- class: [`Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector`](../rules/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php)

```php
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\SymfonyPhpConfig\ValueObjectInliner;

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();

$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => ValueObjectInliner::inline([
new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'),
]),
]]);
};
```


```diff
class SomeClass
{
public function getNodeTypes(): array
{
return [String_::class];
}

+ /**
+ * @param String_ $node
+ */
public function process(Node $node)
{
}
}
```

<br>

### MakeTypedPropertyNullableIfCheckedRector

Make typed property nullable if checked
Expand Down
152 changes: 56 additions & 96 deletions packages/NodeTypeResolver/NodeTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
Expand All @@ -39,7 +38,6 @@
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\Configuration\RenamedClassesDataCollector;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
Expand Down Expand Up @@ -108,7 +106,7 @@ public function isObjectType(Node $node, ObjectType $requiredObjectType): bool
return false;
}

$resolvedType = $this->resolve($node);
$resolvedType = $this->getType($node);
if ($resolvedType instanceof MixedType) {
return false;
}
Expand All @@ -124,32 +122,27 @@ public function isObjectType(Node $node, ObjectType $requiredObjectType): bool
return $this->isMatchingUnionType($resolvedType, $requiredObjectType);
}

/**
* @deprecated
* @see use NodeTypeResolver::getType() instead
*/
public function resolve(Node $node): Type
{
if ($node instanceof Ternary) {
if ($node->if !== null) {
$first = $this->resolve($node->if);
$second = $this->resolve($node->else);

if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}

$condType = $this->resolve($node->cond);
if ($this->isNullableType($node->cond) && $condType instanceof UnionType) {
$first = $condType->getTypes()[0];
$second = $this->resolve($node->else);
return $this->getType($node);
}

if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
public function getType(Node $node): Type
{
if ($node instanceof Ternary) {
$ternaryType = $this->resolveTernaryType($node);
if (! $ternaryType instanceof MixedType) {
return $ternaryType;
}
}

if ($node instanceof Coalesce) {
$first = $this->resolve($node->left);
$second = $this->resolve($node->right);
$first = $this->getType($node->left);
$second = $this->getType($node->right);

if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
Expand All @@ -159,6 +152,9 @@ public function resolve(Node $node): Type
$type = $this->resolveByNodeTypeResolvers($node);
if ($type !== null) {
$type = $this->accessoryNonEmptyStringTypeCorrector->correct($type);

$type = $this->genericClassStringTypeCorrector->correct($type);

return $this->hasOffsetTypeCorrector->correct($type);
}

Expand Down Expand Up @@ -190,8 +186,11 @@ public function resolve(Node $node): Type
}

$type = $scope->getType($node);

$type = $this->accessoryNonEmptyStringTypeCorrector->correct($type);

$type = $this->genericClassStringTypeCorrector->correct($type);

// hot fix for phpstan not resolving chain method calls
if (! $node instanceof MethodCall) {
return $type;
Expand All @@ -201,15 +200,15 @@ public function resolve(Node $node): Type
return $type;
}

return $this->resolve($node->var);
return $this->getType($node->var);
}

/**
* e.g. string|null, ObjectNull|null
*/
public function isNullableType(Node $node): bool
{
$nodeType = $this->resolve($node);
$nodeType = $this->getType($node);
return TypeCombinator::containsNull($nodeType);
}

Expand All @@ -225,16 +224,8 @@ public function getNativeType(Expr $expr): Type

public function getStaticType(Node $node): Type
{
if ($node instanceof Param) {
return $this->resolve($node);
}

if ($node instanceof New_) {
return $this->resolve($node);
}

if ($node instanceof Return_) {
return $this->resolve($node);
if ($node instanceof Param || $node instanceof New_ || $node instanceof Return_) {
return $this->getType($node);
}

if (! $node instanceof Expr) {
Expand All @@ -246,7 +237,7 @@ public function getStaticType(Node $node): Type
}

if ($node instanceof Scalar) {
return $this->resolve($node);
return $this->getType($node);
}

$scope = $node->getAttribute(AttributeKey::SCOPE);
Expand All @@ -268,36 +259,20 @@ public function getStaticType(Node $node): Type

public function isNumberType(Node $node): bool
{
if ($this->isStaticType($node, IntegerType::class)) {
$nodeType = $this->getType($node);
if ($nodeType instanceof IntegerType) {
return true;
}

return $this->isStaticType($node, FloatType::class);
}

/**
* @param class-string<Type> $staticTypeClass
*/
public function isStaticType(Node $node, string $staticTypeClass): bool
{
if (! is_a($staticTypeClass, Type::class, true)) {
throw new ShouldNotHappenException(sprintf(
'"%s" in "%s()" must be type of "%s"',
$staticTypeClass,
__METHOD__,
Type::class
));
}

return is_a($this->resolve($node), $staticTypeClass);
return $nodeType instanceof FloatType;
}

/**
* @param class-string<Type> $desiredType
*/
public function isNullableTypeOfSpecificType(Node $node, string $desiredType): bool
{
$nodeType = $this->resolve($node);
$nodeType = $this->getType($node);
if (! $nodeType instanceof UnionType) {
return false;
}
Expand All @@ -306,17 +281,8 @@ public function isNullableTypeOfSpecificType(Node $node, string $desiredType): b
return false;
}

if (count($nodeType->getTypes()) !== 2) {
return false;
}

foreach ($nodeType->getTypes() as $type) {
if (is_a($type, $desiredType, true)) {
return true;
}
}

return false;
$bareType = TypeCombinator::removeNull($nodeType);
return is_a($bareType, $desiredType, true);
}

/**
Expand All @@ -331,21 +297,6 @@ public function getFullyQualifiedClassName(TypeWithClassName $typeWithClassName)
return $typeWithClassName->getClassName();
}

/**
* @param Type[] $desiredTypes
*/
public function isSameObjectTypes(ObjectType $objectType, array $desiredTypes): bool
{
foreach ($desiredTypes as $desiredType) {
$desiredTypeEquals = $desiredType->equals($objectType);
if ($desiredTypeEquals) {
return true;
}
}

return false;
}

public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType $objectType): bool
{
if ($node instanceof MethodCall) {
Expand All @@ -365,21 +316,6 @@ public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType
return $this->isObjectType($classLike, $objectType);
}

public function resolveObjectTypeFromScope(Scope $scope): ?ObjectType
{
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}

$className = $classReflection->getName();
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}

return new ObjectType($className, null, $classReflection);
}

private function isUnionTypeable(Type $first, Type $second): bool
{
return ! $first instanceof UnionType && ! $second instanceof UnionType && ! $second instanceof NullType;
Expand Down Expand Up @@ -501,4 +437,28 @@ private function resolveObjectType(ObjectType $resolvedObjectType, ObjectType $r

return true;
}

private function resolveTernaryType(Ternary $ternary): MixedType|UnionType
{
if ($ternary->if !== null) {
$first = $this->getType($ternary->if);
$second = $this->getType($ternary->else);

if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}

$condType = $this->getType($ternary->cond);
if ($this->isNullableType($ternary->cond) && $condType instanceof UnionType) {
$first = $condType->getTypes()[0];
$second = $this->getType($ternary->else);

if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}

return new MixedType();
}
}
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ parameters:
- '#Cognitive complexity for "Rector\\BetterPhpDocParser\\PhpDocParser\\DoctrineAnnotationDecorator\:\:mergeNestedDoctrineAnnotations\(\)" is \d+, keep it under 9#'

- '#Cognitive complexity for "Rector\\BetterPhpDocParser\\Printer\\PhpDocInfoPrinter\:\:printDocChildNode\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\NodeTypeResolver\\NodeTypeResolver\:\:resolve\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\NodeTypeResolver\\NodeTypeResolver\:\:getType\(\)" is \d+, keep it under 9#'

-
message: '#Property with protected modifier is not allowed\. Use interface contract method instead#'
Expand Down
Loading