@@ -261,9 +261,13 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool, record fu
261261 }... )
262262
263263 case protoreflect.ServiceDescriptor :
264- rs .Append (rv , []methodAndName {
265- {rv .MethodByName ("Methods" ), "Methods" },
266- }... )
264+ // MethodByName(<constant-string>) makes the linker keep all methods with
265+ // the given name for all reachable types.
266+ // In particular for the name "Methods" it means the linker will keep
267+ // `reflect.Value.Methods()` and `reflect.Type.Methods()` with Go 1.26+,
268+ // which disable method dead code elimination entirely.
269+ // So we avoid using MethodByName for Methods.
270+ rs .appendCallResult (rv , "Methods" , reflect .ValueOf (t .Methods ()))
267271
268272 case protoreflect.MethodDescriptor :
269273 rs .Append (rv , []methodAndName {
@@ -299,66 +303,70 @@ func (rs *records) AppendRecs(fieldName string, newRecs [2]string) {
299303
300304func (rs * records ) Append (v reflect.Value , accessors ... methodAndName ) {
301305 for _ , a := range accessors {
302- if rs .record != nil {
303- rs .record (a .name )
304- }
305306 var rv reflect.Value
306307 if a .method .IsValid () {
307308 rv = a .method .Call (nil )[0 ]
308309 }
309- if v .Kind () == reflect .Struct && ! rv .IsValid () {
310- rv = v .FieldByName (a .name )
311- }
312- if ! rv .IsValid () {
313- panic (fmt .Sprintf ("unknown accessor: %v.%s" , v .Type (), a .name ))
314- }
315- if _ , ok := rv .Interface ().(protoreflect.Value ); ok {
316- rv = rv .MethodByName ("Interface" ).Call (nil )[0 ]
317- if ! rv .IsNil () {
318- rv = rv .Elem ()
319- }
320- }
310+ rs .appendCallResult (v , a .name , rv )
311+ }
312+ }
321313
322- // Ignore zero values.
323- var isZero bool
324- switch rv .Kind () {
325- case reflect .Interface , reflect .Slice :
326- isZero = rv .IsNil ()
327- case reflect .Bool :
328- isZero = rv .Bool () == false
329- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
330- isZero = rv .Int () == 0
331- case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
332- isZero = rv .Uint () == 0
333- case reflect .String :
334- isZero = rv .String () == ""
335- }
336- if n , ok := rv .Interface ().(list ); ok {
337- isZero = n .Len () == 0
338- }
339- if isZero {
340- continue
314+ func (rs * records ) appendCallResult (val reflect.Value , name string , rv reflect.Value ) {
315+ if rs .record != nil {
316+ rs .record (name )
317+ }
318+ if val .Kind () == reflect .Struct && ! rv .IsValid () {
319+ rv = val .FieldByName (name )
320+ }
321+ if ! rv .IsValid () {
322+ panic (fmt .Sprintf ("unknown accessor: %v.%s" , val .Type (), name ))
323+ }
324+ if _ , ok := rv .Interface ().(protoreflect.Value ); ok {
325+ rv = rv .MethodByName ("Interface" ).Call (nil )[0 ]
326+ if ! rv .IsNil () {
327+ rv = rv .Elem ()
341328 }
329+ }
342330
343- // Format the value.
344- var s string
345- v := rv .Interface ()
346- switch v := v .(type ) {
347- case list :
348- s = formatListOpt (v , false , rs .allowMulti )
349- case protoreflect.FieldDescriptor , protoreflect.OneofDescriptor , protoreflect.EnumValueDescriptor , protoreflect.MethodDescriptor :
350- s = string (v .(protoreflect.Descriptor ).Name ())
351- case protoreflect.Descriptor :
352- s = string (v .FullName ())
353- case string :
354- s = strconv .Quote (v )
355- case []byte :
356- s = fmt .Sprintf ("%q" , v )
357- default :
358- s = fmt .Sprint (v )
359- }
360- rs .recs = append (rs .recs , [2 ]string {a .name , s })
331+ // Ignore zero values.
332+ var isZero bool
333+ switch rv .Kind () {
334+ case reflect .Interface , reflect .Slice :
335+ isZero = rv .IsNil ()
336+ case reflect .Bool :
337+ isZero = rv .Bool () == false
338+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
339+ isZero = rv .Int () == 0
340+ case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
341+ isZero = rv .Uint () == 0
342+ case reflect .String :
343+ isZero = rv .String () == ""
344+ }
345+ if n , ok := rv .Interface ().(list ); ok {
346+ isZero = n .Len () == 0
347+ }
348+ if isZero {
349+ return
350+ }
351+
352+ // Format the value.
353+ var s string
354+ v := rv .Interface ()
355+ switch v := v .(type ) {
356+ case list :
357+ s = formatListOpt (v , false , rs .allowMulti )
358+ case protoreflect.FieldDescriptor , protoreflect.OneofDescriptor , protoreflect.EnumValueDescriptor , protoreflect.MethodDescriptor :
359+ s = string (v .(protoreflect.Descriptor ).Name ())
360+ case protoreflect.Descriptor :
361+ s = string (v .FullName ())
362+ case string :
363+ s = strconv .Quote (v )
364+ case []byte :
365+ s = fmt .Sprintf ("%q" , v )
366+ default :
367+ s = fmt .Sprint (v )
361368 }
369+ rs .recs = append (rs .recs , [2 ]string {name , s })
362370}
363371
364372func (rs * records ) Join () string {
0 commit comments