Skip to content

Commit f4dbf86

Browse files
committed
Merge remote-tracking branch 'origin/1.12.x' into 2.0.x
2 parents c3bb055 + 3dd5a01 commit f4dbf86

File tree

10 files changed

+220
-17
lines changed

10 files changed

+220
-17
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,10 @@ private function processStmtNode(
13761376
$loopScope = $this->processExprNode($stmt, $loopExpr, $loopScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
13771377
}
13781378
$finalScope = $finalScope->generalizeWith($loopScope);
1379+
1380+
$alwaysIterates = TrinaryLogic::createFromBoolean($context->isTopLevel());
13791381
if ($lastCondExpr !== null) {
1382+
$alwaysIterates = $alwaysIterates->and($finalScope->getType($lastCondExpr)->toBoolean()->isTrue());
13801383
$finalScope = $finalScope->filterByFalseyValue($lastCondExpr);
13811384
}
13821385

@@ -1403,10 +1406,18 @@ private function processStmtNode(
14031406
}
14041407
}
14051408

1409+
if ($alwaysIterates->yes()) {
1410+
$isAlwaysTerminating = count($finalScopeResult->getExitPointsByType(Break_::class)) === 0;
1411+
} elseif ($isIterableAtLeastOnce->yes()) {
1412+
$isAlwaysTerminating = $finalScopeResult->isAlwaysTerminating();
1413+
} else {
1414+
$isAlwaysTerminating = false;
1415+
}
1416+
14061417
return new StatementResult(
14071418
$finalScope,
14081419
$finalScopeResult->hasYield() || $hasYield,
1409-
false/* $finalScopeResult->isAlwaysTerminating() && $isAlwaysIterable*/,
1420+
$isAlwaysTerminating,
14101421
$finalScopeResult->getExitPointsForOuterLoop(),
14111422
array_merge($throwPoints, $finalScopeResult->getThrowPoints()),
14121423
array_merge($impurePoints, $finalScopeResult->getImpurePoints()),

tests/PHPStan/Analyser/StatementResultTest.php

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,130 @@ public function dataIsAlwaysTerminating(): array
174174
'while (true) { break; }',
175175
false,
176176
],
177+
[
178+
'while (true) { exit; }',
179+
true,
180+
],
181+
[
182+
'while (true) { while (true) { } }',
183+
true,
184+
],
185+
[
186+
'while (true) { while (true) { return; } }',
187+
true,
188+
],
189+
[
190+
'while (true) { while (true) { break; } }',
191+
true,
192+
],
193+
[
194+
'while (true) { while (true) { exit; } }',
195+
true,
196+
],
197+
[
198+
'while (true) { while (true) { break 2; } }',
199+
false,
200+
],
201+
[
202+
'while (true) { while ($x) { } }',
203+
true,
204+
],
205+
[
206+
'while (true) { while ($x) { return; } }',
207+
true,
208+
],
209+
[
210+
'while (true) { while ($x) { break; } }',
211+
true,
212+
],
213+
[
214+
'while (true) { while ($x) { exit; } }',
215+
true,
216+
],
217+
[
218+
'while (true) { while ($x) { break 2; } }',
219+
false,
220+
],
221+
[
222+
'for (;;) { }',
223+
true,
224+
],
225+
[
226+
'for (;;) { return; }',
227+
true,
228+
],
229+
[
230+
'for (;;) { break; }',
231+
false,
232+
],
233+
[
234+
'for (;;) { exit; }',
235+
true,
236+
],
237+
[
238+
'for (;;) { for (;;) { } }',
239+
true,
240+
],
241+
[
242+
'for (;;) { for (;;) { return; } }',
243+
true,
244+
],
245+
[
246+
'for (;;) { for (;;) { break; } }',
247+
true,
248+
],
249+
[
250+
'for (;;) { for (;;) { exit; } }',
251+
true,
252+
],
253+
[
254+
'for (;;) { for (;;) { break 2; } }',
255+
false,
256+
],
257+
[
258+
'for (;;) { for ($i = 0; $i< 5; $i++) { } }',
259+
true,
260+
],
261+
[
262+
'for (;;) { for ($i = 0; $i< 5; $i++) { return; } }',
263+
true,
264+
],
265+
[
266+
'for (;;) { for ($i = 0; $i< 5; $i++) { break; } }',
267+
true,
268+
],
269+
[
270+
'for (;;) { for ($i = 0; $i< 5; $i++) { exit; } }',
271+
true,
272+
],
273+
[
274+
'for (;;) { for ($i = 0; $i< 5; $i++) { break 2; } }',
275+
false,
276+
],
277+
[
278+
'for ($i = 0; $i < 5;) { }',
279+
true,
280+
],
281+
[
282+
'for ($i = 0; $i < 5; $i--) { }',
283+
true,
284+
],
285+
[
286+
'for (; 0, 1;) { }',
287+
true,
288+
],
289+
[
290+
'for (; 1, 0;) { }',
291+
false,
292+
],
293+
[
294+
'for (; "", "a";) { }',
295+
true,
296+
],
297+
[
298+
'for (; "a", "";) { }',
299+
false,
300+
],
177301
[
178302
'do { } while (doFoo());',
179303
false,
@@ -232,7 +356,7 @@ public function dataIsAlwaysTerminating(): array
232356
],
233357
[
234358
'for ($i = 0; $i < 10; $i++) { return; }',
235-
false, // will be true with range types
359+
true,
236360
],
237361
[
238362
'for ($i = 0; $i < 0; $i++) { return; }',

tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ public function testStrictComparison(): void
105105
140,
106106
],
107107
[
108-
'Strict comparison using !== between StrictComparison\Foo|null and 1 will always evaluate to true.',
109-
154,
108+
'Strict comparison using === between non-empty-array and null will always evaluate to false.',
109+
150,
110110
],
111111
[
112-
'Strict comparison using === between non-empty-array and null will always evaluate to false.',
113-
164,
112+
'Strict comparison using !== between StrictComparison\Foo|null and 1 will always evaluate to true.',
113+
161,
114114
],
115115
[
116116
'Strict comparison using !== between StrictComparison\Node|null and false will always evaluate to true.',

tests/PHPStan/Rules/Comparison/data/strict-comparison.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,13 @@ public function whileWithTypeChange()
146146

147147
public function forWithTypeChange()
148148
{
149+
for (; $val = $this->returnArray();) {
150+
if ($val === null) {
151+
152+
}
153+
$val = null;
154+
}
155+
149156
$foo = null;
150157
for (;;) {
151158
if ($foo !== null) {
@@ -159,13 +166,6 @@ public function forWithTypeChange()
159166
$foo = new self();
160167
}
161168
}
162-
163-
for (; $val = $this->returnArray();) {
164-
if ($val === null) {
165-
166-
}
167-
$val = null;
168-
}
169169
}
170170

171171
private function returnArray(): array

tests/PHPStan/Rules/Missing/MissingReturnRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,22 @@ public function testBug9309(): void
333333
$this->analyse([__DIR__ . '/data/bug-9309.php'], []);
334334
}
335335

