diff --git a/src/Rules/Classes/ExistingClassInInstanceOfRule.php b/src/Rules/Classes/ExistingClassInInstanceOfRule.php index 775eadf450..c04a3d4b11 100644 --- a/src/Rules/Classes/ExistingClassInInstanceOfRule.php +++ b/src/Rules/Classes/ExistingClassInInstanceOfRule.php @@ -10,6 +10,8 @@ use PHPStan\Rules\ClassNameNodePair; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\VerbosityLevel; +use function array_merge; use function in_array; use function sprintf; use function strtolower; @@ -57,6 +59,8 @@ public function processNode(Node $node, Scope $scope): array return []; } + $errors = []; + if (!$this->reflectionProvider->hasClass($name)) { if ($scope->isInClassExists($name)) { return []; @@ -66,10 +70,24 @@ public function processNode(Node $node, Scope $scope): array RuleErrorBuilder::message(sprintf('Class %s not found.', $name))->line($class->getLine())->discoveringSymbolsTip()->build(), ]; } elseif ($this->checkClassCaseSensitivity) { - return $this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($name, $class)]); + $errors = array_merge( + $errors, + $this->classCaseSensitivityCheck->checkClassNames([new ClassNameNodePair($name, $class)]), + ); + } + + $classReflection = $this->reflectionProvider->getClass($name); + $expressionType = $scope->getType($node->expr); + + if ($classReflection->isTrait()) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Instanceof between %s and trait %s will always evaluate to false.', + $expressionType->describe(VerbosityLevel::typeOnly()), + $name, + ))->build(); } - return []; + return $errors; } } diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 9febf101f3..d1b7b692a5 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -309,6 +309,10 @@ public function isSuperTypeOf(Type $type): TrinaryLogic $thisClassReflection = $this->getClassReflection(); $thatClassReflection = $reflectionProvider->getClass($thatClassName); + if ($thisClassReflection->isTrait() || $thatClassReflection->isTrait()) { + return TrinaryLogic::createNo(); + } + if ($thisClassReflection->getName() === $thatClassReflection->getName()) { return self::$superTypes[$thisDescription][$description] = $transformResult(TrinaryLogic::createYes()); } diff --git a/tests/PHPStan/Rules/Classes/ExistingClassInInstanceOfRuleTest.php b/tests/PHPStan/Rules/Classes/ExistingClassInInstanceOfRuleTest.php index e346e8cb1a..5533f01879 100644 --- a/tests/PHPStan/Rules/Classes/ExistingClassInInstanceOfRuleTest.php +++ b/tests/PHPStan/Rules/Classes/ExistingClassInInstanceOfRuleTest.php @@ -60,4 +60,14 @@ public function testClassExists(): void $this->analyse([__DIR__ . '/data/instanceof-class-exists.php'], []); } + public function testBug7720(): void + { + $this->analyse([__DIR__ . '/data/bug-7720.php'], [ + [ + 'Instanceof between mixed and trait Bug7720\FooBar will always evaluate to false.', + 17, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Classes/data/bug-7720.php b/tests/PHPStan/Rules/Classes/data/bug-7720.php new file mode 100644 index 0000000000..ff20bd0e61 --- /dev/null +++ b/tests/PHPStan/Rules/Classes/data/bug-7720.php @@ -0,0 +1,21 @@ +foo(); + } + } +} diff --git a/tests/PHPStan/Type/ObjectTypeTest.php b/tests/PHPStan/Type/ObjectTypeTest.php index 204ac35e6a..3cb3212860 100644 --- a/tests/PHPStan/Type/ObjectTypeTest.php +++ b/tests/PHPStan/Type/ObjectTypeTest.php @@ -26,6 +26,7 @@ use PHPStan\Type\Generic\TemplateTypeFactory; use PHPStan\Type\Generic\TemplateTypeScope; use PHPStan\Type\Generic\TemplateTypeVariance; +use PHPStan\Type\Traits\ConstantNumericComparisonTypeTrait; use SimpleXMLElement; use stdClass; use Throwable; @@ -418,6 +419,16 @@ public function dataIsSuperTypeOf(): array new ObjectType(ExtendsThrowable::class), TrinaryLogic::createMaybe(), ], + 59 => [ + new ObjectType(DateTime::class), + new ObjectType(ConstantNumericComparisonTypeTrait::class), + TrinaryLogic::createNo(), + ], + 60 => [ + new ObjectType(ConstantNumericComparisonTypeTrait::class), + new ObjectType(DateTime::class), + TrinaryLogic::createNo(), + ], ]; }