1- using System . Reflection ;
2- using System . Xml . Linq ;
3-
41using Equatable . SourceGenerator . Models ;
52
63using Microsoft . CodeAnalysis ;
@@ -12,53 +9,36 @@ namespace Equatable.SourceGenerator;
129[ Generator ]
1310public class EquatableGenerator : IIncrementalGenerator
1411{
15- private static SymbolDisplayFormat FullyQualifiedNullableFormat = SymbolDisplayFormat . FullyQualifiedFormat . WithMiscellaneousOptions ( SymbolDisplayMiscellaneousOptions . IncludeNullableReferenceTypeModifier ) ;
12+ private static readonly SymbolDisplayFormat FullyQualifiedNullableFormat = SymbolDisplayFormat . FullyQualifiedFormat . WithMiscellaneousOptions ( SymbolDisplayMiscellaneousOptions . IncludeNullableReferenceTypeModifier ) ;
13+ private static readonly SymbolDisplayFormat NameAndNamespaces = new ( SymbolDisplayGlobalNamespaceStyle . Omitted , SymbolDisplayTypeQualificationStyle . NameAndContainingTypesAndNamespaces , SymbolDisplayGenericsOptions . None ) ;
1614
1715 public void Initialize ( IncrementalGeneratorInitializationContext context )
1816 {
19- var provider = context . SyntaxProvider . ForAttributeWithMetadataName (
20- fullyQualifiedMetadataName : "Equatable.Attributes.EquatableAttribute" ,
21- predicate : SyntacticPredicate ,
22- transform : SemanticTransform
23- )
24- . Where ( static context => context is not null ) ;
25-
26- // Emit the diagnostics, if needed
27- var diagnostics = provider
28- . Select ( static ( item , _ ) => item ? . Diagnostics )
29- . Where ( static item => item ? . Count > 0 ) ;
30-
31- context . RegisterSourceOutput ( diagnostics , ReportDiagnostic ) ;
17+ var provider = context . SyntaxProvider
18+ . ForAttributeWithMetadataName (
19+ fullyQualifiedMetadataName : "Equatable.Attributes.EquatableAttribute" ,
20+ predicate : SyntacticPredicate ,
21+ transform : SemanticTransform
22+ )
23+ . Where ( static context => context is not null )
24+ . WithTrackingName ( "EquatableAttribute" ) ;
3225
3326 // output code
3427 var entityClasses = provider
35- . Select ( static ( item , _ ) => item ? . EntityClass )
3628 . Where ( static item => item is not null ) ;
3729
3830 context . RegisterSourceOutput ( entityClasses , Execute ) ;
3931 }
4032
41- private static void ReportDiagnostic ( SourceProductionContext context , EquatableArray < Diagnostic > ? diagnostics )
42- {
43- if ( diagnostics == null )
44- return ;
45-
46- foreach ( var diagnostic in diagnostics )
47- context . ReportDiagnostic ( diagnostic ) ;
48- }
4933
5034 private static void Execute ( SourceProductionContext context , EquatableClass ? entityClass )
5135 {
5236 if ( entityClass == null )
5337 return ;
5438
55- var qualifiedName = entityClass . EntityNamespace is null
56- ? entityClass . EntityName
57- : $ "{ entityClass . EntityNamespace } .{ entityClass . EntityName } ";
58-
5939 var source = EquatableWriter . Generate ( entityClass ) ;
6040
61- context . AddSource ( $ " { qualifiedName } .Equatable.g.cs" , source ) ;
41+ context . AddSource ( entityClass . FileName , source ) ;
6242 }
6343
6444
@@ -69,16 +49,16 @@ private static bool SyntacticPredicate(SyntaxNode syntaxNode, CancellationToken
6949 || ( syntaxNode is StructDeclarationSyntax structDeclaration && ! structDeclaration . Modifiers . Any ( SyntaxKind . StaticKeyword ) ) ;
7050 }
7151
72- private static EquatableContext ? SemanticTransform ( GeneratorAttributeSyntaxContext context , CancellationToken cancellationToken )
52+ private static EquatableClass ? SemanticTransform ( GeneratorAttributeSyntaxContext context , CancellationToken cancellationToken )
7353 {
7454 if ( context . TargetSymbol is not INamedTypeSymbol targetSymbol )
7555 return null ;
7656
77- var diagnostics = new List < Diagnostic > ( ) ;
78-
79- var fullyQualified = targetSymbol . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ;
57+ var fullyQualified = targetSymbol . ToDisplayString ( FullyQualifiedNullableFormat ) ;
8058 var classNamespace = targetSymbol . ContainingNamespace . ToDisplayString ( ) ;
81- var className = targetSymbol . Name ;
59+ var className = targetSymbol . ToDisplayString ( SymbolDisplayFormat . MinimallyQualifiedFormat ) ;
60+ var qualifiedName = targetSymbol . ToDisplayString ( NameAndNamespaces ) ;
61+ var fileName = $ "{ qualifiedName } .Equatable.g.cs";
8262
8363 // support nested types
8464 var containingTypes = GetContainingTypes ( targetSymbol ) ;
@@ -90,7 +70,7 @@ private static bool SyntacticPredicate(SyntaxNode syntaxNode, CancellationToken
9070 var propertySymbols = GetProperties ( targetSymbol , baseHashCode == null && baseEquatable == null ) ;
9171
9272 var propertyArray = propertySymbols
93- . Select ( symbol => CreateProperty ( diagnostics , symbol ) )
73+ . Select ( CreateProperty )
9474 . ToArray ( ) ?? [ ] ;
9575
9676 // the seed value of the hash code method
@@ -106,10 +86,11 @@ private static bool SyntacticPredicate(SyntaxNode syntaxNode, CancellationToken
10686 foreach ( var property in propertyArray )
10787 seedHash = ( seedHash * HashFactor ) + GetFNVHashCode ( property . PropertyName ) ;
10888
109- var entity = new EquatableClass (
89+ return new EquatableClass (
11090 FullyQualified : fullyQualified ,
11191 EntityNamespace : classNamespace ,
11292 EntityName : className ,
93+ FileName : fileName ,
11394 ContainingTypes : containingTypes ,
11495 Properties : propertyArray ,
11596 IsRecord : targetSymbol . IsRecord ,
@@ -119,8 +100,6 @@ private static bool SyntacticPredicate(SyntaxNode syntaxNode, CancellationToken
119100 IncludeBaseHashMethod : baseHashCode != null || baseEquatable != null ,
120101 SeedHash : seedHash
121102 ) ;
122-
123- return new EquatableContext ( entity , diagnostics . ToArray ( ) ) ;
124103 }
125104
126105
@@ -152,10 +131,12 @@ private static IEnumerable<IPropertySymbol> GetProperties(INamedTypeSymbol targe
152131 return properties . Values ;
153132 }
154133
155- private static EquatableProperty CreateProperty ( List < Diagnostic > diagnostics , IPropertySymbol propertySymbol )
134+ private static EquatableProperty CreateProperty ( IPropertySymbol propertySymbol )
156135 {
157136 var propertyType = propertySymbol . Type . ToDisplayString ( FullyQualifiedNullableFormat ) ;
158137 var propertyName = propertySymbol . Name ;
138+ var isValueType = propertySymbol . Type . IsValueType ;
139+ var defaultComparer = isValueType ? ComparerTypes . ValueType : ComparerTypes . Default ;
159140
160141 // look for custom equality
161142 var attributes = propertySymbol . GetAttributes ( ) ;
@@ -164,7 +145,7 @@ private static EquatableProperty CreateProperty(List<Diagnostic> diagnostics, IP
164145 return new EquatableProperty (
165146 propertyName ,
166147 propertyType ,
167- ComparerTypes . Default ) ;
148+ defaultComparer ) ;
168149 }
169150
170151 // search for known attribute
@@ -175,15 +156,13 @@ private static EquatableProperty CreateProperty(List<Diagnostic> diagnostics, IP
175156 if ( ! comparerType . HasValue )
176157 continue ;
177158
178- var diagnostic = ValidateComparer ( propertySymbol , comparerType ) ;
179- if ( diagnostic != null )
159+ var isValid = ValidateComparer ( propertySymbol , comparerType ) ;
160+ if ( ! isValid )
180161 {
181- diagnostics . Add ( diagnostic ) ;
182-
183162 return new EquatableProperty (
184163 propertyName ,
185164 propertyType ,
186- ComparerTypes . Default ) ;
165+ defaultComparer ) ;
187166 }
188167
189168 return new EquatableProperty (
@@ -197,64 +176,28 @@ private static EquatableProperty CreateProperty(List<Diagnostic> diagnostics, IP
197176 return new EquatableProperty (
198177 propertyName ,
199178 propertyType ,
200- ComparerTypes . Default ) ;
179+ defaultComparer ) ;
201180 }
202181
203- private static Diagnostic ? ValidateComparer ( IPropertySymbol propertySymbol , ComparerTypes ? comparerType )
182+ private static bool ValidateComparer ( IPropertySymbol propertySymbol , ComparerTypes ? comparerType )
204183 {
205184 // don't need to validate these types
206185 if ( comparerType is null or ComparerTypes . Default or ComparerTypes . Reference or ComparerTypes . Custom )
207- return null ;
186+ return true ;
208187
209188 if ( comparerType == ComparerTypes . String )
210- {
211- if ( IsString ( propertySymbol . Type ) )
212- return null ;
213-
214- return Diagnostic . Create (
215- DiagnosticDescriptors . InvalidStringEqualityAttributeUsage ,
216- propertySymbol . Locations . FirstOrDefault ( ) ,
217- propertySymbol . Name
218- ) ;
219- }
189+ return IsString ( propertySymbol . Type ) ;
220190
221191 if ( comparerType == ComparerTypes . Dictionary )
222- {
223- if ( propertySymbol . Type . AllInterfaces . Any ( IsDictionary ) )
224- return null ;
225-
226- return Diagnostic . Create (
227- DiagnosticDescriptors . InvalidDictionaryEqualityAttributeUsage ,
228- propertySymbol . Locations . FirstOrDefault ( ) ,
229- propertySymbol . Name
230- ) ;
231- }
192+ return propertySymbol . Type . AllInterfaces . Any ( IsDictionary ) ;
232193
233194 if ( comparerType == ComparerTypes . HashSet )
234- {
235- if ( propertySymbol . Type . AllInterfaces . Any ( IsEnumerable ) )
236- return null ;
237-
238- return Diagnostic . Create (
239- DiagnosticDescriptors . InvalidHashSetEqualityAttributeUsage ,
240- propertySymbol . Locations . FirstOrDefault ( ) ,
241- propertySymbol . Name
242- ) ;
243- }
195+ return propertySymbol . Type . AllInterfaces . Any ( IsEnumerable ) ;
244196
245197 if ( comparerType == ComparerTypes . Sequence )
246- {
247- if ( propertySymbol . Type . AllInterfaces . Any ( IsEnumerable ) )
248- return null ;
198+ return propertySymbol . Type . AllInterfaces . Any ( IsEnumerable ) ;
249199
250- return Diagnostic . Create (
251- DiagnosticDescriptors . InvalidSequenceEqualityAttributeUsage ,
252- propertySymbol . Locations . FirstOrDefault ( ) ,
253- propertySymbol . Name
254- ) ;
255- }
256-
257- return null ;
200+ return true ;
258201 }
259202
260203
0 commit comments