Skip to content

Commit

Permalink
Implement Type::isOffsetAccessLegal() to detect offset access that …
Browse files Browse the repository at this point in the history
…crashes even in `isset()`
  • Loading branch information
rajyan committed May 7, 2024
1 parent 78d3d99 commit e55a83f
Show file tree
Hide file tree
Showing 41 changed files with 404 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Rules/Arrays/NonexistentOffsetInArrayDimFetchRule.php
Expand Up @@ -64,7 +64,7 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if ($scope->isUndefinedExpressionAllowed($node) && !$isOffsetAccessible->no()) {
if ($scope->isUndefinedExpressionAllowed($node) && $isOffsetAccessibleType->isOffsetAccessLegal()->yes()) {
return [];
}

Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryArrayListType.php
Expand Up @@ -142,6 +142,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $this->getIterableKeyType()->isSuperTypeOf($offsetType)->and(TrinaryLogic::createMaybe());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryLiteralStringType.php
Expand Up @@ -132,6 +132,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryNonEmptyStringType.php
Expand Up @@ -134,6 +134,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryNonFalsyStringType.php
Expand Up @@ -134,6 +134,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryNumericStringType.php
Expand Up @@ -137,6 +137,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasMethodType.php
Expand Up @@ -120,6 +120,11 @@ public function describe(VerbosityLevel $level): string
return sprintf('hasMethod(%s)', $this->methodName);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function hasMethod(string $methodName): TrinaryLogic
{
if ($this->getCanonicalMethodName() === strtolower($methodName)) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasOffsetType.php
Expand Up @@ -138,6 +138,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
if ($offsetType->isConstantScalarValue()->yes() && $offsetType->equals($this->offsetType)) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasOffsetValueType.php
Expand Up @@ -146,6 +146,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
if ($offsetType->isConstantScalarValue()->yes() && $offsetType->equals($this->offsetType)) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasPropertyType.php
Expand Up @@ -118,6 +118,11 @@ public function describe(VerbosityLevel $level): string
return sprintf('hasProperty(%s)', $this->propertyName);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function hasProperty(string $propertyName): TrinaryLogic
{
if ($this->propertyName === $propertyName) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/NonEmptyArrayType.php
Expand Up @@ -139,6 +139,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return TrinaryLogic::createMaybe();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/OversizedArrayType.php
Expand Up @@ -135,6 +135,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return TrinaryLogic::createMaybe();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ArrayType.php
Expand Up @@ -393,6 +393,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
$offsetType = $offsetType->toArrayKey();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/BooleanType.php
Expand Up @@ -96,6 +96,11 @@ public function toArrayKey(): Type
return new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function isNull(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/CallableType.php
Expand Up @@ -324,6 +324,11 @@ public function toArrayKey(): Type
return new ErrorType();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function getTemplateTypeMap(): TemplateTypeMap
{
return $this->templateTypeMap;
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ClosureType.php
Expand Up @@ -261,6 +261,11 @@ function (): string {
);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isObject(): TrinaryLogic
{
return $this->objectType->isObject();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/FloatType.php
Expand Up @@ -144,6 +144,11 @@ public function toArrayKey(): Type
return new IntegerType();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function isNull(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IntegerType.php
Expand Up @@ -96,6 +96,11 @@ public function toArrayKey(): Type
return $this;
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function isNull(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IntersectionType.php
Expand Up @@ -672,6 +672,11 @@ public function isOffsetAccessible(): TrinaryLogic
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->isOffsetAccessible());
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->isOffsetAccessLegal());
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
if ($this->isList()->yes() && $this->isIterableAtLeastOnce()->yes()) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IterableType.php
Expand Up @@ -242,6 +242,11 @@ public function toArrayKey(): Type
return new ErrorType();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function isIterable(): TrinaryLogic
{
return TrinaryLogic::createYes();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/MixedType.php
Expand Up @@ -566,6 +566,16 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
if ($this->subtractedType !== null) {
if ($this->subtractedType->isSuperTypeOf(new ObjectWithoutClassType())->yes()) {
return TrinaryLogic::createYes();
}
}
return TrinaryLogic::createMaybe();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
if ($this->isOffsetAccessible()->no()) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/NeverType.php
Expand Up @@ -257,6 +257,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return TrinaryLogic::createYes();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/NonexistentParentClassType.php
Expand Up @@ -152,6 +152,11 @@ public function toArrayKey(): Type
return new ErrorType();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function isScalar(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/NullType.php
Expand Up @@ -174,6 +174,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ObjectShapeType.php
Expand Up @@ -428,6 +428,11 @@ public function describe(VerbosityLevel $level): string
);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function getEnumCases(): array
{
return [];
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ObjectType.php
Expand Up @@ -1099,6 +1099,11 @@ public function isOffsetAccessible(): TrinaryLogic
);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return $this->isOffsetAccessible();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
if ($this->isInstanceOf(ArrayAccess::class)->yes()) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ObjectWithoutClassType.php
Expand Up @@ -140,6 +140,11 @@ function () use ($level): string {
);
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

public function getEnumCases(): array
{
return [];
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ResourceType.php
Expand Up @@ -86,6 +86,11 @@ public function toArrayKey(): Type
return new ErrorType();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function isScalar(): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/StaticType.php
Expand Up @@ -375,6 +375,11 @@ public function isOffsetAccessible(): TrinaryLogic
return $this->getStaticObjectType()->isOffsetAccessible();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return $this->getStaticObjectType()->isOffsetAccessLegal();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $this->getStaticObjectType()->hasOffsetValueType($offsetType);
Expand Down
5 changes: 5 additions & 0 deletions src/Type/StrictMixedType.php
Expand Up @@ -310,6 +310,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return TrinaryLogic::createNo();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/StringType.php
Expand Up @@ -56,6 +56,11 @@ public function isOffsetAccessible(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return TrinaryLogic::createYes();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Traits/LateResolvableTypeTrait.php
Expand Up @@ -222,6 +222,11 @@ public function isOffsetAccessible(): TrinaryLogic
return $this->resolve()->isOffsetAccessible();
}

public function isOffsetAccessLegal(): TrinaryLogic
{
return $this->resolve()->isOffsetAccessLegal();
}

public function hasOffsetValueType(Type $offsetType): TrinaryLogic
{
return $this->resolve()->hasOffsetValueType($offsetType);
Expand Down
2 changes: 2 additions & 0 deletions src/Type/Type.php
Expand Up @@ -131,6 +131,8 @@ public function isList(): TrinaryLogic;

public function isOffsetAccessible(): TrinaryLogic;

public function isOffsetAccessLegal(): TrinaryLogic;

public function hasOffsetValueType(Type $offsetType): TrinaryLogic;

public function getOffsetValueType(Type $offsetType): Type;
Expand Down

0 comments on commit e55a83f

Please sign in to comment.