0

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=)

1 Answers1

0

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.

Kris Vandermotten
  • 10,111
  • 38
  • 49
  • Thank you, i'll try to use this! – Дмитрий К Jan 24 '19 at 11:18
  • Hello, It works! Partually, but i have some problems. When i trying to throw a ReportDiagnostic with the location of methodSymbol (not the node of his delegate), i have nothing. I have no idea, why =(. But, if i throw a ReportDiagnostic with location of delegate node, it works. Maybe it's a Roslyn feature ? Maybe i shouldn't a Report Diagnostic with another node location (different from registered)? – Дмитрий К Jan 25 '19 at 11:10
  • @ДмитрийК Ask a new question for that one – Kris Vandermotten Jan 25 '19 at 14:38