mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 22:41:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			113 lines
		
	
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			113 lines
		
	
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| package rule
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 
 | |
| 	"github.com/mgechev/revive/lint"
 | |
| )
 | |
| 
 | |
| // RangeValAddress lints
 | |
| type RangeValAddress struct{}
 | |
| 
 | |
| // Apply applies the rule to given file.
 | |
| func (r *RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
 | |
| 	var failures []lint.Failure
 | |
| 
 | |
| 	walker := rangeValAddress{
 | |
| 		onFailure: func(failure lint.Failure) {
 | |
| 			failures = append(failures, failure)
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	ast.Walk(walker, file.AST)
 | |
| 
 | |
| 	return failures
 | |
| }
 | |
| 
 | |
| // Name returns the rule name.
 | |
| func (r *RangeValAddress) Name() string {
 | |
| 	return "range-val-address"
 | |
| }
 | |
| 
 | |
| type rangeValAddress struct {
 | |
| 	onFailure func(lint.Failure)
 | |
| }
 | |
| 
 | |
| func (w rangeValAddress) Visit(node ast.Node) ast.Visitor {
 | |
| 	n, ok := node.(*ast.RangeStmt)
 | |
| 	if !ok {
 | |
| 		return w
 | |
| 	}
 | |
| 
 | |
| 	value, ok := n.Value.(*ast.Ident)
 | |
| 	if !ok {
 | |
| 		return w
 | |
| 	}
 | |
| 
 | |
| 	ast.Walk(rangeBodyVisitor{
 | |
| 		valueID:   value.Obj,
 | |
| 		onFailure: w.onFailure,
 | |
| 	}, n.Body)
 | |
| 
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| type rangeBodyVisitor struct {
 | |
| 	valueID   *ast.Object
 | |
| 	onFailure func(lint.Failure)
 | |
| }
 | |
| 
 | |
| func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor {
 | |
| 	asgmt, ok := node.(*ast.AssignStmt)
 | |
| 	if !ok {
 | |
| 		return bw
 | |
| 	}
 | |
| 
 | |
| 	for _, exp := range asgmt.Lhs {
 | |
| 		e, ok := exp.(*ast.IndexExpr)
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		if bw.isAccessingRangeValueAddress(e.Index) { // e.g. a[&value]...
 | |
| 			bw.onFailure(bw.newFailure(e.Index))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, exp := range asgmt.Rhs {
 | |
| 		switch e := exp.(type) {
 | |
| 		case *ast.UnaryExpr: // e.g. ...&value
 | |
| 			if bw.isAccessingRangeValueAddress(e) {
 | |
| 				bw.onFailure(bw.newFailure(e))
 | |
| 			}
 | |
| 		case *ast.CallExpr:
 | |
| 			if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value)
 | |
| 				for _, v := range e.Args {
 | |
| 					if bw.isAccessingRangeValueAddress(v) {
 | |
| 						bw.onFailure(bw.newFailure(e))
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return bw
 | |
| }
 | |
| 
 | |
| func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool {
 | |
| 	u, ok := exp.(*ast.UnaryExpr)
 | |
| 	if !ok {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	v, ok := u.X.(*ast.Ident)
 | |
| 	return ok && u.Op == token.AND && v.Obj == bw.valueID
 | |
| }
 | |
| 
 | |
| func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure {
 | |
| 	return lint.Failure{
 | |
| 		Node:       node,
 | |
| 		Confidence: 1,
 | |
| 		Failure:    fmt.Sprintf("suspicious assignment of '%s'. range-loop variables always have the same address", bw.valueID.Name),
 | |
| 	}
 | |
| }
 |