Skip to content

Commit 7db9920

Browse files
committed
feat(markerscope): add default config and enhance documentation
Signed-off-by: nayuta-ai <nayuta723@gmail.com>
1 parent 6270f45 commit 7db9920

File tree

4 files changed

+96
-42
lines changed

4 files changed

+96
-42
lines changed

pkg/analysis/markerscope/analyzer.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ func newAnalyzer(cfg *MarkerScopeConfig) *analysis.Analyzer {
6060
cfg = &MarkerScopeConfig{}
6161
}
6262

63+
// Apply default configuration
64+
defaultConfig(cfg)
65+
6366
// Convert override and custom marker lists to maps
6467
overrideRules := markerRulesListToMap(cfg.OverrideMarkers)
6568
customRules := markerRulesListToMap(cfg.CustomMarkers)
@@ -87,14 +90,15 @@ func newAnalyzer(cfg *MarkerScopeConfig) *analysis.Analyzer {
8790
markershelper.DefaultRegistry().Register(marker)
8891
}
8992

90-
// Set default policy if not specified
91-
if a.policy == "" {
92-
a.policy = MarkerScopePolicyWarn
93-
}
94-
9593
return &analysis.Analyzer{
96-
Name: name,
97-
Doc: "Validates that markers are applied in the correct scope.",
94+
Name: name,
95+
Doc: `Validates that markers are applied in the correct scope and to compatible data types.
96+
This analyzer performs two levels of validation:
97+
1. Scope validation - ensures markers are placed on the correct location (field vs type)
98+
2. Type constraint validation - ensures markers are applied to compatible data types
99+
The analyzer includes 100+ built-in kubebuilder marker rules. You can override built-in marker
100+
rules using overrideMarkers configuration, or add custom markers using customMarkers configuration.
101+
`,
98102
Run: a.run,
99103
Requires: []*analysis.Analyzer{inspect.Analyzer, markershelper.Analyzer},
100104
RunDespiteErrors: true,
@@ -112,6 +116,16 @@ func markerRulesListToMap(rules []MarkerScopeRule) map[string]MarkerScopeRule {
112116
return result
113117
}
114118

119+
// defaultConfig applies default values to the configuration.
120+
func defaultConfig(cfg *MarkerScopeConfig) {
121+
// Set default policy if not specified
122+
if cfg.Policy == "" {
123+
cfg.Policy = MarkerScopePolicyWarn
124+
}
125+
// allowDangerousTypes defaults to false (zero value)
126+
// overrideMarkers and customMarkers default to empty (zero value)
127+
}
128+
115129
func (a *analyzer) run(pass *analysis.Pass) (any, error) {
116130
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
117131
if !ok {

pkg/analysis/markerscope/analyzer_test.go

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,88 +13,108 @@ 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-
package markerscope
16+
package markerscope_test
1717

1818
import (
1919
"testing"
2020

2121
"golang.org/x/tools/go/analysis/analysistest"
22+
"sigs.k8s.io/kube-api-linter/pkg/analysis/markerscope"
2223
)
2324

24-
func TestAnalyzerWarnOnly(t *testing.T) {
25+
func TestAnalyzerWithDefaultConfig(t *testing.T) {
2526
testdata := analysistest.TestData()
26-
cfg := &MarkerScopeConfig{
27-
Policy: MarkerScopePolicyWarn,
27+
// Test with nil config - should use all defaults:
28+
// - Policy: Warn
29+
// - AllowDangerousTypes: false
30+
// - OverrideMarkers: empty (use built-in defaults)
31+
// - CustomMarkers: empty
32+
analyzer, err := markerscope.Initializer().Init(&markerscope.MarkerScopeConfig{})
33+
if err != nil {
34+
t.Fatal(err)
2835
}
29-
analyzer := newAnalyzer(cfg)
3036
analysistest.Run(t, testdata, analyzer, "a")
3137
}
3238

3339
func TestAnalyzerSuggestFixes(t *testing.T) {
3440
testdata := analysistest.TestData()
35-
cfg := &MarkerScopeConfig{
36-
Policy: MarkerScopePolicySuggestFix,
41+
cfg := &markerscope.MarkerScopeConfig{
42+
Policy: markerscope.MarkerScopePolicySuggestFix,
43+
}
44+
analyzer, err := markerscope.Initializer().Init(cfg)
45+
if err != nil {
46+
t.Fatal(err)
3747
}
38-
analyzer := newAnalyzer(cfg)
3948
analysistest.RunWithSuggestedFixes(t, testdata, analyzer, "a")
4049
}
4150

4251
func TestAnalyzerWithCustomAndOverrideMarkers(t *testing.T) {
4352
testdata := analysistest.TestData()
44-
cfg := &MarkerScopeConfig{
45-
Policy: MarkerScopePolicyWarn,
46-
OverrideMarkers: []MarkerScopeRule{
53+
cfg := &markerscope.MarkerScopeConfig{
54+
Policy: markerscope.MarkerScopePolicyWarn,
55+
OverrideMarkers: []markerscope.MarkerScopeRule{
4756
// Override built-in "optional" to allow on types (default is FieldScope only)
4857
{
4958
Identifier: "optional",
50-
Scope: AnyScope,
59+
Scope: markerscope.AnyScope,
5160
},
5261
// Override built-in "required" to allow on types (default is FieldScope only)
5362
{
5463
Identifier: "required",
55-
Scope: AnyScope,
64+
Scope: markerscope.AnyScope,
5665
},
5766
},
58-
CustomMarkers: []MarkerScopeRule{
67+
CustomMarkers: []markerscope.MarkerScopeRule{
5968
// Custom field-only marker
6069
{
6170
Identifier: "custom:field-only",
62-
Scope: FieldScope,
71+
Scope: markerscope.FieldScope,
6372
},
6473
// Custom type-only marker
6574
{
6675
Identifier: "custom:type-only",
67-
Scope: TypeScope,
76+
Scope: markerscope.TypeScope,
6877
},
6978
// Custom marker with string type constraint
7079
{
7180
Identifier: "custom:string-only",
72-
Scope: FieldScope,
73-
TypeConstraint: &TypeConstraint{
74-
AllowedSchemaTypes: []SchemaType{SchemaTypeString},
81+
Scope: markerscope.FieldScope,
82+
TypeConstraint: &markerscope.TypeConstraint{
83+
AllowedSchemaTypes: []markerscope.SchemaType{
84+
markerscope.SchemaTypeString,
85+
},
7586
},
7687
},
7788
// Custom marker with integer type constraint
7889
{
7990
Identifier: "custom:integer-only",
80-
Scope: FieldScope,
81-
TypeConstraint: &TypeConstraint{
82-
AllowedSchemaTypes: []SchemaType{SchemaTypeInteger},
91+
Scope: markerscope.FieldScope,
92+
TypeConstraint: &markerscope.TypeConstraint{
93+
AllowedSchemaTypes: []markerscope.SchemaType{
94+
markerscope.SchemaTypeInteger,
95+
},
8396
},
8497
},
8598
// Custom marker with array of strings constraint
8699
{
87100
Identifier: "custom:string-array",
88-
Scope: FieldScope,
89-
TypeConstraint: &TypeConstraint{
90-
AllowedSchemaTypes: []SchemaType{SchemaTypeArray},
91-
ElementConstraint: &TypeConstraint{
92-
AllowedSchemaTypes: []SchemaType{SchemaTypeString},
101+
Scope: markerscope.FieldScope,
102+
TypeConstraint: &markerscope.TypeConstraint{
103+
AllowedSchemaTypes: []markerscope.SchemaType{
104+
markerscope.SchemaTypeArray,
105+
},
106+
ElementConstraint: &markerscope.TypeConstraint{
107+
AllowedSchemaTypes: []markerscope.SchemaType{
108+
markerscope.SchemaTypeString,
109+
},
93110
},
94111
},
95112
},
96113
},
97114
}
98-
analyzer := newAnalyzer(cfg)
115+
analyzer, err := markerscope.Initializer().Init(cfg)
116+
if err != nil {
117+
t.Fatal(err)
118+
}
99119
analysistest.Run(t, testdata, analyzer, "b")
100120
}

pkg/analysis/markerscope/doc.go

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

17-
// Package markerscope provides a linter that validates markers are applied in the correct scope.
17+
// Package markerscope provides a linter that validates markers are applied in the correct scope
18+
// and to compatible data types.
19+
//
20+
// # Scope Validation
1821
//
1922
// Some markers are only valid when applied to specific Go constructs:
20-
// - Field-only markers: optional, required, nullable
21-
// - Type/Struct-only markers: MinProperties, MaxProperties, kubebuilder:object:root, kubebuilder:subresource:status
22-
// - Field or Type markers: default, MinLength, MaxLength, etc.
23+
// - Field-only markers: optional, required, nullable
24+
// - Type/Struct-only markers: MinProperties, MaxProperties, kubebuilder:object:root, kubebuilder:subresource:status
25+
// - Field or Type markers: default, MinLength, MaxLength, etc.
26+
//
27+
// # Type Constraint Validation
28+
//
29+
// Markers are also validated for type correctness to ensure they are applied to compatible data types:
30+
// - Numeric markers (Minimum, Maximum, MultipleOf) must be applied to integer or number types
31+
// - String markers (Pattern, MinLength, MaxLength) must be applied to string types
32+
// - Array markers (MinItems, MaxItems, UniqueItems) must be applied to array types
33+
// - Object markers (MinProperties, MaxProperties) must be applied to object types (struct/map)
34+
//
35+
// For example, applying kubebuilder:validation:Maximum to a string field will be flagged as an error
36+
// since Maximum is only valid for numeric types.
37+
//
38+
// # Array Element Type Constraints
39+
//
40+
// For array types, element-level constraints can be specified using items: prefix markers
41+
// (e.g., items:Minimum, items:Pattern). These validate the array element types rather than
42+
// the array itself.
2343
//
24-
// This linter ensures markers are applied in their appropriate contexts to prevent
25-
// configuration errors and improve API consistency.
44+
// This linter ensures markers are applied in their appropriate contexts and to compatible types
45+
// to prevent configuration errors and improve API consistency.
2646
package markerscope

pkg/analysis/markerscope/initializer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func Initializer() initializer.AnalyzerInitializer {
3434
return initializer.NewConfigurableInitializer(
3535
name,
3636
initAnalyzer,
37-
false, // Not enabled by default
37+
true,
3838
validateConfig,
3939
)
4040
}

0 commit comments

Comments
 (0)