// Copyright 2024 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. package name_clash_test import ( "reflect" "testing" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/internal/genid" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protodesc" descpb "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/gofeaturespb" "google.golang.org/protobuf/types/pluginpb" hpb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/nameclash/test_name_clash_hybrid" opb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/nameclash/test_name_clash_opaque" pb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/nameclash/test_name_clash_open" ) // TestOpenMangling tests the backwards compatible mangling of fields // who clashes with the getters. The expected behavior, which is // somewhat surprising, is documented in the proto // test_name_clash_open.proto itself. func TestOpenMangling(t *testing.T) { m1 := &pb.M1{ Foo: proto.Int32(1), GetFoo_: proto.Int32(2), GetGetFoo: proto.Int32(3), } if m1.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1) } if m1.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo_(), m1) } if m1.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m1.GetGetGetFoo(), m1) } m2 := &pb.M2{ Foo: proto.Int32(1), GetFoo_: proto.Int32(2), GetGetFoo: proto.Int32(3), } if m2.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2) } if m2.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo_(), m2) } if m2.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m2.GetGetGetFoo(), m2) } m3 := &pb.M3{ Foo_: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo_: proto.Int32(3), } if m3.GetFoo_() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo_(), m3) } if m3.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3) } if m3.GetGetGetFoo_() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m3.GetGetGetFoo_(), m3) } m4 := &pb.M4{ GetFoo: proto.Int32(2), GetGetFoo_: &pb.M4_GetGetGetFoo{GetGetGetFoo: 3}, Foo_: proto.Int32(1), } if m4.GetFoo_() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.GetFoo_(), m4) } if m4.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.GetGetFoo(), m4) } if m4.GetGetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetGetFoo(), m4) } m5 := &pb.M5{ GetFoo: proto.Int32(2), GetGetGetFoo: &pb.M5_GetGetFoo_{GetGetFoo_: 3}, Foo_: proto.Int32(1), } if m5.GetFoo_() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.GetFoo_(), m5) } if m5.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.GetGetFoo(), m5) } if m5.GetGetGetFoo_() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.GetGetGetFoo_(), m5) } m6 := &pb.M6{ GetGetFoo: &pb.M6_GetGetGetFoo{GetGetGetFoo: 3}, GetFoo_: proto.Int32(2), Foo: proto.Int32(1), } if m6.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.GetFoo(), m6) } if m6.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.GetGetFoo_(), m6) } if m6.GetGetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m6.GetGetGetGetFoo(), m6) } m7 := &pb.M7{ GetGetFoo: &pb.M7_GetFoo_{GetFoo_: 3}, Foo: proto.Int32(1), } if m7.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.GetFoo(), m7) } if m7.GetGetFoo_() != 3 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m7.GetGetFoo_(), m7) } m7.GetGetFoo = &pb.M7_Bar{Bar: true} if !m7.GetBar() { t.Errorf("Proto field 'bar' has unexpected value %v for %T (expected 3)", m7.GetBar(), m7) } m8 := &pb.M8{ GetGetGetFoo_: &pb.M8_GetGetFoo{GetGetFoo: 3}, GetFoo_: proto.Int32(2), Foo: proto.Int32(1), } if m8.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.GetFoo(), m8) } if m8.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.GetGetFoo_(), m8) } if m8.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m8.GetGetGetFoo(), m8) } m9 := &pb.M9{ GetGetGetFoo_: &pb.M9_GetGetFoo{GetGetFoo: 3}, Foo: proto.Int32(1), } if m9.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.GetFoo(), m9) } if m9.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.GetGetGetFoo(), m9) } m9.GetGetGetFoo_ = &pb.M9_GetFoo_{GetFoo_: 2} if m9.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.GetGetFoo_(), m9) } } // TestHybridMangling tests the backwards compatible mangling as well // as new style mangling of fields who clashes with the getters. The // expected behavior, which is somewhat surprising, is documented in // the proto test_name_clash_hybrid.proto itself. func TestHybridMangling(t *testing.T) { m1 := hpb.M1_builder{ Foo: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), }.Build() if m1.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1) } if m1.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1) } if m1.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo_(), m1) } if m1.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo_(), m1) } if m1.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m1.GetGetGetFoo(), m1) } checkNameConsistency(t, m1) m2 := hpb.M2_builder{ Foo: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), }.Build() if m2.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2) } if m2.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2) } if m2.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo_(), m2) } if m2.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo_(), m2) } if m2.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m2.GetGetGetFoo(), m2) } checkNameConsistency(t, m2) m3 := hpb.M3_builder{ Foo: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), }.Build() if m3.GetFoo_() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo_(), m3) } if m3.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo_(), m3) } if m3.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3) } if m3.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3) } if m3.GetGetGetFoo_() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m3.GetGetGetFoo_(), m3) } checkNameConsistency(t, m3) m4 := hpb.M4_builder{ GetFoo: proto.Int32(2), GetGetGetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m4.GetFoo_() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.GetFoo_(), m4) } if m4.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.Get_Foo(), m4) } if m4.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.GetGetFoo(), m4) } if m4.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.Get_GetFoo(), m4) } if m4.GetGetGetFoo_().(*hpb.M4_GetGetGetFoo).GetGetGetFoo != 3 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetFoo_(), m4) } if !m4.HasGetGetFoo() { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected true)", m4.HasGetGetFoo(), m4) } if m4.GetGetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetGetFoo(), m4) } checkNameConsistency(t, m4) m5 := hpb.M5_builder{ GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m5.GetFoo_() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.GetFoo_(), m5) } if m5.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.Get_Foo(), m4) } if m5.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.GetGetFoo(), m5) } if m5.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.Get_GetFoo(), m4) } if m5.GetGetGetFoo_() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.GetGetGetFoo_(), m5) } if m5.Get_GetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.Get_GetGetFoo(), m5) } checkNameConsistency(t, m5) m6 := hpb.M6_builder{ GetGetGetFoo: proto.Int32(3), GetFoo: proto.Int32(2), Foo: proto.Int32(1), }.Build() if m6.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.GetFoo(), m6) } if m6.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.Get_Foo(), m6) } if m6.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.GetGetFoo_(), m6) } if m6.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.Get_GetFoo(), m6) } if m6.GetGetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m6.GetGetGetGetFoo(), m6) } checkNameConsistency(t, m6) m7 := hpb.M7_builder{ GetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m7.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.GetFoo(), m7) } if m7.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.Get_Foo(), m7) } if m7.GetGetFoo_() != 3 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m7.GetGetFoo_(), m7) } if m7.Get_GetFoo() != 3 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m7.Get_GetFoo(), m7) } m7.SetBar(true) if !m7.GetBar() { t.Errorf("Proto field 'bar' has unexpected value %v for %T (expected 3)", m7.GetBar(), m7) } checkNameConsistency(t, m7) m8 := hpb.M8_builder{ GetGetFoo: proto.Int32(3), GetFoo: proto.Int32(2), Foo: proto.Int32(1), }.Build() if m8.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.GetFoo(), m8) } if m8.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.Get_Foo(), m8) } if m8.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.GetGetFoo_(), m8) } if m8.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.Get_GetFoo(), m8) } if m8.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m8.GetGetGetFoo(), m8) } checkNameConsistency(t, m8) m9 := hpb.M9_builder{ GetGetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m9.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.GetFoo(), m9) } if m9.Get_Foo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.Get_Foo(), m9) } if m9.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.GetGetGetFoo(), m9) } if m9.Get_GetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.Get_GetGetFoo(), m9) } m9.Set_GetFoo(2) if m9.GetGetFoo_() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.GetGetFoo_(), m9) } if m9.Get_GetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.Get_GetFoo(), m9) } checkNameConsistency(t, m9) m10 := hpb.M10_builder{ Foo: proto.Int32(1), SetFoo: proto.Int32(2), }.Build() m10.Set_Foo(47) if m10.Get_Foo() != 47 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 47)", m10.Get_Foo(), m10) } m10.SetSetFoo(11) if m10.GetSetFoo() != 11 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m10.GetSetFoo(), m10) } checkNameConsistency(t, m10) m11 := hpb.M11_builder{ Foo: proto.Int32(1), SetSetFoo: proto.Int32(2), }.Build() m11.Set_Foo(47) if m11.Get_Foo() != 47 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 47)", m11.Get_Foo(), m11) } m11.SetSetSetFoo(11) if m11.GetSetSetFoo() != 11 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m11.GetSetSetFoo(), m11) } checkNameConsistency(t, m11) m12 := hpb.M12_builder{ Foo: proto.Int32(1), SetFoo: proto.Int32(2), }.Build() m12.Set_Foo(47) if m12.Get_Foo() != 47 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 47)", m12.Get_Foo(), m12) } m12.Set_SetFoo(11) if m12.Get_SetFoo() != 11 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m12.Get_SetFoo(), m12) } checkNameConsistency(t, m12) m13 := hpb.M13_builder{ Foo: proto.Int32(1), HasFoo: proto.Int32(2), }.Build() if !m13.Has_Foo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.Has_Foo(), m13) } if !m13.HasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.HasHasFoo(), m13) } checkNameConsistency(t, m13) m14 := hpb.M14_builder{ Foo: proto.Int32(1), HasHasFoo: proto.Int32(2), }.Build() if !m14.Has_Foo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.Has_Foo(), m14) } if !m14.Has_HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.Has_HasFoo(), m14) } if !m14.HasHasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasHasHasFoo(), m14) } checkNameConsistency(t, m14) m15 := hpb.M15_builder{ Foo: proto.Int32(1), HasFoo: proto.Int32(2), }.Build() if !m15.Has_Foo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.Has_Foo(), m15) } if !m15.Has_HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.Has_HasFoo(), m15) } if !m15.HasHasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasHasHasFoo(), m15) } checkNameConsistency(t, m15) m16 := hpb.M16_builder{ Foo: proto.Int32(1), ClearFoo: proto.Int32(2), }.Build() m16.Clear_Foo() if m16.Has_Foo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.Has_Foo(), m16) } m16.ClearClearFoo() if m16.HasClearFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.HasClearFoo(), m16) } checkNameConsistency(t, m16) m17 := hpb.M17_builder{ Foo: proto.Int32(1), ClearClearFoo: proto.Int32(2), }.Build() m17.Clear_Foo() if m17.Has_Foo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.Has_Foo(), m17) } m17.ClearClearClearFoo() if m17.HasClearClearFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.HasClearClearFoo(), m17) } checkNameConsistency(t, m17) m18 := hpb.M18_builder{ Foo: proto.Int32(1), ClearFoo: proto.Int32(2), }.Build() m18.Clear_Foo() if m18.Has_Foo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.Has_Foo(), m18) } m18.Clear_ClearFoo() if m18.Has_ClearFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.Has_ClearFoo(), m18) } checkNameConsistency(t, m18) m19 := hpb.M19_builder{ Foo: proto.Int32(1), WhichFoo: proto.Int32(2), }.Build() if m19.WhichWhichWhichFoo() != hpb.M19_WhichFoo_case { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M19_ClearFoo_case)", m19.WhichWhichWhichFoo(), m19) } checkNameConsistency(t, m19) m20 := hpb.M20_builder{ Foo: proto.Int32(1), WhichWhichFoo: proto.Int32(2), }.Build() if m20.Which_WhichFoo() != hpb.M20_WhichWhichFoo_case { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M20_WhichWhichFoo_case)", m20.Which_WhichFoo(), m20) } checkNameConsistency(t, m20) } // TestOpaqueMangling tests the backwards compatible mangling as well // as new style mangling of fields who clashes with the getters. The // expected behavior, which is somewhat surprising, is documented in // the proto test_name_clash_opaque.proto itself. func TestOpaqueMangling(t *testing.T) { m1 := opb.M1_builder{ Foo: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), }.Build() if m1.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1) } if m1.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo(), m1) } if m1.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m1.GetGetGetFoo(), m1) } checkNameConsistency(t, m1) m2 := opb.M2_builder{ Foo: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), }.Build() if m2.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2) } if m2.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo(), m2) } if m2.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m2.GetGetGetFoo(), m2) } checkNameConsistency(t, m2) m3 := opb.M3_builder{ Foo: proto.Int32(1), GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), }.Build() if m3.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo(), m3) } if m3.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3) } if m3.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m3.GetGetGetFoo(), m3) } checkNameConsistency(t, m3) m4 := opb.M4_builder{ GetFoo: proto.Int32(2), GetGetGetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m4.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.GetFoo(), m4) } if m4.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.GetGetFoo(), m4) } if !m4.HasGetGetFoo() { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected true)", m4.HasGetGetFoo(), m4) } if m4.GetGetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetGetFoo(), m4) } checkNameConsistency(t, m4) m5 := opb.M5_builder{ GetFoo: proto.Int32(2), GetGetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m5.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.GetFoo(), m5) } if m5.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.GetGetFoo(), m5) } if m5.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.GetGetGetFoo(), m5) } checkNameConsistency(t, m5) m6 := opb.M6_builder{ GetGetGetFoo: proto.Int32(3), GetFoo: proto.Int32(2), Foo: proto.Int32(1), }.Build() if m6.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.GetFoo(), m6) } if m6.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.GetGetFoo(), m6) } if m6.GetGetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m6.GetGetGetGetFoo(), m6) } checkNameConsistency(t, m6) m7 := opb.M7_builder{ GetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m7.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.GetFoo(), m7) } if m7.GetGetFoo() != 3 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m7.GetGetFoo(), m7) } m7.SetBar(true) if !m7.GetBar() { t.Errorf("Proto field 'bar' has unexpected value %v for %T (expected true)", m7.GetBar(), m7) } checkNameConsistency(t, m7) m8 := opb.M8_builder{ GetGetFoo: proto.Int32(3), GetFoo: proto.Int32(2), Foo: proto.Int32(1), }.Build() if m8.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.GetFoo(), m8) } if m8.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.GetGetFoo(), m8) } if m8.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m8.GetGetGetFoo(), m8) } checkNameConsistency(t, m8) m9 := opb.M9_builder{ GetGetFoo: proto.Int32(3), Foo: proto.Int32(1), }.Build() if m9.GetFoo() != 1 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.GetFoo(), m9) } if m9.GetGetGetFoo() != 3 { t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.GetGetGetFoo(), m9) } m9.SetGetFoo(2) if m9.GetGetFoo() != 2 { t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.GetGetFoo(), m9) } checkNameConsistency(t, m9) m10 := opb.M10_builder{ Foo: proto.Int32(1), SetFoo: proto.Int32(2), }.Build() m10.SetFoo(48) if m10.GetFoo() != 48 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 48)", m10.GetFoo(), m10) } m10.SetSetFoo(11) if m10.GetSetFoo() != 11 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m10.GetSetFoo(), m10) } checkNameConsistency(t, m10) m11 := opb.M11_builder{ Foo: proto.Int32(1), SetSetFoo: proto.Int32(2), }.Build() m11.SetFoo(48) if m11.GetFoo() != 48 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 48)", m11.GetFoo(), m11) } m11.SetSetSetFoo(11) if m11.GetSetSetFoo() != 11 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m11.GetSetSetFoo(), m11) } checkNameConsistency(t, m11) m12 := opb.M12_builder{ Foo: proto.Int32(1), SetFoo: proto.Int32(2), }.Build() m12.SetFoo(48) if m12.GetFoo() != 48 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 48)", m12.GetFoo(), m12) } m12.SetSetFoo(12) if m12.GetSetFoo() != 12 { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 12)", m12.GetSetFoo(), m12) } checkNameConsistency(t, m12) m13 := opb.M13_builder{ Foo: proto.Int32(1), HasFoo: proto.Int32(2), }.Build() if !m13.HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.HasFoo(), m13) } if !m13.HasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.HasHasFoo(), m13) } checkNameConsistency(t, m13) m14 := opb.M14_builder{ Foo: proto.Int32(1), HasHasFoo: proto.Int32(2), }.Build() if !m14.HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasFoo(), m14) } if !m14.HasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasHasFoo(), m14) } if !m14.HasHasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasHasHasFoo(), m14) } checkNameConsistency(t, m14) m15 := opb.M15_builder{ Foo: proto.Int32(1), HasFoo: proto.Int32(2), }.Build() if !m15.HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasFoo(), m15) } if !m15.HasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasHasFoo(), m15) } if !m15.HasHasHasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasHasHasFoo(), m15) } checkNameConsistency(t, m15) m16 := opb.M16_builder{ Foo: proto.Int32(1), ClearFoo: proto.Int32(2), }.Build() m16.SetFoo(4711) m16.ClearFoo() if m16.HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.HasFoo(), m16) } m16.ClearClearFoo() if m16.HasClearFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.HasClearFoo(), m16) } checkNameConsistency(t, m16) m17 := opb.M17_builder{ Foo: proto.Int32(1), ClearClearFoo: proto.Int32(2), }.Build() m17.SetFoo(4711) m17.ClearFoo() if m17.HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.HasFoo(), m17) } m17.ClearClearClearFoo() if m17.HasClearClearFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.HasClearClearFoo(), m17) } checkNameConsistency(t, m17) m18 := opb.M18_builder{ Foo: proto.Int32(1), ClearFoo: proto.Int32(2), }.Build() m18.SetFoo(4711) m18.ClearFoo() if m18.HasFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.HasFoo(), m18) } m18.SetClearFoo(13) m18.ClearClearFoo() if m18.HasClearFoo() { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.HasClearFoo(), m18) } checkNameConsistency(t, m18) m19 := opb.M19_builder{ Foo: proto.Int32(1), WhichFoo: proto.Int32(2), }.Build() if m19.WhichWhichWhichFoo() != opb.M19_WhichFoo_case { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M19_ClearFoo_case)", m19.WhichWhichWhichFoo(), m19) } checkNameConsistency(t, m19) m20 := opb.M20_builder{ Foo: proto.Int32(1), WhichWhichFoo: proto.Int32(2), }.Build() if m20.WhichWhichFoo() != opb.M20_WhichWhichFoo_case { t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M20_WhichWhichFoo_case)", m20.WhichWhichFoo(), m20) } checkNameConsistency(t, m20) } func protogenFor(t *testing.T, m proto.Message) *protogen.Message { t.Helper() md := m.ProtoReflect().Descriptor() // Construct a Protobuf plugin code generation request based on the // transitive closure of dependencies of message m. req := &pluginpb.CodeGeneratorRequest{ ProtoFile: []*descpb.FileDescriptorProto{ protodesc.ToFileDescriptorProto(descpb.File_google_protobuf_descriptor_proto), protodesc.ToFileDescriptorProto(gofeaturespb.File_google_protobuf_go_features_proto), protodesc.ToFileDescriptorProto(md.ParentFile()), }, } plugin, err := protogen.Options{}.New(req) if err != nil { t.Fatalf("protogen.Options.New: %v", err) } if got, want := len(plugin.Files), len(req.ProtoFile); got != want { t.Fatalf("protogen returned %d plugin.Files entries, expected %d", got, want) } file := plugin.Files[len(plugin.Files)-1] for _, msg := range file.Messages { if msg.Desc.FullName() != md.FullName() { continue } return msg } t.Fatalf("BUG: message %q not found in protogen response", md.FullName()) return nil } // checkNameConsistency will go through the fields (deliberately avoiding // protoreflect; querying protogen instead), and for each field check that one // of the two naming schemes is used consistently, so that if you find // e.g. Has_Foo, the setter will be Set_Foo and not SetFoo. It also checks that // at least one of the naming schemes apply. func checkNameConsistency(t *testing.T, m proto.Message) { t.Helper() // It's wrong to use Go reflection on a Message, but in this // case, we're specifically looking at the implementation typ := reflect.TypeOf(m) // The info we need for one field type fi struct { name string prefixes []string } // The method prefixes for different kinds of fields repeatedPrefixes := []string{"Get", "Set"} oneofPrefixes := []string{"Has", "Clear", "Which"} optionalPrefixes := []string{"Get", "Set", "Has", "Clear"} fields := []fi{} msg := protogenFor(t, m) for _, f := range msg.Fields { prefixes := optionalPrefixes if f.Desc.Cardinality() == genid.Field_CARDINALITY_REPEATED_enum_value { prefixes = repeatedPrefixes } fields = append(fields, fi{name: f.GoName, prefixes: prefixes}) } for _, o := range msg.Oneofs { fields = append(fields, fi{name: o.GoName, prefixes: oneofPrefixes}) } if len(fields) == 0 { t.Errorf("Message %v checked for consistency has no fields to check", reflect.TypeOf(m)) } // Check method names for all fields for _, f := range fields { // Remove trailing underscores added by old name mangling algorithm for f.name[len(f.name)-1] == '_' { f.name = f.name[:len(f.name)-1] } // Check consistency of either "underscored" methods or "non underscored" found := "" for _, infix := range []string{"_", ""} { for _, prefix := range f.prefixes { if m, ok := typ.MethodByName(prefix + infix + f.name); ok { found = m.Name break } } if found != "" { for _, prefix := range f.prefixes { if _, ok := typ.MethodByName(prefix + infix + f.name); !ok { t.Errorf("Field %s has inconsistent method names - found %s, but not %s", f.name, found, prefix+infix+f.name) } } break } } // If we found neither, something is wrong if found == "" { t.Errorf("Field %s has neither plain nor underscored methods.", f.name) } } }