Welcome to the wonderful world of Code Analyzers. With Visual Studio 2015, you now have the ability to create a custom analyzer to provide real time warnings and errors to developers as they code. This type of technology has all sorts of uses. If you are creating a library and want to ensure it is being used properly, you can create an analyzer. If you want to be sure your entire development team is using the same style, you can create analyzers.

To get started, make sure you have the release candidate version of Visual Studio 2015. You will also need to install the .Net Compiler Platform SDK Templates from the Visual Studios extensions dialog. It is important to get the templates that match the version of Visual Studio you are using. Since I am using the release candidate, I am getting the RC templates.

Extensions and Updates Dialog with .Net Compiler Platform SDK Templates for RC selected

Now that you have the latest VS and the compiler platform templates, you can create a new analyzer project. The template is available under the Extensibility tab.

New Project Dialog with Analyzer template selected

Once created, you’ll notice that the solution has 3 projects

  • MyFirstAnalyzer (This is actual analyzer)
  • MyFirstAnalyzer.Test (Unit Tests)
  • MyFirstAnalyzer.Vsix (Visual Studio extension)

The MyFirstAnalyzer project already contains a very simple analyzer to get you started. The analyzer simply checks if a symbol contains lower case letters. It also provides a codefix to change the symbol to all uppercase. If you build and run your project, you will see a new instance of Visual Studio start and your analyzer will be loaded. This instance is referred to as an experimental instance. It has its own settings, so you can customize it without affecting your normal day-to-day environment. I recommend using a different theme in this environment, so you can tell them apart.

In the experimental instance, you can open up an existing project or create a new project. If you browse into a .cs file, you will see that some class names will have a green squiggly line underneath them (provided they contain lowercase letters).

Code Analyzer dialog with analyzer message

If you place your cursor on the symbol and hit the Quick Actions key (Default is Ctrl+.), you will see options for fixing this warning. You have the option to fix or suppress the warning. It also provides a nice preview of the operation, so you know how the resulting code will look.

Code fix window with Make Uppercase code fix shown

Since we have verified that the analyzer works, let's look at the code that is creating all of this magic. Open the DiagnosticAnalyzer.cs file. The start of the file contains code that sets up the analyzer. The important lines here are the Rule of type DiagnosticDescriptor, which provides Visual Studio the description of your diagnostic and the SupportedDiagnostics property which indicates which diagnostics are supported by this analyzer. This is where VS gets the information to show the user about your diagnostic.

internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

The Initialize method registers an Action in your analyzer to be called whenever a type of symbol is encountered. In this case the AnalyzeSymbol method is registered to be called anytime a SymbolKind.NamedType (e.g. a class) needs to be analyzed. In a future blog post, we will go over the different SymbolKinds and how they are used.

public override void Initialize(AnalysisContext context)
{
    // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
    context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
}

Finally, the code for the AnalyzeSymbol method is there. This code simply looks at the Symbol passed in and checks if it has any lowercase characters. If it does, it reports a diagnostics at the location of the symbol.

private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
    // TODO: Replace the following code with your own analysis, generating Diagnostic objects for any issues you find
    var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;

    // Find just those named type symbols with names containing lowercase letters.
    if (namedTypeSymbol.Name.ToCharArray().Any(char.IsLower))
    {
        // For all such symbols, produce a diagnostic.
        var diagnostic = Diagnostic.Create(Rule, namedTypeSymbol.Locations[0], namedTypeSymbol.Name);

        context.ReportDiagnostic(diagnostic);
    }
}

The Diagnostic.Create line creates the diagnostic that is then reported to Visual Studio using the context.ReportDiagnostic method. Diagnostic.Create is a very powerful method and has a lot of overloads. I urge you to explore some of these overloads and see how you can change the behavior of the diagnostic that gets created.

As you can see, creating an analyzer is pretty straightforward. You simply have to register yourself correctly and then you are in the pipeline for analyzing the code in a visual studio project. In the next blog post, we will modify the analyzer to support additional symbol types and cover some of the issues you may run into when working with different SymbolKinds.