src

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

sa4006.go (4087B)


      1 package sa4006
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 	"go/token"
      7 
      8 	"honnef.co/go/tools/analysis/code"
      9 	"honnef.co/go/tools/analysis/facts/generated"
     10 	"honnef.co/go/tools/analysis/lint"
     11 	"honnef.co/go/tools/analysis/report"
     12 	"honnef.co/go/tools/go/ir"
     13 	"honnef.co/go/tools/go/ir/irutil"
     14 	"honnef.co/go/tools/internal/passes/buildir"
     15 
     16 	"golang.org/x/tools/go/analysis"
     17 )
     18 
     19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     20 	Analyzer: &analysis.Analyzer{
     21 		Name:     "SA4006",
     22 		Run:      run,
     23 		Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},
     24 	},
     25 	Doc: &lint.RawDocumentation{
     26 		Title:    `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`,
     27 		Since:    "2017.1",
     28 		Severity: lint.SeverityWarning,
     29 		MergeIf:  lint.MergeIfAll,
     30 	},
     31 })
     32 
     33 var Analyzer = SCAnalyzer.Analyzer
     34 
     35 func run(pass *analysis.Pass) (any, error) {
     36 	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
     37 		if irutil.IsExample(fn) {
     38 			continue
     39 		}
     40 		node := fn.Source()
     41 		if node == nil {
     42 			continue
     43 		}
     44 		if gen, ok := code.Generator(pass, node.Pos()); ok && gen == generated.Goyacc {
     45 			// Don't flag unused values in code generated by goyacc.
     46 			// There may be hundreds of those due to the way the state
     47 			// machine is constructed.
     48 			continue
     49 		}
     50 
     51 		switchTags := map[ir.Value]struct{}{}
     52 		ast.Inspect(node, func(node ast.Node) bool {
     53 			s, ok := node.(*ast.SwitchStmt)
     54 			if !ok {
     55 				return true
     56 			}
     57 			v, _ := fn.ValueForExpr(s.Tag)
     58 			switchTags[v] = struct{}{}
     59 			return true
     60 		})
     61 
     62 		// OPT(dh): don't use a map, possibly use a bitset
     63 		var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool
     64 		hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool {
     65 			if _, ok := seen[v]; ok {
     66 				return false
     67 			}
     68 			if _, ok := switchTags[v]; ok {
     69 				return true
     70 			}
     71 			refs := v.Referrers()
     72 			if refs == nil {
     73 				// TODO investigate why refs can be nil
     74 				return true
     75 			}
     76 			for _, ref := range *refs {
     77 				switch ref := ref.(type) {
     78 				case *ir.Phi:
     79 					if seen == nil {
     80 						seen = map[ir.Value]struct{}{}
     81 					}
     82 					seen[v] = struct{}{}
     83 					if hasUse(ref, seen) {
     84 						return true
     85 					}
     86 				default:
     87 					return true
     88 				}
     89 			}
     90 			return false
     91 		}
     92 
     93 		ast.Inspect(node, func(node ast.Node) bool {
     94 			inc, ok := node.(*ast.IncDecStmt)
     95 			if ok {
     96 				val, _ := fn.ValueForExpr(inc.X)
     97 				if val == nil {
     98 					return true
     99 				}
    100 				if _, ok := val.(*ir.Const); ok {
    101 					// a zero-valued constant, for example in 'foo := []string(nil)'
    102 					return true
    103 				}
    104 				if !hasUse(val, nil) {
    105 					report.Report(pass, inc, fmt.Sprintf("this value of %s is never used", inc.X))
    106 				}
    107 				return true
    108 			}
    109 
    110 			assign, ok := node.(*ast.AssignStmt)
    111 			if !ok {
    112 				return true
    113 			}
    114 			if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 {
    115 				// Either a function call with multiple return values,
    116 				// or a comma-ok assignment
    117 
    118 				val, _ := fn.ValueForExpr(assign.Rhs[0])
    119 				if val == nil {
    120 					return true
    121 				}
    122 				refs := val.Referrers()
    123 				if refs == nil {
    124 					return true
    125 				}
    126 				for _, ref := range *refs {
    127 					ex, ok := ref.(*ir.Extract)
    128 					if !ok {
    129 						continue
    130 					}
    131 					if !hasUse(ex, nil) {
    132 						lhs := assign.Lhs[ex.Index]
    133 						if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
    134 							continue
    135 						}
    136 						report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
    137 					}
    138 				}
    139 				return true
    140 			}
    141 			for i, lhs := range assign.Lhs {
    142 				rhs := assign.Rhs[i]
    143 				if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
    144 					continue
    145 				}
    146 				val, _ := fn.ValueForExpr(rhs)
    147 				if val == nil {
    148 					if assign.Tok != token.ASSIGN { // +=, *=, etc.
    149 						val, _ = fn.ValueForExpr(lhs)
    150 					}
    151 					if val == nil {
    152 						continue
    153 					}
    154 				}
    155 
    156 				if _, ok := val.(*ir.Const); ok {
    157 					// a zero-valued constant, for example in 'foo := []string(nil)'
    158 					continue
    159 				}
    160 				if !hasUse(val, nil) {
    161 					report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs))
    162 				}
    163 			}
    164 			return true
    165 		})
    166 	}
    167 	return nil, nil
    168 }