Skip to content

Commit 4b18031

Browse files
committed
Fixed false positives introduced in 0.12.6
1 parent 7c0fdfe commit 4b18031

File tree

9 files changed

+157
-11
lines changed

9 files changed

+157
-11
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ private function processStmtNode(
521521
new StatementExitPoint($stmt, $scope),
522522
]);
523523
} elseif ($stmt instanceof If_) {
524-
$conditionType = $scope->doNotTreatPhpDocTypesAsCertain()->getType($stmt->cond)->toBoolean();
524+
$conditionType = $scope->getType($stmt->cond)->toBoolean();
525525
$ifAlwaysTrue = $conditionType instanceof ConstantBooleanType && $conditionType->getValue();
526526
$condResult = $this->processExprNode($stmt->cond, $scope, $nodeCallback, ExpressionContext::createDeep());
527527
$exitPoints = [];
@@ -545,7 +545,7 @@ private function processStmtNode(
545545
$condScope = $scope;
546546
foreach ($stmt->elseifs as $elseif) {
547547
$nodeCallback($elseif, $scope);
548-
$elseIfConditionType = $condScope->doNotTreatPhpDocTypesAsCertain()->getType($elseif->cond)->toBoolean();
548+
$elseIfConditionType = $condScope->getType($elseif->cond)->toBoolean();
549549
$condResult = $this->processExprNode($elseif->cond, $condScope, $nodeCallback, ExpressionContext::createDeep());
550550
$condScope = $condResult->getScope();
551551
$branchScopeStatementResult = $this->processStmtNodes($elseif, $elseif->stmts, $condResult->getTruthyScope(), $nodeCallback);

tests/PHPStan/Analyser/data/native-types.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public function doIfElse(\DateTimeInterface $date): void
163163
}
164164

165165
assertType(\DateTimeImmutable::class, $date);
166-
assertNativeType(\DateTimeInterface::class, $date);
166+
assertNativeType(\DateTimeImmutable::class, $date); // could be DateTimeInterface
167167

168168
if ($date instanceof \DateTime) {
169169

tests/PHPStan/Rules/Classes/ImpossibleInstanceOfRuleTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ public function testInstanceof(): void
123123
[
124124
'Instanceof between ImpossibleInstanceOf\Bar&ImpossibleInstanceOf\Foo and ImpossibleInstanceOf\Foo will always evaluate to true.',
125125
238,
126-
$tipText,
126+
//$tipText,
127127
],
128128
[
129129
'Instanceof between *NEVER* and ImpossibleInstanceOf\Bar will always evaluate to false.',
130130
240,
131-
$tipText,
131+
//$tipText,
132132
],
133133
[
134134
'Instanceof between object and Exception will always evaluate to false.',
@@ -212,7 +212,7 @@ public function testInstanceofWithoutAlwaysTrue(): void
212212
[
213213
'Instanceof between *NEVER* and ImpossibleInstanceOf\Bar will always evaluate to false.',
214214
240,
215-
$tipText,
215+
//$tipText,
216216
],
217217
[
218218
'Instanceof between object and Exception will always evaluate to false.',
@@ -295,7 +295,7 @@ public function testReportTypesFromPhpDocs(): void
295295
[
296296
'Instanceof between DateTimeImmutable and DateTime will always evaluate to false.',
297297
36,
298-
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
298+
//'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
299299
],
300300
]);
301301
}

tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ public function testRule(): void
8585
[
8686
'Result of && is always true.',
8787
64,
88-
$tipText,
88+
//$tipText,
8989
],
9090
[
9191
'Result of && is always false.',
9292
66,
93-
$tipText,
93+
//$tipText,
9494
],
9595
[
9696
'Result of && is always false.',

tests/PHPStan/Rules/Comparison/UnreachableIfBranchesRuleTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ public function testReportPhpDoc(): void
106106
[
107107
'Else branch is unreachable because previous condition is always true.',
108108
54,
109-
$tipText,
109+
//$tipText,
110110
],
111111
[
112112
'Elseif branch is unreachable because previous condition is always true.',
113113
64,
114-
$tipText,
114+
//$tipText,
115115
],
116116
]);
117117
}

tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,9 @@ public function testRuleTopLevel(): void
4848
]);
4949
}
5050

