Add extensions to the generator.

R=r
CC=golang-dev
http://codereview.appspot.com/774041
diff --git a/README b/README
index c90ae44..6c12f05 100644
--- a/README
+++ b/README
@@ -24,9 +24,6 @@
 There is no support for RPC in Go using protocol buffers.  It may come
 once a standard RPC protocol develops for protobufs.
 
-Extensions are not supported by the plugin in this release, although
-they are in the library.  The work will be completed soon.
-
 There are no insertion points in the plugin.
 
 To install this code:
@@ -49,7 +46,7 @@
 them, with the support library, into your program.
 
 To compile the protocol buffer definition, write a Makefile in the
-style shown in the commentin the file Make.protobuf.  If your Makefile
+style shown in the comment in the file Make.protobuf.  If your Makefile
 includes Make.protobuf, the rest should follow automatically.  The
 generated code can be compiled separately or as part of a normal Go
 package.
diff --git a/compiler/main.go b/compiler/main.go
index f2cb3b2..5864cde 100644
--- a/compiler/main.go
+++ b/compiler/main.go
@@ -37,7 +37,6 @@
 	That may change.
 
 	Not supported yet:
-		Extensions
 		Services
 */
 
@@ -114,9 +113,10 @@
 type Descriptor struct {
 	common
 	*descriptor.DescriptorProto
-	parent   *Descriptor   // The containing message, if any.
-	nested   []*Descriptor // Inner messages, if any.
-	typename []string      // Cached typename vector.
+	parent   *Descriptor            // The containing message, if any.
+	nested   []*Descriptor          // Inner messages, if any.
+	ext      []*ExtensionDescriptor // Extensions, if any.
+	typename []string               // Cached typename vector.
 }
 
 // Return the elements of the dotted type name.  The package name is not part
@@ -188,12 +188,36 @@
 	return ""
 }
 
+// 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
+	*descriptor.FieldDescriptorProto
+	parent   *Descriptor // The containing message, if any.
+}
+
+// Return the elements of the dotted type name.
+func (e *ExtensionDescriptor) typeName() (s []string) {
+	name := proto.GetString(e.Name)
+	if e.parent == nil {
+		s = make([]string, 2)
+		s[0] = "E"  // top-level extension namespace
+	} else {
+		pname := e.parent.typeName()
+		s = make([]string, len(pname)+1)
+		copy(s, pname)
+	}
+	s[len(s)-1] = name
+	return s
+}
+
 // A file. Includes slices of all the messages and enums defined within it.
 // Those slices are constructed by WrapTypes.
 type FileDescriptor struct {
 	*descriptor.FileDescriptorProto
-	desc []*Descriptor     // All the messages defined in this file.
-	enum []*EnumDescriptor // All the enums defined in this file.
+	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.
 }
 
 // The package name we'll use in the generated code to refer to this file.
@@ -204,6 +228,20 @@
 	return proto.GetString(d.Package)
 }
 
+// Whether the proto library needs importing.
+// This will be true if there are any enums or any extensions, or any messages with extension ranges.
+func (d *FileDescriptor) needProtoImport() bool {
+	if len(d.enum) > 0 || len(d.ext) > 0 {
+		return true
+	}
+	for _, desc := range d.desc {
+		if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
+			return true
+		}
+	}
+	return false
+}
+
 // Simplify some things by abstracting the abilities shared by enums and messages.
 type Object interface {
 	packageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
@@ -308,15 +346,12 @@
 			break
 		}
 		// Install it.
-		if pkg != truePkg {
-			log.Stderr("renaming duplicate imported package named", truePkg, "to", pkg)
-		}
 		inUse[pkg] = true
 		uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
 	}
 }
 
-// Walk the incoming data, wrapping DescriptorProtos and EnumDescriptorProtos
+// Walk the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos and FileDescriptorProtos
 // into file-referenced objects within the Generator.  Also create the list of files
 // to generate
 func (g *Generator) WrapTypes() {
@@ -330,10 +365,12 @@
 		descs := WrapDescriptors(f)
 		g.BuildNestedDescriptors(descs)
 		enums := WrapEnumDescriptors(f, descs)
+		exts := WrapExtensions(f)
 		g.allFiles[i] = &FileDescriptor{
 			FileDescriptorProto: f,
 			desc:                descs,
 			enum:                enums,
+			ext:                 exts,
 		}
 	}
 