336+
public function testBug6807(): void
337+
{
338+
$this->checkExplicitMixedMissingReturn = true;
339+
$this->analyse([__DIR__ . '/data/bug-6807.php'], []);
340+
}
341+
342+
public function testBug8463(): void
343+
{
344+
$this->checkExplicitMixedMissingReturn = true;
345+
$this->analyse([__DIR__ . '/data/bug-8463.php'], []);
346+
}
347+
348+
public function testBug9374(): void
349+
{
350+
$this->checkExplicitMixedMissingReturn = true;
351+
$this->analyse([__DIR__ . '/data/bug-9374.php'], []);
352+
}
353+
336354
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug6807;
4+
5+
/** @return int */
6+
function test()
7+
{
8+
for ($attempts = 0; ; $attempts++)
9+
{
10+
if ($attempts > 5)
11+
throw new Exception();
12+
13+
if (rand() == 1)
14+
return 5;
15+
}
16+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Bug8463;
4+
5+
function f1() : int
6+
{
7+
while(true)
8+
{
9+
if(rand() === rand())
10+
{
11+
return 1;
12+
}
13+
}
14+
}
15+
16+
17+
function f2() : int
18+
{
19+
for(;;)
20+
{
21+
if(rand() === rand())
22+
{
23+
return 1;
24+
}
25+
}
26+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9374;
4+
5+
function bar(): string {
6+
for ($i = 0; ; ++$i)
7+
return "";
8+
}

tests/PHPStan/Rules/Variables/data/defined-variables-anonymous-function-use.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function () use (&$errorHandler) {
1414
$onlyInIf = 1;
1515
}
1616

17-
for ($forI = 0; $forI < 10, $anotherVariableFromForCond = 1; $forI++, $forJ = $forI) {
17+
for ($forI = 0; $anotherVariableFromForCond = 1, $forI < 10; $forI++, $forJ = $forI) {
1818

1919
}
2020

tests/PHPStan/Rules/Variables/data/defined-variables.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ function () {
243243

244244
}
245245

246-
for ($forI = 0; $forI < 10, $forK = 5; $forI++, $forK++, $forJ = $forI) {
246+
for ($forI = 0; $forK = 5, $forI < 10; $forI++, $forK++, $forJ = $forI) {
247247
echo $forI;
248248
}
249249

@@ -322,7 +322,7 @@ function () {
322322
include($fileB='includeB.php');
323323
echo $fileB;
324324

325-
for ($forLoopVariableInit = 0; $forLoopVariableInit < 5; $forLoopVariableInit = $forLoopVariable, $anotherForLoopVariable = 1) {
325+
for ($forLoopVariableInit = 0; $forLoopVariableInit < 5 && rand(0, 1); $forLoopVariableInit = $forLoopVariable, $anotherForLoopVariable = 1) {
326326
$forLoopVariable = 2;
327327
}
328328
echo $anotherForLoopVariable;
@@ -357,7 +357,7 @@ function () {
357357

358358
}
359359

360-
for (; $forVariableUsedAndThenDefined && $forVariableUsedAndThenDefined = 1;) {
360+
for (; $forVariableUsedAndThenDefined && $forVariableUsedAndThenDefined = 1 && rand(0, 1);) {
361361

362362
}
363363

0 commit comments

Comments
 (0)