Repository: PHP-CS-Fixer/PHP-CS-Fixer (PHP)

Issue:
simplified_if_return breaks code in braceless for loop
## Bug report
   * PHP version: php -v
   ```
PHP 7.4.16 (cli) (built: Mar  2 2021 14:06:13) ( NTS Visual C++ 2017 x64 )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Xdebug v3.0.3, Copyright (c) 2002-2021, by Derick Rethans
```
   * PHP CS Fixer version: php-cs-fixer -V
   ```
PHP CS Fixer 3.4.0 Si! by Fabien Potencier and Dariusz Ruminski
```
   * the command used to run PHP CS Fixer (run with `-vvv`)
```
php-cs-fixer fix -vvv --rules=simplified_if_return file.php
PHP CS Fixer 3.4.0 Si! by Fabien Potencier and Dariusz Ruminski
Runtime: PHP 7.4.16
Loaded config default.
Using cache file ".php-cs-fixer.cache".
F                                                                                                           1 / 1 (100%)
Legend: ?-unknown, I-invalid file syntax (file ignored), S-skipped (cached or empty file), .-no changes, F-fixed, E-error
   1) file.php (simplified_if_return)

Fixed all files in 0.047 seconds, 12.000 MB memory used
```

### Code snippet that reproduces the problem

#### Before running PHP CS Fixer
```php
<?php

function EqualsAny(string $str, array $letters) : bool
{
   foreach ($letters as $letter)
      if ($str === $letter)
         return true;
   return false;
}

$str = 'b';
$letters = ['a', 'b'];
var_dump(EqualsAny($str, $letters)); //Expected bool(true), Returns bool(true)

$letters = [];
var_dump(EqualsAny($str, $letters)); //Expected bool(false), Returns bool(false)
```

#### After running PHP CS Fixer
```php
<?php

function EqualsAny(string $str, array $letters) : bool
{
   foreach ($letters as $letter)
      return (bool) ($str === $letter)

     ;
}

$str = 'b';
$letters = ['a', 'b'];
var_dump(EqualsAny($str, $letters)); //Expected bool(true), Returns bool(false)

$letters = [];
var_dump(EqualsAny($str, $letters)); //Expected bool(false), Returns throws exception!!
```

#### Expected output
Leave the code unchanged.

This is a braces problem, if the code has braces (or if you run `--rules=braces,simplified_if_return`) the code doesn't change and it's ok.







Discussion:
thanks for reporting @rodrigoq , proposed fix is here https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/6266



