Exceptions. Most developers have a love/hate relationship with them. And catching general exceptions is a topic that gets discussed over and over and over. Regardless of which side of the fence you are on, you may run into a scenario when you want to write an analyzer that can enforce your team's standards when working with exceptions. Roslyn offers a few different SyntaxKinds which you can register for that give you access to various parts of a catch statement.

Syntax Kind Description
SyntaxKind.CatchClause This represents the entirety of the catch statement and the body of the work to be done inside the catch clause.
SyntaxKind.CatchDeclaration This represents the variable declaration portion of the catch clause. So in the catch clause catch (Exception ex) when (i==1), this node would represent Exception ex
SyntaxKind.CatchFilterClause This represents the filter provided as part of the catch clause. So in the catch clause catch (Exception ex) when (i==1), this node would represent i==1
SyntaxKind.CatchKeyword This represents the catch keyword that is part of a catch clause.

So, now that we know the different types of syntax nodes we can work with, we can put them to use in a custom analyzer. For example, say you wanted to raise a diagnostic anytime a System.Exception was caught. As we covered in the Working with Types post, you need to capture the type you want to check from the Compilation engine. Since in this case we care about System.Exception, we will capture that from a CompilationStartAction.

context.RegisterCompilationStartAction((compileContext) =>
{
    var exceptionType = compileContext.Compilation.GetTypeByMetadataName("System.Exception");

Next we need to register our code to run on a SyntaxKind.CatchDeclaration.

compileContext.RegisterSyntaxNodeAction((symbolContext) =>
{
}, SyntaxKind.CatchDeclaration);

Inside the body of this action, we can check a few things. First, we should check that the declaration has a type. The main reasons it may not have a type is that the user is currently typing and they haven't gotten there yet, or just that the document is in a non-compilable state. If we run into this scenario, we will just stop analyzing for now.

var errorSymbol = symbolContext.Node as CatchDeclarationSyntax;

if (errorSymbol.Type.IsMissing)
{
    return;
}

After that, we simply need to get the type of the variable from the semantic model and check it against the exceptionType we stashed away earlier.

var variableTypeInfo = symbolContext.SemanticModel.GetTypeInfo(errorSymbol.Type).ConvertedType as INamedTypeSymbol;

if (variableTypeInfo == null)
    return;

if (variableTypeInfo.Equals(exceptionType))
{
    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, errorSymbol.GetLocation()));
}

So, the full diagnostic is now:

context.RegisterCompilationStartAction((compileContext) =>
{
    var exceptionType = compileContext.Compilation.GetTypeByMetadataName("System.Exception");
    compileContext.RegisterSyntaxNodeAction((symbolContext) =>
    {
        var errorSymbol = symbolContext.Node as CatchDeclarationSyntax;

        if (errorSymbol.Type.IsMissing)
        {
            return;
        }

        var variableTypeInfo = symbolContext.SemanticModel.GetTypeInfo(errorSymbol.Type).ConvertedType as INamedTypeSymbol;

        if (variableTypeInfo == null)
            return;

        if (variableTypeInfo.Equals(exceptionType))
        {
            symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, errorSymbol.GetLocation()));
        }
    }, SyntaxKind.CatchDeclaration);
});

At this point, we have a fully functioning analyzer that raises a diagnostic anytime a general exception is caught. While this is nice, there is also the scenario where there is no CatchDeclaration specified, i.e. the user just has a catch keyword on a line. For these scenarios, we can register a SyntaxNodeAction for a SyntaxKind.CatchClause and check to see if a declaration exists. If no declaration exists, then we can raise the diagnostic.

compileContext.RegisterSyntaxNodeAction((symbolContext) =>
{
    var errorSymbol = symbolContext.Node as CatchClauseSyntax;

    if (errorSymbol.Declaration == null)
    {
        symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, errorSymbol.CatchKeyword.GetLocation()));
        return;
    }
}, SyntaxKind.CatchClause);

As you can see, the code analysis tools in Roslyn make it easy to write an analyzer that will help nudge your team along to use exceptions correctly (for various definitions of correct :) ).