Skip to content

Commit

Permalink
Merge a35f5ed into 4282925
Browse files Browse the repository at this point in the history
  • Loading branch information
jrfnl committed Jan 22, 2020
2 parents 4282925 + a35f5ed commit a3d1c98
Show file tree
Hide file tree
Showing 5 changed files with 600 additions and 0 deletions.
85 changes: 85 additions & 0 deletions PHPCSUtils/Utils/Operators.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,36 @@
* @since 1.0.0 The `isReference()` method is based on and inspired by
* the method of the same name in the PHPCS native `File` class.
* Also see {@see \PHPCSUtils\BackCompat\BCFile}.
* The `isUnaryPlusMinus()` method is, in part, inspired by the
* `Squiz.WhiteSpace.OperatorSpacing` sniff.
*/
class Operators
{

/**
* Tokens which indicate that a plus/minus is unary when they preceed it.
*
* @since 1.0.0
*
* @var array <int|string> => <irrelevant>
*/
private static $extraUnaryIndicators = [
\T_STRING_CONCAT => true,
\T_RETURN => true,
\T_ECHO => true,
\T_PRINT => true,
\T_YIELD => true,
\T_COMMA => true,
\T_OPEN_PARENTHESIS => true,
\T_OPEN_SQUARE_BRACKET => true,
\T_OPEN_SHORT_ARRAY => true,
\T_OPEN_CURLY_BRACKET => true,
\T_COLON => true,
\T_INLINE_THEN => true,
\T_INLINE_ELSE => true,
\T_CASE => true,
];

/**
* Determine if the passed token is a reference operator.
*
Expand Down Expand Up @@ -140,4 +166,63 @@ public static function isReference(File $phpcsFile, $stackPtr)

return false;
}

/**
* Determine whether a T_MINUS/T_PLUS token is a unary operator.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the plus/minus token.
*
* @return bool True if the token passed is a unary operator.
* False otherwise or if the token is not a T_PLUS/T_MINUS token.
*/
public static function isUnaryPlusMinus(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();

if (isset($tokens[$stackPtr]) === false
|| ($tokens[$stackPtr]['code'] !== \T_PLUS
&& $tokens[$stackPtr]['code'] !== \T_MINUS)
) {
return false;
}

$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($next === false) {
// Live coding or parse error.
return false;
}

if (isset(BCTokens::operators()[$tokens[$next]['code']]) === true) {
// Next token is an operator, so this is not a unary.
return false;
}

$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);

/*
* Check the preceeding token for an indication that this is not an arithmetic operation.
*/
if (isset(BCTokens::operators()[$tokens[$prev]['code']]) === true
|| isset(BCTokens::comparisonTokens()[$tokens[$prev]['code']]) === true
|| isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true
|| isset(BCTokens::assignmentTokens()[$tokens[$prev]['code']]) === true
|| isset(Tokens::$castTokens[$tokens[$prev]['code']]) === true
|| isset(self::$extraUnaryIndicators[$tokens[$prev]['code']]) === true
) {
return true;
}

/*
* BC for PHPCS < 3.1.0 in which the PHP 5.5 T_YIELD token was not yet backfilled.
* Note: not accounting for T_YIELD_FROM as that would be a parse error anyway.
*/
if ($tokens[$prev]['code'] === \T_STRING && $tokens[$prev]['content'] === 'yield') {
return true;
}

return false;
}
}
23 changes: 23 additions & 0 deletions Tests/Utils/Operators/IsUnaryPlusMinusJSTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* testNonUnaryPlus */
result = 1 + 2;

/* testNonUnaryMinus */
result = 1-2;

/* testUnaryMinusColon */
$.localScroll({offset: {top: -32}});

switch (result) {
/* testUnaryMinusCase */
case -1:
break;
}

/* testUnaryMinusTernaryThen */
result = x?-y:z;

/* testUnaryPlusTernaryElse */
result = x ? y : +z;

/* testUnaryMinusIfCondition */
if (true || -1 == b) {}
74 changes: 74 additions & 0 deletions Tests/Utils/Operators/IsUnaryPlusMinusJSTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Tests\Utils\Operators;

use PHPCSUtils\Tests\Utils\Operators\IsUnaryPlusMinusTest;

