cmd/protoc-gen-go: generate message fields

This produces exactly the same output as github.com/golang/protobuf.

Change-Id: I01aacc9277c5cb5b4cc295f5ee8af12b4a524781
Reviewed-on: https://go-review.googlesource.com/134955
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go
index c243e00..1f24a1a 100644
--- a/cmd/protoc-gen-go/main.go
+++ b/cmd/protoc-gen-go/main.go
@@ -200,26 +200,55 @@
 
 	genWellKnownType(g, enum.GoIdent, enum.Desc)
 
-	// The name registered is, confusingly, <proto_package>.<go_ident>.
-	// This probably should have been the full name of the proto enum
-	// type instead, but changing it at this point would require thought.
-	regName := string(f.Desc.Package()) + "." + enum.GoIdent.GoName
 	f.init = append(f.init, fmt.Sprintf("%s(%q, %s, %s)",
 		g.QualifiedGoIdent(protogen.GoIdent{
 			GoImportPath: protoPackage,
 			GoName:       "RegisterEnum",
 		}),
-		regName, nameMap, valueMap,
+		enumRegistryName(enum), nameMap, valueMap,
 	))
 }
 
+// enumRegistryName returns the name used to register an enum with the proto
+// package registry.
+//
+// Confusingly, this is <proto_package>.<go_ident>. This probably should have
+// been the full name of the proto enum type instead, but changing it at this
+// point would require thought.
+func enumRegistryName(enum *protogen.Enum) string {
+	// Find the FileDescriptor for this enum.
+	var desc protoreflect.Descriptor = enum.Desc
+	for {
+		p, ok := desc.Parent()
+		if !ok {
+			break
+		}
+		desc = p
+	}
+	fdesc := desc.(protoreflect.FileDescriptor)
+	return string(fdesc.Package()) + "." + enum.GoIdent.GoName
+}
+
 func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, message *protogen.Message) {
-	for _, enum := range message.Enums {
-		genEnum(gen, g, f, enum)
+	for _, e := range message.Enums {
+		genEnum(gen, g, f, e)
 	}
 
 	genComment(g, f, message.Path)
+	// TODO: deprecation
 	g.P("type ", message.GoIdent, " struct {")
+	for _, field := range message.Fields {
+		if field.Desc.OneofType() != nil {
+			// TODO oneofs
+			continue
+		}
+		genComment(g, f, field.Path)
+		g.P(field.GoIdent, " ", fieldGoType(g, field), fmt.Sprintf(" `protobuf:%q json:%q`", fieldProtobufTag(field), fieldJSONTag(field)))
+	}
+	g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
+	// TODO XXX_InternalExtensions
+	g.P("XXX_unrecognized []byte `json:\"-\"`")
+	g.P("XXX_sizecache int32 `json:\"-\"`")
 	g.P("}")
 	g.P()
 
@@ -228,6 +257,123 @@
 	}
 }
 
