Roslyn's SyntaxReceiver - Get Classes Implementing Interface
Asked Answered
B

1

6

I'm attempting to develop a source generator to auto-implement an interface on partial classes with that interface.

I believe this has to be a common use case for Microsoft's new Source Generators, and is even listed as a use case in the Roslyn Source Generator Cookbook, but with no example implementation.

I've searched but have struggled to find questions targeting this scenario in Roslyn Analyzers.

In the cookbook, they use a SyntaxReceiver class to filter which syntax nodes should be processed by the Execute call:

        class SluggableSyntaxReceiver : ISyntaxReceiver
        {
            public List<ClassDeclarationSyntax> ClassesToAugment { get; } = new List<ClassDeclarationSyntax>();

            public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
            {
                // Business logic to decide what we're interested in goes here
                if(syntaxNode is ClassDeclarationSyntax cds && cds.HasInterface("IChangeTracked"))
                    ClassesToAugment.Add(cds)
            }

        }

Check out the cookbook for the implementation details of the generator.

What I am trying to nail down is how to implement my HasInterface extension on the ClassDeclarationSyntax node.

        public static bool HasInterface(this ClassDeclarationSyntax source, string interfaceName)
        {
            IEnumerable<TypeSyntax> baseTypes = source.BaseList.Types.Select(baseType=>baseType.Type);
            // Ideally some call to do something like...
            return baseTypes.Any(baseType=>baseType.Name==interfaceName);
        }

How do I:

  1. take the BaseList property from the ClassDeclarationSyntax,
  2. determine if the class is partial or not
  3. access the interfaces
  4. determine if any of the interfaces is of a specific type.
Blanketing answered 17/6, 2021 at 13:6 Comment(0)
B
8

I have to believe there is a better way to do this than what I did, but for what its worth, here is a working implementation that determines if a particular ClassDeclarationSyntax explicitly implements an interface:

        /// <summary>Indicates whether or not the class has a specific interface.</summary>
        /// <returns>Whether or not the SyntaxList contains the attribute.</returns>
        public static bool HasInterface(this ClassDeclarationSyntax source, string interfaceName)
        {
            IEnumerable<BaseTypeSyntax> baseTypes = source.BaseList.Types.Select(baseType => baseType);

            // To Do - cleaner interface finding.
            return baseTypes.Any(baseType => baseType.ToString() ==interfaceName);
        }
Blanketing answered 17/6, 2021 at 15:12 Comment(2)
We might be able to improve the precision of Taylor's solution by checking the interface's name on the semantic model. We might be able to find indirect implementations by finding all classes/interfaces implementing/extending the searched interface and than recursively checking for classes/interfaces deriving/implementing/extending these.Precede
why not do ``` return source.BaseList?.Types.Any(baseType => baseType.ToString() == interfaceName) ?? false; ``` the baseTypes enumerable does not need to be allocated and the Any will do the loop until condition is trueCabin

© 2022 - 2024 — McMap. All rights reserved.