Skip to content

Commit c5a8a96

Browse files
policy switch case, renaming policy, and dropping references
1 parent 2502e96 commit c5a8a96

File tree

8 files changed

+128
-130
lines changed

8 files changed

+128
-130
lines changed

docs/linters.md

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -567,29 +567,38 @@ By default, `references` is enabled and operates in standard mode, allowing 'Ref
567567
```yaml
568568
lintersConfig:
569569
references:
570-
policy: AllowRefAndRefs | ForbidRefAndRefs # Defaults to `AllowRefAndRefs`.
570+
policy: PreferAbbreviatedReference | NoReferences # Defaults to `PreferAbbreviatedReference`.
571571
```
572572

573-
**Default behavior (policy: AllowRefAndRefs):**
574-
- Reports errors for fields containing 'Reference' or 'References' and suggests replacing with 'Ref' or 'Refs'
573+
**Default behavior (policy: PreferAbbreviatedReference):**
574+
- Reports errors for fields containing 'Reference' or 'References' and replaces with 'Ref' or 'Refs'
575575
- **Allows** fields containing 'Ref' or 'Refs' without reporting errors
576576

577-
**Strict mode (policy: ForbidRefAndRefs):**
578-
- Reports errors for fields containing 'Reference' or 'References' anywhere and suggests replacing with 'Ref' or 'Refs'
579-
- **Also reports errors** for fields containing 'Ref' or 'Refs' anywhere (no automatic fix provided)
580-
- In this strict mode, the goal is to avoid all Ref/Refs/Reference/References in field names entirely
577+
**Strict mode (policy: NoReferences):**
578+
- Reports errors for any reference-related words in field names
579+
- Suggests removing 'Ref', 'Refs', 'Reference', or 'References' entirely from the beginning or end of field names
580+
- In this strict mode, the goal is to avoid all reference-related words in field names
581581

582582
### Fixes
583583

584-
The `references` linter can automatically fix field names by replacing 'Reference' with 'Ref' and 'References' with 'Refs' anywhere in the field name.
585-
586-
For example:
587-
- `NodeReference``NodeRef`
588-
- `ReferenceNode``RefNode`
589-
- `NodeReferenceConfig``NodeRefConfig`
590-
- `NodeReferences``NodeRefs`
591-
592-
Note: In the `ForbidRefAndRefs` mode, the fixes are intermediate. As, the linter will still report errors on the resulting 'Ref'/'Refs' patterns, indicating they should be removed entirely
584+
The `references` linter can automatically fix field names based on the policy:
585+
586+
**PreferAbbreviatedReference mode:**
587+
- Replaces 'Reference' with 'Ref' and 'References' with 'Refs' anywhere in the field name
588+
- Examples:
589+
- `NodeReference``NodeRef`
590+
- `ReferenceNode``RefNode`
591+
- `NodeReferenceConfig``NodeRefConfig`
592+
- `NodeReferences``NodeRefs`
593+
594+
**NoReferences mode:**
595+
- Removes all reference-related words (Ref/Refs/Reference/References) from the beginning or end of field names
596+
- Examples:
597+
- `RefNode``Node`
598+
- `NodeRef``Node`
599+
- `ReferenceName``Name`
600+
- `ConfigReferences``Config`
601+
- `ReferenceCount``Count`
593602

594603
## SSATags
595604

pkg/analysis/references/analyzer.go

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ func newAnalyzer(cfg *Config) *analysis.Analyzer {
3838
cfg = &Config{}
3939
}
4040

41-
// Default to AllowRefAndRefs if no policy is specified
41+
// Default to PreferAbbreviatedReference if no policy is specified
4242
policy := cfg.Policy
4343
if policy == "" {
44-
policy = PolicyAllowRefAndRefs
44+
policy = PolicyPreferAbbreviatedReference
4545
}
4646

