CodeAnalysis: Getting Symbol Info for “External” Types

I found the problem: I was filtering out/ignoring IdentifierNameSyntax nodes but leaving in/processing PredefinedTypeSyntax nodes. The string Type is an example of the latter while ConsoleChannel is an example of the former.

This post serves to flesh out a question I asked on the Roslyn gitter.im feed.

I’m writing a code analyzer to gather metadata information about assemblies, namespaces, types, interfaces, methods, fields, etc., in a set of related C# projects. Currently I use Buildalyzer to determine the assemblies needed to compile the source code and the Roslyn C# compiler to compile the source code files into syntax trees from which I extract the symbol information containing the metadata I want.

The current problem I’m running into involves getting symbol information for a type in a C# project referenced by the project I’m compiling. In other words, it’s a type defined in a separate project which is, in Visual Studio terms, a project dependency. Everything compiles fine within Visual Studio. But I can’t “see” the symbol information for the syntax node resulting from compiling the project source code with Roslyn.

Here’s the type I’m trying to see:

using System;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Configuration;

namespace J4JSoftware.Logging
{

    [Channel("Console")]
    public class ConsoleChannel : LogChannel
    {
        public ConsoleChannel()
        {
        }

        public ConsoleChannel( IConfigurationRoot configRoot, string loggerSection = "Logger")
            : base( configRoot, loggerSection )
        {
        }

        public override LoggerConfiguration Configure( LoggerSinkConfiguration sinkConfig, string? outputTemplate = null )
        {
            return string.IsNullOrEmpty( outputTemplate )
                ? sinkConfig.Console( restrictedToMinimumLevel : MinimumLevel )
                : sinkConfig.Console( restrictedToMinimumLevel : MinimumLevel, outputTemplate : outputTemplate );
        }
    }
}

Again, this file is in a project referenced from the project I’m compiling and analyzing.

Here’s how the ConsoleChannel type is being referred to in the project I’m compiling/analyzing:

using J4JSoftware.Logging;

namespace RoslynNetStandardTestLib
{
    [ DummyType( typeof(string) ) ]
    [ DummyType( typeof(ConsoleChannel) ) ]
    public class AttributedClass
    {
    }
}

The code compiles and both the syntax trees and the SemanticModel are generated without any problems.

However, when I walk down into those two DummyType attribute decorations two different things happen:

  • For the first one, referencing typeof(string), I’m able to retrieve the symbol info (which is an INamedTypeSymbol for the type string). The active syntax node has a Kind of PredefinedTypeSyntax.
  • But for the second one, referencing typeof(ConsoleChannel), no symbol info is returned; it’s null. The active syntax node has a Kind of TypeOfExpressionSyntax.

Here’s the code I’m using to extract symbol information from a SyntaxNode:

        public bool GetSymbol<TSymbol>( SyntaxNode node, out TSymbol? result )
            where TSymbol : class, ISymbol
        {
            result = null;

            var symbolInfo = Model.GetSymbolInfo( node );
            var rawSymbol = symbolInfo.Symbol ?? Model.GetDeclaredSymbol( node );

            if( rawSymbol == null )
                return false;

            if( rawSymbol is TSymbol retVal )
            {
                result = retVal;
                return true;
            }

            return false;
        }

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Archives
Categories