Source file src/cmd/vendor/golang.org/x/tools/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.
     7  package unusedresult
     8  
     9  import (
    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  
    27  const Doc = `check for unused results of calls to some functions
    28  
    29  Some functions like fmt.Errorf return a result and have no side effects,
    30  so it is always a mistake to discard the result. This analyzer reports
    31  calls to certain functions in which the result of the call is ignored.
    32  
    33  The set of functions may be controlled using flags.`
    34  
    35  var Analyzer = &analysis.Analyzer{
    36  	Name:     "unusedresult",
    37  	Doc:      Doc,
    38  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    39  	Run:      run,
    40  }
    41  
    42  // flags
    43  var funcs, stringMethods stringSetFlag
    44  
    45  func 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  
    57  func 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  		call, ok := 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  		selector, ok := fun.(*ast.SelectorExpr)
    80  		if !ok {
    81  			return // neither a method call nor a qualified ident
    82  		}
    83  
    84  		sel, ok := 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(sig, sigNoArgsStringResult) {
    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 obj, ok := 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 nil, nil
   107  }
   108  
   109  // func() string
   110  var sigNoArgsStringResult = types.NewSignature(nil, nil,
   111  	types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
   112  	false)
   113  
   114  type stringSetFlag map[string]bool
   115  
   116  func (ss *stringSetFlag) String() string {
   117  	var items []string
   118  	for item := range *ss {
   119  		items = append(items, item)
   120  	}
   121  	sort.Strings(items)
   122  	return strings.Join(items, ",")
   123  }
   124  
   125  func (ss *stringSetFlag) Set(s string) error {
   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  

View as plain text