sa4009.go raw

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