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); } }
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); }