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
8package main
9
10import (
11	"flag"
12	"go/ast"
13	"go/token"
14	"go/types"
15	"strings"
16)
17
18var 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
22var 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
26func 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
34var sigNoArgsStringResult = types.NewSignature(nil, nil,
35	types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
36	false)
37
38var unusedFuncs = make(map[string]bool)
39var unusedStringMethods = make(map[string]bool)
40
41func 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
56func 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}
94