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// This file defines the check for unused results of calls to certain 6// pure functions. 7 8package main 9 10import ( 11 "flag" 12 "go/ast" 13 "go/token" 14 "go/types" 15 "strings" 16) 17 18var unusedFuncsFlag = flag.String("unusedfuncs", 19 "errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse", 20 "comma-separated list of functions whose results must be used") 21 22var unusedStringMethodsFlag = flag.String("unusedstringmethods", 23 "Error,String", 24 "comma-separated list of names of methods of type func() string whose results must be used") 25 26func init() { 27 register("unusedresult", 28 "check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list", 29 checkUnusedResult, 30 exprStmt) 31} 32 33// func() string 34var sigNoArgsStringResult = types.NewSignature(nil, nil, 35 types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])), 36 false) 37 38var unusedFuncs = make(map[string]bool) 39var unusedStringMethods = make(map[string]bool) 40 41func initUnusedFlags() { 42 commaSplit := func(s string, m map[string]bool) { 43 if s != "" { 44 for _, name := range strings.Split(s, ",") { 45 if len(name) == 0 { 46 flag.Usage() 47 } 48 m[name] = true 49 } 50 } 51 } 52 commaSplit(*unusedFuncsFlag, unusedFuncs) 53 commaSplit(*unusedStringMethodsFlag, unusedStringMethods) 54} 55 56func checkUnusedResult(f *File, n ast.Node) { 57 call, ok := unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr) 58 if !ok { 59 return // not a call statement 60 } 61 fun := unparen(call.Fun) 62 63 if f.pkg.types[fun].IsType() { 64 return // a conversion, not a call 65 } 66 67 selector, ok := fun.(*ast.SelectorExpr) 68 if !ok { 69 return // neither a method call nor a qualified ident 70 } 71 72 sel, ok := f.pkg.selectors[selector] 73 if ok && sel.Kind() == types.MethodVal { 74 // method (e.g. foo.String()) 75 obj := sel.Obj().(*types.Func) 76 sig := sel.Type().(*types.Signature) 77 if types.Identical(sig, sigNoArgsStringResult) { 78 if unusedStringMethods[obj.Name()] { 79 f.Badf(call.Lparen, "result of (%s).%s call not used", 80 sig.Recv().Type(), obj.Name()) 81 } 82 } 83 } else if !ok { 84 // package-qualified function (e.g. fmt.Errorf) 85 obj := f.pkg.uses[selector.Sel] 86 if obj, ok := obj.(*types.Func); ok { 87 qname := obj.Pkg().Path() + "." + obj.Name() 88 if unusedFuncs[qname] { 89 f.Badf(call.Lparen, "result of %v call not used", qname) 90 } 91 } 92 } 93} 94