Skip to content

Commit 65cc372

Browse files
phpstan-botclaude
andcommitted
Apply same non-object guard to hasProperty, hasInstanceProperty, hasStaticProperty, hasConstant
The same lazyMaxMin early-return issue from hasMethod() affects these methods: MixedType returns Yes for all of them, which dominates a non-object type's No via the early return in lazyMaxMin. This causes incorrect Yes results for intersection types like int&T where T is a template bounded by mixed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 92cee1b commit 65cc372

1 file changed

Lines changed: 24 additions & 0 deletions

File tree

src/Type/IntersectionType.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,12 @@ public function canAccessProperties(): TrinaryLogic
552552

553553
public function hasProperty(string $propertyName): TrinaryLogic
554554
{
555+
foreach ($this->types as $type) {
556+
if ($type->isObject()->no() && $type->hasProperty($propertyName)->no()) {
557+
return TrinaryLogic::createNo();
558+
}
559+
}
560+
555561
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasProperty($propertyName));
556562
}
557563

@@ -585,6 +591,12 @@ public function getUnresolvedPropertyPrototype(string $propertyName, ClassMember
585591

586592
public function hasInstanceProperty(string $propertyName): TrinaryLogic
587593
{
594+
foreach ($this->types as $type) {
595+
if ($type->isObject()->no() && $type->hasInstanceProperty($propertyName)->no()) {
596+
return TrinaryLogic::createNo();
597+
}
598+
}
599+
588600
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasInstanceProperty($propertyName));
589601
}
590602

@@ -618,6 +630,12 @@ public function getUnresolvedInstancePropertyPrototype(string $propertyName, Cla
618630

619631
public function hasStaticProperty(string $propertyName): TrinaryLogic
620632
{
633+
foreach ($this->types as $type) {
634+
if ($type->isObject()->no() && $type->hasStaticProperty($propertyName)->no()) {
635+
return TrinaryLogic::createNo();
636+
}
637+
}
638+
621639
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasStaticProperty($propertyName));
622640
}
623641

@@ -700,6 +718,12 @@ public function canAccessConstants(): TrinaryLogic
700718

701719
public function hasConstant(string $constantName): TrinaryLogic
702720
{
721+
foreach ($this->types as $type) {
722+
if ($type->isObject()->no() && $type->hasConstant($constantName)->no()) {
723+
return TrinaryLogic::createNo();
724+
}
725+
}
726+
703727
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasConstant($constantName));
704728
}
705729

0 commit comments

Comments
 (0)