@@ -373,13 +410,20 @@
 
 // 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.ext = make([]*ExtensionDescriptor, len(desc.Extension))
+	for i, field := range desc.Extension {
+		d.ext[i] = &ExtensionDescriptor{common{file: file}, field, d}
+	}
+
 	if len(sl) == cap(sl) {
 		nsl := make([]*Descriptor, len(sl), 2*len(sl))
 		copy(nsl, sl)
 		sl = nsl
 	}
 	sl = sl[0 : len(sl)+1]
-	sl[len(sl)-1] = &Descriptor{common{file: file}, desc, parent, nil, nil}
+	sl[len(sl)-1] = d
 	return sl
 }
 
@@ -437,6 +481,15 @@
 	return sl
 }
 
+// Return a slice of all the top-level ExtensionDescriptors defined within this file.
+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}
+	}
+	return sl
+}
+
 // Build the map from fully qualified type names to objects.  The key for the map
 // comes from the input data, which puts a period at the beginning.
 func (g *Generator) BuildTypeNameMap() {
@@ -525,6 +578,9 @@
 	for _, desc := range g.file.desc {
 		g.GenerateMessage(desc)
 	}
+	for _, ext := range g.file.ext {
+		g.GenerateExtension(ext)
+	}
 	g.GenerateInitFunction()
 }
 
