/
main.go
185 lines (161 loc) · 4.05 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
'gounused' finds unused assignements in your code.
The compiler checks for unused variables, but sometimes assignments
are never read before getting overwriten or ignored. For example this
code:
package main
import "fmt"
func main() {
_, err := fmt.Println("Hello")
_, err = fmt.Println(" world")
_, err = fmt.Println(err)
}
The err variable is used so the compiler does not complain, but the
first and third assignment to the err variable are never checked.
'gounused' finds that mistake as follows:
$ gounused ./testdata/
Unused assignment for 'err' ./testdata/test.go:6:5
Unused assignment for 'err' ./testdata/test.go:8:5
$ echo $?
1
*/
package main
import (
"flag"
"fmt"
"go/ast"
"go/token"
"log"
"os"
_ "golang.org/x/tools/go/gcimporter"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
)
const usage = `
'%[1]s' finds unused assignements in your code.
The compiler checks for unused variables, but sometimes assignments
are never read before getting overwriten or ignored. For example this
code:
package main
import "fmt"
func main() {
_, err := fmt.Println("Hello")
_, err = fmt.Println(" world")
_, err = fmt.Println(err)
}
The err variable is used so the compiler does not complain, but the
first and third assignment to the err variable are never checked.
'%[1]s' finds that mistake as follows:
$ %[1]s ./testdata/
Unused assignment for 'err' ./testdata/test.go:6:5
Unused assignment for 'err' ./testdata/test.go:8:5
$ echo $?
1
`
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, usage, os.Args[0])
}
flag.Parse()
count := myloader(flag.Args())
if count > 0 {
os.Exit(1)
}
}
// Return count of unused vars
func myloader(args []string) (count int) {
var conf loader.Config
conf.FromArgs(args, false)
prog, err := conf.Load()
if err != nil {
log.Fatal(err)
}
for _, pi := range prog.Created {
fmt.Println(pi)
}
info := prog.Package(args[0]).Info
fset := prog.Fset
ssaprog := ssautil.CreateProgram(prog, ssa.GlobalDebug)
ssaprog.Build()
for expr, object := range info.Defs {
unused := checkObj(expr, object, prog, ssaprog, fset)
if unused {
fmt.Fprintf(os.Stderr, "Unused assignment for '%v' %v\n", expr, fset.Position(expr.Pos()))
count++
}
}
for expr, object := range info.Uses {
unused := checkObj(expr, object, prog, ssaprog, fset)
if unused {
fmt.Fprintf(os.Stderr, "Unused assignment for '%v' %v\n", expr, fset.Position(expr.Pos()))
count++
}
}
return count
}
const debug = false
// Returns true if unused
func checkObj(expr *ast.Ident, object types.Object, prog *loader.Program, ssaprog *ssa.Program, fset *token.FileSet) (unused bool) {
if _, ok := object.(*types.Var); !ok {
if debug {
fmt.Println("Skipping object", object)
}
return false
}
pkg, node, _ := prog.PathEnclosingInterval(expr.Pos(), expr.End())
spkg := ssaprog.Package(pkg.Pkg)
f := ssa.EnclosingFunction(spkg, node)
if f == nil {
if debug {
fmt.Printf("Unknown function %v %v %v %v\n", fset.Position(expr.Pos()), object, pkg, prog)
}
return false
}
value, _ := f.ValueForExpr(expr)
// Unwrap unops and grab the value inside
if v, ok := value.(*ssa.UnOp); ok {
if debug {
fmt.Println("Unwrapping unop")
}
value = v.X
}
if debug {
fmt.Printf("%v %v: %v %#v\n", fset.Position(expr.Pos()), expr, object, value)
}
if _, ok := value.(*ssa.Global); ok {
if debug {
fmt.Printf(" is global\n")
}
return false
}
if value == nil {
if debug {
fmt.Println("Value is nil", object)
}
return false
}
refs := value.Referrers()
if refs == nil {
if debug {
fmt.Println("Referrers is nil", object)
}
return false
}
if debug {
fmt.Printf(" (refs) %v\n", refs)
}
hasRef := false
for _, r := range *refs {
_, ok := r.(*ssa.DebugRef)
hasRef = hasRef || !ok
if debug && !ok {
fmt.Printf("%v %v: %v %v\n", fset.Position(expr.Pos()), expr, object, r)
}
}
if !hasRef {
unused = true
}
return unused
}