Skip to content

Commit 36e90ff

Browse files
committed
Integrate inspector helper into linters
1 parent 97eda8f commit 36e90ff

File tree

13 files changed

+88
-212
lines changed

13 files changed

+88
-212
lines changed

pkg/analysis/commentstart/analyzer.go

Lines changed: 15 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ import (
88
"strings"
99

1010
"github.com/JoelSpeed/kal/pkg/analysis/helpers/extractjsontags"
11+
"github.com/JoelSpeed/kal/pkg/analysis/helpers/inspector"
12+
"github.com/JoelSpeed/kal/pkg/analysis/helpers/markers"
1113
"golang.org/x/tools/go/analysis"
12-
"golang.org/x/tools/go/analysis/passes/inspect"
13-
"golang.org/x/tools/go/ast/inspector"
1414
)
1515

1616
const name = "commentstart"
1717

1818
var (
1919
errCouldNotGetInspector = errors.New("could not get inspector")
20-
errCouldNotGetJSONTags = errors.New("could not get json tags")
2120
)
2221

2322
// Analyzer is the analyzer for the commentstart package.
@@ -26,79 +25,37 @@ var Analyzer = &analysis.Analyzer{
2625
Name: name,
2726
Doc: "Check that all struct fields in an API have a godoc, and that the godoc starts with the serialised field name",
2827
Run: run,
29-
Requires: []*analysis.Analyzer{inspect.Analyzer, extractjsontags.Analyzer},
28+
Requires: []*analysis.Analyzer{inspector.Analyzer},
3029
}
3130

3231
func run(pass *analysis.Pass) (interface{}, error) {
33-
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
32+
inspect, ok := pass.ResultOf[inspector.Analyzer].(inspector.Inspector)
3433
if !ok {
3534
return nil, errCouldNotGetInspector
3635
}
3736

38-
jsonTags, ok := pass.ResultOf[extractjsontags.Analyzer].(extractjsontags.StructFieldTags)
39-
if !ok {
40-
return nil, errCouldNotGetJSONTags
41-
}
42-
43-
// Filter to structs so that we can iterate over fields in a struct.
44-
nodeFilter := []ast.Node{
45-
(*ast.Field)(nil),
46-
}
47-
48-
inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) (proceed bool) {
49-
if !push {
50-
return false
51-
}
52-
53-
if len(stack) < 2 {
54-
return true
55-
}
56-
57-
// The 0th node in the stack is the *ast.File.
58-
// The 1st node in the stack is the *ast.GenDecl.
59-
decl, ok := stack[1].(*ast.GenDecl)
60-
if !ok {
61-
return false
62-
}
63-
64-
if decl.Tok != token.TYPE {
65-
// Returning false here means we won't inspect non-type declarations (e.g. var, const, import).
66-
return false
67-
}
68-
69-
field, ok := n.(*ast.Field)
70-
if !ok {
71-
return true
72-
}
73-
74-
return checkField(pass, field, jsonTags)
37+
inspect.InspectFields(func(field *ast.Field, stack []ast.Node, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
38+
checkField(pass, field, jsonTagInfo)
7539
})
7640

7741
return nil, nil //nolint:nilnil
7842
}
7943

