private static MemberDeclarationSyntax GenerateEquatableEquals(RecordDescriptor descriptor) { // public bool Equals(MyRecord other) { // return other != null && PropA == other.PropA && ...; // } const string otherVariableName = "other"; return (MethodDeclaration( PredefinedType( Token(BoolKeyword)), EqualsMethodName) .AddModifiers( Token(PublicKeyword)) .AddParameterListParameters( Parameter( Identifier(otherVariableName)) .WithType(descriptor.TypeSyntax)) .AddBodyStatements( ReturnStatement( GenerateEqualsExpressions(descriptor, otherVariableName) .Aggregate( BinaryExpression( NotEqualsExpression, IdentifierName(otherVariableName), LiteralExpression(NullLiteralExpression)), (prev, next) => BinaryExpression(LogicalAndExpression, prev, next))))); }
private static ExpressionSyntax[] GenerateEqualsExpressions(RecordDescriptor descriptor, string otherVariableName) { // either // Prop == other.Prop // or // EqualityComparer<TProp>.Default.Equals(Prop, other.Prop) return(descriptor.Entries.Select(GenerateEqualityCheck).ToArray()); ExpressionSyntax GenerateEqualityCheck(RecordDescriptor.Entry property) { var typeQualifiedName = property.QualifiedTypeName.TrimEnd('?'); var thisMemberValueAccess = IdentifierName(property.Identifier.Text); var otherMemberValueAccess = MemberAccessExpression( SimpleMemberAccessExpression, IdentifierName(otherVariableName), thisMemberValueAccess); if (OperatorEqualityTypes.Contains(typeQualifiedName)) { return(BinaryExpression(EqualsExpression, thisMemberValueAccess, otherMemberValueAccess)); } return (InvocationExpression( MemberAccessExpression( SimpleMemberAccessExpression, GenerateEqualityComparerDefaultExpression(property.TypeSyntax), IdentifierName(EqualsMethodName))) .AddArgumentListArguments( Argument(thisMemberValueAccess), Argument(otherMemberValueAccess))); } }
private static BlockSyntax GenerateForwardToEquatableEquals(RecordDescriptor descriptor) { // return obj is MyRecord other && this.Equals(other); const string otherVariableName = "other"; return (Block( ReturnStatement( BinaryExpression( LogicalAndExpression, IsPatternExpression( IdentifierName(objVariableName), DeclarationPattern( descriptor.TypeSyntax, SingleVariableDesignation( Identifier(otherVariableName)))), InvocationExpression( MemberAccessExpression( SimpleMemberAccessExpression, ThisExpression(), IdentifierName(EqualsMethodName))) .AddArgumentListArguments( Argument( IdentifierName(otherVariableName))))))); }
private static ConstructorDeclarationSyntax GenerateConstructor(RecordDescriptor descriptor) { return (ConstructorDeclaration(descriptor.TypeIdentifier) .AddModifiers(SyntaxKind.PublicKeyword) .WithParameters( descriptor.Entries.Select(CreateParameter)) .WithBodyStatements( descriptor.Entries .Select(CreateCtorAssignment) .Append(OnConstructedInvocationStatement))); StatementSyntax CreateCtorAssignment(RecordDescriptor.Entry entry) { return (ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(entry.Identifier)), IdentifierName(entry.IdentifierInCamelCase)))); } }
private static MemberDeclarationSyntax GenerateDeconstruct(RecordDescriptor descriptor) { return (MethodDeclaration( PredefinedType(Token(SyntaxKind.VoidKeyword)), Names.Deconstruct) .AddModifiers(SyntaxKind.PublicKeyword) .WithParameters( descriptor.Entries.Select(CreateParameter)) .WithBodyStatements( descriptor.Entries.Select(CreateAssignment))); ParameterSyntax CreateParameter(RecordDescriptor.Entry entry) { return (Parameter(entry.IdentifierInCamelCase) .WithType(entry.TypeSyntax) .AddModifiers(Token(SyntaxKind.OutKeyword))); } StatementSyntax CreateAssignment(RecordDescriptor.Entry entry) { return (ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, IdentifierName(entry.IdentifierInCamelCase), MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName(entry.Identifier))))); } }
private static ClassDeclarationSyntax GenerateBuilder(RecordDescriptor descriptor) { return (ClassDeclaration(Names.Builder) .AddModifiers(SyntaxKind.PublicKeyword, SyntaxKind.PartialKeyword) .WithMembers(GenerateBuilderMembers())); SyntaxList <MemberDeclarationSyntax> GenerateBuilderMembers() { return(List <MemberDeclarationSyntax>() .AddRange(descriptor.Entries.SelectMany(GetPropertyMembers)) .Add(GetBuilderToImmutableMethod(descriptor))); } }
private static MemberDeclarationSyntax GenerateEquatableEquals(RecordDescriptor descriptor) { // public bool Equals(MyRecord other) { // // for struct // return PropA == other.PropA && ...; // // for class // return ReferenceEquals(this, other) || other != null && PropA == other.PropA && ...; // } const string otherVariableName = "other"; var propComparison = GenerateEqualsExpressions(descriptor, otherVariableName) .Aggregate((prev, next) => BinaryExpression(LogicalAndExpression, prev, next)); var returnExpr = descriptor.TypeDeclarationSyntaxKind == SyntaxKind.StructDeclaration ? propComparison : ReferenceTypeComparison(); return (MethodDeclaration( PredefinedType( Token(BoolKeyword)), EqualsMethodName) .AddModifiers( Token(PublicKeyword)) .AddParameterListParameters( Parameter( Identifier(otherVariableName)) .WithType(descriptor.TypeSyntax)) .AddBodyStatements( ReturnStatement(returnExpr))); ExpressionSyntax ReferenceTypeComparison() => BinaryExpression( LogicalOrExpression, InvocationExpression( IdentifierName(nameof(object.ReferenceEquals))) .AddArgumentListArguments( Argument( ThisExpression()), Argument( IdentifierName(otherVariableName))), BinaryExpression( LogicalAndExpression, BinaryExpression( NotEqualsExpression, IdentifierName(otherVariableName), LiteralExpression(NullLiteralExpression)), propComparison)); }
public static MemberDeclarationSyntax GenerateToString(RecordDescriptor descriptor) { var expression = descriptor.Entries.Length == 1 ? SinglePropertyToString() : MultiplePropertiesToString(); return (MethodDeclaration( PredefinedType(Token(SyntaxKind.StringKeyword)), Names.ToString) .AddModifiers(SyntaxKind.PublicKeyword, SyntaxKind.OverrideKeyword) .WithExpressionBody(expression)); ExpressionSyntax SinglePropertyToString() { var name = descriptor.Entries[0].Identifier; return (BinaryExpression( SyntaxKind.AddExpression, BinaryExpression( SyntaxKind.AddExpression, LiteralExpression( SyntaxKind.StringLiteralExpression, Literal("{ " + name + " = ")), IdentifierName(name)), LiteralExpression( SyntaxKind.StringLiteralExpression, Literal(" }")))); } ExpressionSyntax MultiplePropertiesToString() { var properties = from e in descriptor.Entries select AnonymousObjectMemberDeclarator(IdentifierName(e.Identifier)); return (InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, AnonymousObjectCreationExpression() .AddInitializers(properties.ToArray()), IdentifierName(Names.ToString)))); } }
private static BlockSyntax GenerateEqualsEqualsBody(RecordDescriptor descriptor) { // return System.Collections.Generic.EqualityComparer<MyRecord>.Default.Equals(left, right); return (Block( ReturnStatement( InvocationExpression( MemberAccessExpression( SimpleMemberAccessExpression, GenerateEqualityComparerDefaultExpression(descriptor.TypeSyntax), IdentifierName(EqualsMethodName))) .AddArgumentListArguments( Argument( IdentifierName(leftVariableName)), Argument( IdentifierName(rightVariableName)))))); }
private static BlockSyntax GenerateStandaloneObjectEquals(RecordDescriptor descriptor) { // return obj is MyRecord other && PropA == other.PropB && ...; const string otherVariableName = "other"; return (Block( ReturnStatement( GenerateEqualsExpressions(descriptor, otherVariableName) .Aggregate( (ExpressionSyntax)IsPatternExpression( IdentifierName(objVariableName), DeclarationPattern( descriptor.TypeSyntax, SingleVariableDesignation( Identifier(otherVariableName)))), (prev, next) => BinaryExpression(LogicalAndExpression, prev, next))))); }
private static OperatorDeclarationSyntax GenerateOperatorDeclarationBase(RecordDescriptor descriptor, SyntaxKind token) { // public static bool operator [token](MyRecord left, MyRecord right) return (OperatorDeclaration( PredefinedType( Token(BoolKeyword)), Token(token)) .AddModifiers( Token(PublicKeyword), Token(StaticKeyword)) .AddParameterListParameters( Parameter( Identifier(leftVariableName)) .WithType(descriptor.TypeSyntax), Parameter( Identifier(rightVariableName)) .WithType(descriptor.TypeSyntax))); }
private static MethodDeclarationSyntax GenerateUpdateMethod(RecordDescriptor descriptor) { var arguments = descriptor.Entries.Select(x => { return(Argument( IdentifierName(x.IdentifierInCamelCase))); }); return (MethodDeclaration(descriptor.TypeSyntax, Names.Update) .AddModifiers(SyntaxKind.PublicKeyword) .WithParameters( descriptor.Entries.Select(CreateParameter)) .WithBodyStatements( ReturnStatement( ObjectCreationExpression( descriptor.TypeSyntax) .WithArgumentList( ArgumentList( SeparatedList(arguments)))))); }
private static MethodDeclarationSyntax GetBuilderToImmutableMethod(RecordDescriptor descriptor) { return (MethodDeclaration( descriptor.TypeSyntax, Names.ToImmutable) .AddModifiers(SyntaxKind.PublicKeyword) .WithBody( Block( ReturnStatement( ObjectCreationExpression(descriptor.TypeSyntax) .WithArgumentList( CreateArgumentList()))))); ArgumentListSyntax CreateArgumentList() { return(ArgumentList( SeparatedList( descriptor.Entries.Select( entry => Argument(IdentifierName(entry.Identifier)))))); } }
private static IEnumerable <MemberDeclarationSyntax> GenerateMutators(RecordDescriptor descriptor) { return(descriptor.Entries.Select(CreateRecordMutator)); MethodDeclarationSyntax CreateRecordMutator(RecordDescriptor.Entry entry) { var valueIdentifier = Identifier(Names.Value); var arguments = descriptor.Entries.Select(x => { return(Argument( IdentifierName(x == entry ? valueIdentifier : x.Identifier))); }); var mutator = MethodDeclaration( descriptor.TypeSyntax, GetMutatorIdentifier()) .AddModifiers(SyntaxKind.PublicKeyword) .WithParameters( Parameter( valueIdentifier) .WithType(entry.TypeSyntax)) .WithBodyStatements( ReturnStatement( InvocationExpression( IdentifierName(Names.Update)) .WithArgumentList( ArgumentList( SeparatedList(arguments))))); return(mutator); SyntaxToken GetMutatorIdentifier() { return(Identifier($"{Names.WithPrefix}{entry.Identifier.ValueText}")); } } }
protected PartialGeneratorBase(RecordDescriptor descriptor, CancellationToken cancellationToken) { Descriptor = descriptor; CancellationToken = cancellationToken; }
private static MemberDeclarationSyntax GenerateGetHashCode(RecordDescriptor descriptor) { const string varKeyWord = "var"; const string hashCodeVariableName = "hashCode"; const int hashCodeInitialValue = 2085527896; const int hashCodeMultiplicationValue = 1521134295; var statement = descriptor.Entries.Length == 1 ? SinglePropertyGetHashCode() : MultiplePropertiesGetHashCode(); return (MethodDeclaration( PredefinedType( Token(IntKeyword)), Identifier(GetHashCodeMethodName)) .AddModifiers( PublicKeyword, OverrideKeyword) .AddBodyStatements(statement)); StatementSyntax SinglePropertyGetHashCode() { // public override int GetHashCode() { // return EqualityComparer<TProp>.Default.GetHashCode(Prop); // } return (ReturnStatement( HashCodeInvocation(descriptor.Entries[0]))); } StatementSyntax MultiplePropertiesGetHashCode() { // public override int GetHashCode() { // var hashCode = 2085527896; // hashCode = hashCode * -1521134295 + EqualityComparer<TProp>.Default.GetHashCode(Prop); // ... // return hashCode; // } return (CheckedStatement(UncheckedStatement) .AddBlockStatements( LocalDeclarationStatement( VariableDeclaration( IdentifierName(varKeyWord)) .AddVariables( VariableDeclarator( Identifier(hashCodeVariableName)) .WithInitializer( EqualsValueClause( LiteralExpression( NumericLiteralExpression, Literal(hashCodeInitialValue))))))) .AddBlockStatements( descriptor.Entries.Select(EntryHashCodeRecalculation) .ToArray()) .AddBlockStatements( ReturnStatement( IdentifierName(hashCodeVariableName)))); } StatementSyntax EntryHashCodeRecalculation(RecordDescriptor.Entry property) { // hashCode = hashCode * -1521134295 + EqualityComparer<TProp>.Default.GetHashCode(prop); return (ExpressionStatement( AssignmentExpression( SimpleAssignmentExpression, IdentifierName(hashCodeVariableName), BinaryExpression( AddExpression, BinaryExpression( MultiplyExpression, IdentifierName(hashCodeVariableName), PrefixUnaryExpression( UnaryMinusExpression, LiteralExpression( NumericLiteralExpression, Literal(hashCodeMultiplicationValue)))), HashCodeInvocation(property))))); } InvocationExpressionSyntax HashCodeInvocation(RecordDescriptor.Entry property) { // EqualityComparer<TProp>.Default.GetHashCode(prop); var defaultEqualityComparer = GenerateEqualityComparerDefaultExpression(property.TypeSyntax); return (InvocationExpression( MemberAccessExpression( SimpleMemberAccessExpression, defaultEqualityComparer, IdentifierName(GetHashCodeMethodName))) .AddArgumentListArguments( Argument( IdentifierName(property.Identifier.Text)))); } }
public PartialGenerationResult Generate(RecordDescriptor descriptor, Features features) => generator(descriptor, features);
protected DeconstructPartialGenerator(RecordDescriptor descriptor, CancellationToken cancellationToken) : base(descriptor, cancellationToken) { }