Skip to content

Commit 68e7d9e

Browse files
authored
Merge pull request #52 from PHPCSStandards/feature/3736-pear-functiondeclaration-fix-fixer-creating-parse-error
PEAR/FunctionDeclaration: bug fix x 2 - prevent fixer creating a parse error + prevent fixer conflict
2 parents 66b4b36 + dcc257f commit 68e7d9e

File tree

5 files changed

+107
-44
lines changed

5 files changed

+107
-44
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ The file documents changes to the PHP_CodeSniffer project.
109109
- Thanks to Juliette Reinders Folmer (@jrfnl) for the patch
110110
- Fixed bug #3728 : PHP 8.2 | PSR1/SideEffects: allow for readonly classes
111111
- Thanks to Juliette Reinders Folmer (@jrfnl) for the patch
112+
- Fixed bug #3736 : PEAR/FunctionDeclaration: prevent fixer removing the close brace (and creating a parse error) when there is no space between the open brace and close brace of a function
113+
- Thanks to Juliette Reinders Folmer (@jrfnl) for the patch
114+
- Fixed bug #3739 : PEAR/FunctionDeclaration: prevent fixer conflict (and potentially creating a parse error) for unconventionally formatted return types
115+
- Thanks to Juliette Reinders Folmer (@jrfnl) for the patch
112116
- Fixed bug #3770 : Squiz/NonExecutableCode: prevent false positives for switching between PHP and HTML
113117
- Thanks to Dan Wallis (@fredden) for the patch
114118
- Fixed bug #3773 : Tokenizer/PHP: tokenization of the readonly keyword when used in combination with PHP 8.2 disjunctive normal types

src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -297,63 +297,74 @@ public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
297297
return;
298298
}
299299

300-
// The opening brace needs to be one space away from the closing parenthesis.
300+
// The opening brace needs to be on the same line as the closing parenthesis.
301+
// There should only be one space between the closing parenthesis - or the end of the
302+
// return type - and the opening brace.
301303
$opener = $tokens[$stackPtr]['scope_opener'];
302304
if ($tokens[$opener]['line'] !== $tokens[$closeBracket]['line']) {
303305
$error = 'The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line';
304-
$fix = $phpcsFile->addFixableError($error, $opener, 'NewlineBeforeOpenBrace');
305-
if ($fix === true) {
306-
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true);
307-
$phpcsFile->fixer->beginChangeset();
308-
$phpcsFile->fixer->addContent($prev, ' {');
309-
310-
// If the opener is on a line by itself, removing it will create
311-
// an empty line, so just remove the entire line instead.
312-
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true);
313-
$next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
306+
$code = 'NewlineBeforeOpenBrace';
314307

