...
1
2
3
4
5 package unusedwrite
6
7 import (
8 _ "embed"
9 "go/types"
10
11 "golang.org/x/tools/go/analysis"
12 "golang.org/x/tools/go/analysis/passes/buildssa"
13 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
14 "golang.org/x/tools/go/ssa"
15 "golang.org/x/tools/internal/aliases"
16 "golang.org/x/tools/internal/typeparams"
17 )
18
19
20 var doc string
21
22
23
24 var Analyzer = &analysis.Analyzer{
25 Name: "unusedwrite",
26 Doc: analysisutil.MustExtractDoc(doc, "unusedwrite"),
27 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite",
28 Requires: []*analysis.Analyzer{buildssa.Analyzer},
29 Run: run,
30 }
31
32 func run(pass *analysis.Pass) (interface{}, error) {
33 ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
34 for _, fn := range ssainput.SrcFuncs {
35
36 reports := checkStores(fn)
37 for _, store := range reports {
38 switch addr := store.Addr.(type) {
39 case *ssa.FieldAddr:
40 field := typeparams.CoreType(typeparams.MustDeref(addr.X.Type())).(*types.Struct).Field(addr.Field)
41 pass.Reportf(store.Pos(),
42 "unused write to field %s", field.Name())
43 case *ssa.IndexAddr:
44 pass.Reportf(store.Pos(),
45 "unused write to array index %s", addr.Index)
46 }
47 }
48 }
49 return nil, nil
50 }
51
52
53 func checkStores(fn *ssa.Function) []*ssa.Store {
54 var reports []*ssa.Store
55
56 for _, blk := range fn.Blocks {
57 for _, instr := range blk.Instrs {
58
59 if store, ok := instr.(*ssa.Store); ok {
60
61
62 switch addr := store.Addr.(type) {
63 case *ssa.FieldAddr:
64 if isDeadStore(store, addr.X, addr) {
65 reports = append(reports, store)
66 }
67 case *ssa.IndexAddr:
68 if isDeadStore(store, addr.X, addr) {
69 reports = append(reports, store)
70 }
71 }
72 }
73 }
74 }
75 return reports
76 }
77
78
79
80 func isDeadStore(store *ssa.Store, obj ssa.Value, addr ssa.Instruction) bool {
81
82 if !hasStructOrArrayType(obj) {
83 return false
84 }
85
86 for _, ref := range *obj.Referrers() {
87 if ref == store || ref == addr {
88 continue
89 }
90 switch ins := ref.(type) {
91 case ssa.CallInstruction:
92 return false
93 case *ssa.FieldAddr:
94
95 if ins.X == obj {
96 if faddr, ok := addr.(*ssa.FieldAddr); ok {
97 if faddr.Field == ins.Field {
98 return false
99 }
100 }
101 }
102
103 continue
104 case *ssa.IndexAddr:
105 if ins.X == obj {
106 return false
107 }
108 continue
109 case *ssa.Lookup:
110 if ins.X == obj {
111 return false
112 }
113 continue
114 case *ssa.Store:
115 if ins.Val == obj {
116 return false
117 }
118 continue
119 default:
120 return false
121 }
122 }
123 return true
124 }
125
126
127 func isStructOrArray(tp types.Type) bool {
128 switch tp.Underlying().(type) {
129 case *types.Array:
130 return true
131 case *types.Struct:
132 return true
133 }
134 return false
135 }
136
137
138 func hasStructOrArrayType(v ssa.Value) bool {
139 if instr, ok := v.(ssa.Instruction); ok {
140 if alloc, ok := instr.(*ssa.Alloc); ok {
141
142
143
144
145
146 if tp, ok := aliases.Unalias(alloc.Type()).(*types.Pointer); ok {
147 return isStructOrArray(tp.Elem())
148 }
149 return false
150 }
151 }
152 return isStructOrArray(v.Type())
153 }
154
View as plain text