Repository: googleapis/api-linter (go)
Issue: Inherit api-linter comments from parent
Say I have a service:

```
service Stuff {
  rpc ViolatesRule7() returns (google.protobuf.Empty) {}
  rpc AlsoViolatesRule7() returns (google.protobuf.Empty) {}
  rpc ThisTooViolatesRule7() returns (google.protobuf.Empty) {}
 ...
}

```
I want to attach the api-linter: core::007::rule-seven-violatoin=disabled comment to ALL of these RPCs, so I might want to list it at the service level:

```
// (-- api-linter: core::007::rule-seven-violation=disabled --)
service Stuff {
  rpc ViolatesRule7() returns (google.protobuf.Empty) {}
  rpc AlsoViolatesRule7() returns (google.protobuf.Empty) {}
  rpc ThisTooViolatesRule7() returns (google.protobuf.Empty) {}
 ...
}
```

It appears that this does not work.
Pull request description: [feat] Allow disabling rules to cascade.
This PR changes from "disable on descriptor or file" to
"disable on descriptor or any parent, including file".

Fixes #406.

Patch:
diff --git a/lint/rule.go b/lint/rule.go
index 7ae2147..48c7686 100644
--- a/lint/rule.go
+++ b/lint/rule.go
@@ -376,8 +376,12 @@ func ruleIsEnabled(rule ProtoRule, d desc.Descriptor, aliasMap map[string]string
 	ruleName := string(rule.GetName())
 	names := []string{ruleName, aliasMap[ruleName]}
 
-	commentLines := strings.Split(fileHeader(d.GetFile()), "\n")
-	commentLines = append(commentLines, strings.Split(getLeadingComments(d), "\n")...)
+	commentLines := []string{}
+	if f, ok := d.(*desc.FileDescriptor); ok {
+		commentLines = append(commentLines, strings.Split(fileHeader(f), "\n")...)
+	} else {
+		commentLines = append(commentLines, strings.Split(getLeadingComments(d), "\n")...)
+	}
 	disabledRules := []string{}
 	for _, commentLine := range commentLines {
 		r := extractDisabledRuleName(commentLine)
@@ -392,6 +396,12 @@ func ruleIsEnabled(rule ProtoRule, d desc.Descriptor, aliasMap map[string]string
 		}
 	}
 
+	// The rule may have been disabled on a parent. (For example, a field rule
+	// may be disabled at the message level to cover all fields in the message).
+	if parent := d.GetParent(); parent != nil {
+		return ruleIsEnabled(rule, parent, aliasMap)
+	}
+
 	return true
 }
 


Test patch:
diff --git a/lint/rule_test.go b/lint/rule_test.go
index c14b429..6dc9d85 100644
--- a/lint/rule_test.go
+++ b/lint/rule_test.go
@@ -422,7 +422,7 @@ func TestRuleIsEnabledFirstMessage(t *testing.T) {
 		},
 	}
 
-	// Run the specific tests individually.
+	// Build a proto and check that ruleIsEnabled does the right thing.
 	f, err := builder.NewFile("test.proto").AddMessage(
 		builder.NewMessage("FirstMessage").SetComments(builder.Comments{
 			LeadingComment: "api-linter: test=disabled",
@@ -441,6 +441,34 @@ func TestRuleIsEnabledFirstMessage(t *testing.T) {
 	}
 }
 
+func TestRuleIsEnabledParent(t *testing.T) {
+	// Create a rule that we can check enabled status on.
+	rule := &FieldRule{
+		Name: RuleName("test"),
+		LintField: func(f *desc.FieldDescriptor) []Problem {
+			return nil
+		},
+	}
+
+	// Build a proto with two messages, one of which disables the rule.
+	f, err := builder.NewFile("test.proto").AddMessage(
+		builder.NewMessage("Foo").SetComments(builder.Comments{
+			LeadingComment: "api-linter: test=disabled",
+		}).AddField(builder.NewField("foo", builder.FieldTypeBool())),
+	).AddMessage(
+		builder.NewMessage("Bar").AddField(builder.NewField("bar", builder.FieldTypeBool())),
+	).Build()
+	if err != nil {
+		t.Fatalf("Error building test file: %q", err)
+	}
+	if got, want := ruleIsEnabled(rule, f.GetMessageTypes()[0].GetFields()[0], nil), false; got != want {
+		t.Errorf("Expected the foo field to return %v from ruleIsEnabled; got %v", want, got)
+	}
+	if got, want := ruleIsEnabled(rule, f.GetMessageTypes()[1].GetFields()[0], nil), true; got != want {
+		t.Errorf("Expected the foo field to return %v from ruleIsEnabled; got %v", want, got)
+	}
+}
+
 type lintRuleTest struct {
 	testName string
 	problems []Problem