all: implement proto1 weak fields

This implements generation of and reflection support for weak fields.
Weak fields are a proto1 feature where the "weak" option can be specified
on a singular message field. A weak reference results in generated code
that does not directly link in the dependency containing the weak message.

Weak field support is not added to any of the serialization logic.

Change-Id: I08ccfa72bc80b2ffb6af527a1677a0a81dcf33fb
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185399
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Joe Tsai 2019-04-08 13:52:14 -07:00
parent 691d8563c7
commit 3d8e369c4e
19 changed files with 1691 additions and 1160 deletions

View File

@ -403,6 +403,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
g.P("state ", protoimplPackage.Ident("MessageState"))
sf.append("state")
}
hasWeak := false
for _, field := range message.Fields {
if field.Oneof != nil {
// It would be a bit simpler to iterate over the oneofs below,
@ -443,8 +444,14 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)),
)
}
g.Annotate(message.GoIdent.GoName+"."+field.GoName, field.Location)
g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
name := field.GoName
if field.Desc.IsWeak() {
hasWeak = true
name = "XXX_weak_" + name
}
g.Annotate(message.GoIdent.GoName+"."+name, field.Location)
g.P(name, " ", goType, " `", strings.Join(tags, " "), "`",
deprecationComment(field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()))
sf.append(field.GoName)
}
@ -460,6 +467,10 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
g.P("sizeCache", " ", protoimplPackage.Ident("SizeCache"))
sf.append("sizeCache")
}
if hasWeak {
g.P("XXX_weak", " ", protoimplPackage.Ident("WeakFields"), " `json:\"-\"`")
sf.append("XXX_weak")
}
if generateExportedUnknownFields {
g.P("XXX_unrecognized", " ", protoimplPackage.Ident("UnknownFields"), " `json:\"-\"`")
sf.append("XXX_unrecognized")
@ -591,6 +602,19 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
g.P(deprecationComment(true))
}
g.Annotate(message.GoIdent.GoName+".Get"+field.GoName, field.Location)
if field.Desc.IsWeak() {
g.P("func (x *", message.GoIdent, ") Get", field.GoName, "() ", protoifacePackage.Ident("MessageV1"), "{")
g.P("if x != nil {")
g.P("v := x.XXX_weak[", field.Desc.Number(), "]")
g.P("_ = x.XXX_weak_" + field.GoName) // for field tracking
g.P("if v != nil {")
g.P("return v")
g.P("}")
g.P("}")
g.P("return ", protoimplPackage.Ident("X"), ".WeakNil(", strconv.Quote(string(field.Message.Desc.FullName())), ")")
g.P("}")
continue
}
g.P("func (x *", message.GoIdent, ") Get", field.GoName, "() ", goType, " {")
if field.Oneof != nil {
g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", fieldOneofType(field), "); ok {")
@ -614,6 +638,25 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
g.P()
}
// Setter methods.
for _, field := range message.Fields {
if field.Desc.IsWeak() {
g.Annotate(message.GoIdent.GoName+".Set"+field.GoName, field.Location)
g.P("func (x *", message.GoIdent, ") Set", field.GoName, "(v ", protoifacePackage.Ident("MessageV1"), ") {")
g.P("if x.XXX_weak == nil {")
g.P("x.XXX_weak = make(", protoimplPackage.Ident("WeakFields"), ")")
g.P("}")
g.P("if v == nil {")
g.P("delete(x.XXX_weak, ", field.Desc.Number(), ")")
g.P("} else {")
g.P("x.XXX_weak[", field.Desc.Number(), "] = v")
g.P("x.XXX_weak_"+field.GoName, " = struct{}{}") // for field tracking
g.P("}")
g.P("}")
g.P()
}
}
// Oneof wrapper types.
if len(message.Oneofs) > 0 {
genOneofWrappers(gen, g, f, message)
@ -624,6 +667,10 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
//
// If it returns pointer=true, the struct field is a pointer to the type.
func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
if field.Desc.IsWeak() {
return "struct{}", false
}
pointer = true
switch field.Desc.Kind() {
case protoreflect.BoolKind:

View File

@ -48,8 +48,8 @@ func Test(t *testing.T) {
if *regenerate {
t.Run("Generate", func(t *testing.T) {
fmt.Print(mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-types", "-execute"))
fmt.Print(mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-protos", "-execute"))
fmt.Print(mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-types", "-execute"))
fmt.Print(mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-protos", "-execute"))
files := strings.Split(strings.TrimSpace(mustRunCommand(t, ".", "git", "ls-files", "*.go")), "\n")
mustRunCommand(t, ".", append([]string{"gofmt", "-w"}, files...)...)
})
@ -94,11 +94,11 @@ func Test(t *testing.T) {
mustRunCommand(t, ".", runner, "--failure_list", failureList, "--enforce_recommended", driver)
})
t.Run("GeneratedGoFiles", func(t *testing.T) {
diff := mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-types")
diff := mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-types")
if strings.TrimSpace(diff) != "" {
t.Fatalf("stale generated files:\n%v", diff)
}
diff = mustRunCommand(t, ".", "go", "run", "./internal/cmd/generate-protos")
diff = mustRunCommand(t, ".", "go", "run", "-tags", "proto1_legacy", "./internal/cmd/generate-protos")
if strings.TrimSpace(diff) != "" {
t.Fatalf("stale generated files:\n%v", diff)
}

View File

@ -170,9 +170,6 @@ func Marshal(fd pref.FieldDescriptor, enumName string) string {
if fd.IsPacked() {
tag = append(tag, "packed")
}
if fd.IsWeak() {
tag = append(tag, "weak="+string(fd.Message().FullName()))
}
name := string(fd.Name())
if fd.Kind() == pref.GroupKind {
// The name of the FieldDescriptor for a group field is
@ -186,6 +183,9 @@ func Marshal(fd pref.FieldDescriptor, enumName string) string {
// the exact same semantics from the previous generator.
tag = append(tag, "json="+jsonName)
}
if fd.IsWeak() {
tag = append(tag, "weak="+string(fd.Message().FullName()))
}
// The previous implementation does not tag extension fields as proto3,
// even when the field is defined in a proto3 file. Match that behavior
// for consistency.

View File

@ -15,6 +15,7 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
testpb "google.golang.org/protobuf/internal/testprotos/test"
_ "google.golang.org/protobuf/internal/testprotos/test/weak1"
"google.golang.org/protobuf/types/descriptorpb"
)
@ -104,14 +105,28 @@ func visitFields(m protoreflect.Message, f func(protoreflect.FieldDescriptor)) {
func TestWeakInit(t *testing.T) {
file := testpb.File_test_test_proto
fd := file.Messages().ByName("TestWeak").Fields().ByName("weak_message")
if want, got := fd.IsWeak(), true; got != want {
t.Errorf("field %v: IsWeak() = %v, want %v", fd.FullName(), want, got)
// We do not expect to get a placeholder since weak1 is imported.
fd1 := file.Messages().ByName("TestWeak").Fields().ByName("weak_message1")
if got, want := fd1.IsWeak(), true; got != want {
t.Errorf("field %v: IsWeak() = %v, want %v", fd1.FullName(), got, want)
}
if want, got := fd.Message().IsPlaceholder(), false; got != want {
t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd.FullName(), want, got)
if got, want := fd1.Message().IsPlaceholder(), false; got != want {
t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd1.FullName(), got, want)
}
if fd.Message().Fields().Len() == 0 {
t.Errorf("field %v: Message().Fields().Len() == 0, want >0", fd.FullName())
if got, want := fd1.Message().Fields().Len(), 1; got != want {
t.Errorf("field %v: Message().Fields().Len() == %d, want %d", fd1.FullName(), got, want)
}
// We do expect to get a placeholder since weak2 is not imported.
fd2 := file.Messages().ByName("TestWeak").Fields().ByName("weak_message2")
if got, want := fd2.IsWeak(), true; got != want {
t.Errorf("field %v: IsWeak() = %v, want %v", fd2.FullName(), got, want)
}
if got, want := fd2.Message().IsPlaceholder(), true; got != want {
t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd2.FullName(), got, want)
}
if got, want := fd2.Message().Fields().Len(), 0; got != want {
t.Errorf("field %v: Message().Fields().Len() == %d, want %d", fd2.FullName(), got, want)
}
}

View File

@ -7,11 +7,15 @@ package impl
import (
"encoding/binary"
"encoding/json"
"fmt"
"hash/crc32"
"math"
"reflect"
"google.golang.org/protobuf/internal/errors"
pref "google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
piface "google.golang.org/protobuf/runtime/protoiface"
)
// These functions exist to support exported APIs in generated protobufs.
@ -74,3 +78,13 @@ func (Export) CompressGZIP(in []byte) (out []byte) {
out = append(out, gzipFooter[:]...)
return out
}
// WeakNil returns a typed nil pointer to a concrete message.
// It panics if the message is not linked into the binary.
func (Export) WeakNil(s pref.FullName) piface.MessageV1 {
mt, err := protoregistry.GlobalTypes.FindMessageByName(s)
if err == nil {
panic(fmt.Sprintf("weak message %v is not linked in", s))
}
return reflect.Zero(mt.GoType()).Interface().(piface.MessageV1)
}

View File

@ -120,18 +120,21 @@ func (mi *MessageInfo) initOnce() {
type (
SizeCache = int32
WeakFields = map[int32]piface.MessageV1
UnknownFields = []byte
ExtensionFields = map[int32]ExtensionField
)
var (
sizecacheType = reflect.TypeOf(SizeCache(0))
weakFieldsType = reflect.TypeOf(WeakFields(nil))
unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
)
type structInfo struct {
sizecacheOffset offset
weakOffset offset
unknownOffset offset
extensionOffset offset
@ -144,6 +147,7 @@ type structInfo struct {
func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
si := structInfo{
sizecacheOffset: invalidOffset,
weakOffset: invalidOffset,
unknownOffset: invalidOffset,
extensionOffset: invalidOffset,
@ -159,6 +163,9 @@ func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
if f, _ := t.FieldByName("XXX_sizecache"); f.Type == sizecacheType {
si.sizecacheOffset = offsetOf(f, mi.Exporter)
}
if f, _ := t.FieldByName("XXX_weak"); f.Type == weakFieldsType {
si.weakOffset = offsetOf(f, mi.Exporter)
}
if f, _ := t.FieldByName("unknownFields"); f.Type == unknownFieldsType {
si.unknownOffset = offsetOf(f, mi.Exporter)
}
@ -235,6 +242,8 @@ func (mi *MessageInfo) makeKnownFieldsFunc(si structInfo) {
fi = fieldInfoForMap(fd, fs, mi.Exporter)
case fd.IsList():
fi = fieldInfoForList(fd, fs, mi.Exporter)
case fd.IsWeak():
fi = fieldInfoForWeakMessage(fd, si.weakOffset)
case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
fi = fieldInfoForMessage(fd, fs, mi.Exporter)
default:

View File

@ -9,8 +9,10 @@ import (
"math"
"reflect"
"google.golang.org/protobuf/internal/flags"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry"
piface "google.golang.org/protobuf/runtime/protoiface"
)
@ -289,6 +291,89 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField, x expor
}
}
func fieldInfoForWeakMessage(fd pref.FieldDescriptor, weakOffset offset) fieldInfo {
if !flags.Proto1Legacy {
panic("no support for proto1 weak fields")
}
messageName := fd.Message().FullName()
messageType, _ := preg.GlobalTypes.FindMessageByName(messageName)
if messageType == nil {
return fieldInfo{
fieldDesc: fd,
has: func(p pointer) bool { return false },
clear: func(p pointer) {},
get: func(p pointer) pref.Value {
panic(fmt.Sprintf("weak message %v is not linked in", messageName))
},
set: func(p pointer, v pref.Value) {
panic(fmt.Sprintf("weak message %v is not linked in", messageName))
},
mutable: func(p pointer) pref.Value {
panic(fmt.Sprintf("weak message %v is not linked in", messageName))
},
newMessage: func() pref.Message {
panic(fmt.Sprintf("weak message %v is not linked in", messageName))
},
}
}
num := int32(fd.Number())
frozenEmpty := pref.ValueOf(frozenMessage{messageType.New()})
return fieldInfo{
fieldDesc: fd,
has: func(p pointer) bool {
if p.IsNil() {
return false
}
fs := p.Apply(weakOffset).WeakFields()
_, ok := (*fs)[num]
return ok
},
clear: func(p pointer) {
fs := p.Apply(weakOffset).WeakFields()
delete(*fs, num)
},
get: func(p pointer) pref.Value {
if p.IsNil() {
return frozenEmpty
}
fs := p.Apply(weakOffset).WeakFields()
m, ok := (*fs)[num]
if !ok {
return frozenEmpty
}
return pref.ValueOf(m.(pref.ProtoMessage).ProtoReflect())
},
set: func(p pointer, v pref.Value) {
m := v.Message()
if m.Descriptor() != messageType.Descriptor() {
panic("mismatching message descriptor")
}
fs := p.Apply(weakOffset).WeakFields()
if *fs == nil {
*fs = make(WeakFields)
}
(*fs)[num] = m.Interface().(piface.MessageV1)
},
mutable: func(p pointer) pref.Value {
fs := p.Apply(weakOffset).WeakFields()
if *fs == nil {
*fs = make(WeakFields)
}
m, ok := (*fs)[num]
if !ok {
m = messageType.New().Interface().(piface.MessageV1)
(*fs)[num] = m
}
return pref.ValueOf(m.(pref.ProtoMessage).ProtoReflect())
},
newMessage: func() pref.Message {
return messageType.New()
},
}
}
func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
ft := fs.Type
conv, _ := newConverter(ft, fd.Kind())

View File

@ -122,6 +122,7 @@ func (p pointer) StringPtr() **string { return p.v.Interface().(**string) }
func (p pointer) StringSlice() *[]string { return p.v.Interface().(*[]string) }
func (p pointer) Bytes() *[]byte { return p.v.Interface().(*[]byte) }
func (p pointer) BytesSlice() *[][]byte { return p.v.Interface().(*[][]byte) }
func (p pointer) WeakFields() *WeakFields { return p.v.Interface().(*WeakFields) }
func (p pointer) Extensions() *map[int32]ExtensionField {
return p.v.Interface().(*map[int32]ExtensionField)
}

View File

@ -110,6 +110,7 @@ func (p pointer) StringPtr() **string { return (**string)(p.p)
func (p pointer) StringSlice() *[]string { return (*[]string)(p.p) }
func (p pointer) Bytes() *[]byte { return (*[]byte)(p.p) }
func (p pointer) BytesSlice() *[][]byte { return (*[][]byte)(p.p) }
func (p pointer) WeakFields() *WeakFields { return (*WeakFields)(p.p) }
func (p pointer) Extensions() *map[int32]ExtensionField { return (*map[int32]ExtensionField)(p.p) }
func (p pointer) Elem() pointer {

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,8 @@ package goproto.proto.test;
import "test/test_import.proto";
import public "test/test_public.proto";
import weak "test/weak/test_weak.proto";
import weak "test/weak1/test_weak.proto";
import weak "test/weak2/test_weak.proto";
option go_package = "google.golang.org/protobuf/internal/testprotos/test";
@ -262,7 +263,8 @@ message TestRequiredGroupFields {
}
message TestWeak {
optional goproto.proto.test.weak.WeakImportMessage weak_message = 1 [weak=true];
optional goproto.proto.test.weak.WeakImportMessage1 weak_message1 = 1 [weak=true];
optional goproto.proto.test.weak.WeakImportMessage2 weak_message2 = 2 [weak=true];
}
message TestPackedTypes {

View File

@ -0,0 +1,134 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: test/weak1/test_weak.proto
package weak1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
sync "sync"
)
const (
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 0)
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(0 - protoimpl.MinVersion)
)
type WeakImportMessage1 struct {
state protoimpl.MessageState
A *int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *WeakImportMessage1) Reset() {
*x = WeakImportMessage1{}
}
func (x *WeakImportMessage1) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WeakImportMessage1) ProtoMessage() {}
func (x *WeakImportMessage1) ProtoReflect() protoreflect.Message {
mi := &file_test_weak1_test_weak_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WeakImportMessage1.ProtoReflect.Type instead.
func (*WeakImportMessage1) Descriptor() ([]byte, []int) {
return file_test_weak1_test_weak_proto_rawDescGZIP(), []int{0}
}
func (x *WeakImportMessage1) GetA() int32 {
if x != nil && x.A != nil {
return *x.A
}
return 0
}
var File_test_weak1_test_weak_proto protoreflect.FileDescriptor
var file_test_weak1_test_weak_proto_rawDesc = []byte{
0x0a, 0x1a, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31, 0x2f, 0x74, 0x65, 0x73,
0x74, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x2e, 0x77, 0x65, 0x61, 0x6b, 0x22, 0x22, 0x0a, 0x12, 0x57, 0x65, 0x61, 0x6b, 0x49, 0x6d, 0x70,
0x6f, 0x72, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x31, 0x12, 0x0c, 0x0a, 0x01, 0x61,
0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x61, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74,
0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31,
}
var (
file_test_weak1_test_weak_proto_rawDescOnce sync.Once
file_test_weak1_test_weak_proto_rawDescData = file_test_weak1_test_weak_proto_rawDesc
)
func file_test_weak1_test_weak_proto_rawDescGZIP() []byte {
file_test_weak1_test_weak_proto_rawDescOnce.Do(func() {
file_test_weak1_test_weak_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_weak1_test_weak_proto_rawDescData)
})
return file_test_weak1_test_weak_proto_rawDescData
}
var file_test_weak1_test_weak_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_test_weak1_test_weak_proto_goTypes = []interface{}{
(*WeakImportMessage1)(nil), // 0: goproto.proto.test.weak.WeakImportMessage1
}
var file_test_weak1_test_weak_proto_depIdxs = []int32{
0, // starting offset of method output_type sub-list
0, // starting offset of method input_type sub-list
0, // starting offset of extension type_name sub-list
0, // starting offset of extension extendee sub-list
0, // starting offset of field type_name sub-list
}
func init() { file_test_weak1_test_weak_proto_init() }
func file_test_weak1_test_weak_proto_init() {
if File_test_weak1_test_weak_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_test_weak1_test_weak_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WeakImportMessage1); i {
case 0:
return &v.state
case 2:
return &v.sizeCache
case 3:
return &v.unknownFields
default:
return nil
}
}
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
RawDescriptor: file_test_weak1_test_weak_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_weak1_test_weak_proto_goTypes,
DependencyIndexes: file_test_weak1_test_weak_proto_depIdxs,
MessageInfos: file_test_weak1_test_weak_proto_msgTypes,
}.Build()
File_test_weak1_test_weak_proto = out.File
file_test_weak1_test_weak_proto_rawDesc = nil
file_test_weak1_test_weak_proto_goTypes = nil
file_test_weak1_test_weak_proto_depIdxs = nil
}

View File

@ -6,8 +6,8 @@ syntax = "proto2";
package goproto.proto.test.weak;
option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak";
option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak1";
message WeakImportMessage {
message WeakImportMessage1 {
optional int32 a = 1;
}

View File

@ -0,0 +1,134 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: test/weak2/test_weak.proto
package weak1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
sync "sync"
)
const (
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 0)
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(0 - protoimpl.MinVersion)
)
type WeakImportMessage2 struct {
state protoimpl.MessageState
A *int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *WeakImportMessage2) Reset() {
*x = WeakImportMessage2{}
}
func (x *WeakImportMessage2) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WeakImportMessage2) ProtoMessage() {}
func (x *WeakImportMessage2) ProtoReflect() protoreflect.Message {
mi := &file_test_weak2_test_weak_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WeakImportMessage2.ProtoReflect.Type instead.
func (*WeakImportMessage2) Descriptor() ([]byte, []int) {
return file_test_weak2_test_weak_proto_rawDescGZIP(), []int{0}
}
func (x *WeakImportMessage2) GetA() int32 {
if x != nil && x.A != nil {
return *x.A
}
return 0
}
var File_test_weak2_test_weak_proto protoreflect.FileDescriptor
var file_test_weak2_test_weak_proto_rawDesc = []byte{
0x0a, 0x1a, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x77, 0x65, 0x61, 0x6b, 0x32, 0x2f, 0x74, 0x65, 0x73,
0x74, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x2e, 0x77, 0x65, 0x61, 0x6b, 0x22, 0x22, 0x0a, 0x12, 0x57, 0x65, 0x61, 0x6b, 0x49, 0x6d, 0x70,
0x6f, 0x72, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x12, 0x0c, 0x0a, 0x01, 0x61,
0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x61, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74,
0x2f, 0x77, 0x65, 0x61, 0x6b, 0x31,
}
var (
file_test_weak2_test_weak_proto_rawDescOnce sync.Once
file_test_weak2_test_weak_proto_rawDescData = file_test_weak2_test_weak_proto_rawDesc
)
func file_test_weak2_test_weak_proto_rawDescGZIP() []byte {
file_test_weak2_test_weak_proto_rawDescOnce.Do(func() {
file_test_weak2_test_weak_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_weak2_test_weak_proto_rawDescData)
})
return file_test_weak2_test_weak_proto_rawDescData
}
var file_test_weak2_test_weak_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_test_weak2_test_weak_proto_goTypes = []interface{}{
(*WeakImportMessage2)(nil), // 0: goproto.proto.test.weak.WeakImportMessage2
}
var file_test_weak2_test_weak_proto_depIdxs = []int32{
0, // starting offset of method output_type sub-list
0, // starting offset of method input_type sub-list
0, // starting offset of extension type_name sub-list
0, // starting offset of extension extendee sub-list
0, // starting offset of field type_name sub-list
}
func init() { file_test_weak2_test_weak_proto_init() }
func file_test_weak2_test_weak_proto_init() {
if File_test_weak2_test_weak_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_test_weak2_test_weak_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WeakImportMessage2); i {
case 0:
return &v.state
case 2:
return &v.sizeCache
case 3:
return &v.unknownFields
default:
return nil
}
}
}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
RawDescriptor: file_test_weak2_test_weak_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_weak2_test_weak_proto_goTypes,
DependencyIndexes: file_test_weak2_test_weak_proto_depIdxs,
MessageInfos: file_test_weak2_test_weak_proto_msgTypes,
}.Build()
File_test_weak2_test_weak_proto = out.File
file_test_weak2_test_weak_proto_rawDesc = nil
file_test_weak2_test_weak_proto_goTypes = nil
file_test_weak2_test_weak_proto_depIdxs = nil
}

View File

@ -0,0 +1,13 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
syntax = "proto2";
package goproto.proto.test.weak;
option go_package = "google.golang.org/protobuf/internal/testprotos/test/weak1";
message WeakImportMessage2 {
optional int32 a = 1;
}

View File

@ -11,6 +11,7 @@ import (
"google.golang.org/protobuf/internal/encoding/wire"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/flags"
"google.golang.org/protobuf/internal/strs"
"google.golang.org/protobuf/reflect/protoreflect"
@ -102,6 +103,9 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc
return errors.New("message %q has conflicting fields: %q with %q", m.FullName(), f1.Name(), f2.Name())
}
}
if isMessageSet && !flags.Proto1Legacy {
return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName())
}
if isMessageSet && (m.Syntax() != protoreflect.Proto2 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) {
return errors.New("message %q is an invalid proto1 MessageSet", m.FullName())
}
@ -143,7 +147,10 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc
if fd.Extendee != nil {
return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee())
}
if f.IsWeak() && (!isOptionalMessage(f) || f.ContainingOneof() != nil) {
if f.IsWeak() && !flags.Proto1Legacy {
return errors.New("message field %q is a weak field, which is a legacy proto1 feature that is no longer supported", f.FullName())
}
if f.IsWeak() && (f.Syntax() != protoreflect.Proto2 || !isOptionalMessage(f) || f.ContainingOneof() != nil) {
return errors.New("message field %q may only be weak for an optional message", f.FullName())
}
if f.IsPacked() && !isPackable(f) {

View File

@ -10,6 +10,7 @@ import (
"testing"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/internal/flags"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoregistry"
@ -767,7 +768,13 @@ func TestNewFile(t *testing.T) {
options: {message_set_wire_format:true}
}]}]
`),
wantErr: `message "M.M" is an invalid proto1 MessageSet`,
wantErr: func() string {
if flags.Proto1Legacy {
return `message "M.M" is an invalid proto1 MessageSet`
} else {
return `message "M.M" is a MessageSet, which is a legacy proto1 feature that is no longer supported`
}
}(),
}, {
label: "valid MessageSet",
inDesc: mustParseFile(`
@ -779,6 +786,13 @@ func TestNewFile(t *testing.T) {
options: {message_set_wire_format:true}
}]}]
`),
wantErr: func() string {
if flags.Proto1Legacy {
return ""
} else {
return `message "M.M" is a MessageSet, which is a legacy proto1 feature that is no longer supported`
}
}(),
}, {
label: "invalid extension ranges in proto3",
inDesc: mustParseFile(`

