Repository: uber/prototool
Pull request #316 (2019-01-30 01:36:25)

Add linter to verify that messages are not empty except request and response types
Empty messages have no semantic value, they're generally used when people want a placeholder message for future additions. Instead, users should just not add the field that uses this message yet.

Re: #301.

codecov-io: # [Codecov](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=h1) Report
> Merging [#316](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=desc) into [dev](https://codecov.io/gh/uber/prototool/commit/a941763aa2448c7e15cfa42b4f72df69a6598ff1?src=pr&el=desc) will **increase** coverage by `0.21%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/uber/prototool/pull/316/graphs/tree.svg?width=650&token=A9V0DApqZg&height=150&src=pr)](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##              dev     #316      +/-   ##
==========================================
+ Coverage   73.15%   73.36%   +0.21%     
==========================================
  Files         138      139       +1     
  Lines        6202     6252      +50     
==========================================
+ Hits         4537     4587      +50     
  Misses       1246     1246              
  Partials      419      419
```


| [Impacted Files](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/lint/lint.go](https://codecov.io/gh/uber/prototool/pull/316/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbGludC9saW50Lmdv) | `71.01% <ø> (ø)` | :arrow_up: |
| [...essages\_not\_empty\_except\_request\_response\_types.go](https://codecov.io/gh/uber/prototool/pull/316/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbGludC9jaGVja19tZXNzYWdlc19ub3RfZW1wdHlfZXhjZXB0X3JlcXVlc3RfcmVzcG9uc2VfdHlwZXMuZ28=) | `100% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=footer). Last update [a941763...41ba396](https://codecov.io/gh/uber/prototool/pull/316?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/example/gen/go/uber/foo/v1/foo.pb.go b/example/gen/go/uber/foo/v1/foo.pb.go
index a6f8d61..33db00f 100644
--- a/example/gen/go/uber/foo/v1/foo.pb.go
+++ b/example/gen/go/uber/foo/v1/foo.pb.go
@@ -48,7 +48,7 @@ func (x Hello) String() string {
 	return proto.EnumName(Hello_name, int32(x))
 }
 func (Hello) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{0}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{0}
 }
 
 // Bar is a bar.
@@ -69,7 +69,7 @@ func (x Bar) String() string {
 	return proto.EnumName(Bar_name, int32(x))
 }
 func (Bar) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{1}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{1}
 }
 
 // Baz is a baz.
@@ -90,7 +90,7 @@ func (x Foo_Bar_Baz) String() string {
 	return proto.EnumName(Foo_Bar_Baz_name, int32(x))
 }
 func (Foo_Bar_Baz) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{0, 0, 0}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{0, 0, 0}
 }
 
 // Bat is a bat.
@@ -111,7 +111,7 @@ func (x Foo_Bar_Bat) String() string {
 	return proto.EnumName(Foo_Bar_Bat_name, int32(x))
 }
 func (Foo_Bar_Bat) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{0, 0, 1}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{0, 0, 1}
 }
 
 // Foo is a foo.
@@ -126,7 +126,7 @@ func (m *Foo) Reset()         { *m = Foo{} }
 func (m *Foo) String() string { return proto.CompactTextString(m) }
 func (*Foo) ProtoMessage()    {}
 func (*Foo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{0}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{0}
 }
 func (m *Foo) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Foo.Unmarshal(m, b)
@@ -166,7 +166,7 @@ func (m *Foo_Bar) Reset()         { *m = Foo_Bar{} }
 func (m *Foo_Bar) String() string { return proto.CompactTextString(m) }
 func (*Foo_Bar) ProtoMessage()    {}
 func (*Foo_Bar) Descriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{0, 0}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{0, 0}
 }
 func (m *Foo_Bar) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Foo_Bar.Unmarshal(m, b)
@@ -214,7 +214,7 @@ func (m *Barr) Reset()         { *m = Barr{} }
 func (m *Barr) String() string { return proto.CompactTextString(m) }
 func (*Barr) ProtoMessage()    {}
 func (*Barr) Descriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{1}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{1}
 }
 func (m *Barr) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Barr.Unmarshal(m, b)
@@ -278,7 +278,7 @@ func (m *Another) Reset()         { *m = Another{} }
 func (m *Another) String() string { return proto.CompactTextString(m) }
 func (*Another) ProtoMessage()    {}
 func (*Another) Descriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{2}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{2}
 }
 func (m *Another) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Another.Unmarshal(m, b)
@@ -468,7 +468,7 @@ func (m *HasWKT) Reset()         { *m = HasWKT{} }
 func (m *HasWKT) String() string { return proto.CompactTextString(m) }
 func (*HasWKT) ProtoMessage()    {}
 func (*HasWKT) Descriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{3}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{3}
 }
 func (m *HasWKT) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_HasWKT.Unmarshal(m, b)
