Search code examples
c#nrefactory

How to check if a class implements interface methods in NRefactory


I have two file. One of this is a class declaration and other is interface declaration. Class should implements interface. How can I check in NRefactory if class implements interface methods?

I should give more details.

First file - for example:

class Test : IF
{
}

and the second

interface IF
{
    void Foo();
}

I have to read these files and parse with NRefactory. I need to check if class Test implements method from interface IF.

Without compilation and loading compiled assembly.


Solution

  • I found solution in NRefactory code. I've modified this in order to achive my goals. First we should implement visitor which check if classes implements each method from interface:

    public class MissingInterfaceMemberImplementationVisitor : DepthFirstAstVisitor
    {
        private readonly CSharpAstResolver _resolver;
        public bool IsInterfaceMemberMissing { get; private set; }
        public MissingInterfaceMemberImplementationVisitor(CSharpAstResolver resolver)
        {
            _resolver = resolver;
            IsInterfaceMemberMissing = false;
        }
    
        public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
        {
            if (typeDeclaration.ClassType == ClassType.Interface || typeDeclaration.ClassType == ClassType.Enum)
                return;
            base.VisitTypeDeclaration(typeDeclaration);
            var rr = _resolver.Resolve(typeDeclaration);
            if (rr.IsError)
                return;
            foreach (var baseType in typeDeclaration.BaseTypes)
            {
                var bt = _resolver.Resolve(baseType);
                if (bt.IsError || bt.Type.Kind != TypeKind.Interface)
                    continue;
                bool interfaceMissing;
                var toImplement = ImplementInterfaceAction.CollectMembersToImplement(rr.Type.GetDefinition(), bt.Type, false, out interfaceMissing);
                if (toImplement.Count == 0)
                    continue;
    
                IsInterfaceMemberMissing = true;
            }
        }
    }
    

    And now we have to read all files, parse them and invoke above class in a following way:

    var solutionFiles = new List<FileInfo>();
    var trees = new Dictionary<FileInfo, SyntaxTree>();
    IProjectContent projectContent = new CSharpProjectContent();
    foreach (var file in solutionFiles.Where(f => f.Extension == ".cs").Distinct())
    {
        var parser = new ICSharpCode.NRefactory.CSharp.CSharpParser();
        SyntaxTree syntaxTree;
        using (var fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan))
        {
            syntaxTree = parser.Parse(fs, file.FullName);
        }
        trees.Add(file, syntaxTree);
        var unresolvedFile = syntaxTree.ToTypeSystem();
        projectContent = projectContent.AddOrUpdateFiles(unresolvedFile);
    }
    var compilation = projectContent.CreateCompilation();
    
    foreach (var sharpFile in trees)
    {
        var originalResolver = new CSharpAstResolver(compilation, sharpFile.Value, sharpFile.Value.ToTypeSystem());
        var visitor = new MissingInterfaceMemberImplementationVisitor(originalResolver);
        sharpFile.Value.AcceptVisitor(visitor);
        if (visitor.IsInterfaceMemberMissing)
           return false;
        }
    return true;