Skip to content

Commit

Permalink
[DX] Merge getObjectType() and getStaticType() methods to single getT…
Browse files Browse the repository at this point in the history
…ype() (#973)
  • Loading branch information
TomasVotruba committed Oct 7, 2021
1 parent 5ba1499 commit f6f7431
Show file tree
Hide file tree
Showing 66 changed files with 222 additions and 782 deletions.
83 changes: 34 additions & 49 deletions build/target-repository/docs/rector_rules_overview.md
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
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
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

0 comments on commit f6f7431

Please sign in to comment.