@@ -539,7 +595,7 @@
 
 // Generate the header, including package definition and imports
 func (g *Generator) GenerateImports() {
-	if len(g.file.enum) > 0 {
+	if g.file.needProtoImport() {
 		g.p(`import "goprotobuf.googlecode.com/hg/proto"`)
 	}
 	for _, s := range g.file.Dependency {
@@ -763,6 +819,9 @@
 		tag := g.GoTag(field, wiretype)
 		g.p(fieldname, "\t", typename, "\t", tag)
 	}
+	if len(message.ExtensionRange) > 0 {
+		g.p("XXX_extensions\t\tmap[int32][]byte")
+	}
 	g.p("XXX_unrecognized\t[]byte")
 	g.out()
 	g.p("}")
@@ -779,6 +838,34 @@
 	g.out()
 	g.p("}")
 
+	// Extension support methods
+	if len(message.ExtensionRange) > 0 {
+		g.p()
+		g.p("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
+		g.in()
+		for _, r := range message.ExtensionRange {
+			end := fmt.Sprint(*r.End - 1)  // make range inclusive on both ends
+			g.p("proto.ExtensionRange{", r.Start, ", ", end, "},")
+		}
+		g.out()
+		g.p("}")
+		g.p("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
+		g.in()
+		g.p("return extRange_", ccTypeName)
+		g.out()
+		g.p("}")
+		g.p("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
+		g.in()
+		g.p("if this.XXX_extensions == nil {")
+		g.in()
+		g.p("this.XXX_extensions = make(map[int32][]byte)")
+		g.out()
+		g.p("}")
+		g.p("return this.XXX_extensions")
+		g.out()
+		g.p("}")
+	}
+
 	// Default constants
 	for _, field := range message.Field {
 		def := proto.GetString(field.DefaultValue)
@@ -798,7 +885,7 @@
 		case typename == "[]byte":
 			def = "[]byte(" + Quote(def) + ")"
 			kind = "var "
-		case 	*field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
+		case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
 			// Must be an enum.  Need to construct the prefixed name.
 			obj := g.objectNamed(proto.GetString(field.TypeName))
 			enum, ok := obj.(*EnumDescriptor)
@@ -811,6 +898,37 @@
 		g.p(kind, fieldname, " ", typename, " = ", def)
 	}
 	g.p()
+
+	for _, ext := range message.ext {
+		g.GenerateExtension(ext)
+	}
+}
+
+// Generate the extension descriptor for this ExtensionDescriptor.
+func (g *Generator) GenerateExtension(ext *ExtensionDescriptor) {
+	// The full type name
+	typeName := ext.typeName()
+	// Each scope of the extension is individually CamelCased, and all are joined with "_".
+	for i, s := range typeName {
+		typeName[i] = CamelCase(s)
+	}
+	ccTypeName := strings.Join(typeName, "_")
+
+	extendedType := "*" + g.TypeName(g.objectNamed(*ext.Extendee))
+	field := ext.FieldDescriptorProto
+	fieldType, wireType := g.GoType(ext.parent, field)
+	tag := g.GoTag(field, wireType)
+
+	g.p("var ", ccTypeName, " = &proto.ExtensionDesc{")
+	g.in()
+	g.p("ExtendedType: (", extendedType, ")(nil),")
+	g.p("ExtensionType: (", fieldType, ")(nil),")
+	g.p("Field: ", field.Number, ",")
+	g.p("Tag: ", tag, ",")
+
+	g.out()
+	g.p("}")
+	g.p()
 }
 
 func (g *Generator) GenerateInitFunction() {
diff --git a/compiler/testdata/Makefile b/compiler/testdata/Makefile
index 0cc9ad1..3eb2efb 100644
--- a/compiler/testdata/Makefile
+++ b/compiler/testdata/Makefile
@@ -37,9 +37,10 @@
 include $(GOROOT)/src/Make.common
 include $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/Make.protobuf
 
-CLEANFILES+=*.pb.go
+CLEANFILES+=*.pb.go extension_test
 
-test:	golden testbuild
+test:	golden testbuild extension_test
+	./extension_test
 	@echo PASS
 
 golden:
@@ -51,6 +52,9 @@
 testbuild:	main.$O
 	$(LD) -L. main.$O
 
+extension_test:	extension_test.$O
+	$(LD) -L. -o $@ $< 
+
 multi.a: multi3.pb.$O multi2.pb.$O multi1.pb.$O
 	rm -f multi.a
 	$(QUOTED_GOBIN)/gopack grc $@ $<
@@ -61,3 +65,4 @@
 test.pb.go:	imp.pb.go
 multi1.pb.go:	multi2.pb.go multi3.pb.go
 main.$O: imp.pb.$O test.pb.$O multi.a
+extension_test.$O: extension_base.pb.$O extension_user.pb.$O
diff --git a/compiler/testdata/extension_base.proto b/compiler/testdata/extension_base.proto
new file mode 100644
index 0000000..5f91628
--- /dev/null
+++ b/compiler/testdata/extension_base.proto
@@ -0,0 +1,38 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 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 extension_base;
+
+message BaseMessage {
+  optional int32 height = 1;
+  extensions 4 to 9;
+  extensions 16 to max;
+}
diff --git a/compiler/testdata/extension_test.go b/compiler/testdata/extension_test.go
new file mode 100644
index 0000000..1ddb229
--- /dev/null
+++ b/compiler/testdata/extension_test.go
@@ -0,0 +1,159 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 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.
+
+// Test that we can use protocol buffers that use extensions.
+
+package main
+
+import (
+	"testing"
+
+	"goprotobuf.googlecode.com/hg/proto"
+	base "extension_base.pb"
+	user "extension_user.pb"
+)
+
+func TestSingleFieldExtension(t *testing.T) {
+	bm := base.NewBaseMessage()
+	bm.Height = proto.Int32(178)
+
+	// Use extension within scope of another type.
+	vol := proto.Uint32(11)
+	t.Logf("bm: %T, user.LoudMessage_Volume: %T", bm, user.LoudMessage_Volume)
+	err := proto.SetExtension(bm, user.LoudMessage_Volume, vol)
+	if err != nil {
+		t.Fatal("Failed setting extension:", err)
+	}
+	buf, err := proto.Marshal(bm)
+	if err != nil {
+		t.Fatal("Failed encoding message with extension:", err)
+	}
+	bm_new := base.NewBaseMessage()
+	if err := proto.Unmarshal(buf, bm_new); err != nil {
+		t.Fatal("Failed decoding message with extension:", err)
+	}
+	if !proto.HasExtension(bm_new, user.LoudMessage_Volume) {
+		t.Fatal("Decoded message didn't contain extension.")
+	}
+	vol_out, err := proto.GetExtension(bm_new, user.LoudMessage_Volume)
+	if err != nil {
+		t.Fatal("Failed getting extension:", err)
+	}
+	if v := vol_out.(*uint32); *v != *vol {
+		t.Errorf("vol_out = %v, expected %v", *v, *vol)
+	}
+	proto.ClearExtension(bm_new, user.LoudMessage_Volume)
+	if proto.HasExtension(bm_new, user.LoudMessage_Volume) {
+		t.Fatal("Failed clearing extension.")
+	}
+}
+
+func TestMessageExtension(t *testing.T) {
+	bm := base.NewBaseMessage()
+	bm.Height = proto.Int32(179)
+
+	// Use extension that is itself a message.
+	um := &user.UserMessage{
+		Name: proto.String("Dave"),
+		Rank: proto.String("Major"),
+	}
+	err := proto.SetExtension(bm, user.LoginMessage_UserMessage, um)
+	if err != nil {
+		t.Fatal("Failed setting extension:", err)
+	}
+	buf, err := proto.Marshal(bm)
+	if err != nil {
+		t.Fatal("Failed encoding message with extension:", err)
+	}
+	bm_new := base.NewBaseMessage()
+	if err := proto.Unmarshal(buf, bm_new); err != nil {
+		t.Fatal("Failed decoding message with extension:", err)
+	}
+	if !proto.HasExtension(bm_new, user.LoginMessage_UserMessage) {
+		t.Fatal("Decoded message didn't contain extension.")
+	}
+	um_out, err := proto.GetExtension(bm_new, user.LoginMessage_UserMessage)
+	if err != nil {
+		t.Fatal("Failed getting extension:", err)
+	}
+	if n := um_out.(*user.UserMessage).Name; *n != *um.Name {
+		t.Errorf("um_out.Name = %q, expected %q", *n, *um.Name)
+	}
+	if r := um_out.(*user.UserMessage).Rank; *r != *um.Rank {
+		t.Errorf("um_out.Rank = %q, expected %q", *r, *um.Rank)
+	}
+	proto.ClearExtension(bm_new, user.LoginMessage_UserMessage)
+	if proto.HasExtension(bm_new, user.LoginMessage_UserMessage) {
+		t.Fatal("Failed clearing extension.")
+	}
+}
+
+func TestTopLevelExtension(t *testing.T) {
+	bm := base.NewBaseMessage()
+	bm.Height = proto.Int32(179)
+
+	width := proto.Int32(17)
+	err := proto.SetExtension(bm, user.E_Width, width)
+	if err != nil {
+		t.Fatal("Failed setting extension:", err)
+	}
+	buf, err := proto.Marshal(bm)
+	if err != nil {
+		t.Fatal("Failed encoding message with extension:", err)
+	}
+	bm_new := base.NewBaseMessage()
+	if err := proto.Unmarshal(buf, bm_new); err != nil {
+		t.Fatal("Failed decoding message with extension:", err)
+	}
+	if !proto.HasExtension(bm_new, user.E_Width) {
+		t.Fatal("Decoded message didn't contain extension.")
+	}
+	width_out, err := proto.GetExtension(bm_new, user.E_Width)
+	if err != nil {
+		t.Fatal("Failed getting extension:", err)
+	}
+	if w := width_out.(*int32); *w != *width {
+		t.Errorf("width_out = %v, expected %v", *w, *width)
+	}
+	proto.ClearExtension(bm_new, user.E_Width)
+	if proto.HasExtension(bm_new, user.E_Width) {
+		t.Fatal("Failed clearing extension.")
+	}
+}
+
+func main() {
+	// simpler than rigging up gotest
+	testing.Main([]testing.Test{
+		testing.Test{"TestSingleFieldExtension", TestSingleFieldExtension},
+		testing.Test{"TestMessageExtension", TestMessageExtension},
+		testing.Test{"TestTopLevelExtension", TestTopLevelExtension},
+	})
+}
diff --git a/compiler/testdata/extension_user.proto b/compiler/testdata/extension_user.proto
new file mode 100644
index 0000000..6fb3d72
--- /dev/null
+++ b/compiler/testdata/extension_user.proto
@@ -0,0 +1,73 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 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.
+
+import "extension_base.proto";
+
+package extension_user;
+
+message UserMessage {
+  optional string name = 1;
+  optional string rank = 2;
+}
+
+// Extend with a message
+extend extension_base.BaseMessage {
+  optional UserMessage user_message = 5;
+}
+
+// Extend with some primitive types
+extend extension_base.BaseMessage {
+  optional int32 width = 6;
+  optional int64 area = 7;
+}
+
+// Extend inside the scope of another type
+message LoudMessage {
+  extend extension_base.BaseMessage {
+    optional uint32 volume = 8;
+  }
+  extensions 100 to max;
+}
+
+// Extend inside the scope of another type, using a message.
+message LoginMessage {
+  extend extension_base.BaseMessage {
+    required UserMessage user_message = 16;
+  }
+}
+
+// An extension of an extension
+message Announcement {
+  optional string words = 1;
+  extend LoudMessage {
+    optional Announcement loud_ext = 100;
+  }
+}
diff --git a/compiler/testdata/test.pb.go.golden b/compiler/testdata/test.pb.go.golden
index 0d7a7cb..6807d20 100644
--- a/compiler/testdata/test.pb.go.golden
+++ b/compiler/testdata/test.pb.go.golden
@@ -84,6 +84,7 @@
 
 type Reply struct {
 	Found	[]*Reply_Entry	"PB(bytes,1,rep,name=found)"
+	XXX_extensions		map[int32][]byte
 	XXX_unrecognized	[]byte
 }
 func (this *Reply) Reset() {
@@ -93,6 +94,19 @@
 	return new(Reply)
 }
 
+var extRange_Reply = []proto.ExtensionRange{
+	proto.ExtensionRange{100, 536870911},
+}
+func (*Reply) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_Reply
+}
+func (this *Reply) ExtensionMap() map[int32][]byte {
+	if this.XXX_extensions == nil {
+		this.XXX_extensions = make(map[int32][]byte)
+	}
+	return this.XXX_extensions
+}
+
 type Reply_Entry struct {
 	KeyThatNeeds_1234camel_CasIng	*int64	"PB(varint,1,req,name=key_that_needs_1234camel_CasIng)"
 	Value	*int64	"PB(varint,2,opt,name=value,def=7)"
@@ -117,6 +131,20 @@
 	return new(ReplyExtensions)
 }
 
+var ReplyExtensions_Time = &proto.ExtensionDesc{
+	ExtendedType: (*Reply)(nil),
+	ExtensionType: (*float64)(nil),
+	Field: 101,
+	Tag: "PB(fixed64,101,opt,name=time)",
+}
+
+var E_Tag = &proto.ExtensionDesc{
+	ExtendedType: (*Reply)(nil),
+	ExtensionType: (*string)(nil),
+	Field: 103,
+	Tag: "PB(bytes,103,opt,name=tag)",
+}
+
 func init() {
 	proto.RegisterEnum("my_test.HatType", HatType_name, HatType_value)
 	proto.RegisterEnum("my_test.Days", Days_name, Days_value)
diff --git a/compiler/testdata/test.proto b/compiler/testdata/test.proto
index f7e7a3d..ef91b03 100644
--- a/compiler/testdata/test.proto
+++ b/compiler/testdata/test.proto
@@ -73,3 +73,8 @@
     optional double time = 101;
   }
 }
+
+// top-level extension
+extend Reply {
+  optional string tag = 103;
+}
diff --git a/proto/extensions.go b/proto/extensions.go
index 480e8d3..3726d3c 100644
--- a/proto/extensions.go
+++ b/proto/extensions.go
@@ -128,7 +128,7 @@
 	return unsafe.Unreflect(t, base), nil
 }
 
-// TODO(: (needed for repeated extensions)
+// TODO: (needed for repeated extensions)
 //   - ExtensionSize
 //   - AddExtension