From 4fe757694ae56147b085cdef3e606ef911fd9d23 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Thu, 22 Feb 2024 23:11:05 +0000 Subject: [PATCH 01/21] deprecate the old type adding rules --- .../EloquentWhereRelationTypeHintingParameterRector.php | 1 + .../MethodCall/EloquentWhereTypeHintClosureParameterRector.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Rector/MethodCall/EloquentWhereRelationTypeHintingParameterRector.php b/src/Rector/MethodCall/EloquentWhereRelationTypeHintingParameterRector.php index 3b863b06..b40c90e3 100644 --- a/src/Rector/MethodCall/EloquentWhereRelationTypeHintingParameterRector.php +++ b/src/Rector/MethodCall/EloquentWhereRelationTypeHintingParameterRector.php @@ -17,6 +17,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** + * @deprecated use type-declaration set instead * @see \RectorLaravel\Tests\Rector\MethodCall\EloquentWhereRelationTypeHintingParameterRector\EloquentWhereRelationTypeHintingParameterRectorTest */ class EloquentWhereRelationTypeHintingParameterRector extends AbstractRector diff --git a/src/Rector/MethodCall/EloquentWhereTypeHintClosureParameterRector.php b/src/Rector/MethodCall/EloquentWhereTypeHintClosureParameterRector.php index 966cf5dd..c8529680 100644 --- a/src/Rector/MethodCall/EloquentWhereTypeHintClosureParameterRector.php +++ b/src/Rector/MethodCall/EloquentWhereTypeHintClosureParameterRector.php @@ -17,6 +17,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** + * @deprecated use type-declaration set instead * @see \RectorLaravel\Tests\Rector\MethodCall\EloquentWhereTypeHintClosureParameterRector\EloquentWhereTypeHintClosureParameterRectorTest */ class EloquentWhereTypeHintClosureParameterRector extends AbstractRector From 06664677d526a56fdc48788a6a7f8d88bddfab23 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Thu, 22 Feb 2024 23:12:50 +0000 Subject: [PATCH 02/21] Add automated type declarations configs --- config/sets/type-declaration.php | 10 +++ config/sets/type-declaration/eloquent.php | 67 ++++++++++++++++ .../type-declaration/service-container.php | 53 +++++++++++++ .../EloquentTypeDeclarationSetTest.php | 31 ++++++++ .../Fixture/apply-when-to-builder.php.inc | 39 ++++++++++ .../apply-where-relation-to-builder.php.inc | 77 +++++++++++++++++++ .../apply-where-relation-to-model.php.inc | 29 +++++++ .../Fixture/apply-where-to-builder.php.inc | 73 ++++++++++++++++++ .../Fixture/apply-where-to-model.php.inc | 31 ++++++++ .../Eloquent/config/configured_rule.php | 9 +++ .../Fixture/apply-bind-to-application.php.inc | 75 ++++++++++++++++++ ...ServiceContainerTypeDeclarationSetTest.php | 31 ++++++++ .../config/configured_rule.php | 9 +++ 13 files changed, 534 insertions(+) create mode 100644 config/sets/type-declaration.php create mode 100644 config/sets/type-declaration/eloquent.php create mode 100644 config/sets/type-declaration/service-container.php create mode 100644 tests/Sets/TypeDeclaration/Eloquent/EloquentTypeDeclarationSetTest.php create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-when-to-builder.php.inc create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-builder.php.inc create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-model.php.inc create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-builder.php.inc create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-model.php.inc create mode 100644 tests/Sets/TypeDeclaration/Eloquent/config/configured_rule.php create mode 100644 tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc create mode 100644 tests/Sets/TypeDeclaration/ServiceContainer/ServiceContainerTypeDeclarationSetTest.php create mode 100644 tests/Sets/TypeDeclaration/ServiceContainer/config/configured_rule.php diff --git a/config/sets/type-declaration.php b/config/sets/type-declaration.php new file mode 100644 index 00000000..103d8e76 --- /dev/null +++ b/config/sets/type-declaration.php @@ -0,0 +1,10 @@ +import(__DIR__ . '/../config.php'); + + $rectorConfig->import(__DIR__ . '/type-declaration/eloquent.php'); + $rectorConfig->import(__DIR__ . '/type-declaration/service-container.php'); +}; diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php new file mode 100644 index 00000000..b42261b5 --- /dev/null +++ b/config/sets/type-declaration/eloquent.php @@ -0,0 +1,67 @@ +import(__DIR__ . '/../../config.php'); + + $builderClass = new \PHPStan\Type\ObjectType( + 'Illuminate\Contracts\Database\Query\Builder' + ); + + $classesToApplyTo = [ + 'Illuminate\Database\Eloquent\Model', + 'Illuminate\Contracts\Database\Query\Builder', + 'Illuminate\Contracts\Database\Eloquent\Builder', + ]; + + $positionOne = [ + 'where', 'orWhere', 'whereNot', 'whereExists', + ]; + $positionTwo = [ + 'where', 'whereHas', 'orWhereHas', 'whereDoesntHave', 'orWhereDoesntHave', 'withWhereHas', 'when', + ]; + $positionThree = [ + 'where', 'whereHasMorph', 'orWhereHasMorph', 'whereDoesntHaveMorph', 'orWhereDoesntHaveMorph', 'when', + ]; + + $ruleConfiguration = []; + + foreach ($classesToApplyTo as $targetClass) { + foreach ($positionOne as $method) { + $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + $method, + 0, + 0, + $builderClass, + ); + } + foreach ($positionTwo as $method) { + $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + $method, + 1, + 0, + $builderClass, + ); + } + foreach ($positionThree as $method) { + $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + $method, + 2, + 0, + $builderClass, + ); + } + } + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + $ruleConfiguration + ); + +}; diff --git a/config/sets/type-declaration/service-container.php b/config/sets/type-declaration/service-container.php new file mode 100644 index 00000000..21c670e5 --- /dev/null +++ b/config/sets/type-declaration/service-container.php @@ -0,0 +1,53 @@ +import(__DIR__ . '/../../config.php'); + + $applicationClass = new \PHPStan\Type\ObjectType( + 'Illuminate\Contracts\Foundation\Application' + ); + + $classesToApplyTo = [ + 'Illuminate\Support\Facades\App', + 'Illuminate\Contracts\Foundation\Application', + ]; + + $ruleConfiguration = []; + + foreach ($classesToApplyTo as $targetClass) { + foreach ([ + 'bind', 'bindIf', 'singleton', 'singletonIf', 'scoped', 'scopedIf', + ] as $method) { + $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + $method, + 1, + 0, + $applicationClass, + ); + } + $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + 'resolving', + 1, + 1, + $applicationClass, + ); + $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + 'extends', + 1, + 1, + $applicationClass, + ); + } + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + $ruleConfiguration + ); +}; diff --git a/tests/Sets/TypeDeclaration/Eloquent/EloquentTypeDeclarationSetTest.php b/tests/Sets/TypeDeclaration/Eloquent/EloquentTypeDeclarationSetTest.php new file mode 100644 index 00000000..a2311cb3 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/EloquentTypeDeclarationSetTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-when-to-builder.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-when-to-builder.php.inc new file mode 100644 index 00000000..2bfe9bc2 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-when-to-builder.php.inc @@ -0,0 +1,39 @@ +when(true, function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->when(true, function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}, function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +?> +----- +when(true, function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->when(true, function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}, function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-builder.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-builder.php.inc new file mode 100644 index 00000000..261823c8 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-builder.php.inc @@ -0,0 +1,77 @@ +whereHas('posts', function ($query) { + $query->where('is_published', true); +}); + +$query->orWhereHas('posts', function ($query) { + $query->where('is_published', true); +}); + +$query->whereDoesntHave('posts', function ($query) { + $query->where('is_published', true); +}); + +$query->orWhereDoesntHave('posts', function ($query) { + $query->where('is_published', true); +}); + +$query->whereHasMorph('posts', [], function ($query) { + $query->where('is_published', true); +}); + +$query->orWhereHasMorph('posts', [], function ($query) { + $query->where('is_published', true); +}); + +$query->whereDoesntHaveMorph('posts', [], function ($query) { + $query->where('is_published', true); +}); + +$query->orWhereDoesntHaveMorph('posts', [], function ($query) { + $query->where('is_published', true); +}); + +?> +----- +whereHas('posts', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->orWhereHas('posts', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->whereDoesntHave('posts', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->orWhereDoesntHave('posts', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->whereHasMorph('posts', [], function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->orWhereHasMorph('posts', [], function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->whereDoesntHaveMorph('posts', [], function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +$query->orWhereDoesntHaveMorph('posts', [], function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('is_published', true); +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-model.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-model.php.inc new file mode 100644 index 00000000..d71fc329 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-relation-to-model.php.inc @@ -0,0 +1,29 @@ +where('is_published', true); +}); + +?> +----- +where('is_published', true); +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-builder.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-builder.php.inc new file mode 100644 index 00000000..71858528 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-builder.php.inc @@ -0,0 +1,73 @@ +where(function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->where('column', function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->where('column', '>', function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->orWhere(function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->whereNot(function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->whereExists(function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +?> +----- +where(function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->where('column', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->where('column', '>', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->orWhere(function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->whereNot(function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +$query->whereExists(function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-model.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-model.php.inc new file mode 100644 index 00000000..56aee6ff --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-where-to-model.php.inc @@ -0,0 +1,31 @@ +where('id', 1) + ->orWhere('id', 2); +}); + +?> +----- +where('id', 1) + ->orWhere('id', 2); +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Eloquent/config/configured_rule.php b/tests/Sets/TypeDeclaration/Eloquent/config/configured_rule.php new file mode 100644 index 00000000..0b9d5c82 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/config/configured_rule.php @@ -0,0 +1,9 @@ +import(__DIR__ . '/../../../../../config/sets/type-declaration/eloquent.php'); +}; diff --git a/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc b/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc new file mode 100644 index 00000000..85561350 --- /dev/null +++ b/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc @@ -0,0 +1,75 @@ + +----- + diff --git a/tests/Sets/TypeDeclaration/ServiceContainer/ServiceContainerTypeDeclarationSetTest.php b/tests/Sets/TypeDeclaration/ServiceContainer/ServiceContainerTypeDeclarationSetTest.php new file mode 100644 index 00000000..86dd344e --- /dev/null +++ b/tests/Sets/TypeDeclaration/ServiceContainer/ServiceContainerTypeDeclarationSetTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Sets/TypeDeclaration/ServiceContainer/config/configured_rule.php b/tests/Sets/TypeDeclaration/ServiceContainer/config/configured_rule.php new file mode 100644 index 00000000..19bc7065 --- /dev/null +++ b/tests/Sets/TypeDeclaration/ServiceContainer/config/configured_rule.php @@ -0,0 +1,9 @@ +import(__DIR__ . '/../../../../../config/sets/type-declaration/service-container.php'); +}; From 9bc9ec7c4db966a0fd9c29ee62571bd323882872 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Tue, 27 Feb 2024 00:24:27 +0000 Subject: [PATCH 03/21] More type hinting goodness --- config/sets/type-declaration/eloquent.php | 62 +++++- ...allLikeArgArrayValuesDeclarationRector.php | 201 ++++++++++++++++++ ...ikeArgArrayValuesDeclarationRectorTest.php | 31 +++ .../Fixture/fixture.php.inc | 23 ++ .../config/configured_rule.php | 23 ++ ...ly-handleLazyLoadingViolationUsing.php.inc | 23 ++ .../Fixture/apply-with-to-model.php.inc | 41 ++++ 7 files changed, 393 insertions(+), 11 deletions(-) create mode 100644 src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php create mode 100644 tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRectorTest.php create mode 100644 tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/fixture.php.inc create mode 100644 tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-handleLazyLoadingViolationUsing.php.inc create mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index b42261b5..04ca927f 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -3,6 +3,7 @@ use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\FunctionLike\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector; use Rector\TypeDeclaration\ValueObject\AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration; +use RectorLaravel\Rector\Param\AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->import(__DIR__ . '/../../config.php'); @@ -17,21 +18,26 @@ 'Illuminate\Contracts\Database\Eloquent\Builder', ]; - $positionOne = [ + $basicPositionOne = [ 'where', 'orWhere', 'whereNot', 'whereExists', ]; - $positionTwo = [ + $basicPositionTwo = [ 'where', 'whereHas', 'orWhereHas', 'whereDoesntHave', 'orWhereDoesntHave', 'withWhereHas', 'when', ]; - $positionThree = [ + $basicPositionThree = [ 'where', 'whereHasMorph', 'orWhereHasMorph', 'whereDoesntHaveMorph', 'orWhereDoesntHaveMorph', 'when', ]; - $ruleConfiguration = []; + $arrayPositionOne = [ + 'with', 'withCount', + ]; + + $basicRuleConfiguration = []; + $arrayRuleConfiguration = []; foreach ($classesToApplyTo as $targetClass) { - foreach ($positionOne as $method) { - $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + foreach ($basicPositionOne as $method) { + $basicRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( $targetClass, $method, 0, @@ -39,8 +45,8 @@ $builderClass, ); } - foreach ($positionTwo as $method) { - $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + foreach ($basicPositionTwo as $method) { + $basicRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( $targetClass, $method, 1, @@ -48,8 +54,8 @@ $builderClass, ); } - foreach ($positionThree as $method) { - $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + foreach ($basicPositionThree as $method) { + $basicRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( $targetClass, $method, 2, @@ -57,11 +63,45 @@ $builderClass, ); } + + foreach ($arrayPositionOne as $method) { + $arrayRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + $method, + 0, + 0, + $builderClass, + ); + } } $rectorConfig->ruleWithConfiguration( AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, - $ruleConfiguration + $basicRuleConfiguration ); + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector::class, + $arrayRuleConfiguration, + ); + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Database\Eloquent\Model', + 'handleLazyLoadingViolationUsing', + 0, + 0, + new \PHPStan\Type\ObjectType('Illuminate\Database\Eloquent\Model') + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Database\Eloquent\Model', + 'handleLazyLoadingViolationUsing', + 0, + 1, + new \PHPStan\Type\StringType(), + ) + ] + ); }; diff --git a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php new file mode 100644 index 00000000..fbb35272 --- /dev/null +++ b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php @@ -0,0 +1,201 @@ +typeComparator = $typeComparator; + $this->phpVersionProvider = $phpVersionProvider; + $this->staticTypeMapper = $staticTypeMapper; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Add param type for function like within call like arg array values', [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +new \SomeNamespace\SomeClass::method(['value' => function ($value) { + return $value; +}]); +CODE_SAMPLE, + <<<'CODE_SAMPLE' +new \SomeNamespace\SomeClass::method(['value' => function (int $value) { + return $value; +}]); +CODE_SAMPLE, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'SomeNamespace\SomeClass', + 'method', + 0, + 0, + new IntegerType(), + ) + ] + ), + ]); + } + + public function getNodeTypes(): array + { + return [Node\Expr\MethodCall::class, Node\Expr\StaticCall::class]; + } + + public function refactor(Node $node): ?Node + { + $this->hasChanged = \false; + foreach ($this->addParamTypeForFunctionLikeParamDeclarations as $addParamTypeForFunctionLikeParamDeclaration) { + $type = match (true) { + $node instanceof MethodCall => $node->var, + $node instanceof StaticCall => $node->class, + default => null, + }; + if ($type === null) { + continue; + } + if (!$this->isObjectType($type, $addParamTypeForFunctionLikeParamDeclaration->getObjectType())) { + continue; + } + if (($node->name ?? null) === null) { + continue; + } + if (!$node->name instanceof Identifier) { + continue; + } + if (!$this->isName($node->name, $addParamTypeForFunctionLikeParamDeclaration->getMethodName())) { + continue; + } + $this->processFunctionLike($node, $addParamTypeForFunctionLikeParamDeclaration); + } + if (!$this->hasChanged) { + return null; + } + return $node; + } + + private function processFunctionLike(CallLike $callLike, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration) : void + { + if ($callLike->isFirstClassCallable()) { + return; + } + if (\is_int($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition())) { + if ($callLike->getArgs() === []) { + return; + } + $arg = $callLike->args[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition()] ?? null; + if (!$arg instanceof Arg) { + return; + } + // int positions shouldn't have names + if ($arg->name !== null) { + return; + } + } else { + $args = \array_filter($callLike->getArgs(), static function (Arg $arg) use ($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration): bool { + if ($arg->name === null) { + return \false; + } + return $arg->name->name === $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition(); + }); + if ($args === []) { + return; + } + $arg = \array_values($args)[0]; + } + $array = $arg->value; + if (!$array instanceof Node\Expr\Array_) { + return; + } + foreach ($array->items as $item) { + if ($item === null) { + continue; + } + if ($item->value === null) { + continue; + } + if (!$item->value instanceof FunctionLike) { + continue; + } + $functionLike = $item->value; + if (!isset($functionLike->params[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getFunctionLikePosition()])) { + return; + } + $this->refactorParameter($functionLike->params[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getFunctionLikePosition()], $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration); + } + } + private function refactorParameter(Param $param, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration) : void + { + // already set → no change + if ($param->type !== null) { + $currentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + if ($this->typeComparator->areTypesEqual($currentParamType, $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getParamType())) { + return; + } + } + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getParamType(), TypeKind::PARAM); + $this->hasChanged = \true; + // remove it + if ($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getParamType() instanceof MixedType) { + if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::MIXED_TYPE)) { + $param->type = $paramTypeNode; + return; + } + $param->type = null; + return; + } + $param->type = $paramTypeNode; + } + + public function configure(array $configuration) : void + { + Assert::allIsAOf($configuration, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration::class); + $this->addParamTypeForFunctionLikeParamDeclarations = $configuration; + } +} diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRectorTest.php b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRectorTest.php new file mode 100644 index 00000000..55ff2d72 --- /dev/null +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/fixture.php.inc b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/fixture.php.inc new file mode 100644 index 00000000..37e9c920 --- /dev/null +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/fixture.php.inc @@ -0,0 +1,23 @@ + function ($value) { + return $value; + } +]); + +?> +----- + function (string $value) { + return $value; + } +]); + +?> diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php new file mode 100644 index 00000000..bad3933f --- /dev/null +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php @@ -0,0 +1,23 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector::class, + [ + new \Rector\TypeDeclaration\ValueObject\AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'SomeClass', + 'someMethod', + 0, + 0, + new \PHPStan\Type\StringType(), + ), + ] + ); +}; diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-handleLazyLoadingViolationUsing.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-handleLazyLoadingViolationUsing.php.inc new file mode 100644 index 00000000..cb379b63 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-handleLazyLoadingViolationUsing.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc new file mode 100644 index 00000000..45b56ca5 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc @@ -0,0 +1,41 @@ + function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}]); + +User::withCount(['team' => function ($query) { + $query->where('id', 1) + ->orWhere('id', 2); +}]); + +?> +----- + function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}]); + +User::withCount(['team' => function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('id', 1) + ->orWhere('id', 2); +}]); + +?> From eb552d19c355c33da2c13ccfe775224059704254 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Mon, 4 Mar 2024 00:52:31 +0000 Subject: [PATCH 04/21] Fix import and code styles --- config/sets/type-declaration/eloquent.php | 4 +- ...allLikeArgArrayValuesDeclarationRector.php | 65 ++++++++++++------- .../config/configured_rule.php | 2 +- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index 04ca927f..29043fe5 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -100,8 +100,8 @@ 'handleLazyLoadingViolationUsing', 0, 1, - new \PHPStan\Type\StringType(), - ) + new \PHPStan\Type\StringType, + ), ] ); }; diff --git a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php index fbb35272..8ce69e87 100644 --- a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php +++ b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php @@ -2,6 +2,8 @@ namespace RectorLaravel\Rector\Param; +use const false; + use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\CallLike; @@ -20,24 +22,31 @@ use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclaration\ValueObject\AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration; use Rector\ValueObject\PhpVersionFeature; -use RectorPrefix202402\Webmozart\Assert\Assert; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; + +use function array_filter; +use function array_values; +use function is_int; class AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector extends AbstractRector implements ConfigurableRectorInterface { /** * @readonly + * * @var \Rector\NodeTypeResolver\TypeComparator\TypeComparator */ private $typeComparator; /** * @readonly + * * @var \Rector\Php\PhpVersionProvider */ private $phpVersionProvider; /** * @readonly + * * @var \Rector\StaticTypeMapper\StaticTypeMapper */ private $staticTypeMapper; @@ -48,7 +57,8 @@ class AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector e /** * @var bool */ - private $hasChanged = \false; + private $hasChanged = false; + public function __construct(TypeComparator $typeComparator, PhpVersionProvider $phpVersionProvider, StaticTypeMapper $staticTypeMapper) { $this->typeComparator = $typeComparator; @@ -76,8 +86,8 @@ public function getRuleDefinition(): RuleDefinition 'method', 0, 0, - new IntegerType(), - ) + new IntegerType, + ), ] ), ]); @@ -90,7 +100,7 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { - $this->hasChanged = \false; + $this->hasChanged = false; foreach ($this->addParamTypeForFunctionLikeParamDeclarations as $addParamTypeForFunctionLikeParamDeclaration) { $type = match (true) { $node instanceof MethodCall => $node->var, @@ -100,37 +110,44 @@ public function refactor(Node $node): ?Node if ($type === null) { continue; } - if (!$this->isObjectType($type, $addParamTypeForFunctionLikeParamDeclaration->getObjectType())) { + if (! $this->isObjectType($type, $addParamTypeForFunctionLikeParamDeclaration->getObjectType())) { continue; } if (($node->name ?? null) === null) { continue; } - if (!$node->name instanceof Identifier) { + if (! $node->name instanceof Identifier) { continue; } - if (!$this->isName($node->name, $addParamTypeForFunctionLikeParamDeclaration->getMethodName())) { + if (! $this->isName($node->name, $addParamTypeForFunctionLikeParamDeclaration->getMethodName())) { continue; } $this->processFunctionLike($node, $addParamTypeForFunctionLikeParamDeclaration); } - if (!$this->hasChanged) { + if (! $this->hasChanged) { return null; } + return $node; } - private function processFunctionLike(CallLike $callLike, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration) : void + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration::class); + $this->addParamTypeForFunctionLikeParamDeclarations = $configuration; + } + + private function processFunctionLike(CallLike $callLike, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration): void { if ($callLike->isFirstClassCallable()) { return; } - if (\is_int($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition())) { + if (is_int($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition())) { if ($callLike->getArgs() === []) { return; } $arg = $callLike->args[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition()] ?? null; - if (!$arg instanceof Arg) { + if (! $arg instanceof Arg) { return; } // int positions shouldn't have names @@ -138,19 +155,20 @@ private function processFunctionLike(CallLike $callLike, AddParamTypeForFunction return; } } else { - $args = \array_filter($callLike->getArgs(), static function (Arg $arg) use ($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration): bool { + $args = array_filter($callLike->getArgs(), static function (Arg $arg) use ($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration): bool { if ($arg->name === null) { - return \false; + return false; } + return $arg->name->name === $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getCallLikePosition(); }); if ($args === []) { return; } - $arg = \array_values($args)[0]; + $arg = array_values($args)[0]; } $array = $arg->value; - if (!$array instanceof Node\Expr\Array_) { + if (! $array instanceof Node\Expr\Array_) { return; } foreach ($array->items as $item) { @@ -160,17 +178,18 @@ private function processFunctionLike(CallLike $callLike, AddParamTypeForFunction if ($item->value === null) { continue; } - if (!$item->value instanceof FunctionLike) { + if (! $item->value instanceof FunctionLike) { continue; } $functionLike = $item->value; - if (!isset($functionLike->params[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getFunctionLikePosition()])) { + if (! isset($functionLike->params[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getFunctionLikePosition()])) { return; } $this->refactorParameter($functionLike->params[$addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getFunctionLikePosition()], $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration); } } - private function refactorParameter(Param $param, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration) : void + + private function refactorParameter(Param $param, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration): void { // already set → no change if ($param->type !== null) { @@ -185,17 +204,13 @@ private function refactorParameter(Param $param, AddParamTypeForFunctionLikeWith if ($addParamTypeForFunctionLikeWithinCallLikeArgDeclaration->getParamType() instanceof MixedType) { if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::MIXED_TYPE)) { $param->type = $paramTypeNode; + return; } $param->type = null; + return; } $param->type = $paramTypeNode; } - - public function configure(array $configuration) : void - { - Assert::allIsAOf($configuration, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration::class); - $this->addParamTypeForFunctionLikeParamDeclarations = $configuration; - } } diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php index bad3933f..f009b1de 100644 --- a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php @@ -16,7 +16,7 @@ 'someMethod', 0, 0, - new \PHPStan\Type\StringType(), + new \PHPStan\Type\StringType, ), ] ); From cb41eb37c4b45df246315f50bf37788d93c07694 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Mon, 4 Mar 2024 08:41:52 +0000 Subject: [PATCH 05/21] Rector changes --- ...allLikeArgArrayValuesDeclarationRector.php | 41 ++++++++----------- .../config/configured_rule.php | 6 ++- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php index 8ce69e87..b577d038 100644 --- a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php +++ b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php @@ -2,6 +2,7 @@ namespace RectorLaravel\Rector\Param; +use PhpParser\Node\Expr\Array_; use const false; use PhpParser\Node; @@ -32,24 +33,6 @@ class AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector extends AbstractRector implements ConfigurableRectorInterface { - /** - * @readonly - * - * @var \Rector\NodeTypeResolver\TypeComparator\TypeComparator - */ - private $typeComparator; - /** - * @readonly - * - * @var \Rector\Php\PhpVersionProvider - */ - private $phpVersionProvider; - /** - * @readonly - * - * @var \Rector\StaticTypeMapper\StaticTypeMapper - */ - private $staticTypeMapper; /** * @var AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration[] */ @@ -59,11 +42,21 @@ class AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector e */ private $hasChanged = false; - public function __construct(TypeComparator $typeComparator, PhpVersionProvider $phpVersionProvider, StaticTypeMapper $staticTypeMapper) + public function __construct( + /** + * @readonly + */ + private readonly TypeComparator $typeComparator, + /** + * @readonly + */ + private readonly PhpVersionProvider $phpVersionProvider, + /** + * @readonly + */ + private readonly StaticTypeMapper $staticTypeMapper + ) { - $this->typeComparator = $typeComparator; - $this->phpVersionProvider = $phpVersionProvider; - $this->staticTypeMapper = $staticTypeMapper; } public function getRuleDefinition(): RuleDefinition @@ -95,7 +88,7 @@ public function getRuleDefinition(): RuleDefinition public function getNodeTypes(): array { - return [Node\Expr\MethodCall::class, Node\Expr\StaticCall::class]; + return [MethodCall::class, StaticCall::class]; } public function refactor(Node $node): ?Node @@ -168,7 +161,7 @@ private function processFunctionLike(CallLike $callLike, AddParamTypeForFunction $arg = array_values($args)[0]; } $array = $arg->value; - if (! $array instanceof Node\Expr\Array_) { + if (! $array instanceof Array_) { return; } foreach ($array->items as $item) { diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php index f009b1de..3364b5a1 100644 --- a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php @@ -1,6 +1,8 @@ ruleWithConfiguration( AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector::class, [ - new \Rector\TypeDeclaration\ValueObject\AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( 'SomeClass', 'someMethod', 0, 0, - new \PHPStan\Type\StringType, + new StringType, ), ] ); From ef18c2ac4d765041857a554314d2b6c708167ca9 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Mon, 4 Mar 2024 21:21:47 +0000 Subject: [PATCH 06/21] PHPStan fixes --- ...ithinCallLikeArgArrayValuesDeclarationRector.php | 13 +++++++++---- .../config/configured_rule.php | 3 +-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php index b577d038..b1da5cad 100644 --- a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php +++ b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php @@ -2,11 +2,11 @@ namespace RectorLaravel\Rector\Param; -use PhpParser\Node\Expr\Array_; use const false; use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; @@ -31,6 +31,9 @@ use function array_values; use function is_int; +/** + * @see \RectorLaravel\Tests\Rector\Param\AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector\AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRectorTest + */ class AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector extends AbstractRector implements ConfigurableRectorInterface { /** @@ -55,8 +58,7 @@ public function __construct( * @readonly */ private readonly StaticTypeMapper $staticTypeMapper - ) - { + ) { } public function getRuleDefinition(): RuleDefinition @@ -115,6 +117,9 @@ public function refactor(Node $node): ?Node if (! $this->isName($node->name, $addParamTypeForFunctionLikeParamDeclaration->getMethodName())) { continue; } + if (! $node instanceof CallLike) { + continue; + } $this->processFunctionLike($node, $addParamTypeForFunctionLikeParamDeclaration); } if (! $this->hasChanged) { @@ -126,7 +131,7 @@ public function refactor(Node $node): ?Node public function configure(array $configuration): void { - Assert::allIsAOf($configuration, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration::class); + Assert::allIsInstanceOf($configuration, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration::class); $this->addParamTypeForFunctionLikeParamDeclarations = $configuration; } diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php index 3364b5a1..d80477cf 100644 --- a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/config/configured_rule.php @@ -1,10 +1,9 @@ Date: Mon, 4 Mar 2024 21:31:19 +0000 Subject: [PATCH 07/21] CS fixes --- config/sets/type-declaration/eloquent.php | 5 +++-- config/sets/type-declaration/service-container.php | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index 29043fe5..2bf8d6bf 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -1,5 +1,6 @@ import(__DIR__ . '/../../config.php'); - $builderClass = new \PHPStan\Type\ObjectType( + $builderClass = new ObjectType( 'Illuminate\Contracts\Database\Query\Builder' ); @@ -93,7 +94,7 @@ 'handleLazyLoadingViolationUsing', 0, 0, - new \PHPStan\Type\ObjectType('Illuminate\Database\Eloquent\Model') + new ObjectType('Illuminate\Database\Eloquent\Model') ), new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( 'Illuminate\Database\Eloquent\Model', diff --git a/config/sets/type-declaration/service-container.php b/config/sets/type-declaration/service-container.php index 21c670e5..2153eeb1 100644 --- a/config/sets/type-declaration/service-container.php +++ b/config/sets/type-declaration/service-container.php @@ -1,5 +1,6 @@ import(__DIR__ . '/../../config.php'); - $applicationClass = new \PHPStan\Type\ObjectType( + $applicationClass = new ObjectType( 'Illuminate\Contracts\Foundation\Application' ); From eca32c1f2dc44d476555aa4c9dab9a20d946be97 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Tue, 5 Mar 2024 22:23:28 +0000 Subject: [PATCH 08/21] handle nested arrays --- ...allLikeArgArrayValuesDeclarationRector.php | 8 +++++ .../Fixture/apply_it_to_nested_arrays.php.inc | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/apply_it_to_nested_arrays.php.inc diff --git a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php index b1da5cad..1273b8aa 100644 --- a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php +++ b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php @@ -169,6 +169,11 @@ private function processFunctionLike(CallLike $callLike, AddParamTypeForFunction if (! $array instanceof Array_) { return; } + $this->refactorArray($array, $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration); + } + + private function refactorArray(Array_ $array, AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration): void + { foreach ($array->items as $item) { if ($item === null) { continue; @@ -176,6 +181,9 @@ private function processFunctionLike(CallLike $callLike, AddParamTypeForFunction if ($item->value === null) { continue; } + if ($item->value instanceof Array_) { + $this->refactorArray($item->value, $addParamTypeForFunctionLikeWithinCallLikeArgDeclaration); + } if (! $item->value instanceof FunctionLike) { continue; } diff --git a/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/apply_it_to_nested_arrays.php.inc b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/apply_it_to_nested_arrays.php.inc new file mode 100644 index 00000000..0e12d161 --- /dev/null +++ b/tests/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector/Fixture/apply_it_to_nested_arrays.php.inc @@ -0,0 +1,29 @@ + function ($value) { + return $value; + }], + 'key' => function ($value) { + return $value; + } +]); + +?> +----- + function (string $value) { + return $value; + }], + 'key' => function (string $value) { + return $value; + } +]); + +?> From 0e4f7b2b03a3944712da053649b64c88edee3f31 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Tue, 5 Mar 2024 22:30:41 +0000 Subject: [PATCH 09/21] Sets up validation type --- config/sets/type-declaration/validation.php | 117 ++++++++++++++++++ .../Fixture/apply-to-factory.php.inc | 41 ++++++ .../Validation/Fixture/apply-to-rule.php.inc | 39 ++++++ .../Fixture/apply-to-validator.php.inc | 41 ++++++ .../Validation/ValidationSetTest.php | 31 +++++ .../Validation/config/configured_rule.php | 9 ++ 6 files changed, 278 insertions(+) create mode 100644 config/sets/type-declaration/validation.php create mode 100644 tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-factory.php.inc create mode 100644 tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc create mode 100644 tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc create mode 100644 tests/Sets/TypeDeclaration/Validation/ValidationSetTest.php create mode 100644 tests/Sets/TypeDeclaration/Validation/config/configured_rule.php diff --git a/config/sets/type-declaration/validation.php b/config/sets/type-declaration/validation.php new file mode 100644 index 00000000..0315a81a --- /dev/null +++ b/config/sets/type-declaration/validation.php @@ -0,0 +1,117 @@ +import(__DIR__ . '/../../config.php'); + + $targetClasses = [ + 'Illuminate\Support\Facades\Validator', + 'Illuminate\Contracts\Validation\Factory', + ]; + + foreach ($targetClasses as $targetClass) { + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + 'make', + 1, + 0, + new \PHPStan\Type\StringType(), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + 'make', + 1, + 1, + new \PHPStan\Type\MixedType(true), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + 'make', + 1, + 2, + new \PHPStan\Type\ObjectType('Closure'), + ), + ] + ); + } + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Contracts\Validation\Validator', + 'after', + 0, + 0, + new \PHPStan\Type\ObjectType('Illuminate\Contracts\Validation\Validator'), + ), + ] + ); + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Contracts\Validation\Validator', + 'sometimes', + 2, + 0, + new \PHPStan\Type\ObjectType('Illuminate\Support\Fluent'), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Contracts\Validation\Validator', + 'sometimes', + 2, + 1, + new \PHPStan\Type\ObjectType('Illuminate\Support\Fluent'), + ), + ] + ); + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Validation\Rule', + 'forEach', + 0, + 0, + new \PHPStan\Type\MixedType(true), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Validation\Rule', + 'forEach', + 0, + 1, + new \PHPStan\Type\StringType(), + ), + ] + ); + + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Validation\Rules\Exists', + 'where', + 1, + 0, + new \PHPStan\Type\ObjectType('Illuminate\Contracts\Database\Query\Builder'), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Validation\Rules\Exists', + 'using', + 0, + 0, + new \PHPStan\Type\ObjectType('Illuminate\Contracts\Database\Query\Builder'), + ), + ] + ); +}; diff --git a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-factory.php.inc b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-factory.php.inc new file mode 100644 index 00000000..9fc4b569 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-factory.php.inc @@ -0,0 +1,41 @@ + [function ($attribute, $value, $fail) { + $fail('The '.$attribute.' is invalid.'); + }], +]); + +/** @var Factory $factory */ +$factory->make([], [ + 'key' => [function ($attribute, $value, $fail) { + $fail('The '.$attribute.' is invalid.'); + }], +]); + +?> +----- + [function (string $attribute, mixed $value, \Closure $fail) { + $fail('The '.$attribute.' is invalid.'); + }], +]); + +/** @var Factory $factory */ +$factory->make([], [ + 'key' => [function (string $attribute, mixed $value, \Closure $fail) { + $fail('The '.$attribute.' is invalid.'); + }], +]); + +?> diff --git a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc new file mode 100644 index 00000000..61cbf29b --- /dev/null +++ b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc @@ -0,0 +1,39 @@ +where('column', function ($query) { + $query->where('column', 'value'); +}); + +$exists->using(function ($query) { + $query->where('column', 'value'); +}); + +?> +----- +where('column', function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('column', 'value'); +}); + +$exists->using(function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('column', 'value'); +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc new file mode 100644 index 00000000..e614f29d --- /dev/null +++ b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc @@ -0,0 +1,41 @@ +after(function ($validator) { + if ($this->somethingElseIsInvalid()) { + $validator->errors()->add( + 'field', 'Something is wrong with this field!' + ); + } +}); + +$validator->sometimes('reason', 'required|max:500', function ($input, $item) { + return $input->games >= 100; +}); + +?> +----- +after(function (\Illuminate\Contracts\Validation\Validator $validator) { + if ($this->somethingElseIsInvalid()) { + $validator->errors()->add( + 'field', 'Something is wrong with this field!' + ); + } +}); + +$validator->sometimes('reason', 'required|max:500', function (\Illuminate\Support\Fluent $input, \Illuminate\Support\Fluent $item) { + return $input->games >= 100; +}); + +?> diff --git a/tests/Sets/TypeDeclaration/Validation/ValidationSetTest.php b/tests/Sets/TypeDeclaration/Validation/ValidationSetTest.php new file mode 100644 index 00000000..95543692 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Validation/ValidationSetTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Sets/TypeDeclaration/Validation/config/configured_rule.php b/tests/Sets/TypeDeclaration/Validation/config/configured_rule.php new file mode 100644 index 00000000..a5363be4 --- /dev/null +++ b/tests/Sets/TypeDeclaration/Validation/config/configured_rule.php @@ -0,0 +1,9 @@ +import(__DIR__ . '/../../../../../config/sets/type-declaration/validation.php'); +}; From db397d74096eab150cff3cf28a91f94c0b487ef3 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Tue, 12 Mar 2024 22:47:42 +0000 Subject: [PATCH 10/21] Small fixes found --- config/sets/type-declaration.php | 1 + config/sets/type-declaration/validation.php | 36 ++++++++++++++++++- .../Validation/Fixture/apply-to-rule.php.inc | 10 ++++++ .../Fixture/apply-to-validator.php.inc | 2 +- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/config/sets/type-declaration.php b/config/sets/type-declaration.php index 103d8e76..f8021971 100644 --- a/config/sets/type-declaration.php +++ b/config/sets/type-declaration.php @@ -7,4 +7,5 @@ $rectorConfig->import(__DIR__ . '/type-declaration/eloquent.php'); $rectorConfig->import(__DIR__ . '/type-declaration/service-container.php'); + $rectorConfig->import(__DIR__ . '/type-declaration/validation.php'); }; diff --git a/config/sets/type-declaration/validation.php b/config/sets/type-declaration/validation.php index 0315a81a..04845596 100644 --- a/config/sets/type-declaration/validation.php +++ b/config/sets/type-declaration/validation.php @@ -42,6 +42,33 @@ ); } + $rectorConfig->ruleWithConfiguration( + AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector::class, + [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Http\Request', + 'validate', + 0, + 0, + new \PHPStan\Type\StringType(), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Http\Request', + 'validate', + 0, + 1, + new \PHPStan\Type\MixedType(true), + ), + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Http\Request', + 'validate', + 0, + 2, + new \PHPStan\Type\ObjectType('Closure'), + ), + ] + ); + $rectorConfig->ruleWithConfiguration( AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, [ @@ -70,7 +97,7 @@ 'sometimes', 2, 1, - new \PHPStan\Type\ObjectType('Illuminate\Support\Fluent'), + new \PHPStan\Type\MixedType(true), ), ] ); @@ -98,6 +125,13 @@ $rectorConfig->ruleWithConfiguration( AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, [ + new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + 'Illuminate\Validation\Rules\Exists', + 'where', + 0, + 0, + new \PHPStan\Type\ObjectType('Illuminate\Contracts\Database\Query\Builder'), + ), new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( 'Illuminate\Validation\Rules\Exists', 'where', diff --git a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc index 61cbf29b..4a1e3d00 100644 --- a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc +++ b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-rule.php.inc @@ -7,6 +7,11 @@ use Illuminate\Validation\Rule; Rule::forEach(function ($value, $attribute) { }); +/** @var \Illuminate\Validation\Rules\Exists $exists */ +$exists->where(function ($query) { + $query->where('column', 'value'); +}); + /** @var \Illuminate\Validation\Rules\Exists $exists */ $exists->where('column', function ($query) { $query->where('column', 'value'); @@ -27,6 +32,11 @@ use Illuminate\Validation\Rule; Rule::forEach(function (mixed $value, string $attribute) { }); +/** @var \Illuminate\Validation\Rules\Exists $exists */ +$exists->where(function (\Illuminate\Contracts\Database\Query\Builder $query) { + $query->where('column', 'value'); +}); + /** @var \Illuminate\Validation\Rules\Exists $exists */ $exists->where('column', function (\Illuminate\Contracts\Database\Query\Builder $query) { $query->where('column', 'value'); diff --git a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc index e614f29d..1065fa0c 100644 --- a/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc +++ b/tests/Sets/TypeDeclaration/Validation/Fixture/apply-to-validator.php.inc @@ -34,7 +34,7 @@ $validator->after(function (\Illuminate\Contracts\Validation\Validator $validato } }); -$validator->sometimes('reason', 'required|max:500', function (\Illuminate\Support\Fluent $input, \Illuminate\Support\Fluent $item) { +$validator->sometimes('reason', 'required|max:500', function (\Illuminate\Support\Fluent $input, mixed $item) { return $input->games >= 100; }); From 6d3bfec38832e11d9b105a98db4be949d5f55e84 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Wed, 13 Mar 2024 23:06:18 +0000 Subject: [PATCH 11/21] Remove with as it's slightly problematic --- config/sets/type-declaration/eloquent.php | 16 +------- .../Fixture/apply-with-to-model.php.inc | 41 ------------------- 2 files changed, 2 insertions(+), 55 deletions(-) delete mode 100644 tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index 2bf8d6bf..14f67b90 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -17,6 +17,8 @@ 'Illuminate\Database\Eloquent\Model', 'Illuminate\Contracts\Database\Query\Builder', 'Illuminate\Contracts\Database\Eloquent\Builder', + 'Illuminate\Database\Eloquent\Builder', + 'Illuminate\Database\Query\Builder', ]; $basicPositionOne = [ @@ -29,10 +31,6 @@ 'where', 'whereHasMorph', 'orWhereHasMorph', 'whereDoesntHaveMorph', 'orWhereDoesntHaveMorph', 'when', ]; - $arrayPositionOne = [ - 'with', 'withCount', - ]; - $basicRuleConfiguration = []; $arrayRuleConfiguration = []; @@ -64,16 +62,6 @@ $builderClass, ); } - - foreach ($arrayPositionOne as $method) { - $arrayRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, - $method, - 0, - 0, - $builderClass, - ); - } } $rectorConfig->ruleWithConfiguration( diff --git a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc b/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc deleted file mode 100644 index 45b56ca5..00000000 --- a/tests/Sets/TypeDeclaration/Eloquent/Fixture/apply-with-to-model.php.inc +++ /dev/null @@ -1,41 +0,0 @@ - function ($query) { - $query->where('id', 1) - ->orWhere('id', 2); -}]); - -User::withCount(['team' => function ($query) { - $query->where('id', 1) - ->orWhere('id', 2); -}]); - -?> ------ - function (\Illuminate\Contracts\Database\Query\Builder $query) { - $query->where('id', 1) - ->orWhere('id', 2); -}]); - -User::withCount(['team' => function (\Illuminate\Contracts\Database\Query\Builder $query) { - $query->where('id', 1) - ->orWhere('id', 2); -}]); - -?> From 0de1986783504c91a7c61e83cd06a5992f8aae37 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Tue, 7 May 2024 20:44:25 +0100 Subject: [PATCH 12/21] Refactor of the type system --- config/sets/type-declaration/eloquent.php | 148 ++++++++++++------ ...ikeArgDeclarationRectorConfigGenerator.php | 41 +++++ 2 files changed, 143 insertions(+), 46 deletions(-) create mode 100644 src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index 14f67b90..c70a465b 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -9,10 +9,13 @@ return static function (RectorConfig $rectorConfig): void { $rectorConfig->import(__DIR__ . '/../../config.php'); + $generator = new \RectorLaravel\Util\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator(); + $builderClass = new ObjectType( 'Illuminate\Contracts\Database\Query\Builder' ); + /** @var class-string[] $classesToApplyTo */ $classesToApplyTo = [ 'Illuminate\Database\Eloquent\Model', 'Illuminate\Contracts\Database\Query\Builder', @@ -21,59 +24,112 @@ 'Illuminate\Database\Query\Builder', ]; - $basicPositionOne = [ - 'where', 'orWhere', 'whereNot', 'whereExists', - ]; - $basicPositionTwo = [ - 'where', 'whereHas', 'orWhereHas', 'whereDoesntHave', 'orWhereDoesntHave', 'withWhereHas', 'when', + $basicRuleConfiguration = [ + ...$generator->generate( + [0, 1, 2, 'builder'], + $classesToApplyTo, + 0, + 'where', + $builderClass, + ), + ...$generator->generate( + [0, 'builder'], + $classesToApplyTo, + 0, + 'orWhere', + $builderClass, + ), + ...$generator->generate( + [0, 'builder'], + $classesToApplyTo, + 0, + 'whereNot', + $builderClass, + ), + ...$generator->generate( + [0, 'builder'], + $classesToApplyTo, + 0, + 'whereExists', + $builderClass, + ), + ...$generator->generate( + [1, 'builder'], + $classesToApplyTo, + 0, + 'whereHas', + $builderClass, + ), + ...$generator->generate( + [1, 'builder'], + $classesToApplyTo, + 0, + 'orWhereHas', + $builderClass, + ), + ...$generator->generate( + [1, 'builder'], + $classesToApplyTo, + 0, + 'whereDoesntHave', + $builderClass, + ), + ...$generator->generate( + [1, 'builder'], + $classesToApplyTo, + 0, + 'orWhereDoesntHave', + $builderClass, + ), + ...$generator->generate( + [1, 'builder'], + $classesToApplyTo, + 0, + 'withWhereHas', + $builderClass, + ), + ...$generator->generate( + [1, 2, 'builder'], + $classesToApplyTo, + 0, + 'when', + $builderClass, + ), + ...$generator->generate( + [2, 'builder'], + $classesToApplyTo, + 0, + 'whereHasMorph', + $builderClass, + ), + ...$generator->generate( + [2, 'builder'], + $classesToApplyTo, + 0, + 'orWhereHasMorph', + $builderClass, + ), + ...$generator->generate( + [2, 'builder'], + $classesToApplyTo, + 0, + 'whereDoesntHaveMorph', + $builderClass, + ), + ...$generator->generate( + [2, 'builder'], + $classesToApplyTo, + 0, + 'orWhereDoesntHaveMorph', + $builderClass, + ), ]; - $basicPositionThree = [ - 'where', 'whereHasMorph', 'orWhereHasMorph', 'whereDoesntHaveMorph', 'orWhereDoesntHaveMorph', 'when', - ]; - - $basicRuleConfiguration = []; - $arrayRuleConfiguration = []; - - foreach ($classesToApplyTo as $targetClass) { - foreach ($basicPositionOne as $method) { - $basicRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, - $method, - 0, - 0, - $builderClass, - ); - } - foreach ($basicPositionTwo as $method) { - $basicRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, - $method, - 1, - 0, - $builderClass, - ); - } - foreach ($basicPositionThree as $method) { - $basicRuleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, - $method, - 2, - 0, - $builderClass, - ); - } - } $rectorConfig->ruleWithConfiguration( AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, $basicRuleConfiguration ); - $rectorConfig->ruleWithConfiguration( - AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector::class, - $arrayRuleConfiguration, - ); - $rectorConfig->ruleWithConfiguration( AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, [ diff --git a/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php b/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php new file mode 100644 index 00000000..e8cf19cf --- /dev/null +++ b/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php @@ -0,0 +1,41 @@ +> $callPositionsOrNames + * @param class-string[] $targetClasses + * @param int<0, max> $functionPosition + * @param string $methodName + * @param ObjectType $type + * @return AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration[] + */ + public function generate( + array $callPositionsOrNames, + array $targetClasses, + int $functionPosition, + string $methodName, + ObjectType $type + ): array { + $configurations = []; + + foreach ($callPositionsOrNames as $callPositionsOrName) { + foreach ($targetClasses as $targetClass) { + $configurations[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( + $targetClass, + $methodName, + $callPositionsOrName, + $functionPosition, + $type + ); + } + } + + return $configurations; + } +} From 7e300de28f6e2c7bca320e4713812fae185a37f6 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Tue, 7 May 2024 20:58:24 +0100 Subject: [PATCH 13/21] Document the set --- src/Set/LaravelSetList.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Set/LaravelSetList.php b/src/Set/LaravelSetList.php index ad49796a..7c08b59f 100644 --- a/src/Set/LaravelSetList.php +++ b/src/Set/LaravelSetList.php @@ -137,4 +137,9 @@ final class LaravelSetList implements SetListInterface * @var string */ final public const LARAVEL_STATIC_TO_INJECTION = __DIR__ . '/../../config/sets/laravel-static-to-injection.php'; + + /** + * @var string + */ + final public const LARAVEL_TYPE_DECLARATIONS = __DIR__ . '/../../config/sets/type-declaration.php'; } From 37fd929ba2f2346dbf690a6d080541c798a85ac5 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Wed, 8 May 2024 23:21:47 +0100 Subject: [PATCH 14/21] fixes --- config/sets/type-declaration/eloquent.php | 3 +-- config/sets/type-declaration/validation.php | 6 +++--- ...eWithinCallLikeArgDeclarationRectorConfigGenerator.php | 8 +++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index c70a465b..30396d26 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -4,12 +4,11 @@ use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\FunctionLike\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector; use Rector\TypeDeclaration\ValueObject\AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration; -use RectorLaravel\Rector\Param\AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->import(__DIR__ . '/../../config.php'); - $generator = new \RectorLaravel\Util\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator(); + $generator = new \RectorLaravel\Util\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator; $builderClass = new ObjectType( 'Illuminate\Contracts\Database\Query\Builder' diff --git a/config/sets/type-declaration/validation.php b/config/sets/type-declaration/validation.php index 04845596..39502737 100644 --- a/config/sets/type-declaration/validation.php +++ b/config/sets/type-declaration/validation.php @@ -22,7 +22,7 @@ 'make', 1, 0, - new \PHPStan\Type\StringType(), + new \PHPStan\Type\StringType, ), new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( $targetClass, @@ -50,7 +50,7 @@ 'validate', 0, 0, - new \PHPStan\Type\StringType(), + new \PHPStan\Type\StringType, ), new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( 'Illuminate\Http\Request', @@ -117,7 +117,7 @@ 'forEach', 0, 1, - new \PHPStan\Type\StringType(), + new \PHPStan\Type\StringType, ), ] ); diff --git a/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php b/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php index e8cf19cf..cd3703b7 100644 --- a/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php +++ b/src/Util/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator.php @@ -8,11 +8,9 @@ class AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator { /** - * @param array> $callPositionsOrNames - * @param class-string[] $targetClasses - * @param int<0, max> $functionPosition - * @param string $methodName - * @param ObjectType $type + * @param array> $callPositionsOrNames + * @param class-string[] $targetClasses + * @param int<0, max> $functionPosition * @return AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration[] */ public function generate( From c4e286c214bed09300c66b63eede7123b29215d0 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Thu, 9 May 2024 19:58:21 +0100 Subject: [PATCH 15/21] More fixes --- config/sets/type-declaration/eloquent.php | 6 ++-- .../type-declaration/service-container.php | 8 ++--- config/sets/type-declaration/validation.php | 31 ++++++++++--------- ...allLikeArgArrayValuesDeclarationRector.php | 9 ------ ...ikeArgDeclarationRectorConfigGenerator.php | 8 ++--- 5 files changed, 29 insertions(+), 33 deletions(-) diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index 30396d26..2305a3e9 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -1,14 +1,16 @@ import(__DIR__ . '/../../config.php'); - $generator = new \RectorLaravel\Util\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator; + $generator = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRectorConfigGenerator; $builderClass = new ObjectType( 'Illuminate\Contracts\Database\Query\Builder' @@ -144,7 +146,7 @@ 'handleLazyLoadingViolationUsing', 0, 1, - new \PHPStan\Type\StringType, + new StringType, ), ] ); diff --git a/config/sets/type-declaration/service-container.php b/config/sets/type-declaration/service-container.php index 2153eeb1..24fdeafc 100644 --- a/config/sets/type-declaration/service-container.php +++ b/config/sets/type-declaration/service-container.php @@ -19,12 +19,12 @@ $ruleConfiguration = []; - foreach ($classesToApplyTo as $targetClass) { + foreach ($classesToApplyTo as $classToApplyTo) { foreach ([ 'bind', 'bindIf', 'singleton', 'singletonIf', 'scoped', 'scopedIf', ] as $method) { $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, + $classToApplyTo, $method, 1, 0, @@ -32,14 +32,14 @@ ); } $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, + $classToApplyTo, 'resolving', 1, 1, $applicationClass, ); $ruleConfiguration[] = new AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration( - $targetClass, + $classToApplyTo, 'extends', 1, 1, diff --git a/config/sets/type-declaration/validation.php b/config/sets/type-declaration/validation.php index 39502737..3fb93923 100644 --- a/config/sets/type-declaration/validation.php +++ b/config/sets/type-declaration/validation.php @@ -1,5 +1,8 @@ Date: Sat, 21 Sep 2024 23:56:09 +0100 Subject: [PATCH 16/21] Update Sets --- src/Set/LaravelLevelSetList.php | 4 +--- src/Set/LaravelSetList.php | 4 +--- src/Set/LaravelSetProvider.php | 5 +++++ src/Set/Packages/Cashier/CashierLevelSetList.php | 4 +--- src/Set/Packages/Cashier/CashierSetList.php | 4 +--- src/Set/Packages/Livewire/LivewireLevelSetList.php | 4 +--- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/Set/LaravelLevelSetList.php b/src/Set/LaravelLevelSetList.php index 852d3cd5..5dd9db62 100644 --- a/src/Set/LaravelLevelSetList.php +++ b/src/Set/LaravelLevelSetList.php @@ -4,9 +4,7 @@ namespace RectorLaravel\Set; -use Rector\Set\Contract\SetListInterface; - -final class LaravelLevelSetList implements SetListInterface +final class LaravelLevelSetList { /** * @var string diff --git a/src/Set/LaravelSetList.php b/src/Set/LaravelSetList.php index 7c08b59f..ed9205f0 100644 --- a/src/Set/LaravelSetList.php +++ b/src/Set/LaravelSetList.php @@ -4,9 +4,7 @@ namespace RectorLaravel\Set; -use Rector\Set\Contract\SetListInterface; - -final class LaravelSetList implements SetListInterface +final class LaravelSetList { /** * @var string diff --git a/src/Set/LaravelSetProvider.php b/src/Set/LaravelSetProvider.php index 2288cbf5..6bd8ea3f 100644 --- a/src/Set/LaravelSetProvider.php +++ b/src/Set/LaravelSetProvider.php @@ -84,6 +84,11 @@ public function provide(): array 'Upgrade Legacy Factories to Modern Factories', LaravelSetList::LARAVEL_LEGACY_FACTORIES_TO_CLASSES, ), + new Set( + self::GROUP_NAME, + 'Apply type hints to Laravel', + LaravelSetList::LARAVEL_TYPE_DECLARATIONS, + ), new Set( self::GROUP_NAME, 'Livewire 3.0', diff --git a/src/Set/Packages/Cashier/CashierLevelSetList.php b/src/Set/Packages/Cashier/CashierLevelSetList.php index b2fa4ee1..b9522ea7 100644 --- a/src/Set/Packages/Cashier/CashierLevelSetList.php +++ b/src/Set/Packages/Cashier/CashierLevelSetList.php @@ -4,9 +4,7 @@ namespace RectorLaravel\Set\Packages\Cashier; -use Rector\Set\Contract\SetListInterface; - -final class CashierLevelSetList implements SetListInterface +final class CashierLevelSetList { /** * @var string diff --git a/src/Set/Packages/Cashier/CashierSetList.php b/src/Set/Packages/Cashier/CashierSetList.php index ca727341..23f1f9fd 100644 --- a/src/Set/Packages/Cashier/CashierSetList.php +++ b/src/Set/Packages/Cashier/CashierSetList.php @@ -4,9 +4,7 @@ namespace RectorLaravel\Set\Packages\Cashier; -use Rector\Set\Contract\SetListInterface; - -final class CashierSetList implements SetListInterface +final class CashierSetList { /** * @var string diff --git a/src/Set/Packages/Livewire/LivewireLevelSetList.php b/src/Set/Packages/Livewire/LivewireLevelSetList.php index 36c3852a..1451cf63 100644 --- a/src/Set/Packages/Livewire/LivewireLevelSetList.php +++ b/src/Set/Packages/Livewire/LivewireLevelSetList.php @@ -4,9 +4,7 @@ namespace RectorLaravel\Set\Packages\Livewire; -use Rector\Set\Contract\SetListInterface; - -final class LivewireLevelSetList implements SetListInterface +final class LivewireLevelSetList { /** * @var string From 1681437eb0ca8de6ecd4d0e85ca9ca50c6f66c8d Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Sun, 22 Sep 2024 03:00:42 +0100 Subject: [PATCH 17/21] Adds more stubs --- stubs/Illuminate/Contracts/Database/Eloquent/Builder.php | 8 ++++++++ stubs/Illuminate/Contracts/Database/Query/Builder.php | 8 ++++++++ stubs/Illuminate/Database/Query/Builder.php | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 stubs/Illuminate/Contracts/Database/Eloquent/Builder.php create mode 100644 stubs/Illuminate/Contracts/Database/Query/Builder.php diff --git a/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php b/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php new file mode 100644 index 00000000..f4ffd247 --- /dev/null +++ b/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php @@ -0,0 +1,8 @@ + Date: Sun, 22 Sep 2024 03:02:56 +0100 Subject: [PATCH 18/21] Minor tweak --- config/sets/type-declaration/eloquent.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/sets/type-declaration/eloquent.php b/config/sets/type-declaration/eloquent.php index 2305a3e9..08663ddb 100644 --- a/config/sets/type-declaration/eloquent.php +++ b/config/sets/type-declaration/eloquent.php @@ -20,8 +20,6 @@ $classesToApplyTo = [ 'Illuminate\Database\Eloquent\Model', 'Illuminate\Contracts\Database\Query\Builder', - 'Illuminate\Contracts\Database\Eloquent\Builder', - 'Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder', ]; From d003dd290b6f3529838458f0b8818ec6cdaeae19 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Sun, 22 Sep 2024 03:13:17 +0100 Subject: [PATCH 19/21] Test of arg rule Causes an issue if the string is an unknown class --- .../type-declaration/service-container.php | 18 ++++++++++++++++++ .../Fixture/apply-bind-to-application.php.inc | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/config/sets/type-declaration/service-container.php b/config/sets/type-declaration/service-container.php index 24fdeafc..653d3b9e 100644 --- a/config/sets/type-declaration/service-container.php +++ b/config/sets/type-declaration/service-container.php @@ -51,4 +51,22 @@ AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector::class, $ruleConfiguration ); + + $rectorConfig->ruleWithConfiguration( + \Rector\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeFromArgRector::class, + [ + new \Rector\TypeDeclaration\ValueObject\AddClosureParamTypeFromArg( + 'Illuminate\Support\Facades\App', + 'extends', + 1, + 0, + ), + new \Rector\TypeDeclaration\ValueObject\AddClosureParamTypeFromArg( + 'Illuminate\Support\Facades\App', + 'resolving', + 1, + 0, + ) + ] + ); }; diff --git a/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc b/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc index 85561350..c125b35c 100644 --- a/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc +++ b/tests/Sets/TypeDeclaration/ServiceContainer/Fixture/apply-bind-to-application.php.inc @@ -26,11 +26,11 @@ namespace RectorLaravel\Tests\Sets\TypeDeclaration\ServiceContainer\Fixture; return new \stdClass(); }); -\Illuminate\Support\Facades\App::extends('class', function ($instance, $app) { +\Illuminate\Support\Facades\App::extends(\Illuminate\Support\Collection::class, function ($instance, $app) { return new \stdClass(); }); -\Illuminate\Support\Facades\App::resolving('class', function ($instance, $app) { +\Illuminate\Support\Facades\App::resolving(\Illuminate\Support\Collection::class, function ($instance, $app) { return new \stdClass(); }); @@ -64,11 +64,11 @@ namespace RectorLaravel\Tests\Sets\TypeDeclaration\ServiceContainer\Fixture; return new \stdClass(); }); -\Illuminate\Support\Facades\App::extends('class', function ($instance, \Illuminate\Contracts\Foundation\Application $app) { +\Illuminate\Support\Facades\App::extends(\Illuminate\Support\Collection::class, function (\Illuminate\Support\Collection $instance, \Illuminate\Contracts\Foundation\Application $app) { return new \stdClass(); }); -\Illuminate\Support\Facades\App::resolving('class', function ($instance, \Illuminate\Contracts\Foundation\Application $app) { +\Illuminate\Support\Facades\App::resolving(\Illuminate\Support\Collection::class, function (\Illuminate\Support\Collection $instance, \Illuminate\Contracts\Foundation\Application $app) { return new \stdClass(); }); From fb3b8a3d738eb2b5ea70a750a0f51ddef3971ca0 Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Sun, 22 Sep 2024 11:17:36 +0100 Subject: [PATCH 20/21] Fix PHPStan --- phpstan.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index fe446dc4..bb8e6810 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -19,7 +19,7 @@ parameters: ignoreErrors: # false positive - - '#Parameter \#1 \$value of static method PhpParser\\BuilderHelpers\:\:normalizeValue\(\) expects array\|bool\|float\|int\|PhpParser\\Node\\Expr\|string\|UnitEnum\|null, mixed given#' + - '#Parameter \#1 \$value of static method PhpParser\\BuilderHelpers\:\:normalizeValue\(\) expects array\|bool\|float\|int\|PhpParser\\Node\\Expr\|string\|null, mixed given#' - path: src/Rector/Class_/UnifyModelDatesWithCastsRector.php From b39d29fdfec0a2f03a8c4da3789a9d1cff8eb7bf Mon Sep 17 00:00:00 2001 From: Peter Fox Date: Sun, 22 Sep 2024 11:17:53 +0100 Subject: [PATCH 21/21] cs fixes --- config/sets/type-declaration/service-container.php | 10 ++++++---- ...keWithinCallLikeArgArrayValuesDeclarationRector.php | 1 + .../Illuminate/Contracts/Database/Eloquent/Builder.php | 1 - stubs/Illuminate/Contracts/Database/Query/Builder.php | 1 - 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/config/sets/type-declaration/service-container.php b/config/sets/type-declaration/service-container.php index 653d3b9e..0fe17f22 100644 --- a/config/sets/type-declaration/service-container.php +++ b/config/sets/type-declaration/service-container.php @@ -1,5 +1,7 @@ ruleWithConfiguration( - \Rector\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeFromArgRector::class, + AddClosureParamTypeFromArgRector::class, [ - new \Rector\TypeDeclaration\ValueObject\AddClosureParamTypeFromArg( + new AddClosureParamTypeFromArg( 'Illuminate\Support\Facades\App', 'extends', 1, 0, ), - new \Rector\TypeDeclaration\ValueObject\AddClosureParamTypeFromArg( + new AddClosureParamTypeFromArg( 'Illuminate\Support\Facades\App', 'resolving', 1, 0, - ) + ), ] ); }; diff --git a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php index 7fbf2d0a..dc5aae4f 100644 --- a/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php +++ b/src/Rector/Param/AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector.php @@ -40,6 +40,7 @@ class AddParamTypeForFunctionLikeWithinCallLikeArgArrayValuesDeclarationRector e * @var AddParamTypeForFunctionLikeWithinCallLikeArgDeclaration[] */ private $addParamTypeForFunctionLikeParamDeclarations = []; + /** * @var bool */ diff --git a/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php b/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php index f4ffd247..d29a5f84 100644 --- a/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php +++ b/stubs/Illuminate/Contracts/Database/Eloquent/Builder.php @@ -4,5 +4,4 @@ interface Builder extends \Illuminate\Contracts\Database\Query\Builder { - } diff --git a/stubs/Illuminate/Contracts/Database/Query/Builder.php b/stubs/Illuminate/Contracts/Database/Query/Builder.php index 1d6f5567..cb10d60a 100644 --- a/stubs/Illuminate/Contracts/Database/Query/Builder.php +++ b/stubs/Illuminate/Contracts/Database/Query/Builder.php @@ -4,5 +4,4 @@ interface Builder { - }