| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This file defines the check for unused results of calls to certain |
| // pure functions. |
| |
| package main |
| |
| import ( |
| "flag" |
| "go/ast" |
| "go/token" |
| "go/types" |
| "strings" |
| ) |
| |
| var unusedFuncsFlag = flag.String("unusedfuncs", |
| "errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse", |
| "comma-separated list of functions whose results must be used") |
| |
| var unusedStringMethodsFlag = flag.String("unusedstringmethods", |
| "Error,String", |
| "comma-separated list of names of methods of type func() string whose results must be used") |
| |
| func init() { |
| register("unusedresult", |
| "check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list", |
| checkUnusedResult, |
| exprStmt) |
| } |
| |
| // func() string |
| var sigNoArgsStringResult = types.NewSignature(nil, nil, |
| types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])), |
| false) |
| |
| var unusedFuncs = make(map[string]bool) |
| var unusedStringMethods = make(map[string]bool) |
| |
| func initUnusedFlags() { |
| commaSplit := func(s string, m map[string]bool) { |
| if s != "" { |
| for _, name := range strings.Split(s, ",") { |
| if len(name) == 0 { |
| flag.Usage() |
| } |
| m[name] = true |
| } |
| } |
| } |
| commaSplit(*unusedFuncsFlag, unusedFuncs) |
| commaSplit(*unusedStringMethodsFlag, unusedStringMethods) |
| } |
| |
| func checkUnusedResult(f *File, n ast.Node) { |
| call, ok := unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr) |
| if !ok { |
| return // not a call statement |
| } |
| fun := unparen(call.Fun) |
| |
| if f.pkg.types[fun].IsType() { |
| return // a conversion, not a call |
| } |
| |
| selector, ok := fun.(*ast.SelectorExpr) |
| if !ok { |
| return // neither a method call nor a qualified ident |
| } |
| |
| sel, ok := f.pkg.selectors[selector] |
| if ok && sel.Kind() == types.MethodVal { |
| // method (e.g. foo.String()) |
| obj := sel.Obj().(*types.Func) |
| sig := sel.Type().(*types.Signature) |
| if types.Identical(sig, sigNoArgsStringResult) { |
| if unusedStringMethods[obj.Name()] { |
| f.Badf(call.Lparen, "result of (%s).%s call not used", |
| sig.Recv().Type(), obj.Name()) |
| } |
| } |
| } else if !ok { |
| // package-qualified function (e.g. fmt.Errorf) |
| obj := f.pkg.uses[selector.Sel] |
| if obj, ok := obj.(*types.Func); ok { |
| qname := obj.Pkg().Path() + "." + obj.Name() |
| if unusedFuncs[qname] { |
| f.Badf(call.Lparen, "result of %v call not used", qname) |
| } |
| } |
| } |
| } |