4
\$\begingroup\$

Working on VSDiagnostics, I implemented a tool that adds the default access modifiers to C# code. An example use would be starting with this:

static class Foo { }

And ending with this:

internal static class Foo { }

This is the analyzer:

public override void Initialize(AnalysisContext context)
{
    context.RegisterSyntaxNodeAction(AnalyzeSymbol,
        SyntaxKind.ClassDeclaration,
        SyntaxKind.ConstructorDeclaration,
        SyntaxKind.DelegateDeclaration,
        SyntaxKind.EnumDeclaration,
        SyntaxKind.EventDeclaration,
        SyntaxKind.EventFieldDeclaration,
        SyntaxKind.FieldDeclaration,
        SyntaxKind.IndexerDeclaration,
        SyntaxKind.InterfaceDeclaration,
        SyntaxKind.MethodDeclaration,
        SyntaxKind.PropertyDeclaration,
        SyntaxKind.StructDeclaration);
}

private void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
{
    if (context.Node is ClassDeclarationSyntax)
    {
        var declarationExpression = (ClassDeclarationSyntax) context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is StructDeclarationSyntax)
    {
        var declarationExpression = (StructDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is EnumDeclarationSyntax)
    {
        var declarationExpression = (EnumDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is DelegateDeclarationSyntax)
    {
        var declarationExpression = (DelegateDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is InterfaceDeclarationSyntax)
    {
        var declarationExpression = (InterfaceDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is FieldDeclarationSyntax)
    {
        var declarationExpression = (FieldDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                "private"));
        }
    }

    if (context.Node is PropertyDeclarationSyntax)
    {
        var declarationExpression = (PropertyDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is MethodDeclarationSyntax)
    {
        var declarationExpression = (MethodDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is ConstructorDeclarationSyntax)
    {
        var declarationExpression = (ConstructorDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind()) || m.Kind() == SyntaxKind.StaticKeyword))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is EventFieldDeclarationSyntax)
    {
        var declarationExpression = (EventFieldDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                "private"));
        }
    }

    if (context.Node is EventDeclarationSyntax)
    {
        var declarationExpression = (EventDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }

    if (context.Node is IndexerDeclarationSyntax)
    {
        var declarationExpression = (IndexerDeclarationSyntax)context.Node;
        if (!declarationExpression.Modifiers.Any(m => _accessModifierKinds.Contains(m.Kind())))
        {
            var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationExpression).DeclaredAccessibility;

            context.ReportDiagnostic(Diagnostic.Create(Rule, declarationExpression.GetLocation(),
                accessibility.ToString().ToLower()));
        }
    }
}

private readonly SyntaxKind[] _accessModifierKinds =
{
    SyntaxKind.PublicKeyword,
    SyntaxKind.ProtectedKeyword,
    SyntaxKind.InternalKeyword,
    SyntaxKind.PrivateKeyword
};

I cannot get the current access modifier for EventFieldDeclarations and FieldDeclarations because the type containing the access modifier is a child of this type.

This is the code fix:

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
    var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
    var diagnostic = context.Diagnostics.First();
    var diagnosticSpan = diagnostic.Location.SourceSpan;

    var statement = root.FindNode(diagnosticSpan);

    var semanticModel = context.Document.GetSemanticModelAsync().Result;
    var symbol = semanticModel.GetDeclaredSymbol(statement);
    var accessibility = symbol?.DeclaredAccessibility ?? Accessibility.Private;

    context.RegisterCodeFix(CodeAction.Create(VSDiagnosticsResources.ExplicitAccessModifiersCodeFixTitle, x => AddModifier(context.Document, root, statement, accessibility), nameof(ExplicitAccessModifiersAnalyzer)), diagnostic);
}

