@@ -48,44 +48,50 @@ internal static class JsonNamingHelpers
4848
4949 private static EnumMapping GetEnumMapping ( EnumDescriptor enumDescriptor )
5050 {
51- var enumType = enumDescriptor . ClrType ;
52-
53- EnumMapping ? enumMapping ;
54- lock ( _enumMappings )
55- {
56- if ( ! _enumMappings . TryGetValue ( enumType , out enumMapping ) )
57- {
58- _enumMappings [ enumType ] = enumMapping = GetEnumMapping ( enumDescriptor . Name , enumType ) ;
59- }
60- }
61-
62- return enumMapping ;
51+ return _enumMappings . GetOrAdd (
52+ enumDescriptor . ClrType ,
53+ static ( t , descriptor ) => GetEnumMapping ( descriptor . Name , t ) ,
54+ enumDescriptor ) ;
6355 }
6456
6557 private static EnumMapping GetEnumMapping ( string enumName , Type enumType )
6658 {
67- var enumFields = enumType . GetTypeInfo ( ) . DeclaredFields
59+ var nameMappings = enumType . GetTypeInfo ( ) . DeclaredFields
6860 . Where ( f => f . IsStatic )
6961 . Where ( f => f . GetCustomAttributes < OriginalNameAttribute > ( ) . FirstOrDefault ( ) ? . PreferredAlias ?? true )
70- . ToList ( ) ;
71-
72- var writeMapping = enumFields . ToDictionary (
73- f => f . GetValue ( null ) ! ,
74- f =>
62+ . Select ( f =>
7563 {
7664 // If the attribute hasn't been applied, fall back to the name of the field.
7765 var fieldName = f . GetCustomAttributes < OriginalNameAttribute > ( ) . FirstOrDefault ( ) ? . Name ?? f . Name ;
7866
7967 return new NameMapping
8068 {
69+ Value = f . GetValue ( null ) ! ,
8170 OriginalName = fieldName ,
8271 RemoveEnumPrefixName = GetEnumValueName ( enumName , fieldName )
8372 } ;
84- } ) ;
73+ } )
74+ . ToList ( ) ;
8575
86- var removeEnumPrefixMapping = writeMapping . Values . ToDictionary (
87- m => m . RemoveEnumPrefixName ,
88- m => m . OriginalName ) ;
76+ var writeMapping = nameMappings . ToDictionary ( m => m . Value , m => m ) ;
77+
78+ // Protobuf codegen prevents collision of enum names when the prefix is removed.
79+ // For example, the following enum will fail to build because both fields would resolve to "OK":
80+ //
81+ // enum Status {
82+ // STATUS_OK = 0;
83+ // OK = 1;
84+ // }
85+ //
86+ // Tooling error message:
87+ // ----------------------
88+ // Enum name OK has the same name as STATUS_OK if you ignore case and strip out the enum name prefix (if any).
89+ // (If you are using allow_alias, please assign the same number to each enum value name.)
90+ //
91+ // Just in case it does happen, map to the first value rather than error.
92+ var removeEnumPrefixMapping = nameMappings
93+ . GroupBy ( m => m . RemoveEnumPrefixName )
94+ . ToDictionary ( g => g . Key , g => g . First ( ) . OriginalName , StringComparer . Ordinal ) ;
8995
9096 return new EnumMapping { WriteMapping = writeMapping , RemoveEnumPrefixMapping = removeEnumPrefixMapping } ;
9197 }
@@ -146,6 +152,7 @@ private sealed class EnumMapping
146152
147153 private sealed class NameMapping
148154 {
155+ public required object Value { get ; init ; }
149156 public required string OriginalName { get ; init ; }
150157 public required string RemoveEnumPrefixName { get ; init ; }
151158 }
0 commit comments