+func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) string {
+	// TODO: map types
+	var typ string
+	switch field.Desc.Kind() {
+	case protoreflect.BoolKind:
+		typ = "bool"
+	case protoreflect.EnumKind:
+		typ = g.QualifiedGoIdent(field.EnumType.GoIdent)
+	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
+		typ = "int32"
+	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
+		typ = "uint32"
+	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
+		typ = "int64"
+	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
+		typ = "uint64"
+	case protoreflect.FloatKind:
+		typ = "float32"
+	case protoreflect.DoubleKind:
+		typ = "float64"
+	case protoreflect.StringKind:
+		typ = "string"
+	case protoreflect.BytesKind:
+		typ = "[]byte"
+	case protoreflect.MessageKind, protoreflect.GroupKind:
+		typ = "*" + g.QualifiedGoIdent(field.MessageType.GoIdent)
+	}
+	if field.Desc.Cardinality() == protoreflect.Repeated {
+		return "[]" + typ
+	}
+	if field.Desc.Syntax() == protoreflect.Proto3 {
+		return typ
+	}
+	if field.Desc.OneofType() != nil {
+		return typ
+	}
+	nonPointerKinds := map[protoreflect.Kind]bool{
+		protoreflect.GroupKind:   true,
+		protoreflect.MessageKind: true,
+		protoreflect.BytesKind:   true,
+	}
+	if !nonPointerKinds[field.Desc.Kind()] {
+		return "*" + typ
+	}
+	return typ
+}
+
+func fieldProtobufTag(field *protogen.Field) string {
+	var tag []string
+	// wire type
+	tag = append(tag, wireTypes[field.Desc.Kind()])
+	// field number
+	tag = append(tag, strconv.Itoa(int(field.Desc.Number())))
+	// cardinality
+	switch field.Desc.Cardinality() {
+	case protoreflect.Optional:
+		tag = append(tag, "opt")
+	case protoreflect.Required:
+		tag = append(tag, "req")
+	case protoreflect.Repeated:
+		tag = append(tag, "rep")
+	}
+	// TODO: default values
+	// TODO: packed
+	// name
+	name := string(field.Desc.Name())
+	if field.Desc.Kind() == protoreflect.GroupKind {
+		// The name of the FieldDescriptor for a group field is
+		// lowercased. To find the original capitalization, we
+		// look in the field's MessageType.
+		name = string(field.MessageType.Desc.Name())
+	}
+	tag = append(tag, "name="+name)
+	// JSON name
+	if jsonName := field.Desc.JSONName(); jsonName != "" && jsonName != name {
+		tag = append(tag, "json="+jsonName)
+	}
+	// proto3
+	if field.Desc.Syntax() == protoreflect.Proto3 {
+		tag = append(tag, "proto3")
+	}
+	// enum
+	if field.Desc.Kind() == protoreflect.EnumKind {
+		tag = append(tag, "enum="+enumRegistryName(field.EnumType))
+	}
+	// oneof
+	if field.Desc.OneofType() != nil {
+		tag = append(tag, "oneof")
+	}
+	return strings.Join(tag, ",")
+}
+
+var wireTypes = map[protoreflect.Kind]string{
+	protoreflect.BoolKind:     "varint",
+	protoreflect.EnumKind:     "varint",
+	protoreflect.Int32Kind:    "varint",
+	protoreflect.Sint32Kind:   "zigzag32",
+	protoreflect.Uint32Kind:   "varint",
+	protoreflect.Int64Kind:    "varint",
+	protoreflect.Sint64Kind:   "zigzag64",
+	protoreflect.Uint64Kind:   "varint",
+	protoreflect.Sfixed32Kind: "fixed32",
+	protoreflect.Fixed32Kind:  "fixed32",
+	protoreflect.FloatKind:    "fixed32",
+	protoreflect.Sfixed64Kind: "fixed64",
+	protoreflect.Fixed64Kind:  "fixed64",
+	protoreflect.DoubleKind:   "fixed64",
+	protoreflect.StringKind:   "bytes",
+	protoreflect.BytesKind:    "bytes",
+	protoreflect.MessageKind:  "bytes",
+	protoreflect.GroupKind:    "group",
+}
+
+func fieldJSONTag(field *protogen.Field) string {
+	return string(field.Desc.Name()) + ",omitempty"
+}
+
 func genComment(g *protogen.GeneratedFile, f *File, path []int32) {
 	for _, loc := range f.locationMap[pathKey(path)] {
 		if loc.LeadingComments == nil {
diff --git a/cmd/protoc-gen-go/testdata/comments/comments.pb.go b/cmd/protoc-gen-go/testdata/comments/comments.pb.go
index 2b870cd..c222dfd 100644
--- a/cmd/protoc-gen-go/testdata/comments/comments.pb.go
+++ b/cmd/protoc-gen-go/testdata/comments/comments.pb.go
@@ -7,26 +7,44 @@
 
 // COMMENT: Message1
 type Message1 struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 // COMMENT: Message1A
 type Message1_Message1A struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 // COMMENT: Message1B
 type Message1_Message1B struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 // COMMENT: Message2
 type Message2 struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 // COMMENT: Message2A
 type Message2_Message2A struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 // COMMENT: Message2B
 type Message2_Message2B struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 func init() { proto.RegisterFile("comments/comments.proto", fileDescriptor_885e8293f1fab554) }
diff --git a/cmd/protoc-gen-go/testdata/fieldnames/fieldnames.pb.go b/cmd/protoc-gen-go/testdata/fieldnames/fieldnames.pb.go
new file mode 100644
index 0000000..9596379
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/fieldnames/fieldnames.pb.go
@@ -0,0 +1,63 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: fieldnames/fieldnames.proto
+
+package fieldnames
+
+// Assorted edge cases in field name conflict resolution.
+//
+// Not all (or possibly any) of these behave in an easily-understood fashion.
+// This exists to demonstrate the current behavior and catch unintended
+// changes in it.
+type Message struct {
+	// Various CamelCase conversions.
+	FieldOne   *string `protobuf:"bytes,1,opt,name=field_one,json=fieldOne" json:"field_one,omitempty"`
+	FieldTwo   *string `protobuf:"bytes,2,opt,name=FieldTwo" json:"FieldTwo,omitempty"`
+	FieldThree *string `protobuf:"bytes,3,opt,name=fieldThree" json:"fieldThree,omitempty"`
+	Field_Four *string `protobuf:"bytes,4,opt,name=field__four,json=fieldFour" json:"field__four,omitempty"`
+	// Field names that conflict with standard methods on the message struct.
+	Descriptor_   *string `protobuf:"bytes,10,opt,name=descriptor" json:"descriptor,omitempty"`
+	Marshal_      *string `protobuf:"bytes,11,opt,name=marshal" json:"marshal,omitempty"`
+	Unmarshal_    *string `protobuf:"bytes,12,opt,name=unmarshal" json:"unmarshal,omitempty"`
+	ProtoMessage_ *string `protobuf:"bytes,13,opt,name=proto_message,json=protoMessage" json:"proto_message,omitempty"`
+	// Field names that conflict with each other after CamelCasing.
+	CamelCase    *string `protobuf:"bytes,20,opt,name=CamelCase" json:"CamelCase,omitempty"`
+	CamelCase_   *string `protobuf:"bytes,21,opt,name=CamelCase_,json=CamelCase" json:"CamelCase_,omitempty"`
+	CamelCase__  *string `protobuf:"bytes,22,opt,name=camel_case,json=camelCase" json:"camel_case,omitempty"`
+	CamelCase___ *string `protobuf:"bytes,23,opt,name=CamelCase__,json=CamelCase" json:"CamelCase__,omitempty"`
+	// Field with a getter that conflicts with another field.
+	GetName              *string  `protobuf:"bytes,30,opt,name=get_name,json=getName" json:"get_name,omitempty"`
+	Name_                *string  `protobuf:"bytes,31,opt,name=name" json:"name,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func init() { proto.RegisterFile("fieldnames/fieldnames.proto", fileDescriptor_6bbe3f70febb9403) }
+
+var fileDescriptor_6bbe3f70febb9403 = []byte{
+	// 382 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0x4b, 0x6f, 0xd3, 0x40,
+	0x10, 0xc7, 0x09, 0x44, 0x6a, 0x3c, 0x69, 0x79, 0xac, 0x78, 0x6c, 0x69, 0x31, 0x08, 0x2e, 0x39,
+	0x50, 0x5b, 0x82, 0x33, 0x42, 0x24, 0x52, 0xc5, 0x85, 0x56, 0x8a, 0x72, 0xe2, 0xb2, 0x5a, 0xd6,
+	0xe3, 0x4d, 0x24, 0x7b, 0x27, 0x5a, 0x6f, 0xc4, 0x17, 0xe5, 0x03, 0x21, 0xcf, 0xc6, 0x76, 0x94,
+	0xe6, 0x36, 0xf3, 0xfb, 0x3f, 0xac, 0xb1, 0x0d, 0x57, 0xe5, 0x06, 0xab, 0xc2, 0xe9, 0x1a, 0x9b,
+	0x7c, 0x18, 0xb3, 0xad, 0xa7, 0x40, 0xe2, 0xd2, 0x12, 0x0f, 0x71, 0x35, 0xd9, 0x60, 0xf8, 0xf8,
+	0x6f, 0x0c, 0x67, 0xbf, 0xb0, 0x69, 0xb4, 0x45, 0x71, 0x05, 0x09, 0x2b, 0x8a, 0x1c, 0xca, 0xd1,
+	0x87, 0xd1, 0x2c, 0x59, 0x4e, 0x18, 0xdc, 0x3b, 0x14, 0x6f, 0x61, 0x72, 0xdb, 0xce, 0xab, 0xbf,
+	0x24, 0x1f, 0x47, 0xad, 0xdb, 0x45, 0x0a, 0xc0, 0xbe, 0xd5, 0xda, 0x23, 0xca, 0x27, 0xac, 0x1e,
+	0x10, 0x91, 0xc2, 0x34, 0x16, 0xab, 0x92, 0x76, 0x5e, 0x8e, 0xd9, 0x10, 0x9f, 0x75, 0x4b, 0x3b,
+	0xdf, 0xe6, 0x0b, 0x6c, 0x8c, 0xdf, 0x6c, 0x03, 0x79, 0x09, 0x31, 0x3f, 0x10, 0x21, 0xe1, 0xac,
+	0xd6, 0xbe, 0x59, 0xeb, 0x4a, 0x4e, 0x59, 0xec, 0x56, 0x71, 0x0d, 0xc9, 0xce, 0x75, 0xda, 0x79,
+	0xec, 0xed, 0x81, 0xf8, 0x04, 0x17, 0x7c, 0xb1, 0xaa, 0xe3, 0x85, 0xf2, 0x82, 0x1d, 0xe7, 0x0c,
+	0xbb, 0xab, 0xaf, 0x21, 0x59, 0xe8, 0x1a, 0xab, 0x85, 0x6e, 0x50, 0xbe, 0x8c, 0x15, 0x3d, 0x10,
+	0xef, 0x00, 0xfa, 0x45, 0xc9, 0x57, 0x27, 0x64, 0xd3, 0x2e, 0xca, 0xb4, 0xe9, 0xd7, 0x51, 0x36,
+	0xbd, 0x9c, 0xc2, 0x74, 0x48, 0x2b, 0xf9, 0xe6, 0x38, 0x7e, 0x09, 0x13, 0x8b, 0x41, 0xb5, 0x9f,
+	0x42, 0xa6, 0xf1, 0x32, 0x8b, 0xe1, 0x4e, 0xd7, 0x28, 0x04, 0x8c, 0x19, 0xbf, 0x67, 0xcc, 0xb3,
+	0x98, 0xc1, 0xd3, 0x7b, 0x87, 0x54, 0x2e, 0xc8, 0x95, 0xd5, 0xc6, 0x84, 0x1f, 0x72, 0xd6, 0xaa,
+	0x3f, 0x1f, 0x2d, 0x8f, 0xb8, 0xf8, 0x0c, 0x2f, 0xa8, 0x25, 0xca, 0x91, 0x32, 0x7b, 0x2a, 0xbf,
+	0xb0, 0x79, 0xb4, 0x7c, 0xc6, 0xd2, 0x1d, 0x75, 0xf6, 0x07, 0xbd, 0x73, 0xf9, 0x75, 0x6f, 0x3d,
+	0xe2, 0x73, 0x01, 0xcf, 0x63, 0x6f, 0x57, 0xaa, 0xf4, 0x09, 0xf6, 0x67, 0xfe, 0xfd, 0xf7, 0x37,
+	0x4b, 0x64, 0x2b, 0xcc, 0x2c, 0x55, 0xda, 0xd9, 0x8c, 0xbc, 0xcd, 0xf9, 0xb5, 0xe7, 0xa6, 0x2e,
+	0xe2, 0x64, 0x6e, 0x2c, 0xba, 0x1b, 0x4b, 0x79, 0xc0, 0x26, 0x14, 0x3a, 0xe8, 0x83, 0x1f, 0xf7,
+	0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x92, 0xbf, 0xac, 0x4e, 0xd0, 0x02, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/fieldnames/fieldnames.proto b/cmd/protoc-gen-go/testdata/fieldnames/fieldnames.proto
new file mode 100644
index 0000000..9b4b917
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/fieldnames/fieldnames.proto
@@ -0,0 +1,49 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+syntax = "proto2";
+
+package goproto.protoc.fieldnames;
+
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/fieldnames";
+
+// Assorted edge cases in field name conflict resolution.
+//
+// Not all (or possibly any) of these behave in an easily-understood fashion.
+// This exists to demonstrate the current behavior and catch unintended
+// changes in it.
+message Message {
+  // Various CamelCase conversions.
+  optional string field_one = 1;
+  optional string FieldTwo = 2;
+  optional string fieldThree = 3;
+  optional string field__four = 4;
+
+  // Field names that conflict with standard methods on the message struct.
+  optional string descriptor = 10;
+  optional string marshal = 11;
+  optional string unmarshal = 12;
+  optional string proto_message = 13;
+
+  // Field names that conflict with each other after CamelCasing.
+  optional string CamelCase = 20;
+  optional string CamelCase_ = 21;
+  optional string camel_case = 22; // conflicts with 20, 21
+  optional string CamelCase__ = 23; // conflicts with 21, 21, renamed 22
+
+  // Field with a getter that conflicts with another field.
+  optional string get_name = 30;
+  optional string name = 31;
+
+  // Oneof that conflicts with its first field: The oneof is renamed.
+  oneof oneof_conflict_a {
+    string OneofConflictA = 40;
+  }
+
+  // Oneof that conflicts with its second field: The field is renamed.
+  oneof oneof_conflict_b {
+    string oneof_no_conflict = 50;
+    string OneofConflictB = 51;
+  }
+}
diff --git a/cmd/protoc-gen-go/testdata/proto2/enum.pb.go b/cmd/protoc-gen-go/testdata/proto2/enum.pb.go
index 85b2fad..e8e053c 100644
--- a/cmd/protoc-gen-go/testdata/proto2/enum.pb.go
+++ b/cmd/protoc-gen-go/testdata/proto2/enum.pb.go
@@ -165,6 +165,9 @@
 }
 
 type EnumContainerMessage1 struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 // NestedEnumType2A comment.
@@ -244,6 +247,9 @@
 }
 
 type EnumContainerMessage1_EnumContainerMessage2 struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 func init() {
diff --git a/cmd/protoc-gen-go/testdata/proto2/fields.pb.go b/cmd/protoc-gen-go/testdata/proto2/fields.pb.go
new file mode 100644
index 0000000..dee8e9f
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto2/fields.pb.go
@@ -0,0 +1,201 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: proto2/fields.proto
+
+package proto2
+
+import proto "github.com/golang/protobuf/proto"
+
+type FieldTestMessage_Enum int32
+
+const (
+	FieldTestMessage_ZERO FieldTestMessage_Enum = 0
+)
+
+var FieldTestMessage_Enum_name = map[int32]string{
+	0: "ZERO",
+}
+
+var FieldTestMessage_Enum_value = map[string]int32{
+	"ZERO": 0,
+}
+
+func (x FieldTestMessage_Enum) Enum() *FieldTestMessage_Enum {
+	p := new(FieldTestMessage_Enum)
+	*p = x
+	return p
+}
+
+func (x FieldTestMessage_Enum) String() string {
+	return proto.EnumName(FieldTestMessage_Enum_name, int32(x))
+}
+
+func (x *FieldTestMessage_Enum) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(FieldTestMessage_Enum_value, data, "FieldTestMessage_Enum")
+	if err != nil {
+		return err
+	}
+	*x = FieldTestMessage_Enum(value)
+	return nil
+}
+
+func (FieldTestMessage_Enum) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_fd8a9d72b841fd68, []int{0, 0}
+}
+
+type FieldTestMessage struct {
+	OptionalBool         *string                           `protobuf:"bytes,1,opt,name=optional_bool,json=optionalBool" json:"optional_bool,omitempty"`
+	OptionalEnum         *FieldTestMessage_Enum            `protobuf:"varint,2,opt,name=optional_enum,json=optionalEnum,enum=goproto.protoc.proto2.FieldTestMessage_Enum" json:"optional_enum,omitempty"`
+	OptionalInt32        *int32                            `protobuf:"varint,3,opt,name=optional_int32,json=optionalInt32" json:"optional_int32,omitempty"`
+	OptionalSint32       *int32                            `protobuf:"zigzag32,4,opt,name=optional_sint32,json=optionalSint32" json:"optional_sint32,omitempty"`
+	OptionalUint32       *uint32                           `protobuf:"varint,5,opt,name=optional_uint32,json=optionalUint32" json:"optional_uint32,omitempty"`
+	OptionalInt64        *int64                            `protobuf:"varint,6,opt,name=optional_int64,json=optionalInt64" json:"optional_int64,omitempty"`
+	OptionalSint64       *int64                            `protobuf:"zigzag64,7,opt,name=optional_sint64,json=optionalSint64" json:"optional_sint64,omitempty"`
+	OptionalUint64       *uint64                           `protobuf:"varint,8,opt,name=optional_uint64,json=optionalUint64" json:"optional_uint64,omitempty"`
+	OptionalSfixed32     *int32                            `protobuf:"fixed32,9,opt,name=optional_sfixed32,json=optionalSfixed32" json:"optional_sfixed32,omitempty"`
+	OptionalFixed32      *uint32                           `protobuf:"fixed32,10,opt,name=optional_fixed32,json=optionalFixed32" json:"optional_fixed32,omitempty"`
+	OptionalFloat        *float32                          `protobuf:"fixed32,11,opt,name=optional_float,json=optionalFloat" json:"optional_float,omitempty"`
+	OptionalSfixed64     *int64                            `protobuf:"fixed64,12,opt,name=optional_sfixed64,json=optionalSfixed64" json:"optional_sfixed64,omitempty"`
+	OptionalFixed64      *uint64                           `protobuf:"fixed64,13,opt,name=optional_fixed64,json=optionalFixed64" json:"optional_fixed64,omitempty"`
+	OptionalDouble       *float64                          `protobuf:"fixed64,14,opt,name=optional_double,json=optionalDouble" json:"optional_double,omitempty"`
+	OptionalString       *string                           `protobuf:"bytes,15,opt,name=optional_string,json=optionalString" json:"optional_string,omitempty"`
+	OptionalBytes        []byte                            `protobuf:"bytes,16,opt,name=optional_bytes,json=optionalBytes" json:"optional_bytes,omitempty"`
+	Optional_Message     *FieldTestMessage_Message         `protobuf:"bytes,17,opt,name=optional_Message,json=optionalMessage" json:"optional_Message,omitempty"`
+	Optionalgroup        *FieldTestMessage_OptionalGroup   `protobuf:"group,18,opt,name=OptionalGroup,json=optionalgroup" json:"optionalgroup,omitempty"`
+	RequiredBool         *string                           `protobuf:"bytes,101,req,name=required_bool,json=requiredBool" json:"required_bool,omitempty"`
+	RequiredEnum         *FieldTestMessage_Enum            `protobuf:"varint,102,req,name=required_enum,json=requiredEnum,enum=goproto.protoc.proto2.FieldTestMessage_Enum" json:"required_enum,omitempty"`
+	RequiredInt32        *int32                            `protobuf:"varint,103,req,name=required_int32,json=requiredInt32" json:"required_int32,omitempty"`
+	RequiredSint32       *int32                            `protobuf:"zigzag32,104,req,name=required_sint32,json=requiredSint32" json:"required_sint32,omitempty"`
+	RequiredUint32       *uint32                           `protobuf:"varint,105,req,name=required_uint32,json=requiredUint32" json:"required_uint32,omitempty"`
+	RequiredInt64        *int64                            `protobuf:"varint,106,req,name=required_int64,json=requiredInt64" json:"required_int64,omitempty"`
+	RequiredSint64       *int64                            `protobuf:"zigzag64,107,req,name=required_sint64,json=requiredSint64" json:"required_sint64,omitempty"`
+	RequiredUint64       *uint64                           `protobuf:"varint,108,req,name=required_uint64,json=requiredUint64" json:"required_uint64,omitempty"`
+	RequiredSfixed32     *int32                            `protobuf:"fixed32,109,req,name=required_sfixed32,json=requiredSfixed32" json:"required_sfixed32,omitempty"`
+	RequiredFixed32      *uint32                           `protobuf:"fixed32,110,req,name=required_fixed32,json=requiredFixed32" json:"required_fixed32,omitempty"`
+	RequiredFloat        *float32                          `protobuf:"fixed32,111,req,name=required_float,json=requiredFloat" json:"required_float,omitempty"`
+	RequiredSfixed64     *int64                            `protobuf:"fixed64,112,req,name=required_sfixed64,json=requiredSfixed64" json:"required_sfixed64,omitempty"`
+	RequiredFixed64      *uint64                           `protobuf:"fixed64,113,req,name=required_fixed64,json=requiredFixed64" json:"required_fixed64,omitempty"`
+	RequiredDouble       *float64                          `protobuf:"fixed64,114,req,name=required_double,json=requiredDouble" json:"required_double,omitempty"`
+	RequiredString       *string                           `protobuf:"bytes,115,req,name=required_string,json=requiredString" json:"required_string,omitempty"`
+	RequiredBytes        []byte                            `protobuf:"bytes,116,req,name=required_bytes,json=requiredBytes" json:"required_bytes,omitempty"`
+	Required_Message     *FieldTestMessage_Message         `protobuf:"bytes,117,req,name=required_Message,json=requiredMessage" json:"required_Message,omitempty"`
+	Requiredgroup        *FieldTestMessage_RequiredGroup   `protobuf:"group,118,req,name=RequiredGroup,json=requiredgroup" json:"requiredgroup,omitempty"`
+	RepeatedBool         []string                          `protobuf:"bytes,201,rep,name=repeated_bool,json=repeatedBool" json:"repeated_bool,omitempty"`
+	RepeatedEnum         []FieldTestMessage_Enum           `protobuf:"varint,202,rep,name=repeated_enum,json=repeatedEnum,enum=goproto.protoc.proto2.FieldTestMessage_Enum" json:"repeated_enum,omitempty"`
+	RepeatedInt32        []int32                           `protobuf:"varint,203,rep,name=repeated_int32,json=repeatedInt32" json:"repeated_int32,omitempty"`
+	RepeatedSint32       []int32                           `protobuf:"zigzag32,204,rep,name=repeated_sint32,json=repeatedSint32" json:"repeated_sint32,omitempty"`
+	RepeatedUint32       []uint32                          `protobuf:"varint,205,rep,name=repeated_uint32,json=repeatedUint32" json:"repeated_uint32,omitempty"`
+	RepeatedInt64        []int64                           `protobuf:"varint,206,rep,name=repeated_int64,json=repeatedInt64" json:"repeated_int64,omitempty"`
+	RepeatedSint64       []int64                           `protobuf:"zigzag64,207,rep,name=repeated_sint64,json=repeatedSint64" json:"repeated_sint64,omitempty"`
+	RepeatedUint64       []uint64                          `protobuf:"varint,208,rep,name=repeated_uint64,json=repeatedUint64" json:"repeated_uint64,omitempty"`
+	RepeatedSfixed32     []int32                           `protobuf:"fixed32,209,rep,name=repeated_sfixed32,json=repeatedSfixed32" json:"repeated_sfixed32,omitempty"`
+	RepeatedFixed32      []uint32                          `protobuf:"fixed32,210,rep,name=repeated_fixed32,json=repeatedFixed32" json:"repeated_fixed32,omitempty"`
+	RepeatedFloat        []float32                         `protobuf:"fixed32,211,rep,name=repeated_float,json=repeatedFloat" json:"repeated_float,omitempty"`
+	RepeatedSfixed64     []int64                           `protobuf:"fixed64,212,rep,name=repeated_sfixed64,json=repeatedSfixed64" json:"repeated_sfixed64,omitempty"`
+	RepeatedFixed64      []uint64                          `protobuf:"fixed64,213,rep,name=repeated_fixed64,json=repeatedFixed64" json:"repeated_fixed64,omitempty"`
+	RepeatedDouble       []float64                         `protobuf:"fixed64,214,rep,name=repeated_double,json=repeatedDouble" json:"repeated_double,omitempty"`
+	RepeatedString       []string                          `protobuf:"bytes,215,rep,name=repeated_string,json=repeatedString" json:"repeated_string,omitempty"`
+	RepeatedBytes        [][]byte                          `protobuf:"bytes,216,rep,name=repeated_bytes,json=repeatedBytes" json:"repeated_bytes,omitempty"`
+	Repeated_Message     []*FieldTestMessage_Message       `protobuf:"bytes,217,rep,name=repeated_Message,json=repeatedMessage" json:"repeated_Message,omitempty"`
+	Repeatedgroup        []*FieldTestMessage_RepeatedGroup `protobuf:"group,218,rep,name=RepeatedGroup,json=repeatedgroup" json:"repeatedgroup,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                          `json:"-"`
+	XXX_unrecognized     []byte                            `json:"-"`
+	XXX_sizecache        int32                             `json:"-"`
+}
+
+type FieldTestMessage_OptionalGroup struct {
+	OptionalGroup        *string  `protobuf:"bytes,19,opt,name=optional_group,json=optionalGroup" json:"optional_group,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+type FieldTestMessage_RequiredGroup struct {
+	RequiredGroup        *string  `protobuf:"bytes,119,req,name=required_group,json=requiredGroup" json:"required_group,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+type FieldTestMessage_RepeatedGroup struct {
+	RepeatedGroup        []string `protobuf:"bytes,219,rep,name=repeated_group,json=repeatedGroup" json:"repeated_group,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+type FieldTestMessage_Message struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func init() {
+	proto.RegisterEnum("goproto.protoc.proto2.FieldTestMessage_Enum", FieldTestMessage_Enum_name, FieldTestMessage_Enum_value)
+}
+
+func init() { proto.RegisterFile("proto2/fields.proto", fileDescriptor_fd8a9d72b841fd68) }
+
+var fileDescriptor_fd8a9d72b841fd68 = []byte{
+	// 969 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x96, 0xdb, 0x6f, 0x1b, 0x45,
+	0x14, 0xc6, 0x99, 0x89, 0x9b, 0x34, 0xdb, 0x38, 0x71, 0xb6, 0x02, 0x8d, 0x78, 0x3a, 0x2a, 0xb7,
+	0x01, 0x5a, 0x5b, 0x72, 0x87, 0x41, 0x88, 0xb7, 0x88, 0xa6, 0xe2, 0x01, 0x55, 0x2c, 0xf0, 0x92,
+	0x22, 0x55, 0x4e, 0xbc, 0x59, 0x0c, 0x1b, 0x8f, 0x6b, 0xaf, 0xb9, 0xfc, 0x7d, 0x3c, 0x71, 0xbf,
+	0xdf, 0xff, 0x19, 0x74, 0xe6, 0xcc, 0x65, 0x67, 0xdd, 0x87, 0x26, 0x4f, 0xb6, 0x4f, 0xbe, 0xf3,
+	0x7d, 0x73, 0xc6, 0xe7, 0xe7, 0x4d, 0x76, 0x73, 0xb1, 0x34, 0x8d, 0x19, 0x8f, 0xce, 0x67, 0x65,
+	0x3d, 0x5d, 0x0d, 0xed, 0xa7, 0xfc, 0xd9, 0xca, 0xd8, 0x37, 0xf4, 0xf1, 0x8c, 0x5e, 0xc6, 0xb7,
+	0xbe, 0x7e, 0x2e, 0x1b, 0x1c, 0xa3, 0xee, 0xc3, 0x72, 0xd5, 0xbc, 0x57, 0xae, 0x56, 0x93, 0xaa,
+	0xcc, 0x5f, 0xc8, 0xfa, 0x66, 0xd1, 0xcc, 0xcc, 0x7c, 0x52, 0x3f, 0x3a, 0x35, 0xa6, 0x16, 0x0c,
+	0x98, 0xdc, 0x2d, 0xf6, 0x7c, 0xf1, 0xc8, 0x98, 0x3a, 0x7f, 0xbf, 0x25, 0x2a, 0xe7, 0xeb, 0x0b,
+	0xc1, 0x81, 0xc9, 0xfd, 0xf1, 0xed, 0xe1, 0x13, 0x83, 0x86, 0xdd, 0x90, 0xe1, 0xbd, 0xf9, 0xfa,
+	0x22, 0x5a, 0xe2, 0xa7, 0xfc, 0xa5, 0x6c, 0x3f, 0x58, 0xce, 0xe6, 0xcd, 0xdd, 0xb1, 0xd8, 0x02,
+	0x26, 0xaf, 0x15, 0x21, 0xe8, 0x5d, 0x2c, 0xe6, 0xaf, 0x64, 0x07, 0x41, 0xb6, 0x22, 0x5d, 0x0f,
+	0x98, 0x3c, 0x2c, 0x42, 0xf7, 0x07, 0xb3, 0x0d, 0xe1, 0x9a, 0x84, 0xd7, 0x80, 0xc9, 0x7e, 0x14,
+	0x7e, 0x44, 0xc2, 0x4e, 0xb0, 0x56, 0x62, 0x1b, 0x98, 0xdc, 0x4a, 0x82, 0xb5, 0xda, 0x08, 0xd6,
+	0x4a, 0xec, 0x00, 0x93, 0x79, 0x1a, 0xdc, 0x11, 0xae, 0x49, 0x78, 0x1d, 0x98, 0xec, 0xa5, 0xc1,
+	0x5a, 0xe5, 0xaf, 0x67, 0x87, 0xd1, 0xf1, 0x7c, 0xf6, 0x65, 0x39, 0xbd, 0x3b, 0x16, 0xbb, 0xc0,
+	0xe4, 0x41, 0x31, 0x08, 0x9e, 0xae, 0x9e, 0xbf, 0x9a, 0x85, 0xda, 0x23, 0xaf, 0xcd, 0x80, 0xc9,
+	0x9d, 0x22, 0xa4, 0x1d, 0x3b, 0x69, 0x7b, 0xa0, 0xf3, 0xda, 0x4c, 0x1a, 0x71, 0x03, 0x98, 0xe4,
+	0x71, 0xa0, 0x63, 0x2c, 0x3e, 0x21, 0x5e, 0x2b, 0xb1, 0x07, 0x4c, 0x0e, 0xba, 0xf1, 0x5a, 0x6d,
+	0xc6, 0x6b, 0x25, 0xfa, 0xc0, 0xe4, 0x76, 0x27, 0xbe, 0x33, 0xff, 0xd4, 0xac, 0x4f, 0xeb, 0x52,
+	0xec, 0x03, 0x93, 0x2c, 0xce, 0xff, 0x8e, 0xad, 0xa6, 0x37, 0xda, 0x2c, 0x67, 0xf3, 0x4a, 0x1c,
+	0xd8, 0x5d, 0x8b, 0x37, 0x6a, 0xab, 0xc9, 0x40, 0xa7, 0x5f, 0x35, 0xe5, 0x4a, 0x0c, 0x80, 0xc9,
+	0xbd, 0x38, 0xd0, 0x11, 0x16, 0xf3, 0x93, 0xd6, 0x19, 0xdd, 0xa2, 0x89, 0x43, 0x60, 0xf2, 0xc6,
+	0x78, 0xf4, 0xb4, 0x7b, 0xe9, 0x5e, 0xe3, 0x50, 0x9e, 0x8a, 0x87, 0x71, 0xe1, 0xab, 0xa5, 0x59,
+	0x2f, 0x44, 0x0e, 0x4c, 0x66, 0xe3, 0x37, 0x9e, 0xd6, 0xf8, 0x81, 0x6b, 0xbe, 0x8f, 0xcd, 0x45,
+	0xea, 0x85, 0xc8, 0x2d, 0xcb, 0xc7, 0xeb, 0xd9, 0xb2, 0x9c, 0x12, 0x72, 0x25, 0x70, 0x44, 0xce,
+	0x17, 0x3d, 0x72, 0x41, 0x64, 0x91, 0x3b, 0x07, 0x7e, 0x79, 0xe4, 0xbc, 0x85, 0x47, 0x2e, 0x58,
+	0x12, 0x21, 0x15, 0x70, 0x44, 0xce, 0x57, 0x03, 0x72, 0x41, 0xe6, 0x90, 0xfb, 0x04, 0x38, 0x22,
+	0xe7, 0xcb, 0x11, 0xb9, 0x20, 0x74, 0xc8, 0xcd, 0x80, 0x23, 0x72, 0xbe, 0x1c, 0x91, 0x6b, 0x07,
+	0x6b, 0x25, 0x3e, 0x05, 0x8e, 0xc8, 0xb5, 0x82, 0x69, 0x93, 0x92, 0x60, 0xad, 0xc4, 0x67, 0xc0,
+	0x11, 0xb9, 0x76, 0x70, 0x47, 0xe8, 0x90, 0xab, 0x81, 0x23, 0x72, 0xed, 0x60, 0x42, 0x2e, 0x3a,
+	0x7a, 0x8c, 0x2e, 0x80, 0x23, 0x72, 0xc1, 0xb3, 0x85, 0x5c, 0x10, 0x7b, 0xed, 0x1c, 0x38, 0x22,
+	0xe7, 0xeb, 0x2d, 0xe4, 0xa2, 0xd4, 0x22, 0x67, 0x80, 0x23, 0x72, 0x41, 0xe8, 0x91, 0xeb, 0xc4,
+	0x6b, 0x25, 0x16, 0xc0, 0x11, 0xb9, 0x34, 0x9e, 0x90, 0x4b, 0xe3, 0xb5, 0x12, 0x8f, 0x81, 0x23,
+	0x72, 0x49, 0x7c, 0x67, 0x7e, 0x87, 0xdc, 0x12, 0x38, 0x22, 0xe7, 0xcb, 0x11, 0xb9, 0x78, 0x00,
+	0x42, 0x6e, 0x65, 0x77, 0x2d, 0xde, 0x68, 0x40, 0x2e, 0xae, 0xa4, 0x45, 0xae, 0x01, 0x8e, 0xc8,
+	0x85, 0x9d, 0xf4, 0xc8, 0x05, 0x99, 0x47, 0x6e, 0x0d, 0xfc, 0x4a, 0xc8, 0x79, 0xa3, 0x16, 0x72,
+	0xbe, 0x44, 0xc8, 0x7d, 0x0e, 0xfc, 0x32, 0xc8, 0x15, 0xae, 0xd9, 0x21, 0x97, 0x78, 0xe5, 0x2f,
+	0xa2, 0xf9, 0xa2, 0x9c, 0x34, 0x1e, 0xb9, 0x6f, 0x18, 0x6c, 0x11, 0x73, 0x54, 0xb5, 0xcc, 0x15,
+	0x2d, 0x95, 0x65, 0xee, 0x5b, 0x54, 0x5d, 0x01, 0x3a, 0xf2, 0xb0, 0xd0, 0xbd, 0x8c, 0x37, 0xeb,
+	0x3c, 0x89, 0x91, 0xef, 0xd0, 0xd4, 0x52, 0x47, 0x65, 0xa2, 0x4e, 0xe2, 0x57, 0xe5, 0x74, 0x8e,
+	0xba, 0xef, 0x51, 0x68, 0xb1, 0xa3, 0xba, 0xc3, 0xae, 0xad, 0x74, 0xd8, 0xfd, 0x80, 0xca, 0x7e,
+	0x54, 0x3a, 0xee, 0x3a, 0xd9, 0x5a, 0x89, 0x1f, 0x51, 0xb8, 0x95, 0x64, 0x6b, 0xb5, 0x91, 0xad,
+	0x95, 0xf8, 0x09, 0x85, 0x79, 0x9a, 0xdd, 0x51, 0x3a, 0xf2, 0x7e, 0x46, 0x65, 0x2f, 0xcd, 0xd6,
+	0x2a, 0xbf, 0x8d, 0xbb, 0xef, 0x3d, 0x3d, 0x4e, 0xbf, 0xa0, 0xd6, 0xb2, 0xe7, 0x5c, 0x3d, 0x7b,
+	0xaf, 0x65, 0xa1, 0x16, 0xd8, 0xfb, 0x15, 0xc5, 0x16, 0x3e, 0xfa, 0x83, 0x87, 0xaf, 0x3d, 0x15,
+	0xc1, 0xf7, 0x1b, 0x2a, 0x79, 0x9c, 0x8a, 0xe8, 0xdb, 0x3c, 0x81, 0x56, 0xe2, 0x77, 0x94, 0x0e,
+	0xba, 0x27, 0xd0, 0x6a, 0xf3, 0x04, 0x5a, 0x89, 0x3f, 0x50, 0xbc, 0xdd, 0x39, 0x41, 0xe7, 0x16,
+	0x1c, 0x7f, 0x7f, 0xa2, 0x94, 0xc5, 0x5b, 0x70, 0x00, 0x26, 0x37, 0x4b, 0x00, 0xfe, 0x45, 0x9b,
+	0x17, 0x6f, 0x96, 0x08, 0x6c, 0x4f, 0x45, 0x04, 0xfe, 0x8d, 0xc2, 0xbd, 0x38, 0x15, 0x21, 0xf8,
+	0xb0, 0x75, 0x4e, 0x8f, 0xe0, 0x3f, 0xa8, 0xbc, 0x1a, 0x83, 0xe4, 0xe4, 0x19, 0xfc, 0x38, 0x02,
+	0x40, 0x0c, 0xfe, 0x8b, 0xce, 0x97, 0x82, 0x90, 0xba, 0x03, 0x84, 0x2d, 0xb3, 0xe7, 0x75, 0xd6,
+	0x4f, 0x9e, 0x8b, 0xc9, 0x83, 0x9e, 0xf2, 0x6e, 0xda, 0x7f, 0x08, 0xc2, 0xf3, 0xf2, 0xbe, 0xef,
+	0x4b, 0xe0, 0x4e, 0x7e, 0xad, 0xa8, 0xef, 0x0b, 0xfb, 0xab, 0x16, 0xa0, 0xa7, 0xbe, 0x37, 0xb1,
+	0xaf, 0x75, 0x9e, 0xe4, 0x8e, 0xa9, 0xef, 0x3f, 0xfa, 0x32, 0xc2, 0x41, 0xa9, 0x71, 0x37, 0xdb,
+	0x71, 0x03, 0xdd, 0x1a, 0x64, 0x3d, 0x8b, 0xf1, 0xf5, 0xac, 0x77, 0x72, 0xaf, 0x78, 0x30, 0x78,
+	0xe6, 0xe8, 0xed, 0x93, 0xb7, 0x2a, 0x63, 0xaa, 0xba, 0x1c, 0x56, 0xa6, 0x9e, 0xcc, 0xab, 0xa1,
+	0x59, 0x56, 0x23, 0x7b, 0x1f, 0xa3, 0xb3, 0x8b, 0x29, 0xbd, 0x3b, 0xbb, 0x53, 0x95, 0xf3, 0x3b,
+	0x95, 0x19, 0x35, 0xe5, 0xaa, 0x99, 0x4e, 0x9a, 0x09, 0x95, 0xc7, 0xff, 0x07, 0x00, 0x00, 0xff,
+	0xff, 0xc9, 0x6e, 0x69, 0xe6, 0xaf, 0x0b, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/proto2/fields.proto b/cmd/protoc-gen-go/testdata/proto2/fields.proto
new file mode 100644
index 0000000..1e65c6d
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto2/fields.proto
@@ -0,0 +1,77 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+syntax = "proto2";
+
+package goproto.protoc.proto2;
+
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/proto2";
+
+message FieldTestMessage {
+  optional string   optional_bool     = 1;
+  optional Enum     optional_enum     = 2;
+  optional int32    optional_int32    = 3;
+  optional sint32   optional_sint32   = 4;
+  optional uint32   optional_uint32   = 5;
+  optional int64    optional_int64    = 6;
+  optional sint64   optional_sint64   = 7;
+  optional uint64   optional_uint64   = 8;
+  optional sfixed32 optional_sfixed32 = 9;
+  optional fixed32  optional_fixed32  = 10;
+  optional float    optional_float    = 11;
+  optional sfixed64 optional_sfixed64 = 12;
+  optional fixed64  optional_fixed64  = 13;
+  optional double   optional_double   = 14;
+  optional string   optional_string   = 15;
+  optional bytes    optional_bytes    = 16;
+  optional Message  optional_Message  = 17;
+  optional group OptionalGroup = 18 {
+    optional string optional_group = 19;
+  }
+
+  required string   required_bool     = 101;
+  required Enum     required_enum     = 102;
+  required int32    required_int32    = 103;
+  required sint32   required_sint32   = 104;
+  required uint32   required_uint32   = 105;
+  required int64    required_int64    = 106;
+  required sint64   required_sint64   = 107;
+  required uint64   required_uint64   = 108;
+  required sfixed32 required_sfixed32 = 109;
+  required fixed32  required_fixed32  = 110;
+  required float    required_float    = 111;
+  required sfixed64 required_sfixed64 = 112;
+  required fixed64  required_fixed64  = 113;
+  required double   required_double   = 114;
+  required string   required_string   = 115;
+  required bytes    required_bytes    = 116;
+  required Message  required_Message  = 117;
+  required group RequiredGroup = 118 {
+    required string required_group = 119;
+  }
+
+  repeated string   repeated_bool     = 201;
+  repeated Enum     repeated_enum     = 202;
+  repeated int32    repeated_int32    = 203;
+  repeated sint32   repeated_sint32   = 204;
+  repeated uint32   repeated_uint32   = 205;
+  repeated int64    repeated_int64    = 206;
+  repeated sint64   repeated_sint64   = 207;
+  repeated uint64   repeated_uint64   = 208;
+  repeated sfixed32 repeated_sfixed32 = 209;
+  repeated fixed32  repeated_fixed32  = 210;
+  repeated float    repeated_float    = 211;
+  repeated sfixed64 repeated_sfixed64 = 212;
+  repeated fixed64  repeated_fixed64  = 213;
+  repeated double   repeated_double   = 214;
+  repeated string   repeated_string   = 215;
+  repeated bytes    repeated_bytes    = 216;
+  repeated Message  repeated_Message  = 217;
+  repeated group RepeatedGroup = 218 {
+    repeated string repeated_group = 219;
+  }
+
+  enum Enum { ZERO = 0; }
+  message Message {}
+}
diff --git a/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go b/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go
index ba78d1b..f86082d 100644
--- a/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go
+++ b/cmd/protoc-gen-go/testdata/proto2/nested_messages.pb.go
@@ -4,12 +4,24 @@
 package proto2
 
 type Layer1 struct {
+	L2                   *Layer1_Layer2        `protobuf:"bytes,1,opt,name=l2" json:"l2,omitempty"`
+	L3                   *Layer1_Layer2_Layer3 `protobuf:"bytes,2,opt,name=l3" json:"l3,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
 }
 
 type Layer1_Layer2 struct {
+	L3                   *Layer1_Layer2_Layer3 `protobuf:"bytes,1,opt,name=l3" json:"l3,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
 }
 
 type Layer1_Layer2_Layer3 struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 func init() { proto.RegisterFile("proto2/nested_messages.proto", fileDescriptor_7417ee157699d191) }
diff --git a/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go b/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go
index 72dcb71..9dc8ff2 100644
--- a/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go
+++ b/cmd/protoc-gen-go/testdata/proto2/proto2.pb.go
@@ -4,17 +4,25 @@
 package proto2
 
 type Message struct {
+	I32                  *int32   `protobuf:"varint,1,opt,name=i32" json:"i32,omitempty"`
+	M                    *Message `protobuf:"bytes,2,opt,name=m" json:"m,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 func init() { proto.RegisterFile("proto2/proto2.proto", fileDescriptor_d756bbe8817c03c1) }
 
 var fileDescriptor_d756bbe8817c03c1 = []byte{
-	// 107 bytes of a gzipped FileDescriptorProto
+	// 146 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0x28, 0xca, 0x2f,
 	0xc9, 0x37, 0xd2, 0x87, 0x50, 0x7a, 0x60, 0x4a, 0x48, 0x34, 0x3d, 0x1f, 0xcc, 0x80, 0x70, 0x93,
-	0x21, 0x94, 0x91, 0x12, 0x27, 0x17, 0xbb, 0x6f, 0x6a, 0x71, 0x71, 0x62, 0x7a, 0xaa, 0x93, 0x75,
-	0x94, 0x65, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x5e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e,
-	0x7e, 0x51, 0x3a, 0xc4, 0x0c, 0xfd, 0xe4, 0xdc, 0x14, 0x08, 0x2b, 0x59, 0x37, 0x3d, 0x35, 0x4f,
-	0x37, 0x3d, 0x5f, 0xbf, 0x24, 0xb5, 0xb8, 0x24, 0x25, 0xb1, 0x24, 0x11, 0x6a, 0x09, 0x20, 0x00,
-	0x00, 0xff, 0xff, 0xd7, 0x76, 0x0d, 0x22, 0x74, 0x00, 0x00, 0x00,
+	0x21, 0x94, 0x91, 0x92, 0x27, 0x17, 0xbb, 0x6f, 0x6a, 0x71, 0x71, 0x62, 0x7a, 0xaa, 0x90, 0x00,
+	0x17, 0x73, 0xa6, 0xb1, 0x91, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x88, 0x29, 0xa4, 0xc3,
+	0xc5, 0x98, 0x2b, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x6d, 0x24, 0xa7, 0x87, 0x55, 0xbf, 0x1e, 0x54,
+	0x73, 0x10, 0x63, 0xae, 0x93, 0x75, 0x94, 0x65, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x5e, 0x7a,
+	0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x5e, 0x7e, 0x51, 0x3a, 0xc4, 0x0d, 0xfa, 0xc9, 0xb9, 0x29, 0x10,
+	0x56, 0xb2, 0x6e, 0x7a, 0x6a, 0x9e, 0x6e, 0x7a, 0xbe, 0x7e, 0x49, 0x6a, 0x71, 0x49, 0x4a, 0x62,
+	0x49, 0x22, 0xd4, 0x91, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8c, 0x68, 0x66, 0x37, 0xb4, 0x00,
+	0x00, 0x00,
 }
diff --git a/cmd/protoc-gen-go/testdata/proto2/proto2.proto b/cmd/protoc-gen-go/testdata/proto2/proto2.proto
index 055bc71..9e7e29d 100644
--- a/cmd/protoc-gen-go/testdata/proto2/proto2.proto
+++ b/cmd/protoc-gen-go/testdata/proto2/proto2.proto
@@ -9,4 +9,7 @@
 option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/proto2";
 
 message Message {
+  optional int32 i32 = 1;
+
+  optional Message m = 2;
 }
diff --git a/cmd/protoc-gen-go/testdata/proto3/fields.pb.go b/cmd/protoc-gen-go/testdata/proto3/fields.pb.go
new file mode 100644
index 0000000..2cc5e06
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto3/fields.pb.go
@@ -0,0 +1,95 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: proto3/fields.proto
+
+package proto3
+
+import proto "github.com/golang/protobuf/proto"
+
+type FieldTestMessage_Enum int32
+
+const (
+	FieldTestMessage_ZERO FieldTestMessage_Enum = 0
+)
+
+var FieldTestMessage_Enum_name = map[int32]string{
+	0: "ZERO",
+}
+
+var FieldTestMessage_Enum_value = map[string]int32{
+	"ZERO": 0,
+}
+
+func (x FieldTestMessage_Enum) String() string {
+	return proto.EnumName(FieldTestMessage_Enum_name, int32(x))
+}
+
+func (FieldTestMessage_Enum) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_f1e3ea068187307c, []int{0, 0}
+}
+
+type FieldTestMessage struct {
+	OptionalBool         string                    `protobuf:"bytes,1,opt,name=optional_bool,json=optionalBool,proto3" json:"optional_bool,omitempty"`
+	OptionalEnum         FieldTestMessage_Enum     `protobuf:"varint,2,opt,name=optional_enum,json=optionalEnum,proto3,enum=goproto.protoc.proto3.FieldTestMessage_Enum" json:"optional_enum,omitempty"`
+	OptionalInt32        int32                     `protobuf:"varint,3,opt,name=optional_int32,json=optionalInt32,proto3" json:"optional_int32,omitempty"`
+	OptionalSint32       int32                     `protobuf:"zigzag32,4,opt,name=optional_sint32,json=optionalSint32,proto3" json:"optional_sint32,omitempty"`
+	OptionalUint32       uint32                    `protobuf:"varint,5,opt,name=optional_uint32,json=optionalUint32,proto3" json:"optional_uint32,omitempty"`
+	OptionalInt64        int64                     `protobuf:"varint,6,opt,name=optional_int64,json=optionalInt64,proto3" json:"optional_int64,omitempty"`
+	OptionalSint64       int64                     `protobuf:"zigzag64,7,opt,name=optional_sint64,json=optionalSint64,proto3" json:"optional_sint64,omitempty"`
+	OptionalUint64       uint64                    `protobuf:"varint,8,opt,name=optional_uint64,json=optionalUint64,proto3" json:"optional_uint64,omitempty"`
+	OptionalSfixed32     int32                     `protobuf:"fixed32,9,opt,name=optional_sfixed32,json=optionalSfixed32,proto3" json:"optional_sfixed32,omitempty"`
+	OptionalFixed32      uint32                    `protobuf:"fixed32,10,opt,name=optional_fixed32,json=optionalFixed32,proto3" json:"optional_fixed32,omitempty"`
+	OptionalFloat        float32                   `protobuf:"fixed32,11,opt,name=optional_float,json=optionalFloat,proto3" json:"optional_float,omitempty"`
+	OptionalSfixed64     int64                     `protobuf:"fixed64,12,opt,name=optional_sfixed64,json=optionalSfixed64,proto3" json:"optional_sfixed64,omitempty"`
+	OptionalFixed64      uint64                    `protobuf:"fixed64,13,opt,name=optional_fixed64,json=optionalFixed64,proto3" json:"optional_fixed64,omitempty"`
+	OptionalDouble       float64                   `protobuf:"fixed64,14,opt,name=optional_double,json=optionalDouble,proto3" json:"optional_double,omitempty"`
+	OptionalString       string                    `protobuf:"bytes,15,opt,name=optional_string,json=optionalString,proto3" json:"optional_string,omitempty"`
+	OptionalBytes        []byte                    `protobuf:"bytes,16,opt,name=optional_bytes,json=optionalBytes,proto3" json:"optional_bytes,omitempty"`
+	Optional_Message     *FieldTestMessage_Message `protobuf:"bytes,17,opt,name=optional_Message,json=optionalMessage,proto3" json:"optional_Message,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                  `json:"-"`
+	XXX_unrecognized     []byte                    `json:"-"`
+	XXX_sizecache        int32                     `json:"-"`
+}
+
+type FieldTestMessage_Message struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func init() {
+	proto.RegisterEnum("goproto.protoc.proto3.FieldTestMessage_Enum", FieldTestMessage_Enum_name, FieldTestMessage_Enum_value)
+}
+
+func init() { proto.RegisterFile("proto3/fields.proto", fileDescriptor_f1e3ea068187307c) }
+
+var fileDescriptor_f1e3ea068187307c = []byte{
+	// 440 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x6b, 0xdb, 0x40,
+	0x10, 0xc5, 0x3b, 0x89, 0x63, 0xc7, 0x1b, 0xff, 0x91, 0xb7, 0x14, 0x96, 0x9e, 0x96, 0x94, 0xd2,
+	0x2d, 0x6d, 0x24, 0xb0, 0x84, 0xa0, 0xf4, 0x66, 0x1a, 0x43, 0x0f, 0xa5, 0x54, 0x6d, 0x2f, 0xbe,
+	0x04, 0xc9, 0x5a, 0x2f, 0x02, 0x59, 0x13, 0xac, 0x15, 0xb4, 0x1f, 0xb0, 0xdf, 0xab, 0x48, 0xab,
+	0x48, 0x96, 0xec, 0x43, 0x4e, 0x33, 0xf3, 0x78, 0x9a, 0x9f, 0xde, 0x32, 0xe4, 0xe5, 0xe3, 0x01,
+	0x35, 0xba, 0xce, 0x2e, 0x91, 0x69, 0x9c, 0xdb, 0xd5, 0x44, 0x5f, 0x29, 0xac, 0x1a, 0x33, 0x6e,
+	0x4d, 0x71, 0x6f, 0xff, 0x0d, 0x89, 0xb5, 0x2e, 0x7d, 0xbf, 0x64, 0xae, 0xbf, 0xc9, 0x3c, 0x0f,
+	0x95, 0xa4, 0x6f, 0xc8, 0x14, 0x1f, 0x75, 0x82, 0x59, 0x98, 0x3e, 0x44, 0x88, 0x29, 0x03, 0x0e,
+	0x62, 0x1c, 0x4c, 0x9e, 0xc4, 0x15, 0x62, 0x4a, 0x7f, 0x1c, 0x99, 0x64, 0x56, 0xec, 0xd9, 0x05,
+	0x07, 0x31, 0x5b, 0x7e, 0xb4, 0xcf, 0x82, 0xec, 0x3e, 0xc4, 0xbe, 0xcf, 0x8a, 0x7d, 0xbb, 0xb2,
+	0x9c, 0xe8, 0x5b, 0x32, 0x6b, 0x56, 0x26, 0x99, 0x76, 0x97, 0xec, 0x92, 0x83, 0xb8, 0x0a, 0x1a,
+	0xd0, 0xd7, 0x52, 0xa4, 0xef, 0xc8, 0xbc, 0xb1, 0xe5, 0xc6, 0x37, 0xe0, 0x20, 0x16, 0x41, 0xf3,
+	0xf5, 0xcf, 0xe4, 0xc4, 0x58, 0x18, 0xe3, 0x15, 0x07, 0x31, 0x6d, 0x8d, 0xbf, 0x8d, 0xb1, 0x07,
+	0xf6, 0x3d, 0x36, 0xe4, 0x20, 0x2e, 0x3b, 0x60, 0xdf, 0x3b, 0x01, 0xfb, 0x1e, 0x1b, 0x71, 0x10,
+	0xb4, 0x0b, 0xee, 0x19, 0x0b, 0x63, 0xbc, 0xe6, 0x20, 0x06, 0x5d, 0xb0, 0xef, 0xd1, 0x0f, 0x64,
+	0xd1, 0x6e, 0xdc, 0x25, 0x7f, 0x64, 0xec, 0x2e, 0xd9, 0x98, 0x83, 0x98, 0x07, 0x56, 0xb3, 0xb3,
+	0xd6, 0xe9, 0x7b, 0xd2, 0x68, 0x0f, 0x4f, 0x5e, 0xc2, 0x41, 0x8c, 0x82, 0x86, 0xb6, 0xae, 0xad,
+	0xc7, 0x81, 0x76, 0x29, 0x86, 0x9a, 0xdd, 0x70, 0x10, 0x17, 0x6d, 0xa0, 0x75, 0x29, 0x9e, 0xc1,
+	0xfb, 0x1e, 0x9b, 0x70, 0x10, 0x56, 0x1f, 0xef, 0x7b, 0xa7, 0x78, 0xdf, 0x63, 0x53, 0x0e, 0x62,
+	0xd8, 0xc3, 0xf7, 0xf2, 0xc7, 0x58, 0x44, 0xa9, 0x64, 0x33, 0x0e, 0x02, 0xda, 0xfc, 0x5f, 0x2a,
+	0xb5, 0xfb, 0xa2, 0xfa, 0x90, 0x64, 0x8a, 0xcd, 0xab, 0x5b, 0x6b, 0x5f, 0xb4, 0x52, 0x3b, 0x81,
+	0xa2, 0xbf, 0x5a, 0xe6, 0xcc, 0xe2, 0x20, 0x26, 0x6d, 0xa0, 0x55, 0x29, 0xd2, 0xcd, 0xd1, 0x3f,
+	0xd6, 0x87, 0xc6, 0x16, 0x1c, 0xc4, 0xcd, 0xd2, 0x79, 0xee, 0x5d, 0xd6, 0xb5, 0x0d, 0x55, 0x0b,
+	0xaf, 0xc7, 0x64, 0x54, 0xb7, 0xb7, 0x16, 0x19, 0x54, 0x07, 0x7b, 0x4d, 0x06, 0x9b, 0xfb, 0xe0,
+	0xbb, 0xf5, 0x62, 0xf5, 0x79, 0xf3, 0x49, 0x21, 0xaa, 0x54, 0xda, 0x0a, 0xd3, 0x30, 0x53, 0x36,
+	0x1e, 0x94, 0x53, 0x21, 0x9c, 0xed, 0x3e, 0x36, 0xdd, 0xf6, 0x4e, 0xc9, 0xec, 0x4e, 0xa1, 0xa3,
+	0x65, 0xae, 0xe3, 0x50, 0x87, 0x46, 0x76, 0xa3, 0xa1, 0xa9, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff,
+	0xa9, 0x42, 0xe7, 0xab, 0xb9, 0x03, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/proto3/fields.proto b/cmd/protoc-gen-go/testdata/proto3/fields.proto
new file mode 100644
index 0000000..9c46a76
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto3/fields.proto
@@ -0,0 +1,33 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+syntax = "proto3";
+
+package goproto.protoc.proto3;
+
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/proto3";
+
+message FieldTestMessage {
+  string   optional_bool     = 1;
+  Enum     optional_enum     = 2;
+  int32    optional_int32    = 3;
+  sint32   optional_sint32   = 4;
+  uint32   optional_uint32   = 5;
+  int64    optional_int64    = 6;
+  sint64   optional_sint64   = 7;
+  uint64   optional_uint64   = 8;
+  sfixed32 optional_sfixed32 = 9;
+  fixed32  optional_fixed32  = 10;
+  float    optional_float    = 11;
+  sfixed64 optional_sfixed64 = 12;
+  fixed64  optional_fixed64  = 13;
+  double   optional_double   = 14;
+  string   optional_string   = 15;
+  bytes    optional_bytes    = 16;
+  Message  optional_Message  = 17;
+
+  enum Enum { ZERO = 0; }
+  message Message {}
+}
+
diff --git a/protogen/protogen.go b/protogen/protogen.go
index ee4c735..5e1cd83 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -94,10 +94,12 @@
 	Files       []*File
 	filesByName map[string]*File
 
-	fileReg  *protoregistry.Files
-	pathType pathType
-	genFiles []*GeneratedFile
-	err      error
+	fileReg        *protoregistry.Files
+	messagesByName map[protoreflect.FullName]*Message
+	enumsByName    map[protoreflect.FullName]*Enum
+	pathType       pathType
+	genFiles       []*GeneratedFile
+	err            error
 }
 
 // Options are optional parameters to New.
@@ -136,9 +138,11 @@
 		opts = &Options{}
 	}
 	gen := &Plugin{
-		Request:     req,
-		filesByName: make(map[string]*File),
-		fileReg:     protoregistry.NewFiles(),
+		Request:        req,
+		filesByName:    make(map[string]*File),
+		fileReg:        protoregistry.NewFiles(),
+		messagesByName: make(map[protoreflect.FullName]*Message),
+		enumsByName:    make(map[protoreflect.FullName]*Enum),
 	}
 
 	packageNames := make(map[string]GoPackageName) // filename -> package name
@@ -387,7 +391,11 @@
 	f.GeneratedFilenamePrefix = prefix
 
 	for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
-		f.Messages = append(f.Messages, newMessage(gen, f, nil, mdescs.Get(i)))
+		message, err := newMessage(gen, f, nil, mdescs.Get(i))
+		if err != nil {
+			return nil, err
+		}
+		f.Messages = append(f.Messages, message)
 	}
 	for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
 		f.Enums = append(f.Enums, newEnum(gen, f, nil, edescs.Get(i)))
@@ -420,12 +428,13 @@
 	Desc protoreflect.MessageDescriptor
 
 	GoIdent  GoIdent    // name of the generated Go type
+	Fields   []*Field   // message field declarations
 	Messages []*Message // nested message declarations
 	Enums    []*Enum    // nested enum declarations
 	Path     []int32    // location path of this message
 }
 
-func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) *Message {
+func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) (*Message, error) {
 	var path []int32
 	if parent != nil {
 		path = pathAppend(parent.Path, messageMessageField, int32(desc.Index()))
@@ -437,13 +446,107 @@
 		GoIdent: newGoIdent(f, desc),
 		Path:    path,
 	}
+	gen.messagesByName[desc.FullName()] = message
 	for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
-		message.Messages = append(message.Messages, newMessage(gen, f, message, mdescs.Get(i)))
+		m, err := newMessage(gen, f, message, mdescs.Get(i))
+		if err != nil {
+			return nil, err
+		}
+		message.Messages = append(message.Messages, m)
 	}
 	for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
 		message.Enums = append(message.Enums, newEnum(gen, f, message, edescs.Get(i)))
 	}
-	return message
+	for i, fdescs := 0, desc.Fields(); i < fdescs.Len(); i++ {
+		field, err := newField(gen, f, message, fdescs.Get(i))
+		if err != nil {
+			return nil, err
+		}
+		message.Fields = append(message.Fields, field)
+	}
+
+	// Field name conflict resolution.
+	//
+	// We assume well-known method names that may be attached to a generated
+	// message type, as well as a 'Get*' method for each field. For each
+	// field in turn, we add _s to its name until there are no conflicts.
+	//
+	// Any change to the following set of method names is a potential
+	// incompatible API change because it may change generated field names.
+	//
+	// TODO: If we ever support a 'go_name' option to set the Go name of a
+	// field, we should consider dropping this entirely. The conflict
+	// resolution algorithm is subtle and surprising (changing the order
+	// in which fields appear in the .proto source file can change the
+	// names of fields in generated code), and does not adapt well to
+	// adding new per-field methods such as setters.
+	usedNames := map[string]bool{
+		"Reset":               true,
+		"String":              true,
+		"ProtoMessage":        true,
+		"Marshal":             true,
+		"Unmarshal":           true,
+		"ExtensionRangeArray": true,
+		"ExtensionMap":        true,
+		"Descriptor":          true,
+	}
+	makeNameUnique := func(name string) string {
+		for usedNames[name] || usedNames["Get"+name] {
+			name += "_"
+		}
+		usedNames[name] = true
+		usedNames["Get"+name] = true
+		return name
+	}
+	for _, field := range message.Fields {
+		field.GoIdent.GoName = makeNameUnique(field.GoIdent.GoName)
+		// TODO: If this is the first field of a oneof that we haven't seen
+		// before, generate the name for the oneof.
+	}
+
+	return message, nil
+}
+
+// A Field describes a message field.
+type Field struct {
+	Desc protoreflect.FieldDescriptor
+
+	// GoIdent is the base name of this field's Go fields and methods.
+	// For code generated by protoc-gen-go, this means a field named
+	// '{{GoIdent}}' and a getter method named 'Get{{GoIdent}}'.
+	GoIdent GoIdent
+
+	MessageType *Message // type for message or group fields; nil otherwise
+	EnumType    *Enum    // type for enum fields; nil otherwise
+	Path        []int32  // location path of this field
+}
+
+func newField(gen *Plugin, f *File, message *Message, desc protoreflect.FieldDescriptor) (*Field, error) {
+	field := &Field{
+		Desc: desc,
+		GoIdent: GoIdent{
+			GoName:       camelCase(string(desc.Name())),
+			GoImportPath: f.GoImportPath,
+		},
+		Path: pathAppend(message.Path, messageFieldField, int32(desc.Index())),
+	}
+	switch desc.Kind() {
+	case protoreflect.MessageKind, protoreflect.GroupKind:
+		mname := desc.MessageType().FullName()
+		message, ok := gen.messagesByName[mname]
+		if !ok {
+			return nil, fmt.Errorf("field %v: no descriptor for type %v", desc.FullName(), mname)
+		}
+		field.MessageType = message
+	case protoreflect.EnumKind:
+		ename := desc.EnumType().FullName()
+		enum, ok := gen.enumsByName[ename]
+		if !ok {
+			return nil, fmt.Errorf("field %v: no descriptor for enum %v", desc.FullName(), ename)
+		}
+		field.EnumType = enum
+	}
+	return field, nil
 }
 
 // An Enum describes an enum.
@@ -467,6 +570,7 @@
 		GoIdent: newGoIdent(f, desc),
 		Path:    path,
 	}
+	gen.enumsByName[desc.FullName()] = enum
 	for i, evdescs := 0, enum.Desc.Values(); i < evdescs.Len(); i++ {
 		enum.Values = append(enum.Values, newEnumValue(gen, f, parent, enum, evdescs.Get(i)))
 	}
@@ -561,19 +665,6 @@
 	return string(packageName) + "." + ident.GoName
 }
 
-func (g *GeneratedFile) goPackageName(importPath GoImportPath) GoPackageName {
-	if name, ok := g.packageNames[importPath]; ok {
-		return name
-	}
-	name := cleanPackageName(baseName(string(importPath)))
-	for i, orig := 1, name; g.usedPackageNames[name]; i++ {
-		name = orig + GoPackageName(strconv.Itoa(i))
-	}
-	g.packageNames[importPath] = name
-	g.usedPackageNames[name] = true
-	return name
-}
-
 // Write implements io.Writer.
 func (g *GeneratedFile) Write(p []byte) (n int, err error) {
 	return g.buf.Write(p)