Skip to content

Commit 5b02c44

Browse files
committed
Fix build break in generated code when custom structs lack an equality operator
1 parent d3eeb73 commit 5b02c44

2 files changed

Lines changed: 45 additions & 10 deletions

File tree

src/ImmutableObjectGraph.Generation.Tests/TestSources/ImmutableWithComplexStructField.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,15 @@ internal SomeStructWithMultipleFields(int field1, int field2)
1717
internal int Field1 { get; }
1818

1919
internal int Field2 { get; }
20+
21+
////public static bool operator ==(SomeStructWithMultipleFields one, SomeStructWithMultipleFields two)
22+
////{
23+
//// return one.Field1 == two.Field1 && one.Field2 == two.Field2;
24+
////}
25+
26+
////public static bool operator !=(SomeStructWithMultipleFields one, SomeStructWithMultipleFields two)
27+
////{
28+
//// return !(one == two);
29+
////}
2030
}
2131
}

src/ImmutableObjectGraph.Generation/CodeGen.cs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,29 @@ 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+
// Reference types inherit their equality operators from System.Object.
223+
if (symbol.IsReferenceType)
224+
{
225+
return true;
226+
}
227+
228+
var equalityOperators = from method in symbol.GetMembers().OfType<IMethodSymbol>()
229+
where method.MethodKind == MethodKind.BuiltinOperator || method.MethodKind == MethodKind.UserDefinedOperator
230+
where method.Parameters.Length == 2 && method.Parameters.All(p => p.Type == symbol)
231+
where method.Name == "op_Equality"
232+
select method;
233+
return equalityOperators.Any();
234+
}
235+
213236
private void ReportDiagnostic(string id, SyntaxNode blamedSyntax, params string[] formattingArgs)
214237
{
215238
Requires.NotNull(blamedSyntax, nameof(blamedSyntax));
@@ -533,18 +556,20 @@ private IEnumerable<MethodDeclarationSyntax> CreateWithCoreMethods()
533556
private MemberDeclarationSyntax CreateWithFactoryMethod()
534557
{
535558
// (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),
559+
Func<IdentifierNameSyntax, IdentifierNameSyntax, ITypeSymbol, ExpressionSyntax> isChangedByNames = (propertyName, fieldName, fieldType) =>
560+
fieldType == null || HasEqualityOperators(fieldType) ?
561+
(ExpressionSyntax)SyntaxFactory.ParenthesizedExpression(
541562
SyntaxFactory.BinaryExpression(
542-
SyntaxKind.NotEqualsExpression,
543-
Syntax.OptionalValue(fieldName),
544-
Syntax.ThisDot(propertyName))));
545-
Func<MetaField, ExpressionSyntax> isChanged = v => isChangedByNames(v.NameAsProperty, v.NameAsField);
563+
SyntaxKind.LogicalAndExpression,
564+
Syntax.OptionalIsDefined(fieldName),
565+
SyntaxFactory.BinaryExpression(
566+
SyntaxKind.NotEqualsExpression,
567+
Syntax.OptionalValue(fieldName),
568+
Syntax.ThisDot(propertyName)))) :
569+
Syntax.OptionalIsDefined(fieldName);
570+
Func<MetaField, ExpressionSyntax> isChanged = v => isChangedByNames(v.NameAsProperty, v.NameAsField, v.Symbol.Type);
546571
var anyChangesExpression =
547-
new ExpressionSyntax[] { isChangedByNames(IdentityPropertyName, IdentityParameterName) }.Concat(
572+
new ExpressionSyntax[] { isChangedByNames(IdentityPropertyName, IdentityParameterName, null) }.Concat(
548573
this.applyToMetaType.AllFields.Select(isChanged))
549574
.ChainBinaryExpressions(SyntaxKind.LogicalOrExpression);
550575

0 commit comments

Comments
 (0)