aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/macabu/inamedparam/inamedparam.go
blob: 8ba7fe1882b35030b847ade083d6b1eb5195672c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package inamedparam

import (
	"flag"
	"go/ast"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/inspect"
	"golang.org/x/tools/go/ast/inspector"
)

const (
	analyzerName = "inamedparam"

	flagSkipSingleParam = "skip-single-param"
)

var Analyzer = &analysis.Analyzer{
	Name:  analyzerName,
	Doc:   "reports interfaces with unnamed method parameters",
	Run:   run,
	Flags: flags(),
	Requires: []*analysis.Analyzer{
		inspect.Analyzer,
	},
}

func flags() flag.FlagSet {
	flags := flag.NewFlagSet(analyzerName, flag.ExitOnError)

	flags.Bool(flagSkipSingleParam, false, "skip interface methods with a single unnamed parameter")

	return *flags
}

func run(pass *analysis.Pass) (interface{}, error) {
	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

	types := []ast.Node{
		&ast.InterfaceType{},
	}

	skipSingleParam := pass.Analyzer.Flags.Lookup(flagSkipSingleParam).Value.(flag.Getter).Get().(bool)

	inspect.Preorder(types, func(n ast.Node) {
		interfaceType, ok := n.(*ast.InterfaceType)
		if !ok || interfaceType == nil || interfaceType.Methods == nil {
			return
		}

		for _, method := range interfaceType.Methods.List {
			interfaceFunc, ok := method.Type.(*ast.FuncType)
			if !ok || interfaceFunc == nil || interfaceFunc.Params == nil {
				continue
			}

			// Improvement: add test case to reproduce this. Help wanted.
			if len(method.Names) == 0 {
				continue
			}

			methodName := method.Names[0].Name

			if skipSingleParam && len(interfaceFunc.Params.List) == 1 {
				continue
			}

			for _, param := range interfaceFunc.Params.List {
				if param.Names == nil {
					var builtParamType string

					switch paramType := param.Type.(type) {
					case *ast.SelectorExpr:
						if ident := paramType.X.(*ast.Ident); ident != nil {
							builtParamType += ident.Name + "."
						}

						builtParamType += paramType.Sel.Name
					case *ast.Ident:
						builtParamType = paramType.Name
					}

					if builtParamType != "" {
						pass.Reportf(param.Pos(), "interface method %v must have named param for type %v", methodName, builtParamType)
					} else {
						pass.Reportf(param.Pos(), "interface method %v must have all named params", methodName)
					}
				}
			}
		}
	})

	return nil, nil
}