goprotobuf: Improvements to public import code generation.

R=r
CC=golang-dev
http://codereview.appspot.com/4838048
diff --git a/compiler/generator/generator.go b/compiler/generator/generator.go
index f4f4edd..68fcdc7 100644
--- a/compiler/generator/generator.go
+++ b/compiler/generator/generator.go
@@ -85,11 +85,13 @@
 
 // The file and package name method are common to messages and enums.
 type common struct {
-	File *descriptor.FileDescriptorProto // File this object comes from.
+	file *descriptor.FileDescriptorProto // File this object comes from.
 }
 
 // PackageName is name in the package clause in the generated file.
-func (c *common) PackageName() string { return uniquePackageOf(c.File) }
+func (c *common) PackageName() string { return uniquePackageOf(c.file) }
+
+func (c *common) File() *descriptor.FileDescriptorProto { return c.file }
 
 // Descriptor represents a protocol buffer message.
 type Descriptor struct {
@@ -171,7 +173,7 @@
 	return ""
 }
 
-// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
+// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil.
 // Otherwise it will be the descriptor of the message in which it is defined.
 type ExtensionDescriptor struct {
 	common
@@ -206,6 +208,14 @@
 	return "E_" + strings.Join(typeName, "_")
 }
 
+// ImportedDescriptor describes a type that has been publicly imported from another file.
+type ImportedDescriptor struct {
+	common
+	o Object
+}
+
+func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
+
 // FileDescriptor describes an protocol buffer descriptor file (.proto).
 // It includes slices of all the messages and enums defined within it.
 // Those slices are constructed by WrapTypes.
@@ -214,6 +224,7 @@
 	desc []*Descriptor          // All the messages defined in this file.
 	enum []*EnumDescriptor      // All the enums defined in this file.
 	ext  []*ExtensionDescriptor // All the top-level extensions defined in this file.
+	imp  []*ImportedDescriptor  // All types defined in files publicly imported by this file.
 
 	// The full list of symbols that are exported.
 	// This is used for supporting public imports.
@@ -289,14 +300,15 @@
 	g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
 }
 
-// Object is an interface abstracting the abilities shared by enums and messages.
+// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
 type Object interface {
 	PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
 	TypeName() []string
+	File() *descriptor.FileDescriptorProto
 }
 
 // Each package name we generate must be unique. The package we're generating
-// gets its own name but every other package must have a unqiue name that does
+// gets its own name but every other package must have a unique name that does
 // not conflict in the code we generate.  These names are chosen globally (although
 // they don't have to be, it simplifies things to do them globally).
 func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
@@ -461,11 +473,13 @@
 		g.buildNestedDescriptors(descs)
 		enums := wrapEnumDescriptors(f, descs)
 		exts := wrapExtensions(f)
+		imps := wrapImported(f, g)
 		g.allFiles[i] = &FileDescriptor{
 			FileDescriptorProto: f,
 			desc:                descs,
 			enum:                enums,
 			ext:                 exts,
+			imp:                 imps,
 		}
 	}
 
@@ -505,11 +519,11 @@
 
 // Construct the Descriptor and add it to the slice
 func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
-	d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
+	d := &Descriptor{common{file}, desc, parent, nil, nil, nil}
 
 	d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
 	for i, field := range desc.Extension {
-		d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
+		d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
 	}
 
 	return append(sl, d)
@@ -536,7 +550,7 @@
 
 // Construct the EnumDescriptor and add it to the slice
 func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
-	return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
+	return append(sl, &EnumDescriptor{common{file}, desc, parent, nil})
 }
 
 // Return a slice of all the EnumDescriptors defined within this file
@@ -559,11 +573,28 @@
 func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
 	sl := make([]*ExtensionDescriptor, len(file.Extension))
 	for i, field := range file.Extension {
-		sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
+		sl[i] = &ExtensionDescriptor{common{file}, field, nil}
 	}
 	return sl
 }
 
+// Return a slice of all the types that are publicly imported into this file.
+func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
+	for _, index := range file.PublicDependency {
+		df := g.fileByName(file.Dependency[index])
+		for _, d := range df.desc {
+			sl = append(sl, &ImportedDescriptor{common{file}, d})
+		}
+		for _, e := range df.enum {
+			sl = append(sl, &ImportedDescriptor{common{file}, e})
+		}
+		for _, ext := range df.ext {
+			sl = append(sl, &ImportedDescriptor{common{file}, ext})
+		}
+	}
+	return
+}
+
 // BuildTypeNameMap builds the map from fully qualified type names to objects.
 // The key names for the map come from the input data, which puts a period at the beginning.
 // It should be called after SetPackageNames and before GenerateAllFiles.
