Visual Basic has always had a little place in my heart. VB5 was the language I used at my first job and VB.Net was the language I used to transition into managed code. Luckily for us, Microsoft has kept improving VB.Net and the Roslyn compiler supports it as a first class citizen, just like C#.

So I am going to show you how to write an analyzer that analyzes Visual Basic code, but I will write the analyzer in C#, as that is my preferred language. To start analyzing Visual Basic code, you need to add a the Microsoft.CodeAnalysis.VisualBasic nuget package to your project.

Now, when creating a new analyzer, you need to make sure you indicate it is a Visual Basic analyzer by setting the DiagnosticAnalyzer attribute to include LanguageNames.VisualBasic. You should also add usings for the Microsoft.CodeAnalysis.VisualBasic and Microsoft.CodeAnalysis.VisualBasic.Syntax to ensure that the type you need to analyze for Visual Basic code are available.

using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;

namespace AnalyzerSamples.VB
{
    [DiagnosticAnalyzer(LanguageNames.VisualBasic)]
    public class OptionExplicitAndOptionStrictShouldNotBeTurnedOff : DiagnosticAnalyzer
    {
      //...
    }
}

In this analyzer, we are going to analyze Option Statements and ensure that Option Explicit and Option Strict are not set to Off. To do this, we will register a syntax node action for SyntaxKind.OptionStatement. Note that this SyntaxKind is in the Microsoft.CodeAnalysis.VisualBasic.Syntax, instead of the CSharp namespaces we have used in past analyzers.

public override void Initialize(AnalysisContext context)
{
    context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.OptionStatement);
}

Our AnalyzeSyntax method will run a few checks to make sure that we are working with a valid option statement (remember analyzers can run against code that is not compilable) and that it is a type that we care about. To start, our method simply gets the option statement and makes sure it has both the Name and Value keywords. If not, we stop analyzing.

private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
    // TODO: Replace the following code with your own analysis, generating Diagnostic objects for any issues you find
    var optionStatement = context.Node as OptionStatementSyntax;

    if (optionStatement == null)
        return;

    if (optionStatement.NameKeyword.IsMissing || optionStatement.ValueKeyword.IsMissing)
        return;

Next we are going to look at the name keyword and check it's kind. Since we are checking Explicit and Strict, we will look to see if it is a SyntaxKind.Explicit or SyntaxKind.Strict:

// Look to see if the keyword is Strict or Explicit.
if (!(optionStatement.NameKeyword.IsKind(SyntaxKind.StrictKeyword) || optionStatement.NameKeyword.IsKind(SyntaxKind.ExplicitKeyword)))
    return;

After we have validated that it is an option we care about, we can then check the value to see if it is on or off. This is as easy as checking the ValueKeyword to see if its SyntaxKind is SyntaxKind.OffKeyword:

// We only care if it is set to Off
if (!optionStatement.ValueKeyword.IsKind(SyntaxKind.OffKeyword))
    return;

If we have then reached this point in the code, we know that one of the two keywords is set to off and we can raise a diagnostic at the correct location with the correct message. Notice that we use the ValueText of the NameKeyword to allow the message to correctly show the name of the keyword that is causing the violation.

// For all such symbols, produce a diagnostic.
var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation(), optionStatement.NameKeyword.ValueText);
context.ReportDiagnostic(diagnostic);

The full code for the analyzer is:

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;

namespace AnalyzerSamples.VB
{
    [DiagnosticAnalyzer(LanguageNames.VisualBasic)]
    public class OptionExplicitAndOptionStrictShouldNotBeTurnedOff : DiagnosticAnalyzer
    {
        public const string DiagnosticId = "OptionExplicitAndOptionStrictShouldNotBeTurnedOff";
        internal static readonly LocalizableString Title = "Option strict and option explicit should not be turned off";
        internal static readonly LocalizableString MessageFormat = "Option {0} should not be turned off.";
        internal const string Category = "Visual Basic";

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

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

        public override void Initialize(AnalysisContext context)
        {
            context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.OptionStatement);
        }

        private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
        {
            var optionStatement = context.Node as OptionStatementSyntax;

            if (optionStatement == null)
                return;

            if (optionStatement.NameKeyword.IsMissing || optionStatement.ValueKeyword.IsMissing)
                return;

            // Look to see if the keyword is Strict or Explicit.
            if (!(optionStatement.NameKeyword.IsKind(SyntaxKind.StrictKeyword) || optionStatement.NameKeyword.IsKind(SyntaxKind.ExplicitKeyword)))
                return;

            // We only care if it is set to Off
            if (!optionStatement.ValueKeyword.IsKind(SyntaxKind.OffKeyword))
                return;

            // For all such symbols, produce a diagnostic.
            var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation(), optionStatement.NameKeyword.ValueText);

            context.ReportDiagnostic(diagnostic);

        }
    }
}

That's it. Writing analyzers for Visual Basic specific code is just as easy as writing analyzers for C# code. Many analyzers may be able to share a good majority, if not all, of their code to analyze both C# and Visual Basic. So, next time you are writing an analyzer, see how hard it would be to make it work for both C# and Visual Basic. A VB programmer may thank you for it.