private Task<Solution> AddModifier(Document document, SyntaxNode root, SyntaxNode statement, Accessibility accessibility)
{
    SyntaxNode newStatement = null;
    var accessModifierTokens = SyntaxFactory.TokenList(AccessModifiers(accessibility));

    if (statement is ClassDeclarationSyntax)
    {
        var classExpression = (ClassDeclarationSyntax) statement;

        var newClass = classExpression.WithModifiers(classExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newClass);
    }

    if (statement is StructDeclarationSyntax)
    {
        var structExpression = (StructDeclarationSyntax)statement;

        var newStruct = structExpression.WithModifiers(structExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newStruct);
    }

    if (statement is EnumDeclarationSyntax)
    {
        var enumExpression = (EnumDeclarationSyntax)statement;

        var newEnum = enumExpression.WithModifiers(enumExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newEnum);
    }

    if (statement is DelegateDeclarationSyntax)
    {
        var delegateExpression = (DelegateDeclarationSyntax)statement;

        var newDelegate = delegateExpression.WithModifiers(delegateExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newDelegate);
    }

    if (statement is InterfaceDeclarationSyntax)
    {
        var interfaceExpression = (InterfaceDeclarationSyntax)statement;

        var newInterface = interfaceExpression.WithModifiers(interfaceExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newInterface);
    }

    if (statement is FieldDeclarationSyntax)
    {
        var fieldExpression = (FieldDeclarationSyntax)statement;

        var newStruct = fieldExpression.WithModifiers(fieldExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newStruct);
    }

    if (statement is PropertyDeclarationSyntax)
    {
        var propertyExpression = (PropertyDeclarationSyntax)statement;

        var newProperty = propertyExpression.WithModifiers(propertyExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newProperty);
    }

    if (statement is MethodDeclarationSyntax)
    {
        var methodExpression = (MethodDeclarationSyntax)statement;

        var newMethod = methodExpression.WithModifiers(methodExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newMethod);
    }

    if (statement is ConstructorDeclarationSyntax)
    {
        var constructorExpression = (ConstructorDeclarationSyntax)statement;

        var newConstructor = constructorExpression.WithModifiers(constructorExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newConstructor);
    }

    if (statement is EventFieldDeclarationSyntax)
    {
        var eventFieldExpression = (EventFieldDeclarationSyntax)statement;

        var newEventField = eventFieldExpression.WithModifiers(eventFieldExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newEventField);
    }

    if (statement is EventDeclarationSyntax)
    {
        var eventExpression = (EventDeclarationSyntax)statement;

        var newEvent = eventExpression.WithModifiers(eventExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newEvent);
    }

    if (statement is IndexerDeclarationSyntax)
    {
        var indexerExpression = (IndexerDeclarationSyntax)statement;

        var newIndexer = indexerExpression.WithModifiers(indexerExpression.Modifiers.AddRange(accessModifierTokens));
        newStatement = statement.ReplaceNode(statement, newIndexer);
    }

    var newRoot = newStatement == null ? root : root.ReplaceNode(statement, newStatement);
    return Task.FromResult(document.WithSyntaxRoot(newRoot).Project.Solution);
}

private SyntaxToken[] AccessModifiers(Accessibility accessibility)
{
    switch (accessibility)
    {
        case Accessibility.Private:
            return new[] { SyntaxFactory.Token(SyntaxKind.PrivateKeyword) };
        case Accessibility.ProtectedAndInternal:
            return new[] { SyntaxFactory.Token(SyntaxKind.ProtectedKeyword), SyntaxFactory.Token(SyntaxKind.InternalKeyword) };
        case Accessibility.Protected:
            return new[] { SyntaxFactory.Token(SyntaxKind.ProtectedKeyword) };
        case Accessibility.Internal:
            return new[] { SyntaxFactory.Token(SyntaxKind.InternalKeyword) };
        case Accessibility.Public:
            return new[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword) };
    }

    return null;    // this cannot be reached
}

And here is a subset of the 68 unit tests:

[TestMethod]
public void ExplicitAccessModifiers_ClassDeclaration_InvokesWarning()
{
    var original = @"
namespace ConsoleApplication1
{
    class MyClass
    {
    }
}";

    var result = @"
namespace ConsoleApplication1
{
    internal class MyClass
    {
    }
}";

    VerifyDiagnostic(original, string.Format(ExplicitAccessModifiersAnalyzer.Rule.MessageFormat.ToString(), "internal"));
    VerifyFix(original, result);
}

[TestMethod]
public void ExplicitAccessModifiers_ClassDeclaration_ContainsNonAccessModifier_InvokesWarning()
{
    var original = @"
namespace ConsoleApplication1
{
    static class MyClass
    {
    }
}";

    var result = @"
namespace ConsoleApplication1
{
    static internal class MyClass
    {
    }
}";

    VerifyDiagnostic(original, string.Format(ExplicitAccessModifiersAnalyzer.Rule.MessageFormat.ToString(), "internal"));
    VerifyFix(original, result);
}

[TestMethod]
public void ExplicitAccessModifiers_ClassDeclaration_ContainsAccessModifier_DoesNotInvokeWarning()
{
    var original = @"
namespace ConsoleApplication1
{
    public class MyClass
    {
    }
}";

    VerifyDiagnostic(original);
}

[TestMethod]
public void ExplicitAccessModifiers_ClassDeclaration_OnlyChangesAccessModifiers_InvokesWarning()
{
    var original = @"
using System;

namespace ConsoleApplication1
{
    [Obsolete]
    class MyClass
    {
        public void Method() { }
    }
}";

    var result = @"
using System;

namespace ConsoleApplication1
{
    [Obsolete]
    internal class MyClass
    {
        public void Method() { }
    }
}";

    VerifyDiagnostic(original, string.Format(ExplicitAccessModifiersAnalyzer.Rule.MessageFormat.ToString(), "internal"));
    VerifyFix(original, result);
}

I appreciate any and all feedback.

\$\endgroup\$
0

2 Answers 2

3
\$\begingroup\$

private void AnalyzeSymbol(SyntaxNodeAnalysisContext context)

This method is quite large and could at least be improved for readability and performance. Right now you are checking for each expected type although if a previous check had been successful. It would better return early.

That beeing said let us see what the different "DeclarationSyntax"'s have in common.

By checking the rosaly source we see the following inheritance tree

ClassDeclarationSyntax -> TypeDeclarationSyntax -> BaseTypeDeclarationSyntax StructDeclarationSyntax -> TypeDeclarationSyntax -> BaseTypeDeclarationSyntax EnumDeclarationSyntax -> BaseTypeDeclarationSyntax
InterfaceDeclarationSyntax -> TypeDeclarationSyntax -> BaseTypeDeclarationSyntax

DelegateDeclarationSyntax -> MemberDeclarationSyntax

EventFieldDeclarationSyntax -> BaseFieldDeclarationSyntax
FieldDeclarationSyntax -> BaseFieldDeclarationSyntax

PropertyDeclarationSyntax -> BasePropertyDeclarationSyntax
EventDeclarationSyntax -> BasePropertyDeclarationSyntax
IndexerDeclarationSyntax -> BasePropertyDeclarationSyntax

MethodDeclarationSyntax -> BaseMethodDeclarationSyntax
ConstructorDeclarationSyntax -> BaseMethodDeclarationSyntax

and expect for DelegateDeclarationSyntax -> MemberDeclarationSyntax each of these base classes share a SyntaxTokenList Modifiers property.

If we then add a class AnalyzeResult like so

private class AnalyzeResult
{
    public Location NodeLocion { get; private set; }
    public string Accessibility { get; private set; }
    public AnalyzeResult(Location location, string accessibility)
    {
        NodeLocion = location;
        Accessibility = accessibility;
    }
}

We can add methods for processing the separate base classes in a bool TryGetValue(SyntaxNodeAnalysisContext, out AnalyzeResult) fashion.