@@ -591,11 +622,44 @@
 // ObjectNamed, given a fully-qualified input type name as it appears in the input data,
 // returns the descriptor for the message or enum with that name.
 func (g *Generator) ObjectNamed(typeName string) Object {
-	f, ok := g.typeNameToObject[typeName]
+	o, ok := g.typeNameToObject[typeName]
 	if !ok {
 		g.Fail("can't find object with type", typeName)
 	}
-	return f
+
+	// If the file of this object isn't a direct dependency of the current file,
+	// or in the current file, then this object has been publicly imported into
+	// a dependency of the current file.
+	// We should return the ImportedDescriptor object for it instead.
+	direct := *o.File().Name == *g.file.Name
+	if !direct {
+		for _, dep := range g.file.Dependency {
+			if *g.fileByName(dep).Name == *o.File().Name {
+				direct = true
+				break
+			}
+		}
+	}
+	if !direct {
+		found := false
+	Loop:
+		for _, dep := range g.file.Dependency {
+			df := g.fileByName(*g.fileByName(dep).Name)
+			for _, td := range df.imp {
+				if td.o == o {
+					// Found it!
+					o = td
+					found = true
+					break Loop
+				}
+			}
+		}
+		if !found {
+			log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
+		}
+	}
+
+	return o
 }
 
 // P prints the arguments to the generated output.  It handles strings and int32s, plus
@@ -687,6 +751,9 @@
 	g.file = g.FileOf(file.FileDescriptorProto)
 	g.usedPackages = make(map[string]bool)
 
+	for _, td := range g.file.imp {
+		g.generateImported(td)
+	}
 	for _, enum := range g.file.enum {
 		g.generateEnum(enum)
 	}
@@ -769,6 +836,7 @@
 			// For instance, some protos use foreign field extensions, which we don't support.
 			// Until then, this is just annoying spam.
 			//log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
+			g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename))
 		}
 	}
 	g.P()
@@ -784,8 +852,21 @@
 	g.P()
 
 	// Symbols from public imports.
+Loop:
 	for _, index := range g.file.PublicDependency {
-		fd := g.fileByName(g.file.Dependency[index])
+		// Don't generate aliases for public imports of files
+		// that we are generating code for, since those symbols
+		// will already be in this package.
+		filename := g.file.Dependency[index]
+		for _, fd := range g.genFiles {
+			if proto.GetString(fd.Name) == filename {
+				g.P("// Ignoring public import ", filename)
+				continue Loop
+			}
+		}
+
+		fd := g.fileByName(filename)
+
 		g.P("// Types from public import ", *fd.Name)
 		for _, sym := range fd.exported {
 			sym.GenerateAlias(g, fd.PackageName())
@@ -794,6 +875,16 @@
 	g.P()
 }
 
+func (g *Generator) generateImported(id *ImportedDescriptor) {
+	tn := id.TypeName()
+	sn := tn[len(tn)-1]
+	g.P("// ", sn, " from public import ", *id.File().Name)
+	g.usedPackages[id.o.PackageName()] = true
+	g.P()
+
+	// TODO: Move the public import symbol generation into here.
+}
+
 // Generate the enum definitions for this EnumDescriptor.
 func (g *Generator) generateEnum(enum *EnumDescriptor) {
 	// The full type name
@@ -899,8 +990,14 @@
 	}
 	enum := ""
 	if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
+		// We avoid using obj.PackageName(), because we want to use the
+		// original (proto-world) package name.
 		obj := g.ObjectNamed(proto.GetString(field.TypeName))
-		enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
+		enum = ",enum="
+		if pkg := proto.GetString(obj.File().Package); pkg != "" {
+			enum += pkg + "."
+		}
+		enum += CamelCaseSlice(obj.TypeName())
 	}
 	packed := ""
 	if field.Options != nil && proto.GetBool(field.Options.Packed) {
@@ -1016,6 +1113,8 @@
 
 func (g *Generator) RecordTypeUse(t string) {
 	if obj, ok := g.typeNameToObject[t]; ok {
+		// Call ObjectNamed to get the true object to record the use.
+		obj = g.ObjectNamed(t)
 		g.usedPackages[obj.PackageName()] = true
 	}
 }
@@ -1198,7 +1297,11 @@
 }
 
 func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
