...

Source file src/pkg/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	)
    21	
    22	// TODO(adonovan): make this analysis modular: export a mustUseResult
    23	// fact for each function that tail-calls one of the functions that we
    24	// check, and check those functions too.
    25	
    26	const Doc = `check for unused results of calls to some functions
    27	
    28	Some functions like fmt.Errorf return a result and have no side effects,
    29	so it is always a mistake to discard the result. This analyzer reports
    30	calls to certain functions in which the result of the call is ignored.
    31	
    32	The set of functions may be controlled using flags.`
    33	
    34	var Analyzer = &analysis.Analyzer{
    35		Name:     "unusedresult",
    36		Doc:      Doc,
    37		Requires: []*analysis.Analyzer{inspect.Analyzer},
    38		Run:      run,
    39	}
    40	
    41	// flags
    42	var funcs, stringMethods stringSetFlag
    43	
    44	func init() {
    45		// TODO(adonovan): provide a comment syntax to allow users to
    46		// add their functions to this set using facts.
    47		funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse")
    48		Analyzer.Flags.Var(&funcs, "funcs",
    49			"comma-separated list of functions whose results must be used")
    50	
    51		stringMethods.Set("Error,String")
    52		Analyzer.Flags.Var(&stringMethods, "stringmethods",
    53			"comma-separated list of names of methods of type func() string whose results must be used")
    54	}
    55	
    56	func run(pass *analysis.Pass) (interface{}, error) {
    57		inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    58	
    59		nodeFilter := []ast.Node{
    60			(*ast.ExprStmt)(nil),
    61		}
    62		inspect.Preorder(nodeFilter, func(n ast.Node) {
    63			call, ok := analysisutil.Unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr)
    64			if !ok {
    65				return // not a call statement
    66			}
    67			fun := analysisutil.Unparen(call.Fun)
    68	
    69			if pass.TypesInfo.Types[fun].IsType() {
    70				return // a conversion, not a call
    71			}
    72	
    73			selector, ok := fun.(*ast.SelectorExpr)
    74			if !ok {
    75				return // neither a method call nor a qualified ident
    76			}
    77	
    78			sel, ok := pass.TypesInfo.Selections[selector]
    79			if ok && sel.Kind() == types.MethodVal {
    80				// method (e.g. foo.String())
    81				obj := sel.Obj().(*types.Func)
    82				sig := sel.Type().(*types.Signature)
    83				if types.Identical(sig, sigNoArgsStringResult) {
    84					if stringMethods[obj.Name()] {
    85						pass.Reportf(call.Lparen, "result of (%s).%s call not used",
    86							sig.Recv().Type(), obj.Name())
    87					}
    88				}
    89			} else if !ok {
    90				// package-qualified function (e.g. fmt.Errorf)
    91				obj := pass.TypesInfo.Uses[selector.Sel]
    92				if obj, ok := obj.(*types.Func); ok {
    93					qname := obj.Pkg().Path() + "." + obj.Name()
    94					if funcs[qname] {
    95						pass.Reportf(call.Lparen, "result of %v call not used", qname)
    96					}
    97				}
    98			}
    99		})
   100		return nil, nil
   101	}
   102	
   103	// func() string
   104	var sigNoArgsStringResult = types.NewSignature(nil, nil,
   105		types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
   106		false)
   107	
   108	type stringSetFlag map[string]bool
   109	
   110	func (ss *stringSetFlag) String() string {
   111		var items []string
   112		for item := range *ss {
   113			items = append(items, item)
   114		}
   115		sort.Strings(items)
   116		return strings.Join(items, ",")
   117	}
   118	
   119	func (ss *stringSetFlag) Set(s string) error {
   120		m := make(map[string]bool) // clobber previous value
   121		if s != "" {
   122			for _, name := range strings.Split(s, ",") {
   123				if name == "" {
   124					continue // TODO: report error? proceed?
   125				}
   126				m[name] = true
   127			}
   128		}
   129		*ss = m
   130		return nil
   131	}
   132	

View as plain text