예제 #1
0
            private void ImplementSortedChildrenInterface()
            {
                this.baseTypes.Add(SyntaxFactory.SimpleBaseType(Syntax.GetTypeSyntax(typeof(IRecursiveParentWithSortedChildren))));

                // int IRecursiveParentWithSortedChildren.Compare(IRecursiveType first, IRecursiveType second)
                var firstParameterName  = SyntaxFactory.IdentifierName("first");
                var secondParameterName = SyntaxFactory.IdentifierName("second");

                this.innerMembers.Add(SyntaxFactory.MethodDeclaration(
                                          SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)),
                                          nameof(IRecursiveParentWithSortedChildren.Compare))
                                      .WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(SyntaxFactory.IdentifierName(nameof(IRecursiveParentWithSortedChildren))))
                                      .AddParameterListParameters(
                                          SyntaxFactory.Parameter(firstParameterName.Identifier).WithType(Syntax.GetTypeSyntax(typeof(IRecursiveType))),
                                          SyntaxFactory.Parameter(secondParameterName.Identifier).WithType(Syntax.GetTypeSyntax(typeof(IRecursiveType))))
                                      .WithBody(SyntaxFactory.Block(
                                                    // return this.Children.KeyComparer.Compare((<#= templateType.RecursiveType.TypeName #>)first, (<#= templateType.RecursiveType.TypeName #>)second);
                                                    SyntaxFactory.ReturnStatement(
                                                        SyntaxFactory.InvocationExpression(
                                                            SyntaxFactory.MemberAccessExpression(
                                                                SyntaxKind.SimpleMemberAccessExpression,
                                                                SyntaxFactory.MemberAccessExpression(
                                                                    SyntaxKind.SimpleMemberAccessExpression,
                                                                    Syntax.ThisDot(SyntaxFactory.IdentifierName(this.generator.applyToMetaType.RecursiveField.Name.ToPascalCase())),
                                                                    SyntaxFactory.IdentifierName(nameof(ImmutableSortedSet <int> .KeyComparer))),
                                                                SyntaxFactory.IdentifierName(nameof(IComparer <int> .Compare))),
                                                            SyntaxFactory.ArgumentList(Syntax.JoinSyntaxNodes(SyntaxKind.CommaToken,
                                                                                                              SyntaxFactory.Argument(SyntaxFactory.CastExpression(this.generator.applyToMetaType.RecursiveType.TypeSyntax, firstParameterName)),
                                                                                                              SyntaxFactory.Argument(SyntaxFactory.CastExpression(this.generator.applyToMetaType.RecursiveType.TypeSyntax, secondParameterName)))))))));
            }
