diff --git a/src/Type/Accessory/HasMethodType.php b/src/Type/Accessory/HasMethodType.php index 2201e0e5bef..f25e8c844ea 100644 --- a/src/Type/Accessory/HasMethodType.php +++ b/src/Type/Accessory/HasMethodType.php @@ -3,30 +3,39 @@ namespace PHPStan\Type\Accessory; use Closure; +use PHPStan\Php\PhpVersion; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Reflection\ClassMemberAccessAnswerer; use PHPStan\Reflection\Dummy\DummyMethodReflection; use PHPStan\Reflection\ExtendedMethodReflection; -use PHPStan\Reflection\TrivialParametersAcceptor; use PHPStan\Reflection\Type\CallbackUnresolvedMethodPrototypeReflection; use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection; use PHPStan\TrinaryLogic; use PHPStan\Type\AcceptsResult; +use PHPStan\Type\BooleanType; use PHPStan\Type\CompoundType; use PHPStan\Type\Enum\EnumCaseObjectType; use PHPStan\Type\ErrorType; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\IntersectionType; use PHPStan\Type\IsSuperTypeOfResult; +use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; +use PHPStan\Type\Traits\MaybeCallableTypeTrait; +use PHPStan\Type\Traits\MaybeIterableTypeTrait; +use PHPStan\Type\Traits\MaybeObjectTypeTrait; +use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait; +use PHPStan\Type\Traits\MaybeStringTypeTrait; +use PHPStan\Type\Traits\NonArrayTypeTrait; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonRemoveableTypeTrait; -use PHPStan\Type\Traits\ObjectTypeTrait; +use PHPStan\Type\Traits\TruthyBooleanTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use function sprintf; @@ -35,7 +44,13 @@ class HasMethodType implements AccessoryType, CompoundType { - use ObjectTypeTrait; + use MaybeCallableTypeTrait; + use MaybeIterableTypeTrait; + use MaybeObjectTypeTrait; + use MaybeOffsetAccessibleTypeTrait; + use MaybeStringTypeTrait; + use NonArrayTypeTrait; + use TruthyBooleanTypeTrait; use NonGenericTypeTrait; use UndecidedComparisonCompoundTypeTrait; use NonRemoveableTypeTrait; @@ -82,6 +97,10 @@ public function accepts(Type $type, bool $strictTypes): AcceptsResult public function isSuperTypeOf(Type $type): IsSuperTypeOfResult { + if ($type instanceof CompoundType) { + return $type->isSubTypeOf($this); + } + return new IsSuperTypeOfResult($type->hasMethod($this->methodName), []); } @@ -158,6 +177,86 @@ public function isCallable(): TrinaryLogic return TrinaryLogic::createMaybe(); } + public function isNull(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isConstantValue(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isConstantScalarValue(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getConstantScalarTypes(): array + { + return []; + } + + public function getConstantScalarValues(): array + { + return []; + } + + public function isTrue(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isFalse(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isBoolean(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isFloat(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isInteger(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getClassStringObjectType(): Type + { + return $this; + } + + public function getObjectTypeOrClassStringObjectType(): Type + { + return $this; + } + + public function isVoid(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType + { + return new BooleanType(); + } + + public function toNumber(): Type + { + return new ErrorType(); + } + + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { if ($this->getCanonicalMethodName() === '__tostring') { @@ -167,11 +266,33 @@ public function toString(): Type return new ErrorType(); } - public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array + public function toInteger(): Type { - return [ - new TrivialParametersAcceptor(), - ]; + return new ErrorType(); + } + + public function toFloat(): Type + { + return new ErrorType(); + } + + public function toArray(): Type + { + return new MixedType(); + } + + public function toArrayKey(): Type + { + return new ErrorType(); + } + + public function toCoercedArgumentType(bool $strictTypes): Type + { + if (!$strictTypes) { + return TypeCombinator::union($this, $this->toString()); + } + + return $this; } public function getEnumCases(): array diff --git a/src/Type/Accessory/HasOffsetType.php b/src/Type/Accessory/HasOffsetType.php index b6757fefb62..faf598ac8c5 100644 --- a/src/Type/Accessory/HasOffsetType.php +++ b/src/Type/Accessory/HasOffsetType.php @@ -22,6 +22,7 @@ use PHPStan\Type\Traits\MaybeCallableTypeTrait; use PHPStan\Type\Traits\MaybeIterableTypeTrait; use PHPStan\Type\Traits\MaybeObjectTypeTrait; +use PHPStan\Type\Traits\MaybeStringTypeTrait; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonRemoveableTypeTrait; @@ -40,6 +41,7 @@ class HasOffsetType implements CompoundType, AccessoryType use MaybeCallableTypeTrait; use MaybeIterableTypeTrait; use MaybeObjectTypeTrait; + use MaybeStringTypeTrait; use TruthyBooleanTypeTrait; use NonGenericTypeTrait; use UndecidedComparisonCompoundTypeTrait; @@ -73,11 +75,6 @@ public function getObjectClassReflections(): array return []; } - public function getConstantStrings(): array - { - return []; - } - public function accepts(Type $type, bool $strictTypes): AcceptsResult { if ($type instanceof CompoundType) { @@ -285,46 +282,6 @@ public function isInteger(): TrinaryLogic return TrinaryLogic::createNo(); } - public function isString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isNumericString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isNonEmptyString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isNonFalsyString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isLiteralString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isLowercaseString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isClassString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isUppercaseString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - public function getClassStringObjectType(): Type { return new ObjectWithoutClassType(); @@ -340,11 +297,6 @@ public function isVoid(): TrinaryLogic return TrinaryLogic::createNo(); } - public function isScalar(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { return new BooleanType(); diff --git a/src/Type/Accessory/HasOffsetValueType.php b/src/Type/Accessory/HasOffsetValueType.php index 66872dc2f3e..eadf8f2a173 100644 --- a/src/Type/Accessory/HasOffsetValueType.php +++ b/src/Type/Accessory/HasOffsetValueType.php @@ -26,6 +26,7 @@ use PHPStan\Type\Traits\MaybeCallableTypeTrait; use PHPStan\Type\Traits\MaybeIterableTypeTrait; use PHPStan\Type\Traits\MaybeObjectTypeTrait; +use PHPStan\Type\Traits\MaybeStringTypeTrait; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonRemoveableTypeTrait; @@ -44,6 +45,7 @@ class HasOffsetValueType implements CompoundType, AccessoryType use MaybeCallableTypeTrait; use MaybeIterableTypeTrait; use MaybeObjectTypeTrait; + use MaybeStringTypeTrait; use TruthyBooleanTypeTrait; use NonGenericTypeTrait; use UndecidedComparisonCompoundTypeTrait; @@ -79,11 +81,6 @@ public function getObjectClassReflections(): array return []; } - public function getConstantStrings(): array - { - return []; - } - public function accepts(Type $type, bool $strictTypes): AcceptsResult { if ($type instanceof CompoundType) { @@ -373,46 +370,6 @@ public function isInteger(): TrinaryLogic return TrinaryLogic::createNo(); } - public function isString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isNumericString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isNonEmptyString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isNonFalsyString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isLiteralString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isLowercaseString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isUppercaseString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - - public function isClassString(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - public function getClassStringObjectType(): Type { return new ObjectWithoutClassType(); @@ -428,11 +385,6 @@ public function isVoid(): TrinaryLogic return TrinaryLogic::createNo(); } - public function isScalar(): TrinaryLogic - { - return TrinaryLogic::createMaybe(); - } - public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { return new BooleanType(); diff --git a/src/Type/Accessory/HasPropertyType.php b/src/Type/Accessory/HasPropertyType.php index 9c471e44cd7..86ed660242a 100644 --- a/src/Type/Accessory/HasPropertyType.php +++ b/src/Type/Accessory/HasPropertyType.php @@ -2,24 +2,33 @@ namespace PHPStan\Type\Accessory; +use PHPStan\Php\PhpVersion; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Reflection\ClassMemberAccessAnswerer; -use PHPStan\Reflection\TrivialParametersAcceptor; use PHPStan\TrinaryLogic; use PHPStan\Type\AcceptsResult; +use PHPStan\Type\BooleanType; use PHPStan\Type\CompoundType; use PHPStan\Type\Enum\EnumCaseObjectType; use PHPStan\Type\ErrorType; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\IntersectionType; use PHPStan\Type\IsSuperTypeOfResult; +use PHPStan\Type\MixedType; +use PHPStan\Type\Traits\MaybeCallableTypeTrait; +use PHPStan\Type\Traits\MaybeIterableTypeTrait; +use PHPStan\Type\Traits\MaybeObjectTypeTrait; +use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait; +use PHPStan\Type\Traits\MaybeStringTypeTrait; +use PHPStan\Type\Traits\NonArrayTypeTrait; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonRemoveableTypeTrait; -use PHPStan\Type\Traits\ObjectTypeTrait; +use PHPStan\Type\Traits\TruthyBooleanTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use function sprintf; @@ -27,7 +36,13 @@ class HasPropertyType implements AccessoryType, CompoundType { - use ObjectTypeTrait; + use MaybeCallableTypeTrait; + use MaybeIterableTypeTrait; + use MaybeObjectTypeTrait; + use MaybeOffsetAccessibleTypeTrait; + use MaybeStringTypeTrait; + use NonArrayTypeTrait; + use TruthyBooleanTypeTrait; use NonGenericTypeTrait; use UndecidedComparisonCompoundTypeTrait; use NonRemoveableTypeTrait; @@ -58,11 +73,6 @@ public function getClassStringType(): Type return new GenericClassStringType($this); } - public function getConstantStrings(): array - { - return []; - } - public function getPropertyName(): string { return $this->propertyName; @@ -79,6 +89,10 @@ public function accepts(Type $type, bool $strictTypes): AcceptsResult public function isSuperTypeOf(Type $type): IsSuperTypeOfResult { + if ($type instanceof CompoundType) { + return $type->isSubTypeOf($this); + } + return new IsSuperTypeOfResult( $type->hasInstanceProperty($this->propertyName)->or($type->hasStaticProperty($this->propertyName)), [], @@ -146,9 +160,118 @@ public function hasStaticProperty(string $propertyName): TrinaryLogic return TrinaryLogic::createMaybe(); } - public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array + public function isNull(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isConstantValue(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isConstantScalarValue(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getConstantScalarTypes(): array + { + return []; + } + + public function getConstantScalarValues(): array + { + return []; + } + + public function isTrue(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isFalse(): TrinaryLogic { - return [new TrivialParametersAcceptor()]; + return TrinaryLogic::createNo(); + } + + public function isBoolean(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isFloat(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function isInteger(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function getClassStringObjectType(): Type + { + return $this; + } + + public function getObjectTypeOrClassStringObjectType(): Type + { + return $this; + } + + public function isVoid(): TrinaryLogic + { + return TrinaryLogic::createNo(); + } + + public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType + { + return new BooleanType(); + } + + public function toNumber(): Type + { + return new ErrorType(); + } + + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + + public function toString(): Type + { + return new ErrorType(); + } + + public function toInteger(): Type + { + return new ErrorType(); + } + + public function toFloat(): Type + { + return new ErrorType(); + } + + public function toArray(): Type + { + return new MixedType(); + } + + public function toArrayKey(): Type + { + return new ErrorType(); + } + + public function toCoercedArgumentType(bool $strictTypes): Type + { + if (!$strictTypes) { + return TypeCombinator::union($this, $this->toString()); + } + + return $this; } public function getEnumCases(): array diff --git a/src/Type/Traits/MaybeStringTypeTrait.php b/src/Type/Traits/MaybeStringTypeTrait.php new file mode 100644 index 00000000000..caa9abacb42 --- /dev/null +++ b/src/Type/Traits/MaybeStringTypeTrait.php @@ -0,0 +1,60 @@ +analyse([__DIR__ . '/data/bug-12063.php'], []); } + public function testBug8217(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-8217.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-8217.php b/tests/PHPStan/Rules/Comparison/data/bug-8217.php new file mode 100644 index 00000000000..7da3fe99468 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-8217.php @@ -0,0 +1,58 @@ +