mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			135 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package rule
 | |
| 
 | |
| import (
 | |
| 	"go/ast"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/mgechev/revive/lint"
 | |
| )
 | |
| 
 | |
| // ModifiesValRecRule lints assignments to value method-receivers.
 | |
| type ModifiesValRecRule struct{}
 | |
| 
 | |
| // Apply applies the rule to given file.
 | |
| func (r *ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
 | |
| 	var failures []lint.Failure
 | |
| 
 | |
| 	onFailure := func(failure lint.Failure) {
 | |
| 		failures = append(failures, failure)
 | |
| 	}
 | |
| 
 | |
| 	w := lintModifiesValRecRule{file: file, onFailure: onFailure}
 | |
| 	file.Pkg.TypeCheck()
 | |
| 	ast.Walk(w, file.AST)
 | |
| 
 | |
| 	return failures
 | |
| }
 | |
| 
 | |
| // Name returns the rule name.
 | |
| func (r *ModifiesValRecRule) Name() string {
 | |
| 	return "modifies-value-receiver"
 | |
| }
 | |
| 
 | |
| type lintModifiesValRecRule struct {
 | |
| 	file      *lint.File
 | |
| 	onFailure func(lint.Failure)
 | |
| }
 | |
| 
 | |
| func (w lintModifiesValRecRule) Visit(node ast.Node) ast.Visitor {
 | |
| 	switch n := node.(type) {
 | |
| 	case *ast.FuncDecl:
 | |
| 		if n.Recv == nil {
 | |
| 			return nil // skip, not a method
 | |
| 		}
 | |
| 
 | |
| 		receiver := n.Recv.List[0]
 | |
| 		if _, ok := receiver.Type.(*ast.StarExpr); ok {
 | |
| 			return nil // skip, method with pointer receiver
 | |
| 		}
 | |
| 
 | |
| 		if w.skipType(receiver.Type) {
 | |
| 			return nil // skip, receiver is a map or array
 | |
| 		}
 | |
| 
 | |
| 		if len(receiver.Names) < 1 {
 | |
| 			return nil // skip, anonymous receiver
 | |
| 		}
 | |
| 
 | |
| 		receiverName := receiver.Names[0].Name
 | |
| 		if receiverName == "_" {
 | |
| 			return nil // skip, anonymous receiver
 | |
| 		}
 | |
| 
 | |
| 		fselect := func(n ast.Node) bool {
 | |
| 			// look for assignments with the receiver in the right hand
 | |
| 			asgmt, ok := n.(*ast.AssignStmt)
 | |
| 			if !ok {
 | |
| 				return false
 | |
| 			}
 | |
| 
 | |
| 			for _, exp := range asgmt.Lhs {
 | |
| 				switch e := exp.(type) {
 | |
| 				case *ast.IndexExpr: // receiver...[] = ...
 | |
| 					continue
 | |
| 				case *ast.StarExpr: // *receiver = ...
 | |
| 					continue
 | |
| 				case *ast.SelectorExpr: // receiver.field = ...
 | |
| 					name := w.getNameFromExpr(e.X)
 | |
| 					if name == "" || name != receiverName {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if w.skipType(ast.Expr(e.Sel)) {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 				case *ast.Ident: // receiver := ...
 | |
| 					if e.Name != receiverName {
 | |
| 						continue
 | |
| 					}
 | |
| 				default:
 | |
| 					continue
 | |
| 				}
 | |
| 
 | |
| 				return true
 | |
| 			}
 | |
| 
 | |
| 			return false
 | |
| 		}
 | |
| 
 | |
| 		assignmentsToReceiver := pick(n.Body, fselect, nil)
 | |
| 
 | |
| 		for _, assignment := range assignmentsToReceiver {
 | |
| 			w.onFailure(lint.Failure{
 | |
| 				Node:       assignment,
 | |
| 				Confidence: 1,
 | |
| 				Failure:    "suspicious assignment to a by-value method receiver",
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| func (w lintModifiesValRecRule) skipType(t ast.Expr) bool {
 | |
| 	rt := w.file.Pkg.TypeOf(t)
 | |
| 	if rt == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	rt = rt.Underlying()
 | |
| 	rtName := rt.String()
 | |
| 
 | |
| 	// skip when receiver is a map or array
 | |
| 	return strings.HasPrefix(rtName, "[]") || strings.HasPrefix(rtName, "map[")
 | |
| }
 | |
| 
 | |
| func (lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string {
 | |
| 	ident, ok := ie.(*ast.Ident)
 | |
| 	if !ok {
 | |
| 		return ""
 | |
| 	}
 | |
| 
 | |
| 	return ident.Name
 | |
| }
 |