Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(MultiLineFunctionDeclaration): Add new sniff for multi-line function declarations and trailing commas #226

Merged
merged 5 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* before the opening parenthesis.
*
* @deprecated in Coder 8.x, will be removed in Coder 9.x.
* Squiz.Functions.MultiLineFunctionDeclaration is used instead, see ruleset.xml.
* MultiLineFunctionDeclarationSniff is used instead.
*
* @category PHP
* @package PHP_CodeSniffer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php
/**
* \Drupal\Sniffs\Functions\MultiLineFunctionDeclarationSniff
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/

namespace Drupal\Sniffs\Functions;

use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceKernighanRitchieSniff;
use PHP_CodeSniffer\Standards\PEAR\Sniffs\Functions\FunctionDeclarationSniff as PearFunctionDeclarationSniff;
use PHP_CodeSniffer\Standards\Squiz\Sniffs\Functions\MultiLineFunctionDeclarationSniff as SquizFunctionDeclarationSniff;
use PHP_CodeSniffer\Util\Tokens;

/**
* Multi-line function declarations need to have a trailing comma on the last
* parameter. Modified from Squiz, whenever there is a function declaration
* closing parenthesis on a new line we treat it as multi-line.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class MultiLineFunctionDeclarationSniff extends SquizFunctionDeclarationSniff
{


/**
* The number of spaces code should be indented.
*
* @var integer
*/
public $indent = 2;


/**
* Processes single-line declarations.
*
* Just uses the Generic Kernighan Ritchie sniff.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param array<int, array<string, mixed>> $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens)
{
$sniff = new OpeningFunctionBraceKernighanRitchieSniff();
$sniff->checkClosures = true;
$sniff->process($phpcsFile, $stackPtr);

}//end processSingleLineDeclaration()


/**
* Determine if this is a multi-line function declaration.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $openBracket The position of the opening bracket
* in the stack passed in $tokens.
* @param array<int, array<string, mixed>> $tokens The stack of tokens that make up
* the file.
*
* @return bool
*/
public function isMultiLineDeclaration($phpcsFile, $stackPtr, $openBracket, $tokens)
{
$function = $tokens[$stackPtr];
if ($tokens[$function['parenthesis_opener']]['line'] === $tokens[$function['parenthesis_closer']]['line']) {
return false;
}

return true;

}//end isMultiLineDeclaration()


/**
* Processes multi-line declarations.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param array<int, array<string, mixed>> $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens)
{
// We do everything the grandparent sniff does, and a bit more.
PearFunctionDeclarationSniff::processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens);

$openBracket = $tokens[$stackPtr]['parenthesis_opener'];
$this->processBracket($phpcsFile, $openBracket, $tokens, 'function');

// Trailing commas on the last function parameter are only possible in
// PHP 8.0+.
if (version_compare(PHP_VERSION, '8.0.0') < 0) {
return;
}

$function = $tokens[$stackPtr];

$lastTrailingComma = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($function['parenthesis_closer'] - 1),
$function['parenthesis_opener'],
true
);
if ($tokens[$lastTrailingComma]['code'] !== T_COMMA) {
$error = 'Multi-line function declarations must have a trailing comma after the last parameter';
$fix = $phpcsFile->addFixableError($error, $lastTrailingComma, 'MissingTrailingComma');
if ($fix === true) {
$phpcsFile->fixer->addContent($lastTrailingComma, ',');
}
}

}//end processMultiLineDeclaration()


}//end class
18 changes: 0 additions & 18 deletions coder_sniffer/Drupal/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@
<rule ref="Generic.Functions.FunctionCallArgumentSpacing.NoSpaceAfterComma">
<severity>0</severity>
</rule>
<rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie">
<properties>
<property name="checkClosures" value="true"/>
</properties>
</rule>

<rule ref="Generic.NamingConventions.ConstructorName" />
<rule ref="Generic.NamingConventions.UpperCaseConstantName" />
Expand Down Expand Up @@ -255,19 +250,6 @@
<severity>0</severity>
</rule>

<rule ref="Squiz.Functions.MultiLineFunctionDeclaration">
<properties>
<property name="indent" value="2"/>
</properties>
</rule>
<!-- Disable some errors that are already coverd by other sniffs. -->
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.ContentAfterBrace">
<severity>0</severity>
</rule>

<rule ref="Squiz.PHP.LowercasePHPFunctions" />
<rule ref="Squiz.PHP.NonExecutableCode" />
<rule ref="Squiz.Strings.ConcatenationSpacing">
Expand Down
51 changes: 51 additions & 0 deletions tests/Drupal/Functions/MultiLineFunctionDeclarationUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/**
* @file
* Some description.
*/