Patch:
diff --git a/src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php b/src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php
index ec3ee8b79ae..155453c3f3b 100644
--- a/src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php
+++ b/src/Fixer/ControlStructure/SimplifiedIfReturnFixer.php
@@ -96,12 +96,14 @@ public function isCandidate(Tokens $tokens): bool
     protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
     {
         for ($ifIndex = $tokens->count() - 1; 0 <= $ifIndex; --$ifIndex) {
-            $ifToken = $tokens[$ifIndex];
-
-            if (!$ifToken->isGivenKind([T_IF, T_ELSEIF])) {
+            if (!$tokens[$ifIndex]->isGivenKind([T_IF, T_ELSEIF])) {
                 continue;
             }
 
+            if ($tokens[$tokens->getPrevMeaningfulToken($ifIndex)]->equals(')')) {
+                continue; // in a loop without braces
+            }
+
             $startParenthesisIndex = $tokens->getNextTokenOfKind($ifIndex, ['(']);
             $endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
             $firstCandidateIndex = $tokens->getNextMeaningfulToken($endParenthesisIndex);


Test patch:
diff --git a/tests/Fixer/ControlStructure/SimplifiedIfReturnFixerTest.php b/tests/Fixer/ControlStructure/SimplifiedIfReturnFixerTest.php
index 5c76576b0e4..fc4327eab2c 100644
--- a/tests/Fixer/ControlStructure/SimplifiedIfReturnFixerTest.php
+++ b/tests/Fixer/ControlStructure/SimplifiedIfReturnFixerTest.php
@@ -33,47 +33,55 @@ public function testFix(string $expected, ?string $input = null): void
         $this->doTest($expected, $input);
     }
 
-    public function provideFixCases(): array
+    public function provideFixCases(): iterable
     {
-        return [
-            'simple' => [
-                '<?php return (bool) ($foo)      ;',
-                '<?php if ($foo) { return true; } return false;',
-            ],
-            'simple-negative' => [
-                '<?php return ! ($foo)      ;',
-                '<?php if ($foo) { return false; } return true;',
-            ],
-            'simple-negative II' => [
-                '<?php return ! (!$foo && $a())      ;',
-                '<?php if (!$foo && $a()) { return false; } return true;',
-            ],
-            'simple-braceless' => [
-                '<?php return (bool) ($foo)    ;',
-                '<?php if ($foo) return true; return false;',
-            ],
-            'simple-braceless-negative' => [
-                '<?php return ! ($foo)    ;',
-                '<?php if ($foo) return false; return true;',
-            ],
-            'bug-consecutive-ifs' => [
-                '<?php if ($bar) { return 1; } return (bool) ($foo)      ;',
-                '<?php if ($bar) { return 1; } if ($foo) { return true; } return false;',
-            ],
-            'bug-consecutive-ifs-negative' => [
-                '<?php if ($bar) { return 1; } return ! ($foo)      ;',
-                '<?php if ($bar) { return 1; } if ($foo) { return false; } return true;',
-            ],
-            'bug-consecutive-ifs-braceless' => [
-                '<?php if ($bar) return 1; return (bool) ($foo)    ;',
-                '<?php if ($bar) return 1; if ($foo) return true; return false;',
-            ],
-            'bug-consecutive-ifs-braceless-negative' => [
-                '<?php if ($bar) return 1; return ! ($foo)    ;',
-                '<?php if ($bar) return 1; if ($foo) return false; return true;',
-            ],
-            [
-                <<<'EOT'
+        yield 'simple' => [
+            '<?php return (bool) ($foo)      ;',
+            '<?php if ($foo) { return true; } return false;',
+        ];
+
+        yield 'simple-negative' => [
+            '<?php return ! ($foo)      ;',
+            '<?php if ($foo) { return false; } return true;',
+        ];
+
+        yield 'simple-negative II' => [
+            '<?php return ! (!$foo && $a())      ;',
+            '<?php if (!$foo && $a()) { return false; } return true;',
+        ];
+
+        yield 'simple-braceless' => [
+            '<?php return (bool) ($foo)    ;',
+            '<?php if ($foo) return true; return false;',
+        ];
+
+        yield 'simple-braceless-negative' => [
+            '<?php return ! ($foo)    ;',
+            '<?php if ($foo) return false; return true;',
+        ];
+
+        yield 'bug-consecutive-ifs' => [
+            '<?php if ($bar) { return 1; } return (bool) ($foo)      ;',
+            '<?php if ($bar) { return 1; } if ($foo) { return true; } return false;',
+        ];
+
+        yield 'bug-consecutive-ifs-negative' => [
+            '<?php if ($bar) { return 1; } return ! ($foo)      ;',
+            '<?php if ($bar) { return 1; } if ($foo) { return false; } return true;',
+        ];
+
+        yield 'bug-consecutive-ifs-braceless' => [
+            '<?php if ($bar) return 1; return (bool) ($foo)    ;',
+            '<?php if ($bar) return 1; if ($foo) return true; return false;',
+        ];
+
+        yield 'bug-consecutive-ifs-braceless-negative' => [
+            '<?php if ($bar) return 1; return ! ($foo)    ;',
+            '<?php if ($bar) return 1; if ($foo) return false; return true;',
+        ];
+
+        yield [
+            <<<'EOT'
 <?php
 function f1() { return (bool) ($f1)      ; }
 function f2() { return true; } return false;
@@ -85,8 +93,8 @@ function f7() { return ! ($f7)      ; }
 function f8() { return false; } return true;
 function f9() { return ! ($f9)      ; }
 EOT
-                ,
-                <<<'EOT'
+            ,
+            <<<'EOT'
 <?php
 function f1() { if ($f1) { return true; } return false; }
 function f2() { return true; } return false;
@@ -98,10 +106,11 @@ function f7() { if ($f7) { return false; } return true; }
 function f8() { return false; } return true;
 function f9() { if ($f9) { return false; } return true; }
 EOT
-                ,
-            ],
-            'preserve-comments' => [
-                <<<'EOT'
+            ,
+        ];
+
+        yield 'preserve-comments' => [
+            <<<'EOT'
 <?php
 // C1
 return (bool)
@@ -129,8 +138,8 @@ function f9() { if ($f9) { return false; } return true; }
 ;
 /* C13 */
 EOT
-                ,
-                <<<'EOT'
+            ,
+            <<<'EOT'
 <?php
 // C1
 if
@@ -158,10 +167,11 @@ function f9() { if ($f9) { return false; } return true; }
 ;
 /* C13 */
 EOT
-                ,
-            ],
-            'preserve-comments-braceless' => [
-                <<<'EOT'
+            ,
+        ];
+
+        yield 'preserve-comments-braceless' => [
+            <<<'EOT'
 <?php
 // C1
 return (bool)
@@ -187,8 +197,8 @@ function f9() { if ($f9) { return false; } return true; }
 ;
 /* C13 */
 EOT
-                ,
-                <<<'EOT'
+            ,
+            <<<'EOT'
 <?php
 // C1
 if
@@ -214,40 +224,85 @@ function f9() { if ($f9) { return false; } return true; }
 ;
 /* C13 */
 EOT
-                ,
-            ],
-            'else-if' => [
-                '<?php if ($bar) { return $bar; } else return (bool) ($foo)      ;',
-                '<?php if ($bar) { return $bar; } else if ($foo) { return true; } return false;',
-            ],
-            'else-if-negative' => [
-                '<?php if ($bar) { return $bar; } else return ! ($foo)      ;',
-                '<?php if ($bar) { return $bar; } else if ($foo) { return false; } return true;',
-            ],
-            'else-if-braceless' => [
-                '<?php if ($bar) return $bar; else return (bool) ($foo)    ;',
-                '<?php if ($bar) return $bar; else if ($foo) return true; return false;',
-            ],
-            'else-if-braceless-negative' => [
-                '<?php if ($bar) return $bar; else return ! ($foo)    ;',
-                '<?php if ($bar) return $bar; else if ($foo) return false; return true;',
-            ],
-            'elseif' => [
-                '<?php if ($bar) { return $bar; } return (bool) ($foo)      ;',
-                '<?php if ($bar) { return $bar; } elseif ($foo) { return true; } return false;',
-            ],
-            'elseif-negative' => [
-                '<?php if ($bar) { return $bar; } return ! ($foo)      ;',
-                '<?php if ($bar) { return $bar; } elseif ($foo) { return false; } return true;',
-            ],
-            'elseif-braceless' => [
-                '<?php if ($bar) return $bar; return (bool) ($foo)    ;',
-                '<?php if ($bar) return $bar; elseif ($foo) return true; return false;',
-            ],
-            'elseif-braceless-negative' => [
-                '<?php if ($bar) return $bar; return ! ($foo)    ;',
-                '<?php if ($bar) return $bar; elseif ($foo) return false; return true;',
-            ],
+            ,
+        ];
+
+        yield 'else-if' => [
+            '<?php if ($bar) { return $bar; } else return (bool) ($foo)      ;',
+            '<?php if ($bar) { return $bar; } else if ($foo) { return true; } return false;',
+        ];
+
+        yield 'else-if-negative' => [
+            '<?php if ($bar) { return $bar; } else return ! ($foo)      ;',
+            '<?php if ($bar) { return $bar; } else if ($foo) { return false; } return true;',
+        ];
+
+        yield 'else-if-braceless' => [
+            '<?php if ($bar) return $bar; else return (bool) ($foo)    ;',
+            '<?php if ($bar) return $bar; else if ($foo) return true; return false;',
+        ];
+
+        yield 'else-if-braceless-negative' => [
+            '<?php if ($bar) return $bar; else return ! ($foo)    ;',
+            '<?php if ($bar) return $bar; else if ($foo) return false; return true;',
+        ];
+
+        yield 'elseif' => [
+            '<?php if ($bar) { return $bar; } return (bool) ($foo)      ;',
+            '<?php if ($bar) { return $bar; } elseif ($foo) { return true; } return false;',
+        ];
+
+        yield 'elseif-negative' => [
+            '<?php if ($bar) { return $bar; } return ! ($foo)      ;',
+            '<?php if ($bar) { return $bar; } elseif ($foo) { return false; } return true;',
+        ];
+
+        yield 'elseif-braceless' => [
+            '<?php if ($bar) return $bar; return (bool) ($foo)    ;',
+            '<?php if ($bar) return $bar; elseif ($foo) return true; return false;',
+        ];
+
+        yield 'elseif-braceless-negative' => [
+            '<?php if ($bar) return $bar; return ! ($foo)    ;',
+            '<?php if ($bar) return $bar; elseif ($foo) return false; return true;',
+        ];
+
+        yield 'no braces loops' => [
+            '<?php
+function foo1(string $str, array $letters): bool
+{
+    foreach ($letters as $letter)
+        if ($str === $letter)
+            return true;
+    return false;
+}
+
+function foo2(int $z): bool
+{
+    for ($i = 0; $i < 3; ++$i)
+        if ($i === $z)
+            return true;
+    return false;
+}
+
+function foo3($y): bool
+{
+    while ($x = bar())
+        if ($x === $z)
+            return true;
+    return false;
+}
+',
+        ];
+
+        yield 'alternative syntax not supported' => [
+            '<?php
+if ($foo):
+    return true;
+else:
+    return false;
+endif;
+',
         ];
     }
 }