Skip to content

Commit

Permalink
PEAR/FunctionDeclaration: prevent fixer conflict
Browse files Browse the repository at this point in the history
If a return type declaration was not confined to one line, the sniff could have a fixer conflict with itself.
The fixer would also potentially remove a close curly on the same line, causing parse errors.

Fixed now. The diff will be most straight forward to review while ignoring whitespace changes.

Includes unit tests.
  • Loading branch information
jrfnl committed Nov 11, 2023
1 parent fc17f86 commit 661224e
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 46 deletions.
101 changes: 55 additions & 46 deletions src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,65 +297,74 @@ public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
return;
}

// The opening brace needs to be one space away from the closing parenthesis.
// The opening brace needs to be on the same line as the closing parenthesis.
// There should only be one space between the closing parenthesis - or the end of the
// return type - and the opening brace.
$opener = $tokens[$stackPtr]['scope_opener'];
if ($tokens[$opener]['line'] !== $tokens[$closeBracket]['line']) {
$error = 'The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line';
$fix = $phpcsFile->addFixableError($error, $opener, 'NewlineBeforeOpenBrace');
if ($fix === true) {
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true);
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addContent($prev, ' {');

// If the opener is on a line by itself, removing it will create
// an empty line, so remove the entire line instead.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true);
$next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);
$code = 'NewlineBeforeOpenBrace';

if ($tokens[$prev]['line'] < $tokens[$opener]['line']
&& $tokens[$next]['line'] > $tokens[$opener]['line']
) {
// Clear the whole line.
for ($i = ($prev + 1); $i < $next; $i++) {
if ($tokens[$i]['line'] === $tokens[$opener]['line']) {
$phpcsFile->fixer->replaceToken($i, '');
}
}
} else {
// Just remove the opener.
$phpcsFile->fixer->replaceToken($opener, '');
if ($tokens[$next]['line'] === $tokens[$opener]['line']
&& ($opener + 1) !== $next
) {
$phpcsFile->fixer->replaceToken(($opener + 1), '');
}
}

$phpcsFile->fixer->endChangeset();
}//end if
} else {
$prev = $tokens[($opener - 1)];
if ($prev['code'] !== T_WHITESPACE) {
$length = 0;
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), $closeBracket, true);
if ($tokens[$prev]['line'] === $tokens[$opener]['line']) {
// End of the return type is not on the same line as the close parenthesis.
$phpcsFile->addError($error, $opener, $code);
} else {
$length = strlen($prev['content']);
}

if ($length !== 1) {
$error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found %s spaces';
$fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', [$length]);
$fix = $phpcsFile->addFixableError($error, $opener, $code);
if ($fix === true) {
if ($length === 0) {
$phpcsFile->fixer->addContentBefore($opener, ' ');
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addContent($prev, ' {');

// If the opener is on a line by itself, removing it will create
// an empty line, so remove the entire line instead.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($opener - 1), $closeBracket, true);
$next = $phpcsFile->findNext(T_WHITESPACE, ($opener + 1), null, true);

if ($tokens[$prev]['line'] < $tokens[$opener]['line']
&& $tokens[$next]['line'] > $tokens[$opener]['line']
) {
// Clear the whole line.
for ($i = ($prev + 1); $i < $next; $i++) {
if ($tokens[$i]['line'] === $tokens[$opener]['line']) {
$phpcsFile->fixer->replaceToken($i, '');
}
}
} else {
$phpcsFile->fixer->replaceToken(($opener - 1), ' ');
// Just remove the opener.
$phpcsFile->fixer->replaceToken($opener, '');
if ($tokens[$next]['line'] === $tokens[$opener]['line']
&& ($opener + 1) !== $next
) {
$phpcsFile->fixer->replaceToken(($opener + 1), '');
}
}
}

$phpcsFile->fixer->endChangeset();
}//end if

return;
}//end if
}//end if

$prev = $tokens[($opener - 1)];
if ($prev['code'] !== T_WHITESPACE) {
$length = 0;
} else {
$length = strlen($prev['content']);
}

if ($length !== 1) {
$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';
$fix = $phpcsFile->addFixableError($error, ($opener - 1), 'SpaceBeforeOpenBrace', [$length]);
if ($fix === true) {
if ($length === 0) {
$phpcsFile->fixer->addContentBefore($opener, ' ');
} else {
$phpcsFile->fixer->replaceToken(($opener - 1), ' ');
}
}
}

}//end processMultiLineDeclaration()


Expand Down
14 changes: 14 additions & 0 deletions src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,17 @@ class Test
)
{}
}

// Prevent fixer conflict with itself.
function foo(
$param1,
)
: \SomeClass
{
}

function foo(
$param1,
$param2
) : // comment.
\Package\Sub\SomeClass {}
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,16 @@ class Test
) {
}
}

// Prevent fixer conflict with itself.
function foo(
$param1,
)
: \SomeClass {
}

function foo(
$param1,
$param2
) : // comment.
\Package\Sub\SomeClass {}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public function getErrorList($testFile='FunctionDeclarationUnitTest.inc')
402 => 1,
406 => 1,
475 => 1,
483 => 1,
490 => 2,
];
} else {
$errors = [
Expand Down

0 comments on commit 661224e

Please sign in to comment.