Let's imagine we wanted to create an analyzer that compared the name of the namespace to the path of the folder in which the file it resides. If the namespace doesn't match the folder structure, then we should raise a diagnostic. As we have seen in a previous post, you can get at the file name for the code you are analyzing from a SyntaxTreeAction. To start, we'll register a new SyntaxTreeAction and get the file path. We can also take that path and turn it into something that resembles a namespace declaration:

#!cs
compilationSyntax.RegisterSyntaxTreeAction((syntaxTreeContext) =>
{
    var filePath = syntaxTreeContext.Tree.FilePath;

    if (filePath == null)
        return;

    var parentDirectory = System.IO.Path.GetDirectoryName(filePath);

    // This will only work on windows and is not very robust.
    var parentDirectoryWithDots = parentDirectory.Replace("\\", ".");
}

Now that we have the path to use for comparison, we can get the namespaces in the file and then perform the comparison. We can get all of the namespaces in a SyntaxTree by using the OfType extension method and find all NamespaceDeclarationSyntax node types. Note: Make sure you use DescendantNodes and not ChildNodes to ensure you get all nested namespace declarations.

#!cs
var namespaceNodes = syntaxTreeContext.Tree.GetRoot().DescendantNodes().OfType<NamespaceDeclarationSyntax>();

We can loop over those nodes and compare the name to the folder path:

#!cs
foreach (var ns in namespaceNodes)
{
    var name = ????

    if (!parentDirectoryWithDots.EndsWith(name, StringComparison.OrdinalIgnoreCase))
    {
        syntaxTreeContext.ReportDiagnostic(Diagnostic.Create(
           Rule, ns.Name.GetLocation(), parentDirectoryWithDots));
    }
}

You might be tempted to just use the ns.ToFullString() method to get the name of the namespace, however, this does not work for nested namespaces. For example, if we have the code:

#!cs
namespace Foo
{
    namespace Bar
    {
    }
}

The full namespace for Bar is Foo.Bar, but the ns.ToFullString() method will only return Bar. To properly get the full name, we need to ask our friend the SemanticModel for the Symbol for this SyntaxNode and then we can get the DisplayString from there. Getting the semantic model requires a little massaging of our registration code.

The SemanticModel is available off of the Compilation object. As we discussed in the "Working with Types in Your Analyzer" post, you can access the Compilation object by registering for a CompilationStartAction and then registering your other actions from that context.

So our registration code, can be modified to:

#!cs
context.RegisterCompilationStartAction((compilationContext) =>
{
    compilationContext.RegisterSyntaxTreeAction((syntaxTreeContext) =>
    {
        var semModel = compilationContext.Compilation.GetSemanticModel(syntaxTreeContext.Tree);
        //...
    });
});

Then the code to check the namespace name can use the semantic model to get the INamespaceSymbol and get the display string from that.

#!cs
foreach (var ns in namespaceNodes)
{
    var symbolInfo = semModel.GetDeclaredSymbol(ns) as INamespaceSymbol;
    var name = symbolInfo.ToDisplayString();

    if (!parentDirectoryWithDots.EndsWith(name, StringComparison.OrdinalIgnoreCase))
    {
        syntaxTreeContext.ReportDiagnostic(Diagnostic.Create(
           Rule, ns.Name.GetLocation(), parentDirectoryWithDots));
    }
}

So, in the end, the full analyzer method looks like this:

#!cs
public override void Initialize(AnalysisContext context)
{
    context.RegisterCompilationStartAction((compilationContext) =>
    {
        compilationContext.RegisterSyntaxTreeAction((syntaxTreeContext) =>
        {
            var semModel = compilationContext.Compilation.GetSemanticModel(syntaxTreeContext.Tree);
            var filePath = syntaxTreeContext.Tree.FilePath;

            if (filePath == null)
                return;

            var parentDirectory = System.IO.Path.GetDirectoryName(filePath);

            // This will only work on windows and is not very robust.
            var parentDirectoryWithDots = parentDirectory.Replace("\\", ".");

            var namespaceNodes = syntaxTreeContext.Tree.GetRoot().DescendantNodes().OfType<NamespaceDeclarationSyntax>();

            foreach (var ns in namespaceNodes)
            {

                var symbolInfo = semModel.GetDeclaredSymbol(ns) as INamespaceSymbol;
                var name = symbolInfo.ToDisplayString();

                if (!parentDirectoryWithDots.EndsWith(name, StringComparison.OrdinalIgnoreCase))
                {
                    syntaxTreeContext.ReportDiagnostic(Diagnostic.Create(
                       Rule, ns.Name.GetLocation(), parentDirectoryWithDots));
                }
            }
        });
    });
}

And that is everything. I think this post really shows some of the power of using the SyntaxTree and the SemanticModel together to put together an analyzer that can process real world code. This post was inspired by a question I answered on StackOverflow and took a few iterations to get right. I thought I would share the process of how I got there so others can learn from it. If you have done something written a similar analyzer or have great examples of using the SemanticModel and SyntaxTree together, let's talk about it in the comments below.