From b43c057d2d34448b421cd879320974f393d992ac Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 20 Jan 2022 15:15:01 +0100 Subject: [PATCH] Fix unset() on intersection with hasOffset --- src/Analyser/MutatingScope.php | 27 ++------- .../Accessory/AccessoryLiteralStringType.php | 5 ++ .../Accessory/AccessoryNonEmptyStringType.php | 5 ++ .../Accessory/AccessoryNumericStringType.php | 5 ++ src/Type/Accessory/HasOffsetType.php | 8 +++ src/Type/Accessory/NonEmptyArrayType.php | 5 ++ src/Type/ArrayType.php | 5 ++ src/Type/BooleanType.php | 23 +------ src/Type/ClosureType.php | 22 +------ src/Type/FloatType.php | 22 +------ src/Type/IntegerType.php | 23 +------ src/Type/IntersectionType.php | 5 ++ src/Type/MixedType.php | 5 ++ src/Type/NeverType.php | 5 ++ src/Type/NullType.php | 5 ++ src/Type/ObjectType.php | 9 +++ src/Type/ResourceType.php | 23 +------ src/Type/StaticType.php | 5 ++ src/Type/StrictMixedType.php | 5 ++ src/Type/StringType.php | 5 ++ .../Traits/MaybeOffsetAccessibleTypeTrait.php | 5 ++ .../Traits/NonOffsetAccessibleTypeTrait.php | 7 ++- src/Type/Type.php | 2 + src/Type/UnionType.php | 5 ++ .../Analyser/NodeScopeResolverTest.php | 1 + tests/PHPStan/Analyser/data/bug-6399.php | 60 +++++++++++++++++++ .../data/class-implements-out-of-phpstan.php | 5 ++ .../PHPStan/Rules/Variables/UnsetRuleTest.php | 4 -- 28 files changed, 177 insertions(+), 129 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-6399.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 0ea93551c3..6356f5512b 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -3694,27 +3694,12 @@ public function unsetExpression(Expr $expr): self $this->parentScope, ); } elseif ($expr instanceof Expr\ArrayDimFetch && $expr->dim !== null) { - $varType = $this->getType($expr->var); - $constantArrays = TypeUtils::getConstantArrays($varType); - if (count($constantArrays) > 0) { - $unsetArrays = []; - $dimType = $this->getType($expr->dim); - foreach ($constantArrays as $constantArray) { - $unsetArrays[] = $constantArray->unsetOffset($dimType); - } - return $this->specifyExpressionType( - $expr->var, - TypeCombinator::union(...$unsetArrays), - ); - } - - $arrays = TypeUtils::getArrays($varType); - $scope = $this; - if (count($arrays) > 0) { - $scope = $scope->specifyExpressionType($expr->var, TypeCombinator::union(...$arrays)); - } - - return $scope->invalidateExpression($expr->var); + return $this->specifyExpressionType( + $expr->var, + $this->getType($expr->var)->unsetOffset($this->getType($expr->dim)), + )->invalidateExpression( + new FuncCall(new FullyQualified('count'), [new Arg($expr->var)]), + ); } return $this; diff --git a/src/Type/Accessory/AccessoryLiteralStringType.php b/src/Type/Accessory/AccessoryLiteralStringType.php index 39c8c3d651..67e5dd5134 100644 --- a/src/Type/Accessory/AccessoryLiteralStringType.php +++ b/src/Type/Accessory/AccessoryLiteralStringType.php @@ -115,6 +115,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); + } + public function isArray(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryNonEmptyStringType.php b/src/Type/Accessory/AccessoryNonEmptyStringType.php index e5e0134d8b..85de5aed71 100644 --- a/src/Type/Accessory/AccessoryNonEmptyStringType.php +++ b/src/Type/Accessory/AccessoryNonEmptyStringType.php @@ -116,6 +116,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); + } + public function isArray(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryNumericStringType.php b/src/Type/Accessory/AccessoryNumericStringType.php index 689806067c..74768efa0d 100644 --- a/src/Type/Accessory/AccessoryNumericStringType.php +++ b/src/Type/Accessory/AccessoryNumericStringType.php @@ -112,6 +112,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); + } + public function isArray(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/HasOffsetType.php b/src/Type/Accessory/HasOffsetType.php index 4c6006ee8f..8f3da7ff03 100644 --- a/src/Type/Accessory/HasOffsetType.php +++ b/src/Type/Accessory/HasOffsetType.php @@ -114,6 +114,14 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + if ($this->offsetType->isSuperTypeOf($offsetType)->yes()) { + return new ErrorType(); + } + return $this; + } + public function isIterableAtLeastOnce(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/Accessory/NonEmptyArrayType.php b/src/Type/Accessory/NonEmptyArrayType.php index 04936a1b1d..42bccc3008 100644 --- a/src/Type/Accessory/NonEmptyArrayType.php +++ b/src/Type/Accessory/NonEmptyArrayType.php @@ -107,6 +107,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); + } + public function isIterable(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 087e74dc1c..9909d41f43 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -255,6 +255,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni ), new NonEmptyArrayType()); } + public function unsetOffset(Type $offsetType): Type + { + return $this; + } + public function isCallable(): TrinaryLogic { return TrinaryLogic::createMaybe()->and((new StringType())->isSuperTypeOf($this->itemType)); diff --git a/src/Type/BooleanType.php b/src/Type/BooleanType.php index 00ab91ec93..755376be6d 100644 --- a/src/Type/BooleanType.php +++ b/src/Type/BooleanType.php @@ -2,7 +2,6 @@ namespace PHPStan\Type; -use PHPStan\TrinaryLogic; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantFloatType; use PHPStan\Type\Constant\ConstantIntegerType; @@ -11,6 +10,7 @@ use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonIterableTypeTrait; use PHPStan\Type\Traits\NonObjectTypeTrait; +use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait; use PHPStan\Type\Traits\UndecidedBooleanTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; @@ -25,6 +25,7 @@ class BooleanType implements Type use UndecidedBooleanTypeTrait; use UndecidedComparisonTypeTrait; use NonGenericTypeTrait; + use NonOffsetAccessibleTypeTrait; /** @api */ public function __construct() @@ -74,26 +75,6 @@ public function toArray(): Type ); } - public function isOffsetAccessible(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function hasOffsetValueType(Type $offsetType): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getOffsetValueType(Type $offsetType): Type - { - return new ErrorType(); - } - - public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type - { - return new ErrorType(); - } - /** * @param mixed[] $properties */ diff --git a/src/Type/ClosureType.php b/src/Type/ClosureType.php index d6747060e4..fcdc03a5c4 100644 --- a/src/Type/ClosureType.php +++ b/src/Type/ClosureType.php @@ -23,6 +23,7 @@ use PHPStan\Type\Generic\TemplateTypeHelper; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Traits\NonGenericTypeTrait; +use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; use function array_map; use function array_merge; @@ -35,6 +36,7 @@ class ClosureType implements TypeWithClassName, ParametersAcceptor use NonGenericTypeTrait; use UndecidedComparisonTypeTrait; + use NonOffsetAccessibleTypeTrait; private ObjectType $objectType; @@ -228,26 +230,6 @@ public function getIterableValueType(): Type return new ErrorType(); } - public function isOffsetAccessible(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function hasOffsetValueType(Type $offsetType): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getOffsetValueType(Type $offsetType): Type - { - return new ErrorType(); - } - - public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type - { - return new ErrorType(); - } - public function isCallable(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/FloatType.php b/src/Type/FloatType.php index 6c53d3ffb7..b519b38090 100644 --- a/src/Type/FloatType.php +++ b/src/Type/FloatType.php @@ -10,6 +10,7 @@ use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonIterableTypeTrait; use PHPStan\Type\Traits\NonObjectTypeTrait; +use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait; use PHPStan\Type\Traits\UndecidedBooleanTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; use function get_class; @@ -24,6 +25,7 @@ class FloatType implements Type use UndecidedBooleanTypeTrait; use UndecidedComparisonTypeTrait; use NonGenericTypeTrait; + use NonOffsetAccessibleTypeTrait; /** @api */ public function __construct() @@ -109,26 +111,6 @@ public function toArray(): Type ); } - public function isOffsetAccessible(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function hasOffsetValueType(Type $offsetType): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getOffsetValueType(Type $offsetType): Type - { - return new ErrorType(); - } - - public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type - { - return new ErrorType(); - } - public function isArray(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/IntegerType.php b/src/Type/IntegerType.php index 34ec649289..1922e7a079 100644 --- a/src/Type/IntegerType.php +++ b/src/Type/IntegerType.php @@ -2,7 +2,6 @@ namespace PHPStan\Type; -use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryNumericStringType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantIntegerType; @@ -10,6 +9,7 @@ use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonIterableTypeTrait; use PHPStan\Type\Traits\NonObjectTypeTrait; +use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait; use PHPStan\Type\Traits\UndecidedBooleanTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; @@ -24,6 +24,7 @@ class IntegerType implements Type use UndecidedBooleanTypeTrait; use UndecidedComparisonTypeTrait; use NonGenericTypeTrait; + use NonOffsetAccessibleTypeTrait; /** @api */ public function __construct() @@ -75,24 +76,4 @@ public function toArray(): Type ); } - public function isOffsetAccessible(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function hasOffsetValueType(Type $offsetType): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getOffsetValueType(Type $offsetType): Type - { - return new ErrorType(); - } - - public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type - { - return new ErrorType(); - } - } diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 792e447473..32331d84b9 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -380,6 +380,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this->intersectTypes(static fn (Type $type): Type => $type->setOffsetValueType($offsetType, $valueType, $unionValues)); } + public function unsetOffset(Type $offsetType): Type + { + return $this->intersectTypes(static fn (Type $type): Type => $type->unsetOffset($offsetType)); + } + public function isCallable(): TrinaryLogic { return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->isCallable()); diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index e0e7d09d8b..c11e525e9a 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -116,6 +116,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return new self($this->isExplicitMixed); } + public function unsetOffset(Type $offsetType): Type + { + return $this; + } + public function isCallable(): TrinaryLogic { if ( diff --git a/src/Type/NeverType.php b/src/Type/NeverType.php index 7631e7ad59..0d931ebe89 100644 --- a/src/Type/NeverType.php +++ b/src/Type/NeverType.php @@ -171,6 +171,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return new NeverType(); } + public function unsetOffset(Type $offsetType): Type + { + return new NeverType(); + } + public function isCallable(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/NullType.php b/src/Type/NullType.php index f7b5213f9d..3822dcc403 100644 --- a/src/Type/NullType.php +++ b/src/Type/NullType.php @@ -158,6 +158,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $array->setOffsetValueType($offsetType, $valueType, $unionValues); } + public function unsetOffset(Type $offsetType): Type + { + return $this; + } + public function traverse(callable $cb): Type { return $this; diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 192fe6669d..f61468d3c0 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -877,6 +877,15 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + if ($this->isOffsetAccessible()->no()) { + return new ErrorType(); + } + + return $this; + } + public function isCallable(): TrinaryLogic { $parametersAcceptors = $this->findCallableParametersAcceptors(); diff --git a/src/Type/ResourceType.php b/src/Type/ResourceType.php index 7d339209f0..afc4879b72 100644 --- a/src/Type/ResourceType.php +++ b/src/Type/ResourceType.php @@ -2,13 +2,13 @@ namespace PHPStan\Type; -use PHPStan\TrinaryLogic; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Traits\NonCallableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonIterableTypeTrait; use PHPStan\Type\Traits\NonObjectTypeTrait; +use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait; use PHPStan\Type\Traits\TruthyBooleanTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; @@ -23,6 +23,7 @@ class ResourceType implements Type use TruthyBooleanTypeTrait; use NonGenericTypeTrait; use UndecidedComparisonTypeTrait; + use NonOffsetAccessibleTypeTrait; /** @api */ public function __construct() @@ -63,26 +64,6 @@ public function toArray(): Type ); } - public function isOffsetAccessible(): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function hasOffsetValueType(Type $offsetType): TrinaryLogic - { - return TrinaryLogic::createNo(); - } - - public function getOffsetValueType(Type $offsetType): Type - { - return new ErrorType(); - } - - public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type - { - return new ErrorType(); - } - /** * @param mixed[] $properties */ diff --git a/src/Type/StaticType.php b/src/Type/StaticType.php index a9d467f69c..eab5c9fa14 100644 --- a/src/Type/StaticType.php +++ b/src/Type/StaticType.php @@ -317,6 +317,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this->getStaticObjectType()->setOffsetValueType($offsetType, $valueType, $unionValues); } + public function unsetOffset(Type $offsetType): Type + { + return $this->getStaticObjectType()->unsetOffset($offsetType); + } + public function isCallable(): TrinaryLogic { return $this->getStaticObjectType()->isCallable(); diff --git a/src/Type/StrictMixedType.php b/src/Type/StrictMixedType.php index d73b5acad1..9f4e9f9923 100644 --- a/src/Type/StrictMixedType.php +++ b/src/Type/StrictMixedType.php @@ -172,6 +172,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return new ErrorType(); } + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); + } + public function isCallable(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/StringType.php b/src/Type/StringType.php index 4887ec09c1..807ba4a355 100644 --- a/src/Type/StringType.php +++ b/src/Type/StringType.php @@ -72,6 +72,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return new ErrorType(); } + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); + } + public function accepts(Type $type, bool $strictTypes): TrinaryLogic { if ($type instanceof self) { diff --git a/src/Type/Traits/MaybeOffsetAccessibleTypeTrait.php b/src/Type/Traits/MaybeOffsetAccessibleTypeTrait.php index 755bf8c44e..55889eb574 100644 --- a/src/Type/Traits/MaybeOffsetAccessibleTypeTrait.php +++ b/src/Type/Traits/MaybeOffsetAccessibleTypeTrait.php @@ -29,4 +29,9 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this; } + public function unsetOffset(Type $offsetType): Type + { + return $this; + } + } diff --git a/src/Type/Traits/NonOffsetAccessibleTypeTrait.php b/src/Type/Traits/NonOffsetAccessibleTypeTrait.php index 68a703d069..929d8c0002 100644 --- a/src/Type/Traits/NonOffsetAccessibleTypeTrait.php +++ b/src/Type/Traits/NonOffsetAccessibleTypeTrait.php @@ -26,7 +26,12 @@ public function getOffsetValueType(Type $offsetType): Type public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type { - return $this; + return new ErrorType(); + } + + public function unsetOffset(Type $offsetType): Type + { + return new ErrorType(); } } diff --git a/src/Type/Type.php b/src/Type/Type.php index 465c1a1d24..7040b3433d 100644 --- a/src/Type/Type.php +++ b/src/Type/Type.php @@ -71,6 +71,8 @@ public function getOffsetValueType(Type $offsetType): Type; public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type; + public function unsetOffset(Type $offsetType): Type; + public function isCallable(): TrinaryLogic; /** diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 6640032235..4d31f05c9d 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -429,6 +429,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni return $this->unionTypes(static fn (Type $type): Type => $type->setOffsetValueType($offsetType, $valueType, $unionValues)); } + public function unsetOffset(Type $offsetType): Type + { + return $this->unionTypes(static fn (Type $type): Type => $type->unsetOffset($offsetType)); + } + public function isCallable(): TrinaryLogic { return $this->unionResults(static fn (Type $type): TrinaryLogic => $type->isCallable()); diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 647ab3a14e..794b3535b1 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -617,6 +617,7 @@ public function dataFileAsserts(): iterable } yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6404.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6399.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-6399.php b/tests/PHPStan/Analyser/data/bug-6399.php new file mode 100644 index 0000000000..bc3eb70c59 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6399.php @@ -0,0 +1,60 @@ +>|null + */ + private static $threadLocalStorage = null; + + final public function __destruct(){ + assertType('ArrayObject>|null', self::$threadLocalStorage); + if(self::$threadLocalStorage !== null){ + assertType('ArrayObject>', self::$threadLocalStorage); + if (isset(self::$threadLocalStorage[$h = spl_object_id($this)])) { + assertType('ArrayObject>&hasOffset(int)', self::$threadLocalStorage); + unset(self::$threadLocalStorage[$h]); + assertType('ArrayObject>', self::$threadLocalStorage); + if(self::$threadLocalStorage->count() === 0){ + self::$threadLocalStorage = null; + } + } + } + } + + public function doFoo(): void + { + if(self::$threadLocalStorage === null) { + return; + } + + assertType('ArrayObject>', self::$threadLocalStorage); + if (isset(self::$threadLocalStorage[1])) { + assertType('ArrayObject>&hasOffset(1)', self::$threadLocalStorage); + } else { + assertType('ArrayObject>', self::$threadLocalStorage); + } + + assertType('ArrayObject>', self::$threadLocalStorage); + if (isset(self::$threadLocalStorage[1]) && isset(self::$threadLocalStorage[2])) { + assertType('ArrayObject>&hasOffset(1)&hasOffset(2)', self::$threadLocalStorage); + unset(self::$threadLocalStorage[2]); + assertType('ArrayObject>&hasOffset(1)', self::$threadLocalStorage); + } + } + + /** + * @param non-empty-array $a + * @return void + */ + public function doBar(array $a): void + { + assertType('non-empty-array', $a); + unset($a[1]); + assertType('array', $a); + } + +} diff --git a/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php b/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php index aef4bb0cb6..0804ed77dd 100644 --- a/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php +++ b/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php @@ -174,6 +174,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni // TODO: Implement setOffsetValueType() method. } + public function unsetOffset(Type $offsetType): Type + { + // TODO: Implement unsetOffset() method. + } + public function isCallable(): \PHPStan\TrinaryLogic { // TODO: Implement isCallable() method. diff --git a/tests/PHPStan/Rules/Variables/UnsetRuleTest.php b/tests/PHPStan/Rules/Variables/UnsetRuleTest.php index 4cbc824043..370fe14ea6 100644 --- a/tests/PHPStan/Rules/Variables/UnsetRuleTest.php +++ b/tests/PHPStan/Rules/Variables/UnsetRuleTest.php @@ -36,10 +36,6 @@ public function testUnsetRule(): void 'Cannot unset offset \'c\' on 1.', 18, ], - [ - 'Cannot unset offset \'b\' on 1.', - 18, - ], [ 'Cannot unset offset \'string\' on iterable.', 31,