Search code examples
c#roslyncode-analysismono.cecil

How to match type symbol names as returned by Roslyn semantic model to those returned by Mono.Cecil?


I have the following piece of code:

var paramDeclType = m_semanticModel.GetTypeInfo(paramDecl.Type).Type;

Where paramDeclType.ToString() returns

System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<int>>

I also have the same information from Mono.Cecil - paramDef.ParameterType.FullName returns

System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.Int32>>

I would like to compare the type names coming from these two different sources.

I would like to avoid elaborate regex parsing, if possible.

Ideas are welcome.

P.S.

In the meantime, I am going to look for Mono.Cecil replacement from Microsoft - System.Reflection.Metadata. Maybe two libraries from Microsoft would be able to produce compatible type names.


Solution

  • I would like to share the function I currently use:

    private const string GENERIC_ARG_COUNT_PATTERN = @"`\d+";
    private static readonly Regex s_genericArgCountRegex = new Regex(GENERIC_ARG_COUNT_PATTERN);
    
    
    public static bool IsSameType(this TypeReference typeRef, ITypeSymbol typeSymbol, bool isByRef)
    {
        if (isByRef || typeRef.IsByReference)
        {
            return isByRef && typeRef.IsByReference && DoIsSameType(((ByReferenceType)typeRef).ElementType, typeSymbol);
        }
    
        return DoIsSameType(typeRef, typeSymbol);
    
        static bool DoIsSameType(TypeReference typeRef, ITypeSymbol typeSymbol)
        {
            var arrayTypeSymbol = typeSymbol as IArrayTypeSymbol;
            var arrayTypeRef = typeRef as ArrayType;
            if (arrayTypeSymbol != null || arrayTypeRef != null)
            {
                return arrayTypeSymbol != null
                    && arrayTypeRef != null
                    && arrayTypeSymbol.Rank == arrayTypeRef.Rank
                    && DoIsSameType(arrayTypeRef.ElementType, arrayTypeSymbol.ElementType);
            }
    
            // Optimistic approach
            var declFullName = typeSymbol.ToString();
            var typeRefFullName = typeRef.FullName;
            if (typeRefFullName.Contains('`'))
            {
                typeRefFullName = s_genericArgCountRegex.Replace(typeRef.FullName, "");
            }
            if (declFullName == typeRefFullName)
            {
                return true;
            }
    
            // Built-in types - either top level or as generic arguments.
            if (typeRef is GenericInstanceType genTypeRef)
            {
                if (!(typeSymbol is INamedTypeSymbol namedTypeSymbol) ||
                    !namedTypeSymbol.IsGenericType ||
                    genTypeRef.ElementType.Name != namedTypeSymbol.ConstructedFrom.MetadataName ||
                    genTypeRef.GenericArguments.Count != namedTypeSymbol.TypeArguments.Length ||
                    genTypeRef.ElementType.Namespace != namedTypeSymbol.ConstructedFrom.ContainingNamespace.ToString())
                {
                    return false;
                }
                for (int i = 0; i < genTypeRef.GenericArguments.Count; ++i)
                {
                    if (!DoIsSameType(genTypeRef.GenericArguments[i], namedTypeSymbol.TypeArguments[i]))
                    {
                        return false;
                    }
                }
                return true;
            }
    
            return
                typeRef.Name == typeSymbol.Name &&
                typeRef.IsBuiltInType() &&
                typeSymbol.ContainingNamespace.Name == "System";
        }
    }
    
    public static bool IsBuiltInType(this TypeReference t)
    {
        return t.Scope.Name == "mscorlib" && DoIsBuiltInType(t);
    
        static bool DoIsBuiltInType(TypeReference t) => t switch
        {
            ArrayType arrayType => DoIsBuiltInType(arrayType.ElementType),
            ByReferenceType byReferenceType => DoIsBuiltInType(byReferenceType.ElementType),
            _ => t.IsPrimitive || t.Name == "Object" || t.Name == "Decimal" || t.Name == "String",
        };
    }