Skip to content

Commit

Permalink
Merge branch 'hotfix/23'
Browse files Browse the repository at this point in the history
Close #23
  • Loading branch information
michalbundyra committed May 17, 2019
2 parents a61bee2 + d48f4ed commit 4c4599c
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ All notable changes to this project will be documented in this file, in reverse
- `Functions\Param` - param typehint and type within `@param` tag,
- `Functions\ReturnType` - return type declaration and type within `@return` tag.

- [#23](https://github.com/webimpress/coding-standard/pull/23) `Functions\ReturnType` - fixes recognising yoda comparison in return statement and type of returned value

## 1.0.2 - 2019-05-12

### Added
Expand Down
112 changes: 90 additions & 22 deletions src/WebimpressCodingStandard/Sniffs/Functions/ReturnTypeSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
use const T_ARRAY;
use const T_ARRAY_CAST;
use const T_BOOL_CAST;
use const T_BOOLEAN_AND;
use const T_BOOLEAN_NOT;
use const T_BOOLEAN_OR;
use const T_CLOSE_PARENTHESIS;
use const T_CLOSURE;
use const T_COALESCE;
use const T_COLON;
use const T_CONSTANT_ENCAPSED_STRING;
use const T_DIR;
Expand All @@ -48,9 +51,21 @@
use const T_FALSE;
use const T_FILE;
use const T_FUNCTION;
use const T_GREATER_THAN;
use const T_INLINE_ELSE;
use const T_INLINE_THEN;
use const T_INT_CAST;
use const T_IS_EQUAL;
use const T_IS_GREATER_OR_EQUAL;
use const T_IS_IDENTICAL;
use const T_IS_NOT_EQUAL;
use const T_IS_NOT_IDENTICAL;
use const T_IS_SMALLER_OR_EQUAL;
use const T_LESS_THAN;
use const T_LNUMBER;
use const T_LOGICAL_AND;
use const T_LOGICAL_OR;
use const T_LOGICAL_XOR;
use const T_NEW;
use const T_NS_SEPARATOR;
use const T_NULL;
Expand All @@ -62,6 +77,7 @@
use const T_RETURN;
use const T_SELF;
use const T_SEMICOLON;
use const T_SPACESHIP;
use const T_STATIC;
use const T_STRING;
use const T_STRING_CAST;
Expand Down Expand Up @@ -745,10 +761,84 @@ static function (string $a, string $b) {
}
}

private function endOfExpression(File $phpcsFile, int $ptr) : ?int
{
$tokens = $phpcsFile->getTokens();

do {
if ($tokens[$ptr]['code'] === T_OPEN_PARENTHESIS
|| $tokens[$ptr]['code'] === T_ARRAY
) {
$ptr = $tokens[$ptr]['parenthesis_closer'];
} elseif ($tokens[$ptr]['code'] === T_OPEN_SQUARE_BRACKET
|| $tokens[$ptr]['code'] === T_OPEN_CURLY_BRACKET
|| $tokens[$ptr]['code'] === T_OPEN_SHORT_ARRAY
) {
$ptr = $tokens[$ptr]['bracket_closer'];
} elseif (in_array(
$tokens[$ptr]['code'],
Tokens::$booleanOperators + Tokens::$comparisonTokens
+ [
T_SEMICOLON,
T_CLOSE_PARENTHESIS,
T_INLINE_ELSE,
T_INLINE_THEN,
],
true
)) {
return $ptr;
}
} while (++$ptr);

return null;
}

private function getReturnValue(File $phpcsFile, int $ptr) : string
{
$tokens = $phpcsFile->getTokens();

$boolExpressionTokens = [
// comparision tokens
T_IS_EQUAL,
T_IS_IDENTICAL,
T_IS_NOT_EQUAL,
T_IS_NOT_IDENTICAL,
T_LESS_THAN,
T_GREATER_THAN,
T_IS_SMALLER_OR_EQUAL,
T_IS_GREATER_OR_EQUAL,
// boolean operators
T_BOOLEAN_AND,
T_BOOLEAN_OR,
T_LOGICAL_AND,
T_LOGICAL_OR,
T_LOGICAL_XOR,
];

$endOfExpression = $this->endOfExpression($phpcsFile, $ptr);
$isBool = in_array($tokens[$endOfExpression]['code'], $boolExpressionTokens, true);

while ($endOfExpression && in_array($tokens[$endOfExpression]['code'], $boolExpressionTokens, true)) {
$endOfExpression = $this->endOfExpression($phpcsFile, $endOfExpression + 1);
}

if ($endOfExpression
&& ($tokens[$endOfExpression]['code'] === T_INLINE_THEN
|| $tokens[$endOfExpression]['code'] === T_COALESCE
|| $tokens[$endOfExpression]['code'] === T_SPACESHIP)
) {
return 'unknown';
}

if ($isBool) {
if (! $this->hasCorrectType(['bool', '?bool'], ['bool', 'boolean'])) {
$error = 'Function return type is not bool, but function returns boolean value here';
$phpcsFile->addError($error, $ptr, 'ReturnBool');
}

return 'bool';
}

switch ($tokens[$ptr]['code']) {
case T_ARRAY:
case T_ARRAY_CAST:
Expand All @@ -769,28 +859,6 @@ private function getReturnValue(File $phpcsFile, int $ptr) : string

case T_BOOL_CAST:
case T_BOOLEAN_NOT:
$end = $ptr;
while (++$end) {
if ($tokens[$end]['code'] === T_OPEN_PARENTHESIS) {
$end = $tokens[$end]['parenthesis_closer'];
continue;
}
if ($tokens[$end]['code'] === T_OPEN_SQUARE_BRACKET
|| $tokens[$end]['code'] === T_OPEN_CURLY_BRACKET
|| $tokens[$end]['code'] === T_OPEN_SHORT_ARRAY
) {
$end = $tokens[$end]['bracket_closer'];
continue;
}

if (in_array($tokens[$end]['code'], [T_SEMICOLON, T_INLINE_THEN], true)) {
break;
}
}
if ($tokens[$end]['code'] !== T_SEMICOLON) {
return 'unknown';
}

if (! $this->hasCorrectType(['bool', '?bool'], ['bool', 'boolean'])) {
$error = 'Function return type is not bool, but function returns boolean value here';
$phpcsFile->addError($error, $ptr, 'ReturnBool');
Expand Down
37 changes: 37 additions & 0 deletions test/Sniffs/Functions/ReturnTypeUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,41 @@ abstract class FunctionCommentReturn
{
return new $a();
}

public function yodaNull(?int $a) : bool
{
return null !== $a;
}

public function complexReturn($a, $b) : bool
{
return 0 === (static function() {
return ['y'];
})() + ['x'] + array('z') + $a[0] + $b{0};
}

public function ternaryUnrecognisedType1($a) : int
{
return null === $a ?: 'b';
}

public function ternaryUnrecognisedType2($b) : int
{
return null === $b && 1 < 2 ? 'a' : 'b';
}

public function ternaryUnrecognisedType3($a, $b) : int
{
return $a || $b ? 'a' : 'b';
}

public function coalesceUnrecognisedType() : int
{
return self::$a ?? 'b';
}

public function spaceshipUnrecognisedType(array $a, $b, $c) : string
{
return $a[0] * $b <=> $c ? ['a'] : 0.1;
}
}

0 comments on commit 4c4599c

Please sign in to comment.