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.