Skip to content

Commit 013a77c

Browse files
committed
notimestamp: wrap namingconventions linter
Signed-off-by: Bryce Palmer <bpalmer@redhat.com>
1 parent 5180baf commit 013a77c

File tree

5 files changed

+54
-100
lines changed

5 files changed

+54
-100
lines changed

pkg/analysis/notimestamp/analyzer.go

Lines changed: 33 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -13,100 +13,55 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
16-
1716
package notimestamp
1817

1918
import (
19+
"errors"
2020
"fmt"
21-
"go/ast"
22-
"go/token"
23-
"regexp"
24-
"strings"
2521

2622
"golang.org/x/tools/go/analysis"
27-
kalerrors "sigs.k8s.io/kube-api-linter/pkg/analysis/errors"
28-
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/extractjsontags"
29-
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/inspector"
30-
markershelper "sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/markers"
31-
"sigs.k8s.io/kube-api-linter/pkg/analysis/utils"
23+
"k8s.io/apimachinery/pkg/util/validation/field"
24+
"sigs.k8s.io/kube-api-linter/pkg/analysis/initializer"
25+
"sigs.k8s.io/kube-api-linter/pkg/analysis/namingconventions"
3226
)
3327

34-
const name = "notimestamp"
35-
36-
// Analyzer is the analyzer for the notimestamp package.
37-
// It checks that no struct fields named 'timestamp', or that contain timestamp as a
38-
// substring are present.
39-
var Analyzer = &analysis.Analyzer{
40-
Name: name,
41-
Doc: "Suggest the usage of the term 'time' over 'timestamp'",
42-
Run: run,
43-
Requires: []*analysis.Analyzer{inspector.Analyzer},
44-
}
45-
46-
// case-insensitive regular expression to match 'timestamp' string in field or json tag.
47-
var timeStampRegEx = regexp.MustCompile("(?i)timestamp")
28+
const (
29+
name = "notimestamp"
30+
doc = "Suggest the usage of the term 'time' over 'timestamp'"
31+
)
4832

