From 8b81d5794b488a42b310b86d5e83b0ea58167405 Mon Sep 17 00:00:00 2001 From: Can Vural Date: Thu, 24 Oct 2024 20:20:49 +0200 Subject: [PATCH] feat: adjust AddGenericReturnTypeToRelationsRector rule to be able to generate new generic style code --- docs/rector_rules_overview.md | 19 ++ .../AddGenericReturnTypeToRelationsRector.php | 162 ++++++++++++++++-- .../Eloquent/Relations/HasOneThrough.php | 11 ++ ...rnTypeToRelationsRectorNewGenericsTest.php | 32 ++++ ...nTypeToRelationsRectorOldGenericsTest.php} | 6 +- .../Fixture/NewGenerics/empty-phpdoc.php.inc | 42 +++++ .../NewGenerics/has-one-through.php.inc | 58 +++++++ .../Fixture/NewGenerics/no-phpdoc.php.inc | 40 +++++ .../phpdoc-with-existing-return.php.inc | 4 +- .../phpdoc-with-supported-return.php.inc | 43 +++++ .../NewGenerics/phpdoc-without-return.php.inc | 44 +++++ .../NewGenerics/relation-in-trait.php.inc | 50 ++++++ .../relation-with-child-generics.php.inc | 6 +- ...lation-with-existing-child-generic.php.inc | 63 +++++++ ...th-existing-morph-to-child-generic.php.inc | 2 +- .../relation-without-return-type.php.inc | 6 +- .../{ => OldGenerics}/empty-phpdoc.php.inc | 6 +- .../{ => OldGenerics}/no-phpdoc.php.inc | 6 +- .../phpdoc-with-existing-return.php.inc | 43 +++++ .../phpdoc-with-supported-return.php.inc | 6 +- .../phpdoc-without-return.php.inc | 6 +- .../relation-in-trait.php.inc | 6 +- .../relation-with-child-generics.php.inc | 40 +++++ ...lation-with-existing-child-generic.php.inc | 4 +- ...th-existing-morph-to-child-generic.php.inc | 17 ++ .../relation-without-return-type.php.inc | 35 ++++ ...p => use_new_generics_configured_rule.php} | 4 +- .../use_old_generics_configured_rule.php | 14 ++ 28 files changed, 728 insertions(+), 47 deletions(-) create mode 100644 stubs/Illuminate/Database/Eloquent/Relations/HasOneThrough.php create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorNewGenericsTest.php rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/{AddGenericReturnTypeToRelationsRectorTest.php => AddGenericReturnTypeToRelationsRectorOldGenericsTest.php} (76%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/empty-phpdoc.php.inc create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/has-one-through.php.inc create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/no-phpdoc.php.inc rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => NewGenerics}/phpdoc-with-existing-return.php.inc (89%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-with-supported-return.php.inc create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-without-return.php.inc create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-in-trait.php.inc rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => NewGenerics}/relation-with-child-generics.php.inc (81%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-child-generic.php.inc rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => NewGenerics}/relation-with-existing-morph-to-child-generic.php.inc (89%) rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => NewGenerics}/relation-without-return-type.php.inc (76%) rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => OldGenerics}/empty-phpdoc.php.inc (86%) rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => OldGenerics}/no-phpdoc.php.inc (86%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-existing-return.php.inc rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => OldGenerics}/phpdoc-with-supported-return.php.inc (86%) rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => OldGenerics}/phpdoc-without-return.php.inc (87%) rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => OldGenerics}/relation-in-trait.php.inc (87%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-child-generics.php.inc rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/{ => OldGenerics}/relation-with-existing-child-generic.php.inc (71%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-morph-to-child-generic.php.inc create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-without-return-type.php.inc rename tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/{configured_rule.php => use_new_generics_configured_rule.php} (67%) create mode 100644 tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index f8efff26..dbb640ac 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -63,6 +63,8 @@ Adds the `@extends` annotation to Factories. Add generic return type to relations in child of `Illuminate\Database\Eloquent\Model` +:wrench: **configure it!** + - class: [`RectorLaravel\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector`](../src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php) ```diff @@ -82,6 +84,23 @@ Add generic return type to relations in child of `Illuminate\Database\Eloquent\M
+```diff + use App\Account; + use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\Relations\HasMany; + + class User extends Model + { ++ /** @return HasMany */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } + } +``` + +
+ ## AddGuardToLoginEventRector Add new `$guard` argument to Illuminate\Auth\Events\Login diff --git a/src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php b/src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php index d88ac995..5f4f10c5 100644 --- a/src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php +++ b/src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php @@ -12,23 +12,30 @@ use PHPStan\Analyser\Scope; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\Reflection\ClassReflection; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\ObjectType; +use PHPStan\Type\ThisType; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\ValueObject\Type\FullyQualifiedIdentifierTypeNode; use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\Rector\AbstractScopeAwareRector; use Rector\StaticTypeMapper\StaticTypeMapper; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; -/** @see \RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\AddGenericReturnTypeToRelationsRectorTest */ -class AddGenericReturnTypeToRelationsRector extends AbstractScopeAwareRector +/** + * @see \RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\AddGenericReturnTypeToRelationsRectorNewGenericsTest + * @see \RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\AddGenericReturnTypeToRelationsRectorOldGenericsTest + */ +class AddGenericReturnTypeToRelationsRector extends AbstractScopeAwareRector implements ConfigurableRectorInterface { // Relation methods which are supported by this Rector. private const RELATION_METHODS = [ @@ -41,6 +48,11 @@ class AddGenericReturnTypeToRelationsRector extends AbstractScopeAwareRector // Relation methods which need the class as TChildModel. private const RELATION_WITH_CHILD_METHODS = ['belongsTo', 'morphTo']; + // Relation methods which need the class as TIntermediateModel. + private const RELATION_WITH_INTERMEDIATE_METHODS = ['hasManyThrough', 'hasOneThrough']; + + private bool $shouldUseNewGenerics = false; + public function __construct( private readonly TypeComparator $typeComparator, private readonly DocBlockUpdater $docBlockUpdater, @@ -55,7 +67,7 @@ public function getRuleDefinition(): RuleDefinition return new RuleDefinition( 'Add generic return type to relations in child of Illuminate\Database\Eloquent\Model', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' use App\Account; use Illuminate\Database\Eloquent\Model; @@ -84,8 +96,39 @@ public function accounts(): HasMany return $this->hasMany(Account::class); } } +CODE_SAMPLE, + ['shouldUseNewGenerics' => false]), + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +use App\Account; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; + +class User extends Model +{ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} CODE_SAMPLE - ), + + , + <<<'CODE_SAMPLE' +use App\Account; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; + +class User extends Model +{ + /** @return HasMany */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} +CODE_SAMPLE, + ['shouldUseNewGenerics' => true]), ] ); } @@ -154,6 +197,7 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node } $classForChildGeneric = $this->getClassForChildGeneric($scope, $relationMethodCall); + $classForIntermediateGeneric = $this->getClassForIntermediateGeneric($relationMethodCall); // Don't update the docblock if return type already contains the correct generics. This avoids overwriting // non-FQCN with our fully qualified ones. @@ -163,7 +207,8 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node $node, $phpDocInfo->getReturnTagValue(), $relatedClass, - $classForChildGeneric + $classForChildGeneric, + $classForIntermediateGeneric ) ) { return null; @@ -171,7 +216,7 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node $genericTypeNode = new GenericTypeNode( new FullyQualifiedIdentifierTypeNode($methodReturnTypeName), - $this->getGenericTypes($relatedClass, $classForChildGeneric), + $this->getGenericTypes($relatedClass, $classForChildGeneric, $classForIntermediateGeneric), ); // Update or add return tag @@ -187,6 +232,18 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node return $node; } + /** + * {@inheritDoc} + */ + public function configure(array $configuration): void + { + Assert::count($configuration, 1); + Assert::keyExists($configuration, 'shouldUseNewGenerics'); + Assert::boolean($configuration['shouldUseNewGenerics']); + + $this->shouldUseNewGenerics = $configuration['shouldUseNewGenerics']; + } + private function getRelatedModelClassFromMethodCall(MethodCall $methodCall): ?string { $argType = $this->getType($methodCall->getArgs()[0]->value); @@ -243,6 +300,10 @@ private function getRelationMethodCall(ClassMethod $classMethod): ?MethodCall */ private function getClassForChildGeneric(Scope $scope, MethodCall $methodCall): ?string { + if ($this->shouldUseNewGenerics) { + return null; + } + if (! $this->doesMethodHasName($methodCall, self::RELATION_WITH_CHILD_METHODS)) { return null; } @@ -252,6 +313,45 @@ private function getClassForChildGeneric(Scope $scope, MethodCall $methodCall): return $classReflection?->getName(); } + /** + * We need the intermediate class for generics which need a TIntermediateModel. + * This is the case for *through relations + */ + private function getClassForIntermediateGeneric(MethodCall $methodCall): ?string + { + if (! $this->shouldUseNewGenerics) { + return null; + } + + if (! $this->doesMethodHasName($methodCall, self::RELATION_WITH_INTERMEDIATE_METHODS)) { + return null; + } + + $args = $methodCall->getArgs(); + + if (count($args) < 2) { + return null; + } + + $argType = $this->getType($args[1]->value); + + if ($argType instanceof ConstantStringType && $argType->isClassStringType()->yes()) { + return $argType->getValue(); + } + + if (! $argType instanceof GenericClassStringType) { + return null; + } + + $modelType = $argType->getGenericType(); + + if (! $modelType instanceof ObjectType) { + return null; + } + + return $modelType->getClassName(); + } + private function areNativeTypeAndPhpDocReturnTypeEqual( ClassMethod $classMethod, Node $node, @@ -279,7 +379,8 @@ private function areGenericTypesEqual( Node $node, ReturnTagValueNode $returnTagValueNode, string $relatedClass, - ?string $classForChildGeneric + ?string $classForChildGeneric, + ?string $classForIntermediateGeneric ): bool { $phpDocPHPStanType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType( $returnTagValueNode->type, @@ -299,16 +400,37 @@ private function areGenericTypesEqual( return false; } - $phpDocHasChildGeneric = count($phpDocTypes) === 2; - if ($classForChildGeneric === null && ! $phpDocHasChildGeneric) { - return true; + if (! $this->shouldUseNewGenerics) { + $phpDocHasChildGeneric = count($phpDocTypes) === 2; + + if ($classForChildGeneric === null && ! $phpDocHasChildGeneric) { + return true; + } + + if ($classForChildGeneric === null || ! $phpDocHasChildGeneric) { + return false; + } + + return $this->typeComparator->areTypesEqual($phpDocTypes[1], new ObjectType($classForChildGeneric)); } - if ($classForChildGeneric === null || ! $phpDocHasChildGeneric) { + $phpDocHasIntermediateGeneric = count($phpDocTypes) === 3; + + if ($classForIntermediateGeneric === null && ! $phpDocHasIntermediateGeneric) { + // If there is only one generic, it means method is using the old format. We should update it. + if (count($phpDocTypes) === 1) { + return false; + } + + // We want to convert the existing relationship definition to use `$this` as the second generic + return $phpDocTypes[1] instanceof ThisType; + } + + if ($classForIntermediateGeneric === null || ! $phpDocHasIntermediateGeneric) { return false; } - return $this->typeComparator->areTypesEqual($phpDocTypes[1], new ObjectType($classForChildGeneric)); + return $this->typeComparator->areTypesEqual($phpDocTypes[1], new ObjectType($classForIntermediateGeneric)); } private function shouldSkipNode(ClassMethod $classMethod, Scope $scope): bool @@ -341,16 +463,24 @@ private function doesMethodHasName(MethodCall $methodCall, array $methodNames): } /** - * @return FullyQualifiedIdentifierTypeNode[] + * @return IdentifierTypeNode[] */ - private function getGenericTypes(string $relatedClass, ?string $childClass): array + private function getGenericTypes(string $relatedClass, ?string $childClass, ?string $intermediateClass): array { $generics = [new FullyQualifiedIdentifierTypeNode($relatedClass)]; - if ($childClass !== null) { + if (! $this->shouldUseNewGenerics && $childClass !== null) { $generics[] = new FullyQualifiedIdentifierTypeNode($childClass); } + if ($this->shouldUseNewGenerics) { + if ($intermediateClass !== null) { + $generics[] = new FullyQualifiedIdentifierTypeNode($intermediateClass); + } + + $generics[] = new IdentifierTypeNode('$this'); + } + return $generics; } } diff --git a/stubs/Illuminate/Database/Eloquent/Relations/HasOneThrough.php b/stubs/Illuminate/Database/Eloquent/Relations/HasOneThrough.php new file mode 100644 index 00000000..cd1a893b --- /dev/null +++ b/stubs/Illuminate/Database/Eloquent/Relations/HasOneThrough.php @@ -0,0 +1,11 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/use_new_generics_configured_rule.php'; + } +} diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorTest.php b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorOldGenericsTest.php similarity index 76% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorTest.php rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorOldGenericsTest.php index 3876b6a2..1ac1713a 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorTest.php +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/AddGenericReturnTypeToRelationsRectorOldGenericsTest.php @@ -8,11 +8,11 @@ use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class AddGenericReturnTypeToRelationsRectorTest extends AbstractRectorTestCase +final class AddGenericReturnTypeToRelationsRectorOldGenericsTest extends AbstractRectorTestCase { public static function provideData(): Iterator { - return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture/OldGenerics'); } /** @@ -26,6 +26,6 @@ public function test(string $filePath): void public function provideConfigFilePath(): string { - return __DIR__ . '/config/configured_rule.php'; + return __DIR__ . '/config/use_old_generics_configured_rule.php'; } } diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/empty-phpdoc.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/empty-phpdoc.php.inc new file mode 100644 index 00000000..2089947b --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/empty-phpdoc.php.inc @@ -0,0 +1,42 @@ +hasMany(Account::class); + } +} + +?> +----- + + */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/has-one-through.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/has-one-through.php.inc new file mode 100644 index 00000000..282e7d73 --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/has-one-through.php.inc @@ -0,0 +1,58 @@ +hasOneThrough( + Owner::class, + Car::class, + 'mechanic_id', // Foreign key on the cars table... + 'car_id', // Foreign key on the owners table... + 'id', // Local key on the mechanics table... + 'id' // Local key on the cars table... + ); + } +} + +?> +----- + + */ + public function carOwner(): HasOneThrough + { + return $this->hasOneThrough( + Owner::class, + Car::class, + 'mechanic_id', // Foreign key on the cars table... + 'car_id', // Foreign key on the owners table... + 'id', // Local key on the mechanics table... + 'id' // Local key on the cars table... + ); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/no-phpdoc.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/no-phpdoc.php.inc new file mode 100644 index 00000000..461c6d39 --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/no-phpdoc.php.inc @@ -0,0 +1,40 @@ +hasMany(Account::class); + } +} + +?> +----- + + */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-with-existing-return.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-with-existing-return.php.inc similarity index 89% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-with-existing-return.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-with-existing-return.php.inc index 9e5112c4..b3bd39da 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-with-existing-return.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-with-existing-return.php.inc @@ -1,6 +1,6 @@ hasMany(Account::class); + } +} + +?> +----- + + */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-without-return.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-without-return.php.inc new file mode 100644 index 00000000..990671aa --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/phpdoc-without-return.php.inc @@ -0,0 +1,44 @@ +hasMany(Account::class); + } +} + +?> +----- + + */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-in-trait.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-in-trait.php.inc new file mode 100644 index 00000000..f306036a --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-in-trait.php.inc @@ -0,0 +1,50 @@ +hasMany(Account::class); + } +} + +class User extends Model +{ + use AccountTrait; +} + +?> +----- + + */ + public function accounts(): HasMany + { + return $this->hasMany(Account::class); + } +} + +class User extends Model +{ + use AccountTrait; +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-child-generics.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-child-generics.php.inc similarity index 81% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-child-generics.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-child-generics.php.inc index 3069de72..456e94b3 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-child-generics.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-child-generics.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\NewGenerics\Account, $this> */ public function accounts(): BelongsTo { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-child-generic.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-child-generic.php.inc new file mode 100644 index 00000000..aef0502f --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-child-generic.php.inc @@ -0,0 +1,63 @@ + + */ + public function account(): BelongsTo + { + return $this->belongsTo(Account::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\NewGenerics\Post> + */ + public function posts(): HasMany + { + return $this->hasMany(Post::class); + } +} +?> +----- + + */ + public function account(): BelongsTo + { + return $this->belongsTo(Account::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\NewGenerics\Post, $this> + */ + public function posts(): HasMany + { + return $this->hasMany(Post::class); + } +} +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-existing-morph-to-child-generic.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-morph-to-child-generic.php.inc similarity index 89% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-existing-morph-to-child-generic.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-morph-to-child-generic.php.inc index 01066874..fa86aeb3 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-existing-morph-to-child-generic.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/NewGenerics/relation-with-existing-morph-to-child-generic.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\Account> */ public function accounts(): HasMany { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/no-phpdoc.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/no-phpdoc.php.inc similarity index 86% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/no-phpdoc.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/no-phpdoc.php.inc index ffa906d4..d0571602 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/no-phpdoc.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/no-phpdoc.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\Account> */ public function accounts(): HasMany { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-existing-return.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-existing-return.php.inc new file mode 100644 index 00000000..c263f2c3 --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-existing-return.php.inc @@ -0,0 +1,43 @@ +hasMany(Account::class); + } +} + +?> +----- +hasMany(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-with-supported-return.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-supported-return.php.inc similarity index 86% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-with-supported-return.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-supported-return.php.inc index 0d3294f5..5f277e7b 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-with-supported-return.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-with-supported-return.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\Account> */ public function accounts(): HasMany { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-without-return.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-without-return.php.inc similarity index 87% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-without-return.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-without-return.php.inc index 8a145a57..dfbb889e 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/phpdoc-without-return.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/phpdoc-without-return.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\Account> */ public function accounts(): HasMany { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-in-trait.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-in-trait.php.inc similarity index 87% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-in-trait.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-in-trait.php.inc index 5e5e0ffa..6aefd2b8 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-in-trait.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-in-trait.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\Account> */ public function accounts(): HasMany { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-child-generics.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-child-generics.php.inc new file mode 100644 index 00000000..53ac3458 --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-child-generics.php.inc @@ -0,0 +1,40 @@ +belongsTo(Account::class); + } +} + +?> +----- + + */ + public function accounts(): BelongsTo + { + return $this->belongsTo(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-existing-child-generic.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-child-generic.php.inc similarity index 71% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-existing-child-generic.php.inc rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-child-generic.php.inc index 778daa8e..5f09c5de 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/relation-with-existing-child-generic.php.inc +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-child-generic.php.inc @@ -1,6 +1,6 @@ + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\Account, \RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Fixture\OldGenerics\User> */ public function accounts(): BelongsTo { diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-morph-to-child-generic.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-morph-to-child-generic.php.inc new file mode 100644 index 00000000..638573f7 --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-with-existing-morph-to-child-generic.php.inc @@ -0,0 +1,17 @@ + + */ + public function translatable(): MorphTo + { + return $this->morphTo('translatable', 'model', 'value'); + } +} diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-without-return-type.php.inc b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-without-return-type.php.inc new file mode 100644 index 00000000..5d6f5f9e --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/Fixture/OldGenerics/relation-without-return-type.php.inc @@ -0,0 +1,35 @@ +hasMany(Account::class); + } +} + +?> +----- +hasMany(Account::class); + } +} + +?> diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/configured_rule.php b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_new_generics_configured_rule.php similarity index 67% rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/configured_rule.php rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_new_generics_configured_rule.php index aacc7549..5fa617b1 100644 --- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/configured_rule.php +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_new_generics_configured_rule.php @@ -8,5 +8,7 @@ return static function (RectorConfig $rectorConfig): void { $rectorConfig->import(__DIR__ . '/../../../../../config/config.php'); - $rectorConfig->rule(AddGenericReturnTypeToRelationsRector::class); + $rectorConfig->ruleWithConfiguration(AddGenericReturnTypeToRelationsRector::class, [ + 'shouldUseNewGenerics' => true, + ]); }; diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php new file mode 100644 index 00000000..07456db9 --- /dev/null +++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php @@ -0,0 +1,14 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->ruleWithConfiguration(AddGenericReturnTypeToRelationsRector::class, [ + 'shouldUseNewGenerics' => false, + ]); +};