Reputation: 4896
I'm in the middle of writing a code analyzer with code fix using .NET Compiler Platform (Roslyn). I need to translate all (non sealed) public fields into a separate auto properties, because I know that a FieldDeclarationSyntax can have multiple field declaration in one line, based on the code sample in the previous Stackoverflow question below.
This fact of multiple field declaration has been discussed here: How to get a Roslyn FieldSymbol from a FieldDeclarationSyntax node?
The sample code I want to analyze is this:
public int AnyNumericField = 0;
public string AnyStringField = "";
// multiple field declaration sample
public int field1, field2, field3 = 0;
public StringBuilder sb1 = new StringBuilder();
I want to have code fixer into this:
public int AnyNumericField { get; set; } = 0;
public string AnyStringField { get; set; } = "";
// multiple field declaration sample
public int field1 { get; set; } = 0;
public int field2 { get; set; } = 0;
public int field3 { get; set; } = 0;
public StringBuilder sb1 { get; set; } = new StringBuilder();
My current analyzer code is this:
private void AnalyzePublicField(SymbolAnalysisContext context)
{
var aPublicField = (IFieldSymbol)context.Symbol;
if ((aPublicField.DeclaredAccessibility == Accessibility.Public) && (!aPublicField.IsSealed))
{
var diagnostic = Diagnostic.Create(RuleCA1501, aPublicField.Locations[0], aPublicField.Name);
context.ReportDiagnostic(diagnostic);
}
}
My question is, how do I write a code fixer for this? Do I need to change the analyzer to pass the FieldDeclarationSyntax instead of the field symbol? I can't find any concrete sample of how to translate/transform a line of multiple field declarations into a separate auto properties.
UPDATE 1: So far my understanding to construct an auto property using this code:
PropertyDeclarationSyntax @propSymbol = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(fieldSymbol.MetadataName), memberName).AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
@propSymbol = @propSymbol.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)
));
@propSymbol = @propSymbol.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)
));
The code for PropertyDeclarationSyntax
is inspired by this article: https://dogschasingsquirrels.com/2014/08/04/code-generation-with-roslyn-fields-and-properties/
Upvotes: 0
Views: 721
Reputation: 6420
In the codefix, you can get to the VariableDeclarationSyntax
with the following code:
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var syntaxNode = root.FindNode(diagnosticSpan);
var variableDeclarator = syntaxNode.FirstAncestorOrSelf<VariableDeclaratorSyntax>();
var variableDeclaration = variableDeclarator?.Parent as VariableDeclarationSyntax;
This declaration might have many declarators, but all of them shares the same visibility and modifiers, so you'll want to fix them all. This means that you'll generate the corresponding properties for them, and then you'll need to replace the above variableDeclaration
with the generated properties.
You might want to a have a look here for a sample how multiple variables in the same declaration are separated into their own declarations: https://github.com/SonarSource-VisualStudio/sonaranalyzer-dotnet/blob/master/src/SonarAnalyzer.CSharp/Rules/MultipleVariableDeclarationCodeFixProvider.cs
BTW, I think
public int field1, field2, field3 = 0;
should be converted to
public int field1 { get; set; }
public int field2 { get; set; }
public int field3 { get; set; } = 0;
and not
public int field1 { get; set; } = 0;
public int field2 { get; set; } = 0;
public int field3 { get; set; } = 0;
Also note that this conversion might break your code. For example if you pass a field as ref
or out
, then it won't be changeable to a property.
Upvotes: 2