diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md
index 80c3b863..40b6a11c 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..694fba94 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,24 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node
return $node;
}
+ /**
+ * {@inheritDoc}
+ */
+ public function configure(array $configuration): void
+ {
+ if ($configuration === []) {
+ $this->shouldUseNewGenerics = false;
+
+ return;
+ }
+
+ 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 +306,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 +319,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 +385,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 +406,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));
+ }
+
+ $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 ($classForChildGeneric === null || ! $phpDocHasChildGeneric) {
+ 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 +469,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/use_new_generics_configured_rule.php b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_new_generics_configured_rule.php
new file mode 100644
index 00000000..5fa617b1
--- /dev/null
+++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_new_generics_configured_rule.php
@@ -0,0 +1,14 @@
+import(__DIR__ . '/../../../../../config/config.php');
+
+ $rectorConfig->ruleWithConfiguration(AddGenericReturnTypeToRelationsRector::class, [
+ 'shouldUseNewGenerics' => true,
+ ]);
+};
diff --git a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/configured_rule.php b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php
similarity index 75%
rename from tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/configured_rule.php
rename to tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php
index aacc7549..b26b46cf 100644
--- a/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/configured_rule.php
+++ b/tests/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector/config/use_old_generics_configured_rule.php
@@ -8,5 +8,5 @@
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');
- $rectorConfig->rule(AddGenericReturnTypeToRelationsRector::class);
+ $rectorConfig->ruleWithConfiguration(AddGenericReturnTypeToRelationsRector::class, []);
};