Repository: VKCOM/noverify
Pull request #1084 (2021-08-17 08:15:27)

embeddedrules: added `concatenationPrecedence` checker


Patch:
diff --git a/docs/checkers_doc.md b/docs/checkers_doc.md
index ed7a38c..b0fb794 100644
--- a/docs/checkers_doc.md
+++ b/docs/checkers_doc.md
@@ -4,7 +4,7 @@
 
 | Total checks | Checks enabled by default | Disabled checks by default | Autofixable checks |
 | ------------ | ------------------------- | -------------------------- | ------------------ |
-| 88           | 78                        | 10                         | 12                 |
+| 89           | 79                        | 10                         | 12                 |
 ## Table of contents
  - Enabled by default
    - [`accessLevel` checker](#accesslevel-checker)
@@ -23,6 +23,7 @@
    - [`caseContinue` checker](#casecontinue-checker)
    - [`catchOrder` checker](#catchorder-checker)
    - [`complexity` checker](#complexity-checker)
+   - [`concatenationPrecedence` checker](#concatenationprecedence-checker)
    - [`constCase` checker (autofixable)](#constcase-checker)
    - [`countUse` checker (autofixable)](#countuse-checker)
    - [`deadCode` checker](#deadcode-checker)
@@ -452,6 +453,24 @@ function checkRights() {
 <p><br></p>
 
 
+### `concatenationPrecedence` checker
+
+#### Description
+
+Report when use unparenthesized expression containing both '.' and binary operator.
+
+#### Non-compliant code:
+```php
+"id: " . $id - 10
+```
+
+#### Compliant code:
+```php
+"id: " . ($id - 10)
+```
+<p><br></p>
+
+
 ### `constCase` checker
 
 > Auto fix available
diff --git a/src/cmd/embeddedrules/rules.php b/src/cmd/embeddedrules/rules.php
index 7c754cd..4ec4b1f 100644
--- a/src/cmd/embeddedrules/rules.php
+++ b/src/cmd/embeddedrules/rules.php
@@ -654,3 +654,21 @@ function errorSilence() {
    */
   @$_;
 }
+
+
+/**
+ * @comment Report when use unparenthesized expression containing both '.' and binary operator.
+ * @before  "id: " . $id - 10
+ * @after   "id: " . ($id - 10)
+ */
+function concatenationPrecedence() {
+    /**
+     * @warning Unparenthesized expression containing both '.' and binary operator may produce unexpected results
+     */
+    any_concat: {
+        $_ . $_ - $_;
+        $_ . $_ + $_;
+        $_ . $_ << $_;
+        $_ . $_ >> $_;
+    }
+}


Test patch:
diff --git a/src/linttest/inline.go b/src/linttest/inline.go
index 0655a78..e055d5e 100644
--- a/src/linttest/inline.go
+++ b/src/linttest/inline.go
@@ -119,7 +119,12 @@ func (s *inlineTestSuite) compare(expects []string, reports []string) (unmatched
 
 // handleFileContents reads, parses the resulting file, and splits it into lines.
 func (s *inlineTestSuite) handleFileContents(file string) (lines []string, reports []*linter.Report, err error) {
+	rawCheckerName := file
 	lint := linter.NewLinter(linter.NewConfig("8.1"))
+	if strings.Contains(file, "_7.4") {
+		lint = linter.NewLinter(linter.NewConfig("7.4"))
+		rawCheckerName = strings.ReplaceAll(file, "_7.4", "")
+	}
 
 	err = cmd.LoadEmbeddedStubs(lint, defaultStubs)
 	if err != nil {
@@ -146,8 +151,8 @@ func (s *inlineTestSuite) handleFileContents(file string) (lines []string, repor
 
 	lines = strings.Split(content, "\n")
 
-	fileName := filepath.Base(file)
-	checkerName := fileName[:len(fileName)-len(filepath.Ext(file))]
+	checkerName := filepath.Base(rawCheckerName)
+	checkerName = checkerName[:len(checkerName)-len(filepath.Ext(file))]
 	if !lint.Config().Checkers.Contains(checkerName) {
 		return nil, nil, fmt.Errorf("file name must be the name of the checker that is tested. Checker %s does not exist", checkerName)
 	}
diff --git a/src/tests/inline/testdata/embeddedrules/concatenationPrecedence_7.4.php b/src/tests/inline/testdata/embeddedrules/concatenationPrecedence_7.4.php
new file mode 100644
index 0000000..15de382
--- /dev/null
+++ b/src/tests/inline/testdata/embeddedrules/concatenationPrecedence_7.4.php
@@ -0,0 +1,23 @@
+<?php
+
+function concatenationPrecedence_74($id) {
+    echo "id: " . $id - 10;  // want `Unparenthesized expression containing both '.' and binary operator`
+    echo "id: " . $id + 10;  // want `Unparenthesized expression containing both '.' and binary operator`
+    echo "id: " . $id << 10; // want `Unparenthesized expression containing both '.' and binary operator`
+    echo "id: " . $id >> 10; // want `Unparenthesized expression containing both '.' and binary operator`
+
+    echo "id: " . ($id - 10);  // ok
+    echo "id: " . ($id + 10);  // ok
+    echo "id: " . ($id << 10); // ok
+    echo "id: " . ($id >> 10); // ok
+
+    echo $id - 10 . ": id";  // ok
+    echo $id + 10 . ": id";  // ok
+    echo $id << 10 . ": id"; // ok
+    echo $id >> 10 . ": id"; // ok
+
+    echo ($id - 10) . ": id";  // ok
+    echo ($id + 10) . ": id";  // ok
+    echo ($id << 10) . ": id"; // ok
+    echo ($id >> 10) . ": id"; // ok
+}