|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions;
internal static partial class ITypeSymbolExtensions
{
public const string DefaultParameterName = "value";
public static bool IsIntegralType([NotNullWhen(returnValue: true)] this ITypeSymbol? type)
=> type?.SpecialType.IsIntegralType() == true;
public static bool IsSignedIntegralType([NotNullWhen(returnValue: true)] this ITypeSymbol? type)
=> type?.SpecialType.IsSignedIntegralType() == true;
public static bool CanAddNullCheck([NotNullWhen(returnValue: true)] this ITypeSymbol? type)
{
if (type == null)
return false;
var isNullableValueType = type.IsNullable();
var isNonNullableReferenceType = type.IsReferenceType && type.NullableAnnotation != NullableAnnotation.Annotated;
return isNullableValueType || isNonNullableReferenceType;
}
public static IList<INamedTypeSymbol> GetAllInterfacesIncludingThis(this ITypeSymbol type)
{
var allInterfaces = type.AllInterfaces;
if (type is INamedTypeSymbol namedType && namedType.TypeKind == TypeKind.Interface && !allInterfaces.Contains(namedType))
{
var result = new List<INamedTypeSymbol>(allInterfaces.Length + 1)
{
namedType
};
result.AddRange(allInterfaces);
return result;
}
return allInterfaces;
}
public static bool IsAbstractClass([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.TypeKind == TypeKind.Class && symbol.IsAbstract;
public static bool IsSystemVoid([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.SpecialType == SpecialType.System_Void;
public static bool IsNullable([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;
public static bool IsNonNullableValueType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol is { IsValueType: true } && !symbol.IsNullable();
public static bool IsNullable(
[NotNullWhen(true)] this ITypeSymbol? symbol,
[NotNullWhen(true)] out ITypeSymbol? underlyingType)
{
if (IsNullable(symbol))
{
underlyingType = ((INamedTypeSymbol)symbol).TypeArguments[0];
return true;
}
underlyingType = null;
return false;
}
public static bool IsModuleType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.TypeKind == TypeKind.Module;
public static bool IsInterfaceType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.TypeKind == TypeKind.Interface;
public static bool IsDelegateType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.TypeKind == TypeKind.Delegate;
public static bool IsFunctionPointerType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.TypeKind == TypeKind.FunctionPointer;
public static bool IsStructType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
=> symbol?.TypeKind == TypeKind.Struct;
public static bool IsAnonymousType([NotNullWhen(returnValue: true)] this INamedTypeSymbol? symbol)
=> symbol?.IsAnonymousType == true;
private static HashSet<INamedTypeSymbol> GetOriginalInterfacesAndTheirBaseInterfaces(
this ITypeSymbol type,
HashSet<INamedTypeSymbol>? symbols = null)
{
symbols ??= new HashSet<INamedTypeSymbol>(SymbolEquivalenceComparer.Instance);
foreach (var interfaceType in type.Interfaces)
{
symbols.Add(interfaceType.OriginalDefinition);
symbols.AddRange(interfaceType.AllInterfaces.Select(i => i.OriginalDefinition));
}
return symbols;
}
public static IEnumerable<ITypeSymbol> GetBaseTypesAndThis(this ITypeSymbol? type)
{
var current = type;
while (current != null)
{
yield return current;
current = current.BaseType;
}
}
public static IEnumerable<INamedTypeSymbol> GetBaseTypes(this ITypeSymbol? type)
{
var current = type?.BaseType;
while (current != null)
{
yield return current;
current = current.BaseType;
}
}
public static IEnumerable<ITypeSymbol> GetContainingTypesAndThis(this ITypeSymbol? type)
{
var current = type;
while (current != null)
{
yield return current;
current = current.ContainingType;
}
}
public static IEnumerable<INamedTypeSymbol> GetContainingTypes(this ITypeSymbol type)
{
var current = type.ContainingType;
while (current != null)
{
yield return current;
current = current.ContainingType;
}
}
// Determine if "type" inherits from "baseType", ignoring constructed types, optionally including interfaces,
// dealing only with original types.
public static bool InheritsFromOrEquals(
this ITypeSymbol type, ITypeSymbol baseType, bool includeInterfaces)
{
if (!includeInterfaces)
{
return InheritsFromOrEquals(type, baseType);
}
return type.GetBaseTypesAndThis().Concat(type.AllInterfaces).Contains(t => SymbolEquivalenceComparer.Instance.Equals(t, baseType));
}
// Determine if "type" inherits from "baseType", ignoring constructed types and interfaces, dealing
// only with original types.
public static bool InheritsFromOrEquals(
this ITypeSymbol type, ITypeSymbol baseType)
{
return type.GetBaseTypesAndThis().Contains(t => SymbolEquivalenceComparer.Instance.Equals(t, baseType));
}
// Determine if "type" inherits from or implements "baseType", ignoring constructed types, and dealing
// only with original types.
public static bool InheritsFromOrImplementsOrEqualsIgnoringConstruction(
this ITypeSymbol type, ITypeSymbol baseType)
{
var originalBaseType = baseType.OriginalDefinition;
type = type.OriginalDefinition;
if (SymbolEquivalenceComparer.Instance.Equals(type, originalBaseType))
{
return true;
}
IEnumerable<ITypeSymbol> baseTypes = (baseType.TypeKind == TypeKind.Interface) ? type.AllInterfaces : type.GetBaseTypes();
return baseTypes.Contains(t => SymbolEquivalenceComparer.Instance.Equals(t.OriginalDefinition, originalBaseType));
}
// Determine if "type" inherits from "baseType", ignoring constructed types, and dealing
// only with original types.
public static bool InheritsFromIgnoringConstruction(
this ITypeSymbol type, ITypeSymbol baseType)
{
var originalBaseType = baseType.OriginalDefinition;
// We could just call GetBaseTypes and foreach over it, but this
// is a hot path in Find All References. This avoid the allocation
// of the enumerator type.
var currentBaseType = type.BaseType;
while (currentBaseType != null)
{
if (SymbolEquivalenceComparer.Instance.Equals(currentBaseType.OriginalDefinition, originalBaseType))
{
return true;
}
currentBaseType = currentBaseType.BaseType;
}
return false;
}
public static bool ImplementsIgnoringConstruction(
this ITypeSymbol type, ITypeSymbol interfaceType)
{
var originalInterfaceType = interfaceType.OriginalDefinition;
return type.AllInterfaces.Any(static (t, originalInterfaceType) => SymbolEquivalenceComparer.Instance.Equals(t.OriginalDefinition, originalInterfaceType), originalInterfaceType);
}
public static bool Implements(
this ITypeSymbol type, ITypeSymbol interfaceType)
{
return type.AllInterfaces.Contains(t => SymbolEquivalenceComparer.Instance.Equals(t, interfaceType));
}
public static bool IsAttribute(this ITypeSymbol symbol)
{
for (var b = symbol.BaseType; b != null; b = b.BaseType)
{
if (b.MetadataName == "Attribute" &&
b.ContainingType == null &&
b.ContainingNamespace != null &&
b.ContainingNamespace.Name == "System" &&
b.ContainingNamespace.ContainingNamespace != null &&
b.ContainingNamespace.ContainingNamespace.IsGlobalNamespace)
{
return true;
}
}
return false;
}
public static bool IsFormattableStringOrIFormattable([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
{
return symbol?.MetadataName is nameof(FormattableString) or nameof(IFormattable)
&& symbol.ContainingType == null
&& symbol.ContainingNamespace?.Name == "System"
&& symbol.ContainingNamespace.ContainingNamespace?.IsGlobalNamespace == true;
}
public static bool IsUnexpressibleTypeParameterConstraint(
this ITypeSymbol typeSymbol, bool allowDelegateAndEnumConstraints = false)
{
if (typeSymbol.IsSealed || typeSymbol.IsValueType)
{
return true;
}
switch (typeSymbol.TypeKind)
{
case TypeKind.Array:
case TypeKind.Delegate:
return true;
}
switch (typeSymbol.SpecialType)
{
case SpecialType.System_Array or SpecialType.System_ValueType:
return true;
case SpecialType.System_Delegate:
case SpecialType.System_MulticastDelegate:
case SpecialType.System_Enum:
return !allowDelegateAndEnumConstraints;
}
return false;
}
public static bool IsNumericType([NotNullWhen(returnValue: true)] this ITypeSymbol? type)
{
if (type != null)
{
switch (type.SpecialType)
{
case SpecialType.System_Byte:
case SpecialType.System_SByte:
case SpecialType.System_Int16:
case SpecialType.System_UInt16:
case SpecialType.System_Int32:
case SpecialType.System_UInt32:
case SpecialType.System_Int64:
case SpecialType.System_UInt64:
case SpecialType.System_Single:
case SpecialType.System_Double:
case SpecialType.System_Decimal:
case SpecialType.System_IntPtr when type.IsNativeIntegerType:
case SpecialType.System_UIntPtr when type.IsNativeIntegerType:
return true;
}
}
return false;
}
public static Accessibility DetermineMinimalAccessibility(this ITypeSymbol typeSymbol)
=> typeSymbol.Accept(MinimalAccessibilityVisitor.Instance);
public static bool ContainsAnonymousType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
{
return symbol switch
{
IArrayTypeSymbol a => ContainsAnonymousType(a.ElementType),
IPointerTypeSymbol p => ContainsAnonymousType(p.PointedAtType),
INamedTypeSymbol n => ContainsAnonymousType(n),
_ => false,
};
}
private static bool ContainsAnonymousType(INamedTypeSymbol type)
{
if (type.IsAnonymousType)
return true;
foreach (var typeArg in type.GetAllTypeArguments())
{
if (ContainsAnonymousType(typeArg))
return true;
}
return false;
}
public static string CreateParameterName(this ITypeSymbol type, bool capitalize = false)
{
while (true)
{
switch (type)
{
case IArrayTypeSymbol arrayType:
type = arrayType.ElementType;
continue;
case IPointerTypeSymbol pointerType:
type = pointerType.PointedAtType;
continue;
}
break;
}
var shortName = GetParameterName(type);
return capitalize ? shortName.ToPascalCase() : shortName.ToCamelCase();
}
private static string GetParameterName(ITypeSymbol? type)
{
if (type == null || type.IsAnonymousType() || type.IsTupleType)
{
return DefaultParameterName;
}
var shortName = type.GetShortName();
return shortName.Length == 0
? DefaultParameterName
: shortName;
}
public static bool IsSpecialType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol)
{
if (symbol != null)
{
switch (symbol.SpecialType)
{
case SpecialType.System_Object:
case SpecialType.System_Void:
case SpecialType.System_Boolean:
case SpecialType.System_SByte:
case SpecialType.System_Byte:
case SpecialType.System_Decimal:
case SpecialType.System_Single:
case SpecialType.System_Double:
case SpecialType.System_Int16:
case SpecialType.System_Int32:
case SpecialType.System_Int64:
case SpecialType.System_Char:
case SpecialType.System_String:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
case SpecialType.System_IntPtr when symbol.IsNativeIntegerType:
case SpecialType.System_UIntPtr when symbol.IsNativeIntegerType:
return true;
}
}
return false;
}
public static bool CanSupportCollectionInitializer(this ITypeSymbol typeSymbol, ISymbol within)
{
return
typeSymbol.AllInterfaces.Any(static i => i.SpecialType == SpecialType.System_Collections_IEnumerable) &&
typeSymbol.GetBaseTypesAndThis()
.Union(typeSymbol.GetOriginalInterfacesAndTheirBaseInterfaces())
.SelectAccessibleMembers<IMethodSymbol>(WellKnownMemberNames.CollectionInitializerAddMethodName, within ?? typeSymbol)
.OfType<IMethodSymbol>()
.Any(m => m.Parameters.Any());
}
public static INamedTypeSymbol? GetDelegateType(this ITypeSymbol? typeSymbol, Compilation compilation)
{
if (typeSymbol != null)
{
var expressionOfT = compilation.ExpressionOfTType();
if (typeSymbol.OriginalDefinition.Equals(expressionOfT))
{
var typeArgument = ((INamedTypeSymbol)typeSymbol).TypeArguments[0];
return typeArgument as INamedTypeSymbol;
}
if (typeSymbol.IsDelegateType())
{
return typeSymbol as INamedTypeSymbol;
}
}
return null;
}
public static IEnumerable<T> GetAccessibleMembersInBaseTypes<T>(this ITypeSymbol containingType, ISymbol within) where T : class, ISymbol
{
if (containingType == null)
return [];
var types = containingType.GetBaseTypes();
return types.SelectMany(x => x.GetMembers().OfType<T>().Where(m => m.IsAccessibleWithin(within)));
}
public static ImmutableArray<T> GetAccessibleMembersInThisAndBaseTypes<T>(this ITypeSymbol? containingType, ISymbol within) where T : class, ISymbol
{
if (containingType == null)
{
return [];
}
return [.. containingType.GetBaseTypesAndThis().SelectAccessibleMembers<T>(within)];
}
public static ImmutableArray<T> GetAccessibleMembersInThisAndBaseTypes<T>(this ITypeSymbol? containingType, string memberName, ISymbol within) where T : class, ISymbol
{
if (containingType == null)
{
return [];
}
return [.. containingType.GetBaseTypesAndThis().SelectAccessibleMembers<T>(memberName, within)];
}
public static bool? AreMoreSpecificThan(this IList<ITypeSymbol> t1, IList<ITypeSymbol> t2)
{
if (t1.Count != t2.Count)
{
return null;
}
// For t1 to be more specific than t2, it has to be not less specific in every member,
// and more specific in at least one.
bool? result = null;
for (var i = 0; i < t1.Count; ++i)
{
var r = t1[i].IsMoreSpecificThan(t2[i]);
if (r == null)
{
// We learned nothing. Do nothing.
}
else if (result == null)
{
// We have found the first more specific type. See if
// all the rest on this side are not less specific.
result = r;
}
else if (result != r)
{
// We have more specific types on both left and right, so we
// cannot succeed in picking a better type list. Bail out now.
return null;
}
}
return result;
}
public static IEnumerable<T> SelectAccessibleMembers<T>(this IEnumerable<ITypeSymbol>? types, ISymbol within) where T : class, ISymbol
{
if (types == null)
{
return [];
}
return types.SelectMany(x => x.GetMembers().OfType<T>().Where(m => m.IsAccessibleWithin(within)));
}
private static IEnumerable<T> SelectAccessibleMembers<T>(this IEnumerable<ITypeSymbol>? types, string memberName, ISymbol within) where T : class, ISymbol
{
if (types == null)
{
return [];
}
return types.SelectMany(x => x.GetMembers(memberName).OfType<T>().Where(m => m.IsAccessibleWithin(within)));
}
private static bool? IsMoreSpecificThan(this ITypeSymbol t1, ITypeSymbol t2)
{
// SPEC: A type parameter is less specific than a non-type parameter.
var isTypeParameter1 = t1 is ITypeParameterSymbol;
var isTypeParameter2 = t2 is ITypeParameterSymbol;
if (isTypeParameter1 && !isTypeParameter2)
{
return false;
}
if (!isTypeParameter1 && isTypeParameter2)
{
return true;
}
if (isTypeParameter1)
{
Debug.Assert(isTypeParameter2);
return null;
}
if (t1.TypeKind != t2.TypeKind)
{
return null;
}
// There is an identity conversion between the types and they are both substitutions on type parameters.
// They had better be the same kind.
// UNDONE: Strip off the dynamics.
// SPEC: An array type is more specific than another
// SPEC: array type (with the same number of dimensions)
// SPEC: if the element type of the first is
// SPEC: more specific than the element type of the second.
if (t1 is IArrayTypeSymbol arr1)
{
var arr2 = (IArrayTypeSymbol)t2;
// We should not have gotten here unless there were identity conversions
// between the two types.
return arr1.ElementType.IsMoreSpecificThan(arr2.ElementType);
}
// SPEC EXTENSION: We apply the same rule to pointer types.
if (t1 is IPointerTypeSymbol)
{
var p1 = (IPointerTypeSymbol)t1;
var p2 = (IPointerTypeSymbol)t2;
return p1.PointedAtType.IsMoreSpecificThan(p2.PointedAtType);
}
// SPEC: A constructed type is more specific than another
// SPEC: constructed type (with the same number of type arguments) if at least one type
// SPEC: argument is more specific and no type argument is less specific than the
// SPEC: corresponding type argument in the other.
var n1 = t1 as INamedTypeSymbol;
var n2 = t2 as INamedTypeSymbol;
if (n1 == null)
{
return null;
}
// We should not have gotten here unless there were identity conversions between the
// two types.
var allTypeArgs1 = n1.GetAllTypeArguments();
var allTypeArgs2 = n2.GetAllTypeArguments();
return allTypeArgs1.AreMoreSpecificThan(allTypeArgs2);
}
public static bool IsOrDerivesFromExceptionType([NotNullWhen(returnValue: true)] this ITypeSymbol? type, Compilation compilation)
{
if (type != null)
{
switch (type.Kind)
{
case SymbolKind.NamedType:
foreach (var baseType in type.GetBaseTypesAndThis())
{
if (baseType.Equals(compilation.ExceptionType()))
{
return true;
}
}
break;
case SymbolKind.TypeParameter:
foreach (var constraint in ((ITypeParameterSymbol)type).ConstraintTypes)
{
if (constraint.IsOrDerivesFromExceptionType(compilation))
{
return true;
}
}
break;
}
}
return false;
}
public static bool IsEnumType([NotNullWhen(true)] this ITypeSymbol? type)
=> IsEnumType(type, out _);
public static bool IsEnumType([NotNullWhen(true)] this ITypeSymbol? type, [NotNullWhen(true)] out INamedTypeSymbol? enumType)
{
if (type != null && type.IsValueType && type.TypeKind == TypeKind.Enum)
{
enumType = (INamedTypeSymbol)type;
return true;
}
enumType = null;
return false;
}
public static bool? IsMutableValueType(this ITypeSymbol type)
{
if (type.IsNullable(out var underlyingType))
{
// Nullable<T> can only be mutable if T is mutable. This case ensures types like 'int?' are treated as
// immutable.
type = underlyingType;
}
switch (type.SpecialType)
{
case SpecialType.System_Boolean:
case SpecialType.System_Char:
case SpecialType.System_SByte:
case SpecialType.System_Byte:
case SpecialType.System_Int16:
case SpecialType.System_UInt16:
case SpecialType.System_Int32:
case SpecialType.System_UInt32:
case SpecialType.System_Int64:
case SpecialType.System_UInt64:
case SpecialType.System_Decimal:
case SpecialType.System_Single:
case SpecialType.System_Double:
return false;
case SpecialType.System_IntPtr:
case SpecialType.System_UIntPtr:
return false;
case SpecialType.System_DateTime:
return false;
default:
break;
}
if (type.IsErrorType())
{
return null;
}
if (type.TypeKind != TypeKind.Struct)
{
return false;
}
var hasPrivateField = false;
foreach (var member in type.GetMembers())
{
if (member is not IFieldSymbol fieldSymbol)
{
continue;
}
hasPrivateField |= fieldSymbol.DeclaredAccessibility == Accessibility.Private;
if (!fieldSymbol.IsConst && !fieldSymbol.IsReadOnly && !fieldSymbol.IsStatic)
{
return true;
}
}
if (!hasPrivateField)
{
// Some reference assemblies omit information about private fields. If we can't be sure the field is
// immutable, treat it as potentially mutable.
foreach (var attributeData in type.ContainingAssembly.GetAttributes())
{
if (attributeData.AttributeClass?.Name == nameof(ReferenceAssemblyAttribute)
&& attributeData.AttributeClass.ToNameDisplayString() == typeof(ReferenceAssemblyAttribute).FullName)
{
return null;
}
}
}
return false;
}
public static bool IsDisposable([NotNullWhen(returnValue: true)] this ITypeSymbol? type, [NotNullWhen(returnValue: true)] ITypeSymbol? iDisposableType)
=> iDisposableType != null &&
(Equals(iDisposableType, type) ||
type?.AllInterfaces.Contains(iDisposableType) == true);
public static ITypeSymbol WithNullableAnnotationFrom(this ITypeSymbol type, ITypeSymbol symbolForNullableAnnotation)
=> type.WithNullableAnnotation(symbolForNullableAnnotation.NullableAnnotation);
[return: NotNullIfNotNull(parameterName: nameof(symbol))]
public static ITypeSymbol? RemoveNullableIfPresent(this ITypeSymbol? symbol)
{
if (symbol.IsNullable(out var underlyingType))
{
return underlyingType;
}
return symbol;
}
public static bool IsSpanOrReadOnlySpan([NotNullWhen(true)] this ITypeSymbol? type)
=> type.IsSpan() || type.IsReadOnlySpan();
public static bool IsSpan([NotNullWhen(true)] this ITypeSymbol? type)
=> type is INamedTypeSymbol
{
Name: nameof(Span<int>),
TypeArguments.Length: 1,
ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true }
};
public static bool IsReadOnlySpan([NotNullWhen(true)] this ISymbol? symbol)
=> symbol is INamedTypeSymbol
{
Name: nameof(ReadOnlySpan<int>),
TypeArguments.Length: 1,
ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true }
};
public static bool IsInlineArray([NotNullWhen(true)] this ITypeSymbol? type)
=> type is INamedTypeSymbol namedType &&
namedType.OriginalDefinition.GetAttributes().Any(static a => a.AttributeClass?.SpecialType == SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute);
[return: NotNullIfNotNull(parameterName: nameof(type))]
public static ITypeSymbol? RemoveUnavailableTypeParameters(
this ITypeSymbol? type,
Compilation compilation,
IEnumerable<ITypeParameterSymbol> availableTypeParameters)
{
return type?.RemoveUnavailableTypeParameters(compilation, availableTypeParameters.Select(t => t.Name).ToSet());
}
[return: NotNullIfNotNull(parameterName: nameof(type))]
private static ITypeSymbol? RemoveUnavailableTypeParameters(
this ITypeSymbol? type,
Compilation compilation,
ISet<string> availableTypeParameterNames)
{
return type?.Accept(new UnavailableTypeParameterRemover(compilation, availableTypeParameterNames));
}
[return: NotNullIfNotNull(parameterName: nameof(type))]
public static ITypeSymbol? RemoveAnonymousTypes(
this ITypeSymbol? type,
Compilation compilation)
{
return type?.Accept(new AnonymousTypeRemover(compilation));
}
[return: NotNullIfNotNull(parameterName: nameof(type))]
public static ITypeSymbol? RemoveUnnamedErrorTypes(
this ITypeSymbol? type,
Compilation compilation)
{
return type?.Accept(new UnnamedErrorTypeRemover(compilation));
}
public static IList<ITypeParameterSymbol> GetReferencedMethodTypeParameters(
this ITypeSymbol? type, IList<ITypeParameterSymbol>? result = null)
{
result ??= [];
type?.Accept(new CollectTypeParameterSymbolsVisitor(result, onlyMethodTypeParameters: true));
return result;
}
public static IList<ITypeParameterSymbol> GetReferencedTypeParameters(
this ITypeSymbol? type, IList<ITypeParameterSymbol>? result = null)
{
result ??= [];
type?.Accept(new CollectTypeParameterSymbolsVisitor(result, onlyMethodTypeParameters: false));
return result;
}
[return: NotNullIfNotNull(parameterName: nameof(type))]
public static ITypeSymbol? SubstituteTypes<TType1, TType2>(
this ITypeSymbol? type,
IDictionary<TType1, TType2> mapping,
Compilation compilation)
where TType1 : ITypeSymbol
where TType2 : ITypeSymbol
{
return type.SubstituteTypes(mapping, new CompilationTypeGenerator(compilation));
}
[return: NotNullIfNotNull(parameterName: nameof(type))]
public static ITypeSymbol? SubstituteTypes<TType1, TType2>(
this ITypeSymbol? type,
IDictionary<TType1, TType2> mapping,
ITypeGenerator typeGenerator)
where TType1 : ITypeSymbol
where TType2 : ITypeSymbol
{
return type?.Accept(new SubstituteTypesVisitor<TType1, TType2>(mapping, typeGenerator));
}
}
|