4747
// Build the namingconventions config based on the policy
@@ -75,36 +75,45 @@ func newAnalyzer(cfg *Config) *analysis.Analyzer {
7575

7676
// buildConventions creates the naming conventions based on the policy.
7777
func buildConventions(policy Policy) []namingconventions.Convention {
78-
// Base convention: Replace "Reference" or "References" with "Ref" or "Refs"
79-
// Using a single regex with optional 's' capture group to handle both cases
80-
conventions := []namingconventions.Convention{
81-
{
82-
Name: "reference-to-ref",
83-
ViolationMatcher: "(?i)reference(s?)",
84-
Operation: namingconventions.OperationReplacement,
85-
Replacement: "Ref$1",
86-
Message: "field names should use 'Ref' instead of 'Reference'",
87-
},
88-
}
89-
90-
// If policy is ForbidRefAndRefs, also flag Ref/Refs as problematic (no fix provided)
91-
// This creates a two-step hint: fix Reference→Ref, but know that Ref also needs changing
92-
if policy == PolicyForbidRefAndRefs {
93-
conventions = append(conventions,
94-
namingconventions.Convention{
95-
Name: "forbid-refs",
96-
ViolationMatcher: "(?i)refs([^a-z]|$)",
97-
Operation: namingconventions.OperationInform,
98-
Message: "should not use 'Refs'",
78+
switch policy {
79+
case PolicyPreferAbbreviatedReference:
80+
// Replace "Reference" or "References" with "Ref" or "Refs"
81+
// Using a single regex with optional 's' capture group to handle both cases
82+
return []namingconventions.Convention{
83+
{
84+
Name: "reference-to-ref",
85+
ViolationMatcher: "(?i)reference(s?)",
86+
Operation: namingconventions.OperationReplacement,
87+
Replacement: "Ref$1",
88+
Message: "field names should use 'Ref' instead of 'Reference'",
9989
},
100-
namingconventions.Convention{
101-
Name: "forbid-ref",
102-
ViolationMatcher: "(?i)ref([^ers]|$)",
103-
Operation: namingconventions.OperationInform,
104-
Message: "should not use 'Ref'",
90+
}
91+
92+
case PolicyNoReferences:
93+
// Drop any reference-related words from field names
94+
// Using a regex that matches Ref/Refs/Reference/References at start or end
95+
// At start: matches when followed by an uppercase letter, preserving that letter
96+
// At end: matches at the end of the field name
97+
return []namingconventions.Convention{
98+
{
99+
Name: "no-references",
100+
ViolationMatcher: "^([Rr]ef(?:erence)?s?)([A-Z])|([Rr]ef(?:erence)?s?)$",
101+
Operation: namingconventions.OperationReplacement,
102+
Replacement: "$2",
103+
Message: "field names should not contain reference-related words",
105104
},
106-
)
105+
}
106+
107+
default:
108+
// Should not happen due to validation, but return PreferAbbreviatedReference conventions as fallback
109+
return []namingconventions.Convention{
110+
{
111+
Name: "reference-to-ref",
112+
ViolationMatcher: "(?i)reference(s?)",
113+
Operation: namingconventions.OperationReplacement,
114+
Replacement: "Ref$1",
115+
Message: "field names should use 'Ref' instead of 'Reference'",
116+
},
117+
}
107118
}
108-
109-
return conventions
110119
}

pkg/analysis/references/analyzer_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ import (
2222
"sigs.k8s.io/kube-api-linter/pkg/analysis/references"
2323
)
2424

25-
func TestAllowRefAndRefs(t *testing.T) {
25+
func TestPreferAbbreviatedReference(t *testing.T) {
2626
testdata := analysistest.TestData()
2727

2828
cfg := &references.Config{
29-
Policy: references.PolicyAllowRefAndRefs,
29+
Policy: references.PolicyPreferAbbreviatedReference,
3030
}
3131

3232
analyzer, err := references.Initializer().Init(cfg)
@@ -40,24 +40,24 @@ func TestAllowRefAndRefs(t *testing.T) {
4040
func TestEmptyConfig(t *testing.T) {
4141
testdata := analysistest.TestData()
4242

43-
// Test with empty config - should default to AllowRefAndRefs behavior
43+
// Test with empty config - should default to PreferAbbreviatedReference behavior
4444
cfg := &references.Config{}
4545

4646
analyzer, err := references.Initializer().Init(cfg)
4747
if err != nil {
4848
t.Fatalf("initializing references linter: %v", err)
4949
}
5050

51-
// With default config (empty Policy), it should default to AllowRefAndRefs behavior
51+
// With default config (empty Policy), it should default to PreferAbbreviatedReference behavior
5252
// So we test with folder 'a' which has the same expectations
5353
analysistest.RunWithSuggestedFixes(t, testdata, analyzer, "a")
5454
}
5555

56-
func TestForbidRefAndRefs(t *testing.T) {
56+
func TestNoReferences(t *testing.T) {
5757
testdata := analysistest.TestData()
5858

5959
cfg := &references.Config{
60-
Policy: references.PolicyForbidRefAndRefs,
60+
Policy: references.PolicyNoReferences,
6161
}
6262

6363
analyzer, err := references.Initializer().Init(cfg)

pkg/analysis/references/config.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,22 @@ limitations under the License.
1515
*/
1616
package references
1717

18-
// policy defines the policy for handling Ref/Refs.
18+
// policy defines the policy for handling references in field names.
1919
type Policy string
2020

2121
const (
22-
// PolicyAllowRefAndRefs allows Ref/Refs in field names.
23-
PolicyAllowRefAndRefs Policy = "AllowRefAndRefs"
24-
// PolicyForbidRefAndRefs forbids Ref/Refs in field names.
25-
PolicyForbidRefAndRefs Policy = "ForbidRefAndRefs"
22+
// PolicyPreferAbbreviatedReference allows abbreviated forms (Ref/Refs) in field names.
23+
// It suggests replacing Reference/References with Ref/Refs.
24+
PolicyPreferAbbreviatedReference Policy = "PreferAbbreviatedReference"
25+
// PolicyNoReferences forbids any reference-related words in field names.
26+
// It suggests removing Ref/Refs/Reference/References entirely.
27+
PolicyNoReferences Policy = "NoReferences"
2628
)
2729

2830
// Config represents the configuration for the references linter.
2931
type Config struct {
30-
// policy controls whether Ref/Refs are allowed or forbidden in field names.
31-
// When set to AllowRefAndRefs (default), fields containing Ref/Refs are allowed.
32-
// When set to ForbidRefAndRefs, fields containing Ref/Refs are flagged as errors.
32+
// policy controls how reference-related words are handled in field names.
33+
// When set to PreferAbbreviatedReference (default), Reference/References are replaced with Ref/Refs.
34+
// When set to NoReferences, all reference-related words are suggested to be removed.
3335
Policy Policy `json:"policy,omitempty"`
3436
}

pkg/analysis/references/initializer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ func validateConfig(cfg *Config, fldPath *field.Path) field.ErrorList {
5151

5252
// Validate Policy enum if provided
5353
switch cfg.Policy {
54-
case PolicyAllowRefAndRefs, PolicyForbidRefAndRefs, "":
54+
case PolicyPreferAbbreviatedReference, PolicyNoReferences, "":
5555
default:
5656
errs = append(errs, field.NotSupported(
5757
fldPath.Child("policy"),
5858
cfg.Policy,
59-
[]string{string(PolicyAllowRefAndRefs), string(PolicyForbidRefAndRefs)},
59+
[]string{string(PolicyPreferAbbreviatedReference), string(PolicyNoReferences)},
6060
))
6161
}
6262
return errs

pkg/analysis/references/initializer_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ var _ = Describe("references initializer", func() {
4242
Expect(errs).To(HaveLen(0), "No errors were expected")
4343
}
4444
},
45-
Entry("With a valid references configuration with policy=ForbidRefAndRefs", testCase{
45+
Entry("With a valid references configuration with policy=NoReferences", testCase{
4646
config: &references.Config{
47-
Policy: references.PolicyForbidRefAndRefs,
47+
Policy: references.PolicyNoReferences,
4848
},
4949
expectedErr: "",
5050
}),
51-
Entry("With a valid references configuration with policy=AllowRefAndRefs", testCase{
51+
Entry("With a valid references configuration with policy=PreferAbbreviatedReference", testCase{
5252
config: &references.Config{
53-
Policy: references.PolicyAllowRefAndRefs,
53+
Policy: references.PolicyPreferAbbreviatedReference,
5454
},
5555
expectedErr: "",
5656
}),
@@ -66,7 +66,7 @@ var _ = Describe("references initializer", func() {
6666
config: &references.Config{
6767
Policy: "InvalidPolicy",
6868
},
69-
expectedErr: "references.policy: Unsupported value: \"InvalidPolicy\": supported values: \"AllowRefAndRefs\", \"ForbidRefAndRefs\"",
69+
expectedErr: "references.policy: Unsupported value: \"InvalidPolicy\": supported values: \"PreferAbbreviatedReference\", \"NoReferences\"",
7070
}),
7171
)
7272
})
Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,40 @@
11
package b
22

3-
// TestWithPolicyForbidRefAndRefs tests the linter with PolicyForbidRefAndRefs (strict mode)
4-
// In this mode, Reference/References AND Ref/Refs (at end) are flagged
5-
type TestWithPolicyForbidRefAndRefs struct {
3+
// TestWithPolicyNoReferences tests the linter with PolicyNoReferences (strict mode)
4+
// In this mode, all reference-related words (Reference/References/Ref/Refs) are removed
5+
type TestWithPolicyNoReferences struct {
66
// Fields ending with Reference should be flagged
7-
NodeReference string `json:"nodeReference"` // want `naming convention "reference-to-ref": field NodeReference: field names should use 'Ref' instead of 'Reference'`
8-
9-
ConfigReference string `json:"configReference"` // want `naming convention "reference-to-ref": field ConfigReference: field names should use 'Ref' instead of 'Reference'`
7+
NodeReference string `json:"nodeReference"` // want `naming convention "no-references": field NodeReference: field names should not contain reference-related words`
108

119
// Fields ending with References should be flagged
12-
NodeReferences []string `json:"nodeReferences"` // want `naming convention "reference-to-ref": field NodeReferences: field names should use 'Ref' instead of 'Reference'`
13-
14-
ConfigReferences []string `json:"configReferences"` // want `naming convention "reference-to-ref": field ConfigReferences: field names should use 'Ref' instead of 'Reference'`
10+
ConfigReferences []string `json:"configReferences"` // want `naming convention "no-references": field ConfigReferences: field names should not contain reference-related words`
1511

1612
// Fields with Reference at beginning should be flagged
17-
ReferenceCount int `json:"referenceCount"` // want `naming convention "reference-to-ref": field ReferenceCount: field names should use 'Ref' instead of 'Reference'`
18-
19-
ReferenceData string `json:"referenceData"` // want `naming convention "reference-to-ref": field ReferenceData: field names should use 'Ref' instead of 'Reference'`
13+
ReferenceCount int `json:"referenceCount"` // want `naming convention "no-references": field ReferenceCount: field names should not contain reference-related words`
2014

2115
// Fields with References at beginning should be flagged
22-
ReferencesCount int `json:"referencesCount"` // want `naming convention "reference-to-ref": field ReferencesCount: field names should use 'Ref' instead of 'Reference'`
23-
24-
ReferencesData []string `json:"referencesData"` // want `naming convention "reference-to-ref": field ReferencesData: field names should use 'Ref' instead of 'Reference'`
25-
26-
// Fields with Reference in middle should be flagged
27-
CrossReferenceID string `json:"crossReferenceID"` // want `naming convention "reference-to-ref": field CrossReferenceID: field names should use 'Ref' instead of 'Reference'`
28-
29-
// Fields with References in middle should be flagged
30-
CrossReferencesMap map[string]string `json:"crossReferencesMap"` // want `naming convention "reference-to-ref": field CrossReferencesMap: field names should use 'Ref' instead of 'Reference'`
16+
ReferencesData []string `json:"referencesData"` // want `naming convention "no-references": field ReferencesData: field names should not contain reference-related words`
3117

32-
// Fields ending with Ref should be FORBIDDEN in this mode
33-
NodeRef string `json:"nodeRef"` // want `naming convention "forbid-ref": field NodeRef: should not use 'Ref'`
18+
// Fields ending with Ref should be flagged
19+
PodRef string `json:"podRef"` // want `naming convention "no-references": field PodRef: field names should not contain reference-related words`
3420

35-
ConfigRef string `json:"configRef"` // want `naming convention "forbid-ref": field ConfigRef: should not use 'Ref'`
21+
// Fields ending with Refs should be flagged
22+
ContainerRefs []string `json:"containerRefs"` // want `naming convention "no-references": field ContainerRefs: field names should not contain reference-related words`
3623

37-
// Fields ending with Refs should be FORBIDDEN in this mode
38-
NodeRefs []string `json:"nodeRefs"` // want `naming convention "forbid-refs": field NodeRefs: should not use 'Refs'`
24+
// Fields starting with Ref should be flagged
25+
RefStatus string `json:"refStatus"` // want `naming convention "no-references": field RefStatus: field names should not contain reference-related words`
3926

40-
ConfigRefs []string `json:"configRefs"` // want `naming convention "forbid-refs": field ConfigRefs: should not use 'Refs'`
27+
// Fields starting with Refs should be flagged
28+
RefsTotal int `json:"refsTotal"` // want `naming convention "no-references": field RefsTotal: field names should not contain reference-related words`
4129

42-
// Normal fields should not be flagged (no Reference/References/Ref/Refs)
30+
// Normal fields should not be flagged
4331
Name string `json:"name"`
4432

4533
Namespace string `json:"namespace"`
4634

47-
// Edge cases - Preference contains "reference" and will be flagged
48-
PreferenceType string `json:"preferenceType"` // want `naming convention "reference-to-ref": field PreferenceType: field names should use 'Ref' instead of 'Reference'`
35+
// Edge cases - "erence" in the middle of a word is not flagged (only start/end boundaries)
36+
PreferenceType string `json:"preferenceType"`
4937

50-
Preferences map[string]string `json:"preferences,omitempty"` // want `naming convention "reference-to-ref": field Preferences: field names should use 'Ref' instead of 'Reference'`
38+
// But "Preferences" ending with 's' after "erence" IS flagged since it ends with "ences"
39+
Preferences map[string]string `json:"preferences,omitempty"` // want `naming convention "no-references": field Preferences: field names should not contain reference-related words`
5140
}

0 commit comments

Comments
 (0)