Skip to content

Commit e339eae

Browse files
committed
add duplicatemarkers linter
Signed-off-by: sivchari <[email protected]>
1 parent 2e78eb0 commit e339eae

File tree

11 files changed

+390
-0
lines changed

11 files changed

+390
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ The `commentstart` linter can automatically fix comments that do not start with
163163

164164
When the `json` tag is present, and matches the first word of the field comment in all but casing, the linter will suggest that the comment be updated to match the `json` tag.
165165

166+
## DuplicateMarkers
167+
168+
The `duplicatemarkers` linter checks that all markers in the API types which have duplicated markers related with field and type.
169+
170+
### Fixes
171+
172+
The `duplicatemarkers` linter can automatically fix markers that are duplicated.
173+
174+
When the marker is duplicated, the linter will suggest to remove the duplicate marker.
175+
166176
## Integers
167177

168178
The `integers` linter checks for usage of unsupported integer types.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package duplicatemarkers
17+
18+
import (
19+
"fmt"
20+
"go/ast"
21+
22+
"golang.org/x/tools/go/analysis"
23+
24+
kalerrors "sigs.k8s.io/kube-api-linter/pkg/analysis/errors"
25+
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/extractjsontags"
26+
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/inspector"
27+
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/markers"
28+
)
29+
30+
const (
31+
name = "duplicatemarkers"
32+
)
33+
34+
// Analyzer is the analyzer for the duplicatemarkers package.
35+
// It checks for duplicate markers on struct fields.
36+
var Analyzer = &analysis.Analyzer{
37+
Name: name,
38+
Doc: "Check for duplicate markers on struct fields.",
39+
Run: run,
40+
Requires: []*analysis.Analyzer{inspector.Analyzer},
41+
}
42+
43+
func run(pass *analysis.Pass) (any, error) {
44+
inspect, ok := pass.ResultOf[inspector.Analyzer].(inspector.Inspector)
45+
if !ok {
46+
return nil, kalerrors.ErrCouldNotGetInspector
47+
}
48+
49+
inspect.InspectFields(func(field *ast.Field, _ []ast.Node, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
50+
if len(field.Names) == 0 {
51+
return
52+
}
53+
checkField(pass, field, markersAccess)
54+
})
55+
56+
return nil, nil //nolint:nilnil
57+
}
58+
59+
func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markers.Markers) {
60+
set := markersAccess.FieldMarkers(field)
61+
62+
fieldName := field.Names[0].Name
63+
64+
for _, marker := range set.UnsortedList() {
65+
// TODO: Add check whether the marker is a duuplicate or not.
66+
pass.Report(analysis.Diagnostic{
67+
Pos: field.Pos(),
68+
Message: fmt.Sprintf("%s has duplicated markers %s", fieldName, marker.String()),
69+
SuggestedFixes: []analysis.SuggestedFix{
70+
{
71+
Message: fmt.Sprintf("should remove `// +%s`", marker.String()),
72+
TextEdits: []analysis.TextEdit{
73+
{
74+
Pos: marker.Pos,
75+
End: marker.End,
76+
NewText: nil,
77+
},
78+
},
79+
},
80+
},
81+
})
82+
}
83+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package duplicatemarkers_test
17+
18+
import (
19+
"testing"
20+
21+
"golang.org/x/tools/go/analysis/analysistest"
22+
"sigs.k8s.io/kube-api-linter/pkg/analysis/duplicatemarkers"
23+
)
24+
25+
func Test(t *testing.T) {
26+
testdata := analysistest.TestData()
27+
analysistest.RunWithSuggestedFixes(t, testdata, duplicatemarkers.Analyzer, "a")
28+
}

pkg/analysis/duplicatemarkers/doc.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/*
18+
duplicatemarkers is an analyzer that checks for duplicated markers in the code.
19+
*/
20+
package duplicatemarkers
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package duplicatemarkers
17+
18+
import (
19+
"golang.org/x/tools/go/analysis"
20+
"sigs.k8s.io/kube-api-linter/pkg/config"
21+
)
22+
23+
// Initializer returns the AnalyzerInitializer for this
24+
// Analyzer so that it can be added to the registry.
25+
func Initializer() initializer {
26+
return initializer{}
27+
}
28+
29+
// intializer implements the AnalyzerInitializer interface.
30+
type initializer struct{}
31+
32+
// Name returns the name of the Analyzer.
33+
func (initializer) Name() string {
34+
return name
35+
}
36+
37+
// Init returns the intialized Analyzer.
38+
func (initializer) Init(cfg config.LintersConfig) (*analysis.Analyzer, error) {
39+
return Analyzer, nil
40+
}
41+
42+
// Default determines whether this Analyzer is on by default, or not.
43+
func (initializer) Default() bool {
44+
// Duplicated markers are a sign of bad code, and should be avoided.
45+
// This is a good rule to have on by default.
46+
return true
47+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package a
2+
3+
// It must be ignored since it is not a type
4+
// +kubebuilder:validation:Enum=foo;bar;baz
5+
var Variable string
6+
7+
// +kubebuilder:validation:Enum=foo;bar;baz
8+
// +kubebuilder:validation:Enum=foo;bar;baz
9+
type Enum string // want "Enum has duplicated markers kubebuilder:validation:Enum"
10+
11+
// +kubebuilder:validation:MaxLength=10
12+
// +kubebuilder:validation:MaxLength=11
13+
type MaxLength int // want "MaxLength has duplicated markers kubebuilder:validation:MaxLength"
14+
15+
// +kubebuilder:object:root=true
16+
// +kubebuilder:subresource:status
17+
// +kubebuilder:object:root=true
18+
type DuplicateMarkerSpec struct { // want "DuplicateMarkerSpec has duplicated markers kubebuilder:object:root"
19+
// +kubebuilder:validation:Required
20+
// shpuld be ignored since it only has single marker
21+
UniqueRequired string `json:"uniqueRequired"`
22+
23+
// +listType=map
24+
// +listMapKey=primaryKey
25+
// +listMapKey=secondaryKey
26+
// +required
27+
// should be ignored since listMapKey is allowed to have different values
28+
Map Map `json:"map"`
29+
30+
// +optional
31+
// +kubebuilder:validation:XValidation:rule="self >= 1 && self <= 3",message="must be 1 to 5"
32+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="replicas must be immutable"
33+
// should be ignored since XValidation is allowed to have different values
34+
Replicas *int `json:"replicas"`
35+
36+
// +kubebuilder:validation:Required
37+
// +kubebuilder:validation:Required
38+
DuplicatedRequired string `json:"duplicatedRequired"` // want "DuplicatedRequired has duplicated markers kubebuilder:validation:Required"
39+
40+
// +kubebuilder:validation:Enum=foo;bar;baz
41+
// +kubebuilder:validation:Enum=foo;bar;baz
42+
DuplicatedEnum string `json:"duplicatedEnum"` // want "DuplicatedEnum has duplicated markers kubebuilder:validation:Enum"
43+
44+
// +kubebuilder:validation:MaxLength=11
45+
// +kubebuilder:validation:MaxLength=10
46+
DuplicatedMaxlength int `json:"maxlength"` // want "DuplicatedMaxlength has duplicated markers kubebuilder:validation:MaxLength"
47+
48+
// +listType=map
49+
// +listMapKey=primaryKey
50+
// +listMapKey=secondaryKey
51+
// +listType=map
52+
// +required
53+
DuplicatedListTypeMap Map `json:"duplicatedListTypeMap"` // want "DuplicatedListTypeMap has duplicated markers listType=map, listMapKey=primaryKey, listMapKey=secondaryKey"
54+
55+
// +optional
56+
// +kubebuilder:validation:XValidation:rule="self >= 1 && self <= 3",message="must be 1 to 5"
57+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="replicas must be immutable"
58+
// +kubebuilder:validation:XValidation:rule="self >= 1 && self <= 3",message="must be 1 to 5"
59+
DuplicatedReplicas *int `json:"duplicatedReplicas"` // want "DuplicatedReplicas has duplicated markers kubebuilder:validation:XValidation:rule=\"self >= 1 && self <= 3\",message=\"must be 1 to 5\""
60+
}
61+
62+
type Map struct {
63+
// +required
64+
PrimaryKey string `json:"primaryKey"`
65+
// +required
66+
SecondaryKey string `json:"secondaryKey"`
67+
// +required
68+
Value string `json:"value"`
69+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package a
2+
3+
// +optional
4+
// +kubebuilder:validation:Enum=foo;bar;baz
5+
type Enum string // want "Enum has duplicated markers kubebuilder:validation:Enum"
6+
7+
// It must be ignored since it is not a type
8+
// +required
9+
var Variable string
10+
11+
// +kubebuilder:subresource:status
12+
// +kubebuilder:object:root=true
13+
type DuplicateMarkerSpec struct { // want "DuplicateMarkerSpec has duplicated markers kubebuilder:object:root"
14+
// +kubebuilder:validation:Required
15+
Foo string `json:"foo"`
16+
17+
// +kubebuilder:validation:Required
18+
// +kubebuilder:validation:MaxLength=10
19+
DuplicatedFoo string `json:"duplicatedFoo"` // want "DuplicatedFoo has duplicated markers kubebuilder:validation:Required"
20+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module a
2+
3+
go 1.25
4+
5+
require k8s.io/apimachinery v0.32.3
6+
7+
require (
8+
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
9+
github.com/go-logr/logr v1.4.2 // indirect
10+
github.com/gogo/protobuf v1.3.2 // indirect
11+
github.com/google/gofuzz v1.2.0 // indirect
12+
github.com/json-iterator/go v1.1.12 // indirect
13+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
14+
github.com/modern-go/reflect2 v1.0.2 // indirect
15+
github.com/x448/float16 v0.8.4 // indirect
16+
golang.org/x/net v0.30.0 // indirect
17+
golang.org/x/text v0.19.0 // indirect
18+
gopkg.in/inf.v0 v0.9.1 // indirect
19+
k8s.io/klog/v2 v2.130.1 // indirect
20+
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
21+
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
22+
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
23+
sigs.k8s.io/yaml v1.4.0 // indirect
24+
)

0 commit comments

Comments
 (0)