51+
public function testBugWithoutGitHubIssue1(): void
52+
{
53+
$this->analyse([__DIR__ . '/data/bug-without-issue-1.php'], []);
54+
}
55+
5156
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
namespace BugWithoutIssue1;
4+
5+
interface User {
6+
function getId() : int;
7+
}
8+
9+
interface Message {
10+
/** @return object|null */
11+
public function getScheduleRequest();
12+
public function getFromUser() : User;
13+
}
14+
15+
interface MessageThread {
16+
/** @return object|null */
17+
public function getLastScheduleRequest();
18+
/** @return User[] */
19+
public function getParticipants() : array;
20+
/** @return Message[] */
21+
public function getMessages() : array;
22+
}
23+
24+
class X
25+
{
26+
public function checkAndSendThreadNotRepliedNotification(MessageThread $thread) : bool
27+
{
28+
$threadSR = $thread->getLastScheduleRequest();
29+
30+
$p = [];
31+
foreach($thread->getParticipants() as $user)
32+
$p[$user->getId()] = $user;
33+
34+
$reminderMsg = null;
35+
$reachedSR = !$threadSR;
36+
foreach($thread->getMessages() as $msg)
37+
{
38+
$msgSR = $msg->getScheduleRequest();
39+
if(!$reachedSR && ($threadSR && $msgSR != $threadSR))
40+
continue;
41+
42+
$reachedSR = true;
43+
44+
if(!$reminderMsg)
45+
$reminderMsg = $msg;
46+
47+
unset($p[$msg->getFromUser()->getId()]);
48+
49+
if(!$p)
50+
return false;
51+
}
52+
53+
if(!$reminderMsg)
54+
throw new \UnexpectedValueException('Expected a reminderMsg but got null for thread');
55+
56+
return true;
57+
}
58+
}
59+
60+
class Foo
61+
{
62+
/** @var int */
63+
protected $index = 0;
64+
65+
/** @var string[][] */
66+
protected $data = [
67+
0 => ['type' => 'id', 'value' => 'foo'],
68+
1 => ['type' => 'special', 'value' => '.'],
69+
2 => ['type' => 'id', 'value' => 'bar'],
70+
3 => ['type' => 'special', 'value' => ';'],
71+
];
72+
73+
protected function next(): void
74+
{
75+
$this->index = $this->index + 1;
76+
}
77+
78+
protected function check(string $type, ?string $value = null): bool {
79+
return ($this->type() === $type) && (($value === null) || ($this->value() === $value));
80+
}
81+
82+
protected function type(): string
83+
{
84+
return $this->data[$this->index]['type'];
85+
}
86+
87+
protected function value(): string
88+
{
89+
return $this->data[$this->index]['value'];
90+
}
91+
92+
public function separatedName(): string
93+
{
94+
$name = '';
95+
$previousType = null;
96+
$separator = '.';
97+
98+
$currentValue = $this->value();
99+
100+
while ((($this->check('special', $separator)) || ($this->check('id'))) &&
101+
(($previousType === null) || ($this->type() !== $previousType)) &&
102+
(($previousType !== null) || ($currentValue !== $separator))
103+
) {
104+
$name .= $currentValue;
105+
$previousType = $this->type();
106+
107+
$this->next();
108+
$currentValue = $this->value();
109+
}
110+
111+
if (($previousType === null) || ($previousType !== 'id')) {
112+
throw new \RuntimeException();
113+
}
114+
115+
return $name;
116+
}
117+
}

tests/PHPStan/Rules/Missing/MissingReturnRuleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,10 @@ public function testCheckMissingReturnWithTemplateMixedType(): void
110110
]);
111111
}
112112

113+
public function testBug2875(): void
114+
{
115+
$this->checkExplicitMixedMissingReturn = true;
116+
$this->analyse([__DIR__ . '/data/bug-2875.php'], []);
117+
}
118+
113119
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Bug2875MissingReturn;
4+
5+
class A {}
6+
class B {}
7+
8+
class HelloWorld
9+
{
10+
/** @param A|B|null $obj */
11+
function one($obj): int
12+
{
13+
if ($obj === null) return 1;
14+
else if ($obj instanceof A) return 2;
15+
else if ($obj instanceof B) return 3;
16+
}
17+
18+
}

0 commit comments

Comments
 (0)