-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpass.go
More file actions
93 lines (83 loc) · 2.7 KB
/
Copy pathpass.go
File metadata and controls
93 lines (83 loc) · 2.7 KB
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
package goago
import (
"go/ast"
"reflect"
"golang.org/x/tools/go/analysis"
)
// reflectTypeOfIgnoreIndex is the ResultType of ignoresAnalyzer.
var reflectTypeOfIgnoreIndex = reflect.TypeOf((*ignoreIndex)(nil))
// newAnalyzer builds the analyzer for a rule. Every rule analyzer requires
// ignoresAnalyzer, so one place enforces suppression rather than each rule
// re-implementing it.
func newAnalyzer(name, doc string, run func(*checkPass)) *analysis.Analyzer {
a := &analysis.Analyzer{
Name: identName(name),
Doc: doc,
URL: "https://github.com/agentstation/goago/blob/main/docs/rules.md#" + name,
Requires: []*analysis.Analyzer{ignoresAnalyzer},
}
a.Run = func(pass *analysis.Pass) (any, error) {
ix, _ := pass.ResultOf[ignoresAnalyzer].(*ignoreIndex)
run(&checkPass{Pass: pass, rule: name, ignores: ix})
return nil, nil
}
return a
}
// A checkPass is the analysis pass a rule sees. It narrows [analysis.Pass] to
// the reporting path that honours //goago:ignore, so that a rule cannot report
// around suppression by accident.
type checkPass struct {
*analysis.Pass
rule string
ignores *ignoreIndex
}
// reportf records a violation at n unless an //goago:ignore directive covers
// that line for this rule.
func (c *checkPass) reportf(n ast.Node, format string, args ...any) {
if c.ignores.suppressed(c.Fset, n.Pos(), c.rule) {
return
}
// Reported through the embedded Pass explicitly: this is the single place
// allowed to bypass reportf, and naming it keeps that visible.
c.Pass.Report(analysis.Diagnostic{ //nolint:staticcheck // QF1008: the qualifier is deliberate
Pos: n.Pos(),
End: n.End(),
Category: c.rule,
Message: sprintf(format, args...),
})
}
// forEachFuncDecl calls fn for every top-level function and method
// declaration in the package under analysis.
func (c *checkPass) forEachFuncDecl(fn func(*ast.FuncDecl)) {
for _, f := range c.Files {
for _, d := range f.Decls {
if fd, ok := d.(*ast.FuncDecl); ok {
fn(fd)
}
}
}
}
// forEachNode calls fn for every node in the package under analysis. Returning
// false from fn skips the node's children, matching [ast.Inspect].
func (c *checkPass) forEachNode(fn func(ast.Node) bool) {
for _, f := range c.Files {
ast.Inspect(f, fn)
}
}
// namedResults reports whether a signature declares named result parameters,
// which is the precondition for a naked return.
func namedResults(t *ast.FuncType) bool {
if t.Results == nil {
return false
}
for _, f := range t.Results.List {
if len(f.Names) > 0 {
return true
}
}
return false
}
// hasTypeParams reports whether a type parameter list declares anything.
func hasTypeParams(tp *ast.FieldList) bool {
return tp != nil && len(tp.List) > 0
}