// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// <summary> /// Contains extension methods for the type system. /// </summary> public static class TypeSystemExtensions { #region GetAllBaseTypes /// <summary> /// Gets all base types. /// </summary> /// <remarks>This is the reflexive and transitive closure of <see cref="IType.DirectBaseTypes"/>. /// Note that this method does not return all supertypes - doing so is impossible due to contravariance /// (and undesirable for covariance as the list could become very large). /// /// The output is ordered so that base types occur before derived types. /// </remarks> public static IEnumerable<IType> GetAllBaseTypes(this IType type) { if (type == null) throw new ArgumentNullException("type"); BaseTypeCollector collector = new BaseTypeCollector(); collector.CollectBaseTypes(type); return collector; } /// <summary> /// Gets all non-interface base types. /// </summary> /// <remarks> /// When <paramref name="type"/> is an interface, this method will also return base interfaces (return same output as GetAllBaseTypes()). /// /// The output is ordered so that base types occur before derived types. /// </remarks> public static IEnumerable<IType> GetNonInterfaceBaseTypes(this IType type) { if (type == null) throw new ArgumentNullException("type"); BaseTypeCollector collector = new BaseTypeCollector(); collector.SkipImplementedInterfaces = true; collector.CollectBaseTypes(type); return collector; } #endregion #region GetAllBaseTypeDefinitions /// <summary> /// Gets all base type definitions. /// The output is ordered so that base types occur before derived types. /// </summary> /// <remarks> /// This is equivalent to type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct(). /// </remarks> public static IEnumerable<ITypeDefinition> GetAllBaseTypeDefinitions(this IType type) { if (type == null) throw new ArgumentNullException("type"); return type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct(); } /// <summary> /// Gets whether this type definition is derived from the base type definition. /// </summary> public static bool IsDerivedFrom(this ITypeDefinition type, ITypeDefinition baseType) { if (type == null) throw new ArgumentNullException("type"); if (baseType == null) return false; if (type.Compilation != baseType.Compilation) { throw new InvalidOperationException("Both arguments to IsDerivedFrom() must be from the same compilation."); } return type.GetAllBaseTypeDefinitions().Contains(baseType); } /// <summary> /// Gets whether this type definition is derived from a given known type. /// </summary> public static bool IsDerivedFrom(this ITypeDefinition type, KnownTypeCode baseType) { if (type == null) throw new ArgumentNullException("type"); if (baseType == KnownTypeCode.None) return false; return IsDerivedFrom(type, type.Compilation.FindType(baseType).GetDefinition()); } #endregion #region GetDeclaringTypeDefinitionsOrThis /// <summary> /// Returns all declaring type definitions of this type definition. /// The output is ordered so that inner types occur before outer types. /// </summary> public static IEnumerable<ITypeDefinition> GetDeclaringTypeDefinitions(this ITypeDefinition definition) { if (definition == null) { throw new ArgumentNullException(nameof(definition)); } while (definition != null) { yield return definition; definition = definition.DeclaringTypeDefinition; } } #endregion #region IsOpen / IsUnbound / IsKnownType sealed class TypeClassificationVisitor : TypeVisitor { internal bool isOpen; internal IEntity typeParameterOwner; int typeParameterOwnerNestingLevel; public override IType VisitTypeParameter(ITypeParameter type) { isOpen = true; // If both classes and methods, or different classes (nested types) // are involved, find the most specific one int newNestingLevel = GetNestingLevel(type.Owner); if (newNestingLevel > typeParameterOwnerNestingLevel) { typeParameterOwner = type.Owner; typeParameterOwnerNestingLevel = newNestingLevel; } return base.VisitTypeParameter(type); } static int GetNestingLevel(IEntity entity) { int level = 0; while (entity != null) { level++; entity = entity.DeclaringTypeDefinition; } return level; } } /// <summary> /// Gets whether the type is an open type (contains type parameters). /// </summary> /// <example> /// <code> /// class X<T> { /// List<T> open; /// X<X<T[]>> open; /// X<string> closed; /// int closed; /// } /// </code> /// </example> public static bool IsOpen(this IType type) { if (type == null) throw new ArgumentNullException("type"); TypeClassificationVisitor v = new TypeClassificationVisitor(); type.AcceptVisitor(v); return v.isOpen; } /// <summary> /// Gets the entity that owns the type parameters occurring in the specified type. /// If both class and method type parameters are present, the method is returned. /// Returns null if the specified type is closed. /// </summary> /// <seealso cref="IsOpen"/> static IEntity GetTypeParameterOwner(IType type) { if (type == null) throw new ArgumentNullException("type"); TypeClassificationVisitor v = new TypeClassificationVisitor(); type.AcceptVisitor(v); return v.typeParameterOwner; } /// <summary> /// Gets whether the type is unbound (is a generic type, but no type arguments were provided). /// </summary> /// <remarks> /// In "<c>typeof(List<Dictionary<,>>)</c>", only the Dictionary is unbound, the List is considered /// bound despite containing an unbound type. /// This method returns false for partially parameterized types (<c>Dictionary<string, ></c>). /// </remarks> public static bool IsUnbound(this IType type) { if (type == null) throw new ArgumentNullException("type"); return type is ITypeDefinition && type.TypeParameterCount > 0; } /// <summary> /// Gets whether the type is the specified known type. /// For generic known types, this returns true for any parameterization of the type (and also for the definition itself). /// </summary> public static bool IsKnownType(this IType type, KnownTypeCode knownType) { var def = type.GetDefinition(); return def != null && def.KnownTypeCode == knownType; } /// <summary> /// Gets whether the type is the specified known type. /// For generic known types, this returns true for any parameterization of the type (and also for the definition itself). /// </summary> internal static bool IsKnownType(this IType type, KnownAttribute knownType) { var def = type.GetDefinition(); return def != null && def.FullTypeName.IsKnownType(knownType); } public static bool IsKnownType(this FullTypeName typeName, KnownTypeCode knownType) { return typeName == KnownTypeReference.Get(knownType).TypeName; } public static bool IsKnownType(this TopLevelTypeName typeName, KnownTypeCode knownType) { return typeName == KnownTypeReference.Get(knownType).TypeName; } internal static bool IsKnownType(this FullTypeName typeName, KnownAttribute knownType) { return typeName == knownType.GetTypeName(); } internal static bool IsKnownType(this TopLevelTypeName typeName, KnownAttribute knownType) { return typeName == knownType.GetTypeName(); } #endregion #region GetDelegateInvokeMethod /// <summary> /// Gets the invoke method for a delegate type. /// </summary> /// <remarks> /// Returns null if the type is not a delegate type; or if the invoke method could not be found. /// </remarks> public static IMethod GetDelegateInvokeMethod(this IType type) { if (type == null) throw new ArgumentNullException("type"); if (type.Kind == TypeKind.Delegate) return type.GetMethods(m => m.Name == "Invoke", GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); else return null; } #endregion public static IType SkipModifiers(this IType ty) { while (ty is ModifiedType mt) { ty = mt.ElementType; } return ty; } #region GetType/Member /// <summary> /// Gets all type definitions in the compilation. /// This may include types from referenced assemblies that are not accessible in the main assembly. /// </summary> public static IEnumerable<ITypeDefinition> GetAllTypeDefinitions (this ICompilation compilation) { return compilation.Modules.SelectMany(a => a.TypeDefinitions); } /// <summary> /// Gets all top level type definitions in the compilation. /// This may include types from referenced assemblies that are not accessible in the main assembly. /// </summary> public static IEnumerable<ITypeDefinition> GetTopLevelTypeDefinitions (this ICompilation compilation) { return compilation.Modules.SelectMany(a => a.TopLevelTypeDefinitions); } #endregion #region Resolve on collections public static IReadOnlyList<IType> Resolve(this IList<ITypeReference> typeReferences, ITypeResolveContext context) { if (typeReferences == null) throw new ArgumentNullException("typeReferences"); if (typeReferences.Count == 0) return EmptyList<IType>.Instance; else return new ProjectedList<ITypeResolveContext, ITypeReference, IType>(context, typeReferences, (c, t) => t.Resolve(c)); } // There is intentionally no Resolve() overload for IList<IMemberReference>: the resulting IList<Member> would // contains nulls when there are resolve errors. #endregion #region IAssembly.GetTypeDefinition() /// <summary> /// Retrieves the specified type in this compilation. /// Returns an <see cref="UnknownType"/> if the type cannot be found in this compilation. /// </summary> /// <remarks> /// There can be multiple types with the same full name in a compilation, as a /// full type name is only unique per assembly. /// If there are multiple possible matches, this method will return just one of them. /// When possible, use <see cref="IModule.GetTypeDefinition"/> instead to /// retrieve a type from a specific assembly. /// </remarks> public static IType FindType(this ICompilation compilation, FullTypeName fullTypeName) { if (compilation == null) throw new ArgumentNullException("compilation"); foreach (IModule asm in compilation.Modules) { ITypeDefinition def = asm.GetTypeDefinition(fullTypeName); if (def != null) return def; } return new UnknownType(fullTypeName); } /// <summary> /// Gets the type definition for the specified unresolved type. /// Returns null if the unresolved type does not belong to this assembly. /// </summary> public static ITypeDefinition GetTypeDefinition(this IModule module, FullTypeName fullTypeName) { if (module == null) throw new ArgumentNullException("assembly"); TopLevelTypeName topLevelTypeName = fullTypeName.TopLevelTypeName; ITypeDefinition typeDef = module.GetTypeDefinition(topLevelTypeName); if (typeDef == null) return null; int typeParameterCount = topLevelTypeName.TypeParameterCount; for (int i = 0; i < fullTypeName.NestingLevel; i++) { string name = fullTypeName.GetNestedTypeName(i); typeParameterCount += fullTypeName.GetNestedTypeAdditionalTypeParameterCount(i); typeDef = FindNestedType(typeDef, name, typeParameterCount); if (typeDef == null) break; } return typeDef; } static ITypeDefinition FindNestedType(ITypeDefinition typeDef, string name, int typeParameterCount) { foreach (var nestedType in typeDef.NestedTypes) { if (nestedType.Name == name && nestedType.TypeParameterCount == typeParameterCount) return nestedType; } return null; } #endregion #region IEntity.GetAttribute /// <summary> /// Gets whether the entity has an attribute of the specified attribute type (or derived attribute types). /// </summary> /// <param name="entity">The entity on which the attributes are declared.</param> /// <param name="attributeType">The attribute type to look for.</param> /// <param name="inherit"> /// Specifies whether attributes inherited from base classes and base members /// (if the given <paramref name="entity"/> in an <c>override</c>) /// should be returned. /// </param> public static bool HasAttribute(this IEntity entity, KnownAttribute attrType, bool inherit=false) { return GetAttribute(entity, attrType, inherit) != null; } /// <summary> /// Gets the attribute of the specified attribute type (or derived attribute types). /// </summary> /// <param name="entity">The entity on which the attributes are declared.</param> /// <param name="attributeType">The attribute type to look for.</param> /// <param name="inherit"> /// Specifies whether attributes inherited from base classes and base members /// (if the given <paramref name="entity"/> in an <c>override</c>) /// should be returned. /// </param> /// <returns> /// Returns the attribute that was found; or <c>null</c> if none was found. /// If inherit is true, an from the entity itself will be returned if possible; /// and the base entity will only be searched if none exists. /// </returns> public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false) { return GetAttributes(entity, inherit).FirstOrDefault(a => a.AttributeType.IsKnownType(attributeType)); } /// <summary> /// Gets the attributes on the entity. /// </summary> /// <param name="entity">The entity on which the attributes are declared.</param> /// <param name="inherit"> /// Specifies whether attributes inherited from base classes and base members /// (if the given <paramref name="entity"/> in an <c>override</c>) /// should be returned. /// </param> /// <returns> /// Returns the list of attributes that were found. /// If inherit is true, attributes from the entity itself are returned first; /// followed by attributes inherited from the base entity. /// </returns> public static IEnumerable<IAttribute> GetAttributes(this IEntity entity, bool inherit) { if (inherit) { if (entity is ITypeDefinition td) { return InheritanceHelper.GetAttributes(td); } else if (entity is IMember m) { return InheritanceHelper.GetAttributes(m); } else { throw new NotSupportedException("Unknown entity type"); } } else { return entity.GetAttributes(); } } #endregion #region IParameter.GetAttribute /// <summary> /// Gets whether the parameter has an attribute of the specified attribute type (or derived attribute types). /// </summary> /// <param name="parameter">The parameter on which the attributes are declared.</param> /// <param name="attributeType">The attribute type to look for.</param> public static bool HasAttribute(this IParameter parameter, KnownAttribute attrType) { return GetAttribute(parameter, attrType) != null; } /// <summary> /// Gets the attribute of the specified attribute type (or derived attribute types). /// </summary> /// <param name="parameter">The parameter on which the attributes are declared.</param> /// <param name="attributeType">The attribute type to look for.</param> /// <returns> /// Returns the attribute that was found; or <c>null</c> if none was found. /// </returns> public static IAttribute GetAttribute(this IParameter parameter, KnownAttribute attributeType) { return parameter.GetAttributes().FirstOrDefault(a => a.AttributeType.IsKnownType(attributeType)); } #endregion #region IAssembly.GetTypeDefinition(string,string,int) /// <summary> /// Gets the type definition for a top-level type. /// </summary> /// <remarks>This method uses ordinal name comparison, not the compilation's name comparer.</remarks> public static ITypeDefinition GetTypeDefinition(this IModule module, string namespaceName, string name, int typeParameterCount = 0) { if (module == null) throw new ArgumentNullException ("assembly"); return module.GetTypeDefinition (new TopLevelTypeName (namespaceName, name, typeParameterCount)); } #endregion #region ResolveResult public static ISymbol GetSymbol(this ResolveResult rr) { if (rr is LocalResolveResult) { return ((LocalResolveResult)rr).Variable; } else if (rr is MemberResolveResult) { return ((MemberResolveResult)rr).Member; } else if (rr is TypeResolveResult) { return ((TypeResolveResult)rr).Type.GetDefinition(); } else if (rr is ConversionResolveResult) { return ((ConversionResolveResult)rr).Input.GetSymbol(); } return null; } #endregion public static IType GetElementTypeFromIEnumerable(this IType collectionType, ICompilation compilation, bool allowIEnumerator, out bool? isGeneric) { bool foundNonGenericIEnumerable = false; foreach (IType baseType in collectionType.GetAllBaseTypes()) { ITypeDefinition baseTypeDef = baseType.GetDefinition(); if (baseTypeDef != null) { KnownTypeCode typeCode = baseTypeDef.KnownTypeCode; if (typeCode == KnownTypeCode.IEnumerableOfT || (allowIEnumerator && typeCode == KnownTypeCode.IEnumeratorOfT)) { ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { isGeneric = true; return pt.GetTypeArgument(0); } } if (typeCode == KnownTypeCode.IEnumerable || (allowIEnumerator && typeCode == KnownTypeCode.IEnumerator)) foundNonGenericIEnumerable = true; } } // System.Collections.IEnumerable found in type hierarchy -> Object is element type. if (foundNonGenericIEnumerable) { isGeneric = false; return compilation.FindType(KnownTypeCode.Object); } isGeneric = null; return SpecialType.UnknownType; } public static bool FullNameIs(this IMember member, string type, string name) { return member.Name == name && member.DeclaringType?.FullName == type; } public static KnownAttribute IsBuiltinAttribute(this ITypeDefinition type) { return KnownAttributes.IsKnownAttributeType(type); } } }