I recently had to create a Roslyn Analyzer that envelopes code in a try/catch statement.

This reasoning was to prevent loading errors in any exported MEF component. Here’s the analyzer in action:

 

The analyzer does the following:

  1. Adds a Diagnostic that finds a MEF ImportingConstructor with content that’s not entirely wrapped in a try/catch statement.
  2. Provides a Code Fix to handle the problem.
  3. The Code Fix will:
    1. Add a try/catch statement around the entire content
    2. Add ErrorNotificationLogger.LogErrorWithoutShowingErrorNotificationUI("Error in MEF ctor", e);  inside the catch.
    3. Add a using  statement that for the static class  ErrorNotificationLogger

The analyzer code is available on GitHub, but if you’re interested in the explanation, we’ll see how this sort of analyzer can be created.

Getting Started

If you never created Roslyn Analyzers before, you might want to read the getting started tutorial first. If you never worked with Roslyn before, I suggest first starting with Josh Varty’s tutorials.

Start with the regular Roslyn Analyzer template in File | New Project | C# | Extensibility | Analyzer with Code Fix (NuGet + VSIX) template.

Each analyzer consists of a Diagnostic and a CodeFix.

The Diagnostic in our case will find constructors with the  ImportingConstructor attribute and mark them as Error.

The CodeFix will wrap the code in the constructor with try/catch.

The Diagnostic

Here’s what happens here:

  1. In Initialize method we register an Action on any ConstructorDeclaration.
  2. In AnalyzeConstructor we check 3 conditions:
    1. if the constructor has the  ImportingConstructor attribute. should be true
    2. If the body of the constructor is white space only . should be false
    3. If the entire body is wrapped in a single try/catch statement. should be false
  3. If the 3 conditions are met, then we call ReportDiagnostic.

The Code Fix

The Code Fix is a bit more complicated. We’ll see it in parts (the entire file can be seen here on GitHub)

The code fix starts with:

Explanation:

  • According to FixableDiagnosticIds, the code fix will run only for our specific Diagnostic.
  • RegisterCodeFixesAsync will run for each found Diagnostic. It will:
    • Find the diagnostic span
    • Register a code fix with a createChangedDocument function
      c => ChangeBlock(context.Document, ctor, c)  where ‘c’ is a CancellationToken

This is it for the boilerplate part of the analyzer. The next part is code manipulation with Roslyn, where we will transform any constructor to be wrapped in a try/catch statement. This will be done in the ChangeBlock method next.

Explanation:

  1. We create a new constructor with the desired try/catch statement. We can see in the bottom of CreateConstructorWithTryCatch that the Body of try is the body of the previous constructor.
    You can easily generate such code with Kiril Osenkov’s Roslyn Quoter.
  2. We replace the old constructor with the new constructor with
    var newRoot = root.ReplaceNode(ctorInOrig2, ctorInEntirelyNormalized);
  3. The entire following code is to add using statements. This was needed because for Sysetm.Exception and for my own static class ErrorNotificationLogger.
    We basically find the last using statement, and insert a new using statement afterward.
  4. We return the new root ( CompilationUnitSyntax)

This is it. As mentioned, the Analyzer code is available on GitHub and it’s also published as a Nuget package OzCode.VisualStudioExtensionAnalyzers.

For more tutorials on Visual Studio Extensibility, you can start with https://michaelscodingspot.com/2017/10/08/visual-studio-2017-extension-development-tutorial-part-1/

For more tutorials on Roslyn, you can start with Josh Varty’s tutorials.

Get Exclusive Articles and Level-up Your C# Game Performance Optimizations in C#: 10 Best Practices