By adding generic methods for processing the extending classes in the same fashion like so

private bool TryAnalyzeBaseType<T>(SyntaxNodeAnalysisContext context, out AnalyzeResult value) where T : BaseTypeDeclarationSyntax
{
    value = null;

    var declaration = context.Node as T;
    if (declaration == null) { return false; }

    var accessibility = context.SemanticModel.GetDeclaredSymbol(declaration).DeclaredAccessibility.ToString().ToLower();
    value = new AnalyzeResult(declaration.GetLocation(), accessibility);

    return true;
}

which are called from here

private bool TryAnalyzeBaseTypeSymbol(SyntaxNodeAnalysisContext context, out AnalyzeResult result)
{
    result = null;

    var declarationSyntax = context.Node as BaseTypeDeclarationSyntax;
    if (declarationSyntax == null)
    {
        return false;
    }
    if (IsContainedInDeclaredModifiers(declarationSyntax.Modifiers))
    {
        return true;
    }

    if (TryAnalyzeBaseType<ClassDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseType<StructDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseType<EnumDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseType<InterfaceDeclarationSyntax>(context, out result))
    {
        return true;
    }

    return false;
}

private bool IsContainedInDeclaredModifiers(SyntaxTokenList modifiers)
{
    return modifiers.Any(m => _accessModifierKinds.Contains(m.Kind()));
}

We have a clear chain of execution for each class which extends BaseTypeDeclarationSyntax.

For completeness all these TryX..() and their generic methods

private bool IsContainedInDeclaredModifiers(SyntaxTokenList modifiers)
{
    return modifiers.Any(m => _accessModifierKinds.Contains(m.Kind()));
}

private bool TryAnalyzeBaseTypeSymbol(SyntaxNodeAnalysisContext context, out AnalyzeResult result)
{
    result = null;

    var declarationSyntax = context.Node as BaseTypeDeclarationSyntax;
    if (declarationSyntax == null)
    {
        return false;
    }
    if (IsContainedInDeclaredModifiers(declarationSyntax.Modifiers))
    {
        return true;
    }

    if (TryAnalyzeBaseTypeSymbol<ClassDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseTypeSymbol<StructDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseTypeSymbol<EnumDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseTypeSymbol<InterfaceDeclarationSyntax>(context, out result))
    {
        return true;
    }

    return false;
}

private bool TryAnalyzeBaseTypeSymbol<T>(SyntaxNodeAnalysisContext context, out AnalyzeResult value) where T : BaseTypeDeclarationSyntax
{
    value = null;

    var declaration = context.Node as T;
    if (declaration == null) { return false; }

    var accessibility = context.SemanticModel.GetDeclaredSymbol(declaration).DeclaredAccessibility.ToString().ToLower();
    value = new AnalyzeResult(declaration.GetLocation(), accessibility);

    return true;
}


private bool TryAnalyzeBasePropertySymbol(SyntaxNodeAnalysisContext context, out AnalyzeResult result)
{
    result = null;

    var declarationSyntax = context.Node as BasePropertyDeclarationSyntax;
    if (declarationSyntax == null)
    {
        return false;
    }
    if (IsContainedInDeclaredModifiers(declarationSyntax.Modifiers))
    {
        return true;
    }

    if (TryAnalyzeBasePropertySymbol<PropertyDeclarationSyntax>(context,out result))
    {
        return true;
    }
    if (TryAnalyzeBasePropertySymbol<EventDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBasePropertySymbol<IndexerDeclarationSyntax>(context, out result))
    {
        return true;
    }

    return false;
}

private bool TryAnalyzeBasePropertySymbol<T>(SyntaxNodeAnalysisContext context, out AnalyzeResult value) where T : BasePropertyDeclarationSyntax
{
    value = null;

    var declaration = context.Node as T;
    if (declaration == null) { return false; }

    var accessibility = context.SemanticModel.GetDeclaredSymbol(declaration).DeclaredAccessibility.ToString().ToLower();
    value = new AnalyzeResult(declaration.GetLocation(), accessibility);

    return true;

}

