Repository: googleapis/api-linter
Pull request #335 (2019-11-12 01:24:56)

[feat] Add suggestions and locations.
This PR adds more specific locations and corresponding suggestions
as a follow-up to the new locations methods introduced in #309.


Patch:
diff --git a/rules/aip0131/request_message_name.go b/rules/aip0131/request_message_name.go
index cda4e17..3dbc9df 100644
--- a/rules/aip0131/request_message_name.go
+++ b/rules/aip0131/request_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,6 +37,7 @@ var requestMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0131/request_name_field.go b/rules/aip0131/request_name_field.go
index 3d22faf..ec556f9 100644
--- a/rules/aip0131/request_name_field.go
+++ b/rules/aip0131/request_name_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -41,6 +42,8 @@ var standardFields = &lint.MessageRule{
 			return []lint.Problem{{
 				Message:    "`name` field on Get RPCs should be a string",
 				Descriptor: name,
+				Location:   locations.FieldType(name),
+				Suggestion: "string",
 			}}
 		}
 
diff --git a/rules/aip0131/response_message_name.go b/rules/aip0131/response_message_name.go
index 40db464..9dc87e8 100644
--- a/rules/aip0131/response_message_name.go
+++ b/rules/aip0131/response_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,6 +37,7 @@ var responseMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0132/request_message_name.go b/rules/aip0132/request_message_name.go
index 7aca9dc..9aecd46 100644
--- a/rules/aip0132/request_message_name.go
+++ b/rules/aip0132/request_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,6 +37,7 @@ var requestMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0132/request_parent_field.go b/rules/aip0132/request_parent_field.go
index 9bafbed..4e4ae48 100644
--- a/rules/aip0132/request_parent_field.go
+++ b/rules/aip0132/request_parent_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -40,7 +41,9 @@ var standardFields = &lint.MessageRule{
 		if parentField.GetType() != builder.FieldTypeString().GetType() {
 			return []lint.Problem{{
 				Message:    "`parent` field on List RPCs must be a string",
+				Suggestion: "string",
 				Descriptor: parentField,
+				Location:   locations.FieldType(parentField),
 			}}
 		}
 
diff --git a/rules/aip0132/response_message_name.go b/rules/aip0132/response_message_name.go
index 0285ee9..8f2754d 100644
--- a/rules/aip0132/response_message_name.go
+++ b/rules/aip0132/response_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,6 +37,7 @@ var responseMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodResponseType(m),
 			}}
 		}
 
diff --git a/rules/aip0133/request_message_name.go b/rules/aip0133/request_message_name.go
index ed5134c..ead2dd5 100644
--- a/rules/aip0133/request_message_name.go
+++ b/rules/aip0133/request_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,9 +37,9 @@ var inputName = &lint.MethodRule{
 					"Post RPCs should have a properly named request message %q, but not %q",
 					want, got,
 				),
-				// TODO: suggestion will be set after the location is set properly
-				// Suggestion: want,
+				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0133/request_parent_field.go b/rules/aip0133/request_parent_field.go
index c31f0e6..ab9dae8 100644
--- a/rules/aip0133/request_parent_field.go
+++ b/rules/aip0133/request_parent_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -40,7 +41,9 @@ var parentField = &lint.MessageRule{
 		if parentField.GetType() != builder.FieldTypeString().GetType() {
 			return []lint.Problem{{
 				Message:    "`parent` field on create request message must be a string",
+				Suggestion: "string",
 				Descriptor: parentField,
+				Location:   locations.FieldType(parentField),
 			}}
 		}
 
diff --git a/rules/aip0133/response_message_name.go b/rules/aip0133/response_message_name.go
index a4ac017..cc0bfd1 100644
--- a/rules/aip0133/response_message_name.go
+++ b/rules/aip0133/response_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
 )
@@ -48,9 +49,9 @@ var outputName = &lint.MethodRule{
 					"Create RPCs should have the corresponding resource as the response message, such as %q.",
 					want,
 				),
