/
ifacenames.go
113 lines (103 loc) · 3.18 KB
/
ifacenames.go
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package ifacenames
import (
"fmt"
"go/ast"
"strings"
"github.com/gostaticanalysis/comment/passes/commentmap"
"github.com/k1LoW/gostyle/config"
"github.com/k1LoW/gostyle/reporter"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
const (
name = "ifacenames"
doc = "Analyzer based on https://go.dev/doc/effective_go#interface-names."
msg = "By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun. (ref: https://go.dev/doc/effective_go#interface-names )"
msgc = "All interface names with the -er suffix are required. (THIS IS NOT IN Effective Go)"
)
var (
disable bool
includeGenerated bool
all bool
)
// Analyzer based on https://go.dev/doc/effective_go#interface-names.
var Analyzer = &analysis.Analyzer{
Name: name,
Doc: doc,
URL: "https://github.com/k1LoW/gostyle/tree/main/analyzer/effective/ifacenames",
Run: run,
Requires: []*analysis.Analyzer{
inspect.Analyzer,
commentmap.Analyzer,
},
}
// AnalyzerWithConfig based on https://go.dev/doc/effective_go#interface-names.
var AnalyzerWithConfig = &analysis.Analyzer{
Name: name,
Doc: doc,
URL: "https://github.com/k1LoW/gostyle/tree/main/analyzer/effective/ifacenames",
Run: run,
Requires: []*analysis.Analyzer{
config.Loader,
inspect.Analyzer,
commentmap.Analyzer,
},
}
func run(pass *analysis.Pass) (any, error) {
c, err := config.Load(pass)
if err != nil {
return nil, err
}
var opts []reporter.Option
if c != nil {
disable = c.IsDisabled(name)
includeGenerated = c.AnalyzersSettings.Ifacenames.IncludeGenerated
all = c.AnalyzersSettings.Ifacenames.All
opts = append(opts, reporter.ExcludeFiles(c.ConfigDir, c.ExcludeFiles))
}
if disable {
return nil, nil
}
i, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
if !ok {
return nil, fmt.Errorf("unexpected result type from inspect: %T", pass.ResultOf[inspect.Analyzer])
}
nodeFilter := []ast.Node{
(*ast.Ident)(nil),
(*ast.InterfaceType)(nil),
}
var ii *ast.Ident
if includeGenerated {
opts = append(opts, reporter.IncludeGenerated())
}
r, err := reporter.New(name, pass, opts...)
if err != nil {
return nil, err
}
i.Preorder(nodeFilter, func(n ast.Node) {
switch n := n.(type) {
case *ast.InterfaceType:
if len(n.Methods.List) == 1 && len(n.Methods.List[0].Names) > 0 {
mn := n.Methods.List[0].Names[0].Name
if !strings.HasPrefix(ii.Name, mn[:len(mn)-1]) || !(strings.HasSuffix(ii.Name, "er") || strings.HasSuffix(ii.Name, "or")) { // huristic
r.Append(n.Pos(), fmt.Sprintf("%s: %s", msg, ii.Name))
return
}
}
if all && !(strings.HasSuffix(ii.Name, "er") || strings.HasSuffix(ii.Name, "or")) {
r.Append(n.Pos(), fmt.Sprintf("%s: %s", msgc, ii.Name))
return
}
case *ast.Ident:
ii = n
}
})
r.Report()
return nil, nil
}
func init() {
Analyzer.Flags.BoolVar(&disable, "disable", false, "disable "+name+" analyzer")
Analyzer.Flags.BoolVar(&includeGenerated, "include-generated", false, "include generated codes")
Analyzer.Flags.BoolVar(&all, "all", false, "all interface names with the -er suffix are required")
}