Search code examples
c#locationmetadataroslynfully-qualified-naming

Fully Qualified Namespace Metadata Error with Roslyn


I am trying to make a code analyzer which checks for fully qualified using statements. This link has been incredibly helpful, and the basis for my solution (How can I get the fully qualified namespace from a using directive in Roslyn?) but I am running into a problem when I try to access the location of the symbol for the using directive. My code looks like this:

    private static void AnalyzeModel(SemanticModelAnalysisContext semanticModelAnalysisContext)
    {
        var semanticModel = semanticModelAnalysisContext.SemanticModel;
        var root = semanticModel.SyntaxTree.GetRoot();

        // compare each using statement's name with its fully qualified name
        foreach (var usingDirective in root.DescendantNodes().OfType<UsingDirectiveSyntax>())
        {
            var symbol = semanticModel.GetSymbolInfo(usingDirective.Name).Symbol;
            var fullyQualifiedName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

            if (fullyQualifiedName.Contains(GlobalTag))
            {
                fullyQualifiedName = fullyQualifiedName.Substring(GlobalTag.Length);
            }

            if (usingDirective.Name.ToString() != fullyQualifiedName)
            {
                // for each name that is not fully qualified, produce a diagnostic.
                var diagnostic = Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name);
                semanticModelAnalysisContext.ReportDiagnostic(diagnostic);
            }
        }
    }

The problem is the symbol.Locations[0] only contains items in metadata, not items in source. This leads to the following error:

Assert.IsTrue failed. Test base does not currently handle diagnostics in metadata locations.

My source in my unit tests looks like this:

private const string incorrectSourceCode = @" namespace System { using IO; using Threading; }";

Why are there no items in symbol.Locations that are in source? Is there another place I can get this location? I've tried using symbol.ContainingSymbol.Locations[0] or symbol.ContainingNamespace.Locations[0], but those are not referring to the using specific using that I am interested in. I've been pulling out my hair over this for hours, and some clarity would be very greatly appreciated.

Thanks in advance!


Solution

  • Symbol contains MetadateLocation, so if you want to see SourceLocation just retrieve it from the appropriate SyntaxNode:

    var diagnostic = Diagnostic.Create(Rule, usingDirective.Name.GetLocation(), symbol.Name)
    

    instead of

    var diagnostic = Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name)