1
2
3
4
5
6
7 package unusedresult
8
9 import (
10 "go/ast"
11 "go/token"
12 "go/types"
13 "sort"
14 "strings"
15
16 "golang.org/x/tools/go/analysis"
17 "golang.org/x/tools/go/analysis/passes/inspect"
18 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
19 "golang.org/x/tools/go/ast/inspector"
20 "golang.org/x/tools/internal/typeparams"
21 )
22
23
24
25
26
27 const Doc = `check for unused results of calls to some functions
28
29 Some functions like fmt.Errorf return a result and have no side effects,
30 so it is always a mistake to discard the result. This analyzer reports
31 calls to certain functions in which the result of the call is ignored.
32
33 The set of functions may be controlled using flags.`
34
35 var Analyzer = &analysis.Analyzer{
36 Name: "unusedresult",
37 Doc: Doc,
38 Requires: []*analysis.Analyzer{inspect.Analyzer},
39 Run: run,
40 }
41
42
43 var funcs, stringMethods stringSetFlag
44
45 func init() {
46
47
48 funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse,context.WithValue,context.WithCancel,context.WithDeadline,context.WithTimeout")
49 Analyzer.Flags.Var(&funcs, "funcs",
50 "comma-separated list of functions whose results must be used")
51
52 stringMethods.Set("Error,String")
53 Analyzer.Flags.Var(&stringMethods, "stringmethods",
54 "comma-separated list of names of methods of type func() string whose results must be used")
55 }
56
57 func run(pass *analysis.Pass) (interface{}, error) {
58 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
59
60 nodeFilter := []ast.Node{
61 (*ast.ExprStmt)(nil),
62 }
63 inspect.Preorder(nodeFilter, func(n ast.Node) {
64 call, ok := analysisutil.Unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr)
65 if !ok {
66 return
67 }
68 fun := analysisutil.Unparen(call.Fun)
69
70 if pass.TypesInfo.Types[fun].IsType() {
71 return
72 }
73
74 x, _, _, _ := typeparams.UnpackIndexExpr(fun)
75 if x != nil {
76 fun = x
77 }
78
79 selector, ok := fun.(*ast.SelectorExpr)
80 if !ok {
81 return
82 }
83
84 sel, ok := pass.TypesInfo.Selections[selector]
85 if ok && sel.Kind() == types.MethodVal {
86
87 obj := sel.Obj().(*types.Func)
88 sig := sel.Type().(*types.Signature)
89 if types.Identical(sig, sigNoArgsStringResult) {
90 if stringMethods[obj.Name()] {
91 pass.Reportf(call.Lparen, "result of (%s).%s call not used",
92 sig.Recv().Type(), obj.Name())
93 }
94 }
95 } else if !ok {
96
97 obj := pass.TypesInfo.Uses[selector.Sel]
98 if obj, ok := obj.(*types.Func); ok {
99 qname := obj.Pkg().Path() + "." + obj.Name()
100 if funcs[qname] {
101 pass.Reportf(call.Lparen, "result of %v call not used", qname)
102 }
103 }
104 }
105 })
106 return nil, nil
107 }
108
109
110 var sigNoArgsStringResult = types.NewSignature(nil, nil,
111 types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
112 false)
113
114 type stringSetFlag map[string]bool
115
116 func (ss *stringSetFlag) String() string {
117 var items []string
118 for item := range *ss {
119 items = append(items, item)
120 }
121 sort.Strings(items)
122 return strings.Join(items, ",")
123 }
124
125 func (ss *stringSetFlag) Set(s string) error {
126 m := make(map[string]bool)
127 if s != "" {
128 for _, name := range strings.Split(s, ",") {
129 if name == "" {
130 continue
131 }
132 m[name] = true
133 }
134 }
135 *ss = m
136 return nil
137 }
138
View as plain text