.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / go / analysis / passes / unusedwrite / unusedwrite.go
1 // Copyright 2021 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package unusedwrite checks for unused writes to the elements of a struct or array object.
6 package unusedwrite
7
8 import (
9         "fmt"
10         "go/types"
11
12         "golang.org/x/tools/go/analysis"
13         "golang.org/x/tools/go/analysis/passes/buildssa"
14         "golang.org/x/tools/go/ssa"
15 )
16
17 // Doc is a documentation string.
18 const Doc = `checks for unused writes
19
20 The analyzer reports instances of writes to struct fields and
21 arrays that are never read. Specifically, when a struct object
22 or an array is copied, its elements are copied implicitly by
23 the compiler, and any element write to this copy does nothing
24 with the original object.
25
26 For example:
27
28         type T struct { x int }
29         func f(input []T) {
30                 for i, v := range input {  // v is a copy
31                         v.x = i  // unused write to field x
32                 }
33         }
34
35 Another example is about non-pointer receiver:
36
37         type T struct { x int }
38         func (t T) f() {  // t is a copy
39                 t.x = i  // unused write to field x
40         }
41 `
42
43 // Analyzer reports instances of writes to struct fields and arrays
44 //that are never read.
45 var Analyzer = &analysis.Analyzer{
46         Name:     "unusedwrite",
47         Doc:      Doc,
48         Requires: []*analysis.Analyzer{buildssa.Analyzer},
49         Run:      run,
50 }
51
52 func run(pass *analysis.Pass) (interface{}, error) {
53         // Check the writes to struct and array objects.
54         checkStore := func(store *ssa.Store) {
55                 // Consider field/index writes to an object whose elements are copied and not shared.
56                 // MapUpdate is excluded since only the reference of the map is copied.
57                 switch addr := store.Addr.(type) {
58                 case *ssa.FieldAddr:
59                         if isDeadStore(store, addr.X, addr) {
60                                 // Report the bug.
61                                 pass.Reportf(store.Pos(),
62                                         "unused write to field %s",
63                                         getFieldName(addr.X.Type(), addr.Field))
64                         }
65                 case *ssa.IndexAddr:
66                         if isDeadStore(store, addr.X, addr) {
67                                 // Report the bug.
68                                 pass.Reportf(store.Pos(),
69                                         "unused write to array index %s", addr.Index)
70                         }
71                 }
72         }
73
74         ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
75         for _, fn := range ssainput.SrcFuncs {
76                 // Visit each block. No need to visit fn.Recover.
77                 for _, blk := range fn.Blocks {
78                         for _, instr := range blk.Instrs {
79                                 // Identify writes.
80                                 if store, ok := instr.(*ssa.Store); ok {
81                                         checkStore(store)
82                                 }
83                         }
84                 }
85         }
86         return nil, nil
87 }
88
89 // isDeadStore determines whether a field/index write to an object is dead.
90 // Argument "obj" is the object, and "addr" is the instruction fetching the field/index.
91 func isDeadStore(store *ssa.Store, obj ssa.Value, addr ssa.Instruction) bool {
92         // Consider only struct or array objects.
93         if !hasStructOrArrayType(obj) {
94                 return false
95         }
96         // Check liveness: if the value is used later, then don't report the write.
97         for _, ref := range *obj.Referrers() {
98                 if ref == store || ref == addr {
99                         continue
100                 }
101                 switch ins := ref.(type) {
102                 case ssa.CallInstruction:
103                         return false
104                 case *ssa.FieldAddr:
105                         // Check whether the same field is used.
106                         if ins.X == obj {
107                                 if faddr, ok := addr.(*ssa.FieldAddr); ok {
108                                         if faddr.Field == ins.Field {
109                                                 return false
110                                         }
111                                 }
112                         }
113                         // Otherwise another field is used, and this usage doesn't count.
114                         continue
115                 case *ssa.IndexAddr:
116                         if ins.X == obj {
117                                 return false
118                         }
119                         continue // Otherwise another object is used
120                 case *ssa.Lookup:
121                         if ins.X == obj {
122                                 return false
123                         }
124                         continue // Otherwise another object is used
125                 case *ssa.Store:
126                         if ins.Val == obj {
127                                 return false
128                         }
129                         continue // Otherwise other object is stored
130                 default: // consider live if the object is used in any other instruction
131                         return false
132                 }
133         }
134         return true
135 }
136
137 // isStructOrArray returns whether the underlying type is struct or array.
138 func isStructOrArray(tp types.Type) bool {
139         if named, ok := tp.(*types.Named); ok {
140                 tp = named.Underlying()
141         }
142         switch tp.(type) {
143         case *types.Array:
144                 return true
145         case *types.Struct:
146                 return true
147         }
148         return false
149 }
150
151 // hasStructOrArrayType returns whether a value is of struct or array type.
152 func hasStructOrArrayType(v ssa.Value) bool {
153         if instr, ok := v.(ssa.Instruction); ok {
154                 if alloc, ok := instr.(*ssa.Alloc); ok {
155                         // Check the element type of an allocated register (which always has pointer type)
156                         // e.g., for
157                         //   func (t T) f() { ...}
158                         // the receiver object is of type *T:
159                         //   t0 = local T (t)   *T
160                         if tp, ok := alloc.Type().(*types.Pointer); ok {
161                                 return isStructOrArray(tp.Elem())
162                         }
163                         return false
164                 }
165         }
166         return isStructOrArray(v.Type())
167 }
168
169 // getFieldName returns the name of a field in a struct.
170 // It the field is not found, then it returns the string format of the index.
171 //
172 // For example, for struct T {x int, y int), getFieldName(*T, 1) returns "y".
173 func getFieldName(tp types.Type, index int) string {
174         if pt, ok := tp.(*types.Pointer); ok {
175                 tp = pt.Elem()
176         }
177         if named, ok := tp.(*types.Named); ok {
178                 tp = named.Underlying()
179         }
180         if stp, ok := tp.(*types.Struct); ok {
181                 return stp.Field(index).Name()
182         }
183         return fmt.Sprintf("%d", index)
184 }