private bool TryAnalyzeBaseMethodSymbol(SyntaxNodeAnalysisContext context, out AnalyzeResult result)
{
    result = null;

    var declarationSyntax = context.Node as BaseMethodDeclarationSyntax;
    if (declarationSyntax == null)
    {
        return false;
    }
    if (IsContainedInDeclaredModifiers(declarationSyntax.Modifiers))
    {
        return true;
    }

    if (TryAnalyzeBaseMethodSymbol<MethodDeclarationSyntax>(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseMethodSymbol<ConstructorDeclarationSyntax>(context, out result))
    {
        return true;
    }

    return false;
}

private bool TryAnalyzeBaseMethodSymbol<T>(SyntaxNodeAnalysisContext context, out AnalyzeResult value) where T : BaseMethodDeclarationSyntax
{
    value = null;

    var declaration = context.Node as T;
    if (declaration == null) { return false; }

    var accessibility = context.SemanticModel.GetDeclaredSymbol(declaration).DeclaredAccessibility.ToString().ToLower();
    value = new AnalyzeResult(declaration.GetLocation(), accessibility);

    return true;

}

private bool TryAnalyzeDelegateSymbol(SyntaxNodeAnalysisContext context, out AnalyzeResult result)
{
    result = null;

    var declarationSyntax = context.Node as DelegateDeclarationSyntax;
    if (declarationSyntax == null)
    {
        return false;
    }
    if (IsContainedInDeclaredModifiers(declarationSyntax.Modifiers))
    {
        return true;
    }

    var accessibility = context.SemanticModel.GetDeclaredSymbol(declarationSyntax).DeclaredAccessibility.ToString().ToLower();

    result = new AnalyzeResult(declarationSyntax.GetLocation(), accessibility);

    return true;
}

private bool TryAnalyzeBaseFieldSymbol(SyntaxNodeAnalysisContext context, out AnalyzeResult result)
{
    result = null;

    var declarationSyntax = context.Node as BaseFieldDeclarationSyntax;
    if (declarationSyntax == null)
    {
        return false;
    }
    if (IsContainedInDeclaredModifiers(declarationSyntax.Modifiers))
    {
        return true;
    }

    result = new AnalyzeResult(declarationSyntax.GetLocation(), "private");

    return true;
}  

then we will change the former AnalyzeSymbol() method like so

private void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
{


    AnalyzeResult result;
    if (TryAnalyzeBaseTypeSymbol(context, out result))
    {
        if (result == null) { return; }

    }
    else if (TryAnalyzeBaseFieldSymbol(context, out result))
    {
        if (result == null) { return; }

    } else if(TryAnalyzeBasePropertySymbol(context,out result))
    {
        if (result == null) { return; }

    } else if (TryAnalyzeBaseMethodSymbol(context,out result))
    {
        if (result == null) { return; }

    } else if(TryAnalyzeDelegateSymbol(context,out result))
    {
        if (result == null) { return; }
    }
    else
    {
        return;
    }

    context.ReportDiagnostic(Diagnostic.Create(Rule, result.NodeLocion,
                result.Accessibility));

}
\$\endgroup\$
0
3
\$\begingroup\$

For the code fix, we can use the built-in SyntaxGenerator:

private Task<Solution> AddModifier(Document document, SyntaxNode root, SyntaxNode statement, Accessibility accessibility)
{
    var generator = SyntaxGenerator.GetGenerator(document);
    var newStatement = generator.WithAccessibility(statement, accessibility);

    var newRoot = root.ReplaceNode(statement, newStatement);
    return Task.FromResult(document.WithSyntaxRoot(newRoot).Project.Solution);
}

That's it! Thanks to @mattwar on GitHub for telling me about this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.