Skip to content

Commit

Permalink
fix: Make the required and optional rules less noisy. (#760)
Browse files Browse the repository at this point in the history
This commit makes those lint rules stop complaining if *both*
the terms "required" and "optional" are present; this is a common
occurrence when fields are conditionally required, which is a
good reason to use the terms in comments without an annotation.

Fixes #759.
  • Loading branch information
Luke Sneeringer committed Feb 17, 2021
1 parent e253419 commit f27c257
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 34 deletions.
23 changes: 15 additions & 8 deletions rules/aip0203/aip0203.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,22 @@ func AddRules(r lint.RuleRegistry) error {

// check leading comments of a field and produce a problem
// if the comments match the give pattern.
func checkLeadingComments(f *desc.FieldDescriptor, pattern *regexp.Regexp, annotation string) []lint.Problem {
leadingComments := f.GetSourceInfo().GetLeadingComments()
if pattern.MatchString(leadingComments) {
return []lint.Problem{{
Message: fmt.Sprintf("Use the `google.api.field_behavior` annotation instead of %q in the leading comments. For example, `string name = 1 [(google.api.field_behavior) = %s];`.", pattern.FindString(leadingComments), annotation),
Descriptor: f,
}}
func checkLeadingComments(pattern *regexp.Regexp, annotation string, unless ...*regexp.Regexp) func(*desc.FieldDescriptor) []lint.Problem {
return func(f *desc.FieldDescriptor) []lint.Problem {
leadingComments := f.GetSourceInfo().GetLeadingComments()
for _, ul := range unless {
if ul.MatchString(leadingComments) {
return nil
}
}
if pattern.MatchString(leadingComments) {
return []lint.Problem{{
Message: fmt.Sprintf("Use the `google.api.field_behavior` annotation instead of %q in the leading comments. For example, `string name = 1 [(google.api.field_behavior) = %s];`.", pattern.FindString(leadingComments), annotation),
Descriptor: f,
}}
}
return nil
}
return nil
}

func withoutFieldBehavior(f *desc.FieldDescriptor) bool {
Expand Down
8 changes: 3 additions & 5 deletions rules/aip0203/immutable.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import (
)

var immutable = &lint.FieldRule{
Name: lint.NewRuleName(203, "immutable"),
OnlyIf: withoutImmutableFieldBehavior,
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
return checkLeadingComments(f, immutableRegexp, "IMMUTABLE")
},
Name: lint.NewRuleName(203, "immutable"),
OnlyIf: withoutImmutableFieldBehavior,
LintField: checkLeadingComments(immutableRegexp, "IMMUTABLE"),
}

var immutableRegexp = regexp.MustCompile("(?i).*immutable.*")
Expand Down
8 changes: 3 additions & 5 deletions rules/aip0203/input_only.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import (
)

var inputOnly = &lint.FieldRule{
Name: lint.NewRuleName(203, "input-only"),
OnlyIf: withoutInputOnlyFieldBehavior,
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
return checkLeadingComments(f, inputOnlyRegexp, "INPUT_ONLY")
},
Name: lint.NewRuleName(203, "input-only"),
OnlyIf: withoutInputOnlyFieldBehavior,
LintField: checkLeadingComments(inputOnlyRegexp, "INPUT_ONLY"),
}

var inputOnlyRegexp = regexp.MustCompile("(?i).*input.?only.*")
Expand Down
8 changes: 3 additions & 5 deletions rules/aip0203/optional.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import (
)

var optional = &lint.FieldRule{
Name: lint.NewRuleName(203, "optional"),
OnlyIf: withoutFieldBehavior,
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
return checkLeadingComments(f, optionalRegexp, "OPTIONAL")
},
Name: lint.NewRuleName(203, "optional"),
OnlyIf: withoutFieldBehavior,
LintField: checkLeadingComments(optionalRegexp, "OPTIONAL", requiredRegexp),
}

var optionalRegexp = regexp.MustCompile("(?i).*optional.*")
Expand Down
6 changes: 6 additions & 0 deletions rules/aip0203/optional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ func TestOptional(t *testing.T) {
Message: "google.api.field_behavior",
}},
},
{
name: "valid-required-and-optional",
comment: "optional required",
field: titleField,
problems: nil,
},
{
name: "Invalid-Optional",
comment: "Optional",
Expand Down
8 changes: 3 additions & 5 deletions rules/aip0203/output_only.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import (
)

var outputOnly = &lint.FieldRule{
Name: lint.NewRuleName(203, "output-only"),
OnlyIf: withoutOutputOnlyFieldBehavior,
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
return checkLeadingComments(f, outputOnlyRegexp, "OUTPUT_ONLY")
},
Name: lint.NewRuleName(203, "output-only"),
OnlyIf: withoutOutputOnlyFieldBehavior,
LintField: checkLeadingComments(outputOnlyRegexp, "OUTPUT_ONLY"),
}

var outputOnlyRegexp = regexp.MustCompile("(?i).*output.?only.*")
Expand Down
9 changes: 3 additions & 6 deletions rules/aip0203/required.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@ import (
"regexp"

"github.com/googleapis/api-linter/lint"
"github.com/jhump/protoreflect/desc"
)

var required = &lint.FieldRule{
Name: lint.NewRuleName(203, "required"),
OnlyIf: withoutFieldBehavior,
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
return checkLeadingComments(f, requiredRegexp, "REQUIRED")
},
Name: lint.NewRuleName(203, "required"),
OnlyIf: withoutFieldBehavior,
LintField: checkLeadingComments(requiredRegexp, "REQUIRED", optionalRegexp),
}

var requiredRegexp = regexp.MustCompile("(?i).*required.*")

0 comments on commit f27c257

Please sign in to comment.