315-
if ($tokens[$prev]['line'] < $tokens[$opener]['line']
316-
&& $tokens[$next]['line'] > $tokens[$opener]['line']
317-
) {
318-
// Clear the whole line.
319-
for ($i = ($prev + 1); $i < $next; $i++) {
320-
if ($tokens[$i]['line'] === $tokens[$opener]['line']) {
321-
$phpcsFile->fixer->replaceToken($i, '');
322-
}
323-
}
324-
} else {
325-
// Just remove the opener.
326-
$phpcsFile->fixer->replaceToken($opener, '');
327-
if ($tokens[$next]['line'] === $tokens[$opener]['line']) {
328-
$phpcsFile->fixer->replaceToken(($opener + 1), '');
329-
}
330-
}
331-
332-
$phpcsFile->fixer->endChangeset();
333-
}//end if
334-
} else {
335-
$prev = $tokens[($opener - 1)];
336-
if ($prev['code'] !== T_WHITESPACE) {
337-
$length = 0;
308+
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true);
309+
if ($tokens[$prev]['line'] === $tokens[$opener]['line']) {
310+
// End of the return type is not on the same line as the close parenthesis.
311+
$phpcsFile->addError($error, $opener, $code);
338312
} else {
339-
$length = strlen($prev['content']);
340-
}
341-
342-
if ($length !== 1) {
343-
$error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found %s spaces';
344-
$fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', [$length]);
313+
$fix = $phpcsFile->addFixableError($error, $opener, $code);
345314
if ($fix === true) {
346-
if ($length === 0) {
347-
$phpcsFile->fixer->addContentBefore($opener, ' ');
315+
$phpcsFile->fixer->beginChangeset();
316+
$phpcsFile->fixer->addContent($prev, ' {');
317+
318+
// If the opener is on a line by itself, removing it will create
319+
// an empty line, so remove the entire line instead.
320+
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true);
321+
$next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
322+
323+
if ($tokens[$prev]['line'] < $tokens[$opener]['line']
324+
&& $tokens[$next]['line'] > $tokens[$opener]['line']
325+
) {
326+
// Clear the whole line.
327+
for ($i = ($prev + 1); $i < $next; $i++) {
328+
if ($tokens[$i]['line'] === $tokens[$opener]['line']) {
329+
$phpcsFile->fixer->replaceToken($i, '');
330+
}
331+
}
348332
} else {
349-
$phpcsFile->fixer->replaceToken(($opener - 1), ' ');
333+
// Just remove the opener.
334+
$phpcsFile->fixer->replaceToken($opener, '');
335+
if ($tokens[$next]['line'] === $tokens[$opener]['line']
336+
&& ($opener + 1) !== $next
337+
) {
338+
$phpcsFile->fixer->replaceToken(($opener + 1), '');
339+
}
350340
}
351-
}
341+
342+
$phpcsFile->fixer->endChangeset();
343+
}//end if
352344

353345
return;
354346
}//end if
355347
}//end if
356348

349+
$prev = $tokens[($opener - 1)];
350+
if ($prev['code'] !== T_WHITESPACE) {
351+
$length = 0;
352+
} else {
353+
$length = strlen($prev['content']);
354+
}
355+
356+
if ($length !== 1) {
357+
$error = 'There must be a single space between the closing parenthesis/return type and the opening brace of a multi-line function declaration; found %s spaces';
358+
$fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', [$length]);
359+
if ($fix === true) {
360+
if ($length === 0) {
361+
$phpcsFile->fixer->addContentBefore($opener, ' ');
362+
} else {
363+
$phpcsFile->fixer->replaceToken(($opener - 1), ' ');
364+
}
365+
}
366+
}
367+
357368
}//end processMultiLineDeclaration()
358369

359370

src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,26 @@ new ExceptionMessage(),
465465
) {
466466
}
467467
}
468+
469+
// Issue #3736 - prevent the fixer creating a parse error by removing the function close brace.
470+
class Test
471+
{
472+
public function __construct(
473+
protected int $id
474+
)
475+
{}
476+
}
477+
478+
// Prevent fixer conflict with itself.
479+
function foo(
480+
$param1,
481+
)
482+
: \SomeClass
483+
{
484+
}
485+
486+
function foo(
487+
$param1,
488+
$param2
489+
) : // comment.
490+
\Package\Sub\SomeClass {}

src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc.fixed

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,25 @@ new ExceptionMessage(),
463463
) {
464464
}
465465
}
466+
467+
// Issue #3736 - prevent the fixer creating a parse error by removing the function close brace.
468+
class Test
469+
{
470+
public function __construct(
471+
protected int $id
472+
) {
473+
}
474+
}
475+
476+
// Prevent fixer conflict with itself.
477+
function foo(
478+
$param1,
479+
)
480+
: \SomeClass {
481+
}
482+
483+
function foo(
484+
$param1,
485+
$param2
486+
) : // comment.
487+
\Package\Sub\SomeClass {}

src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ public function getErrorList($testFile='FunctionDeclarationUnitTest.inc')
9999
371 => 1,
100100
402 => 1,
101101
406 => 1,
102+
475 => 1,
103+
483 => 1,
104+
490 => 2,
102105
];
103106
} else {
104107
$errors = [

0 commit comments

Comments
 (0)