예제 #2
0
            private void ImplementRecursiveParentInterface()
            {
                var irecursiveParentOfT = CreateIRecursiveParentOfTSyntax(GetFullyQualifiedSymbolName(this.generator.applyToMetaType.RecursiveType.TypeSymbol));

                this.baseTypes.Add(SyntaxFactory.SimpleBaseType(irecursiveParentOfT));

                // this.Children;
                var thisDotChildren = Syntax.ThisDot(SyntaxFactory.IdentifierName(this.generator.applyToMetaType.RecursiveField.Name.ToPascalCase()));

                // System.Collections.Generic.IReadOnlyCollection<IRecursiveType> IRecursiveParent.Children
                this.innerMembers.Add(
                    SyntaxFactory.PropertyDeclaration(
                        Syntax.GetTypeSyntax(typeof(IReadOnlyCollection <IRecursiveType>)),
                        nameof(IRecursiveParent.Children))
                    .WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(Syntax.GetTypeSyntax(typeof(IRecursiveParent))))
                    .WithExpressionBody(SyntaxFactory.ArrowExpressionClause(thisDotChildren))
                    .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
                    .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(DebuggerBrowsableNeverAttribute))));

                // public ParentedRecursiveType<TRecursiveParent, TRecursiveType> GetParentedNode(uint identity)
                this.innerMembers.Add(
                    SyntaxFactory.MethodDeclaration(
                        SyntaxFactory.GenericName(nameof(ParentedRecursiveType <IRecursiveParent <IRecursiveType>, IRecursiveType>)).AddTypeArgumentListArguments(
                            this.applyTo.RecursiveParent.TypeSyntax,
                            this.applyTo.RecursiveType.TypeSyntax),
                        nameof(IRecursiveParent.GetParentedNode))
                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                    //.WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(Syntax.GetTypeSyntax(typeof(IRecursiveParent))))
                    .AddParameterListParameters(RequiredIdentityParameter)
                    .WithBody(SyntaxFactory.Block(
                                  // return this.GetParentedNode<TRecursiveParent, TRecursiveType>(identity);
                                  SyntaxFactory.ReturnStatement(
                                      SyntaxFactory.InvocationExpression(
                                          Syntax.ThisDot(SyntaxFactory.GenericName(nameof(RecursiveTypeExtensions.GetParentedNode)).AddTypeArgumentListArguments(
                                                             this.applyTo.RecursiveParent.TypeSyntax,
                                                             this.applyTo.RecursiveType.TypeSyntax)))
                                      .AddArgumentListArguments(SyntaxFactory.Argument(IdentityParameterName))))));

                // ParentedRecursiveType<IRecursiveParent<IRecursiveType>, IRecursiveType> IRecursiveParent.GetParentedNode(<#= templateType.RequiredIdentityField.TypeName #> identity) {
                var parentedVar = SyntaxFactory.IdentifierName("parented");
                var returnType  = Syntax.GetTypeSyntax(typeof(ParentedRecursiveTypeNonGeneric));

                this.innerMembers.Add(
                    SyntaxFactory.MethodDeclaration(
                        returnType,
                        nameof(IRecursiveParent.GetParentedNode))
                    .WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(Syntax.GetTypeSyntax(typeof(IRecursiveParent))))
                    .AddParameterListParameters(RequiredIdentityParameter)
                    .WithBody(SyntaxFactory.Block(
                                  // var parented = this.GetParentedNode(identity);
                                  SyntaxFactory.LocalDeclarationStatement(SyntaxFactory.VariableDeclaration(varType).AddVariables(
                                                                              SyntaxFactory.VariableDeclarator(parentedVar.Identifier).WithInitializer(SyntaxFactory.EqualsValueClause(
                                                                                                                                                           SyntaxFactory.InvocationExpression(Syntax.ThisDot(SyntaxFactory.IdentifierName(nameof(RecursiveTypeExtensions.GetParentedNode))))
                                                                                                                                                           .AddArgumentListArguments(SyntaxFactory.Argument(IdentityParameterName)))))),
                                  // return new ParentedRecursiveType<IRecursiveParent<IRecursiveType>, IRecursiveType>(parented.Value, parented.Parent);
                                  SyntaxFactory.ReturnStatement(SyntaxFactory.ObjectCreationExpression(returnType).AddArgumentListArguments(
                                                                    SyntaxFactory.Argument(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, parentedVar, SyntaxFactory.IdentifierName(nameof(ParentedRecursiveTypeNonGeneric.Value)))),
                                                                    SyntaxFactory.Argument(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, parentedVar, SyntaxFactory.IdentifierName(nameof(ParentedRecursiveTypeNonGeneric.Parent)))))))));

                ////System.Collections.Generic.IReadOnlyCollection<<#= templateType.RecursiveType.TypeName #>> IRecursiveParent<<#= templateType.RecursiveType.TypeName #>>.Children
                ////	=> return this.Children;
                this.innerMembers.Add(
                    SyntaxFactory.PropertyDeclaration(
                        Syntax.IReadOnlyCollectionOf(this.generator.applyToMetaType.RecursiveType.TypeSyntax),
                        nameof(IRecursiveParent <IRecursiveType> .Children))
                    .WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(irecursiveParentOfT))
                    .WithExpressionBody(SyntaxFactory.ArrowExpressionClause(thisDotChildren))
                    .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
                    .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(DebuggerBrowsableNeverAttribute))));
            }
