Esempio n. 1
0
        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)))));
        }
Esempio n. 2
0
        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)));
            }
        }
Esempio n. 3
0
        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))));
            }
        }
Esempio n. 5
0
        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)))));
            }
        }
Esempio n. 6
0
        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))));
            }
        }
Esempio n. 9
0
 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))))));
 }
Esempio n. 10
0
        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)))));
        }
Esempio n. 11
0
 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))))));
        }
Esempio n. 13
0
        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;
 }
Esempio n. 16
0
        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))));
            }
        }
Esempio n. 17
0
 public PartialGenerationResult Generate(RecordDescriptor descriptor, Features features) =>
 generator(descriptor, features);
 protected DeconstructPartialGenerator(RecordDescriptor descriptor, CancellationToken cancellationToken) : base(descriptor, cancellationToken)
 {
 }