49-
func run(pass *analysis.Pass) (any, error) {
50-
inspect, ok := pass.ResultOf[inspector.Analyzer].(inspector.Inspector)
51-
if !ok {
52-
return nil, kalerrors.ErrCouldNotGetInspector
33+
var errUnexpectedInitializerType = errors.New("expected namingconventions.Initializer() to be of type initializer.ConfigurableAnalyzerInitializer, but was not")
34+
35+
func newAnalyzer() *analysis.Analyzer {
36+
cfg := &namingconventions.Config{
37+
Conventions: []namingconventions.Convention{
38+
{
39+
Name: "notimestamp",
40+
ViolationMatcher: "(?i)timestamp",
41+
Operation: namingconventions.OperationReplace,
42+
Message: "prefer use of the term 'time' over 'timestamp'",
43+
Replace: "Time",
44+
},
45+
},
5346
}
5447

55-
inspect.InspectFields(func(field *ast.Field, stack []ast.Node, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markershelper.Markers) {
56-
checkFieldsAndTags(pass, field, jsonTagInfo)
57-
})
58-
59-
return nil, nil //nolint:nilnil
60-
}
61-
62-
func checkFieldsAndTags(pass *analysis.Pass, field *ast.Field, tagInfo extractjsontags.FieldTagInfo) {
63-
fieldName := utils.FieldName(field)
64-
if fieldName == "" {
65-
return
48+
configInit, ok := namingconventions.Initializer().(initializer.ConfigurableAnalyzerInitializer)
49+
if !ok {
50+
panic(fmt.Errorf("getting initializer: %w", errUnexpectedInitializerType))
6651
}
6752

68-
var suggestedFixes []analysis.SuggestedFix
69-
70-
// check if filed name contains timestamp in it.
71-
fieldReplacementName := timeStampRegEx.ReplaceAllString(fieldName, "Time")
72-
if fieldReplacementName != fieldName {
73-
suggestedFixes = append(suggestedFixes, analysis.SuggestedFix{
74-
Message: fmt.Sprintf("replace %s with %s", fieldName, fieldReplacementName),
75-
TextEdits: []analysis.TextEdit{
76-
{
77-
Pos: field.Pos(),
78-
NewText: []byte(fieldReplacementName),
79-
End: field.Pos() + token.Pos(len(fieldName)),
80-
},
81-
},
82-
})
53+
errs := configInit.ValidateConfig(cfg, field.NewPath("notimestamp"))
54+
if err := errs.ToAggregate(); err != nil {
55+
panic(fmt.Errorf("notimestamp linter has an invalid namingconventions configuration: %w", err))
8356
}
8457

85-
// check if the tag contains timestamp in it.
86-
tagReplacementName := timeStampRegEx.ReplaceAllString(tagInfo.Name, "Time")
87-
if strings.HasPrefix(strings.ToLower(tagInfo.Name), "time") {
88-
// If the tag starts with 'timeStamp', the replacement should be 'time' not 'Time'.
89-
tagReplacementName = timeStampRegEx.ReplaceAllString(tagInfo.Name, "time")
58+
analyzer, err := configInit.Init(cfg)
59+
if err != nil {
60+
panic(fmt.Errorf("notimestamp linter encountered an error initializing wrapped namingconventions analyzer: %w", err))
9061
}
9162

92-
if tagReplacementName != tagInfo.Name {
93-
suggestedFixes = append(suggestedFixes, analysis.SuggestedFix{
94-
Message: fmt.Sprintf("replace %s json tag with %s", tagInfo.Name, tagReplacementName),
95-
TextEdits: []analysis.TextEdit{
96-
{
97-
Pos: tagInfo.Pos,
98-
NewText: []byte(tagReplacementName),
99-
End: tagInfo.Pos + token.Pos(len(tagInfo.Name)),
100-
},
101-
},
102-
})
103-
}
63+
analyzer.Name = name
64+
analyzer.Doc = doc
10465

105-
if len(suggestedFixes) > 0 {
106-
pass.Report(analysis.Diagnostic{
107-
Pos: field.Pos(),
108-
Message: fmt.Sprintf("field %s: prefer use of the term time over timestamp", fieldName),
109-
SuggestedFixes: suggestedFixes,
110-
})
111-
}
66+
return analyzer
11267
}

pkg/analysis/notimestamp/analyzer_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,15 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package notimestamp_test
17+
package notimestamp
1818

1919
import (
2020
"testing"
2121

2222
"golang.org/x/tools/go/analysis/analysistest"
23-
"sigs.k8s.io/kube-api-linter/pkg/analysis/notimestamp"
2423
)
2524

2625
func Test(t *testing.T) {
2726
testdata := analysistest.TestData()
28-
analysistest.RunWithSuggestedFixes(t, testdata, notimestamp.Analyzer, "a")
27+
analysistest.RunWithSuggestedFixes(t, testdata, newAnalyzer(), "a")
2928
}

pkg/analysis/notimestamp/initializer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func init() {
3030
func Initializer() initializer.AnalyzerInitializer {
3131
return initializer.NewInitializer(
3232
name,
33-
Analyzer,
33+
newAnalyzer(),
3434
true,
3535
)
3636
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,36 @@ import (
88

99
type NoTimeStampTestStruct struct {
1010
// +optional
11-
TimeStamp *time.Time `json:"timeStamp,omitempty"` // want "field TimeStamp: prefer use of the term time over timestamp"
11+
TimeStamp *time.Time `json:"timeStamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
1212

1313
// +optional
14-
Timestamp *time.Time `json:"timestamp,omitempty"` // want "field Timestamp: prefer use of the term time over timestamp"
14+
Timestamp *time.Time `json:"timestamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
1515

1616
// +optional
17-
FooTimeStamp *time.Time `json:"fooTimeStamp,omitempty"` // want "field FooTimeStamp: prefer use of the term time over timestamp"
17+
FooTimeStamp *time.Time `json:"fooTimeStamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
1818

1919
// +optional
20-
FootimeStamp *time.Time `json:"footimeStamp,omitempty"` // want "field FootimeStamp: prefer use of the term time over timestamp"
20+
FootimeStamp *time.Time `json:"footimeStamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
2121

2222
// +optional
23-
BarTimestamp *time.Time `json:"barTimestamp,omitempty"` // want "field BarTimestamp: prefer use of the term time over timestamp"
23+
BarTimestamp *time.Time `json:"barTimestamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
2424

2525
// +optional
26-
FootimestampBar *time.Time `json:"fooTimestampBar,omitempty"` // want "field FootimestampBar: prefer use of the term time over timestamp"
26+
FootimestampBar *time.Time `json:"fooTimestampBar,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
2727

2828
// +optional
29-
FooTimestampBarTimeStamp *time.Time `json:"fooTimestampBarTimeStamp,omitempty"` // want "field FooTimestampBarTimeStamp: prefer use of the term time over timestamp"
29+
FooTimestampBarTimeStamp *time.Time `json:"fooTimestampBarTimeStamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
3030

3131
// +optional
32-
MetaTimeStamp *metav1.Time `json:"metaTimeStamp,omitempty"` // want "field MetaTimeStamp: prefer use of the term time over timestamp"
32+
MetaTimeStamp *metav1.Time `json:"metaTimeStamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
3333
}
3434

3535
// DoNothing is used to check that the analyser doesn't report on methods.
3636
func (NoTimeStampTestStruct) DoNothing() {}
3737

3838
type NoSubTimeStampTestStruct struct {
3939
// +optional
40-
FooTimeStamp *time.Time `json:"fooTimeStamp,omitempty"` // want "field FooTimeStamp: prefer use of the term time over timestamp"
40+
FooTimeStamp *time.Time `json:"fooTimeStamp,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
4141
}
4242

4343
type SerializedTimeStampTestStruct struct {

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,36 @@ import (
88

99
type NoTimeStampTestStruct struct {
1010
// +optional
11-
Time *time.Time `json:"time,omitempty"` // want "field TimeStamp: prefer use of the term time over timestamp"
11+
Time *time.Time `json:"time,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
1212

1313
// +optional
14-
Time *time.Time `json:"time,omitempty"` // want "field Timestamp: prefer use of the term time over timestamp"
14+
Time *time.Time `json:"time,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
1515

1616
// +optional
17-
FooTime *time.Time `json:"fooTime,omitempty"` // want "field FooTimeStamp: prefer use of the term time over timestamp"
17+
FooTime *time.Time `json:"fooTime,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
1818

1919
// +optional
20-
FooTime *time.Time `json:"fooTime,omitempty"` // want "field FootimeStamp: prefer use of the term time over timestamp"
20+
FooTime *time.Time `json:"fooTime,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
2121

2222
// +optional
23-
BarTime *time.Time `json:"barTime,omitempty"` // want "field BarTimestamp: prefer use of the term time over timestamp"
23+
BarTime *time.Time `json:"barTime,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
2424

2525
// +optional
26-
FooTimeBar *time.Time `json:"fooTimeBar,omitempty"` // want "field FootimestampBar: prefer use of the term time over timestamp"
26+
FooTimeBar *time.Time `json:"fooTimeBar,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
2727

2828
// +optional
29-
FooTimeBarTime *time.Time `json:"fooTimeBarTime,omitempty"` // want "field FooTimestampBarTimeStamp: prefer use of the term time over timestamp"
29+
FooTimeBarTime *time.Time `json:"fooTimeBarTime,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
3030

3131
// +optional
32-
MetaTime *metav1.Time `json:"metaTime,omitempty"` // want "field MetaTimeStamp: prefer use of the term time over timestamp"
32+
MetaTime *metav1.Time `json:"metaTime,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
3333
}
3434

3535
// DoNothing is used to check that the analyser doesn't report on methods.
3636
func (NoTimeStampTestStruct) DoNothing() {}
3737

3838
type NoSubTimeStampTestStruct struct {
3939
// +optional
40-
FooTime *time.Time `json:"fooTime,omitempty"` // want "field FooTimeStamp: prefer use of the term time over timestamp"
40+
FooTime *time.Time `json:"fooTime,omitempty"` // want `naming convention "notimestamp": prefer use of the term 'time' over 'timestamp'`
4141
}
4242

4343
type SerializedTimeStampTestStruct struct {

0 commit comments

Comments
 (0)