Search code examples
c#roslynroslyn-code-analysisroslynator

C# How can i detect EvenHandler subscription in Roslyn


I'm writing a custom analyzer rule using Roslyn.

I want to find a method which is a handler for some event (via subscription). Like this:

public class Counter
{
    public event EventHandler ThresholdReached;
}

public class TestEvent
{
    public TestEvent()
    {
        Counter с = new Counter();
        с.ThresholdReached += OnThresholdReached;
    }

    private void OnThresholdReached(object sender, EventArgs e)
    {

    }
}

In my realization it looks:

    private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
    {
        MethodDeclarationSyntax methodDeclaration = (MethodDeclarationSyntax)context.Node;
        if (methodDeclaration.Identifier.IsMissing)
        {
            return;
        }

        IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration);

    }

I don't know how to detect that OnThresholdReached is subscription of Event ThresholdReached. If someone knows how to do it, please help=)


Solution

  • In an analyzer, you cannot know only from looking at a MethodDeclarationSyntax, whether that method is converted to a delegate or not. Because of that, you can not know (only from looking at a MethodDeclarationSyntax) whether that delegate is passed to the add accessor of an event or not.

    First of all, remember that a Roslyn analyzer can only see usages in the current assembly (project). If your method is converted to a delegate in another assembly, there is no way for the analyzer to see this.

    Secondly, remember that

    с.ThresholdReached += OnThresholdReached;
    

    may be expressed as

    EventHandler handler = OnThresholdReached;
    с.ThresholdReached += handler;
    

    If you only want to detect the first case, you can look at AssignmentExpressionSyntax instances of kind SyntaxKind.AddAssignmentExpression, and analyze those.

    If you want to detect all cases where a method group is converted to a delegate, you need to look at all instances of type SimpleNameSyntax and analyze those as follows:

    void Analyze(SyntaxNodeAnalysisContext context)
    {
        var node = context.Node as SimpleNameSyntax;
    
        // we're only interested in delegates
        var type = context.SemanticModel.GetTypeInfo(node, context.CancellationToken).ConvertedType;
    
        if (type == null || type.TypeKind != TypeKind.Delegate)
        {
            return;
        }
    
        // we're only interested in methods from the current assembly
        var symbol = context.SemanticModel.GetSymbolInfo(node, context.CancellationToken).Symbol;
    
        if (symbol == null ||
            symbol.Kind != SymbolKind.Method ||
            !symbol.ContainingAssembly.Equals(context.SemanticModel.Compilation.Assembly))
        {
            return;
        }
    
        // now you know symbol is a method in the same assembly, that is converted to a delegate
    }
    

    To find the source code for that method, see https://stackoverflow.com/a/45362532/1403794.