GoPLS Viewer

Home|gopls/go/analysis/passes/unusedresult/unusedresult.go
1// Copyright 2015 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 unusedresult defines an analyzer that checks for unused
6// results of calls to certain pure functions.
7package unusedresult
8
9import (
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// TODO(adonovan): make this analysis modular: export a mustUseResult
24// fact for each function that tail-calls one of the functions that we
25// check, and check those functions too.
26
27const Doc = `check for unused results of calls to some functions
28
29Some functions like fmt.Errorf return a result and have no side effects,
30so it is always a mistake to discard the result. This analyzer reports
31calls to certain functions in which the result of the call is ignored.
32
33The set of functions may be controlled using flags.`
34
35var Analyzer = &analysis.Analyzer{
36    Name:     "unusedresult",
37    Doc:      Doc,
38    Requires: []*analysis.Analyzer{inspect.Analyzer},
39    Run:      run,
40}
41
42// flags
43var funcsstringMethods stringSetFlag
44
45func init() {
46    // TODO(adonovan): provide a comment syntax to allow users to
47    // add their functions to this set using facts.
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
57func 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        callok := analysisutil.Unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr)
65        if !ok {
66            return // not a call statement
67        }
68        fun := analysisutil.Unparen(call.Fun)
69
70        if pass.TypesInfo.Types[fun].IsType() {
71            return // a conversion, not a call
72        }
73
74        x___ := typeparams.UnpackIndexExpr(fun)
75        if x != nil {
76            fun = x // If this is generic function or method call, skip the instantiation arguments
77        }
78
79        selectorok := fun.(*ast.SelectorExpr)
80        if !ok {
81            return // neither a method call nor a qualified ident
82        }
83
84        selok := pass.TypesInfo.Selections[selector]
85        if ok && sel.Kind() == types.MethodVal {
86            // method (e.g. foo.String())
87            obj := sel.Obj().(*types.Func)
88            sig := sel.Type().(*types.Signature)
89            if types.Identical(sigsigNoArgsStringResult) {
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            // package-qualified function (e.g. fmt.Errorf)
97            obj := pass.TypesInfo.Uses[selector.Sel]
98            if objok := 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 nilnil
107}
108
109// func() string
110var sigNoArgsStringResult = types.NewSignature(nilnil,
111    types.NewTuple(types.NewVar(token.NoPosnil""types.Typ[types.String])),
112    false)
113
114type stringSetFlag map[string]bool
115
116func (ss *stringSetFlagString() string {
117    var items []string
118    for item := range *ss {
119        items = append(itemsitem)
120    }
121    sort.Strings(items)
122    return strings.Join(items",")
123}
124
125func (ss *stringSetFlagSet(s stringerror {
126    m := make(map[string]bool// clobber previous value
127    if s != "" {
128        for _name := range strings.Split(s",") {
129            if name == "" {
130                continue // TODO: report error? proceed?
131            }
132            m[name] = true
133        }
134    }
135    *ss = m
136    return nil
137}
138
MembersX
typeparams
stringSetFlag.Set.s
token
Doc
inspector
funcs
run
stringSetFlag.Set.ss
stringSetFlag.Set
sort
analysisutil
stringMethods
stringSetFlag.Set.BlockStmt.RangeStmt_3842.name
stringSetFlag.Set.m
analysis
init
inspect
run.nodeFilter
run.BlockStmt.x
stringSetFlag.String
types
strings
stringSetFlag.String.items
run.pass
run.BlockStmt.fun
stringSetFlag
stringSetFlag.String.ss
stringSetFlag.String.RangeStmt_3611.item
ast
run.BlockStmt._
Members
X