@@ -210,6 +210,35 @@ private static IdentifierNameSyntax GetGenerationalMethodName(IdentifierNameSynt
210210 return SyntaxFactory . IdentifierName ( baseName . Identifier . ValueText + generation . ToString ( CultureInfo . InvariantCulture ) ) ;
211211 }
212212
213+ /// <summary>
214+ /// Checks whether a type defines equality operators for itself.
215+ /// </summary>
216+ /// <param name="symbol">The type to check.</param>
217+ /// <returns><c>true</c> if the == and != operators are defined on the type.</returns>
218+ private static bool HasEqualityOperators ( ITypeSymbol symbol )
219+ {
220+ Requires . NotNull ( symbol , nameof ( symbol ) ) ;
221+
222+ if ( symbol . IsReferenceType )
223+ {
224+ // Reference types inherit their equality operators from System.Object.
225+ return true ;
226+ }
227+
228+ if ( symbol . SpecialType != SpecialType . None )
229+ {
230+ // C# knows how to run equality checks for special (built-in) types like int.
231+ return true ;
232+ }
233+
234+ var equalityOperators = from method in symbol . GetMembers ( ) . OfType < IMethodSymbol > ( )
235+ where method . MethodKind == MethodKind . BuiltinOperator || method . MethodKind == MethodKind . UserDefinedOperator
236+ where method . Parameters . Length == 2 && method . Parameters . All ( p => p . Type == symbol )
237+ where method . Name == "op_Equality"
238+ select method ;
239+ return equalityOperators . Any ( ) ;
240+ }
241+
213242 private void ReportDiagnostic ( string id , SyntaxNode blamedSyntax , params string [ ] formattingArgs )
214243 {
215244 Requires . NotNull ( blamedSyntax , nameof ( blamedSyntax ) ) ;
@@ -533,18 +562,20 @@ private IEnumerable<MethodDeclarationSyntax> CreateWithCoreMethods()
533562 private MemberDeclarationSyntax CreateWithFactoryMethod ( )
534563 {
535564 // (field.IsDefined && field.Value != this.field)
536- Func < IdentifierNameSyntax , IdentifierNameSyntax , ExpressionSyntax > isChangedByNames = ( propertyName , fieldName ) =>
537- SyntaxFactory . ParenthesizedExpression (
538- SyntaxFactory . BinaryExpression (
539- SyntaxKind . LogicalAndExpression ,
540- Syntax . OptionalIsDefined ( fieldName ) ,
565+ Func < IdentifierNameSyntax , IdentifierNameSyntax , ITypeSymbol , ExpressionSyntax > isChangedByNames = ( propertyName , fieldName , fieldType ) =>
566+ fieldType == null || HasEqualityOperators ( fieldType ) ?
567+ ( ExpressionSyntax ) SyntaxFactory . ParenthesizedExpression (
541568 SyntaxFactory . BinaryExpression (
542- SyntaxKind . NotEqualsExpression ,
543- Syntax . OptionalValue ( fieldName ) ,
544- Syntax . ThisDot ( propertyName ) ) ) ) ;
545- Func < MetaField , ExpressionSyntax > isChanged = v => isChangedByNames ( v . NameAsProperty , v . NameAsField ) ;
569+ SyntaxKind . LogicalAndExpression ,
570+ Syntax . OptionalIsDefined ( fieldName ) ,
571+ SyntaxFactory . BinaryExpression (
572+ SyntaxKind . NotEqualsExpression ,
573+ Syntax . OptionalValue ( fieldName ) ,
574+ Syntax . ThisDot ( propertyName ) ) ) ) :
575+ Syntax . OptionalIsDefined ( fieldName ) ;
576+ Func < MetaField , ExpressionSyntax > isChanged = v => isChangedByNames ( v . NameAsProperty , v . NameAsField , v . Symbol . Type ) ;
546577 var anyChangesExpression =
547- new ExpressionSyntax [ ] { isChangedByNames ( IdentityPropertyName , IdentityParameterName ) } . Concat (
578+ new ExpressionSyntax [ ] { isChangedByNames ( IdentityPropertyName , IdentityParameterName , null ) } . Concat (
548579 this . applyToMetaType . AllFields . Select ( isChanged ) )
549580 . ChainBinaryExpressions ( SyntaxKind . LogicalOrExpression ) ;
550581
0 commit comments