src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa4009.go (2354B)


      1 package sa4009
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 
      7 	"honnef.co/go/tools/analysis/lint"
      8 	"honnef.co/go/tools/analysis/report"
      9 	"honnef.co/go/tools/go/ir"
     10 	"honnef.co/go/tools/internal/passes/buildir"
     11 
     12 	"golang.org/x/tools/go/analysis"
     13 )
     14 
     15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     16 	Analyzer: &analysis.Analyzer{
     17 		Name:     "SA4009",
     18 		Run:      run,
     19 		Requires: []*analysis.Analyzer{buildir.Analyzer},
     20 	},
     21 	Doc: &lint.RawDocumentation{
     22 		Title:    `A function argument is overwritten before its first use`,
     23 		Since:    "2017.1",
     24 		Severity: lint.SeverityWarning,
     25 		MergeIf:  lint.MergeIfAny,
     26 	},
     27 })
     28 
     29 var Analyzer = SCAnalyzer.Analyzer
     30 
     31 func run(pass *analysis.Pass) (any, error) {
     32 	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
     33 		cb := func(node ast.Node) bool {
     34 			var typ *ast.FuncType
     35 			var body *ast.BlockStmt
     36 			switch fn := node.(type) {
     37 			case *ast.FuncDecl:
     38 				typ = fn.Type
     39 				body = fn.Body
     40 			case *ast.FuncLit:
     41 				typ = fn.Type
     42 				body = fn.Body
     43 			}
     44 			if body == nil {
     45 				return true
     46 			}
     47 			if len(typ.Params.List) == 0 {
     48 				return true
     49 			}
     50 			for _, field := range typ.Params.List {
     51 				for _, arg := range field.Names {
     52 					obj := pass.TypesInfo.ObjectOf(arg)
     53 					var irobj *ir.Parameter
     54 					for _, param := range fn.Params {
     55 						if param.Object() == obj {
     56 							irobj = param
     57 							break
     58 						}
     59 					}
     60 					if irobj == nil {
     61 						continue
     62 					}
     63 					refs := irobj.Referrers()
     64 					if refs == nil {
     65 						continue
     66 					}
     67 					if len(*refs) != 0 {
     68 						continue
     69 					}
     70 
     71 					var assignment ast.Node
     72 					ast.Inspect(body, func(node ast.Node) bool {
     73 						if assignment != nil {
     74 							return false
     75 						}
     76 						assign, ok := node.(*ast.AssignStmt)
     77 						if !ok {
     78 							return true
     79 						}
     80 						for _, lhs := range assign.Lhs {
     81 							ident, ok := lhs.(*ast.Ident)
     82 							if !ok {
     83 								continue
     84 							}
     85 							if pass.TypesInfo.ObjectOf(ident) == obj {
     86 								assignment = assign
     87 								return false
     88 							}
     89 						}
     90 						return true
     91 					})
     92 					if assignment != nil {
     93 						report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg),
     94 							report.Related(assignment, fmt.Sprintf("assignment to %s", arg)))
     95 					}
     96 				}
     97 			}
     98 			return true
     99 		}
    100 		if source := fn.Source(); source != nil {
    101 			ast.Inspect(source, cb)
    102 		}
    103 	}
    104 	return nil, nil
    105 }