Protobuf: protoc-gen-go: compiling nested extensions generates invalid code

Created on 26 Sep 2017  路  5Comments  路  Source: golang/protobuf

When trying to use extensions nested in a message, a build error happens with a error message like "cannot convert nil to type bool."

In generator.go, I'm expecting the extension fields should reach needsStar(), but actually they won't because they have a parent and the parent's syntax is considered as proto3.

https://github.com/golang/protobuf/blob/130e6b02ab059e7b717a096f397c5b60111cae74/protoc-gen-go/generator/generator.go#L1674-L1680

Versions

protoc version: libprotoc 3.3.2
golang/protobuf: using the current master https://github.com/golang/protobuf/tree/130e6b02ab059e7b717a096f397c5b60111cae74

Command to reproduce the issue

~/go/src/github.com/occho/protobuf-test/test$ pwd
<home>/go/src/github.com/occho/protobuf-test/test
~/go/src/github.com/occho/protobuf-test/test$ ls
test.proto
~/go/src/github.com/occho/protobuf-test/test$ protoc --go_out=${GOPATH}/src test.proto
~/go/src/github.com/occho/protobuf-test/test$ go build
# github.com/occho/protobuf-test/test
./test.pb.go:41:23: cannot convert nil to type bool
./test.pb.go:50:25: cannot convert nil to type string
./test.pb.go:59:24: cannot convert nil to type int32

File test.proto

syntax = "proto3";

package test;

import "google/protobuf/descriptor.proto";

option go_package = "github.com/occho/protobuf-test/test";

message Namespace {
  extend google.protobuf.FieldOptions {
    bool namespace_bool = 50001;
    string namespace_string = 50002;
    int32 namespace_int32 = 50003;
  }
}

extend google.protobuf.FieldOptions {
  bool my_bool = 51001;
  string my_string = 51002;
  int32 my_int32 = 51003;
}

Generated file test.pb.go

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: test.proto

/*
Package test is a generated protocol buffer package.

It is generated from these files:
    test.proto

It has these top-level messages:
    Namespace
*/
package test

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import google_protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

type Namespace struct {
}

func (m *Namespace) Reset()                    { *m = Namespace{} }
func (m *Namespace) String() string            { return proto.CompactTextString(m) }
func (*Namespace) ProtoMessage()               {}
func (*Namespace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }

var E_Namespace_NamespaceBool = &proto.ExtensionDesc{
    ExtendedType:  (*google_protobuf.FieldOptions)(nil),
    ExtensionType: (bool)(nil),
    Field:         50001,
    Name:          "test.Namespace.namespace_bool",
    Tag:           "varint,50001,opt,name=namespace_bool,json=namespaceBool",
    Filename:      "test.proto",
}

var E_Namespace_NamespaceString = &proto.ExtensionDesc{
    ExtendedType:  (*google_protobuf.FieldOptions)(nil),
    ExtensionType: (string)(nil),
    Field:         50002,
    Name:          "test.Namespace.namespace_string",
    Tag:           "bytes,50002,opt,name=namespace_string,json=namespaceString",
    Filename:      "test.proto",
}

var E_Namespace_NamespaceInt32 = &proto.ExtensionDesc{
    ExtendedType:  (*google_protobuf.FieldOptions)(nil),
    ExtensionType: (int32)(nil),
    Field:         50003,
    Name:          "test.Namespace.namespace_int32",
    Tag:           "varint,50003,opt,name=namespace_int32,json=namespaceInt32",
    Filename:      "test.proto",
}

var E_MyBool = &proto.ExtensionDesc{
    ExtendedType:  (*google_protobuf.FieldOptions)(nil),
    ExtensionType: (*bool)(nil),
    Field:         51001,
    Name:          "test.my_bool",
    Tag:           "varint,51001,opt,name=my_bool,json=myBool",
    Filename:      "test.proto",
}

var E_MyString = &proto.ExtensionDesc{
    ExtendedType:  (*google_protobuf.FieldOptions)(nil),
    ExtensionType: (*string)(nil),
    Field:         51002,
    Name:          "test.my_string",
    Tag:           "bytes,51002,opt,name=my_string,json=myString",
    Filename:      "test.proto",
}

var E_MyInt32 = &proto.ExtensionDesc{
    ExtendedType:  (*google_protobuf.FieldOptions)(nil),
    ExtensionType: (*int32)(nil),
    Field:         51003,
    Name:          "test.my_int32",
    Tag:           "varint,51003,opt,name=my_int32,json=myInt32",
    Filename:      "test.proto",
}

func init() {
    proto.RegisterType((*Namespace)(nil), "test.Namespace")
    proto.RegisterExtension(E_Namespace_NamespaceBool)
    proto.RegisterExtension(E_Namespace_NamespaceString)
    proto.RegisterExtension(E_Namespace_NamespaceInt32)
    proto.RegisterExtension(E_MyBool)
    proto.RegisterExtension(E_MyString)
    proto.RegisterExtension(E_MyInt32)
}

