From cf06b0c33cda11f4cb6e0151c9f7c0542fa3110f Mon Sep 17 00:00:00 2001 From: Cody Schroeder Date: Thu, 27 Apr 2023 08:39:18 -0700 Subject: [PATCH] compiler/protogen: add Semantic.SET to setter annotations Provide an API to add the GeneratedCodeInfo.Annotation.Semantic enum to annotations. Change-Id: I92ab30619a94a117679a0eb16d8cb5b3a1352586 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/489795 Reviewed-by: Lasse Folger Reviewed-by: Damien Neil --- cmd/protoc-gen-go/annotation_test.go | 51 ++++++++++--- cmd/protoc-gen-go/internal_gengo/main.go | 5 +- .../testdata/annotations/annotations.pb.go | 47 +++++++++--- .../annotations/annotations.pb.go.meta | 2 +- .../testdata/annotations/annotations.proto | 4 ++ compiler/protogen/protogen.go | 56 ++++++++++++--- compiler/protogen/protogen_test.go | 72 +++++++++++++++++++ 7 files changed, 202 insertions(+), 35 deletions(-) diff --git a/cmd/protoc-gen-go/annotation_test.go b/cmd/protoc-gen-go/annotation_test.go index c0f3c079..4ec1a2ba 100644 --- a/cmd/protoc-gen-go/annotation_test.go +++ b/cmd/protoc-gen-go/annotation_test.go @@ -9,9 +9,11 @@ import ( "io/ioutil" "testing" + "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/internal/genid" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/descriptorpb" ) @@ -33,22 +35,48 @@ func TestAnnotations(t *testing.T) { wantInfo := &descriptorpb.GeneratedCodeInfo{} for _, want := range []struct { prefix, text, suffix string - path []int32 + annotation *descriptorpb.GeneratedCodeInfo_Annotation }{{ "type ", "AnnotationsTestEnum", " int32", - []int32{int32(genid.FileDescriptorProto_EnumType_field_number), 0}, + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_EnumType_field_number), 0}, + }, }, { "\t", "AnnotationsTestEnum_ANNOTATIONS_TEST_ENUM_VALUE", " AnnotationsTestEnum = 0", - []int32{int32(genid.FileDescriptorProto_EnumType_field_number), 0, int32(genid.EnumDescriptorProto_Value_field_number), 0}, + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_EnumType_field_number), 0, int32(genid.EnumDescriptorProto_Value_field_number), 0}, + }, }, { "type ", "AnnotationsTestMessage", " struct {", - []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0}, + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0}, + }, }, { "\t", "AnnotationsTestField", " ", - []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 0}, + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 0}, + }, + }, { + "\t", "XXX_weak_M", " ", + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 1}, + }, }, { "func (x *AnnotationsTestMessage) ", "GetAnnotationsTestField", "() string {", - []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 0}, + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 0}, + }, + }, { + "func (x *AnnotationsTestMessage) ", "GetM", "() proto.Message {", + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 1}, + }, + }, { + "func (x *AnnotationsTestMessage) ", "SetM", "(v proto.Message) {", + &descriptorpb.GeneratedCodeInfo_Annotation{ + Path: []int32{int32(genid.FileDescriptorProto_MessageType_field_number), 0, int32(genid.DescriptorProto_Field_field_number), 1}, + Semantic: descriptorpb.GeneratedCodeInfo_Annotation_SET.Enum(), + }, }} { s := want.prefix + want.text + want.suffix pos := bytes.Index(sourceFile, []byte(s)) @@ -58,14 +86,15 @@ func TestAnnotations(t *testing.T) { } begin := pos + len(want.prefix) end := begin + len(want.text) - wantInfo.Annotation = append(wantInfo.Annotation, &descriptorpb.GeneratedCodeInfo_Annotation{ - Path: want.path, + a := &descriptorpb.GeneratedCodeInfo_Annotation{ Begin: proto.Int32(int32(begin)), End: proto.Int32(int32(end)), SourceFile: proto.String("cmd/protoc-gen-go/testdata/annotations/annotations.proto"), - }) + } + proto.Merge(a, want.annotation) + wantInfo.Annotation = append(wantInfo.Annotation, a) } - if !proto.Equal(gotInfo, wantInfo) { - t.Errorf("unexpected annotations for annotations.proto; got:\n%v\nwant:\n%v", gotInfo, wantInfo) + if diff := cmp.Diff(wantInfo, gotInfo, protocmp.Transform()); diff != "" { + t.Fatalf("unexpected annotations for annotations.proto (-want +got):\n%s", diff) } } diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go index f8b76bf5..8cae4320 100644 --- a/cmd/protoc-gen-go/internal_gengo/main.go +++ b/cmd/protoc-gen-go/internal_gengo/main.go @@ -614,7 +614,10 @@ func genMessageSetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageI genNoInterfacePragma(g, m.isTracked) - g.Annotate(m.GoIdent.GoName+".Set"+field.GoName, field.Location) + g.AnnotateSymbol(m.GoIdent.GoName+".Set"+field.GoName, protogen.Annotation{ + Location: field.Location, + Semantic: descriptorpb.GeneratedCodeInfo_Annotation_SET.Enum(), + }) leadingComments := appendDeprecationSuffix("", field.Desc.ParentFile(), field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()) diff --git a/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go b/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go index 2a5fd247..91caa37b 100644 --- a/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go +++ b/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go @@ -8,6 +8,7 @@ package annotations import ( + proto "google.golang.org/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -70,9 +71,11 @@ func (AnnotationsTestEnum) EnumDescriptor() ([]byte, []int) { type AnnotationsTestMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache + weakFields protoimpl.WeakFields unknownFields protoimpl.UnknownFields - AnnotationsTestField *string `protobuf:"bytes,1,opt,name=AnnotationsTestField" json:"AnnotationsTestField,omitempty"` + AnnotationsTestField *string `protobuf:"bytes,1,opt,name=AnnotationsTestField" json:"AnnotationsTestField,omitempty"` + XXX_weak_M struct{} `protobuf:"bytes,2,opt,name=m,weak=fmt.M" json:"m,omitempty"` } func (x *AnnotationsTestMessage) Reset() { @@ -114,6 +117,22 @@ func (x *AnnotationsTestMessage) GetAnnotationsTestField() string { return "" } +func (x *AnnotationsTestMessage) GetM() proto.Message { + var w protoimpl.WeakFields + if x != nil { + w = x.weakFields + } + return protoimpl.X.GetWeak(w, 2, "fmt.M") +} + +func (x *AnnotationsTestMessage) SetM(v proto.Message) { + var w *protoimpl.WeakFields + if x != nil { + w = &x.weakFields + } + protoimpl.X.SetWeak(w, 2, "fmt.M", v) +} + var File_cmd_protoc_gen_go_testdata_annotations_annotations_proto protoreflect.FileDescriptor var file_cmd_protoc_gen_go_testdata_annotations_annotations_proto_rawDesc = []byte{ @@ -122,20 +141,24 @@ var file_cmd_protoc_gen_go_testdata_annotations_annotations_proto_rawDesc = []by 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2e, 0x61, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, 0x16, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x2e, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, + 0x74, 0x61, 0x2f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2f, 0x66, 0x6d, 0x74, 0x2f, 0x6d, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x66, 0x0a, 0x16, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x54, 0x65, 0x73, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x54, 0x65, 0x73, 0x74, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x2a, 0x36, 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x1b, 0x41, - 0x4e, 0x4e, 0x4f, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, - 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x00, 0x42, 0x43, 0x5a, 0x41, - 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, 0x63, 0x6d, 0x64, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, - 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, + 0x69, 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x01, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x06, 0x2e, 0x66, 0x6d, 0x74, 0x2e, 0x4d, 0x42, 0x02, 0x50, 0x01, 0x52, 0x01, 0x6d, 0x2a, 0x36, + 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x54, 0x65, 0x73, + 0x74, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x56, + 0x41, 0x4c, 0x55, 0x45, 0x10, 0x00, 0x42, 0x43, 0x5a, 0x41, 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, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, + 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x58, 0x00, } var ( @@ -177,6 +200,8 @@ func file_cmd_protoc_gen_go_testdata_annotations_annotations_proto_init() { case 1: return &v.sizeCache case 2: + return &v.weakFields + case 3: return &v.unknownFields default: return nil diff --git a/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go.meta b/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go.meta index 91f84f35..d0d5ea65 100644 --- a/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go.meta +++ b/cmd/protoc-gen-go/testdata/annotations/annotations.pb.go.meta @@ -1 +1 @@ -annotation:{path:5 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:470 end:489} annotation:{path:5 path:0 path:2 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:506 end:553} annotation:{path:4 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:1912 end:1934} annotation:{path:4 path:0 path:2 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:2058 end:2078} annotation:{path:4 path:0 path:2 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:3225 end:3248} \ No newline at end of file +annotation:{path:5 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:512 end:531} annotation:{path:5 path:0 path:2 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:548 end:595} annotation:{path:4 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:1954 end:1976} annotation:{path:4 path:0 path:2 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:2136 end:2156} annotation:{path:4 path:0 path:2 path:1 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:2256 end:2266} annotation:{path:4 path:0 path:2 path:0 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:3397 end:3420} annotation:{path:4 path:0 path:2 path:1 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:3563 end:3567} annotation:{path:4 path:0 path:2 path:1 source_file:"cmd/protoc-gen-go/testdata/annotations/annotations.proto" begin:3730 end:3734 semantic:SET} \ No newline at end of file diff --git a/cmd/protoc-gen-go/testdata/annotations/annotations.proto b/cmd/protoc-gen-go/testdata/annotations/annotations.proto index acd7b866..d022ab37 100644 --- a/cmd/protoc-gen-go/testdata/annotations/annotations.proto +++ b/cmd/protoc-gen-go/testdata/annotations/annotations.proto @@ -6,10 +6,14 @@ syntax = "proto2"; package goproto.protoc.annotations; +import weak "cmd/protoc-gen-go/testdata/imports/fmt/m.proto"; + option go_package = "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/annotations"; message AnnotationsTestMessage { optional string AnnotationsTestField = 1; + + optional fmt.M m = 2 [weak = true]; } enum AnnotationsTestEnum { diff --git a/compiler/protogen/protogen.go b/compiler/protogen/protogen.go index 2d2171e5..431e8804 100644 --- a/compiler/protogen/protogen.go +++ b/compiler/protogen/protogen.go @@ -920,7 +920,7 @@ type GeneratedFile struct { packageNames map[GoImportPath]GoPackageName usedPackageNames map[GoPackageName]bool manualImports map[GoImportPath]bool - annotations map[string][]Location + annotations map[string][]Annotation } // NewGeneratedFile creates a new generated file with the given filename @@ -933,7 +933,7 @@ func (gen *Plugin) NewGeneratedFile(filename string, goImportPath GoImportPath) packageNames: make(map[GoImportPath]GoPackageName), usedPackageNames: make(map[GoPackageName]bool), manualImports: make(map[GoImportPath]bool), - annotations: make(map[string][]Location), + annotations: make(map[string][]Annotation), } // All predeclared identifiers in Go are already used. @@ -1012,8 +1012,32 @@ func (g *GeneratedFile) Unskip() { // The symbol may refer to a type, constant, variable, function, method, or // struct field. The "T.sel" syntax is used to identify the method or field // 'sel' on type 'T'. +// +// Deprecated: Use the AnnotateSymbol method instead. func (g *GeneratedFile) Annotate(symbol string, loc Location) { - g.annotations[symbol] = append(g.annotations[symbol], loc) + g.AnnotateSymbol(symbol, Annotation{Location: loc}) +} + +// An Annotation provides semantic detail for a generated proto element. +// +// See the google.protobuf.GeneratedCodeInfo.Annotation documentation in +// descriptor.proto for details. +type Annotation struct { + // Location is the source .proto file for the element. + Location Location + + // Semantic is the symbol's effect on the element in the original .proto file. + Semantic *descriptorpb.GeneratedCodeInfo_Annotation_Semantic +} + +// AnnotateSymbol associates a symbol in a generated Go file with a location +// in a source .proto file and a semantic type. +// +// The symbol may refer to a type, constant, variable, function, method, or +// struct field. The "T.sel" syntax is used to identify the method or field +// 'sel' on type 'T'. +func (g *GeneratedFile) AnnotateSymbol(symbol string, info Annotation) { + g.annotations[symbol] = append(g.annotations[symbol], info) } // Content returns the contents of the generated file. @@ -1106,25 +1130,24 @@ func (g *GeneratedFile) Content() ([]byte, error) { return out.Bytes(), nil } -// metaFile returns the contents of the file's metadata file, which is a -// text formatted string of the google.protobuf.GeneratedCodeInfo. -func (g *GeneratedFile) metaFile(content []byte) (string, error) { +func (g *GeneratedFile) generatedCodeInfo(content []byte) (*descriptorpb.GeneratedCodeInfo, error) { fset := token.NewFileSet() astFile, err := parser.ParseFile(fset, "", content, 0) if err != nil { - return "", err + return nil, err } info := &descriptorpb.GeneratedCodeInfo{} seenAnnotations := make(map[string]bool) annotate := func(s string, ident *ast.Ident) { seenAnnotations[s] = true - for _, loc := range g.annotations[s] { + for _, a := range g.annotations[s] { info.Annotation = append(info.Annotation, &descriptorpb.GeneratedCodeInfo_Annotation{ - SourceFile: proto.String(loc.SourceFile), - Path: loc.Path, + SourceFile: proto.String(a.Location.SourceFile), + Path: a.Location.Path, Begin: proto.Int32(int32(fset.Position(ident.Pos()).Offset)), End: proto.Int32(int32(fset.Position(ident.End()).Offset)), + Semantic: a.Semantic, }) } } @@ -1171,10 +1194,21 @@ func (g *GeneratedFile) metaFile(content []byte) (string, error) { } for a := range g.annotations { if !seenAnnotations[a] { - return "", fmt.Errorf("%v: no symbol matching annotation %q", g.filename, a) + return nil, fmt.Errorf("%v: no symbol matching annotation %q", g.filename, a) } } + return info, nil +} + +// metaFile returns the contents of the file's metadata file, which is a +// text formatted string of the google.protobuf.GeneratedCodeInfo. +func (g *GeneratedFile) metaFile(content []byte) (string, error) { + info, err := g.generatedCodeInfo(content) + if err != nil { + return "", err + } + b, err := prototext.Marshal(info) if err != nil { return "", err diff --git a/compiler/protogen/protogen_test.go b/compiler/protogen/protogen_test.go index 4f5ceeda..d7b5407b 100644 --- a/compiler/protogen/protogen_test.go +++ b/compiler/protogen/protogen_test.go @@ -11,8 +11,10 @@ import ( "github.com/google/go-cmp/cmp" + "google.golang.org/protobuf/internal/genid" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/pluginpb" @@ -344,3 +346,73 @@ var _ = bar.X t.Fatalf("content mismatch (-want +got):\n%s", diff) } } + +func TestAnnotations(t *testing.T) { + gen, err := Options{}.New(&pluginpb.CodeGeneratorRequest{}) + if err != nil { + t.Fatal(err) + } + loc := Location{SourceFile: "foo.go"} + g := gen.NewGeneratedFile("foo.go", "golang.org/x/foo") + + g.P("package foo") + g.P() + + structName := "S" + fieldName := "Field" + + messageLoc := loc.appendPath(genid.FileDescriptorProto_MessageType_field_number, 1) + fieldLoc := messageLoc.appendPath(genid.DescriptorProto_Field_field_number, 1) + + g.Annotate(structName, messageLoc) // use deprecated version to test existing usages + g.P("type ", structName, " struct {") + g.AnnotateSymbol(structName+"."+fieldName, Annotation{Location: fieldLoc}) + g.P(fieldName, " string") + g.P("}") + g.P() + + g.AnnotateSymbol(fmt.Sprintf("%s.Set%s", structName, fieldName), Annotation{ + Location: fieldLoc, + Semantic: descriptorpb.GeneratedCodeInfo_Annotation_SET.Enum(), + }) + g.P("func (m *", structName, ") Set", fieldName, "(x string) {") + g.P("m.", fieldName, " = x") + g.P("}") + g.P() + + want := &descriptorpb.GeneratedCodeInfo{ + Annotation: []*descriptorpb.GeneratedCodeInfo_Annotation{ + { // S + SourceFile: proto.String("foo.go"), + Path: []int32{4, 1}, // message S + Begin: proto.Int32(18), + End: proto.Int32(19), + }, + { // S.F + SourceFile: proto.String("foo.go"), + Path: []int32{4, 1, 2, 1}, + Begin: proto.Int32(30), + End: proto.Int32(35), + }, + { // SetF + SourceFile: proto.String("foo.go"), + Path: []int32{4, 1, 2, 1}, + Begin: proto.Int32(58), + End: proto.Int32(66), + Semantic: descriptorpb.GeneratedCodeInfo_Annotation_SET.Enum(), + }, + }, + } + + content, err := g.Content() + if err != nil { + t.Fatalf("g.Content() = %v", err) + } + got, err := g.generatedCodeInfo(content) + if err != nil { + t.Fatalf("g.generatedCodeInfo(...) = %v", err) + } + if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { + t.Fatalf("GeneratedCodeInfo mismatch (-want +got):\n%s", diff) + } +}