FP S1200: Classes should not be coupled to too many other classes

I think I disagree with this rule when dealing with extension methods: I’ve I define an static class containing multiple extension methods, each dealing with multiple different classes, I think the class is not to blame, that is just a container. The rule can be tricked by moving different extension methods to different classes, but that would not improve things.

Here is the extension class that is reported on:

namespace Microsoft.CodeAnalysis;

public static class SyntaxNodeExtensions
{
    public static TNode? AncestorsAndSelf<TNode>(this SyntaxNode node) where TNode : SyntaxNode
        => node.AncestorsAndSelf().OfType<TNode>().FirstOrDefault();

    [Pure]
    public static string? Name(this SyntaxNode? node) => node switch
    {
        ArgumentSyntax n /*...............*/ => Name(n.Expression),
        AttributeSyntax n /*..............*/ => Name(n.Name),
        FieldDeclarationSyntax n /*.......*/ => Name(n.Declaration),
        IdentifierNameSyntax n /*.........*/ => n.Identifier.Text,
        InvocationExpressionSyntax n /*...*/ => Name(n.Expression),
        MemberAccessExpressionSyntax n /*.*/ => Name(n.Name),
        ParameterSyntax n /*..............*/ => n.Identifier.Text,
        SimpleNameSyntax n /*.............*/ => n.Identifier.Text,
        NameSyntax n /*...................*/ => n.ToFullString(),
        PropertyDeclarationSyntax n /*....*/ => n.Identifier.Text,
        VariableDeclarationSyntax n /*....*/ => Name(n.Variables.FirstOrDefault()),
        VariableDeclaratorSyntax n /*.....*/ => n.Identifier.Text,
        _ => null,
    };

    [Pure]
    public static TNode Cast<TNode>(this SyntaxNode node) where TNode : SyntaxNode
        => node as TNode
        ?? throw new InvalidOperationException($"Unexpected {node.GetType().Name}, expected {typeof(TNode).Name}.");

    [Pure]
    public static PropertyDeclaration PropertyDeclaration(this SyntaxNode node, SemanticModel model)
        => new(node.Cast<PropertyDeclarationSyntax>(), model);

    [Pure]
    public static TypeDeclaration TypeDeclaration(this SyntaxNode node, SemanticModel model)
        => TryTypeDeclaration(node, model)
        ?? throw new InvalidOperationException($"Unexpected {node.GetType().Name}, expected a type declaration.");

    [Pure]
    public static TypeDeclaration? TryTypeDeclaration(this SyntaxNode node, SemanticModel model) => node switch
    {
        ClassDeclarationSyntax @class => new TypeDeclaration.Class(@class, model),
        InterfaceDeclarationSyntax @interface => new TypeDeclaration.Interface(@interface, model),
        RecordDeclarationSyntax record => new TypeDeclaration.Record(record, model),
        StructDeclarationSyntax @struct => new TypeDeclaration.Struct(@struct, model),
        _ => null,
    };

    [Pure]
    public static MemberDeclaration MemberDeclaration(this SyntaxNode node, SemanticModel model)
        => TryMemberDeclaration(node, model)
        ?? throw new InvalidOperationException($"Unexpected {node.GetType().Name}, expected a member declaration.");

    [Pure]
    public static MemberDeclaration? TryMemberDeclaration(this SyntaxNode node, SemanticModel model) => node switch
    {
        FieldDeclarationSyntax field => new MemberDeclaration.Field(field, model),
        ParameterSyntax param => new MemberDeclaration.Parameter(param, model),
        PropertyDeclarationSyntax prop => new MemberDeclaration.Property(prop, model),
        _ => null,
    };

    [Pure]
    public static TypeNode TypeNode(this SyntaxNode node, SemanticModel model)
        => TryTypeNode(node, model)
        ?? throw new InvalidOperationException($"Unexpected {node.GetType().Name}, expected a type declaration.");

    [Pure]
    public static TypeNode? TryTypeNode(this SyntaxNode node, SemanticModel model) => node switch
    {
        ArrayTypeSyntax @array => new TypeNode.Array(@array, model),
        GenericNameSyntax generic => new TypeNode.Generic(generic, model),
        TypeSyntax @type => new TypeNode(type, model),
        _ => null,
    };
}

So I suggest to consider extension methods the unit to analyze, instead of the class. Good luck with considering if this can/should be taken into account.

Reported by: NuGet Gallery | SonarAnalyzer.CSharp 10.22.0.136894

Hi Corniel!

I think this makes sense, to except the static classes which contains only extensions.
I’ll open an issue in our backlog (internal only).

1 Like