-
-
Notifications
You must be signed in to change notification settings - Fork 492
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Only slightly adapted from the [work](https://github.com/zendframework/zend-coding-standard/blob/52b435c714879609262aa36b004c4075cd967acc/src/ZendCodingStandard/Sniffs/Formatting/ReturnTypeSniff.php) Michał Bundyra (@webimpress) did for the Zend Coding Standard. Required format for WordPress is the same as most others in the PHP community: - no space before the colon - exactly one space after colon before return type - no space after nullable operator (hasn't been discussed yet, but I see no reason to vary) - simple types should be given as lowercase End result: ```php function foo(): bool { // Simple return type. }; function foo(): ?string { // Nullable simple return type. }; function foo( $a ): \MyClass { // Return type of a class. }; ``` ❗️ Note: Not yet added to `WordPress-Core/ruleset.xml`. See #547.
- Loading branch information
Showing
4 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
<?php | ||
/** | ||
* WordPress Coding Standard. | ||
* | ||
* @package WPCS\WordPressCodingStandards | ||
* @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards | ||
* @license https://opensource.org/licenses/MIT MIT | ||
*/ | ||
|
||
namespace WordPress\Sniffs\Functions; | ||
|
||
use PHP_CodeSniffer_Tokens as Tokens; | ||
use WordPress\Sniff; | ||
|
||
/** | ||
* Enforces formatting of return types. | ||
* | ||
* @package WPCS\WordPressCodingStandards | ||
* | ||
* @since 0.14.0 | ||
* | ||
* @link https://github.com/zendframework/zend-coding-standard/blob/52b435c714879609262aa36b004c4075cd967acc/src/ZendCodingStandard/Sniffs/Formatting/ReturnTypeSniff.php | ||
*/ | ||
class ReturnTypeSniff extends Sniff { | ||
/** | ||
* Simple return types. | ||
* | ||
* @since 0.14.0 | ||
* | ||
* @var string[] | ||
*/ | ||
private $simple_return_types = array( | ||
'void', | ||
'int', | ||
'float', | ||
'double', | ||
'string', | ||
'array', | ||
'iterable', | ||
'callable', | ||
'parent', | ||
'self', | ||
'bool', | ||
); | ||
|
||
/** | ||
* Returns an array of tokens this test wants to listen for. | ||
* | ||
* @return array | ||
*/ | ||
public function register() { | ||
return array( | ||
T_RETURN_TYPE, | ||
); | ||
} | ||
|
||
/** | ||
* Processes this test, when one of its tokens is encountered. | ||
* | ||
* @param int $stackPtr The position of the current token in the stack. | ||
*/ | ||
public function process_token( $stackPtr ) { | ||
$colon = (int) $this->phpcsFile->findPrevious( T_COLON, $stackPtr - 1 ); | ||
|
||
// Space before colon disallowed. | ||
if ( T_CLOSE_PARENTHESIS !== $this->tokens[ $colon - 1 ]['code'] ) { | ||
$error = 'There must be no space before colon before a return type.'; | ||
$fix = $this->phpcsFile->addFixableError( $error, $colon - 1, 'SpaceBeforeColon' ); | ||
|
||
if ( true === $fix ) { | ||
$this->phpcsFile->fixer->beginChangeset(); | ||
$token = $colon - 1; | ||
do { | ||
$this->phpcsFile->fixer->replaceToken( $token, '' ); | ||
-- $token; | ||
} while ( T_CLOSE_PARENTHESIS !== $this->tokens[ $token ]['code'] ); | ||
$this->phpcsFile->fixer->endChangeset(); | ||
} | ||
} | ||
|
||
// Only one space after colon. | ||
if ( T_WHITESPACE !== $this->tokens[ $colon + 1 ]['code'] ) { | ||
$error = 'There must be a space after colon and before return type declaration.'; | ||
$fix = $this->phpcsFile->addFixableError( $error, $colon, 'NoSpaceAfterColon' ); | ||
if ( true === $fix ) { | ||
$this->phpcsFile->fixer->addContent( $colon, ' ' ); | ||
} | ||
} elseif ( ' ' !== $this->tokens[ $colon + 1 ]['content'] ) { | ||
$error = 'There must be exactly one space after colon and before return type declaration.'; | ||
$fix = $this->phpcsFile->addFixableError( $error, $colon + 1, 'TooManySpacesAfterColon' ); | ||
if ( true === $fix ) { | ||
$this->phpcsFile->fixer->replaceToken( $colon + 1, ' ' ); | ||
} | ||
} | ||
|
||
$nullable = $this->phpcsFile->findNext( T_NULLABLE, $colon + 1, $stackPtr ); | ||
if ( $nullable ) { | ||
// Check if there is space after nullable operator. | ||
if ( T_WHITESPACE === $this->tokens[ $nullable + 1 ]['code'] ) { | ||
$error = 'Space is not not allowed after nullable operator.'; | ||
$fix = $this->phpcsFile->addFixableError( $error, $nullable + 1, 'SpaceAfterNullable' ); | ||
if ( $fix ) { | ||
$this->phpcsFile->fixer->replaceToken( $nullable + 1, '' ); | ||
} | ||
} | ||
} | ||
|
||
$first = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $nullable ?: $colon ) + 1, null, true ); | ||
$end = $this->phpcsFile->findNext( [ T_SEMICOLON, T_OPEN_CURLY_BRACKET ], $stackPtr + 1 ); | ||
$last = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $end - 1, null, true ); | ||
$invalid = $this->phpcsFile->findNext( [ T_STRING, T_NS_SEPARATOR, T_RETURN_TYPE ], $first, $last + 1, true ); | ||
if ( $invalid ) { | ||
$error = 'Return type declaration contains invalid token %s'; | ||
$data = array( $this->tokens[ $invalid ]['type'] ); | ||
$fix = $this->phpcsFile->addFixableError( $error, $invalid, 'InvalidToken', $data ); | ||
if ( true === $fix ) { | ||
$this->phpcsFile->fixer->replaceToken( $invalid, '' ); | ||
} | ||
|
||
return; | ||
} | ||
|
||
$returnType = trim( $this->phpcsFile->getTokensAsString( $first, $last - $first + 1 ) ); | ||
|
||
if ( $first === $last | ||
&& in_array( strtolower( $returnType ), $this->simple_return_types, true ) | ||
&& ! in_array( $returnType, $this->simple_return_types, true ) | ||
) { | ||
$error = 'Simple return type must be lowercase. Found "%s", expected "%s"'; | ||
$data = array( | ||
$returnType, | ||
strtolower( $returnType ), | ||
); | ||
$fix = $this->phpcsFile->addFixableError( $error, $first, 'LowerCaseSimpleType', $data ); | ||
if ( true === $fix ) { | ||
$this->phpcsFile->fixer->replaceToken( $stackPtr, strtolower( $returnType ) ); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<?php | ||
|
||
function fooa(): array {} // Good. | ||
function foob() : array {} // Bad. | ||
function fooc() : array {} // Bad. | ||
function food() // Bad. | ||
: array {} | ||
function fooe():array {} // Bad. | ||
function foof(): array {} // Bad. | ||
function foog(): | ||
array {} | ||
|
||
// Don't touch case statements here. | ||
$v = 1; | ||
switch ($v) { | ||
case 1: | ||
$x = $f1(-1 * $v); | ||
break; | ||
case (2) : | ||
$x = $f2(-1 * $v, $v); | ||
break; | ||
default: | ||
$x = $f1($v) + $f2(-1 * $v, $v); | ||
break; | ||
} | ||
|
||
class ReturnType | ||
{ | ||
public function method() :array // Bad. | ||
{ | ||
return []; | ||
} | ||
|
||
private function priv( | ||
$a, | ||
$b | ||
) | ||
: int { // Bad. | ||
return $a * $b; | ||
} | ||
} | ||
|
||
$c = new class() { | ||
public function method() : | ||
float { | ||
return mt_rand(); | ||
} | ||
}; | ||
|
||
abstract class AbsCla | ||
{ | ||
abstract public function x() :bool; // Bad. | ||
} | ||
|
||
interface MyInterface | ||
{ // All below are bad. | ||
public function a():vOid; | ||
public function b() : Int; | ||
public function c() :?int; | ||
public function d() :Float; | ||
public function e() :? float; | ||
public function f():Double; | ||
public function g():?double; | ||
public function h():Array; | ||
public function i() : ?array; | ||
public function j():String; | ||
public function k():?string; | ||
public function l():Parent; | ||
public function m():?parent; | ||
public function n():Callable; | ||
public function o():?callable; | ||
public function p():Bool; | ||
public function q():?bool; | ||
public function r():Self; | ||
public function s():?self; | ||
public function t():Iterable; | ||
public function u():?iterable; | ||
|
||
public function v($a) : \DateTime; | ||
public function w():?\DateTime; | ||
|
||
public function y($a, $b, $c) : \My\TestClass; | ||
public function z():? \ReturnType \MyType; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
function fooa(): array {} // Good. | ||
function foob(): array {} // Bad. | ||
function fooc(): array {} // Bad. | ||
function food(): array {} | ||
function fooe(): array {} // Bad. | ||
function foof(): array {} // Bad. | ||
function foog(): array {} | ||
|
||
// Don't touch case statements here. | ||
$v = 1; | ||
switch ($v) { | ||
case 1: | ||
$x = $f1(-1 * $v); | ||
break; | ||
case (2) : | ||
$x = $f2(-1 * $v, $v); | ||
break; | ||
default: | ||
$x = $f1($v) + $f2(-1 * $v, $v); | ||
break; | ||
} | ||
|
||
class ReturnType | ||
{ | ||
public function method(): array // Bad. | ||
{ | ||
return []; | ||
} | ||
|
||
private function priv( | ||
$a, | ||
$b | ||
): int { // Bad. | ||
return $a * $b; | ||
} | ||
} | ||
|
||
$c = new class() { | ||
public function method(): float { | ||
return mt_rand(); | ||
} | ||
}; | ||
|
||
abstract class AbsCla | ||
{ | ||
abstract public function x(): bool; // Bad. | ||
} | ||
|
||
interface MyInterface | ||
{ // All below are bad. | ||
public function a(): void; | ||
public function b(): int; | ||
public function c(): ?int; | ||
public function d(): float; | ||
public function e(): ?float; | ||
public function f(): double; | ||
public function g(): ?double; | ||
public function h(): array; | ||
public function i(): ?array; | ||
public function j(): string; | ||
public function k(): ?string; | ||
public function l(): parent; | ||
public function m(): ?parent; | ||
public function n(): callable; | ||
public function o(): ?callable; | ||
public function p(): bool; | ||
public function q(): ?bool; | ||
public function r(): self; | ||
public function s(): ?self; | ||
public function t(): iterable; | ||
public function u(): ?iterable; | ||
|
||
public function v($a): \DateTime; | ||
public function w(): ?\DateTime; | ||
|
||
public function y($a, $b, $c): \My\TestClass; | ||
public function z(): ?\ReturnType\MyType; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
/** | ||
* Unit test class for WordPress Coding Standard. | ||
* | ||
* @package WPCS\WordPressCodingStandards | ||
* @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards | ||
* @license https://opensource.org/licenses/MIT MIT | ||
*/ | ||
|
||
namespace WordPress\Tests\Functions; | ||
|
||
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest; | ||
|
||
/** | ||
* Unit test class for the ReturnType sniff. | ||
* | ||
* @package WPCS\WordPressCodingStandards | ||
* | ||
* @since 0.14.0 | ||
*/ | ||
class ReturnTypeUnitTest extends AbstractSniffUnitTest { | ||
|
||
/** | ||
* Returns the lines where errors should occur. | ||
* | ||
* @return array <int line number> => <int number of errors> | ||
*/ | ||
public function getErrorList() { | ||
return array( | ||
4 => 1, | ||
5 => 1, | ||
6 => 1, | ||
8 => 1, | ||
9 => 1, | ||
10 => 1, | ||
29 => 2, | ||
38 => 1, | ||
44 => 2, | ||
52 => 2, | ||
57 => 2, | ||
58 => 2, | ||
59 => 2, | ||
60 => 3, | ||
61 => 3, | ||
62 => 2, | ||
63 => 1, | ||
64 => 2, | ||
65 => 1, | ||
66 => 2, | ||
67 => 1, | ||
68 => 2, | ||
69 => 1, | ||
70 => 2, | ||
71 => 1, | ||
72 => 2, | ||
73 => 1, | ||
74 => 2, | ||
75 => 1, | ||
76 => 2, | ||
77 => 1, | ||
79 => 2, | ||
80 => 1, | ||
82 => 1, | ||
83 => 3, | ||
); | ||
} | ||
|
||
/** | ||
* Returns the lines where warnings should occur. | ||
* | ||
* @return array <int line number> => <int number of warnings> | ||
*/ | ||
public function getWarningList() { | ||
return array(); | ||
|
||
} | ||
|
||
} |