github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/vet/unused.go (about)

     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  }