-				// TODO: suggestion will be set after the location is set properly
-				// Suggestion: want,
+				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodResponseType(m),
 			}}
 		}
 
diff --git a/rules/aip0134/request_mask_field.go b/rules/aip0134/request_mask_field.go
index 374608f..c0bd932 100644
--- a/rules/aip0134/request_mask_field.go
+++ b/rules/aip0134/request_mask_field.go
@@ -16,6 +16,7 @@ package aip0134
 
 import (
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -33,7 +34,9 @@ var requestMaskField = &lint.MessageRule{
 		if t := updateMask.GetMessageType(); t == nil || t.GetFullyQualifiedName() != "google.protobuf.FieldMask" {
 			return []lint.Problem{{
 				Message:    "The `update_mask` field should be a google.protobuf.FieldMask.",
+				Suggestion: "google.protobuf.FieldMask",
 				Descriptor: updateMask,
+				Location:   locations.FieldType(updateMask),
 			}}
 		}
 		return nil
diff --git a/rules/aip0134/request_message_name.go b/rules/aip0134/request_message_name.go
index 622fb87..a13c9c6 100644
--- a/rules/aip0134/request_message_name.go
+++ b/rules/aip0134/request_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,6 +37,7 @@ var requestMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0134/response_message_name.go b/rules/aip0134/response_message_name.go
index 6657f46..97a4321 100644
--- a/rules/aip0134/response_message_name.go
+++ b/rules/aip0134/response_message_name.go
@@ -19,6 +19,7 @@ import (
 	"strings"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
 )
@@ -52,6 +53,7 @@ var responseMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodResponseType(m),
 			}}
 		}
 		return nil
diff --git a/rules/aip0135/request_message_name.go b/rules/aip0135/request_message_name.go
index fd20490..1c688a8 100644
--- a/rules/aip0135/request_message_name.go
+++ b/rules/aip0135/request_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -36,6 +37,7 @@ var requestMessageName = &lint.MethodRule{
 				),
 				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0135/request_name_field.go b/rules/aip0135/request_name_field.go
index 5835f20..6028755 100644
--- a/rules/aip0135/request_name_field.go
+++ b/rules/aip0135/request_name_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -40,7 +41,9 @@ var standardFields = &lint.MessageRule{
 		if name.GetType() != builder.FieldTypeString().GetType() {
 			return []lint.Problem{{
 				Message:    "`name` field on Get RPCs should be a string",
+				Suggestion: "string",
 				Descriptor: name,
+				Location:   locations.FieldType(name),
 			}}
 		}
 
diff --git a/rules/aip0135/response_message_name.go b/rules/aip0135/response_message_name.go
index 618be43..e29ce80 100644
--- a/rules/aip0135/response_message_name.go
+++ b/rules/aip0135/response_message_name.go
@@ -20,6 +20,7 @@ import (
 
 	"bitbucket.org/creachadair/stringset"
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
 )
@@ -60,6 +61,7 @@ var responseMessageName = &lint.MethodRule{
 				),
 				Suggestion: "google.protobuf.Empty",
 				Descriptor: m,
+				Location:   locations.MethodResponseType(m),
 			}}
 		}
 