80-
func checkField(pass *analysis.Pass, field *ast.Field, jsonTags extractjsontags.StructFieldTags) (proceed bool) {
81-
if field == nil || len(field.Names) == 0 {
82-
// Returning false here means we don't inspect inline fields.
83-
// Types of inline fields will be inspected on its declaration.
84-
return false
85-
}
86-
87-
tagInfo := jsonTags.FieldTags(field)
88-
if tagInfo.Ignored {
89-
// Returning false here means we won't inspect the children of an ignored field.
90-
return false
91-
}
92-
44+
func checkField(pass *analysis.Pass, field *ast.Field, tagInfo extractjsontags.FieldTagInfo) {
9345
if tagInfo.Name == "" {
94-
return true
46+
return
9547
}
9648

97-
fieldName := field.Names[0].Name
49+
var fieldName string
50+
if len(field.Names) > 0 {
51+
fieldName = field.Names[0].Name
52+
} else if ident, ok := field.Type.(*ast.Ident); ok {
53+
fieldName = ident.Name
54+
}
9855

9956
if field.Doc == nil {
10057
pass.Reportf(field.Pos(), "field %s is missing godoc comment", fieldName)
101-
return true
58+
return
10259
}
10360

10461
firstLine := field.Doc.List[0]
@@ -125,6 +82,4 @@ func checkField(pass *analysis.Pass, field *ast.Field, jsonTags extractjsontags.
12582
pass.Reportf(field.Doc.List[0].Pos(), "godoc for field %s should start with '%s ...'", fieldName, tagInfo.Name)
12683
}
12784
}
128-
129-
return true
13085
}

pkg/analysis/commentstart/testdata/src/a/a.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type CommentStartTestStruct struct {
2222

2323
StructForInlineField `json:",inline"`
2424

25+
A `json:"a"` // want "field A is missing godoc comment"
26+
2527
// IncorrectStartComment is a field with an incorrect start to the comment. // want "godoc for field IncorrectStartComment should start with 'incorrectStartComment ...'"
2628
IncorrectStartComment string `json:"incorrectStartComment"`
2729

@@ -53,6 +55,10 @@ type StructForInlineField struct {
5355
NoComment string `json:"noComment"` // want "field NoComment is missing godoc comment"
5456
}
5557

58+
type A struct {
59+
NoComment string `json:"noComment"` // want "field NoComment is missing godoc comment"
60+
}
61+
5662
type unexportedStruct struct {
5763
NoComment string `json:"noComment"` // want "field NoComment is missing godoc comment"
5864
}
@@ -71,3 +77,7 @@ func FunctionWithStructs() {
7177
NoComment string `json:"noComment"`
7278
}
7379
}
80+
81+
type Interface interface {
82+
InaccessibleFunction() string
83+
}

pkg/analysis/commentstart/testdata/src/a/a.go.golden

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type CommentStartTestStruct struct {
2222

2323
StructForInlineField `json:",inline"`
2424

25+
A `json:"a"` // want "field A is missing godoc comment"
26+
2527
// incorrectStartComment is a field with an incorrect start to the comment. // want "godoc for field IncorrectStartComment should start with 'incorrectStartComment ...'"
2628
IncorrectStartComment string `json:"incorrectStartComment"`
2729

@@ -53,6 +55,10 @@ type StructForInlineField struct {
5355
NoComment string `json:"noComment"` // want "field NoComment is missing godoc comment"
5456
}
5557

58+
type A struct {
59+
NoComment string `json:"noComment"` // want "field NoComment is missing godoc comment"
60+
}
61+
5662
type unexportedStruct struct {
5763
NoComment string `json:"noComment"` // want "field NoComment is missing godoc comment"
5864
}
@@ -71,3 +77,7 @@ func FunctionWithStructs() {
7177
NoComment string `json:"noComment"`
7278
}
7379
}
80+
81+
type Interface interface {
82+
InaccessibleFunction() string
83+
}

pkg/analysis/jsontags/analyzer.go

Lines changed: 10 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import (
44
"errors"
55
"fmt"
66
"go/ast"
7-
"go/token"
87
"regexp"
98

109
"github.com/JoelSpeed/kal/pkg/analysis/helpers/extractjsontags"
10+
"github.com/JoelSpeed/kal/pkg/analysis/helpers/inspector"
11+
"github.com/JoelSpeed/kal/pkg/analysis/helpers/markers"
1112
"github.com/JoelSpeed/kal/pkg/config"
1213

1314
"golang.org/x/tools/go/analysis"
14-
"golang.org/x/tools/go/analysis/passes/inspect"
15-
"golang.org/x/tools/go/ast/inspector"
1615
)
1716

