Home | History | Annotate | Download | only in vet
      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 
      8 package main
      9 
     10 import (
     11 	"flag"
     12 	"go/ast"
     13 	"go/token"
     14 	"go/types"
     15 	"strings"
     16 )
     17 
     18 var 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 
     22 var 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 
     26 func 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
     34 var sigNoArgsStringResult = types.NewSignature(nil, nil,
     35 	types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
     36 	false)
     37 
     38 var unusedFuncs = make(map[string]bool)
     39 var unusedStringMethods = make(map[string]bool)
     40 
     41 func 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 
     56 func 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