Skip to content

Commit 6b9535d

Browse files
phpstan-botclaude
andcommitted
Fix BooleanNot false positive for $this-dependent checks in traits
When ImpossibleCheckTypeHelper returns null (uncertain) for trait+$this function calls, ConstantConditionRuleHelper fell through to scope type resolution which still gave a definitive answer. This caused BooleanNotConstantConditionRule to report "always false" for negated type checks like `! is_string($this->message)` in traits. The fix skips function calls with $this-dependent arguments in trait context in ConstantConditionRuleHelper::shouldSkip(), consistent with how ImpossibleCheckTypeHelper already handles these cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent af1247c commit 6b9535d

4 files changed

Lines changed: 71 additions & 1 deletion

File tree

src/Rules/Comparison/ConstantConditionRuleHelper.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ private function shouldSkip(Scope $scope, Expr $expr): bool
6161
if ($isAlways !== null) {
6262
return true;
6363
}
64+
65+
if ($scope->isInTrait()) {
66+
foreach ($expr->getArgs() as $arg) {
67+
if (ImpossibleCheckTypeHelper::isExpressionDependentOnThis($arg->value)) {
68+
return true;
69+
}
70+
}
71+
}
6472
}
6573

6674
return false;

src/Rules/Comparison/ImpossibleCheckTypeHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ private static function isSpecified(Scope $scope, Expr $node, Expr $expr): bool
391391
) && $scope->hasExpressionType($expr)->yes();
392392
}
393393

394-
private static function isExpressionDependentOnThis(Expr $expr): bool
394+
public static function isExpressionDependentOnThis(Expr $expr): bool
395395
{
396396
if ($expr instanceof Expr\Variable && $expr->name === 'this') {
397397
return true;

tests/PHPStan/Rules/Comparison/BooleanNotConstantConditionRuleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,10 @@ public function testBug6702(): void
236236
$this->analyse([__DIR__ . '/data/bug-6702.php'], []);
237237
}
238238

239+
public function testBug13023(): void
240+
{
241+
$this->treatPhpDocTypesAsCertain = true;
242+
$this->analyse([__DIR__ . '/data/bug-13023.php'], []);
243+
}
244+
239245
}

tests/PHPStan/Rules/Comparison/data/bug-13023.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,59 @@ public function getRandom(): int
110110
return $value;
111111
}
112112
}
113+
114+
class HelloWorld
115+
{
116+
use SomeTrait;
117+
118+
public string $message = 'Hello';
119+
120+
public function foo(): void
121+
{
122+
$this->bar();
123+
}
124+
}
125+
126+
class EmptyClass {
127+
use SomeTrait;
128+
}
129+
130+
trait SomeTrait {
131+
public function bar(): void
132+
{
133+
if (property_exists($this, 'message')) {
134+
if (! is_string($this->message)) {
135+
return;
136+
}
137+
138+
echo $this->message . "\n";
139+
}
140+
}
141+
}
142+
143+
class SomeClass9
144+
{
145+
use MyTrait5;
146+
147+
public string $prop = 'foo';
148+
}
149+
150+
class SomeClass10
151+
{
152+
use MyTrait5;
153+
154+
public int $prop = 1;
155+
}
156+
157+
trait MyTrait5
158+
{
159+
public function getRandom(): int
160+
{
161+
$value = random_int(1, 100);
162+
if (!\is_int($this->prop)) {
163+
return $value;
164+
}
165+
166+
return $value * $value;
167+
}
168+
}

0 commit comments

Comments
 (0)