View File

@ -68,6 +68,7 @@ type (
MessageInfo = impl.MessageInfo
MessageState = impl.MessageState
SizeCache = impl.SizeCache
WeakFields = impl.WeakFields
UnknownFields = impl.UnknownFields
ExtensionFields = impl.ExtensionFields
ExtensionFieldV1 = impl.ExtensionField

View File

@ -8,22 +8,30 @@ import (
"fmt"
"testing"
irregularpb "google.golang.org/protobuf/internal/testprotos/irregular"
testpb "google.golang.org/protobuf/internal/testprotos/test"
test3pb "google.golang.org/protobuf/internal/testprotos/test3"
"google.golang.org/protobuf/internal/flags"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/prototest"
irregularpb "google.golang.org/protobuf/internal/testprotos/irregular"
testpb "google.golang.org/protobuf/internal/testprotos/test"
_ "google.golang.org/protobuf/internal/testprotos/test/weak1"
_ "google.golang.org/protobuf/internal/testprotos/test/weak2"
test3pb "google.golang.org/protobuf/internal/testprotos/test3"
)
func Test(t *testing.T) {
for _, m := range []proto.Message{
ms := []proto.Message{
(*testpb.TestAllTypes)(nil),
(*test3pb.TestAllTypes)(nil),
(*testpb.TestRequired)(nil),
(*testpb.TestWeak)(nil),
(*irregularpb.Message)(nil),
(*testpb.TestAllExtensions)(nil),
} {
}
if flags.Proto1Legacy {
ms = append(ms, (*testpb.TestWeak)(nil))
}
for _, m := range ms {
t.Run(fmt.Sprintf("%T", m), func(t *testing.T) {
prototest.TestMessage(t, m, prototest.MessageOptions{})
})