-
Notifications
You must be signed in to change notification settings - Fork 21
New Linter: Dependent tag #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: yongruilin The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
docs/linters.md
Outdated
| - main: "listType" | ||
| dependents: | ||
| - "k8s:listType" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there are dependencies, how do values work? Can this linter fix dependencies? If I want the same value between these two, is that achievable? If I wanted different values, is that achievable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Values are ignored: The linter only checks if a marker like +listType exists; it does not care about its value (e.g., =map).
- No automatic fixes: The linter will report that a dependent marker is missing, but it cannot automatically add it for you.
- Value matching is not supported: Enforcing that two markers have the same or different values is outside the scope of this linter.
I have updated the docs/linters.md file with a "Behavior" section to make this clear.
pkg/analysis/dependenttags/config.go
Outdated
| // defaultConfig is the default configuration for the dependenttags linter. | ||
| var defaultConfig = Config{ | ||
| Rules: []Rule{}, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is an empty config, do we really need it?
| } | ||
|
|
||
| rulesPath := fldPath.Child("rules") | ||
| for i, rule := range cfg.Rules { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be an error to have no rules
c549928 to
34fe862
Compare
547d0ff to
c1dcbd1
Compare
Introduces a new configurable linter named `dependenttags` to enforce dependencies between markers. This linter ensures that if a main marker is present on a field, a set of dependent markers are also present, preventing API inconsistencies. The linter supports two dependency types: - `All`: Requires all dependent markers to be present. - `Any`: Requires at least one of the dependent markers to be present. This addresses issue kubernetes-sigs#146.
Adds documentation for the new `dependenttags` linter to `docs/linters.md`. This includes a description of the linter, its configuration options, and examples of how to use it.
c1dcbd1 to
a5a9a02
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please ensure we consistently updated all/any to All/Any
I think it would be useful (especially for the listType case) to be able to say "this identifier with this value relies on this identifier"
What if we changed the config to
- identifier: ...
payload: ... # optional
dependents:
- identifier: ...
payload: ... # optional
And then you would be able to say that if it has a specified payload, then the dependents are either requiring just the identifier, or the identifier with a particular payload
This could become a bit more complex with DV parsing though, not sure how arguments will fit into this CC @everettraven
|
|
||
| This linter only checks for the presence or absence of markers; it does not inspect or enforce specific values within those markers. Therefore: | ||
|
|
||
| - **Values:** The linter does not care about the values of the `identifier` or `dependent` markers. It only verifies if the markers themselves are present. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+listType=map needs +listMapKey=..., that cannot be represented here then? Feels like that could be something that is useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can, you can directly put identifier as +listType=map
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure there are test cases for this
|
|
||
| type Union struct { | ||
| // +k8s:unionMember | ||
| InvalidMember string // want "field with marker \\+k8s:unionMember is missing required marker\\(s\\): \\+k8s:optional" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should include the field name, and ideally struct name in the error message
| InvalidMember string // want "field with marker \\+k8s:unionMember is missing required marker\\(s\\): \\+k8s:optional" | |
| InvalidMember string // want "field Union.InvalidMember with marker \\+k8s:unionMember is missing required marker\\(s\\): \\+k8s:optional" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
pkg/analysis/dependenttags/config.go
Outdated
|
|
||
| const ( | ||
| // DependencyTypeAll indicates that all dependent markers are required. | ||
| DependencyTypeAll DependencyType = "all" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| DependencyTypeAll DependencyType = "all" | |
| DependencyTypeAll DependencyType = "All" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
| t.Run(tc.name, func(t *testing.T) { | ||
| initializer := dependenttags.Initializer() | ||
| errs := initializer.ValidateConfig(tc.cfg, field.NewPath("")) | ||
|
|
||
| if tc.expectErr && len(errs) == 0 { | ||
| t.Errorf("expected validation errors, but got none") | ||
| } | ||
|
|
||
| if !tc.expectErr && len(errs) > 0 { | ||
| t.Errorf("unexpected validation errors: %v", errs) | ||
| } | ||
|
|
||
| if !tc.expectErr { | ||
| if _, err := initializer.Init(tc.cfg); err != nil { | ||
| t.Errorf("unexpected error initializing analyzer: %v", err) | ||
| } | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest of our tests use gomega for assertions, worth doing the same here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
| } | ||
|
|
||
| if rule.Type == "" { | ||
| errs = append(errs, field.Required(rulesPath.Index(i).Child("type"), "type must be explicitly set to 'all' or 'any'")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a format string with the actual constant values please
| case DependencyTypeAll, DependencyTypeAny: | ||
| // valid | ||
| default: | ||
| errs = append(errs, field.Invalid(rulesPath.Index(i).Child("type"), rule.Type, "type must be 'all' or 'any'")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't invalid take a list of expected valid values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems not. I've updated to use format string in error detail.
…standardize type values
This PR introduces a new dependenttags linter to enforce dependencies between markers, preventing API inconsistencies where one marker requires another.
The linter is configurable with rules and supports requiring either all (default) or any of the specified dependent markers.
Fixes #146