/**
* Test.
*/
function missing_trailing_comma(
$a,
$b
) {

}

$foo = 1;
$bar = 2;
$x = function (
$a,
$b
) use (
$foo,
$bar
) {

};

/**
*
*/
class Test {

/**
* Test.
*/
public function lookupSource(string $key, string $migrationNames = '', array $options = [
'all' => NULL,
'group' => NULL,
]): void {
}

}

$x = function (
$a,
$b,
) use ($foo, $bar) {

};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/**
* @file
* Some description.
*/

/**
* Test.
*/
function missing_trailing_comma(
$a,
$b,
) {

}

$foo = 1;
$bar = 2;
$x = function (
$a,
$b,
) use (
$foo,
$bar
) {

};

/**
*
*/
class Test {

/**
* Test.
*/
public function lookupSource(
string $key,
string $migrationNames = '',
array $options = [
'all' => NULL,
'group' => NULL,
],
): void {
}

}

$x = function (
$a,
$b,
) use ($foo, $bar) {

};
66 changes: 66 additions & 0 deletions tests/Drupal/Functions/MultiLineFunctionDeclarationUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Drupal\Test\Functions;

use Drupal\Test\CoderSniffUnitTest;

class MultiLineFunctionDeclarationUnitTest extends CoderSniffUnitTest
{


/**
* Returns the lines where errors should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of errors that should occur on that line.
*
* @param string $testFile The name of the file being tested.
*
* @return array<int, int>
*/
protected function getErrorList(string $testFile): array
{
return [
13 => 1,
22 => 1,
38 => 3,
41 => 2,
];

}//end getErrorList()


/**
* Returns the lines where warnings should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of warnings that should occur on that line.
*
* @param string $testFile The name of the file being tested.
*
* @return array<int, int>
*/
protected function getWarningList(string $testFile): array
{
return [];

}//end getWarningList()


/**
* Skip this test on PHP versions lower than 8 because the syntax is not allowed there.
*
* @return bool
*/
protected function shouldSkipTest()
{
if (version_compare(PHP_VERSION, '8.0.0') < 0) {
return true;
}

return false;

}//end shouldSkipTest()


}//end class
18 changes: 17 additions & 1 deletion tests/Drupal/bad/BadUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ protected function getErrorList(string $testFile): array
836 => 1,
838 => 1,
849 => 2,
860 => 1,
860 => 2,
864 => 2,
];
}//end switch
Expand Down Expand Up @@ -480,4 +480,20 @@ protected function checkAllSniffCodes()
}//end checkAllSniffCodes()


/**
* Skip this test on PHP versions lower than 8 because of MultiLineTrailingCommaSniff.
*
* @return bool
*/
protected function shouldSkipTest()
{
if (version_compare(PHP_VERSION, '8.0.0') < 0) {
return true;
}

return false;

}//end shouldSkipTest()


}//end class
2 changes: 1 addition & 1 deletion tests/Drupal/bad/bad.php.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@ class ScopeKeyword {
*/
function test29(
int $a,
string $b
string $b,
) {
echo "Hello";
}
2 changes: 1 addition & 1 deletion tests/Drupal/good/good.php
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,7 @@ function test18(
CacheTagsInvalidatorInterface $cache_invalidator,
ModuleHandlerInterface $module_handler,
EntityFieldManagerInterface $entity_field_manager,
EntityTypeBundleInfoInterface $entity_type_bundle_info
EntityTypeBundleInfoInterface $entity_type_bundle_info,
) {
return 0;
}
Expand Down
Loading