-
Notifications
You must be signed in to change notification settings - Fork 2
/
AutoPropertyRewriter.cs
99 lines (82 loc) · 3.77 KB
/
AutoPropertyRewriter.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
93
94
95
96
97
98
99
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
namespace SyntaxRewriter;
class AutoPropertyRewriter(SemanticModel semanticModel) : CSharpSyntaxRewriter
{
private readonly SemanticModel _semanticModel = semanticModel;
private readonly List<string> _fieldsToRemove = new();
public IEnumerable<string> FieldsToRemove => _fieldsToRemove;
public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
{
return base.VisitClassDeclaration(node);
}
public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node)
{
return base.VisitFieldDeclaration(node);
}
public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node)
{
if (HasBothAccessors(node))
{
AccessorDeclarationSyntax? accessorDeclaration = node.AccessorList?.Accessors.Single(
ad => ad.Kind() == SyntaxKind.GetAccessorDeclaration);
if (accessorDeclaration is null)
return null;
IFieldSymbol? backingField = GetBackingFieldFromGetter(accessorDeclaration);
if (backingField is null)
return null;
SyntaxNode? fieldDeclaration = backingField.DeclaringSyntaxReferences
.First()
.GetSyntax()
.Ancestors()
.Where(a => a is FieldDeclarationSyntax)
.FirstOrDefault();
if (fieldDeclaration is FieldDeclarationSyntax fieldDeclarationSyntax)
{
string fieldToRemove = fieldDeclarationSyntax.GetText().ToString();
_fieldsToRemove.Add(fieldToRemove);
}
PropertyDeclarationSyntax property = ConvertToAutoProperty(node).WithAdditionalAnnotations(Formatter.Annotation);
return property;
}
return node;
}
private static bool HasBothAccessors(BasePropertyDeclarationSyntax property)
{
var accessors = property.AccessorList?.Accessors;
if (accessors is null)
return false;
var getter = accessors?.FirstOrDefault(
ad => ad.Kind() == SyntaxKind.GetAccessorDeclaration);
var setter = accessors?.FirstOrDefault(
ad => ad.Kind() == SyntaxKind.SetAccessorDeclaration);
return getter?.Body?.Statements.Count == 1 && setter?.Body?.Statements.Count == 1;
}
public override SyntaxNode? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
{
return base.VisitNamespaceDeclaration(node);
}
private static PropertyDeclarationSyntax ConvertToAutoProperty(PropertyDeclarationSyntax propertyDeclaration)
{
var newProperty = propertyDeclaration
.WithAccessorList(
SyntaxFactory.AccessorList(
SyntaxFactory.List(new[]
{
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)).WithAdditionalAnnotations(Formatter.Annotation),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
})));
return newProperty;
}
private IFieldSymbol? GetBackingFieldFromGetter(AccessorDeclarationSyntax getter)
{
if (getter.Body?.Statements.Count != 1)
return null;
var statement = getter.Body.Statements.Single() as ReturnStatementSyntax;
if (statement?.Expression == null)
return null;
return _semanticModel.GetSymbolInfo(statement.Expression).Symbol as IFieldSymbol;
}
}