-	pkg := g.packageName + "." // We always print the full package name here.
+	// // We always print the full (proto-world) package name here.
+	pkg := proto.GetString(enum.File().Package)
+	if pkg != "" {
+		pkg += "."
+	}
 	// The full type name
 	typeName := enum.TypeName()
 	// The full type name, CamelCased.
diff --git a/compiler/testdata/imp.pb.go.golden b/compiler/testdata/imp.pb.go.golden
new file mode 100644
index 0000000..b09597a
--- /dev/null
+++ b/compiler/testdata/imp.pb.go.golden
@@ -0,0 +1,107 @@
+// Code generated by protoc-gen-go from "imp.proto"
+// DO NOT EDIT!
+
+package imp
+
+import proto "goprotobuf.googlecode.com/hg/proto"
+import "math"
+import "os"
+import imp1 "imp2.pb"
+
+// Reference proto, math & os imports to suppress error if they are not otherwise used.
+var _ = proto.GetString
+var _ = math.Inf
+var _ os.Error
+
+// Types from public import imp2.proto
+type PubliclyImportedMessage imp1.PubliclyImportedMessage
+
+func (this *PubliclyImportedMessage) Reset() { (*imp1.PubliclyImportedMessage)(this).Reset() }
+func (this *PubliclyImportedMessage) String() string {
+	return (*imp1.PubliclyImportedMessage)(this).String()
+}
+
+// PubliclyImportedMessage from public import imp.proto
+
+type ImportedMessage_Owner int32
+
+const (
+	ImportedMessage_DAVE = 1
+	ImportedMessage_MIKE = 2
+)
+
+var ImportedMessage_Owner_name = map[int32]string{
+	1: "DAVE",
+	2: "MIKE",
+}
+var ImportedMessage_Owner_value = map[string]int32{
+	"DAVE": 1,
+	"MIKE": 2,
+}
+
+func NewImportedMessage_Owner(x int32) *ImportedMessage_Owner {
+	e := ImportedMessage_Owner(x)
+	return &e
+}
+func (x ImportedMessage_Owner) String() string {
+	return proto.EnumName(ImportedMessage_Owner_name, int32(x))
+}
+
+type ImportedMessage struct {
+	Field            *int64 `protobuf:"varint,1,req,name=field"`
+	XXX_extensions   map[int32][]byte
+	XXX_unrecognized []byte
+}
+
+func (this *ImportedMessage) Reset()         { *this = ImportedMessage{} }
+func (this *ImportedMessage) String() string { return proto.CompactTextString(this) }
+
+var extRange_ImportedMessage = []proto.ExtensionRange{
+	proto.ExtensionRange{90, 100},
+}
+
+func (*ImportedMessage) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_ImportedMessage
+}
+func (this *ImportedMessage) ExtensionMap() map[int32][]byte {
+	if this.XXX_extensions == nil {
+		this.XXX_extensions = make(map[int32][]byte)
+	}
+	return this.XXX_extensions
+}
+
+type ImportedExtendable struct {
+	XXX_extensions   map[int32][]byte
+	XXX_unrecognized []byte
+}
+
+func (this *ImportedExtendable) Reset()         { *this = ImportedExtendable{} }
+func (this *ImportedExtendable) String() string { return proto.CompactTextString(this) }
+
+func (this *ImportedExtendable) Marshal() ([]byte, os.Error) {
+	return proto.MarshalMessageSet(this.ExtensionMap())
+}
+func (this *ImportedExtendable) Unmarshal(buf []byte) os.Error {
+	return proto.UnmarshalMessageSet(buf, this.ExtensionMap())
+}
+// ensure ImportedExtendable satisfies proto.Marshaler and proto.Unmarshaler
+var _ proto.Marshaler = (*ImportedExtendable)(nil)
+var _ proto.Unmarshaler = (*ImportedExtendable)(nil)
+
+var extRange_ImportedExtendable = []proto.ExtensionRange{
+	proto.ExtensionRange{100, 536870911},
+}
+
+func (*ImportedExtendable) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_ImportedExtendable
+}
+func (this *ImportedExtendable) ExtensionMap() map[int32][]byte {
+	if this.XXX_extensions == nil {
+		this.XXX_extensions = make(map[int32][]byte)
+	}
+	return this.XXX_extensions
+}
+
+func init() {
+	proto.RegisterEnum("imp.ImportedMessage_Owner", ImportedMessage_Owner_name, ImportedMessage_Owner_value)
+}
diff --git a/compiler/testdata/imp.proto b/compiler/testdata/imp.proto
index 6bf2a13..b059db2 100644
--- a/compiler/testdata/imp.proto
+++ b/compiler/testdata/imp.proto
@@ -31,6 +31,8 @@
 
 package imp;
 