예제 #3
0
            private void ImplementOrderedChildrenInterface()
            {
                // We only need to declare this interface if the children are not sorted,
                // since sorted children merit a derived interface making this redundant.
                if (!this.generator.applyToMetaType.ChildrenAreSorted)
                {
                    this.baseTypes.Add(SyntaxFactory.SimpleBaseType(Syntax.GetTypeSyntax(typeof(IRecursiveParentWithOrderedChildren))));
                }

                // IReadOnlyList<IRecursiveType> IRecursiveParentWithOrderedChildren.Children => this.children;
                this.innerMembers.Add(SyntaxFactory.PropertyDeclaration(
                                          Syntax.IReadOnlyListOf(Syntax.GetTypeSyntax(typeof(IRecursiveType))),
                                          nameof(IRecursiveParentWithOrderedChildren.Children))
                                      .WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(SyntaxFactory.IdentifierName(nameof(IRecursiveParentWithOrderedChildren))))
                                      .WithExpressionBody(SyntaxFactory.ArrowExpressionClause(Syntax.ThisDot(SyntaxFactory.IdentifierName(this.generator.applyToMetaType.RecursiveField.Name))))
                                      .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
                                      .AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(DebuggerBrowsableNeverAttribute))));

                // int IRecursiveParentWithOrderedChildren.IndexOf(IRecursiveType value)
                var valueParameterName = SyntaxFactory.IdentifierName("value");

                this.innerMembers.Add(SyntaxFactory.MethodDeclaration(
                                          SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)),
                                          nameof(IRecursiveParentWithOrderedChildren.IndexOf))
                                      .WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(SyntaxFactory.IdentifierName(nameof(IRecursiveParentWithOrderedChildren))))
                                      .AddParameterListParameters(SyntaxFactory.Parameter(valueParameterName.Identifier).WithType(Syntax.GetTypeSyntax(typeof(IRecursiveType))))
                                      .WithBody(SyntaxFactory.Block(
                                                    // return this.Children.IndexOf((<#= templateType.RecursiveType.TypeName #>)value);
                                                    SyntaxFactory.ReturnStatement(
                                                        SyntaxFactory.InvocationExpression(
                                                            SyntaxFactory.MemberAccessExpression(
                                                                SyntaxKind.SimpleMemberAccessExpression,
                                                                Syntax.ThisDot(SyntaxFactory.IdentifierName(this.generator.applyToMetaType.RecursiveField.Name.ToPascalCase())),
                                                                SyntaxFactory.IdentifierName(nameof(IList <int> .IndexOf))),
                                                            SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(
                                                                                           SyntaxFactory.Argument(
                                                                                               SyntaxFactory.CastExpression(
                                                                                                   this.generator.applyToMetaType.RecursiveType.TypeSyntax,
                                                                                                   valueParameterName)))))))));
            }
            protected MethodDeclarationSyntax CreateToImmutableMethod()
            {
                // var fieldName = this.fieldName.IsDefined ? this.fieldName.Value?.ToImmutable() : this.immutable.FieldName;
                var body = SyntaxFactory.Block(
                    from field in this.generator.applyToMetaType.AllFields
                    where field.IsGeneratedImmutableType
                    let thisField = Syntax.ThisDot(field.NameAsField)                                                                                                                                                               // this.fieldName
                                    let thisFieldValue = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, thisField, SyntaxFactory.IdentifierName(nameof(ImmutableObjectGraph.Optional <int> .Value))) // this.fieldName.Value
                                                         select SyntaxFactory.LocalDeclarationStatement(
                        SyntaxFactory.VariableDeclaration(varType))
                                                         .AddDeclarationVariables(
                        SyntaxFactory.VariableDeclarator(field.Name).WithInitializer(
                            SyntaxFactory.EqualsValueClause(
                                SyntaxFactory.ConditionalExpression(
                                    Syntax.OptionalIsDefined(thisField),    // this.fieldName.IsDefined
                                    SyntaxFactory.InvocationExpression(     // this.fieldName.Value?.ToImmutable()
                                        SyntaxFactory.ConditionalAccessExpression(thisFieldValue, SyntaxFactory.MemberBindingExpression(ToImmutableMethodName)),
                                        SyntaxFactory.ArgumentList()),
                                    SyntaxFactory.MemberAccessExpression(     // this.immutable.FieldName
                                        SyntaxKind.SimpleMemberAccessExpression,
                                        Syntax.ThisDot(ImmutableFieldName),
                                        field.NameAsProperty))))));

                ExpressionSyntax returnExpression;

                if (this.generator.applyToMetaType.AllFields.Any())
                {
                    // this.immutable = this.immutable.With(...)
                    returnExpression = SyntaxFactory.AssignmentExpression(
                        SyntaxKind.SimpleAssignmentExpression,
                        Syntax.ThisDot(ImmutableFieldName),
                        SyntaxFactory.InvocationExpression(
                            SyntaxFactory.MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                Syntax.ThisDot(ImmutableFieldName),
                                WithMethodName),
                            SyntaxFactory.ArgumentList(
                                Syntax.JoinSyntaxNodes(
                                    SyntaxKind.CommaToken,
                                    this.generator.applyToMetaType.AllFields.Select(
                                        f => SyntaxFactory.Argument(Syntax.OptionalFor(f.IsGeneratedImmutableType ? SyntaxFactory.IdentifierName(f.Name) : Syntax.ThisDot(SyntaxFactory.IdentifierName(f.Name.ToPascalCase())))))))));
                }
                else
                {
                    // this.immutable
                    returnExpression = Syntax.ThisDot(ImmutableFieldName);
                }

                body = body.AddStatements(
                    SyntaxFactory.ReturnStatement(returnExpression));

                // public TemplateType ToImmutable() { ... }
                var method = SyntaxFactory.MethodDeclaration(
                    SyntaxFactory.IdentifierName(this.generator.applyTo.Identifier),
                    ToImmutableMethodName.Identifier)
                             .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                             .AddAttributeLists(PureAttributeList)
                             .WithBody(body);

                if (this.generator.applyToMetaType.HasAncestor)
                {
                    method = Syntax.AddNewKeyword(method);
                }

                return(method);
            }
            protected IReadOnlyList <MemberDeclarationSyntax> CreateMutableProperties()
            {
                var properties = new List <PropertyDeclarationSyntax>();

                foreach (var field in this.generator.applyToMetaType.LocalFields)
                {
                    var thisField = Syntax.ThisDot(field.NameAsField);
                    var optionalFieldNotYetDefined = SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, Syntax.OptionalIsDefined(thisField));
                    var getterBlock = field.IsGeneratedImmutableType
                        ? SyntaxFactory.Block(
                        // if (!this.fieldName.IsDefined) {
                        SyntaxFactory.IfStatement(
                            optionalFieldNotYetDefined,
                            SyntaxFactory.Block(
                                // this.fieldName = this.immutable.fieldName?.ToBuilder();
                                SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
                                                                      SyntaxKind.SimpleAssignmentExpression,
                                                                      thisField,
                                                                      SyntaxFactory.ConditionalAccessExpression(
                                                                          SyntaxFactory.MemberAccessExpression(
                                                                              SyntaxKind.SimpleMemberAccessExpression,
                                                                              Syntax.ThisDot(ImmutableFieldName),
                                                                              field.NameAsField),
                                                                          SyntaxFactory.InvocationExpression(
                                                                              SyntaxFactory.MemberBindingExpression(ToBuilderMethodName),
                                                                              SyntaxFactory.ArgumentList())))))),
                        SyntaxFactory.ReturnStatement(Syntax.OptionalValue(thisField)))
                        : SyntaxFactory.Block(SyntaxFactory.ReturnStatement(thisField));
                    var setterValueArg  = SyntaxFactory.IdentifierName("value");
                    var setterCondition = field.IsGeneratedImmutableType ?
                                          SyntaxFactory.BinaryExpression(
                        SyntaxKind.LogicalOrExpression,
                        optionalFieldNotYetDefined,
                        SyntaxFactory.BinaryExpression(
                            SyntaxKind.NotEqualsExpression,
                            Syntax.OptionalValue(thisField),
                            setterValueArg)) :
                                          HasEqualityOperators(field.Symbol.Type) ?
                                          SyntaxFactory.BinaryExpression(
                        SyntaxKind.NotEqualsExpression,
                        thisField,
                        setterValueArg) :
                                          null;
                    var setterSignificantBlock = SyntaxFactory.Block(
                        SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
                                                              SyntaxKind.SimpleAssignmentExpression,
                                                              thisField,
                                                              setterValueArg)),
                        SyntaxFactory.ExpressionStatement(
                            SyntaxFactory.InvocationExpression(
                                SyntaxFactory.MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    SyntaxFactory.ThisExpression(),
                                    OnPropertyChangedMethodName))));
                    var setterBlock = setterCondition != null?
                                      SyntaxFactory.Block(
                        SyntaxFactory.IfStatement(
                            setterCondition,
                            setterSignificantBlock)) :
                                          setterSignificantBlock;

                    var property = SyntaxFactory.PropertyDeclaration(
                        this.GetPropertyTypeForBuilder(field),
                        field.Name.ToPascalCase())
                                   .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                                   .WithAccessorList(SyntaxFactory.AccessorList(SyntaxFactory.List(new AccessorDeclarationSyntax[]
                    {
                        SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, getterBlock),
                        SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, setterBlock),
                    })));
                    properties.Add(property);
                }

                return(properties);
            }
            protected MemberDeclarationSyntax CreateConstructor()
            {
                var immutableParameterName = SyntaxFactory.IdentifierName("immutable");
                var body = SyntaxFactory.Block(
                    SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
                                                          SyntaxKind.SimpleAssignmentExpression,
                                                          Syntax.ThisDot(ImmutableFieldName),
                                                          immutableParameterName)));

                foreach (var field in this.generator.applyToMetaType.LocalFields)
                {
                    if (!field.IsGeneratedImmutableType)
                    {
                        body = body.AddStatements(SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
                                                                                        SyntaxKind.SimpleAssignmentExpression,
                                                                                        Syntax.ThisDot(field.NameAsField),
                                                                                        SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, immutableParameterName, field.NameAsField))));
                    }
                }

                var ctor = SyntaxFactory.ConstructorDeclaration(BuilderTypeName.Identifier)
                           .AddParameterListParameters(
                    SyntaxFactory.Parameter(immutableParameterName.Identifier).WithType(SyntaxFactory.IdentifierName(this.generator.applyTo.Identifier)))
                           .AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
                           .WithBody(body);

                if (this.generator.applyToMetaType.HasAncestor)
                {
                    ctor = ctor.WithInitializer(SyntaxFactory.ConstructorInitializer(
                                                    SyntaxKind.BaseConstructorInitializer,
                                                    SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(immutableParameterName)))));
                }

                return(ctor);
            }
            private MemberDeclarationSyntax CreateCreateWithIdentityMethod()
            {
                ExpressionSyntax returnExpression = DefaultInstanceFieldName;

                if (this.generator.applyToMetaType.LocalFields.Any())
                {
                    returnExpression = SyntaxFactory.InvocationExpression(
                        SyntaxFactory.MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            returnExpression,
                            WithFactoryMethodName),
                        this.generator.CreateArgumentList(this.generator.applyToMetaType.AllFields, ArgSource.OptionalArgumentOrTemplate, OptionalStyle.Always)
                        .AddArguments(OptionalIdentityArgument));
                }

                var method = SyntaxFactory.MethodDeclaration(
                    this.generator.applyToTypeName,
                    CreateWithIdentityMethodName.Identifier)
                             .AddModifiers(
                    SyntaxFactory.Token(SyntaxKind.InternalKeyword),
                    SyntaxFactory.Token(SyntaxKind.StaticKeyword))
                             .WithParameterList(
                    this.generator.CreateParameterList(
                        this.generator.applyToMetaType.AllFields,
                        ParameterStyle.OptionalOrRequired)
                    .AddParameters(OptionalIdentityParameter))
                             .WithBody(SyntaxFactory.Block(
                                           SyntaxFactory.IfStatement(
                                               SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, Syntax.OptionalIsDefined(IdentityParameterName)),
                                               SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
                                                                                     SyntaxKind.SimpleAssignmentExpression,
                                                                                     IdentityParameterName,
                                                                                     SyntaxFactory.InvocationExpression(NewIdentityMethodName, SyntaxFactory.ArgumentList())))),
                                           SyntaxFactory.ReturnStatement(returnExpression)));

                // BUG: the condition should be if there are local fields on *any* ancestor
                // from the closest non-abstract ancestor (exclusive) to this type (inclusive).
                if (!this.generator.applyToMetaType.LocalFields.Any() && this.generator.applyToMetaType.Ancestors.Any(a => !a.TypeSymbol.IsAbstract))
                {
                    method = Syntax.AddNewKeyword(method);
                }

                return(method);
            }
            private MemberDeclarationSyntax CreateToDerivedTypeMethod(MetaType derivedType)
            {
                var derivedTypeName = GetFullyQualifiedSymbolName(derivedType.TypeSymbol);
                var thatLocal       = SyntaxFactory.IdentifierName("that");
                var body            = new List <StatementSyntax>();

                // var that = this as DerivedType;
                body.Add(SyntaxFactory.LocalDeclarationStatement(
                             SyntaxFactory.VariableDeclaration(
                                 varType,
                                 SyntaxFactory.SingletonSeparatedList(
                                     SyntaxFactory.VariableDeclarator(thatLocal.Identifier)
                                     .WithInitializer(SyntaxFactory.EqualsValueClause(
                                                          SyntaxFactory.BinaryExpression(
                                                              SyntaxKind.AsExpression,
                                                              SyntaxFactory.ThisExpression(),
                                                              derivedTypeName)))))));

                // this.GetType()
                var thisDotGetType = SyntaxFactory.InvocationExpression(
                    SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName("GetType")),
                    SyntaxFactory.ArgumentList());

                // {0}.Equals(typeof(derivedType))
                var thisTypeIsEquivalentToDerivedType =
                    SyntaxFactory.InvocationExpression(
                        SyntaxFactory.MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            thisDotGetType,
                            SyntaxFactory.IdentifierName(nameof(Type.Equals))),
                        SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(
                                                                                            SyntaxFactory.TypeOfExpression(derivedTypeName)))));

                var ifEquivalentTypeBlock = new List <StatementSyntax>();
                var fieldsBeyond          = derivedType.GetFieldsBeyond(this.generator.applyToMetaType);

                if (fieldsBeyond.Any())
                {
                    Func <MetaField, ExpressionSyntax> isUnchanged = v =>
                                                                     SyntaxFactory.ParenthesizedExpression(
                        v.IsRequired
                                ? // ({0} == that.{1})
                        SyntaxFactory.BinaryExpression(
                            SyntaxKind.EqualsExpression,
                            v.NameAsField,
                            SyntaxFactory.MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                thatLocal,
                                v.NameAsProperty))
                                : // (!{0}.IsDefined || {0}.Value == that.{1})
                        SyntaxFactory.BinaryExpression(
                            SyntaxKind.LogicalOrExpression,
                            SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, Syntax.OptionalIsDefined(v.NameAsField)),
                            SyntaxFactory.BinaryExpression(
                                SyntaxKind.EqualsExpression,
                                Syntax.OptionalValue(v.NameAsField),
                                SyntaxFactory.MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    thatLocal,
                                    v.NameAsProperty))));
                    var noChangesExpression = fieldsBeyond.Select(isUnchanged).ChainBinaryExpressions(SyntaxKind.LogicalAndExpression);

                    ifEquivalentTypeBlock.Add(SyntaxFactory.IfStatement(
                                                  noChangesExpression,
                                                  SyntaxFactory.ReturnStatement(thatLocal)));
                }
                else
                {
                    ifEquivalentTypeBlock.Add(SyntaxFactory.ReturnStatement(thatLocal));
                }

                // if (that != null && this.GetType().IsEquivalentTo(typeof(derivedType))) { ... }
                body.Add(SyntaxFactory.IfStatement(
                             SyntaxFactory.BinaryExpression(
                                 SyntaxKind.LogicalAndExpression,
                                 SyntaxFactory.BinaryExpression(SyntaxKind.NotEqualsExpression, thatLocal, SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)),
                                 thisTypeIsEquivalentToDerivedType),
                             SyntaxFactory.Block(ifEquivalentTypeBlock)));

                // return DerivedType.CreateWithIdentity(...)
                body.Add(SyntaxFactory.ReturnStatement(
                             SyntaxFactory.InvocationExpression(
                                 SyntaxFactory.MemberAccessExpression(
                                     SyntaxKind.SimpleMemberAccessExpression,
                                     derivedTypeName,
                                     CreateWithIdentityMethodName),
                                 this.generator.CreateArgumentList(this.generator.applyToMetaType.AllFields, asOptional: OptionalStyle.WhenNotRequired)
                                 .AddArguments(RequiredIdentityArgumentFromProperty)
                                 .AddArguments(this.generator.CreateArgumentList(fieldsBeyond, ArgSource.Argument).Arguments.ToArray()))));

                return(SyntaxFactory.MethodDeclaration(
                           derivedTypeName,
                           GetToTypeMethodName(derivedType.TypeSymbol.Name).Identifier)
                       .AddModifiers(
                           SyntaxFactory.Token(SyntaxKind.PublicKeyword),
                           SyntaxFactory.Token(SyntaxKind.VirtualKeyword))
                       .WithParameterList(this.generator.CreateParameterList(fieldsBeyond, ParameterStyle.OptionalOrRequired))
                       .WithBody(SyntaxFactory.Block(body)));
            }