@@ -497,6 +497,7 @@ func (m *HasWKT) GetWktDuration() *duration.Duration {
 
 // Bazz is a bazzzz.
 type Bazz struct {
+	One                  int64    `protobuf:"varint,1,opt,name=one,proto3" json:"one,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -506,7 +507,7 @@ func (m *Bazz) Reset()         { *m = Bazz{} }
 func (m *Bazz) String() string { return proto.CompactTextString(m) }
 func (*Bazz) ProtoMessage()    {}
 func (*Bazz) Descriptor() ([]byte, []int) {
-	return fileDescriptor_foo_18696cee75944a2c, []int{4}
+	return fileDescriptor_foo_cdfe0650a637b36d, []int{4}
 }
 func (m *Bazz) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Bazz.Unmarshal(m, b)
@@ -526,6 +527,13 @@ func (m *Bazz) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_Bazz proto.InternalMessageInfo
 
+func (m *Bazz) GetOne() int64 {
+	if m != nil {
+		return m.One
+	}
+	return 0
+}
+
 func init() {
 	proto.RegisterType((*Foo)(nil), "uber.foo.v1.Foo")
 	proto.RegisterType((*Foo_Bar)(nil), "uber.foo.v1.Foo.Bar")
@@ -540,48 +548,48 @@ func init() {
 	proto.RegisterEnum("uber.foo.v1.Foo_Bar_Bat", Foo_Bar_Bat_name, Foo_Bar_Bat_value)
 }
 
-func init() { proto.RegisterFile("uber/foo/v1/foo.proto", fileDescriptor_foo_18696cee75944a2c) }
-
-var fileDescriptor_foo_18696cee75944a2c = []byte{
-	// 625 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x53, 0x51, 0x6f, 0x12, 0x41,
-	0x10, 0xee, 0x71, 0x40, 0xcb, 0x9c, 0x6d, 0x71, 0x53, 0x9b, 0x13, 0x13, 0x6d, 0x78, 0x30, 0xd8,
-	0x87, 0x23, 0xa0, 0x89, 0xc6, 0xf8, 0xc2, 0xa5, 0x5c, 0x68, 0x44, 0xda, 0xac, 0xb4, 0x9a, 0xa6,
-	0x09, 0xd9, 0xd3, 0xa5, 0x25, 0x85, 0x9b, 0x66, 0xbb, 0xd0, 0x94, 0x1f, 0xe0, 0x9b, 0x7f, 0xc2,
-	0x47, 0xfd, 0x0d, 0xfe, 0x01, 0x7f, 0x95, 0x99, 0xdd, 0xbb, 0xd0, 0x2a, 0xd1, 0x17, 0x98, 0xf9,
-	0xf6, 0x9b, 0xd9, 0x6f, 0xbe, 0x9b, 0x85, 0x07, 0xd3, 0x58, 0xaa, 0xfa, 0x10, 0xb1, 0x3e, 0x6b,
-	0xd0, 0x5f, 0x70, 0xa9, 0x50, 0x23, 0xf3, 0x08, 0x0e, 0x28, 0x9f, 0x35, 0x2a, 0x8f, 0xcf, 0x10,
-	0xcf, 0xc6, 0xb2, 0x6e, 0x8e, 0xe2, 0xe9, 0xb0, 0xfe, 0x79, 0xaa, 0x84, 0x1e, 0x61, 0x62, 0xc9,
-	0x95, 0x27, 0x7f, 0x9e, 0xeb, 0xd1, 0x44, 0x5e, 0x69, 0x31, 0xb9, 0x4c, 0x09, 0xf6, 0x92, 0x58,
-	0x28, 0xba, 0x24, 0x16, 0xca, 0xc2, 0xd5, 0x9f, 0x0e, 0xb8, 0x11, 0x22, 0x7b, 0x0a, 0x6e, 0x2c,
-	0x94, 0xef, 0xec, 0x38, 0x35, 0xaf, 0xb9, 0x15, 0xdc, 0xba, 0x3a, 0x88, 0x10, 0x83, 0x50, 0x28,
-	0x4e, 0x84, 0xca, 0x57, 0x07, 0xdc, 0x50, 0x28, 0xb6, 0x4b, 0xfc, 0xb9, 0xe1, 0x6f, 0x34, 0xfd,
-	0x65, 0xfc, 0x20, 0x14, 0x73, 0xaa, 0x99, 0x5b, 0xae, 0xf6, 0x73, 0xff, 0xe4, 0x6a, 0xe2, 0xea,
-	0xea, 0x36, 0xb5, 0x9f, 0xb3, 0x4d, 0xf0, 0xc2, 0xd6, 0xc9, 0x60, 0xbf, 0x77, 0xdc, 0xea, 0xee,
-	0xef, 0x95, 0x57, 0x2c, 0xae, 0x2d, 0xde, 0xbf, 0x85, 0x7f, 0x71, 0x20, 0x1f, 0x0a, 0xa5, 0xd8,
-	0x16, 0x14, 0xce, 0xe5, 0x78, 0x8c, 0x46, 0x92, 0xcb, 0x6d, 0xc2, 0x5e, 0x42, 0x29, 0x16, 0x4a,
-	0x0d, 0xc8, 0x0d, 0x23, 0xc0, 0x6b, 0x56, 0x02, 0x6b, 0x55, 0x90, 0x59, 0x15, 0xf4, 0x33, 0xab,
-	0xf8, 0x1a, 0x91, 0x29, 0x65, 0x75, 0x53, 0x38, 0xb0, 0x2d, 0x5d, 0x53, 0xc8, 0xac, 0x72, 0xf2,
-	0x6e, 0xd6, 0x08, 0x3a, 0x74, 0x62, 0x0a, 0x4c, 0x54, 0xfd, 0xe1, 0xc2, 0x6a, 0x2b, 0x41, 0x7d,
-	0x2e, 0x15, 0x2b, 0x83, 0x8b, 0x89, 0x4c, 0x95, 0x50, 0x48, 0x88, 0xbe, 0x46, 0xa3, 0xa0, 0xc4,
-	0x29, 0x64, 0x01, 0xac, 0x0a, 0x4b, 0x4f, 0xdb, 0xdf, 0x35, 0x3d, 0x6d, 0xc5, 0x33, 0x12, 0x63,
-	0x90, 0x1f, 0xe2, 0x54, 0xf9, 0xf9, 0x1d, 0xb7, 0x56, 0xe2, 0x26, 0x66, 0xb5, 0x6c, 0xe6, 0x82,
-	0xb1, 0x96, 0xdd, 0xe9, 0x60, 0x05, 0xa6, 0x3e, 0x3c, 0x03, 0x67, 0xe2, 0x17, 0x77, 0xdc, 0x9a,
-	0xd7, 0x7c, 0xb4, 0xec, 0x9e, 0xe0, 0x5d, 0x3b, 0xd1, 0xea, 0x86, 0x3b, 0x13, 0xb6, 0x0d, 0x85,
-	0x2b, 0x39, 0x93, 0x89, 0xbf, 0x4a, 0xf2, 0x3b, 0x2b, 0xdc, 0xa6, 0x84, 0xcb, 0xd1, 0xd9, 0xb9,
-	0xf6, 0xd7, 0x68, 0x08, 0xc2, 0x4d, 0xca, 0x6a, 0x90, 0x4f, 0x46, 0x89, 0xf4, 0x4b, 0xa6, 0xfb,
-	0xf2, 0x29, 0x0c, 0x83, 0xed, 0x41, 0x39, 0x9d, 0x66, 0x90, 0x6d, 0xaf, 0x0f, 0x66, 0xf6, 0x87,
-	0x7f, 0x7d, 0x93, 0xbd, 0x94, 0xc0, 0x37, 0xd3, 0x92, 0x0c, 0xa8, 0xbc, 0x80, 0xa2, 0x15, 0x4b,
-	0xa6, 0x5e, 0xc8, 0x1b, 0x63, 0x73, 0x89, 0x53, 0x48, 0x4b, 0x30, 0x13, 0xe3, 0xa9, 0xfd, 0xd4,
-	0x2e, 0xb7, 0xc9, 0xeb, 0xdc, 0x2b, 0x27, 0x5c, 0x07, 0x0f, 0x13, 0x89, 0xc3, 0x81, 0xf9, 0xad,
-	0x46, 0x50, 0xec, 0x88, 0xab, 0x0f, 0x6f, 0xfb, 0xec, 0x0d, 0xdc, 0xbb, 0xbe, 0xd0, 0x0b, 0x41,
-	0xce, 0xff, 0x04, 0x79, 0xd7, 0x17, 0x3a, 0x4b, 0xaa, 0x45, 0xda, 0xbe, 0xf9, 0x7c, 0xb7, 0x07,
-	0x05, 0xe3, 0x37, 0xbb, 0x0f, 0xeb, 0x9d, 0x76, 0xb7, 0x7b, 0xb0, 0x58, 0x51, 0xda, 0x59, 0x0b,
-	0x1d, 0xf5, 0xde, 0xb7, 0xfb, 0x65, 0x87, 0x6d, 0x00, 0x58, 0xa0, 0xcf, 0xdb, 0xed, 0x72, 0x6e,
-	0x51, 0x13, 0xb6, 0xba, 0xdd, 0x83, 0x83, 0x5e, 0xd9, 0xdd, 0xdd, 0xb6, 0xaf, 0xcc, 0xac, 0x3b,
-	0x5f, 0xf4, 0x0a, 0x23, 0xd8, 0xfc, 0x84, 0x93, 0xdb, 0x1e, 0x87, 0x6b, 0x11, 0xe2, 0x21, 0xa9,
-	0x3c, 0x74, 0x4e, 0x0a, 0x43, 0xc4, 0x59, 0xe3, 0x5b, 0xce, 0x3d, 0x8a, 0x3e, 0x7e, 0xcf, 0x79,
-	0x47, 0x44, 0xa3, 0x27, 0x76, 0xdc, 0xf8, 0x65, 0xb3, 0xd3, 0x08, 0xf1, 0xf4, 0xb8, 0x11, 0x17,
-	0xcd, 0x5c, 0xcf, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xde, 0xd3, 0x9d, 0x7b, 0x04, 0x00,
-	0x00,
+func init() { proto.RegisterFile("uber/foo/v1/foo.proto", fileDescriptor_foo_cdfe0650a637b36d) }
+
+var fileDescriptor_foo_cdfe0650a637b36d = []byte{
+	// 630 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x53, 0xd1, 0x6e, 0x12, 0x4d,
+	0x14, 0xee, 0xb2, 0x40, 0xcb, 0xd9, 0xbf, 0x2d, 0xff, 0xa4, 0x36, 0x2b, 0x26, 0xda, 0x70, 0x61,
+	0xb0, 0x17, 0x4b, 0x40, 0x13, 0x8d, 0xf1, 0x86, 0x4d, 0xd9, 0xd0, 0x88, 0xb4, 0x19, 0x69, 0x35,
+	0x4d, 0x13, 0x32, 0xab, 0x43, 0x4b, 0x0a, 0x7b, 0x9a, 0xe9, 0x40, 0x53, 0x1e, 0xc0, 0x3b, 0x5f,
+	0xc2, 0x4b, 0x7d, 0x06, 0x5f, 0xc0, 0xa7, 0x32, 0x67, 0x66, 0x37, 0xb4, 0x96, 0xe8, 0x0d, 0x9c,
+	0xf3, 0xed, 0x77, 0xce, 0x7c, 0xe7, 0x9b, 0x33, 0xf0, 0x60, 0x1a, 0x4b, 0x55, 0x1f, 0x22, 0xd6,
+	0x67, 0x0d, 0xfa, 0x0b, 0x2e, 0x15, 0x6a, 0x64, 0x1e, 0xc1, 0x01, 0xe5, 0xb3, 0x46, 0xe5, 0xf1,
+	0x19, 0xe2, 0xd9, 0x58, 0xd6, 0xcd, 0xa7, 0x78, 0x3a, 0xac, 0x7f, 0x9e, 0x2a, 0xa1, 0x47, 0x98,
+	0x58, 0x72, 0xe5, 0xc9, 0x9f, 0xdf, 0xf5, 0x68, 0x22, 0xaf, 0xb4, 0x98, 0x5c, 0xa6, 0x04, 0x7b,
+	0x48, 0x2c, 0x14, 0x1d, 0x12, 0x0b, 0x65, 0xe1, 0xea, 0x4f, 0x07, 0xdc, 0x08, 0x91, 0x3d, 0x05,
+	0x37, 0x16, 0xca, 0x77, 0x76, 0x9c, 0x9a, 0xd7, 0xdc, 0x0a, 0x6e, 0x1d, 0x1d, 0x44, 0x88, 0x41,
+	0x28, 0x14, 0x27, 0x42, 0xe5, 0xab, 0x03, 0x6e, 0x28, 0x14, 0xdb, 0x25, 0xfe, 0xdc, 0xf0, 0x37,
+	0x9a, 0xfe, 0x32, 0x7e, 0x10, 0x8a, 0x39, 0xd5, 0xcc, 0x2d, 0x57, 0xfb, 0xb9, 0xbf, 0x72, 0x35,
+	0x71, 0x75, 0x75, 0x9b, 0xda, 0xcf, 0xd9, 0x26, 0x78, 0x61, 0xeb, 0x64, 0xb0, 0xdf, 0x3b, 0x6e,
+	0x75, 0xf7, 0xf7, 0xca, 0x2b, 0x16, 0xd7, 0x16, 0xef, 0xdf, 0xc2, 0xbf, 0x38, 0x90, 0x0f, 0x85,
+	0x52, 0x6c, 0x0b, 0x0a, 0xe7, 0x72, 0x3c, 0x46, 0x23, 0xc9, 0xe5, 0x36, 0x61, 0x2f, 0xa1, 0x14,
+	0x0b, 0xa5, 0x06, 0xe4, 0x86, 0x11, 0xe0, 0x35, 0x2b, 0x81, 0xb5, 0x2a, 0xc8, 0xac, 0x0a, 0xfa,
+	0x99, 0x55, 0x7c, 0x8d, 0xc8, 0x94, 0xb2, 0xba, 0x29, 0x1c, 0xd8, 0x96, 0xae, 0x29, 0x64, 0x56,
+	0x39, 0x79, 0x37, 0x6b, 0x04, 0x1d, 0xfa, 0x62, 0x0a, 0x4c, 0x54, 0xfd, 0xe1, 0xc2, 0x6a, 0x2b,
+	0x41, 0x7d, 0x2e, 0x15, 0x2b, 0x83, 0x8b, 0x89, 0x4c, 0x95, 0x50, 0x48, 0x88, 0xbe, 0x46, 0xa3,
+	0xa0, 0xc4, 0x29, 0x64, 0x01, 0xac, 0x0a, 0x4b, 0x4f, 0xdb, 0xdf, 0x35, 0x3d, 0x6d, 0xc5, 0x33,
+	0x12, 0x63, 0x90, 0x1f, 0xe2, 0x54, 0xf9, 0xf9, 0x1d, 0xb7, 0x56, 0xe2, 0x26, 0x66, 0xb5, 0x6c,
+	0xe6, 0x82, 0xb1, 0x96, 0xdd, 0xe9, 0x60, 0x05, 0xa6, 0x3e, 0x3c, 0x03, 0x67, 0xe2, 0x17, 0x77,
+	0xdc, 0x9a, 0xd7, 0x7c, 0xb4, 0xec, 0x9c, 0xe0, 0x5d, 0x3b, 0xd1, 0xea, 0x86, 0x3b, 0x13, 0xb6,
+	0x0d, 0x85, 0x2b, 0x39, 0x93, 0x89, 0xbf, 0x4a, 0xf2, 0x3b, 0x2b, 0xdc, 0xa6, 0x84, 0xcb, 0xd1,
+	0xd9, 0xb9, 0xf6, 0xd7, 0x68, 0x08, 0xc2, 0x4d, 0xca, 0x6a, 0x90, 0x4f, 0x46, 0x89, 0xf4, 0x4b,
+	0xa6, 0xfb, 0xf2, 0x29, 0x0c, 0x83, 0xed, 0x41, 0x39, 0x9d, 0x66, 0x90, 0x6d, 0xaf, 0x0f, 0x66,
+	0xf6, 0x87, 0xf7, 0xee, 0x64, 0x2f, 0x25, 0xf0, 0xcd, 0xb4, 0x24, 0x03, 0x2a, 0x2f, 0xa0, 0x68,
+	0xc5, 0x92, 0xa9, 0x17, 0xf2, 0xc6, 0xd8, 0x5c, 0xe2, 0x14, 0xd2, 0x12, 0xcc, 0xc4, 0x78, 0x6a,
+	0xaf, 0xda, 0xe5, 0x36, 0x79, 0x9d, 0x7b, 0xe5, 0x84, 0xeb, 0xe0, 0x61, 0x22, 0x71, 0x38, 0x30,
+	0xbf, 0xd5, 0x08, 0x8a, 0x1d, 0x71, 0xf5, 0xe1, 0x6d, 0x9f, 0xbd, 0x81, 0xff, 0xae, 0x2f, 0xf4,
+	0x42, 0x90, 0xf3, 0x2f, 0x41, 0xde, 0xf5, 0x85, 0xce, 0x92, 0xaa, 0x4f, 0xdb, 0x37, 0x9f, 0xdf,
+	0xbf, 0xf1, 0xdd, 0x1e, 0x14, 0xcc, 0x0d, 0xb0, 0xff, 0x61, 0xbd, 0xd3, 0xee, 0x76, 0x0f, 0x16,
+	0x4b, 0x4b, 0x5b, 0x6c, 0xa1, 0xa3, 0xde, 0xfb, 0x76, 0xbf, 0xec, 0xb0, 0x0d, 0x00, 0x0b, 0xf4,
+	0x79, 0xbb, 0x5d, 0xce, 0x2d, 0x6a, 0xc2, 0x56, 0xb7, 0x7b, 0x70, 0xd0, 0x2b, 0xbb, 0xbb, 0xdb,
+	0xf6, 0xdd, 0x99, 0x07, 0xc0, 0x17, 0xbd, 0xc2, 0x08, 0x36, 0x3f, 0xe1, 0xe4, 0xb6, 0xeb, 0xe1,
+	0x5a, 0x84, 0x78, 0x48, 0xba, 0x0f, 0x9d, 0x93, 0xc2, 0x10, 0x71, 0xd6, 0xf8, 0x96, 0x73, 0x8f,
+	0xa2, 0x8f, 0xdf, 0x73, 0xde, 0x11, 0xd1, 0xe8, 0xd1, 0x1d, 0x37, 0x7e, 0xd9, 0xec, 0x34, 0x42,
+	0x3c, 0x3d, 0x6e, 0xc4, 0x45, 0x33, 0xe9, 0xf3, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x60, 0x32,
+	0x17, 0x7b, 0x8d, 0x04, 0x00, 0x00,
 }
diff --git a/example/proto/uber/foo/v1/foo.proto b/example/proto/uber/foo/v1/foo.proto
index 142d56d..22c3c7b 100644
--- a/example/proto/uber/foo/v1/foo.proto
+++ b/example/proto/uber/foo/v1/foo.proto
@@ -94,4 +94,6 @@ message HasWKT {
 }
 
 // Bazz is a bazzzz.
-message Bazz {}
+message Bazz {
+  int64 one = 1;
+}
diff --git a/internal/lint/BUILD.bazel b/internal/lint/BUILD.bazel
index 4cd69c7..3297a50 100644
--- a/internal/lint/BUILD.bazel
+++ b/internal/lint/BUILD.bazel
@@ -38,6 +38,7 @@ go_library(
         "check_messages_have_comments.go",
         "check_messages_have_comments_except_request_response_types.go",
         "check_messages_have_sentence_comments_except_request_response_types.go",
+        "check_messages_not_empty_except_request_response_types.go",
         "check_names.go",
         "check_oneof_names_lower_snake_case.go",
         "check_package_is_declared.go",
diff --git a/internal/lint/check_messages_not_empty_except_request_response_types.go b/internal/lint/check_messages_not_empty_except_request_response_types.go
new file mode 100644
index 0000000..a07a446
--- /dev/null
+++ b/internal/lint/check_messages_not_empty_except_request_response_types.go
@@ -0,0 +1,129 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package lint
+
+import (
+	"strings"
+
+	"github.com/emicklei/proto"
+	"github.com/uber/prototool/internal/text"
+)
+
+var messagesNotEmptyExceptRequestResponseTypesLinter = NewLinter(
+	"MESSAGES_NOT_EMPTY_EXCEPT_REQUEST_RESPONSE_TYPES",
+	`Verifies that all messages except for request and response types are not empty.`,
+	checkMessagesNotEmptyExceptRequestResponseTypes,
+)
+
+func checkMessagesNotEmptyExceptRequestResponseTypes(add func(*text.Failure), dirPath string, descriptors []*FileDescriptor) error {
+	return runVisitor(&messagesNotEmptyExceptRequestResponseTypesVisitor{baseAddVisitor: newBaseAddVisitor(add)}, descriptors)
+}
+
+type messagesNotEmptyExceptRequestResponseTypesVisitor struct {
+	baseAddVisitor
+	messageNameToWrapper map[string]*messagesNotEmptyExceptRequestResponseTypesWrapper
+	requestResponseTypes map[string]struct{}
+	nestedMessageNames   []string
+	curFieldCount        int
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) OnStart(*FileDescriptor) error {
+	v.messageNameToWrapper = nil
+	v.requestResponseTypes = nil
+	v.nestedMessageNames = nil
+	v.curFieldCount = 0
+	return nil
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitMessage(message *proto.Message) {
+	v.nestedMessageNames = append(v.nestedMessageNames, message.Name)
+	oldFieldCount := v.curFieldCount
+	v.curFieldCount = 0
+	for _, child := range message.Elements {
+		child.Accept(v)
+	}
+	wrapper := &messagesNotEmptyExceptRequestResponseTypesWrapper{
+		message:    message,
+		fieldCount: v.curFieldCount,
+	}
+	v.curFieldCount = oldFieldCount
+	v.nestedMessageNames = v.nestedMessageNames[:len(v.nestedMessageNames)-1]
+
+	if v.messageNameToWrapper == nil {
+		v.messageNameToWrapper = make(map[string]*messagesNotEmptyExceptRequestResponseTypesWrapper)
+	}
+	if len(v.nestedMessageNames) > 0 {
+		v.messageNameToWrapper[strings.Join(v.nestedMessageNames, ".")+"."+message.Name] = wrapper
+	} else {
+		v.messageNameToWrapper[message.Name] = wrapper
+	}
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitOneof(oneof *proto.Oneof) {
+	for _, element := range oneof.Elements {
+		element.Accept(v)
+	}
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitNormalField(field *proto.NormalField) {
+	v.curFieldCount++
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitOneofField(field *proto.OneOfField) {
+	v.curFieldCount++
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitMapField(field *proto.MapField) {
+	v.curFieldCount++
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitService(service *proto.Service) {
+	for _, child := range service.Elements {
+		child.Accept(v)
+	}
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) VisitRPC(rpc *proto.RPC) {
+	if v.requestResponseTypes == nil {
+		v.requestResponseTypes = make(map[string]struct{})
+	}
+	v.requestResponseTypes[rpc.RequestType] = struct{}{}
+	v.requestResponseTypes[rpc.ReturnsType] = struct{}{}
+}
+
+func (v *messagesNotEmptyExceptRequestResponseTypesVisitor) Finally() error {
+	if v.messageNameToWrapper == nil {
+		v.messageNameToWrapper = make(map[string]*messagesNotEmptyExceptRequestResponseTypesWrapper)
+	}
+	for messageName, wrapper := range v.messageNameToWrapper {
+		if _, ok := v.requestResponseTypes[messageName]; !ok {
+			if wrapper.fieldCount == 0 {
+				v.AddFailuref(wrapper.message.Position, `Message %q should not be empty. Only request and response types are allowed to be empty.`, messageName)
+			}
+		}
+	}
+	return nil
+}
+
+type messagesNotEmptyExceptRequestResponseTypesWrapper struct {
+	message    *proto.Message
+	fieldCount int
+}
diff --git a/internal/lint/lint.go b/internal/lint/lint.go
index c26ddfa..b742cf6 100644
--- a/internal/lint/lint.go
+++ b/internal/lint/lint.go
@@ -90,6 +90,7 @@ var (
 		messagesHaveCommentsLinter,
 		messagesHaveCommentsExceptRequestResponseTypesLinter,
 		messagesHaveSentenceCommentsExceptRequestResponseTypesLinter,
+		messagesNotEmptyExceptRequestResponseTypesLinter,
 		namesNoCommonLinter,
 		namesNoDataLinter,
 		namesNoUUIDLinter,
@@ -218,6 +219,7 @@ var (
 		importsNotPublicLinter,
 		importsNotWeakLinter,
 		messagesHaveSentenceCommentsExceptRequestResponseTypesLinter,
+		messagesNotEmptyExceptRequestResponseTypesLinter,
 		messageFieldNamesLowerSnakeCaseLinter,
 		messageFieldsNoJSONNameLinter,
 		messageFieldsNotFloatsLinter,


Test patch:
diff --git a/internal/cmd/cmd_test.go b/internal/cmd/cmd_test.go
index 394de44..8162994 100644
--- a/internal/cmd/cmd_test.go
+++ b/internal/cmd/cmd_test.go
@@ -462,21 +462,36 @@ func TestLint(t *testing.T) {
 		26:3:NAMES_NO_UUID
 		27:5:NAMES_NO_UUID
 		28:7:NAMES_NO_UUID
-		33:1:NAMES_NO_COMMON
-		33:1:NAMES_NO_DATA
-		37:1:NAMES_NO_UUID
-		38:3:NAMES_NO_DATA
+		35:1:NAMES_NO_COMMON
+		35:1:NAMES_NO_DATA
+		39:1:NAMES_NO_UUID
 		40:3:NAMES_NO_DATA
-		41:5:NAMES_NO_COMMON
-		45:1:NAMES_NO_COMMON
-		46:3:NAMES_NO_COMMON
-		49:1:NAMES_NO_DATA
-		50:3:NAMES_NO_COMMON
-		53:1:NAMES_NO_UUID
-		54:3:NAMES_NO_COMMON`,
+		42:3:NAMES_NO_DATA
+		43:5:NAMES_NO_COMMON
+		47:1:NAMES_NO_COMMON
+		48:3:NAMES_NO_COMMON
+		51:1:NAMES_NO_DATA
+		52:3:NAMES_NO_COMMON
+		55:1:NAMES_NO_UUID
+		56:3:NAMES_NO_COMMON`,
 		"testdata/lint/naming/foo/v1/foo.proto",
 	)
 
+	assertDoLintFile(
+		t,
+		false,
+		`13:1:MESSAGES_NOT_EMPTY_EXCEPT_REQUEST_RESPONSE_TYPES
+		14:3:MESSAGES_NOT_EMPTY_EXCEPT_REQUEST_RESPONSE_TYPES
+		15:5:MESSAGES_NOT_EMPTY_EXCEPT_REQUEST_RESPONSE_TYPES`,
+		"testdata/lint/notempty/foo/v1/hello.proto",
+	)
+	assertDoLintFile(
+		t,
+		false,
+		``,
+		"testdata/lint/notempty/foo/v1/hello_api.proto",
+	)
+
 	assertDoLintFile(
 		t,
 		false,
@@ -537,7 +552,7 @@ func TestLint(t *testing.T) {
 		t,
 		false,
 		`23:1:REQUEST_RESPONSE_TYPES_ONLY_IN_FILE
-		27:1:REQUEST_RESPONSE_TYPES_ONLY_IN_FILE`,
+		30:1:REQUEST_RESPONSE_TYPES_ONLY_IN_FILE`,
 		"testdata/lint/onlyinfile/foo/v1/hello_api.proto",
 	)
 
diff --git a/internal/cmd/testdata/lint/naming/foo/v1/foo.proto b/internal/cmd/testdata/lint/naming/foo/v1/foo.proto
index 5a564ab..5aa5e0b 100644
--- a/internal/cmd/testdata/lint/naming/foo/v1/foo.proto
+++ b/internal/cmd/testdata/lint/naming/foo/v1/foo.proto
@@ -27,11 +27,13 @@ message Common {
     enum HelloUUID {
       HELLO_UUID_INVALID = 0;
     }
+    int64 dummy = 1;
   }
+  int64 dummy = 1;
 }
 
 message DataCommon {
-
+  int64 dummy = 1;
 }
 
 message UUID {
diff --git a/internal/cmd/testdata/lint/notempty/foo/v1/hello.proto b/internal/cmd/testdata/lint/notempty/foo/v1/hello.proto
new file mode 100644
index 0000000..f56bf80
--- /dev/null
+++ b/internal/cmd/testdata/lint/notempty/foo/v1/hello.proto
@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+package foo.v1;
+
+option csharp_namespace = "Foo.V1";
+option go_package = "foov1";
+option java_multiple_files = true;
+option java_outer_classname = "HelloProto";
+option java_package = "com.foo.v1";
+option objc_class_prefix = "FXX";
+option php_namespace = "Foo\\V1";
+
+message Foo {
+  message NestedFoo {
+    message NestedNestedFoo {}
+  }
+}
diff --git a/internal/cmd/testdata/lint/notempty/foo/v1/hello_api.proto b/internal/cmd/testdata/lint/notempty/foo/v1/hello_api.proto
new file mode 100644
index 0000000..07115dd
--- /dev/null
+++ b/internal/cmd/testdata/lint/notempty/foo/v1/hello_api.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package foo.v1;
+
+option csharp_namespace = "Foo.V1";
+option go_package = "foov1";
+option java_multiple_files = true;
+option java_outer_classname = "HelloApiProto";
+option java_package = "com.foo.v1";
+option objc_class_prefix = "FXX";
+option php_namespace = "Foo\\V1";
+
+service HelloAPI {
+  rpc SendFoo(SendFooRequest) returns (SendFooResponse);
+}
+
+message SendFooRequest {}
+
+message SendFooResponse {}
diff --git a/internal/cmd/testdata/lint/notempty/prototool.yaml b/internal/cmd/testdata/lint/notempty/prototool.yaml
new file mode 100644
index 0000000..ee80b43
--- /dev/null
+++ b/internal/cmd/testdata/lint/notempty/prototool.yaml
@@ -0,0 +1,8 @@
+lint:
+  group: uber2
+  rules:
+    remove:
+      - ENUMS_HAVE_SENTENCE_COMMENTS
+      - MESSAGES_HAVE_SENTENCE_COMMENTS_EXCEPT_REQUEST_RESPONSE_TYPES
+      - RPCS_HAVE_SENTENCE_COMMENTS
+      - SERVICES_HAVE_SENTENCE_COMMENTS
diff --git a/internal/cmd/testdata/lint/onlyinfile/foo/v1/hello_api.proto b/internal/cmd/testdata/lint/onlyinfile/foo/v1/hello_api.proto
index 1f0cb19..9ac71ef 100644
--- a/internal/cmd/testdata/lint/onlyinfile/foo/v1/hello_api.proto
+++ b/internal/cmd/testdata/lint/onlyinfile/foo/v1/hello_api.proto
@@ -21,7 +21,10 @@ message SendFooRequest {
 message SendFooResponse {}
 
 message Foo {
-  message NestedFoo {}
+  message NestedFoo {
+    int64 dummy = 1;
+  }
+  int64 dummy = 1;
 }
 
 enum Bar {