From 7b0cce732577c310fbb31c41357dcd5b5ae11089 Mon Sep 17 00:00:00 2001 From: Can Vural Date: Mon, 28 Nov 2022 14:00:59 +0100 Subject: [PATCH] feat(refactor): get rid of most instanceof ConstantStringType checks --- composer.json | 2 +- ...AccessDynamicMethodReturnTypeExtension.php | 38 +++++++----- ...DynamicStaticMethodReturnTypeExtension.php | 23 +++---- src/ReturnTypes/GuardExtension.php | 12 ++-- src/ReturnTypes/TestCaseExtension.php | 22 +++++-- .../ModelPropertiesRuleHelper.php | 61 ++++++++++--------- src/Rules/NoModelMakeRule.php | 14 ++--- src/Rules/RelationExistenceRule.php | 51 ++++++---------- src/Support/CollectionHelper.php | 5 +- ...lationDynamicMethodReturnTypeExtension.php | 7 ++- .../ReturnTypes/ContainerArrayAccessTest.php | 19 ------ tests/Rules/Data/model-property-builder.php | 2 + tests/Rules/Data/model-property-model.php | 2 +- tests/Rules/ModelPropertyRuleTest.php | 15 ++--- tests/Type/GeneralTypeTest.php | 1 + tests/Type/data/container-array-access.php | 14 +++++ 16 files changed, 148 insertions(+), 140 deletions(-) delete mode 100644 tests/Features/ReturnTypes/ContainerArrayAccessTest.php create mode 100644 tests/Type/data/container-array-access.php diff --git a/composer.json b/composer.json index 732824273..469a0eb9a 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "illuminate/support": "^9", "mockery/mockery": "^1.4.4", "phpmyadmin/sql-parser": "^5.5", - "phpstan/phpstan": "^1.9.0" + "phpstan/phpstan": "^1.9.2" }, "require-dev": { "nikic/php-parser": "^4.13.2", diff --git a/src/ReturnTypes/ContainerArrayAccessDynamicMethodReturnTypeExtension.php b/src/ReturnTypes/ContainerArrayAccessDynamicMethodReturnTypeExtension.php index bc1f606f5..6d66b32c4 100644 --- a/src/ReturnTypes/ContainerArrayAccessDynamicMethodReturnTypeExtension.php +++ b/src/ReturnTypes/ContainerArrayAccessDynamicMethodReturnTypeExtension.php @@ -8,12 +8,12 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ErrorType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeUtils; class ContainerArrayAccessDynamicMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -43,31 +43,41 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type { + ): ?Type { $args = $methodCall->getArgs(); if (count($args) === 0) { - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } $argType = $scope->getType($args[0]->value); - if (! $argType instanceof ConstantStringType) { - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + $argStrings = TypeUtils::getConstantStrings($argType); + + if ($argStrings === []) { + return null; } - $resolvedValue = $this->resolve($argType->getValue()); + $argTypes = []; - if ($resolvedValue === null) { - return new ErrorType(); - } + foreach ($argStrings as $argString) { + $resolvedValue = $this->resolve($argString->getValue()); + + if ($resolvedValue === null) { + $argTypes[] = new ErrorType(); + continue; + } + + if (is_object($resolvedValue)) { + $class = get_class($resolvedValue); - if (is_object($resolvedValue)) { - $class = get_class($resolvedValue); + $argTypes[] = new ObjectType($class); + continue; + } - return new ObjectType($class); + $argTypes[] = $scope->getTypeFromValue($resolvedValue); } - return $scope->getTypeFromValue($resolvedValue); + return count($argTypes) > 1 ? TypeCombinator::union(...$argTypes) : $argTypes[0]; } } diff --git a/src/ReturnTypes/GuardDynamicStaticMethodReturnTypeExtension.php b/src/ReturnTypes/GuardDynamicStaticMethodReturnTypeExtension.php index 2ed709483..0571b4650 100644 --- a/src/ReturnTypes/GuardDynamicStaticMethodReturnTypeExtension.php +++ b/src/ReturnTypes/GuardDynamicStaticMethodReturnTypeExtension.php @@ -11,11 +11,11 @@ use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeUtils; class GuardDynamicStaticMethodReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension { @@ -58,25 +58,22 @@ public function getTypeFromStaticMethodCall( } $argType = $scope->getType($methodCall->getArgs()[0]->value); + $argStrings = TypeUtils::getConstantStrings($argType); - if (! $argType instanceof ConstantStringType) { + if (count($argStrings) !== 1) { return $defaultReturnType; } - return $this->findTypeFromGuardDriver($argType->getValue()) ?? $defaultReturnType; + return $this->findTypeFromGuardDriver($argStrings[0]->getValue()) ?? $defaultReturnType; } private function findTypeFromGuardDriver(string $driver): ?Type { - switch ($driver) { - case 'session': - return new ObjectType(\Illuminate\Auth\SessionGuard::class); - case 'token': - return new ObjectType(\Illuminate\Auth\TokenGuard::class); - case 'passport': - return new ObjectType(\Illuminate\Auth\RequestGuard::class); - default: - return null; - } + return match ($driver) { + 'session' => new ObjectType(\Illuminate\Auth\SessionGuard::class), + 'token' => new ObjectType(\Illuminate\Auth\TokenGuard::class), + 'passport' => new ObjectType(\Illuminate\Auth\RequestGuard::class), + default => null, + }; } } diff --git a/src/ReturnTypes/GuardExtension.php b/src/ReturnTypes/GuardExtension.php index c43a99135..c729ef91d 100644 --- a/src/ReturnTypes/GuardExtension.php +++ b/src/ReturnTypes/GuardExtension.php @@ -11,12 +11,11 @@ use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeUtils; final class GuardExtension implements DynamicMethodReturnTypeExtension { @@ -40,7 +39,7 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type { + ): ?Type { $config = $this->getContainer()->get('config'); $authModel = null; @@ -50,7 +49,7 @@ public function getTypeFromMethodCall( } if ($authModel === null) { - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } return TypeCombinator::addNull(new ObjectType($authModel)); @@ -71,11 +70,12 @@ private function getGuardFromMethodCall(Scope $scope, MethodCall $methodCall): ? } $guardType = $scope->getType($methodCall->var->getArgs()[0]->value); + $constantStrings = TypeUtils::getConstantStrings($guardType); - if (! $guardType instanceof ConstantStringType) { + if (count($constantStrings) !== 1) { return null; } - return $guardType->getValue(); + return $constantStrings[0]->getValue(); } } diff --git a/src/ReturnTypes/TestCaseExtension.php b/src/ReturnTypes/TestCaseExtension.php index 2a76ba1b8..96b3d65af 100644 --- a/src/ReturnTypes/TestCaseExtension.php +++ b/src/ReturnTypes/TestCaseExtension.php @@ -8,11 +8,12 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\ErrorType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\TypeUtils; /** * @internal @@ -40,14 +41,27 @@ public function getTypeFromMethodCall( ): Type { $defaultReturnType = new ObjectType('Mockery\\MockInterface'); + if (count($methodCall->args) === 0) { + return new ErrorType(); + } + $classType = $scope->getType($methodCall->getArgs()[0]->value); + $constantStrings = TypeUtils::getConstantStrings($classType); - if (! $classType instanceof ConstantStringType) { + if ($constantStrings === []) { return $defaultReturnType; } - $objectType = new ObjectType($classType->getValue()); + $returnTypes = []; + + foreach ($constantStrings as $constantString) { + $objectType = new ObjectType($constantString->getValue()); + + $returnTypes[] = TypeCombinator::intersect($defaultReturnType, $objectType); + } - return TypeCombinator::intersect($defaultReturnType, $objectType); + return count($returnTypes) === 1 + ? $returnTypes[0] + : TypeCombinator::union(...$returnTypes); } } diff --git a/src/Rules/ModelProperties/ModelPropertiesRuleHelper.php b/src/Rules/ModelProperties/ModelPropertiesRuleHelper.php index 3d8a9abfc..fecfe7c89 100644 --- a/src/Rules/ModelProperties/ModelPropertiesRuleHelper.php +++ b/src/Rules/ModelProperties/ModelPropertiesRuleHelper.php @@ -16,11 +16,8 @@ use PHPStan\ShouldNotHappenException; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantArrayType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\GeneralizePrecision; -use PHPStan\Type\IntegerType; use PHPStan\Type\ObjectType; -use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; @@ -68,59 +65,67 @@ public function check(MethodReflection $methodReflection, Scope $scope, array $a if ($argType instanceof ConstantArrayType) { $errors = []; - $keyType = TypeUtils::generalizeType($argType->getKeyType(), GeneralizePrecision::lessSpecific()); + $keyType = $argType->getKeyType()->generalize(GeneralizePrecision::lessSpecific()); - if ($keyType instanceof IntegerType) { + if ($keyType->isInteger()->yes()) { $valueTypes = $argType->getValuesArray()->getValueTypes(); - } elseif ($keyType instanceof StringType) { + } elseif ($keyType->isString()->yes()) { $valueTypes = $argType->getKeysArray()->getValueTypes(); } else { $valueTypes = []; } foreach ($valueTypes as $valueType) { + $strings = TypeUtils::getConstantStrings($valueType); + // It could be something like `DB::raw` // We only want to analyze strings - if (! $valueType instanceof ConstantStringType) { + if ($strings === []) { continue; } - // TODO: maybe check table names and columns here. And for JSON access maybe just the column name - if (mb_strpos($valueType->getValue(), '.') !== false || mb_strpos($valueType->getValue(), '->') !== false) { - continue; - } + foreach ($strings as $string) { + // TODO: maybe check table names and columns here. And for JSON access maybe just the column name + if (mb_strpos($string->getValue(), '.') !== false || mb_strpos($string->getValue(), '->') !== false) { + continue; + } - if (! $modelType->hasProperty($valueType->getValue())->yes()) { - $error = sprintf('Property \'%s\' does not exist in %s model.', $valueType->getValue(), $modelType->describe(VerbosityLevel::typeOnly())); + if (! $modelType->hasProperty($string->getValue())->yes()) { + $error = sprintf('Property \'%s\' does not exist in %s model.', $string->getValue(), $modelType->describe(VerbosityLevel::typeOnly())); - if ($methodReflection->getDeclaringClass()->getName() === BelongsToMany::class) { - $error .= sprintf(" If '%s' exists as a column on the pivot table, consider using 'wherePivot' or prefix the column with table name instead.", $valueType->getValue()); - } + if ($methodReflection->getDeclaringClass()->getName() === BelongsToMany::class) { + $error .= sprintf(" If '%s' exists as a column on the pivot table, consider using 'wherePivot' or prefix the column with table name instead.", $string->getValue()); + } - $errors[] = $error; + $errors[] = $error; + } } } return $errors; } - if (! $argType instanceof ConstantStringType) { - return []; - } + $argStrings = TypeUtils::getConstantStrings($argType); - // TODO: maybe check table names and columns here. And for JSON access maybe just the column name - if (mb_strpos($argType->getValue(), '.') !== false || mb_strpos($argType->getValue(), '->') !== false) { + if ($argStrings === []) { return []; } - if (! $modelType->hasProperty($argType->getValue())->yes()) { - $error = sprintf('Property \'%s\' does not exist in %s model.', $argType->getValue(), $modelType->describe(VerbosityLevel::typeOnly())); - - if ((new ObjectType(BelongsToMany::class))->isSuperTypeOf(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType())->yes()) { - $error .= sprintf(" If '%s' exists as a column on the pivot table, consider using 'wherePivot' or prefix the column with table name instead.", $argType->getValue()); + foreach ($argStrings as $argString) { + // TODO: maybe check table names and columns here. And for JSON access maybe just the column name + if (mb_strpos($argString->getValue(), '.') !== false || mb_strpos($argString->getValue(), '->') !== false) { + return []; } - return [$error]; + if (! $modelType->hasProperty($argString->getValue())->yes()) { + $error = sprintf('Property \'%s\' does not exist in %s model.', $argString->getValue(), $modelType->describe(VerbosityLevel::typeOnly())); + + if ((new ObjectType(BelongsToMany::class))->isSuperTypeOf(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType())->yes()) { + $error .= sprintf(" If '%s' exists as a column on the pivot table, consider using 'wherePivot' or prefix the column with table name instead.", $argString->getValue()); + } + + return [$error]; + } } return []; diff --git a/src/Rules/NoModelMakeRule.php b/src/Rules/NoModelMakeRule.php index 5a2afbdd6..dc469f3f4 100644 --- a/src/Rules/NoModelMakeRule.php +++ b/src/Rules/NoModelMakeRule.php @@ -16,8 +16,8 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ObjectType; +use PHPStan\Type\TypeUtils; /** * Catches inefficient instantiation of models using Model::make(). @@ -96,17 +96,11 @@ protected function isCalledOnModel(StaticCall $call, Scope $scope): bool if ($class instanceof FullyQualified) { $type = new ObjectType($class->toString()); } elseif ($class instanceof Expr) { - $exprType = $scope->getType($class); + $type = $scope->getType($class); - if (! $exprType instanceof ConstantStringType) { - return false; + if ($type->isClassStringType()->yes() && TypeUtils::getConstantStrings($type) !== []) { + $type = new ObjectType($type->getValue()); // @phpstan-ignore-line } - - if (! $exprType->isClassString()) { - return false; - } - - $type = new ObjectType($exprType->getValue()); } else { // TODO can we handle relative names, do they even occur here? return false; diff --git a/src/Rules/RelationExistenceRule.php b/src/Rules/RelationExistenceRule.php index f9570955d..02db9221f 100644 --- a/src/Rules/RelationExistenceRule.php +++ b/src/Rules/RelationExistenceRule.php @@ -11,14 +11,10 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantStringType; -use PHPStan\Type\GeneralizePrecision; -use PHPStan\Type\IntegerType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use PHPStan\Type\TypeTraverser; -use PHPStan\Type\UnionType; +use PHPStan\Type\TypeUtils; /** @implements Rule */ class RelationExistenceRule implements Rule @@ -72,39 +68,32 @@ public function processNode(Node $node, Scope $scope): array $valueType = $scope->getType($args[0]->value); - if (! $valueType instanceof ConstantStringType && ! $valueType instanceof ConstantArrayType) { - return []; - } - - if ($valueType instanceof ConstantStringType) { - $relations = [$valueType]; + /** @var ConstantStringType[] $relations */ + $relations = []; + + if ($valueType->isConstantArray()->yes()) { + $relations = array_merge( + $relations, + ...array_map(function (Type $type) { + return TypeUtils::getConstantStrings($type); + }, $valueType->getKeyTypes()), // @phpstan-ignore-line + ...array_map(function (Type $type) { + return TypeUtils::getConstantStrings($type); + }, $valueType->getValueTypes()), // @phpstan-ignore-line + ); } else { - if ($valueType->getKeyType()->generalize(GeneralizePrecision::lessSpecific()) instanceof IntegerType) { - $relations = $valueType->getValueTypes(); - } else { - $relations = $valueType->getKeyTypes(); + $constants = TypeUtils::getConstantStrings($valueType); + + if ($constants === []) { + return []; } + + $relations = $constants; } $errors = []; foreach ($relations as $relationType) { - $relationType = TypeTraverser::map($relationType, static function (Type $type, callable $traverse) { - if ($type instanceof UnionType) { - return $traverse($type); - } - - if ($type instanceof ConstantStringType) { - return $type; - } - - return $traverse($type); - }); - - if (! $relationType instanceof ConstantStringType) { - continue; - } - $relationName = explode(':', $relationType->getValue())[0]; $calledOnNode = $node instanceof MethodCall ? $node->var : $node->class; diff --git a/src/Support/CollectionHelper.php b/src/Support/CollectionHelper.php index 62ac63466..0b09f8c93 100644 --- a/src/Support/CollectionHelper.php +++ b/src/Support/CollectionHelper.php @@ -17,7 +17,6 @@ use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -use PHPStan\Type\TypeUtils; use PHPStan\Type\TypeWithClassName; use Traversable; @@ -50,8 +49,8 @@ public function determineGenericCollectionTypeFromType(Type $type): GenericObjec } return new GenericObjectType(Collection::class, [ - TypeUtils::generalizeType($type->getIterableKeyType(), GeneralizePrecision::lessSpecific()), - TypeUtils::generalizeType($type->getIterableValueType(), GeneralizePrecision::lessSpecific()), + $type->getIterableKeyType()->generalize(GeneralizePrecision::lessSpecific()), + $type->getIterableValueType()->generalize(GeneralizePrecision::lessSpecific()), ]); } diff --git a/src/Types/RelationDynamicMethodReturnTypeExtension.php b/src/Types/RelationDynamicMethodReturnTypeExtension.php index 29f146ac2..e0949483d 100644 --- a/src/Types/RelationDynamicMethodReturnTypeExtension.php +++ b/src/Types/RelationDynamicMethodReturnTypeExtension.php @@ -14,12 +14,12 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\ReflectionProvider; use PHPStan\ShouldNotHappenException; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\ObjectType; use PHPStan\Type\StaticType; use PHPStan\Type\Type; +use PHPStan\Type\TypeUtils; class RelationDynamicMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -77,12 +77,13 @@ public function getTypeFromMethodCall( } $argType = $scope->getType($methodCall->getArgs()[0]->value); + $argStrings = TypeUtils::getConstantStrings($argType); - if (! $argType instanceof ConstantStringType) { + if (count($argStrings) !== 1) { return $returnType; } - $argClassName = $argType->getValue(); + $argClassName = $argStrings[0]->getValue(); if (! $this->provider->hasClass($argClassName)) { $argClassName = Model::class; diff --git a/tests/Features/ReturnTypes/ContainerArrayAccessTest.php b/tests/Features/ReturnTypes/ContainerArrayAccessTest.php deleted file mode 100644 index b98b832b8..000000000 --- a/tests/Features/ReturnTypes/ContainerArrayAccessTest.php +++ /dev/null @@ -1,19 +0,0 @@ -app['auth']; - } -} diff --git a/tests/Rules/Data/model-property-builder.php b/tests/Rules/Data/model-property-builder.php index 029036bc1..0c15fdd80 100644 --- a/tests/Rules/Data/model-property-builder.php +++ b/tests/Rules/Data/model-property-builder.php @@ -1,6 +1,8 @@ firstWhere('foo', 'bar'); +\App\User::query()->firstWhere($union, 'bar'); \App\User::query()->where('foo', 'bar')->get(); /** @return \Illuminate\Database\Eloquent\Builder<\App\User> */ diff --git a/tests/Rules/Data/model-property-model.php b/tests/Rules/Data/model-property-model.php index 0791c9d43..1cdf0d83e 100644 --- a/tests/Rules/Data/model-property-model.php +++ b/tests/Rules/Data/model-property-model.php @@ -20,7 +20,7 @@ public function unionMethod(\App\User|\App\Account $model): void public function unionMethodWithPropertyOnlyInOne(\App\User|\App\Account $model): void { - $model->update(['name' => 'bar']); + $model->update(['email_verified_at' => 'bar']); } public function unionMethodGreen(\App\User|\App\Account $model): void diff --git a/tests/Rules/ModelPropertyRuleTest.php b/tests/Rules/ModelPropertyRuleTest.php index da7b6bd88..5b5d6e27d 100644 --- a/tests/Rules/ModelPropertyRuleTest.php +++ b/tests/Rules/ModelPropertyRuleTest.php @@ -13,15 +13,16 @@ public function testModelPropertyRuleOnBuilder(): void $errors = $this->setConfigPath(__DIR__.DIRECTORY_SEPARATOR.'Data/modelPropertyConfig.neon')->findErrorsByLine(__DIR__.'/Data/model-property-builder.php'); self::assertEquals([ - 3 => 'Property \'foo\' does not exist in App\\User model.', 4 => 'Property \'foo\' does not exist in App\\User model.', - 9 => 'Property \'foo\' does not exist in App\\User model.', - 18 => 'Property \'foo\' does not exist in App\\User model.', - 23 => 'Property \'foo\' does not exist in App\\User model.', + 5 => 'Property \'unionNotExisting\' does not exist in App\\User model.', + 6 => 'Property \'foo\' does not exist in App\\User model.', + 11 => 'Property \'foo\' does not exist in App\\User model.', + 20 => 'Property \'foo\' does not exist in App\\User model.', 25 => 'Property \'foo\' does not exist in App\\User model.', - 26 => 'Property \'foo\' does not exist in App\\User model.', 27 => 'Property \'foo\' does not exist in App\\User model.', - 30 => 'Property \'foo\' does not exist in App\\User model.', + 28 => 'Property \'foo\' does not exist in App\\User model.', + 29 => 'Property \'foo\' does not exist in App\\User model.', + 32 => 'Property \'foo\' does not exist in App\\User model.', ], $errors); } @@ -46,7 +47,7 @@ public function testModelPropertyRuleOnModel(): void self::assertEquals([ 9 => 'Property \'foo\' does not exist in ModelPropertyModel\ModelPropertyOnModel model.', 16 => 'Property \'foo\' does not exist in App\Account|App\User model.', - 23 => 'Property \'name\' does not exist in App\Account|App\User model.', + 23 => 'Property \'email_verified_at\' does not exist in App\Account|App\User model.', ], $errors); } diff --git a/tests/Type/GeneralTypeTest.php b/tests/Type/GeneralTypeTest.php index 7bfd20d88..7b22d5288 100644 --- a/tests/Type/GeneralTypeTest.php +++ b/tests/Type/GeneralTypeTest.php @@ -33,6 +33,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__.'/data/validator.php'); yield from $this->gatherAssertTypes(__DIR__.'/data/form-request.php'); yield from $this->gatherAssertTypes(__DIR__.'/data/database-transaction.php'); + yield from $this->gatherAssertTypes(__DIR__.'/data/container-array-access.php'); } /** diff --git a/tests/Type/data/container-array-access.php b/tests/Type/data/container-array-access.php new file mode 100644 index 000000000..a7b0465e0 --- /dev/null +++ b/tests/Type/data/container-array-access.php @@ -0,0 +1,14 @@ +