Repository: googleapis/api-linter
Pull request #346 (2019-11-15 02:51:59)

[feat] Add location method for the HTTP rule.
Sadly, it appears this is the most specific we are able to get
at this time.


Patch:
diff --git a/locations/method_locations.go b/locations/method_locations.go
index f5f1b1e..fa07e70 100644
--- a/locations/method_locations.go
+++ b/locations/method_locations.go
@@ -17,6 +17,7 @@ package locations
 import (
 	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/jhump/protoreflect/desc"
+	apb "google.golang.org/genproto/googleapis/api/annotations"
 )
 
 // MethodRequestType returns the precise location of the method's input type.
@@ -36,3 +37,13 @@ func MethodResponseType(m *desc.MethodDescriptor) *dpb.SourceCodeInfo_Location {
 	}
 	return nil
 }
+
+// MethodHTTPRule returns the precise location of the method's `google.api.http`
+// rule, if any.
+func MethodHTTPRule(m *desc.MethodDescriptor) *dpb.SourceCodeInfo_Location {
+	if sourceInfo := m.GetSourceInfo(); sourceInfo != nil {
+		path := append(m.GetSourceInfo().Path, 4, apb.E_Http.Field) // options == 4
+		return pathLocation(m.GetFile(), path)
+	}
+	return nil
+}


Test patch:
diff --git a/locations/locations_test.go b/locations/locations_test.go
index 07d9eb3..a7ea8d4 100644
--- a/locations/locations_test.go
+++ b/locations/locations_test.go
@@ -21,6 +21,12 @@ import (
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/protoparse"
 	"github.com/lithammer/dedent"
+
+	// These imports cause the common protos to be registered with
+	// the protocol buffer registry, and therefore make the call to
+	// `proto.FileDescriptor` work for the imported files.
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	_ "google.golang.org/genproto/googleapis/longrunning"
 )
 
 func parse(t *testing.T, s string) *desc.FileDescriptor {
@@ -33,6 +39,7 @@ func parse(t *testing.T, s string) *desc.FileDescriptor {
 			"test.proto": strings.TrimSpace(dedent.Dedent(s)),
 		}),
 		IncludeSourceCodeInfo: true,
+		LookupImport:          desc.LoadFileDescriptor,
 	}
 	fds, err := parser.ParseFiles("test.proto")
 	if err != nil {
diff --git a/locations/method_locations_test.go b/locations/method_locations_test.go
index 0a2cd6d..4a4600b 100644
--- a/locations/method_locations_test.go
+++ b/locations/method_locations_test.go
@@ -29,6 +29,7 @@ func TestMethodRequestType(t *testing.T) {
 		message Book {}
 	`)
 	loc := MethodRequestType(f.GetServices()[0].GetMethods()[0])
+	// Three character span: line, start column, end column.
 	if diff := cmp.Diff(loc.GetSpan(), []int32{3, 14, 28}); diff != "" {
 		t.Errorf(diff)
 	}
@@ -43,7 +44,28 @@ func TestMethodResponseType(t *testing.T) {
 		message Book {}
 	`)
 	loc := MethodResponseType(f.GetServices()[0].GetMethods()[0])
+	// Three character span: line, start column, end column.
 	if diff := cmp.Diff(loc.GetSpan(), []int32{3, 39, 43}); diff != "" {
 		t.Errorf(diff)
 	}
 }
+
+func TestMethodHTTPRule(t *testing.T) {
+	f := parse(t, `
+		import "google/api/annotations.proto";
+		service Library {
+		  rpc GetBook(GetBookRequest) returns (Book) {
+		    option (google.api.http) = {
+		      get: "/v1/{name=publishers/*/books/*}"
+		    };
+		  }
+		}
+		message GetBookRequest{}
+		message Book {}
+	`)
+	loc := MethodHTTPRule(f.GetServices()[0].GetMethods()[0])
+	// Four character span: start line, start column, end line, end column.
+	if diff := cmp.Diff(loc.GetSpan(), []int32{5, 4, 7, 6}); diff != "" {
+		t.Errorf(diff)
+	}
+}