HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Tool to Add Access Modifiers to Code

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
modifierstoolcodeaccessadd

Problem

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 EnumDeclarationSynt

Solution

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(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(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseType(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseType(context, out result))
    {
        return true;
    }
    if (TryAnalyzeBaseType(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(context, out result))
{
return true;
}
if (TryAnalyzeBaseTypeSymbol(context, out result))
{
return true;
}
if (TryAnalyzeBaseTypeSymbol(context, out result))
{
return true;
}
if (TryAnalyzeBaseTypeSymbol(context, out result))
{
return true;
}

return false;
}

private bool TryAnalyzeBaseTypeSymbol(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 BasePropertyDeclaration

Code Snippets

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

}

Context

StackExchange Code Review Q#100601, answer score: 3

Revisions (0)

No revisions yet.