+import "imp2.proto";
+
 message ImportedMessage {
   required int64 field = 1;
 
diff --git a/compiler/testdata/imp2.proto b/compiler/testdata/imp2.proto
new file mode 100644
index 0000000..e69870a
--- /dev/null
+++ b/compiler/testdata/imp2.proto
@@ -0,0 +1,35 @@
+//
+// Copyright 2011 Google Inc.  All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package imp;
+
+message PubliclyImportedMessage {
+  optional int64 field = 1;
+}
diff --git a/compiler/testdata/test.pb.go.golden b/compiler/testdata/test.pb.go.golden
index 5b3e30d..83e6e43 100644
--- a/compiler/testdata/test.pb.go.golden
+++ b/compiler/testdata/test.pb.go.golden
@@ -6,7 +6,8 @@
 import proto "goprotobuf.googlecode.com/hg/proto"
 import "math"
 import "os"
-import imp "imp.pb"
+import imp1 "imp.pb"
+// discarding unused import multitest2 "multi1.pb"
 
 // Reference proto, math & os imports to suppress error if they are not otherwise used.
 var _ = proto.GetString
@@ -116,13 +117,13 @@
 }
 
 type Request struct {
-	Key              []int64                    `protobuf:"varint,1,rep,name=key"`
-	ImportedMessage  *imp.ImportedMessage       `protobuf:"bytes,2,opt,name=imported_message"`
-	Hue              *Request_Color             `protobuf:"varint,3,opt,name=hue,enum=my_test.Request_Color"`
-	Hat              *HatType                   `protobuf:"varint,4,opt,name=hat,enum=my_test.HatType,def=1"`
-	Owner            *imp.ImportedMessage_Owner `protobuf:"varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner"`
-	Deadline         *float32                   `protobuf:"fixed32,7,opt,name=deadline,def=inf"`
-	Somegroup        *Request_SomeGroup         `protobuf:"group,8,opt,name=SomeGroup"`
+	Key              []int64                     `protobuf:"varint,1,rep,name=key"`
+	ImportedMessage  *imp1.ImportedMessage       `protobuf:"bytes,2,opt,name=imported_message"`
+	Hue              *Request_Color              `protobuf:"varint,3,opt,name=hue,enum=my.test.Request_Color"`
+	Hat              *HatType                    `protobuf:"varint,4,opt,name=hat,enum=my.test.HatType,def=1"`
+	Owner            *imp1.ImportedMessage_Owner `protobuf:"varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner"`
+	Deadline         *float32                    `protobuf:"fixed32,7,opt,name=deadline,def=inf"`
+	Somegroup        *Request_SomeGroup          `protobuf:"group,8,opt,name=SomeGroup"`
 	XXX_unrecognized []byte
 }
 
@@ -233,10 +234,10 @@
 }
 
 func init() {
-	proto.RegisterEnum("my_test.HatType", HatType_name, HatType_value)
-	proto.RegisterEnum("my_test.Days", Days_name, Days_value)
-	proto.RegisterEnum("my_test.Request_Color", Request_Color_name, Request_Color_value)
-	proto.RegisterEnum("my_test.Reply_Entry_Game", Reply_Entry_Game_name, Reply_Entry_Game_value)
+	proto.RegisterEnum("my.test.HatType", HatType_name, HatType_value)
+	proto.RegisterEnum("my.test.Days", Days_name, Days_value)
+	proto.RegisterEnum("my.test.Request_Color", Request_Color_name, Request_Color_value)
+	proto.RegisterEnum("my.test.Reply_Entry_Game", Reply_Entry_Game_name, Reply_Entry_Game_value)
 	proto.RegisterExtension(E_ReplyExtensions_Time)
 	proto.RegisterExtension(E_Tag)
 }