protected override void GenerateCore()
            {
                var valueParameterName = SyntaxFactory.IdentifierName("value");

                foreach (var field in this.generator.applyToMetaType.LocalFields)
                {
                    var withPropertyMethod = SyntaxFactory.MethodDeclaration(
                        GetFullyQualifiedSymbolName(this.generator.applyToSymbol),
                        WithPropertyMethodPrefix + field.Name.ToPascalCase())
                                             .WithAdditionalAnnotations()
                                             .AddModifiers(
                        SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                                             .AddAttributeLists(PureAttributeList)
                                             .AddParameterListParameters(
                        SyntaxFactory.Parameter(valueParameterName.Identifier)
                        .WithType(GetFullyQualifiedSymbolName(field.Type)))
                                             .WithBody(SyntaxFactory.Block(
                                                           SyntaxFactory.IfStatement(
                                                               SyntaxFactory.BinaryExpression(
                                                                   SyntaxKind.EqualsExpression,
                                                                   valueParameterName,
                                                                   SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), field.NameAsField)),
                                                               SyntaxFactory.Block(
                                                                   SyntaxFactory.ReturnStatement(SyntaxFactory.ThisExpression()))),
                                                           SyntaxFactory.ReturnStatement(
                                                               SyntaxFactory.InvocationExpression(
                                                                   SyntaxFactory.MemberAccessExpression(
                                                                       SyntaxKind.SimpleMemberAccessExpression,
                                                                       SyntaxFactory.ThisExpression(),
                                                                       WithMethodName),
                                                                   SyntaxFactory.ArgumentList(
                                                                       SyntaxFactory.SingletonSeparatedList(
                                                                           SyntaxFactory.Argument(
                                                                               SyntaxFactory.NameColon(field.Name),
                                                                               NoneToken,
                                                                               Syntax.OptionalFor(valueParameterName))))))));

                    this.innerMembers.Add(withPropertyMethod);
                }

                foreach (var field in this.generator.applyToMetaType.InheritedFields)
                {
                    string withMethodName     = WithPropertyMethodPrefix + field.Name.ToPascalCase();
                    var    withPropertyMethod = SyntaxFactory.MethodDeclaration(
                        GetFullyQualifiedSymbolName(this.generator.applyToSymbol),
                        withMethodName)
                                                .AddModifiers(
                        SyntaxFactory.Token(SyntaxKind.NewKeyword),
                        SyntaxFactory.Token(SyntaxKind.PublicKeyword))
                                                .AddAttributeLists(PureAttributeList)
                                                .AddParameterListParameters(
                        SyntaxFactory.Parameter(valueParameterName.Identifier)
                        .WithType(GetFullyQualifiedSymbolName(field.Type)))
                                                .WithBody(SyntaxFactory.Block(
                                                              SyntaxFactory.ReturnStatement(
                                                                  SyntaxFactory.CastExpression(
                                                                      GetFullyQualifiedSymbolName(this.generator.applyToSymbol),
                                                                      SyntaxFactory.InvocationExpression(
                                                                          SyntaxFactory.MemberAccessExpression(
                                                                              SyntaxKind.SimpleMemberAccessExpression,
                                                                              SyntaxFactory.BaseExpression(),
                                                                              SyntaxFactory.IdentifierName(withMethodName)),
                                                                          SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(valueParameterName))))))));

                    this.innerMembers.Add(withPropertyMethod);
                }
            }
