Skip to content

Commit c2b6235

Browse files
authored
Merge pull request #647 from PHPCSStandards/feature/529-tokenizer-yield-from-tokenization-review
PHP 8.3 | Tokenizer/PHP: support "yield from" with comments
2 parents db1ebe1 + fc6d98a commit c2b6235

29 files changed

+682
-84
lines changed

src/Standards/Generic/Sniffs/WhiteSpace/DisallowTabIndentSniff.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public function process(File $phpcsFile, $stackPtr)
7878
T_COMMENT => true,
7979
T_END_HEREDOC => true,
8080
T_END_NOWDOC => true,
81+
T_YIELD_FROM => true,
8182
];
8283

8384
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {

src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -80,42 +80,57 @@ public function process(File $phpcsFile, $stackPtr)
8080
if ($tokens[$stackPtr]['code'] === T_YIELD_FROM
8181
&& strtolower($content) !== 'yield from'
8282
) {
83-
if ($tokens[($stackPtr - 1)]['code'] === T_YIELD_FROM) {
84-
// A multi-line statement that has already been processed.
85-
return;
86-
}
83+
$found = $content;
84+
$hasComment = false;
85+
$yieldFromEnd = $stackPtr;
86+
87+
// Handle potentially multi-line/multi-token "yield from" expressions.
88+
if (preg_match('`yield\s+from`i', $content) !== 1) {
89+
for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
90+
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false
91+
&& $tokens[$i]['code'] !== T_YIELD_FROM
92+
) {
93+
break;
94+
}
95+
96+
if (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) {
97+
$hasComment = true;
98+
}
8799

88-
$found = $content;
89-
if ($tokens[($stackPtr + 1)]['code'] === T_YIELD_FROM) {
90-
// This yield from statement is split over multiple lines.
91-
$i = ($stackPtr + 1);
92-
do {
93100
$found .= $tokens[$i]['content'];
94-
$i++;
95-
} while ($tokens[$i]['code'] === T_YIELD_FROM);
96-
}
101+
102+
if ($tokens[$i]['code'] === T_YIELD_FROM
103+
&& strtolower(trim($tokens[$i]['content'])) === 'from'
104+
) {
105+
break;
106+
}
107+
}
108+
109+
$yieldFromEnd = $i;
110+
}//end if
97111

98112
$error = 'Language constructs must be followed by a single space; expected 1 space between YIELD FROM found "%s"';
99113
$data = [Common::prepareForOutput($found)];
100-
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'IncorrectYieldFrom', $data);
101-
if ($fix === true) {
102-
preg_match('/yield/i', $found, $yield);
103-
preg_match('/from/i', $found, $from);
104-
$phpcsFile->fixer->beginChangeset();
105-
$phpcsFile->fixer->replaceToken($stackPtr, $yield[0].' '.$from[0]);
106-
107-
if ($tokens[($stackPtr + 1)]['code'] === T_YIELD_FROM) {
108-
$i = ($stackPtr + 1);
109-
do {
114+
115+
if ($hasComment === true) {
116+
$phpcsFile->addError($error, $stackPtr, 'IncorrectYieldFromWithComment', $data);
117+
} else {
118+
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'IncorrectYieldFrom', $data);
119+
if ($fix === true) {
120+
preg_match('/yield/i', $found, $yield);
121+
preg_match('/from/i', $found, $from);
122+
$phpcsFile->fixer->beginChangeset();
123+
$phpcsFile->fixer->replaceToken($stackPtr, $yield[0].' '.$from[0]);
124+
125+
for ($i = ($stackPtr + 1); $i <= $yieldFromEnd; $i++) {
110126
$phpcsFile->fixer->replaceToken($i, '');
111-
$i++;
112-
} while ($tokens[$i]['code'] === T_YIELD_FROM);
113-
}
127+
}
114128

115-
$phpcsFile->fixer->endChangeset();
116-
}
129+
$phpcsFile->fixer->endChangeset();
130+
}
131+
}//end if
117132

118-
return;
133+
return ($yieldFromEnd + 1);
119134
}//end if
120135

121136
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {

src/Standards/Generic/Tests/PHP/LowerCaseKeywordUnitTest.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,12 @@ if (ISSET($a) && !Empty($a)) { UnSeT($a); }
5353
eval('foo');
5454
eVaL('foo');
5555

56+
$c = function() {
57+
Yield /*comment*/ From fun();
58+
YIELD
59+
/*comment*/
60+
FROM fun();
61+
}
62+
5663
__HALT_COMPILER(); // An exception due to phar support.
5764
function

src/Standards/Generic/Tests/PHP/LowerCaseKeywordUnitTest.inc.fixed

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,12 @@ if (isset($a) && !empty($a)) { unset($a); }
5353
eval('foo');
5454
eval('foo');
5555

56+
$c = function() {
57+
yield /*comment*/ from fun();
58+
yield
59+
/*comment*/
60+
from fun();
61+
}
62+
5663
__HALT_COMPILER(); // An exception due to phar support.
5764
function

src/Standards/Generic/Tests/PHP/LowerCaseKeywordUnitTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public function getErrorList()
5252
48 => 1,
5353
52 => 3,
5454
54 => 1,
55+
57 => 2,
56+
58 => 1,
57+
60 => 1,
5558
];
5659

5760
}//end getErrorList()

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.1.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,10 @@ $x = 1;
116116
117117
Another line.
118118
*/
119+
120+
// A `yield from` can be multiline and may contain spaces in the indentation whitespace between the keywords.
121+
function myGenerator() {
122+
yield
123+
from
124+
gen2();
125+
}

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.1.inc.fixed

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,10 @@ $x = 1;
116116

117117
Another line.
118118
*/
119+
120+
// A `yield from` can be multiline and may contain spaces in the indentation whitespace between the keywords.
121+
function myGenerator() {
122+
yield
123+
from
124+
gen2();
125+
}

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.2.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,10 @@ $x = 1;
116116
117117
Another line.
118118
*/
119+
120+
// A `yield from` can be multiline and may contain spaces in the indentation whitespace between the keywords.
121+
function myGenerator() {
122+
yield
123+
from
124+
gen2();
125+
}

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.2.inc.fixed

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,10 @@ $x = 1;
116116

117117
Another line.
118118
*/
119+
120+
// A `yield from` can be multiline and may contain spaces in the indentation whitespace between the keywords.
121+
function myGenerator() {
122+
yield
123+
from
124+
gen2();
125+
}

src/Standards/Generic/Tests/WhiteSpace/DisallowSpaceIndentUnitTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public function getErrorList($testFile='')
8989
115 => 1,
9090
117 => 1,
9191
118 => 1,
92+
123 => 1,
9293
];
9394

9495
case 'DisallowSpaceIndentUnitTest.3.inc':

0 commit comments

Comments
 (0)