Repository: mgechev/revive (go)

Issue:
modifies-value-receiver: miss modification by `++`/`--`
**Describe the bug**
modifies-value-receiver does not trigger on `++` and `--`.

**To Reproduce**
Steps to reproduce the behavior:
1. I updated revive `go install github.com/mgechev/revive@latest`
2. I run it with the following flags & configuration file:

```shell
$ nl -ba main.go
     1	package main
     2	
     3	import "fmt"
     4	
     5	type Data struct {
     6		i int
     7	}
     8	
     9	func (d Data) Inc() {
    10		d.i++
    11	}
    12	
    13	func (d Data) IncBy(n int) {
    14		d.i += n
    15	}
    16	
    17	func (d Data) Dec() {
    18		d.i--
    19	}
    20	
    21	func (d Data) DecBy(n int) {
    22		d.i -= n
    23	}
    24	
    25	func main() {
    26		var d Data
    27		d.Inc()
    28		d.IncBy(10)
    29		d.Dec()
    30		d.DecBy(5)
    31		fmt.Println(d)
    32	}
$ revive
main.go:14:2: suspicious assignment to a by-value method receiver
main.go:22:2: suspicious assignment to a by-value method receiver
$
```

```toml
[rule.modifies-value-receiver]
```

**Expected behavior**
```
main.go:10:2: suspicious assignment to a by-value method receiver
main.go:14:2: suspicious assignment to a by-value method receiver
main.go:18:2: suspicious assignment to a by-value method receiver
main.go:22:2: suspicious assignment to a by-value method receiver
```

**Desktop (please complete the following information):**
 - OS: Gentoo Linux
 - go version go1.23.2 linux/amd64


Pull request:
fix(rule/modifies-value-receiver): miss modification by ++/--
Closes #1109 
Adds test for assignment operators


Patch:
diff --git a/rule/modifies_value_receiver.go b/rule/modifies_value_receiver.go
index d811c48..a33a79a 100644
--- a/rule/modifies_value_receiver.go
+++ b/rule/modifies_value_receiver.go
@@ -29,7 +29,7 @@ func (r *ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fai
 		}
 
 		receiverName := receiver.Names[0].Name
-		assignmentsToReceiver := r.getAssignmentsToReceiver(receiverName, funcDecl.Body)
+		assignmentsToReceiver := r.getReceiverModifications(receiverName, funcDecl.Body)
 		if len(assignmentsToReceiver) == 0 {
 			continue // receiver is not modified
 		}
@@ -140,38 +140,44 @@ func (r *ModifiesValRecRule) mustSkip(receiver *ast.Field, pkg *lint.Package) bo
 	return false
 }
 
-func (r *ModifiesValRecRule) getAssignmentsToReceiver(receiverName string, funcBody *ast.BlockStmt) []ast.Node {
-	receiverAssignmentFinder := func(n ast.Node) bool {
-		// look for assignments with the receiver in the right hand
-		assignment, ok := n.(*ast.AssignStmt)
-		if !ok {
-			return false
-		}
+func (r *ModifiesValRecRule) getReceiverModifications(receiverName string, funcBody *ast.BlockStmt) []ast.Node {
+	receiverModificationFinder := func(n ast.Node) bool {
+		switch node := n.(type) {
+		case *ast.IncDecStmt:
+			se, ok := node.X.(*ast.SelectorExpr)
+			if !ok {
+				return false
+			}
 
-		for _, exp := range assignment.Lhs {
-			switch e := exp.(type) {
-			case *ast.IndexExpr: // receiver...[] = ...
-				continue
-			case *ast.StarExpr: // *receiver = ...
-				continue
-			case *ast.SelectorExpr: // receiver.field = ...
-				name := r.getNameFromExpr(e.X)
-				if name == "" || name != receiverName {
+			name := r.getNameFromExpr(se.X)
+			return name == receiverName
+		case *ast.AssignStmt:
+			// look for assignments with the receiver in the right hand
+			for _, exp := range node.Lhs {
+				switch e := exp.(type) {
+				case *ast.IndexExpr: // receiver...[] = ...
 					continue
-				}
-			case *ast.Ident: // receiver := ...
-				if e.Name != receiverName {
+				case *ast.StarExpr: // *receiver = ...
+					continue
+				case *ast.SelectorExpr: // receiver.field = ...
+					name := r.getNameFromExpr(e.X)
+					if name == "" || name != receiverName {
+						continue
+					}
+				case *ast.Ident: // receiver := ...
+					if e.Name != receiverName {
+						continue
+					}
+				default:
 					continue
 				}
-			default:
-				continue
-			}
 
-			return true
+				return true
+			}
 		}
 
 		return false
 	}
 
-	return pick(funcBody, receiverAssignmentFinder)
+	return pick(funcBody, receiverModificationFinder)
 }


Test patch:
diff --git a/testdata/modifies_value_receiver.go b/testdata/modifies_value_receiver.go
index e6a7166..11757c9 100644
--- a/testdata/modifies_value_receiver.go
+++ b/testdata/modifies_value_receiver.go
@@ -28,3 +28,22 @@ func (b JailerCommandBuilder) WithBin(bin string) JailerCommandBuilder {
 	b.bin = bin
 	return b
 }
+
+func (this data) incrementDecrement() {
+	this.num++ // MATCH /suspicious assignment to a by-value method receiver/
+	this.num-- // MATCH /suspicious assignment to a by-value method receiver/
+	other++
+}
+
+func (this data) assignmentOperators() {
+	this.num += 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num -= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num *= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num /= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num %= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num &= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num ^= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num |= 1  // MATCH /suspicious assignment to a by-value method receiver/
+	this.num >>= 1 // MATCH /suspicious assignment to a by-value method receiver/
+	this.num <<= 1 // MATCH /suspicious assignment to a by-value method receiver/
+}