diff --git a/rules/aip0141/forbidden_types.go b/rules/aip0141/forbidden_types.go
index a03e729..d1485da 100644
--- a/rules/aip0141/forbidden_types.go
+++ b/rules/aip0141/forbidden_types.go
@@ -19,6 +19,7 @@ import (
 
 	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
@@ -43,7 +44,9 @@ var forbiddenTypes = &lint.FieldRule{
 			want := "int" + typeName[len(typeName)-2:]
 			return []lint.Problem{{
 				Message:    fmt.Sprintf("Use %q instead of %q.", want, typeName),
+				Suggestion: want,
 				Descriptor: f,
+				Location:   locations.FieldType(f),
 			}}
 		}
 		return nil
diff --git a/rules/aip0142/time_field_type.go b/rules/aip0142/time_field_type.go
index 535b15b..ae61ded 100644
--- a/rules/aip0142/time_field_type.go
+++ b/rules/aip0142/time_field_type.go
@@ -19,6 +19,7 @@ import (
 
 	"bitbucket.org/creachadair/stringset"
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
 )
@@ -35,7 +36,9 @@ var fieldType = &lint.FieldRule{
 		if suffixes.Contains(tokens[len(tokens)-1]) && utils.GetMessageTypeName(f) != "google.protobuf.Timestamp" {
 			return []lint.Problem{{
 				Message:    "Fields representing timestamps should use `google.protobuf.Timestamp`.",
+				Suggestion: "google.protobuf.Timestamp",
 				Descriptor: f,
+				Location:   locations.FieldType(f),
 			}}
 		}
 		return nil
diff --git a/rules/aip0143/string_type.go b/rules/aip0143/string_type.go
index a89145f..2263631 100644
--- a/rules/aip0143/string_type.go
+++ b/rules/aip0143/string_type.go
@@ -19,6 +19,7 @@ import (
 
 	"bitbucket.org/creachadair/stringset"
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
 )
@@ -38,7 +39,9 @@ var fieldTypes = &lint.FieldRule{
 		if typeName := utils.GetScalarTypeName(f); typeName != "string" {
 			return []lint.Problem{{
 				Message:    fmt.Sprintf("Field %q should be a string, not %s.", f.GetName(), typeName),
+				Suggestion: "string",
 				Descriptor: f,
+				Location:   locations.FieldType(f),
 			}}
 		}
 		return nil
diff --git a/rules/aip0158/request_page_size_field.go b/rules/aip0158/request_page_size_field.go
index da89aa0..270f1da 100644
--- a/rules/aip0158/request_page_size_field.go
+++ b/rules/aip0158/request_page_size_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -38,8 +39,10 @@ var requestPaginationPageSize = &lint.MessageRule{
 		// Rule check: Ensure that the name page_size is the correct type.
 		if pageSize.GetType() != builder.FieldTypeInt32().GetType() {
 			return []lint.Problem{{
-				Message:    "`page_size` field on List RPCs should be a int32",
+				Message:    "`page_size` field on List RPCs should be an int32",
+				Suggestion: "int32",
 				Descriptor: pageSize,
+				Location:   locations.FieldType(pageSize),
 			}}
 		}
 
diff --git a/rules/aip0158/request_page_token_field.go b/rules/aip0158/request_page_token_field.go
index 1599df1..4635c40 100644
--- a/rules/aip0158/request_page_token_field.go
+++ b/rules/aip0158/request_page_token_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -39,7 +40,9 @@ var requestPaginationPageToken = &lint.MessageRule{
 		if pageToken.GetType() != builder.FieldTypeString().GetType() {
 			return []lint.Problem{{
 				Message:    "`page_token` field on List RPCs should be a string",
+				Suggestion: "string",
 				Descriptor: pageToken,
+				Location:   locations.FieldType(pageToken),
 			}}
 		}
 
diff --git a/rules/aip0158/response_next_page_token_field.go b/rules/aip0158/response_next_page_token_field.go
index bb2e1d0..bc11e51 100644
--- a/rules/aip0158/response_next_page_token_field.go
+++ b/rules/aip0158/response_next_page_token_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -39,7 +40,9 @@ var responsePaginationNextPageToken = &lint.MessageRule{
 		if nextPageToken.GetType() != builder.FieldTypeString().GetType() {
 			return []lint.Problem{{
 				Message:    "`next_page_token` field on List RPCs should be a string",
+				Suggestion: "string",
 				Descriptor: nextPageToken,
+				Location:   locations.FieldType(nextPageToken),
 			}}
 		}
 
diff --git a/rules/aip0231/request_message_name.go b/rules/aip0231/request_message_name.go
index ba812a3..1e0ccb6 100644
--- a/rules/aip0231/request_message_name.go
+++ b/rules/aip0231/request_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -34,7 +35,9 @@ var inputName = &lint.MethodRule{
 					"Batch Get RPCs should have a properly named request message %q, but not %q",
 					want, got,
 				),
+				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodRequestType(m),
 			}}
 		}
 
diff --git a/rules/aip0231/request_names_field.go b/rules/aip0231/request_names_field.go
index c379a88..30eb4c8 100644
--- a/rules/aip0231/request_names_field.go
+++ b/rules/aip0231/request_names_field.go
@@ -19,6 +19,7 @@ import (
 
 	"github.com/gertd/go-pluralize"
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -53,7 +54,9 @@ var namesField = &lint.MessageRule{
 		if names != nil && !names.IsRepeated() {
 			problems = append(problems, lint.Problem{
 				Message:    `The "names"" field should be repeated`,
+				Suggestion: "repeated string",
 				Descriptor: names,
+				Location:   locations.FieldType(names),
 			})
 		}
 
@@ -61,7 +64,9 @@ var namesField = &lint.MessageRule{
 		if names != nil && names.GetType() != builder.FieldTypeString().GetType() {
 			problems = append(problems, lint.Problem{
 				Message:    `"names" field on Batch Get Request should be a "string" type`,
+				Suggestion: "string",
 				Descriptor: names,
+				Location:   locations.FieldType(names),
 			})
 		}
 
diff --git a/rules/aip0231/request_parent_field.go b/rules/aip0231/request_parent_field.go
index 09db26b..dc09bd1 100644
--- a/rules/aip0231/request_parent_field.go
+++ b/rules/aip0231/request_parent_field.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
@@ -40,7 +41,9 @@ var parentField = &lint.MessageRule{
 		if parentField.GetType() != builder.FieldTypeString().GetType() {
 			return []lint.Problem{{
 				Message:    "`parent` field on create request message must be a string",
+				Suggestion: "string",
 				Descriptor: parentField,
+				Location:   locations.FieldType(parentField),
 			}}
 		}
 
diff --git a/rules/aip0231/response_message_name.go b/rules/aip0231/response_message_name.go
index f96f264..d72a69b 100644
--- a/rules/aip0231/response_message_name.go
+++ b/rules/aip0231/response_message_name.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/locations"
 	"github.com/jhump/protoreflect/desc"
 )
 
@@ -34,7 +35,9 @@ var outputName = &lint.MethodRule{
 					"Batch Get RPCs should have a properly named response message %q, but not %q",
 					want, got,
 				),
+				Suggestion: want,
 				Descriptor: m,
+				Location:   locations.MethodResponseType(m),
 			}}
 		}
 


Test patch:
diff --git a/rules/aip0131/request_name_field_test.go b/rules/aip0131/request_name_field_test.go
index 948a449..638cfad 100644
--- a/rules/aip0131/request_name_field_test.go
+++ b/rules/aip0131/request_name_field_test.go
@@ -53,3 +53,24 @@ func TestStandardFields(t *testing.T) {
 		})
 	}
 }
