33using Microsoft . CodeAnalysis ;
44using Microsoft . CodeAnalysis . CSharp ;
55using Microsoft . CodeAnalysis . CSharp . Syntax ;
6+ using Microsoft . CodeAnalysis . Diagnostics ;
67using Microsoft . CodeAnalysis . Operations ;
78using Microsoft . CodeAnalysis . Text ;
89
@@ -13,11 +14,16 @@ public class EnumGenerator : IIncrementalGenerator
1314{
1415 public void Initialize ( IncrementalGeneratorInitializationContext context )
1516 {
17+ var defaultMetadataSource = context . AnalyzerConfigOptionsProvider
18+ . Select ( GetDefaultMetadataSource ) ;
19+
1620 var csharp14IsSupported = context . CompilationProvider
1721 . Select ( ( x , _ ) => x is CSharpCompilation
1822 {
1923 LanguageVersion : LanguageVersion . Preview or >= ( LanguageVersion ) 1400 // C#14
2024 } ) ;
25+
26+ var defaults = csharp14IsSupported . Combine ( defaultMetadataSource ) ;
2127
2228 IncrementalValuesProvider < EnumToGenerate > enumsToGenerate = context . SyntaxProvider
2329 . ForAttributeWithMetadataName ( Attributes . EnumExtensionsAttribute ,
@@ -37,16 +43,35 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3743 . SelectMany ( static ( m , _ ) => m ! . Value )
3844 . WithTrackingName ( TrackingNames . InitialExternalExtraction ) ;
3945
40- context . RegisterSourceOutput ( enumsToGenerate . Combine ( csharp14IsSupported ) ,
41- static ( spc , enumToGenerate ) => Execute ( in enumToGenerate . Left , enumToGenerate . Right , spc ) ) ;
46+ context . RegisterSourceOutput ( enumsToGenerate . Combine ( defaults ) ,
47+ static ( spc , enumToGenerate ) => Execute ( in enumToGenerate . Left , enumToGenerate . Right . Left , enumToGenerate . Right . Right , spc ) ) ;
4248
43- context . RegisterSourceOutput ( externalEnums . Combine ( csharp14IsSupported ) ,
44- static ( spc , enumToGenerate ) => Execute ( in enumToGenerate . Left , enumToGenerate . Right , spc ) ) ;
49+ context . RegisterSourceOutput ( externalEnums . Combine ( defaults ) ,
50+ static ( spc , enumToGenerate ) => Execute ( in enumToGenerate . Left , enumToGenerate . Right . Left , enumToGenerate . Right . Right , spc ) ) ;
4551 }
4652
47- static void Execute ( in EnumToGenerate enumToGenerate , bool csharp14IsSupported , SourceProductionContext context )
53+ private static MetadataSource GetDefaultMetadataSource ( AnalyzerConfigOptionsProvider configOptions , CancellationToken ct )
4854 {
49- var ( result , filename ) = SourceGenerationHelper . GenerateExtensionClass ( in enumToGenerate , csharp14IsSupported ) ;
55+ const MetadataSource defaultValue = MetadataSource . EnumMemberAttribute ;
56+ if ( configOptions . GlobalOptions . TryGetValue ( $ "build_property.{ Constants . MetadataSourcePropertyName } ",
57+ out var source ) )
58+ {
59+ return source switch
60+ {
61+ nameof ( MetadataSource . None ) => MetadataSource . None ,
62+ nameof ( MetadataSource . DisplayAttribute ) => MetadataSource . DisplayAttribute ,
63+ nameof ( MetadataSource . DescriptionAttribute ) => MetadataSource . DescriptionAttribute ,
64+ nameof ( MetadataSource . EnumMemberAttribute ) => MetadataSource . EnumMemberAttribute ,
65+ _ => defaultValue ,
66+ } ;
67+ }
68+
69+ return defaultValue ;
70+ }
71+
72+ static void Execute ( in EnumToGenerate enumToGenerate , bool csharp14IsSupported , MetadataSource source , SourceProductionContext context )
73+ {
74+ var ( result , filename ) = SourceGenerationHelper . GenerateExtensionClass ( in enumToGenerate , csharp14IsSupported , source ) ;
5075 context . AddSource ( filename , SourceText . From ( result , Encoding . UTF8 ) ) ;
5176 }
5277
@@ -74,6 +99,7 @@ static void Execute(in EnumToGenerate enumToGenerate, bool csharp14IsSupported,
7499 bool hasFlags = false ;
75100 string ? name = null ;
76101 string ? nameSpace = null ;
102+ MetadataSource ? source = null ;
77103
78104 foreach ( KeyValuePair < string , TypedConstant > namedArgument in attribute . NamedArguments )
79105 {
@@ -89,6 +115,12 @@ static void Execute(in EnumToGenerate enumToGenerate, bool csharp14IsSupported,
89115 {
90116 name = n ;
91117 }
118+
119+ if ( namedArgument . Key == "MetadataSource"
120+ && namedArgument . Value is { Kind : TypedConstantKind . Enum , Value : { } ms } )
121+ {
122+ source = ( MetadataSource ) ( int ) ms ;
123+ }
92124 }
93125
94126 foreach ( var attrData in enumSymbol . GetAttributes ( ) )
@@ -102,7 +134,7 @@ static void Execute(in EnumToGenerate enumToGenerate, bool csharp14IsSupported,
102134 }
103135 }
104136
105- var enumToGenerate = TryExtractEnumSymbol ( enumSymbol , name , nameSpace , hasFlags ) ;
137+ var enumToGenerate = TryExtractEnumSymbol ( enumSymbol , name , nameSpace , source , hasFlags ) ;
106138 if ( enumToGenerate is not null )
107139 {
108140 enums ??= new ( ) ;
@@ -135,6 +167,7 @@ static void Execute(in EnumToGenerate enumToGenerate, bool csharp14IsSupported,
135167 var hasFlags = false ;
136168 string ? nameSpace = null ;
137169 string ? name = null ;
170+ MetadataSource ? metadataSource = null ;
138171
139172 foreach ( AttributeData attributeData in enumSymbol . GetAttributes ( ) )
140173 {
@@ -146,13 +179,17 @@ static void Execute(in EnumToGenerate enumToGenerate, bool csharp14IsSupported,
146179 continue ;
147180 }
148181
149- TryGetExtensionAttributeDetails ( attributeData , ref nameSpace , ref name ) ;
182+ TryGetExtensionAttributeDetails ( attributeData , ref nameSpace , ref name , ref metadataSource ) ;
150183 }
151184
152- return TryExtractEnumSymbol ( enumSymbol , name , nameSpace , hasFlags ) ;
185+ return TryExtractEnumSymbol ( enumSymbol , name , nameSpace , metadataSource , hasFlags ) ;
153186 }
154187
155- internal static bool TryGetExtensionAttributeDetails ( AttributeData attributeData , ref string ? nameSpace , ref string ? name )
188+ internal static bool TryGetExtensionAttributeDetails (
189+ AttributeData attributeData ,
190+ ref string ? nameSpace ,
191+ ref string ? name ,
192+ ref MetadataSource ? source )
156193 {
157194 if ( attributeData . AttributeClass ? . Name != "EnumExtensionsAttribute" ||
158195 attributeData . AttributeClass . ToDisplayString ( ) != Attributes . EnumExtensionsAttribute )
@@ -174,6 +211,12 @@ internal static bool TryGetExtensionAttributeDetails(AttributeData attributeData
174211 {
175212 name = n ;
176213 }
214+
215+ if ( namedArgument . Key == "MetadataSource"
216+ && namedArgument . Value is { Kind : TypedConstantKind . Enum , Value : { } ms } )
217+ {
218+ source = ( MetadataSource ) ( int ) ms ;
219+ }
177220 }
178221
179222 return true ;
@@ -185,7 +228,12 @@ internal static string GetEnumExtensionNamespace(INamedTypeSymbol enumSymbol)
185228 internal static string GetEnumExtensionName ( INamedTypeSymbol enumSymbol )
186229 => enumSymbol . Name + "Extensions" ;
187230
188- static EnumToGenerate ? TryExtractEnumSymbol ( INamedTypeSymbol enumSymbol , string ? name , string ? nameSpace , bool hasFlags )
231+ static EnumToGenerate ? TryExtractEnumSymbol (
232+ INamedTypeSymbol enumSymbol ,
233+ string ? name ,
234+ string ? nameSpace ,
235+ MetadataSource ? metadataSource ,
236+ bool hasFlags )
189237 {
190238 name ??= GetEnumExtensionName ( enumSymbol ) ;
191239 nameSpace ??= GetEnumExtensionNamespace ( enumSymbol ) ;
@@ -195,8 +243,6 @@ internal static string GetEnumExtensionName(INamedTypeSymbol enumSymbol)
195243
196244 var enumMembers = enumSymbol . GetMembers ( ) ;
197245 var members = new List < ( string , EnumValueOption ) > ( enumMembers . Length ) ;
198- HashSet < string > ? displayNames = null ;
199- var isDisplayNameTheFirstPresence = false ;
200246
201247 foreach ( var member in enumMembers )
202248 {
@@ -206,6 +252,8 @@ internal static string GetEnumExtensionName(INamedTypeSymbol enumSymbol)
206252 }
207253
208254 string ? displayName = null ;
255+ string ? description = null ;
256+ string ? enumMemberValue = null ;
209257 foreach ( var attribute in member . GetAttributes ( ) )
210258 {
211259 if ( attribute . AttributeClass ? . Name == "DisplayAttribute" &&
@@ -215,9 +263,7 @@ internal static string GetEnumExtensionName(INamedTypeSymbol enumSymbol)
215263 {
216264 if ( namedArgument . Key == "Name" && namedArgument . Value . Value ? . ToString ( ) is { } dn )
217265 {
218- // found display attribute, all done
219266 displayName = dn ;
220- goto addDisplayName ;
221267 }
222268 }
223269 }
@@ -228,22 +274,24 @@ internal static string GetEnumExtensionName(INamedTypeSymbol enumSymbol)
228274 {
229275 if ( attribute . ConstructorArguments [ 0 ] . Value ? . ToString ( ) is { } dn )
230276 {
231- // found display attribute, all done
232- // Handle cases where contains a quote or a backslash
233- displayName = dn ;
234- goto addDisplayName ;
277+ description = dn ;
235278 }
236279 }
237- }
238280
239- addDisplayName :
240- if ( displayName is not null )
241- {
242- displayNames ??= new ( ) ;
243- isDisplayNameTheFirstPresence = displayNames . Add ( displayName ) ;
281+ if ( attribute . AttributeClass ? . Name == "EnumMemberAttribute" &&
282+ attribute . AttributeClass . ToDisplayString ( ) == Attributes . EnumMemberAttribute )
283+ {
284+ foreach ( var namedArgument in attribute . NamedArguments )
285+ {
286+ if ( namedArgument . Key == "Value" && namedArgument . Value . Value ? . ToString ( ) is { } dn )
287+ {
288+ enumMemberValue = dn ;
289+ }
290+ }
291+ }
244292 }
245-
246- members . Add ( ( member . Name , new EnumValueOption ( displayName , isDisplayNameTheFirstPresence , constantValue ) ) ) ;
293+
294+ members . Add ( ( member . Name , new EnumValueOption ( displayName , description , enumMemberValue , constantValue ) ) ) ;
247295 }
248296
249297 return new EnumToGenerate (
@@ -254,7 +302,7 @@ internal static string GetEnumExtensionName(INamedTypeSymbol enumSymbol)
254302 isPublic : enumSymbol . DeclaredAccessibility == Accessibility . Public ,
255303 hasFlags : hasFlags ,
256304 names : members ,
257- isDisplayAttributeUsed : displayNames ? . Count > 0 ) ;
305+ metadataSource : metadataSource ) ;
258306 }
259307
260308}
0 commit comments