protogen: use protoreflect descriptors

Change the protogen types wrapping FileDescriptorProto et al. to use
protoreflect descriptors instead.

Change-Id: I99fe83b995a0a6f4fc88f03a6e4b827109a2ec80
Reviewed-on: https://go-review.googlesource.com/133815
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go
index e144aa6..bfae8eb 100644
--- a/cmd/protoc-gen-go/main.go
+++ b/cmd/protoc-gen-go/main.go
@@ -25,9 +25,9 @@
 }
 
 func genFile(gen *protogen.Plugin, f *protogen.File) {
-	g := gen.NewGeneratedFile(strings.TrimSuffix(f.Desc.GetName(), ".proto")+".pb.go", f.GoImportPath)
+	g := gen.NewGeneratedFile(strings.TrimSuffix(f.Desc.Path(), ".proto")+".pb.go", f.GoImportPath)
 	g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
-	g.P("// source: ", f.Desc.GetName())
+	g.P("// source: ", f.Desc.Path())
 	g.P()
 	g.P("package TODO")
 	g.P()
diff --git a/protogen/names.go b/protogen/names.go
index ea3d057..0c220bc 100644
--- a/protogen/names.go
+++ b/protogen/names.go
@@ -7,6 +7,8 @@
 	"strings"
 	"unicode"
 	"unicode/utf8"
+
+	"google.golang.org/proto/reflect/protoreflect"
 )
 
 // A GoIdent is a Go identifier, consisting of a name and import path.
@@ -17,6 +19,15 @@
 
 func (id GoIdent) String() string { return fmt.Sprintf("%q.%v", id.GoImportPath, id.GoName) }
 
+// newGoIdent returns the Go identifier for a descriptor.
+func newGoIdent(f *File, d protoreflect.Descriptor) GoIdent {
+	name := strings.TrimPrefix(string(d.FullName()), string(f.Desc.Package())+".")
+	return GoIdent{
+		GoName:       camelCase(name),
+		GoImportPath: f.GoImportPath,
+	}
+}
+
 // A GoImportPath is the import path of a Go package. e.g., "google.golang.org/genproto/protobuf".
 type GoImportPath string
 
diff --git a/protogen/protogen.go b/protogen/protogen.go
index 2d65ee0..ffba172 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -28,6 +28,9 @@
 	descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	pluginpb "github.com/golang/protobuf/protoc-gen-go/plugin"
 	"golang.org/x/tools/go/ast/astutil"
+	"google.golang.org/proto/reflect/protoreflect"
+	"google.golang.org/proto/reflect/protoregistry"
+	"google.golang.org/proto/reflect/prototype"
 )
 
 // Run executes a function as a protoc plugin.
@@ -88,6 +91,8 @@
 	Files       []*File
 	filesByName map[string]*File
 
+	fileReg *protoregistry.Files
+
 	packageImportPath string // Go import path of the package we're generating code for.
 
 	genFiles []*GeneratedFile
@@ -99,6 +104,7 @@
 	gen := &Plugin{
 		Request:     req,
 		filesByName: make(map[string]*File),
+		fileReg:     protoregistry.NewFiles(),
 	}
 
 	// TODO: Figure out how to pass parameters to the generator.
@@ -130,8 +136,11 @@
 	}
 
 	for _, fdesc := range gen.Request.ProtoFile {
-		f := newFile(gen, fdesc)
-		name := f.Desc.GetName()
+		f, err := newFile(gen, fdesc)
+		if err != nil {
+			return nil, err
+		}
+		name := f.Desc.Path()
 		if gen.filesByName[name] != nil {
 			return nil, fmt.Errorf("duplicate file name: %q", name)
 		}
@@ -186,44 +195,45 @@
 
 // A File describes a .proto source file.
 type File struct {
-	Desc *descpb.FileDescriptorProto // TODO: protoreflect.FileDescriptor
+	Desc protoreflect.FileDescriptor
 
 	GoImportPath GoImportPath // import path of this file's Go package
 	Messages     []*Message   // top-level message declarations
 	Generate     bool         // true if we should generate code for this file
 }
 
-func newFile(gen *Plugin, p *descpb.FileDescriptorProto) *File {
+func newFile(gen *Plugin, p *descpb.FileDescriptorProto) (*File, error) {
+	desc, err := prototype.NewFileFromDescriptorProto(p, gen.fileReg)
+	if err != nil {
+		return nil, fmt.Errorf("invalid FileDescriptorProto %q: %v", p.GetName(), err)
+	}
+	if err := gen.fileReg.Register(desc); err != nil {
+		return nil, fmt.Errorf("cannot register descriptor %q: %v", p.GetName(), err)
+	}
 	f := &File{
-		Desc: p,
+		Desc: desc,
 	}
-	for i, mdesc := range p.MessageType {
-		f.Messages = append(f.Messages, newMessage(gen, f, nil, mdesc, i))
+	for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
+		f.Messages = append(f.Messages, newMessage(gen, f, nil, mdescs.Get(i), i))
 	}
-	return f
+	return f, nil
 }
 
 // A Message describes a message.
 type Message struct {
-	Desc *descpb.DescriptorProto // TODO: protoreflect.MessageDescriptor
+	Desc protoreflect.MessageDescriptor
 
 	GoIdent  GoIdent    // name of the generated Go type
 	Messages []*Message // nested message declarations
 }
 
-func newMessage(gen *Plugin, f *File, parent *Message, p *descpb.DescriptorProto, index int) *Message {
+func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor, index int) *Message {
 	m := &Message{
-		Desc: p,
-		GoIdent: GoIdent{
-			GoName:       camelCase(p.GetName()),
-			GoImportPath: f.GoImportPath,
-		},
+		Desc:    desc,
+		GoIdent: newGoIdent(f, desc),
 	}
-	if parent != nil {
-		m.GoIdent.GoName = parent.GoIdent.GoName + "_" + m.GoIdent.GoName
-	}
-	for i, nested := range p.GetNestedType() {
-		m.Messages = append(m.Messages, newMessage(gen, f, m, nested, i))
+	for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
+		m.Messages = append(m.Messages, newMessage(gen, f, m, mdescs.Get(i), i))
 	}
 	return m
 }