Repository: VKCOM/noverify
Pull request #719 (2020-08-26 22:20:15)

linter: add argsOrder checker
Detects incorrect potential issues in builtin funcs args order.

To avoid false positives, some string literals are limited
to the length of 1. This is achieved by a new "char" phpgrep class.

The `strpos("xy", $s)` may have sense while `strpos("/", $s)` never
makes any sense.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>


Patch:
diff --git a/src/cmd/embeddedrules/rules.go b/src/cmd/embeddedrules/rules.go
index 1e20b64..09532c1 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\x97\xdf\x73\xe2\x36\x10\xc7\xdf\xf9\x2b\xbe\xcc\x10\x06\xb8\x03\x37\xd3\xb7\x04\xc2\xb5\x6f\x9d\xb9\xe9\xcd\xb4\x8f\x9d\x04\x64\x7b\x8d\xd5\x18\xc9\x95\xe4\x03\x5a\xee\x7f\xef\x48\x82\x60\xc0\x3e\xec\xb4\x79\x60\xec\x95\x77\x3f\xfb\x4b\x5a\x65\x3a\xcf\xd3\xbc\xd3\x09\x46\xa3\x0e\x46\xf8\x24\x24\x17\x3a\xa7\xc8\x70\x29\xf0\xd3\xe7\xcf\x4e\x98\x71\x61\x48\xc1\xff\xc5\x5c\xb3\x30\xa3\x0e\x46\xc1\x49\x2d\x92\xeb\x35\x09\x83\xdf\x28\x97\xca\xc0\x90\x12\x4c\xed\x40\xdb\x5c\x91\xd6\x5c\x0a\x0d\x93\x32\x83\x88\x09\x84\x04\xcd\xd7\x79\xc6\x13\x4e\xf1\xc4\xa9\x87\x94\x48\x45\x40\x6f\x8b\xb9\xfd\x79\x40\x6f\xe7\x16\x58\xe2\xc1\x76\xe1\x20\x0c\x3a\x49\x21\xbc\x7f\x07\xcc\xef\xde\xda\x6e\x30\xc4\x3f\x1d\xc0\xf9\x04\xab\xbd\x66\xbb\x90\x10\xc9\x22\x8b\xa1\x28\xcf\x58\x44\x30\x29\xbd\xb9\xb7\xe1\x26\xc5\x9f\x85\x36\xe8\x45\x52\xc4\x07\x2d\xb3\xcb\x09\xa1\x94\x59\x49\x1a\x74\xe0\xdf\x30\x87\x51\x05\xe1\x01\x09\xcb\x34\x3d\x76\xbe\x03\xdc\x28\x6e\x08\x4c\x63\x39\xb0\xe6\x86\xce\xc0\xb2\x4c\x41\xf7\xff\xe5\x1c\xd3\x74\x84\x24\x7c\x5b\x4a\x9d\x13\xe5\x85\x22\xf4\xb6\x27\x5a\x29\xe5\x2d\x28\xf3\x0a\xca\xfc\x48\xb1\x76\xb9\xd6\x64\x06\xbd\xed\xb0\x6c\xfe\x5b\x6d\xc7\xe4\xd2\x90\x30\x9c\x65\x90\x39\x29\xe6\xca\x9b\x2b\x8a\x28\x26\x11\x91\xb5\x56\x90\xbe\x6a\x96\x3e\x7a\x6b\xa6\x5f\x31\x9b\xe1\x87\x47\x04\x81\x7d\x48\x99\x46\xca\x57\x29\xa9\xb2\x01\x93\x32\x81\xfe\x59\x4f\x0d\x4e\x06\x86\xce\xc2\x79\x73\x9d\x94\x8f\x7d\x15\xe0\x57\x69\xe8\x01\x1b\xb2\xdd\x64\xbd\x5e\x96\x7c\xe8\xce\x6c\x4a\x6c\x82\xd8\x95\xe7\xa0\xaf\x24\xbc\x09\x9e\x80\xbf\xed\x83\x88\x15\xab\xd4\xf8\x3e\x5c\xda\x8e\x88\x52\x8a\x5e\xbf\xe4\xcb\xc3\x6e\xb1\x6f\x1a\xa1\x34\xa9\x4f\x8b\xeb\x92\x20\x80\xfd\x54\xc3\x75\x15\xb8\x86\x90\x06\x0c\x2b\x29\xe3\xe3\x67\x48\xa4\x42\xc8\xcd\x86\x6b\x3a\x65\x74\x38\x71\xea\xde\x86\x4f\x3c\x17\x2b\x2c\xb9\xf8\xca\x32\x1e\x7b\xb3\x1f\xed\xae\xa5\xc8\x50\x0c\x51\xac\x43\x52\x48\x64\x21\x62\xb7\x23\x96\xe0\xda\x6b\x5b\xa4\xf3\x31\xa5\x2c\x4f\x8a\xec\x23\x42\x8a\x58\xa1\xfd\x06\x53\x52\x1a\xc8\xc4\x3d\xe7\x4a\x86\x19\xad\xad\x9f\xa7\xb4\x78\x47\xf0\x4b\x19\x0c\xa6\x08\x0c\x8a\x74\x91\x1d\xb4\x99\x99\x1c\x5a\x12\x9f\x36\x4c\x09\xeb\xed\x8d\x12\xfb\xee\x63\x62\xb7\xa0\xbf\x16\x21\x37\x4c\xc4\x0f\xae\x7c\x40\x6f\x61\x95\x7b\x0b\x5b\xb2\xc5\xe3\x51\xd4\x7f\x93\x5b\xd1\xb7\x4b\x5c\xb7\x29\x4e\x54\xf1\xba\x35\xbc\x6e\x2d\x6f\xd6\x22\xbe\x1f\xab\x02\xac\x8d\xf0\x3b\x21\xb6\x88\xb1\x82\xd9\xad\x63\x76\x4b\xcc\x76\x65\xdc\x5f\x96\x51\xaa\xab\x2a\xee\xcb\xbc\xfd\x7f\xa9\xe2\xfe\xaa\x8a\x67\xb8\x6e\x0d\xee\xdd\x45\xdc\x5f\x15\xf1\x22\xbc\xda\xf8\xde\x5b\xc3\xfd\x75\x0d\x2f\x42\xac\x8d\xf1\x84\xac\x3f\xbd\x99\xd6\x7c\x25\xac\xa4\xf1\x9c\x9f\xd9\x9f\x0f\x6e\x2a\x5c\x0c\xfa\x0f\xb3\x83\xb4\x74\x18\x7b\xc0\x97\xfc\xc6\x88\x3f\x9b\x51\xce\xce\xc5\x8c\x72\xb2\xfa\x49\x58\x72\xaa\x29\x65\x5c\x41\x19\x37\xa0\x8c\x5b\x51\x46\x15\x94\x51\x03\xca\xa8\x15\x25\xa8\xa0\x04\x0d\x28\x41\x2b\xca\x5d\x05\xe5\xae\x01\xe5\xae\x15\xa5\x5f\x41\xe9\x37\xa0\xf4\x5b\x51\xf6\x15\x94\x7d\x03\xca\xbe\x15\xe5\xa5\x82\xf2\xd2\x80\xf2\xd2\x8a\x32\x9d\x56\x60\xbc\xf0\x06\x67\x3a\x6d\x05\x7a\x7a\xaa\x00\x79\xe1\x0d\xd0\xd3\x53\x2b\xd0\xa4\x82\x33\x69\x80\x99\xb4\xbc\x0b\x57\x60\xbc\xf0\x06\xc7\xdd\x98\x1b\xde\x8a\x93\x64\x1c\xee\xc6\x52\x10\xd6\x5c\x1b\xf6\x7a\x75\x1b\x66\x7f\x44\xb2\x10\x66\xd0\x63\xc3\xe7\xf3\x23\xb5\xb4\x32\xbe\x7f\x3e\x3f\x58\x65\x92\xfc\xbc\xbb\xbf\x3a\x56\x8f\x63\xc5\x5e\xde\x58\x98\xed\x60\xff\x03\x14\x31\xc5\x30\x12\xf6\x96\xe7\x2c\x8e\xef\xdd\x75\x57\x80\x8b\x98\xb6\xa5\x04\xe0\x02\x8b\x31\x2c\xf8\x18\x7e\xd9\xd5\x8b\x3c\xdf\x04\x6b\xfe\x37\xc9\xe4\x16\xd9\x7f\x55\x89\x3e\x2d\x3d\xdb\xd4\xff\x1b\x00\x00\xff\xff\xfa\x48\x39\x30\x00\x0f\x00\x00")
+var _embeddedrulesRulesPhp = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x97\x5f\x6f\xea\x36\x14\xc0\xdf\xf9\x14\x07\x94\x52\xe0\x02\x59\xb5\xb7\xb6\xb4\x77\x7b\x9b\x74\xb5\x4a\xdb\xe3\xd4\x82\x93\x9c\x10\xdf\x06\x3b\xb3\x9d\x5b\x58\xe1\xbb\x4f\xb6\x93\x12\xc0\x81\xa4\x5b\xa5\x22\x38\xf6\x39\xbf\xf3\x2f\x3e\xce\xfd\x63\x96\x64\x9d\x8e\x3f\x1a\x75\x60\x04\x5f\x19\xa7\x4c\x66\x18\x2a\xca\x19\xfc\xf2\xed\x9b\x11\xa6\x94\x29\x14\x60\xff\x22\x2a\x49\x90\x62\x07\x46\xfe\x5e\x2d\xe4\xab\x15\x32\x05\x7f\x60\xc6\x85\x02\x85\x82\x11\xb1\x01\x5c\x67\x02\xa5\xa4\x9c\x49\x50\x09\x51\x10\x12\x06\x01\x82\xa4\xab\x2c\xa5\x31\xc5\x68\x6a\xd4\x03\x8c\xb9\x40\x00\x6f\x0d\x8f\xfa\xe3\x16\xbc\x8d\x59\x20\xb1\x05\xeb\x85\x42\xe8\x77\xe2\x9c\x59\xff\x0a\xcc\x9f\xd6\xda\x66\x30\x84\xf7\x0e\x80\xf1\x09\xb4\xf6\x8a\x6c\x02\x84\x90\xe7\x69\x04\x02\xb3\x94\x84\x08\x2a\xc1\x0f\xf7\xde\xa8\x4a\xe0\x7b\x2e\x15\x78\x21\x67\x51\xa1\xa5\x36\x19\x42\xc0\x79\x5a\x91\xfa\x1d\xb0\xbf\xe0\x11\x94\xc8\x11\x6e\x21\x26\xa9\xc4\xbb\xce\x19\xe0\x9b\xa0\x0a\x81\x48\x58\x0c\xb4\xb9\xa1\x31\xb0\xa8\x52\xa0\xfb\xff\x72\xca\x34\x95\x90\x98\xae\x2b\xa9\x33\xa2\x2c\x17\x08\xde\x7a\x4f\xab\xa4\xbc\x05\xe5\xd1\x41\x79\x2c\x29\xda\x2e\x95\x12\xd5\xc0\x5b\x0f\xab\xe6\x77\xb5\x1d\x93\x71\x85\x4c\x51\x92\x02\xcf\x50\x10\x53\xde\x4c\x60\x88\x11\xb2\x10\xb5\xb5\x1c\xe5\x49\xb3\xf4\xc1\x5b\x11\xf9\x0a\xb3\x19\xfc\x74\x07\xbe\xaf\xbf\x24\x44\x42\x42\x97\x09\x8a\xaa\x01\x95\x10\x06\xfd\x83\x9e\x1a\xec\x0d\x0c\x8d\x85\xc3\xe6\xda\x2b\x97\x7d\xe5\xc3\xef\x5c\xe1\x2d\xbc\xa1\xee\x26\xed\xf5\xa2\xe2\x43\x77\xa6\x53\xa2\x13\x44\x4e\x3c\x07\xfc\x81\xcc\x9a\xa0\x31\xd0\x8f\xe7\x20\x24\xf9\x32\x51\xb6\x0f\x17\xba\x23\xc2\x04\xc3\xd7\xa7\x6c\x51\x3c\x2d\xfa\x97\x84\x80\xab\xc4\xa6\xc5\x74\x89\xef\x83\xde\x2a\xc1\x74\x15\x50\x09\x8c\x2b\x20\xb0\xe4\x3c\x2a\xb7\x41\xcc\x05\x04\x54\xbd\x51\x89\xfb\x8c\x0e\xa7\x46\xdd\xda\xb0\x89\xa7\x6c\x09\x0b\xca\x7e\x90\x94\x46\xd6\xec\x58\x3f\xb5\x18\x2a\x8c\x80\xe5\xab\x00\x05\xc4\x3c\x67\x91\x79\x22\x16\x40\xa5\xd5\xd6\x48\xe3\x63\x82\x69\x16\xe7\xe9\x18\x02\x0c\x49\x2e\xed\x03\x26\x38\x57\xc0\x63\xf3\x3d\x13\x3c\x48\x71\xa5\xfd\xdc\xa7\xc5\x3a\x02\xbf\x55\xc1\x40\x04\x02\x01\x81\x32\x4f\x0b\x6d\xa2\xa6\x45\x4b\xc2\xd7\x37\x22\x98\xf6\xf6\x42\x89\x6d\xf7\x11\xb6\x99\xe3\xdf\xf3\x80\x2a\xc2\xa2\x5b\x53\x3e\x00\x6f\xae\x95\xbd\xb9\x2e\xd9\xfc\xae\x14\xf5\x3f\xe4\x5a\xb4\x3b\xc6\x75\x9b\xe2\x98\x8b\xd7\xad\xe1\x75\x6b\x79\xb3\x16\xf1\xfd\xec\x0a\xb0\x36\xc2\x33\x21\xb6\x88\xd1\xc1\xec\xd6\x31\xbb\x15\x66\xbb\x32\x6e\x8f\xcb\xc8\xc5\x49\x15\xb7\x55\xde\xf6\xbf\x54\x71\x7b\x52\xc5\x03\x5c\xb7\x06\xf7\xe9\x22\x6e\x4f\x8a\x78\x14\x5e\x6d\x7c\x9f\xad\xe1\xf6\xb4\x86\x47\x21\xd6\xc6\xb8\x47\xd6\x9f\xde\x44\x4a\xba\x64\x5a\xd2\x78\xce\xcf\xf4\xc7\x17\x33\x15\x8e\x06\xfd\x97\x59\x21\xad\x1c\xc6\x16\xf0\x94\x5d\x18\xf1\x07\x33\xca\xd8\x39\x9a\x51\x46\x56\x3f\x09\x2b\x4e\x35\xa5\x4c\x1c\x94\x49\x03\xca\xa4\x15\x65\xe4\xa0\x8c\x1a\x50\x46\xad\x28\xbe\x83\xe2\x37\xa0\xf8\xad\x28\x57\x0e\xca\x55\x03\xca\x55\x2b\x4a\xdf\x41\xe9\x37\xa0\xf4\x5b\x51\xb6\x0e\xca\xb6\x01\x65\xdb\x8a\xf2\xe2\xa0\xbc\x34\xa0\xbc\xb4\xa2\xdc\xdf\x3b\x30\x56\x78\x81\x73\x7f\xdf\x0a\xf4\xf0\xe0\x00\x59\xe1\x05\xd0\xc3\x43\x2b\xd0\xd4\xc1\x99\x36\xc0\x4c\x5b\xde\x85\x1d\x18\x2b\xbc\xc0\x31\x37\xe6\x86\xb7\xe2\x38\x9e\x04\x9b\x09\x67\x08\x2b\x2a\x15\x79\x3d\xb9\x0d\x93\xbf\x42\x9e\x33\x35\xf0\xc8\xf0\xf9\xf0\x48\xad\xac\x4c\x6e\x9e\x0f\x0f\x56\x1e\xc7\xbf\x6e\x6e\x4e\x8e\xd5\x72\xac\xe8\xcb\x1b\x09\xd2\x0d\xe8\x37\x40\x16\x61\x04\x8a\x83\xbe\xe5\x19\x8b\x93\x1b\x73\xdd\x65\x40\x59\x84\xeb\x4a\x02\xe0\x08\x0b\x13\xd0\xe0\x32\xfc\xaa\xab\x47\x79\xbe\x08\x96\xf4\x1f\xe4\xf1\x25\xb2\xdd\xe5\x44\xef\x97\x9e\xcf\xa5\x5e\xe6\x32\xa3\x21\xe5\xb9\xbe\x97\x2e\x73\x3b\xdb\xb8\x88\x50\x1c\xe6\x5d\x2a\x91\x71\x39\xb8\xf6\xaf\xc7\xe0\xc9\xe1\x41\xe6\x8b\x35\x4f\x8e\xe1\xda\xbf\x1e\x1e\x8d\x34\xb1\x94\x4f\xda\x5e\x7d\xf2\xcb\xea\x9b\x34\x84\x5c\x08\x0c\x15\x24\x64\x23\x15\x09\x5f\x41\x5f\xf3\x19\x62\x94\xe2\xb1\x87\x1f\xe1\xea\x79\x5f\xee\x9f\xdb\xbd\xe5\xd0\x2f\x7d\x7b\xef\x85\x09\x11\xbd\xdd\x18\xbc\xf7\xde\xa8\xb7\x1b\xde\x95\xcb\xf4\xc2\xfa\x79\xfd\x3c\x90\x4a\xcc\x8b\x3a\x3b\x37\xed\x6a\x2a\xef\x8c\xba\x78\x7d\x37\x15\xd2\x81\xcb\x3c\xf8\xae\xe5\x75\x91\x67\x02\x97\xf3\x42\x69\xe0\xcd\xc7\x60\xfe\xdf\x7b\x52\x1d\xba\xd1\xde\x05\x8b\x57\x42\x6f\x3c\x97\x77\x1d\x7e\xa1\x52\xc9\xb9\xcb\xa7\xba\x04\x9f\xec\xed\xf5\x3e\x9f\xc0\x08\x53\xba\xa2\xba\x2f\x1b\xf8\x8f\xeb\x2c\xe5\x51\x01\x76\x38\xb8\xeb\xfc\x1b\x00\x00\xff\xff\x83\x66\xca\xe8\x37\x12\x00\x00")
 
 func embeddedrulesRulesPhpBytes() ([]byte, error) {
 	return bindataRead(
diff --git a/src/cmd/embeddedrules/rules.php b/src/cmd/embeddedrules/rules.php
index 4c68768..38b90bf 100644
--- a/src/cmd/embeddedrules/rules.php
+++ b/src/cmd/embeddedrules/rules.php
@@ -203,3 +203,38 @@ function offBy1() {
    */
   $a[sizeof($a)];
 }
+
+/**
+ * @comment Report suspicious arguments order.
+ * @before  strpos('/', $s)
+ * @after   strpos($s, '/')
+ */
+function argsOrder() {
+  /**
+   * @warning potentially incorrect haystack and needle arguments order
+   */
+  any_haystack_needle: {
+    strpos(${"char"}, ${"*"});
+    stripos(${"char"}, ${"*"});
+    strrpos(${"char"}, ${"*"});
+    substr_count(${"str"}, ${"*"});
+  }
+
+  /**
+   * @warning potentially incorrect replacement and subject arguments order
+   */
+  preg_replace($_, $_, ${"str"}, ${"*"});
+
+  /**
+   * @warning potentially incorrect replace and string arguments order
+   */
+  any_str_replace: {
+    str_replace($_, $_, ${"char"}, ${"*"});
+    str_replace($_, $_, "", ${"*"});
+  }
+
+  /**
+   * @warning potentially incorrect delimiter and string arguments order
+   */
+  explode($_, ${"char"}, ${"*"});
+}
diff --git a/src/phpgrep/compile.go b/src/phpgrep/compile.go
index f081eea..d1ed55b 100644
--- a/src/phpgrep/compile.go
+++ b/src/phpgrep/compile.go
@@ -79,6 +79,8 @@ func (c *compiler) EnterNode(n ir.Node) bool {
 		v.Expr = anyFloat{metaNode{name: name}}
 	case "str":
 		v.Expr = anyStr{metaNode{name: name}}
+	case "char":
+		v.Expr = anyStr1{metaNode{name: name}}
 	case "num":
 		v.Expr = anyNum{metaNode{name: name}}
 	case "expr":
diff --git a/src/phpgrep/matcher.go b/src/phpgrep/matcher.go
index b4be994..ebea603 100644
--- a/src/phpgrep/matcher.go
+++ b/src/phpgrep/matcher.go
@@ -739,6 +739,9 @@ func (m *matcher) eqVar(state *matcherState, x *ir.Var, y ir.Node) bool {
 	case anyStr:
 		_, ok := y.(*ir.String)
 		return ok && m.matchNamed(state, vn.name, y)
+	case anyStr1:
+		y, ok := y.(*ir.String)
+		return ok && len(y.Value) == 1 && m.matchNamed(state, vn.name, y)
 	case anyNum:
 		switch y.(type) {
 		case *ir.Lnumber, *ir.Dnumber:
diff --git a/src/phpgrep/meta_nodes.go b/src/phpgrep/meta_nodes.go
index e0c12a5..2038617 100644
--- a/src/phpgrep/meta_nodes.go
+++ b/src/phpgrep/meta_nodes.go
@@ -18,6 +18,7 @@ type (
 	anyInt   struct{ metaNode }
 	anyFloat struct{ metaNode }
 	anyStr   struct{ metaNode }
+	anyStr1  struct{ metaNode }
 	anyNum   struct{ metaNode }
 	anyExpr  struct{ metaNode }
 	anyFunc  struct{ metaNode }


Test patch:
diff --git a/src/phpgrep/matcher_test.go b/src/phpgrep/matcher_test.go
index 9c7fca0..7f21fab 100644
--- a/src/phpgrep/matcher_test.go
+++ b/src/phpgrep/matcher_test.go
@@ -308,6 +308,7 @@ func TestMatch(t *testing.T) {
 		{`${"int"}`, `13`},
 		{`${"float"}`, `3.4`},
 		{`${"str"}`, `"123"`},
+		{`${"char"}`, `"x"`},
 		{`${"num"}`, `13`},
 		{`${"num"}`, `3.4`},
 
@@ -457,6 +458,8 @@ func TestMatchNegative(t *testing.T) {
 		{`${"int"}`, `13.5`},
 		{`${"float"}`, `3`},
 		{`${"str"}`, `5`},
+		{`${"char"}`, `"aa"`},
+		{`${"char"}`, `1`},
 		{`${"num"}`, `$x`},
 		{`${"num"}`, `"1"`},
 
diff --git a/src/tests/golden/testdata/embeddedrules/argsOrder.php b/src/tests/golden/testdata/embeddedrules/argsOrder.php
new file mode 100644
index 0000000..48775cf
--- /dev/null
+++ b/src/tests/golden/testdata/embeddedrules/argsOrder.php
@@ -0,0 +1,57 @@
+<?php
+
+function test_strpos_bad(string $s, string $sub) {
+  // Give warning: single-char haystack strings are suspicious.
+  $_ = strpos('/', $s);
+  $_ = strpos("/", $s);
+}
+
+function test_strpos_ok(string $s, string $sub) {
+  // This code is kinda OK, it checks for ($s == "x" || $s == "y").
+  $_ = strpos("xy", $s);
+
+  // Good code: needle is literal, haystack is non-constant.
+  $_ = strpos($s, "x");
+
+  // OK: both arguments are non-constant.
+  $_ = strpos($s, $sub);
+}
+
+function test_stripos_bad(string $s, string $sub) {
+  $_ = stripos('/', $s);
+  $_ = stripos("/", $s);
+}
+
+function test_stripos_ok(string $s, string $sub) {
+  $_ = stripos("xY", $s);
+  $_ = stripos($s, "x");
+  $_ = stripos($s, $sub);
+}
+
+function test_preg_replace_bad($pat, $subj) {
+  $_ = preg_replace($pat, $subj, 'replacement');
+}
+
+function test_preg_replace_ok($pat, $subj, $repl) {
+  $_ = preg_replace($pat, 'replacement', $subj);
+  $_ = preg_replace($pat, $repl, $subj);
+}
+
+function test_explode_bad($s) {
+  $_ = explode($s, '/');
+}
+
+function test_explode_ok($s, $delim) {
+  $_ = explode('/', $s);
+  $_ = explode($delim, $s);
+}
+
+function test_str_replace_bad($search, $subj) {
+  $_ = str_replace($search, $subj, ' ');
+  $_ = str_replace($search, $subj, '');
+}
+
+function test_str_replace_ok($search, $subj, $repl) {
+  $_ = str_replace($search, 'replacement', $subj);
+  $_ = str_replace($search, $repl, $subj);
+}
\ 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 9ff3ccb..0b0be17 100644
--- a/src/tests/golden/testdata/embeddedrules/golden.txt
+++ b/src/tests/golden/testdata/embeddedrules/golden.txt
@@ -1,3 +1,27 @@
+WARNING argsOrder: potentially incorrect haystack and needle arguments order at testdata/embeddedrules/argsOrder.php:5
+  $_ = strpos('/', $s);
+       ^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect haystack and needle arguments order at testdata/embeddedrules/argsOrder.php:6
+  $_ = strpos("/", $s);
+       ^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect haystack and needle arguments order at testdata/embeddedrules/argsOrder.php:21
+  $_ = stripos('/', $s);
+       ^^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect haystack and needle arguments order at testdata/embeddedrules/argsOrder.php:22
+  $_ = stripos("/", $s);
+       ^^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect replacement and subject arguments order at testdata/embeddedrules/argsOrder.php:32
+  $_ = preg_replace($pat, $subj, 'replacement');
+       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect delimiter and string arguments order at testdata/embeddedrules/argsOrder.php:41
+  $_ = explode($s, '/');
+       ^^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect replace and string arguments order at testdata/embeddedrules/argsOrder.php:50
+  $_ = str_replace($search, $subj, ' ');
+       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING argsOrder: potentially incorrect replace and string arguments order at testdata/embeddedrules/argsOrder.php:51
+  $_ = str_replace($search, $subj, '');
+       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 MAYBE   assignOp: could rewrite as `$a += $b` at testdata/embeddedrules/assignOp.php:8
 $a = $a + $b; // Could rewrite
 ^^^^^^^^^^^^