Skip to content

Commit 9dd5b64

Browse files
committed
ImpossibleInstanceOfRule - respect reportAlwaysTrueInLastCondition
1 parent 4984270 commit 9dd5b64

File tree

4 files changed

+86
-9
lines changed

4 files changed

+86
-9
lines changed

conf/config.level4.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ services:
3737
arguments:
3838
checkAlwaysTrueInstanceof: %checkAlwaysTrueInstanceof%
3939
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
40+
reportAlwaysTrueInLastCondition: %reportAlwaysTrueInLastCondition%
4041
tags:
4142
- phpstan.rules.rule
4243

src/Rules/Classes/ImpossibleInstanceOfRule.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class ImpossibleInstanceOfRule implements Rule
2424
public function __construct(
2525
private bool $checkAlwaysTrueInstanceof,
2626
private bool $treatPhpDocTypesAsCertain,
27+
private bool $reportAlwaysTrueInLastCondition,
2728
)
2829
{
2930
}
@@ -84,19 +85,22 @@ public function processNode(Node $node, Scope $scope): array
8485
)))->build(),
8586
];
8687
} elseif ($this->checkAlwaysTrueInstanceof) {
87-
if ($node->getAttribute(LastConditionVisitor::ATTRIBUTE_NAME) === true) {
88+
$isLast = $node->getAttribute(LastConditionVisitor::ATTRIBUTE_NAME);
89+
if ($isLast === true && !$this->reportAlwaysTrueInLastCondition) {
8890
return [];
8991
}
9092

9193
$exprType = $this->treatPhpDocTypesAsCertain ? $scope->getType($node->expr) : $scope->getNativeType($node->expr);
94+
$errorBuilder = $addTip(RuleErrorBuilder::message(sprintf(
95+
'Instanceof between %s and %s will always evaluate to true.',
96+
$exprType->describe(VerbosityLevel::typeOnly()),
97+
$classType->describe(VerbosityLevel::getRecommendedLevelByType($classType)),
98+
)));
99+
if ($isLast === false && !$this->reportAlwaysTrueInLastCondition) {
100+
$errorBuilder->tip('Remove remaining cases below this one and this error will disappear too.');
101+
}
92102

93-
return [
94-
$addTip(RuleErrorBuilder::message(sprintf(
95-
'Instanceof between %s and %s will always evaluate to true.',
96-
$exprType->describe(VerbosityLevel::typeOnly()),
97-
$classType->describe(VerbosityLevel::getRecommendedLevelByType($classType)),
98-
)))->build(),
99-
];
103+
return [$errorBuilder->build()];
100104
}
101105

102106
return [];

tests/PHPStan/Rules/Classes/ImpossibleInstanceOfRuleTest.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ class ImpossibleInstanceOfRuleTest extends RuleTestCase
1616

1717
private bool $treatPhpDocTypesAsCertain;
1818

19+
private bool $reportAlwaysTrueInLastCondition = false;
20+
1921
protected function getRule(): Rule
2022
{
21-
return new ImpossibleInstanceOfRule($this->checkAlwaysTrueInstanceOf, $this->treatPhpDocTypesAsCertain);
23+
return new ImpossibleInstanceOfRule($this->checkAlwaysTrueInstanceOf, $this->treatPhpDocTypesAsCertain, $this->reportAlwaysTrueInLastCondition);
2224
}
2325

2426
protected function shouldTreatPhpDocTypesAsCertain(): bool
@@ -378,10 +380,12 @@ public function testBug8042(): void
378380
[
379381
'Instanceof between Bug8042\B and Bug8042\B will always evaluate to true.',
380382
18,
383+
'Remove remaining cases below this one and this error will disappear too.',
381384
],
382385
[
383386
'Instanceof between Bug8042\B and Bug8042\B will always evaluate to true.',
384387
26,
388+
'Remove remaining cases below this one and this error will disappear too.',
385389
],
386390
]);
387391
}
@@ -417,6 +421,7 @@ public function testUnreachableIfBranches(): void
417421
[
418422
'Instanceof between stdClass and stdClass will always evaluate to true.',
419423
37,
424+
'Remove remaining cases below this one and this error will disappear too.',
420425
],
421426
]);
422427
}
@@ -433,10 +438,12 @@ public function testIfBranchesDoNotReportPhpDoc(): void
433438
[
434439
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
435440
26,
441+
'Remove remaining cases below this one and this error will disappear too.',
436442
],
437443
[
438444
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
439445
36,
446+
'Remove remaining cases below this one and this error will disappear too.',
440447
],
441448
]);
442449
}
@@ -454,10 +461,12 @@ public function testIfBranchesReportPhpDoc(): void
454461
[
455462
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
456463
26,
464+
'Remove remaining cases below this one and this error will disappear too.',
457465
],
458466
[
459467
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
460468
36,
469+
'Remove remaining cases below this one and this error will disappear too.',
461470
],
462471
[
463472
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
@@ -467,10 +476,12 @@ public function testIfBranchesReportPhpDoc(): void
467476
[
468477
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
469478
52,
479+
'Remove remaining cases below this one and this error will disappear too.',
470480
],
471481
[
472482
'Instanceof between UnreachableIfBranchesNotPhpDoc\Foo and UnreachableIfBranchesNotPhpDoc\Foo will always evaluate to true.',
473483
62,
484+
'Remove remaining cases below this one and this error will disappear too.',
474485
],
475486
]);
476487
}
@@ -541,4 +552,37 @@ public function testBug4689(): void
541552
$this->analyse([__DIR__ . '/data/bug-4689.php'], []);
542553
}
543554

555+
public function dataReportAlwaysTrueInLastCondition(): iterable
556+
{
557+
yield [false, [
558+
[
559+
'Instanceof between Exception and Exception will always evaluate to true.',
560+
21,
561+
'Remove remaining cases below this one and this error will disappear too.',
562+
],
563+
]];
564+
yield [true, [
565+
[
566+
'Instanceof between Exception and Exception will always evaluate to true.',
567+
12,
568+
],
569+
[
570+
'Instanceof between Exception and Exception will always evaluate to true.',
571+
21,
572+
],
573+
]];
574+
}
575+
576+
/**
577+
* @dataProvider dataReportAlwaysTrueInLastCondition
578+
* @param list<array{0: string, 1: int, 2?: string}> $expectedErrors
579+
*/
580+
public function testReportAlwaysTrueInLastCondition(bool $reportAlwaysTrueInLastCondition, array $expectedErrors): void
581+
{
582+
$this->checkAlwaysTrueInstanceOf = true;
583+
$this->treatPhpDocTypesAsCertain = true;
584+
$this->reportAlwaysTrueInLastCondition = $reportAlwaysTrueInLastCondition;
585+
$this->analyse([__DIR__ . '/data/impossible-instanceof-report-always-true-last-condition.php'], $expectedErrors);
586+
}
587+
544588
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace ImpossibleInstanceOfReportAlwaysTrueLastCondition;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(\Exception $e)
9+
{
10+
if (rand(0, 1)) {
11+
12+
} elseif ($e instanceof \Exception) {
13+
14+
}
15+
}
16+
17+
public function doBar(\Exception $e)
18+
{
19+
if (rand(0, 1)) {
20+
21+
} elseif ($e instanceof \Exception) {
22+
23+
} else {
24+
25+
}
26+
}
27+
28+
}

0 commit comments

Comments
 (0)