/**
* Tests for the \PHPCSUtils\Utils\Operators::isUnaryPlusMinus() method.
*
* @covers \PHPCSUtils\Utils\Operators::isUnaryPlusMinus
*
* @group operators
*
* @since 1.0.0
*/
class IsUnaryPlusMinusJSTest extends IsUnaryPlusMinusTest
{

/**
* The file extension of the test case file (without leading dot).
*
* @var string
*/
protected static $fileExtension = 'js';

/**
* Data provider.
*
* @see IsUnaryPlusMinusTest::testIsUnaryPlusMinus() For the array format.
*
* @return array
*/
public function dataIsUnaryPlusMinus()
{
return [
'non-unary-plus' => [
'/* testNonUnaryPlus */',
false,
],
'non-unary-minus' => [
'/* testNonUnaryMinus */',
false,
],
'unary-minus-colon' => [
'/* testUnaryMinusColon */',
true,
],
'unary-minus-switch-case' => [
'/* testUnaryMinusCase */',
true,
],
'unary-minus-ternary-then' => [
'/* testUnaryMinusTernaryThen */',
true,
],
'unary-minus-ternary-else' => [
'/* testUnaryPlusTernaryElse */',
true,
],
'unary-minus-if-condition' => [
'/* testUnaryMinusIfCondition */',
true,
],
];
}
}
151 changes: 151 additions & 0 deletions Tests/Utils/Operators/IsUnaryPlusMinusTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

/* testNonUnaryPlus */
$a = 1 + 2;

/* testNonUnaryMinus */
$a = 1-2;

/* testNonUnaryPlusArrays */
$a = [1] + [2];

/* testUnaryMinusArithmetic */
$a = 1 / -2;

/* testUnaryPlusArithmetic */
$a = 1 ** +2;

/* testUnaryMinusConcatenation */
$a = 'ID' . -$var;

/* testUnaryPlusIntAssignment */
$a = +1;

/* testUnaryMinusVariableAssignment */
$a += -$b;

/* testUnaryPlusFloatAssignment */
$a **= + 1.1;

/* testUnaryMinusBoolAssignment */
$a /= -true;

/* testUnaryPlusStringAssignmentWithComment */
$a &= + /* comment */ '1';

/* testUnaryMinusStringAssignment */
$a .= - <<<'EOD'
123
EOD;

/* testUnaryPlusNullAssignment */
$a = +null;

/* testUnaryMinusVariableVariableAssignment */
$a .= -${$var};

/* testUnaryPlusIntComparison */
$a = ($b === + 1);

/* testUnaryPlusIntComparisonYoda */
$a = (+1 <=> $b);

/* testUnaryMinusFloatComparison */
$a = ($b !== - 1.1);

/* testUnaryMinusStringComparisonYoda */
$a = (-'1' != $b);

/* testUnaryPlusVariableBoolean */
$a = ($a && - $b);

/* testUnaryMinusVariableBoolean */
$a = ($a || -$b);

/* testUnaryPlusLogicalXor */
$a = ($a xor -$b);

/* testUnaryMinusTernaryThen */
$a = $a ? -1 :
/* testUnaryPlusTernaryElse */
+ 10;

/* testUnaryMinusCoalesce */
$a = $a ?? -1 :

/* testUnaryPlusIntReturn */
return +1;

/* testUnaryMinusFloatReturn */
return -1.1;

/* testUnaryPlusPrint */
print +$b;

/* testUnaryMinusEcho */
echo -$a;

/* testUnaryPlusYield */
yield +$a;

/* testUnaryPlusArrayAccess */
$a = $array[ + 2 ];

/* testUnaryMinusStringArrayAccess */
if ($line{-1} === ':') {}

$array = array(
/* testUnaryPlusLongArrayAssignment */
+1,
/* testUnaryMinusLongArrayAssignmentKey */
-1 =>
/* testUnaryPlusLongArrayAssignmentValue */
+20,
);

$array = [
/* testUnaryPlusShortArrayAssignment */
+1 =>
/* testNonUnaryMinusShortArrayAssignment */
5-20,
];

/* testUnaryMinusCast */
$a = (bool) -2;

functionCall(
/* testUnaryPlusFunctionCallParam */
+2,
/* testUnaryMinusFunctionCallParam */
- 123.456,
);

/* testUnaryPlusDeclare */
declare( ticks = +10 );

switch ($a) {
/* testUnaryPlusCase */
case +20:
// Something.
break;

/* testUnaryMinusCase */
case -1.23:
// Something.
break;
}

// Testing `$a = -+-+10`;
$a =
/* testSequenceNonUnary1 */
-
/* testSequenceNonUnary2 */
+
/* testSequenceNonUnary3 */
-
/* testSequenceUnaryEnd */
+ /*comment*/ 10;

// Intentional parse error. This has to be the last test in the file.
/* testParseError */
$a = -
Loading

0 comments on commit a3d1c98

Please sign in to comment.