1817
const (
@@ -24,7 +23,6 @@ const (
2423

2524
var (
2625
errCouldNotGetInspector = errors.New("could not get inspector")
27-
errCouldNotGetJSONTags = errors.New("could not get json tags")
2826
)
2927

3028
type analyzer struct {
@@ -48,61 +46,24 @@ func newAnalyzer(cfg config.JSONTagsConfig) (*analysis.Analyzer, error) {
4846
Name: name,
4947
Doc: "Check that all struct fields in an API are tagged with json tags",
5048
Run: a.run,
51-
Requires: []*analysis.Analyzer{inspect.Analyzer, extractjsontags.Analyzer},
49+
Requires: []*analysis.Analyzer{inspector.Analyzer},
5250
}, nil
5351
}
5452

5553
func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) {
56-
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
54+
inspect, ok := pass.ResultOf[inspector.Analyzer].(inspector.Inspector)
5755
if !ok {
5856
return nil, errCouldNotGetInspector
5957
}
6058

61-
jsonTags, ok := pass.ResultOf[extractjsontags.Analyzer].(extractjsontags.StructFieldTags)
62-
if !ok {
63-
return nil, errCouldNotGetJSONTags
64-
}
65-
66-
// Filter to fields so that we can iterate over fields in a struct.
67-
nodeFilter := []ast.Node{
68-
(*ast.Field)(nil),
69-
}
70-
71-
inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) (proceed bool) {
72-
if !push {
73-
return false
74-
}
75-
76-
if len(stack) < 2 {
77-
return true
78-
}
79-
80-
// The 0th node in the stack is the *ast.File.
81-
// The 1st node in the stack is the *ast.GenDecl.
82-
decl, ok := stack[1].(*ast.GenDecl)
83-
if !ok {
84-
return false
85-
}
86-
87-
if decl.Tok != token.TYPE {
88-
// Returning false here means we won't inspect non-type declarations (e.g. var, const, import).
89-
return false
90-
}
91-
92-
field, ok := n.(*ast.Field)
93-
if !ok {
94-
return true
95-
}
96-
97-
return a.checkField(pass, field, jsonTags)
59+
inspect.InspectFields(func(field *ast.Field, stack []ast.Node, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
60+
a.checkField(pass, field, jsonTagInfo)
9861
})
9962

10063
return nil, nil //nolint:nilnil
10164
}
10265

103-
func (a *analyzer) checkField(pass *analysis.Pass, field *ast.Field, jsonTags extractjsontags.StructFieldTags) (proceed bool) {
104-
tagInfo := jsonTags.FieldTags(field)
105-
66+
func (a *analyzer) checkField(pass *analysis.Pass, field *ast.Field, tagInfo extractjsontags.FieldTagInfo) {
10667
var prefix string
10768
if len(field.Names) > 0 && field.Names[0] != nil {
10869
prefix = fmt.Sprintf("field %s", field.Names[0].Name)
@@ -112,29 +73,22 @@ func (a *analyzer) checkField(pass *analysis.Pass, field *ast.Field, jsonTags ex
11273

11374
if tagInfo.Missing {
11475
pass.Reportf(field.Pos(), "%s is missing json tag", prefix)
115-
return true
76+
return
11677
}
11778

11879
if tagInfo.Inline {
119-
return true
120-
}
121-
122-
if tagInfo.Ignored {
123-
// Returning false here means we won't inspect the children of an ignored field.
124-
return false
80+
return
12581
}
12682

12783
if tagInfo.Name == "" {
12884
pass.Reportf(field.Pos(), "%s has empty json tag", prefix)
129-
return true
85+
return
13086
}
13187

13288
matched := a.jsonTagRegex.Match([]byte(tagInfo.Name))
13389
if !matched {
13490
pass.Reportf(field.Pos(), "%s json tag does not match pattern %q: %s", prefix, a.jsonTagRegex.String(), tagInfo.Name)
13591
}
136-
137-
return true
13892
}
13993

14094
func defaultConfig(cfg *config.JSONTagsConfig) {

pkg/analysis/jsontags/testdata/src/a/a.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,7 @@ type C struct{}
4646
type D struct{}
4747

4848
type E struct{}
49+
50+
type Interface interface {
51+
InaccessibleFunction() string
52+
}

pkg/analysis/maxlength/analyzer.go

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import (
55
"fmt"
66
"go/ast"
77

8+
"github.com/JoelSpeed/kal/pkg/analysis/helpers/extractjsontags"
9+
"github.com/JoelSpeed/kal/pkg/analysis/helpers/inspector"
810
"github.com/JoelSpeed/kal/pkg/analysis/helpers/markers"
911
"golang.org/x/tools/go/analysis"
10-
"golang.org/x/tools/go/analysis/passes/inspect"
11-
"golang.org/x/tools/go/ast/inspector"
1212
)
1313

1414
const (
@@ -27,7 +27,6 @@ const (
2727

2828
var (
2929
errCouldNotGetInspector = errors.New("could not get inspector")
30-
errCouldNotGetMarkers = errors.New("could not get markers")
3130
)
3231

3332
// Analyzer is the analyzer for the maxlength package.
@@ -36,38 +35,17 @@ var Analyzer = &analysis.Analyzer{
3635
Name: name,
3736
Doc: "Checks that all strings formatted fields are marked with a maximum length, and that arrays are marked with max items.",
3837
Run: run,
39-
Requires: []*analysis.Analyzer{inspect.Analyzer, markers.Analyzer},
38+
Requires: []*analysis.Analyzer{inspector.Analyzer},
4039
}
4140

4241
func run(pass *analysis.Pass) (interface{}, error) {
43-
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
42+
inspect, ok := pass.ResultOf[inspector.Analyzer].(inspector.Inspector)
4443
if !ok {
4544
return nil, errCouldNotGetInspector
4645
}
4746

48-
markersAccess, ok := pass.ResultOf[markers.Analyzer].(markers.Markers)
49-
if !ok {
50-
return nil, errCouldNotGetMarkers
51-
}
52-
53-
// Filter to structs so that we can iterate over the fields in a struct.
54-
nodeFilter := []ast.Node{
55-
(*ast.StructType)(nil),
56-
}
57-
58-
inspect.Preorder(nodeFilter, func(n ast.Node) {
59-
sTyp, ok := n.(*ast.StructType)
60-
if !ok {
61-
return
62-
}
63-
64-
if sTyp.Fields == nil {
65-
return
66-
}
67-
68-
for _, field := range sTyp.Fields.List {
69-
checkField(pass, field, markersAccess)
70-
}
47+
inspect.InspectFields(func(field *ast.Field, stack []ast.Node, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
48+
checkField(pass, field, markersAccess)
7149
})
7250

7351
return nil, nil //nolint:nilnil

pkg/analysis/maxlength/testdata/src/a/a.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ type MaxLength struct {
7272
StringAliasArrayWithMaxItemsAndMaxElementLengthOnAlias []StringAliasWithMaxLength
7373

7474
StringAliasArrayWithoutMaxItemsWithMaxElementLengthOnAlias []StringAliasWithMaxLength // want "field StringAliasArrayWithoutMaxItemsWithMaxElementLengthOnAlias must have a maximum items, add kubebuilder:validation:MaxItems"
75+
76+
Struct struct {
77+
// +kubebuilder:validation:MaxLength:=256
78+
StringWithMaxLength string
79+
80+
StringWithoutMaxLength string // want "field StringWithoutMaxLength must have a maximum length, add kubebuilder:validation:MaxLength marker"
81+
} `json:"struct"`
7582
}
7683

7784
// StringAlias is a string without a MaxLength.

0 commit comments

Comments
 (0)