cmd/protoc-gen-go: generate public imports

Generate forwarding declarations for all types, consts, and vars in
publicly imported files.

Change-Id: I2f1041fa91b0ae18b1e123935951618b8d594f84
Reviewed-on: https://go-review.googlesource.com/136175
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go
index 0c1b119..b477fe7 100644
--- a/cmd/protoc-gen-go/main.go
+++ b/cmd/protoc-gen-go/main.go
@@ -74,13 +74,11 @@
 	//
 	// TODO: Eventually make this consistent.
 	f.allEnums = append(f.allEnums, f.File.Enums...)
-	for _, message := range f.Messages {
-		walkMessage(message, func(message *protogen.Message) {
-			f.allMessages = append(f.allMessages, message)
-			f.allEnums = append(f.allEnums, message.Enums...)
-			f.allExtensions = append(f.allExtensions, message.Extensions...)
-		})
-	}
+	walkMessages(f.Messages, func(message *protogen.Message) {
+		f.allMessages = append(f.allMessages, message)
+		f.allEnums = append(f.allEnums, message.Enums...)
+		f.allExtensions = append(f.allExtensions, message.Extensions...)
+	})
 	f.allExtensions = append(f.allExtensions, f.File.Extensions...)
 
 	// Determine the name of the var holding the file descriptor:
@@ -123,6 +121,9 @@
 	}, "// please upgrade the proto package")
 	g.P()
 
+	for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
+		genImport(gen, g, f, imps.Get(i))
+	}
 	for _, enum := range f.allEnums {
 		genEnum(gen, g, f, enum)
 	}
@@ -138,11 +139,49 @@
 	genFileDescriptor(gen, g, f)
 }
 
-// walkMessage calls f on message and all of its descendants.
-func walkMessage(message *protogen.Message, f func(*protogen.Message)) {
-	f(message)
-	for _, m := range message.Messages {
-		walkMessage(m, f)
+// walkMessages calls f on each message and all of its descendants.
+func walkMessages(messages []*protogen.Message, f func(*protogen.Message)) {
+	for _, m := range messages {
+		f(m)
+		walkMessages(m.Messages, f)
+	}
+}
+
+func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, imp protoreflect.FileImport) {
+	if !imp.IsPublic {
+		return
+	}
+	impFile, ok := gen.FileByName(imp.Path())
+	if !ok {
+		return
+	}
+	if impFile.GoImportPath == f.GoImportPath {
+		// Don't generate aliases for types in the same Go package.
+		return
+	}
+	var enums []*protogen.Enum
+	enums = append(enums, impFile.Enums...)
+	walkMessages(impFile.Messages, func(message *protogen.Message) {
+		enums = append(enums, message.Enums...)
+		g.P("// ", message.GoIdent.GoName, " from public import ", imp.Path())
+		g.P("type ", message.GoIdent.GoName, " = ", message.GoIdent)
+		for _, oneof := range message.Oneofs {
+			for _, field := range oneof.Fields {
+				typ := fieldOneofType(field)
+				g.P("type ", typ.GoName, " = ", typ)
+			}
+		}
+		g.P()
+	})
+	for _, enum := range enums {
+		g.P("// ", enum.GoIdent.GoName, " from public import ", imp.Path())
+		g.P("type ", enum.GoIdent.GoName, " = ", enum.GoIdent)
+		g.P("var ", enum.GoIdent.GoName, "_name = ", enum.GoIdent, "_name")
+		g.P("var ", enum.GoIdent.GoName, "_value = ", enum.GoIdent, "_value")
+		g.P()
+		for _, value := range enum.Values {
+			g.P("const ", value.GoIdent.GoName, " = ", enum.GoIdent.GoName, "(", value.GoIdent, ")")
+		}
 	}
 }