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; }