+++ /dev/null
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package unusedresult defines an analyzer that checks for unused
-// results of calls to certain pure functions.
-package unusedresult
-
-import (
- "go/ast"
- "go/token"
- "go/types"
- "sort"
- "strings"
-
- "golang.org/x/tools/go/analysis"
- "golang.org/x/tools/go/analysis/passes/inspect"
- "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
- "golang.org/x/tools/go/ast/inspector"
-)
-
-// TODO(adonovan): make this analysis modular: export a mustUseResult
-// fact for each function that tail-calls one of the functions that we
-// check, and check those functions too.
-
-const Doc = `check for unused results of calls to some functions
-
-Some functions like fmt.Errorf return a result and have no side effects,
-so it is always a mistake to discard the result. This analyzer reports
-calls to certain functions in which the result of the call is ignored.
-
-The set of functions may be controlled using flags.`
-
-var Analyzer = &analysis.Analyzer{
- Name: "unusedresult",
- Doc: Doc,
- Requires: []*analysis.Analyzer{inspect.Analyzer},
- Run: run,
-}
-
-// flags
-var funcs, stringMethods stringSetFlag
-
-func init() {
- // TODO(adonovan): provide a comment syntax to allow users to
- // add their functions to this set using facts.
- funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse,context.WithValue,context.WithCancel,context.WithDeadline,context.WithTimeout")
- Analyzer.Flags.Var(&funcs, "funcs",
- "comma-separated list of functions whose results must be used")
-
- stringMethods.Set("Error,String")
- Analyzer.Flags.Var(&stringMethods, "stringmethods",
- "comma-separated list of names of methods of type func() string whose results must be used")
-}
-
-func run(pass *analysis.Pass) (interface{}, error) {
- inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
-
- nodeFilter := []ast.Node{
- (*ast.ExprStmt)(nil),
- }
- inspect.Preorder(nodeFilter, func(n ast.Node) {
- call, ok := analysisutil.Unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr)
- if !ok {
- return // not a call statement
- }
- fun := analysisutil.Unparen(call.Fun)
-
- if pass.TypesInfo.Types[fun].IsType() {
- return // a conversion, not a call
- }
-
- selector, ok := fun.(*ast.SelectorExpr)
- if !ok {
- return // neither a method call nor a qualified ident
- }
-
- sel, ok := pass.TypesInfo.Selections[selector]
- if ok && sel.Kind() == types.MethodVal {
- // method (e.g. foo.String())
- obj := sel.Obj().(*types.Func)
- sig := sel.Type().(*types.Signature)
- if types.Identical(sig, sigNoArgsStringResult) {
- if stringMethods[obj.Name()] {
- pass.Reportf(call.Lparen, "result of (%s).%s call not used",
- sig.Recv().Type(), obj.Name())
- }
- }
- } else if !ok {
- // package-qualified function (e.g. fmt.Errorf)
- obj := pass.TypesInfo.Uses[selector.Sel]
- if obj, ok := obj.(*types.Func); ok {
- qname := obj.Pkg().Path() + "." + obj.Name()
- if funcs[qname] {
- pass.Reportf(call.Lparen, "result of %v call not used", qname)
- }
- }
- }
- })
- return nil, nil
-}
-
-// func() string
-var sigNoArgsStringResult = types.NewSignature(nil, nil,
- types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
- false)
-
-type stringSetFlag map[string]bool
-
-func (ss *stringSetFlag) String() string {
- var items []string
- for item := range *ss {
- items = append(items, item)
- }
- sort.Strings(items)
- return strings.Join(items, ",")
-}
-
-func (ss *stringSetFlag) Set(s string) error {
- m := make(map[string]bool) // clobber previous value
- if s != "" {
- for _, name := range strings.Split(s, ",") {
- if name == "" {
- continue // TODO: report error? proceed?
- }
- m[name] = true
- }
- }
- *ss = m
- return nil
-}