func init() { proto.RegisterFile("test.proto", fileDescriptor0) }

var fileDescriptor0 = []byte{
    // 267 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0x2d, 0x2e,
    0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0xa5, 0x14, 0xd2, 0xf3, 0xf3, 0xd3,
    0x73, 0x52, 0xf5, 0xc1, 0x62, 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa9, 0xc5, 0xc9, 0x45, 0x99, 0x05,
    0x25, 0xf9, 0x45, 0x10, 0x75, 0x4a, 0x2f, 0x19, 0xb9, 0x38, 0xfd, 0x12, 0x73, 0x53, 0x8b, 0x0b,
    0x12, 0x93, 0x53, 0x8d, 0xdc, 0xb8, 0xf8, 0xf2, 0x60, 0x9c, 0xf8, 0xa4, 0xfc, 0xfc, 0x1c, 0x21,
    0x59, 0x3d, 0x88, 0x11, 0x7a, 0x30, 0x23, 0xf4, 0xdc, 0x32, 0x53, 0x73, 0x52, 0xfc, 0x0b, 0x4a,
    0x32, 0xf3, 0xf3, 0x8a, 0x25, 0x2e, 0xb6, 0x31, 0x2b, 0x30, 0x6a, 0x70, 0x04, 0xf1, 0xc2, 0xb5,
    0x39, 0xe5, 0xe7, 0xe7, 0x18, 0x79, 0x71, 0x09, 0x20, 0xcc, 0x29, 0x2e, 0x29, 0xca, 0xcc, 0x4b,
    0x27, 0x64, 0xd2, 0x25, 0xb0, 0x49, 0x9c, 0x41, 0xfc, 0x70, 0x8d, 0xc1, 0x60, 0x7d, 0x46, 0x1e,
    0x5c, 0x08, 0xa1, 0xf8, 0xcc, 0xbc, 0x12, 0x63, 0x23, 0x42, 0x46, 0x5d, 0x06, 0x1b, 0xc5, 0x1a,
    0x84, 0xf0, 0x8b, 0x27, 0x48, 0x9b, 0x95, 0x05, 0x17, 0x7b, 0x6e, 0x25, 0x51, 0xde, 0xda, 0xd9,
    0x07, 0xf1, 0x16, 0x5b, 0x6e, 0x25, 0xc8, 0x3f, 0x56, 0x36, 0x5c, 0x9c, 0xb9, 0x95, 0x44, 0x7a,
    0x64, 0x57, 0x1f, 0xc4, 0x23, 0x1c, 0xb9, 0x95, 0x10, 0x1f, 0x58, 0x59, 0x71, 0x71, 0xe4, 0x56,
    0x12, 0xe7, 0xf4, 0xdd, 0x7d, 0x10, 0xa7, 0xb3, 0xe7, 0x56, 0x82, 0xdd, 0xec, 0xa4, 0x1a, 0xa5,
    0x9c, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x9f, 0x9c, 0x9c, 0x91,
    0x0f, 0x8f, 0x4d, 0x5d, 0x50, 0x1c, 0xeb, 0x83, 0x88, 0x24, 0x36, 0xb0, 0x98, 0x31, 0x20, 0x00,
    0x00, 0xff, 0xff, 0x6c, 0xa5, 0xed, 0x4d, 0x03, 0x02, 0x00, 0x00,
}

This issue is originally posted in https://groups.google.com/forum/#!topic/protobuf/MCtjXxkndzw.

bug

Most helpful comment

@occho, I've looked a bit at the code and IMO your proposal, to pass the extended message to GoType instead of the enclosing message, is correct. Since extensions are proto2 (old syntax, seem to be getting less attention from the proto team), I think you'll have higher chance of getting a fix landed if you send a PR, instead of waiting for a fix.

All 5 comments

I don't know what side-effect it has, but when passing extDesc instead of ext.parent, the error disappears.

https://github.com/golang/protobuf/blob/130e6b02ab059e7b717a096f397c5b60111cae74/protoc-gen-go/generator/generator.go#L2586

@occho, I've looked a bit at the code and IMO your proposal, to pass the extended message to GoType instead of the enclosing message, is correct. Since extensions are proto2 (old syntax, seem to be getting less attention from the proto team), I think you'll have higher chance of getting a fix landed if you send a PR, instead of waiting for a fix.

The v2 rewrite of protoc-gen-go fixes this issue (see http://golang.org/cl/144282). Unfortunately, the fix won't land in protoc-gen-go for some period of time.

This should be fixed in the v1.20.0 release

The v1.20.0 release fixes this issue.

Was this page helpful?
0 / 5 - 0 ratings