Exemple #2
0
            public override ClassDeclarationSyntax ProcessApplyToClassDeclaration(ClassDeclarationSyntax applyTo)
            {
                applyTo = base.ProcessApplyToClassDeclaration(applyTo);

                if (this.applyTo.IsRecursiveParentOrDerivative)
                {
                    // Add the lookupTable parameter to the constructor's signature.
                    var origCtor    = GetMeaningfulConstructor(applyTo);
                    var alteredCtor = origCtor.AddParameterListParameters(SyntaxFactory.Parameter(LookupTableFieldName.Identifier).WithType(Syntax.OptionalOf(this.lookupTableType)));

                    // If this type isn't itself the recursive parent then we derive from it. And we must propagate the value to the chained base type.
                    if (!this.applyTo.IsRecursiveParent)
                    {
                        Assumes.NotNull(alteredCtor.Initializer); // we expect a chained call to the base constructor.
                        alteredCtor = alteredCtor.WithInitializer(
                            alteredCtor.Initializer.AddArgumentListArguments(
                                SyntaxFactory.Argument(SyntaxFactory.NameColon(LookupTableFieldName), NoneToken, LookupTableFieldName)));
                    }

                    // Apply the updated constructor back to the generated type.
                    applyTo = applyTo.ReplaceNode(origCtor, alteredCtor);

                    // Search for invocations of the constructor that we now have to update.
                    var creationInvocations = (
                        from n in applyTo.DescendantNodes()
                        let ctorInvocation = n as ObjectCreationExpressionSyntax
                                             let instantiatedTypeName = ctorInvocation?.Type?.ToString()
                                                                        where instantiatedTypeName == this.applyTo.TypeSyntax.ToString() || instantiatedTypeName == this.applyTo.TypeSymbol.Name
                                                                        select ctorInvocation).ToImmutableArray();
                    var chainedInvocations = (
                        from n in applyTo.DescendantNodes()
                        let chained = n as ConstructorInitializerSyntax
                                      where chained.IsKind(SyntaxKind.ThisConstructorInitializer) && chained.FirstAncestorOrSelf <ConstructorDeclarationSyntax>().Identifier.ValueText == this.applyTo.TypeSymbol.Name
                                      select chained).ToImmutableArray();
                    var invocations = creationInvocations.Concat <CSharpSyntaxNode>(chainedInvocations);
                    var trackedTree = applyTo.TrackNodes(invocations);

                    var recursiveField = this.applyTo.RecursiveParent.RecursiveField;
                    foreach (var ctorInvocation in invocations)
                    {
                        ExpressionSyntax lookupTableValue = SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression);

                        var currentInvocation         = trackedTree.GetCurrentNode(ctorInvocation);
                        var currentCreationInvocation = currentInvocation as ObjectCreationExpressionSyntax;
                        var currentChainedInvocation  = currentInvocation as ConstructorInitializerSyntax;

                        if (currentCreationInvocation != null)
                        {
                            var containingMethod = currentInvocation.FirstAncestorOrSelf <MethodDeclarationSyntax>();
                            if (containingMethod != null)
                            {
                                if (containingMethod.ParameterList.Parameters.Any(p => p.Identifier.ToString() == recursiveField.Name))
                                {
                                    // We're in a method that accepts the recursive field as a parameter.
                                    // The value we want to pass in for the lookup table is:
                                    // (children.IsDefined && children.Value != this.Children) ? default(Optional<ImmutableDictionary<uint, KeyValuePair<RecursiveType, uint>>>) : Optional.For(this.lookupTable);
                                    lookupTableValue = SyntaxFactory.ConditionalExpression(
                                        SyntaxFactory.ParenthesizedExpression(
                                            SyntaxFactory.BinaryExpression(
                                                SyntaxKind.LogicalAndExpression,
                                                Syntax.OptionalIsDefined(recursiveField.NameAsField),
                                                SyntaxFactory.BinaryExpression(
                                                    SyntaxKind.NotEqualsExpression,
                                                    Syntax.OptionalValue(recursiveField.NameAsField),
                                                    Syntax.ThisDot(recursiveField.NameAsProperty)))),
                                        SyntaxFactory.DefaultExpression(Syntax.OptionalOf(this.lookupTableType)),
                                        Syntax.OptionalFor(Syntax.ThisDot(LookupTableFieldName)));
                                }
                            }

                            var alteredInvocation = currentCreationInvocation.AddArgumentListArguments(
                                SyntaxFactory.Argument(SyntaxFactory.NameColon(LookupTableFieldName), NoneToken, lookupTableValue));
                            trackedTree = trackedTree.ReplaceNode(currentInvocation, alteredInvocation);
                        }
                        else
                        {
                            var alteredInvocation = currentChainedInvocation.AddArgumentListArguments(
                                SyntaxFactory.Argument(SyntaxFactory.NameColon(LookupTableFieldName), NoneToken, lookupTableValue));
                            trackedTree = trackedTree.ReplaceNode(currentInvocation, alteredInvocation);
                        }
                    }

                    applyTo = trackedTree;
                }

                return(applyTo);
            }
            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);
            }