+
+func TestStandardFieldsInvalidType(t *testing.T) {
+	// Create an appropriate message descriptor.
+	message, err := builder.NewMessage("GetBookRequest").AddField(
+		builder.NewField("name", builder.FieldTypeBytes()),
+	).Build()
+	if err != nil {
+		t.Fatalf("Could not build descriptor.")
+	}
+
+	// Run the lint rule, and establish that it returns the correct
+	// number of problems.
+	wantProblems := testutils.Problems{{
+		Descriptor: message.GetFields()[0],
+		Suggestion: "string",
+	}}
+	gotProblems := standardFields.Lint(message.GetFile())
+	if diff := wantProblems.Diff(gotProblems); diff != "" {
+		t.Errorf(diff)
+	}
+}
diff --git a/rules/aip0131/request_unknown_fields_test.go b/rules/aip0131/request_unknown_fields_test.go
index 767d4dd..ac90e8e 100644
--- a/rules/aip0131/request_unknown_fields_test.go
+++ b/rules/aip0131/request_unknown_fields_test.go
@@ -23,27 +23,6 @@ import (
 	fpb "google.golang.org/genproto/protobuf/field_mask"
 )
 
-func TestStandardFieldsInvalidType(t *testing.T) {
-	// Create an appropriate message descriptor.
-	message, err := builder.NewMessage("GetBookRequest").AddField(
-		builder.NewField("name", builder.FieldTypeBytes()),
-	).Build()
-	if err != nil {
-		t.Fatalf("Could not build descriptor.")
-	}
-
-	// Run the lint rule, and establish that it returns the correct
-	// number of problems.
-	wantProblems := testutils.Problems{{
-		Descriptor: message.GetFields()[0],
-		Message:    "string",
-	}}
-	gotProblems := standardFields.Lint(message.GetFile())
-	if diff := wantProblems.Diff(gotProblems); diff != "" {
-		t.Errorf(diff)
-	}
-}
-
 func TestUnknownFields(t *testing.T) {
 	// Get the correct message type for google.protobuf.FieldMask.
 	fieldMask, err := desc.LoadMessageDescriptorForMessage(&fpb.FieldMask{})
diff --git a/rules/aip0132/request_parent_field_test.go b/rules/aip0132/request_parent_field_test.go
index 2922a9a..ed0a810 100644
--- a/rules/aip0132/request_parent_field_test.go
+++ b/rules/aip0132/request_parent_field_test.go
@@ -39,7 +39,7 @@ func TestStandardFields(t *testing.T) {
 			"ListBooksRequest",
 			"parent",
 			builder.FieldTypeBytes(),
-			testutils.Problems{{Message: "string"}},
+			testutils.Problems{{Suggestion: "string"}},
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.GetFields()[0]
 			},
diff --git a/rules/aip0133/request_parent_field_test.go b/rules/aip0133/request_parent_field_test.go
index 9827074..19ea1e6 100644
--- a/rules/aip0133/request_parent_field_test.go
+++ b/rules/aip0133/request_parent_field_test.go
@@ -48,7 +48,7 @@ func TestParentField(t *testing.T) {
 			"InvalidType",
 			"CreateBookRequest",
 			&field{"parent", builder.FieldTypeDouble()},
-			testutils.Problems{{Message: "string"}},
+			testutils.Problems{{Suggestion: "string"}},
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("parent")
 			},
diff --git a/rules/aip0133/response_message_name_test.go b/rules/aip0133/response_message_name_test.go
index 64bb25f..836e826 100644
--- a/rules/aip0133/response_message_name_test.go
+++ b/rules/aip0133/response_message_name_test.go
@@ -31,8 +31,8 @@ func TestOutputMessageName(t *testing.T) {
 	}{
 		{"ValidResource", "CreateBook", "Book", false, testutils.Problems{}},
 		{"ValidLRO", "CreateBook", "Book", true, testutils.Problems{}},
-		{"Invalid", "CreateBook", "CreateBookResponse", false, testutils.Problems{{Message: "Book"}}},
-		{"InvalidLRO", "CreateBook", "CreateBookResponse", true, testutils.Problems{{Message: "Book"}}},
+		{"Invalid", "CreateBook", "CreateBookResponse", false, testutils.Problems{{Suggestion: "Book"}}},
+		{"InvalidLRO", "CreateBook", "CreateBookResponse", true, testutils.Problems{{Suggestion: "Book"}}},
 		{"Irrelevant", "BuildBook", "BuildBookResponse", false, testutils.Problems{}},
 	}
 
diff --git a/rules/aip0134/request_mask_field_test.go b/rules/aip0134/request_mask_field_test.go
index db425f8..7b2950e 100644
--- a/rules/aip0134/request_mask_field_test.go
+++ b/rules/aip0134/request_mask_field_test.go
@@ -36,7 +36,7 @@ func TestRequestMaskField(t *testing.T) {
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.GetFields()[1]
 			},
-			testutils.Problems{{Message: "google.protobuf.FieldMask"}},
+			testutils.Problems{{Suggestion: "google.protobuf.FieldMask"}},
 		},
 	}
 	for _, test := range tests {
diff --git a/rules/aip0135/request_name_field_test.go b/rules/aip0135/request_name_field_test.go
index 205ee83..06d1de0 100644
--- a/rules/aip0135/request_name_field_test.go
+++ b/rules/aip0135/request_name_field_test.go
@@ -67,7 +67,7 @@ func TestStandardFieldsInvalidType(t *testing.T) {
 	// number of problems.
 	wantProblems := testutils.Problems{{
 		Descriptor: message.GetFields()[0],
-		Message:    "string",
+		Suggestion: "string",
 	}}
 	gotProblems := standardFields.Lint(message.GetFile())
 	if diff := wantProblems.Diff(gotProblems); diff != "" {
diff --git a/rules/aip0141/forbidden_types_test.go b/rules/aip0141/forbidden_types_test.go
index 041c2d5..91b47c6 100644
--- a/rules/aip0141/forbidden_types_test.go
+++ b/rules/aip0141/forbidden_types_test.go
@@ -27,10 +27,10 @@ func TestForbiddenTypes(t *testing.T) {
 	}{
 		{"int32", testutils.Problems{}},
 		{"int64", testutils.Problems{}},
-		{"uint32", testutils.Problems{{Message: "int32"}}},
-		{"uint64", testutils.Problems{{Message: "int64"}}},
-		{"fixed32", testutils.Problems{{Message: "int32"}}},
-		{"fixed64", testutils.Problems{{Message: "int64"}}},
+		{"uint32", testutils.Problems{{Suggestion: "int32"}}},
+		{"uint64", testutils.Problems{{Suggestion: "int64"}}},
+		{"fixed32", testutils.Problems{{Suggestion: "int32"}}},
+		{"fixed64", testutils.Problems{{Suggestion: "int64"}}},
 	}
 	for _, test := range tests {
 		t.Run(test.TypeName, func(t *testing.T) {
diff --git a/rules/aip0142/time_field_type_test.go b/rules/aip0142/time_field_type_test.go
index ae31728..53c1743 100644
--- a/rules/aip0142/time_field_type_test.go
+++ b/rules/aip0142/time_field_type_test.go
@@ -28,7 +28,7 @@ func TestFieldType(t *testing.T) {
 		problems  testutils.Problems
 	}{
 		{"Valid", "google.protobuf.Timestamp", "create_time", testutils.Problems{}},
-		{"Invalid", "int32", "created_ms", testutils.Problems{{Message: "Timestamp"}}},
+		{"Invalid", "int32", "created_ms", testutils.Problems{{Suggestion: "google.protobuf.Timestamp"}}},
 		{"Irrelevant", "int32", "foo", testutils.Problems{}},
 	}
 	for _, test := range tests {
diff --git a/rules/aip0143/string_type_test.go b/rules/aip0143/string_type_test.go
index e0a4d76..329060f 100644
--- a/rules/aip0143/string_type_test.go
+++ b/rules/aip0143/string_type_test.go
@@ -29,8 +29,8 @@ func TestFieldTypes(t *testing.T) {
 	}{
 		{"Irrelevant", "int32", "book_count", testutils.Problems{}},
 		{"Valid", "string", "language_code", testutils.Problems{}},
-		{"InvalidScalar", "bytes", "language_code", testutils.Problems{{Message: "string"}}},
-		{"InvalidEnum", "Language", "language_code", testutils.Problems{{Message: "string"}}},
+		{"InvalidScalar", "bytes", "language_code", testutils.Problems{{Suggestion: "string"}}},
+		{"InvalidEnum", "Language", "language_code", testutils.Problems{{Suggestion: "string"}}},
 	}
 	for _, test := range tests {
 		t.Run(test.testName, func(t *testing.T) {
diff --git a/rules/aip0158/request_page_size_field_test.go b/rules/aip0158/request_page_size_field_test.go
index 9cfa3af..03c24e7 100644
--- a/rules/aip0158/request_page_size_field_test.go
+++ b/rules/aip0158/request_page_size_field_test.go
@@ -48,7 +48,7 @@ func TestRequestPaginationPageSize(t *testing.T) {
 			"InvalidType",
 			"ListFooRequest",
 			[]field{{"page_size", builder.FieldTypeDouble()}},
-			testutils.Problems{{Message: "int32"}},
+			testutils.Problems{{Suggestion: "int32"}},
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("page_size")
 			},
diff --git a/rules/aip0158/request_page_token_field_test.go b/rules/aip0158/request_page_token_field_test.go
index 1daf27c..86277f5 100644
--- a/rules/aip0158/request_page_token_field_test.go
+++ b/rules/aip0158/request_page_token_field_test.go
@@ -49,7 +49,7 @@ func TestRequestPaginationPageToken(t *testing.T) {
 			"InvalidType",
 			"ListFooRequest",
 			[]field{{"page_token", builder.FieldTypeDouble()}},
-			testutils.Problems{{Message: "string"}},
+			testutils.Problems{{Suggestion: "string"}},
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("page_token")
 			},
diff --git a/rules/aip0158/response_next_page_token_field_test.go b/rules/aip0158/response_next_page_token_field_test.go
index 0de47da..3ec93ac 100644
--- a/rules/aip0158/response_next_page_token_field_test.go
+++ b/rules/aip0158/response_next_page_token_field_test.go
@@ -54,7 +54,7 @@ func TestResponsePaginationNextPageToken(t *testing.T) {
 			"InvalidType",
 			"ListFooResponse",
 			[]field{{"name", builder.FieldTypeString()}, {"next_page_token", builder.FieldTypeDouble()}},
-			testutils.Problems{{Message: "string"}},
+			testutils.Problems{{Suggestion: "string"}},
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("next_page_token")
 			},
diff --git a/rules/aip0231/request_message_name_test.go b/rules/aip0231/request_message_name_test.go
index aedc447..dd77c4a 100644
--- a/rules/aip0231/request_message_name_test.go
+++ b/rules/aip0231/request_message_name_test.go
@@ -29,7 +29,7 @@ func TestInputName(t *testing.T) {
 		problems   testutils.Problems
 	}{
 		{"Valid", "BatchGetBooks", "BatchGetBooksRequest", testutils.Problems{}},
-		{"Invalid", "BatchGetBooks", "GetBookRequest", testutils.Problems{{Message: "BatchGetBooksRequest"}}},
+		{"Invalid", "BatchGetBooks", "GetBookRequest", testutils.Problems{{Suggestion: "BatchGetBooksRequest"}}},
 		{"Irrelevant", "GetBookRequest", "Foo", testutils.Problems{}},
 	}
 
diff --git a/rules/aip0231/request_names_field_test.go b/rules/aip0231/request_names_field_test.go
index ebb4b89..41d039f 100644
--- a/rules/aip0231/request_names_field_test.go
+++ b/rules/aip0231/request_names_field_test.go
@@ -32,39 +32,42 @@ func TestNamesField(t *testing.T) {
 		{
 			testName: "Valid-Names",
 			src: `
-message BatchGetBooksRequest {
-repeated string names = 1;
-}`,
+				message BatchGetBooksRequest {
+					repeated string names = 1;
+				}
+			`,
 			problems: testutils.Problems{},
 		},
 		{
 			testName: "Valid-StandardGetReq",
 			src: `
-message BatchGetBooksRequest {
-repeated GetBookRequest requests = 1;
-}
+				message BatchGetBooksRequest {
+				repeated GetBookRequest requests = 1;
+				}
 
-message GetBookRequest {}
-`,
+				message GetBookRequest {}
+			`,
 			problems: testutils.Problems{},
 		},
 		{
 			testName: "Invalid-MissingNamesField",
 			src: `
-message BatchGetBooksRequest {
- string parent = 1;
-}`,
+				message BatchGetBooksRequest {
+				string parent = 1;
+				}
+			`,
 			problems: testutils.Problems{{Message: `Message "BatchGetBooksRequest" has no "names" field`}},
 		},
 		{
 			testName: "Invalid-KeepingNamesFieldOnly",
 			src: `
-message BatchGetBooksRequest {
-repeated string names = 1;
-repeated GetBookRequest requests = 2;
-}
+				message BatchGetBooksRequest {
+				repeated string names = 1;
+				repeated GetBookRequest requests = 2;
+				}
 
-message GetBookRequest {}`,
+				message GetBookRequest {}
+			`,
 			problems: testutils.Problems{{Message: `Message "BatchGetBooksRequest" should delete "requests" field, only keep the "names" field`}},
 			problemDesc: func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("requests")
@@ -73,10 +76,10 @@ message GetBookRequest {}`,
 		{
 			testName: "Invalid-NamesFieldIsNotRepeated",
 			src: `
-message BatchGetBooksRequest {
-string names = 1;
-}`,
-			problems: testutils.Problems{{Message: `The "names"" field should be repeated`}},
+				message BatchGetBooksRequest {
+					string names = 1;
+				}`,
+			problems: testutils.Problems{{Suggestion: "repeated string"}},
 			problemDesc: func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("names")
 			},
@@ -84,10 +87,11 @@ string names = 1;
 		{
 			testName: "Invalid-NamesFieldWrongType",
 			src: `
-message BatchGetBooksRequest {
-repeated int32 names = 1;
-}`,
-			problems: testutils.Problems{{Message: `"names" field on Batch Get Request should be a "string" type`}},
+				message BatchGetBooksRequest {
+					repeated int32 names = 1;
+				}
+			`,
+			problems: testutils.Problems{{Suggestion: "string"}},
 			problemDesc: func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("names")
 			},
@@ -95,11 +99,12 @@ repeated int32 names = 1;
 		{
 			testName: "Invalid-GetReqFieldIsNotRepeated",
 			src: `
-message BatchGetBooksRequest {
-GetBookRequest requests = 1;
-}
+				message BatchGetBooksRequest {
+				GetBookRequest requests = 1;
+				}
 
-message GetBookRequest {}`,
+				message GetBookRequest {}
+			`,
 			problems: testutils.Problems{{Message: `The "requests" field should be repeated`}},
 			problemDesc: func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("requests")
@@ -108,9 +113,10 @@ message GetBookRequest {}`,
 		{
 			testName: "Invalid-NamesFieldWrongType",
 			src: `
-message BatchGetBooksRequest {
-	repeated string requests = 1;
-}`,
+				message BatchGetBooksRequest {
+					repeated string requests = 1;
+				}
+			`,
 			problems: testutils.Problems{{Message: `The "requests" field on Batch Get Request should be a "GetBookRequest" type`}},
 			problemDesc: func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("requests")
diff --git a/rules/aip0231/request_parent_field_test.go b/rules/aip0231/request_parent_field_test.go
index 916f12f..4009ab1 100644
--- a/rules/aip0231/request_parent_field_test.go
+++ b/rules/aip0231/request_parent_field_test.go
@@ -53,7 +53,7 @@ func TestParentField(t *testing.T) {
 			"InvalidType",
 			"BatchGetBooksRequest",
 			&field{"parent", builder.FieldTypeDouble()},
-			testutils.Problems{{Message: "string"}},
+			testutils.Problems{{Suggestion: "string"}},
 			func(m *desc.MessageDescriptor) desc.Descriptor {
 				return m.FindFieldByName("parent")
 			},
diff --git a/rules/aip0231/response_message_name_test.go b/rules/aip0231/response_message_name_test.go
index 1232dc3..346a092 100644
--- a/rules/aip0231/response_message_name_test.go
+++ b/rules/aip0231/response_message_name_test.go
@@ -29,7 +29,7 @@ func TestOutputName(t *testing.T) {
 		problems   testutils.Problems
 	}{
 		{"Valid", "BatchGetBooks", "BatchGetBooksResponse", testutils.Problems{}},
-		{"Invalid", "BatchGetBooks", "Book", testutils.Problems{{Message: "BatchGetBooksResponse"}}},
+		{"Invalid", "BatchGetBooks", "Book", testutils.Problems{{Suggestion: "BatchGetBooksResponse"}}},
 		{"Irrelevant", "GetBookRequest", "Book", testutils.Problems{}},
 	}