...

Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go

Documentation: cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult

     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 functions.
     7  package unusedresult
     8  
     9  // It is tempting to make this analysis inductive: for each function
    10  // that tail-calls one of the functions that we check, check those
    11  // functions too. However, just because you must use the result of
    12  // fmt.Sprintf doesn't mean you need to use the result of every
    13  // function that returns a formatted string: it may have other results
    14  // and effects.
    15  
    16  import (
    17  	_ "embed"
    18  	"go/ast"
    19  	"go/token"
    20  	"go/types"
    21  	"sort"
    22  	"strings"
    23  
    24  	"golang.org/x/tools/go/analysis"
    25  	"golang.org/x/tools/go/analysis/passes/inspect"
    26  	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    27  	"golang.org/x/tools/go/ast/astutil"
    28  	"golang.org/x/tools/go/ast/inspector"
    29  	"golang.org/x/tools/go/types/typeutil"
    30  )
    31  
    32  //go:embed doc.go
    33  var doc string
    34  
    35  var Analyzer = &analysis.Analyzer{
    36  	Name:     "unusedresult",
    37  	Doc:      analysisutil.MustExtractDoc(doc, "unusedresult"),
    38  	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedresult",
    39  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    40  	Run:      run,
    41  }
    42  
    43  // flags
    44  var funcs, stringMethods stringSetFlag
    45  
    46  func init() {
    47  	// TODO(adonovan): provide a comment or declaration syntax to
    48  	// allow users to add their functions to this set using facts.
    49  	// For example:
    50  	//
    51  	//    func ignoringTheErrorWouldBeVeryBad() error {
    52  	//      type mustUseResult struct{} // enables vet unusedresult check
    53  	//      ...
    54  	//    }
    55  	//
    56  	//    ignoringTheErrorWouldBeVeryBad() // oops
    57  	//
    58  
    59  	// List standard library functions here.
    60  	// The context.With{Cancel,Deadline,Timeout} entries are
    61  	// effectively redundant wrt the lostcancel analyzer.
    62  	funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse,context.WithValue,context.WithCancel,context.WithDeadline,context.WithTimeout")
    63  	Analyzer.Flags.Var(&funcs, "funcs",
    64  		"comma-separated list of functions whose results must be used")
    65  
    66  	stringMethods.Set("Error,String")
    67  	Analyzer.Flags.Var(&stringMethods, "stringmethods",
    68  		"comma-separated list of names of methods of type func() string whose results must be used")
    69  }
    70  
    71  func run(pass *analysis.Pass) (interface{}, error) {
    72  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    73  
    74  	// Split functions into (pkg, name) pairs to save allocation later.
    75  	pkgFuncs := make(map[[2]string]bool, len(funcs))
    76  	for s := range funcs {
    77  		if i := strings.LastIndexByte(s, '.'); i > 0 {
    78  			pkgFuncs[[2]string{s[:i], s[i+1:]}] = true
    79  		}
    80  	}
    81  
    82  	nodeFilter := []ast.Node{
    83  		(*ast.ExprStmt)(nil),
    84  	}
    85  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    86  		call, ok := astutil.Unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr)
    87  		if !ok {
    88  			return // not a call statement
    89  		}
    90  
    91  		// Call to function or method?
    92  		fn, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func)
    93  		if !ok {
    94  			return // e.g. var or builtin
    95  		}
    96  		if sig := fn.Type().(*types.Signature); sig.Recv() != nil {
    97  			// method (e.g. foo.String())
    98  			if types.Identical(sig, sigNoArgsStringResult) {
    99  				if stringMethods[fn.Name()] {
   100  					pass.Reportf(call.Lparen, "result of (%s).%s call not used",
   101  						sig.Recv().Type(), fn.Name())
   102  				}
   103  			}
   104  		} else {
   105  			// package-level function (e.g. fmt.Errorf)
   106  			if pkgFuncs[[2]string{fn.Pkg().Path(), fn.Name()}] {
   107  				pass.Reportf(call.Lparen, "result of %s.%s call not used",
   108  					fn.Pkg().Path(), fn.Name())
   109  			}
   110  		}
   111  	})
   112  	return nil, nil
   113  }
   114  
   115  // func() string
   116  var sigNoArgsStringResult = types.NewSignature(nil, nil,
   117  	types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
   118  	false)
   119  
   120  type stringSetFlag map[string]bool
   121  
   122  func (ss *stringSetFlag) String() string {
   123  	var items []string
   124  	for item := range *ss {
   125  		items = append(items, item)
   126  	}
   127  	sort.Strings(items)
   128  	return strings.Join(items, ",")
   129  }
   130  
   131  func (ss *stringSetFlag) Set(s string) error {
   132  	m := make(map[string]bool) // clobber previous value
   133  	if s != "" {
   134  		for _, name := range strings.Split(s, ",") {
   135  			if name == "" {
   136  				continue // TODO: report error? proceed?
   137  			}
   138  			m[name] = true
   139  		}
   140  	}
   141  	*ss = m
   142  	return nil
   143  }
   144  

View as plain text