-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathImmutable.cs
92 lines (75 loc) · 3.9 KB
/
Immutable.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using F = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using K = Microsoft.CodeAnalysis.CSharp.SyntaxKind;
namespace Cometary.Tests
{
/// <summary>
/// <see cref="Component"/> that adds With*() members to a class.
/// </summary>
public sealed class Immutable : Component
{
/// <inheritdoc />
public override ClassDeclarationSyntax Apply(ClassDeclarationSyntax node, INamedTypeSymbol symbol, CancellationToken cancellationToken)
{
var parameters = ImmutableArray.CreateBuilder<ParameterSyntax>();
var arguments = ImmutableArray.CreateBuilder<ArgumentSyntax>();
var ctorStmts = ImmutableArray.CreateBuilder<StatementSyntax>();
// Generate all parameters and statements
var members = symbol.GetMembers();
for (int i = 0; i < members.Length; i++)
{
IPropertySymbol member = members[i] as IPropertySymbol;
if (member == null || !member.IsReadOnly || !member.CanBeReferencedByName)
continue;
// Read-only prop, we good
string propName = member.Name;
string paramName = $"{char.ToLower(propName[0]).ToString()}{propName.Substring(1)}";
TypeSyntax paramType = ((PropertyDeclarationSyntax)member.DeclaringSyntaxReferences[0]
.GetSyntax(cancellationToken)).Type;
MemberAccessExpressionSyntax propAccess = F.MemberAccessExpression(
K.SimpleMemberAccessExpression,
F.ThisExpression(),
F.IdentifierName(propName)
);
// Make parameter & argument
parameters.Add(F.Parameter(F.Identifier(paramName)).WithType(paramType));
arguments.Add(F.Argument(propAccess));
// Make ctor stmt
ctorStmts.Add(F.ExpressionStatement(
F.AssignmentExpression(K.SimpleAssignmentExpression,
propAccess,
F.IdentifierName(paramName)
)
));
}
// The ctor is full, make all the 'with' methods
TypeSyntax returnType = F.IdentifierName(symbol.Name);
MemberDeclarationSyntax[] additionalMethods = new MemberDeclarationSyntax[parameters.Count + 1];
arguments.Capacity = arguments.Count;
ImmutableArray<ArgumentSyntax> args = arguments.MoveToImmutable();
for (int i = 0; i < parameters.Count; i++)
{
ParameterSyntax parameter = parameters[i];
string parameterName = parameter.Identifier.Text;
ArgumentSyntax name = F.Argument(F.IdentifierName(parameterName));
SeparatedSyntaxList<ArgumentSyntax> allArguments = F.SeparatedList(args.Replace(args[i], name));
StatementSyntax returnStmt = F.ReturnStatement(
F.ObjectCreationExpression(returnType).WithArgumentList(F.ArgumentList(allArguments))
);
additionalMethods[i] = F.MethodDeclaration(returnType, $"With{char.ToUpper(parameterName[0]).ToString()}{parameterName.Substring(1)}")
.AddModifiers(F.Token(K.PublicKeyword), F.Token(K.NewKeyword))
.AddParameterListParameters(parameter)
.AddBodyStatements(returnStmt);
}
// Add the private ctor
additionalMethods[parameters.Count] = F.ConstructorDeclaration(symbol.Name)
.AddModifiers(F.Token(K.PrivateKeyword))
.AddParameterListParameters(parameters.ToArray())
.AddBodyStatements(ctorStmts.ToArray());
return node.AddMembers(additionalMethods);
}
}
}