Repository: VKCOM/noverify
Pull request #638 (2020-08-06 11:48:11)

linter: added embedded rules for assign
Example:
```php
$a = $a + $b => $a += $b
$a = $a - $b => $a -= $b
$a = $a * $b => $a *= $b
$a = $a / $b => $a /= $b
$a = $a % $b => $a %= $b

$a = $a . $b => $a .= $b

$a = $a & $b => $a &= $b
$a = $a | $b => $a |= $b
$a = $a ^ $b => $a ^= $b
$a = $a << $b => $a <<= $b
$a = $a >> $b => $a >>= $b
$a = $a ?? $b => $a ??= $b
```

For Coalesce added case to `phpgrep` to handle `assign.Coalesce`.

Added tests.


Patch:
diff --git a/src/cmd/embeddedrules/rules.go b/src/cmd/embeddedrules/rules.go
index ce2ebed..db574b4 100644
--- a/src/cmd/embeddedrules/rules.go
+++ b/src/cmd/embeddedrules/rules.go
@@ -77,7 +77,7 @@ func (fi bindataFileInfo) Sys() interface{} {
 	return nil
 }
 
-var _embeddedrulesRulesPhp = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x8d\xc1\x6a\xb4\x30\x14\x46\xf7\xf7\x29\xbe\x1f\x5c\xa8\x1b\xf7\xf3\x17\xd2\xee\x67\xd7\x17\x98\xa8\x57\xbc\x25\x93\x84\xe4\xca\x28\xa5\xef\x5e\x74\x46\xea\xa2\x14\x0a\xdd\x04\x72\xb8\xdf\x39\x4f\x26\x8e\x91\xa8\xa9\x6b\x42\x8d\x67\x1f\xc4\xe7\xc8\x9d\x4a\xf0\x78\x39\x9f\x37\xe8\xc4\x2b\x27\xf4\x92\x6d\xeb\x98\x50\x37\x44\xc3\xe4\xef\x47\xca\xc9\xdb\xb4\xbc\xca\x35\x3a\x19\x96\xb2\xc2\x3b\x01\x9b\x0f\xeb\xf8\x6a\x97\x96\xd1\x85\xc9\xf5\x48\x1c\x9d\xed\x18\x3a\xf2\xbe\xc3\x4d\x74\xc4\xdb\x94\x15\x45\x17\x7c\xff\x58\xe9\x12\x19\x6d\x08\xee\x40\x1b\xc2\xfd\x07\x03\x4d\x13\xe3\x84\xc1\xba\xcc\xff\xe9\x87\xe0\x2d\x89\x32\x6c\xc6\xa5\x5c\x75\xd5\x26\xb8\x1c\x2b\xff\xfe\x36\x53\xcc\x30\x27\x14\xcb\xde\x18\x64\xc6\xce\x1e\x28\x4e\x89\x51\xcc\x5f\xb5\x19\x66\x7d\xd6\x93\x5f\x54\xcc\x37\x15\xb3\x57\x56\xaf\xe4\xcc\x5a\x16\x73\x75\xd4\x7f\xd0\x67\x00\x00\x00\xff\xff\x38\xe8\x4b\x41\xf3\x01\x00\x00")
+var _embeddedrulesRulesPhp = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\xd4\xc1\x8a\xb3\x30\x10\x07\xf0\xbb\xe0\x3b\xcc\x07\xb6\xb4\x7e\xb4\xde\xbb\x56\x77\xef\x85\x3d\xec\xbd\xd4\xda\x71\x9b\x25\x8d\x21\x89\x54\xd9\xee\xbb\x2f\x31\x58\x2a\x84\x9a\x1c\xf6\xa2\xf8\x67\x66\x7e\x86\xc0\xa4\x39\x3f\xf3\x30\x08\x83\x24\x8e\xc3\x00\x62\x78\x65\x35\x61\x92\x63\xa9\x48\xcd\xe0\x6d\xb7\x33\x29\x25\x4c\xa1\x80\x13\x91\xc5\x91\xa2\xce\x12\xdd\x55\x35\xcc\x14\x2a\x14\xac\x10\xdd\x07\xb9\x70\x4a\xaa\x6e\xb1\x84\xef\x30\x00\x30\x53\x41\x4f\xb8\x14\xdd\x11\xa1\xac\x1b\x7a\x02\x81\x9c\x16\x25\x82\x3a\xe3\xd0\x09\x57\xa2\xce\xf0\xd5\x48\x05\x51\x59\xb3\xd3\xd0\xa6\x3a\x8e\x70\xac\x6b\xfa\x18\x27\xfa\xd5\x7f\x43\x0e\x4a\x34\x08\x1b\xa8\x0a\x2a\xf1\x45\xff\xd4\x13\xf6\x2a\x88\x42\x28\x24\x1c\x16\x7a\xe6\xb2\x9f\x71\x18\x59\xff\xfe\x00\x8b\x5a\xc8\x37\x10\x75\x77\xa9\x22\x2d\x0c\xe1\x90\xf1\x46\x20\x44\xed\x23\xda\x42\xae\x1f\xba\xca\x0f\xcb\x6d\x58\x7e\xc7\xfa\xe9\x44\x4a\x54\x8b\xa8\x5d\x8e\x90\x9f\xd1\xb5\x16\x52\x92\x4f\xf6\xce\x27\xef\x73\xc4\xff\xdf\x5a\xf8\x3e\x7c\x7a\xd6\x6d\x5f\xe6\x7b\xd6\x95\x0d\x5b\xb9\x61\x2b\x5f\x2c\xb6\x61\xb1\x1b\x16\xfb\x62\x89\x0d\x4b\xdc\xb0\xc4\x17\x9b\xd9\xb0\x99\x1b\x36\xf3\xc5\xe6\x36\x6c\xee\x86\xcd\x7d\xb1\x9b\x0d\xbb\xb9\x61\x37\x5f\x6c\x6f\xc3\xf6\x6e\xd8\xde\x17\x4b\x53\x9b\x66\xd2\x69\x2e\x4d\x7d\xbd\x2c\xb3\x79\x26\x9d\xf6\xb2\xcc\xd7\x5b\xdb\xb8\xb5\x9b\xb6\xf6\xdf\x99\x36\xcd\xa4\xd3\x5c\xbf\x5c\xcd\xfa\xfc\x0d\x00\x00\xff\xff\x53\x84\xf1\x97\x53\x07\x00\x00")
 
 func embeddedrulesRulesPhpBytes() ([]byte, error) {
 	return bindataRead(
diff --git a/src/cmd/embeddedrules/rules.php b/src/cmd/embeddedrules/rules.php
index 612340e..4d11707 100644
--- a/src/cmd/embeddedrules/rules.php
+++ b/src/cmd/embeddedrules/rules.php
@@ -31,3 +31,89 @@ function ternarySimplify() {
    */
   isset($x) ? $x : $y;
 }
+
+function assignOp() {
+  /**
+   * @maybe could rewrite as `$x += $y`
+   * @fix $x += $y
+   * @pure $x
+   */
+  $x = $x + $y;
+
+  /**
+   * @maybe could rewrite as `$x -= $y`
+   * @fix $x -= $y
+   * @pure $x
+   */
+  $x = $x - $y;
+
+  /**
+   * @maybe could rewrite as `$x *= $y`
+   * @fix $x *= $y
+   * @pure $x
+   */
+  $x = $x * $y;
+
+  /**
+   * @maybe could rewrite as `$x /= $y`
+   * @fix $x /= $y
+   * @pure $x
+   */
+  $x = $x / $y;
+
+  /**
+   * @maybe could rewrite as `$x %= $y`
+   * @fix $x %= $y
+   * @pure $x
+   */
+  $x = $x % $y;
+
+  /**
+   * @maybe could rewrite as `$x &= $y`
+   * @fix $x &= $y
+   * @pure $x
+   */
+  $x = $x & $y;
+
+  /**
+   * @maybe could rewrite as `$x |= $y`
+   * @fix $x |= $y
+   * @pure $x
+   */
+  $x = $x | $y;
+
+  /**
+   * @maybe could rewrite as `$x ^= $y`
+   * @fix $x ^= $y
+   * @pure $x
+   */
+  $x = $x ^ $y;
+
+  /**
+   * @maybe could rewrite as `$x <<= $y`
+   * @fix $x <<= $y
+   * @pure $x
+   */
+  $x = $x << $y;
+
+  /**
+   * @maybe could rewrite as `$x >>= $y`
+   * @fix $x >>= $y
+   * @pure $x
+   */
+  $x = $x >> $y;
+
+  /**
+   * @maybe could rewrite as `$x .= $y`
+   * @fix $x .= $y
+   * @pure $x
+   */
+  $x = $x . $y;
+
+  /**
+   * @maybe could rewrite as `$x ??= $y`
+   * @fix $x ??= $y
+   * @pure $x
+   */
+  $x = $x ?? $y;
+}
diff --git a/src/phpgrep/matcher.go b/src/phpgrep/matcher.go
index 127288a..a3cc718 100644
--- a/src/phpgrep/matcher.go
+++ b/src/phpgrep/matcher.go
@@ -370,6 +370,9 @@ func (m *matcher) eqNode(state *matcherState, x, y node.Node) bool {
 	case *assign.Concat:
 		y, ok := y.(*assign.Concat)
 		return ok && m.eqNode(state, x.Variable, y.Variable) && m.eqNode(state, x.Expression, y.Expression)
+	case *assign.Coalesce:
+		y, ok := y.(*assign.Coalesce)
+		return ok && m.eqNode(state, x.Variable, y.Variable) && m.eqNode(state, x.Expression, y.Expression)
 	case *assign.Div:
 		y, ok := y.(*assign.Div)
 		return ok && m.eqNode(state, x.Variable, y.Variable) && m.eqNode(state, x.Expression, y.Expression)


Test patch:
diff --git a/src/tests/golden/testdata/embeddedrules/assignOp.php b/src/tests/golden/testdata/embeddedrules/assignOp.php
new file mode 100644
index 0000000..33f10f9
--- /dev/null
+++ b/src/tests/golden/testdata/embeddedrules/assignOp.php
@@ -0,0 +1,53 @@
+<?php
+$a = 100;
+$a1 = 100;
+$b = 5;
+
+
+// $a += $b
+$a = $a + $b; // Could rewrite
+$a = $a1 + $b; // Ok
+
+// $a -= $b
+$a = $a - $b; // Could rewrite
+$a = $a1 - $b; // Ok
+
+// $a *= $b
+$a = $a * $b; // Could rewrite
+$a = $a1 * $b; // Ok
+
+// $a /= $b
+$a = $a / $b; // Could rewrite
+$a = $a1 / $b; // Ok
+
+// $a %= $b
+$a = $a % $b; // Could rewrite
+$a = $a1 % $b; // Ok
+
+// $a .= $b
+$a = $a . $b; // Could rewrite
+$a = $a1 . $b; // Ok
+
+// $a &= $b
+$a = $a & $b; // Could rewrite
+$a = $a1 & $b; // Ok
+
+// $a |= $b
+$a = $a | $b; // Could rewrite
+$a = $a1 | $b; // Ok
+
+// $a ^= $b
+$a = $a ^ $b; // Could rewrite
+$a = $a1 ^ $b; // Ok
+
+// $a <<= $b
+$a = $a << $b; // Could rewrite
+$a = $a1 << $b; // Ok
+
+// $a >>= $b
+$a = $a >> $b; // Could rewrite
+$a = $a1 >> $b; // Ok
+
+// $a ??= $b
+$a = $a ?? $b; // Could rewrite
+$a = $a1 ?? $b; // Ok
\ No newline at end of file
diff --git a/src/tests/golden/testdata/embeddedrules/golden.txt b/src/tests/golden/testdata/embeddedrules/golden.txt
index 2a0991b..1aff48f 100644
--- a/src/tests/golden/testdata/embeddedrules/golden.txt
+++ b/src/tests/golden/testdata/embeddedrules/golden.txt
@@ -1,15 +1,51 @@
-MAYBE   ternarySimplify: could rewrite as `(bool)$x` at testdata/embeddedrules/file.php:6
+MAYBE   assignOp: could rewrite as `$a += $b` at testdata/embeddedrules/assignOp.php:8
+$a = $a + $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a -= $b` at testdata/embeddedrules/assignOp.php:12
+$a = $a - $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a *= $b` at testdata/embeddedrules/assignOp.php:16
+$a = $a * $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a /= $b` at testdata/embeddedrules/assignOp.php:20
+$a = $a / $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a %= $b` at testdata/embeddedrules/assignOp.php:24
+$a = $a % $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a .= $b` at testdata/embeddedrules/assignOp.php:28
+$a = $a . $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a &= $b` at testdata/embeddedrules/assignOp.php:32
+$a = $a & $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a |= $b` at testdata/embeddedrules/assignOp.php:36
+$a = $a | $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a ^= $b` at testdata/embeddedrules/assignOp.php:40
+$a = $a ^ $b; // Could rewrite
+^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a <<= $b` at testdata/embeddedrules/assignOp.php:44
+$a = $a << $b; // Could rewrite
+^^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a >>= $b` at testdata/embeddedrules/assignOp.php:48
+$a = $a >> $b; // Could rewrite
+^^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$a ??= $b` at testdata/embeddedrules/assignOp.php:52
+$a = $a ?? $b; // Could rewrite
+^^^^^^^^^^^^^
+MAYBE   ternarySimplify: could rewrite as `(bool)$x` at testdata/embeddedrules/ternarySimplify.php:6
     sink($x ? true : false);
          ^^^^^^^^^^^^^^^^^
-MAYBE   ternarySimplify: could replace the ternary with just $x > $y at testdata/embeddedrules/file.php:9
+MAYBE   ternarySimplify: could replace the ternary with just $x > $y at testdata/embeddedrules/ternarySimplify.php:9
     sink($x > $y ? true : false);
          ^^^^^^^^^^^^^^^^^^^^^^
-MAYBE   ternarySimplify: could rewrite as `$x ?: $y` at testdata/embeddedrules/file.php:12
+MAYBE   ternarySimplify: could rewrite as `$x ?: $y` at testdata/embeddedrules/ternarySimplify.php:12
     sink($x ? $x : $y);
          ^^^^^^^^^^^^
-MAYBE   ternarySimplify: could rewrite as `$x[1] ?? $y` at testdata/embeddedrules/file.php:15
+MAYBE   ternarySimplify: could rewrite as `$x[1] ?? $y` at testdata/embeddedrules/ternarySimplify.php:15
     sink(isset($x[1]) ? $x[1] : $y);
          ^^^^^^^^^^^^^^^^^^^^^^^^^
-MAYBE   ternarySimplify: could rewrite as `(bool)($flags & SOME_MASK)` at testdata/embeddedrules/file.php:27
+MAYBE   ternarySimplify: could rewrite as `(bool)($flags & SOME_MASK)` at testdata/embeddedrules/ternarySimplify.php:27
     sink(($flags & SOME_MASK) ? true : false);
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tests/golden/testdata/embeddedrules/file.php b/src/tests/golden/testdata/embeddedrules/ternarySimplify.php
similarity index 100%
rename from src/tests/golden/testdata/embeddedrules/file.php
rename to src/tests/golden/testdata/embeddedrules/ternarySimplify.php
diff --git a/src/tests/golden/testdata/idn/golden.txt b/src/tests/golden/testdata/idn/golden.txt
index 9a8d275..bf868c6 100644
--- a/src/tests/golden/testdata/idn/golden.txt
+++ b/src/tests/golden/testdata/idn/golden.txt
@@ -25,6 +25,15 @@ MAYBE   arraySyntax: Use of old array syntax (use short form instead) at testdat
 MAYBE   redundantCast: expression already has int type at testdata/idn/idn.php:233
             $delta = (int) ($delta / 35);
                            ^^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$k += 36` at testdata/idn/idn.php:234
+            $k = $k + 36;
+            ^^^^^^^^^^^^
 MAYBE   redundantCast: expression already has int type at testdata/idn/idn.php:237
         return $k + (int) (36 * $delta / ($delta + 38));
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$n += (int) ($i / $outputLength)` at testdata/idn/idn.php:274
+            $n = $n + (int) ($i / $outputLength);
+            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$i %= $outputLength` at testdata/idn/idn.php:275
+            $i = $i % $outputLength;
+            ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tests/golden/testdata/math/golden.txt b/src/tests/golden/testdata/math/golden.txt
index 43b3aa9..2e71052 100644
--- a/src/tests/golden/testdata/math/golden.txt
+++ b/src/tests/golden/testdata/math/golden.txt
@@ -4,3 +4,6 @@ INFO    unused: Variable a is unused (use $_ to ignore this inspection) at testd
 MAYBE   ternarySimplify: could rewrite as `$matches['fractional'] ?? ''` at testdata/math/src/BigNumber.php:90
             $fractional = isset($matches['fractional']) ? $matches['fractional'] : '';
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+MAYBE   assignOp: could rewrite as `$number ^= $xor` at testdata/math/src/Internal/Calculator.php:622
+        $number = $number ^ $xor;
+        ^^^^^^^^^^^^^^^^^^^^^^^^