|
// 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 Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal static partial class TypeSymbolExtensions
{
private sealed class VisitTypeData
{
public Symbol? Symbol;
public CompoundUseSiteInfo<AssemblySymbol> UseSiteInfo;
}
private static readonly ObjectPool<VisitTypeData> s_visitTypeDataPool
= new ObjectPool<VisitTypeData>(() => new VisitTypeData(), size: 4);
public static bool ImplementsInterface(this TypeSymbol subType, TypeSymbol superInterface, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
foreach (NamedTypeSymbol @interface in subType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo))
{
if (@interface.IsInterface && TypeSymbol.Equals(@interface, superInterface, TypeCompareKind.ConsiderEverything2))
{
return true;
}
}
return false;
}
public static bool CanBeAssignedNull(this TypeSymbol type)
{
return type.IsReferenceType || type.IsPointerOrFunctionPointer() || type.IsNullableType();
}
public static bool CanContainNull(this TypeSymbol type)
{
// unbound type parameters might contain null, even though they cannot be *assigned* null.
return !type.IsValueType || type.IsNullableTypeOrTypeParameter();
}
public static bool CanBeConst(this TypeSymbol typeSymbol)
{
RoslynDebug.Assert((object)typeSymbol != null);
return typeSymbol.IsReferenceType || typeSymbol.IsEnumType() || typeSymbol.SpecialType.CanBeConst() || typeSymbol.IsNativeIntegerType;
}
/// <summary>
/// Assuming that nullable annotations are enabled:
/// T => true
/// T where T : struct => false
/// T where T : class => false
/// T where T : class? => true
/// T where T : IComparable => true
/// T where T : IComparable? => true
/// T where T : notnull => true
/// </summary>
/// <remarks>
/// In C#9, annotations are allowed regardless of constraints.
/// </remarks>
public static bool IsTypeParameterDisallowingAnnotationInCSharp8(this TypeSymbol type)
{
if (type.TypeKind != TypeKind.TypeParameter)
{
return false;
}
var typeParameter = (TypeParameterSymbol)type;
// https://github.com/dotnet/roslyn/issues/30056: Test `where T : unmanaged`. See
// UninitializedNonNullableFieldTests.TypeParameterConstraints for instance.
return !typeParameter.IsValueType && !(typeParameter.IsReferenceType && typeParameter.IsNotNullable == true);
}
/// <summary>
/// Assuming that nullable annotations are enabled:
/// T => true
/// T where T : struct => false
/// T where T : class => false
/// T where T : class? => true
/// T where T : IComparable => false
/// T where T : IComparable? => true
/// </summary>
public static bool IsPossiblyNullableReferenceTypeTypeParameter(this TypeSymbol type)
{
return type is TypeParameterSymbol { IsValueType: false, IsNotNullable: false };
}
public static bool IsNonNullableValueType(this TypeSymbol typeArgument)
{
if (!typeArgument.IsValueType)
{
return false;
}
return !IsNullableTypeOrTypeParameter(typeArgument);
}
public static bool IsVoidType(this TypeSymbol type)
{
return type.SpecialType == SpecialType.System_Void;
}
public static bool IsNullableTypeOrTypeParameter(this TypeSymbol? type)
{
if (type is null)
{
return false;
}
if (type.TypeKind == TypeKind.TypeParameter)
{
var constraintTypes = ((TypeParameterSymbol)type).ConstraintTypesNoUseSiteDiagnostics;
foreach (var constraintType in constraintTypes)
{
if (constraintType.Type.IsNullableTypeOrTypeParameter())
{
return true;
}
}
return false;
}
return type.IsNullableType();
}
/// <summary>
/// Is this System.Nullable`1 type, or its substitution.
///
/// To check whether a type is System.Nullable`1 or is a type parameter constrained to System.Nullable`1
/// use <see cref="TypeSymbolExtensions.IsNullableTypeOrTypeParameter" /> instead.
/// </summary>
public static bool IsNullableType(this TypeSymbol type)
{
return type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;
}
public static bool IsValidNullableTypeArgument(this TypeSymbol type)
{
return type is { IsValueType: true }
&& !type.IsNullableType()
&& !type.IsPointerOrFunctionPointer()
&& !type.IsRestrictedType();
}
public static TypeSymbol GetNullableUnderlyingType(this TypeSymbol type)
{
return type.GetNullableUnderlyingTypeWithAnnotations().Type;
}
public static bool IsNullableType(this TypeSymbol? type, [NotNullWhen(true)] out TypeSymbol? underlyingType)
{
if (type is NamedTypeSymbol nt
&& nt.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
{
underlyingType = nt.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type;
return true;
}
underlyingType = null;
return false;
}
public static TypeWithAnnotations GetNullableUnderlyingTypeWithAnnotations(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
RoslynDebug.Assert(IsNullableType(type));
RoslynDebug.Assert(type is NamedTypeSymbol); //not testing Kind because it may be an ErrorType
return ((NamedTypeSymbol)type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
}
public static TypeSymbol StrippedType(this TypeSymbol type)
{
return type.IsNullableType() ? type.GetNullableUnderlyingType() : type;
}
public static TypeSymbol EnumUnderlyingTypeOrSelf(this TypeSymbol type)
{
return type.GetEnumUnderlyingType() ?? type;
}
public static bool IsNativeIntegerOrNullableThereof(this TypeSymbol? type)
{
return type?.StrippedType().IsNativeIntegerType == true;
}
public static bool IsObjectType(this TypeSymbol type)
{
return type.SpecialType == SpecialType.System_Object;
}
public static bool IsStringType(this TypeSymbol type)
{
return type.SpecialType == SpecialType.System_String;
}
public static bool IsCharType(this TypeSymbol type)
{
return type.SpecialType == SpecialType.System_Char;
}
public static bool IsIntegralType(this TypeSymbol type)
{
return type.SpecialType.IsIntegralType();
}
public static NamedTypeSymbol? GetEnumUnderlyingType(this TypeSymbol? type)
{
return (type is NamedTypeSymbol namedType) ? namedType.EnumUnderlyingType : null;
}
public static bool IsEnumType(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.Enum;
}
public static bool IsValidEnumType(this TypeSymbol type)
{
var underlyingType = type.GetEnumUnderlyingType();
// SpecialType will be None if the underlying type is invalid.
return underlyingType is not null && underlyingType.SpecialType.CanOptimizeBehavior();
}
/// <summary>
/// Determines if the given type is a valid attribute parameter type.
/// </summary>
/// <param name="type">Type to validated</param>
/// <param name="compilation">compilation</param>
/// <returns></returns>
public static bool IsValidAttributeParameterType(this TypeSymbol type, CSharpCompilation compilation)
{
return GetAttributeParameterTypedConstantKind(type, compilation) != TypedConstantKind.Error;
}
/// <summary>
/// Gets the typed constant kind for the given attribute parameter type.
/// </summary>
/// <param name="type">Type to validated</param>
/// <param name="compilation">compilation</param>
/// <returns>TypedConstantKind for the attribute parameter type.</returns>
public static TypedConstantKind GetAttributeParameterTypedConstantKind(this TypeSymbol type, CSharpCompilation compilation)
{
// Spec (17.1.3)
// The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:
// 1) One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
// 2) The type object.
// 3) The type System.Type.
// 4) An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility.
// 5) Single-dimensional arrays of the above types.
// A constructor argument or public field which does not have one of these types, cannot be used as a positional
// or named parameter in an attribute specification.
TypedConstantKind kind = TypedConstantKind.Error;
if ((object)type == null)
{
return TypedConstantKind.Error;
}
if (type.Kind == SymbolKind.ArrayType)
{
var arrayType = (ArrayTypeSymbol)type;
if (!arrayType.IsSZArray)
{
return TypedConstantKind.Error;
}
kind = TypedConstantKind.Array;
type = arrayType.ElementType;
}
// enum or enum[]
if (type.IsEnumType())
{
// SPEC VIOLATION: Dev11 doesn't enforce either the Enum type or its enclosing types (if any) to have public accessibility.
// We will be consistent with Dev11 behavior.
if (kind == TypedConstantKind.Error)
{
// set only if kind is not already set (i.e. its not an array of enum)
kind = TypedConstantKind.Enum;
}
type = type.GetEnumUnderlyingType()!;
}
var typedConstantKind = TypedConstant.GetTypedConstantKind(type, compilation);
switch (typedConstantKind)
{
case TypedConstantKind.Array:
case TypedConstantKind.Enum:
case TypedConstantKind.Error:
return TypedConstantKind.Error;
default:
if (kind == TypedConstantKind.Array || kind == TypedConstantKind.Enum)
{
// Array/Enum type with valid element/underlying type
return kind;
}
return typedConstantKind;
}
}
public static bool IsValidExtensionParameterType(this TypeSymbol type)
{
switch (type.TypeKind)
{
case TypeKind.Pointer:
case TypeKind.Dynamic:
case TypeKind.FunctionPointer:
return false;
default:
return true;
}
}
public static bool IsInterfaceType(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.Kind == SymbolKind.NamedType && ((NamedTypeSymbol)type).IsInterface;
}
public static bool IsClassType(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.Class;
}
public static bool IsStructType(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.Struct;
}
public static bool IsErrorType(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.Kind == SymbolKind.ErrorType;
}
public static bool IsMethodTypeParameter(this TypeParameterSymbol p)
{
return p.ContainingSymbol.Kind == SymbolKind.Method;
}
public static bool IsDynamic(this TypeSymbol type)
{
return type.TypeKind == TypeKind.Dynamic;
}
public static bool IsTypeParameter(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.TypeParameter;
}
public static bool IsArray(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.Array;
}
public static bool IsSZArray(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.Array && ((ArrayTypeSymbol)type).IsSZArray;
}
internal static bool IsArrayInterface(this TypeSymbol type, out TypeWithAnnotations typeArgument)
{
if (type is NamedTypeSymbol
{
OriginalDefinition.SpecialType:
SpecialType.System_Collections_Generic_IEnumerable_T or
SpecialType.System_Collections_Generic_IReadOnlyCollection_T or
SpecialType.System_Collections_Generic_IReadOnlyList_T or
SpecialType.System_Collections_Generic_ICollection_T or
SpecialType.System_Collections_Generic_IList_T,
TypeArgumentsWithAnnotationsNoUseSiteDiagnostics: [var typeArg]
})
{
typeArgument = typeArg;
return true;
}
typeArgument = default;
return false;
}
public static bool IsFunctionPointer(this TypeSymbol type)
{
return type.TypeKind == TypeKind.FunctionPointer;
}
public static bool IsPointerOrFunctionPointer(this TypeSymbol type)
{
switch (type.TypeKind)
{
case TypeKind.Pointer:
case TypeKind.FunctionPointer:
return true;
default:
return false;
}
}
internal static ImmutableArray<NamedTypeSymbol> GetAllInterfacesOrEffectiveInterfaces(this TypeSymbol type)
{
switch (type.TypeKind)
{
case TypeKind.Class:
case TypeKind.Struct:
return type.AllInterfacesNoUseSiteDiagnostics;
case TypeKind.TypeParameter:
{
var typeParameter = (TypeParameterSymbol)type;
return typeParameter.EffectiveBaseClassNoUseSiteDiagnostics.AllInterfacesNoUseSiteDiagnostics.Concat(typeParameter.AllEffectiveInterfacesNoUseSiteDiagnostics);
}
default:
return ImmutableArray<NamedTypeSymbol>.Empty;
}
}
// If the type is a delegate type, it returns it. If the type is an
// expression tree type associated with a delegate type, it returns
// the delegate type. Otherwise, null.
public static NamedTypeSymbol? GetDelegateType(this TypeSymbol? type)
{
if (type is null) return null;
if (type.IsExpressionTree())
{
type = ((NamedTypeSymbol)type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type;
}
return type.IsDelegateType() ? (NamedTypeSymbol)type : null;
}
public static TypeSymbol? GetDelegateOrFunctionPointerType(this TypeSymbol? type)
{
return (TypeSymbol?)GetDelegateType(type) ?? type as FunctionPointerTypeSymbol;
}
/// <summary>
/// Returns true if the type is constructed from a generic type named "System.Linq.Expressions.Expression"
/// with one type parameter.
/// </summary>
public static bool IsExpressionTree(this TypeSymbol type)
{
return type.IsGenericOrNonGenericExpressionType(out bool isGenericType) && isGenericType;
}
/// <summary>
/// Returns true if the type is a non-generic type named "System.Linq.Expressions.Expression"
/// or "System.Linq.Expressions.LambdaExpression".
/// </summary>
public static bool IsNonGenericExpressionType(this TypeSymbol type)
{
return type.IsGenericOrNonGenericExpressionType(out bool isGenericType) && !isGenericType;
}
/// <summary>
/// Returns true if the type is constructed from a generic type named "System.Linq.Expressions.Expression"
/// with one type parameter, or if the type is a non-generic type named "System.Linq.Expressions.Expression"
/// or "System.Linq.Expressions.LambdaExpression".
/// </summary>
public static bool IsGenericOrNonGenericExpressionType(this TypeSymbol _type, out bool isGenericType)
{
if (_type.OriginalDefinition is NamedTypeSymbol type)
{
switch (type.Name)
{
case "Expression":
if (IsNamespaceName(type.ContainingSymbol, s_expressionsNamespaceName))
{
if (type.Arity == 0)
{
isGenericType = false;
return true;
}
if (type.Arity == 1 &&
type.MangleName)
{
isGenericType = true;
return true;
}
}
break;
case "LambdaExpression":
if (IsNamespaceName(type.ContainingSymbol, s_expressionsNamespaceName) &&
type.Arity == 0)
{
isGenericType = false;
return true;
}
break;
}
}
isGenericType = false;
return false;
}
/// <summary>
/// return true if the type is constructed from a generic interface that
/// might be implemented by an array.
/// </summary>
public static bool IsPossibleArrayGenericInterface(this TypeSymbol type)
{
if (!(type is NamedTypeSymbol t))
{
return false;
}
t = t.OriginalDefinition;
SpecialType st = t.SpecialType;
if (st == SpecialType.System_Collections_Generic_IList_T ||
st == SpecialType.System_Collections_Generic_ICollection_T ||
st == SpecialType.System_Collections_Generic_IEnumerable_T ||
st == SpecialType.System_Collections_Generic_IReadOnlyList_T ||
st == SpecialType.System_Collections_Generic_IReadOnlyCollection_T)
{
return true;
}
return false;
}
internal static bool IsErrorOrRefLikeOrAllowsRefLikeType(this TypeSymbol type)
{
return type.IsErrorType() || type.IsRefLikeOrAllowsRefLikeType();
}
internal static bool IsRefLikeOrAllowsRefLikeType(this TypeSymbol type)
{
return type is { IsRefLikeType: true } or TypeParameterSymbol { AllowsRefLikeType: true };
}
private static readonly string[] s_expressionsNamespaceName = { "Expressions", "Linq", MetadataHelpers.SystemString, "" };
private static bool IsNamespaceName(Symbol symbol, string[] names)
{
if (symbol.Kind != SymbolKind.Namespace)
{
return false;
}
for (int i = 0; i < names.Length; i++)
{
if ((object)symbol == null || symbol.Name != names[i]) return false;
symbol = symbol.ContainingSymbol;
}
return true;
}
public static bool IsDelegateType(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
return type.TypeKind == TypeKind.Delegate;
}
public static ImmutableArray<ParameterSymbol> DelegateParameters(this TypeSymbol type)
{
var invokeMethod = type.DelegateInvokeMethod();
if (invokeMethod is null)
{
return default(ImmutableArray<ParameterSymbol>);
}
return invokeMethod.Parameters;
}
public static ImmutableArray<ParameterSymbol> DelegateOrFunctionPointerParameters(this TypeSymbol type)
{
Debug.Assert(type is FunctionPointerTypeSymbol || type.IsDelegateType());
if (type is FunctionPointerTypeSymbol { Signature: { Parameters: var functionPointerParameters } })
{
return functionPointerParameters;
}
else
{
return type.DelegateParameters();
}
}
public static bool TryGetElementTypesWithAnnotationsIfTupleType(this TypeSymbol type, out ImmutableArray<TypeWithAnnotations> elementTypes)
{
if (type.IsTupleType)
{
elementTypes = ((NamedTypeSymbol)type).TupleElementTypesWithAnnotations;
return true;
}
// source not a tuple
elementTypes = default(ImmutableArray<TypeWithAnnotations>);
return false;
}
public static MethodSymbol? DelegateInvokeMethod(this TypeSymbol type)
{
RoslynDebug.Assert((object)type != null);
RoslynDebug.Assert(type.IsDelegateType() || type.IsExpressionTree());
return type.GetDelegateType()!.DelegateInvokeMethod;
}
/// <summary>
/// Return the default value constant for the given type,
/// or null if the default value is not a constant.
/// </summary>
public static ConstantValue? GetDefaultValue(this TypeSymbol type)
{
// SPEC: A default-value-expression is a constant expression (§7.19) if the type
// SPEC: is a reference type or a type parameter that is known to be a reference type (§10.1.5).
// SPEC: In addition, a default-value-expression is a constant expression if the type is
// SPEC: one of the following value types:
// SPEC: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, or any enumeration type.
RoslynDebug.Assert((object)type != null);
if (type.IsErrorType())
{
return null;
}
if (type.IsReferenceType)
{
return ConstantValue.Null;
}
if (type.IsValueType)
{
type = type.EnumUnderlyingTypeOrSelf();
switch (type.SpecialType)
{
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_IntPtr when type.IsNativeIntegerType:
case SpecialType.System_UIntPtr when type.IsNativeIntegerType:
case SpecialType.System_Char:
case SpecialType.System_Boolean:
case SpecialType.System_Single:
case SpecialType.System_Double:
case SpecialType.System_Decimal:
return ConstantValue.Default(type.SpecialType);
}
}
return null;
}
public static SpecialType GetSpecialTypeSafe(this TypeSymbol? type)
{
return type is object ? type.SpecialType : SpecialType.None;
}
public static bool IsAtLeastAsVisibleAs(this TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
var visitTypeData = s_visitTypeDataPool.Allocate();
try
{
visitTypeData.UseSiteInfo = useSiteInfo;
visitTypeData.Symbol = sym;
var result = type.VisitType(static (type1, arg, unused) => IsTypeLessVisibleThan(type1, arg.Symbol!, ref arg.UseSiteInfo),
arg: visitTypeData,
canDigThroughNullable: true); // System.Nullable is public
useSiteInfo = visitTypeData.UseSiteInfo;
return result is null;
}
finally
{
visitTypeData.UseSiteInfo = default;
visitTypeData.Symbol = null;
s_visitTypeDataPool.Free(visitTypeData);
}
}
private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
switch (type.TypeKind)
{
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Interface:
case TypeKind.Enum:
case TypeKind.Delegate:
case TypeKind.Submission:
return !IsAsRestrictive((NamedTypeSymbol)type, sym, ref useSiteInfo);
default:
return false;
}
}
/// <summary>
/// Visit the given type and, in the case of compound types, visit all "sub type"
/// (such as A in A[], or { A<T>, T, U } in A<T>.B<U>) invoking 'predicate'
/// with the type and 'arg' at each sub type. If the predicate returns true for any type,
/// traversal stops and that type is returned from this method. Otherwise if traversal
/// completes without the predicate returning true for any type, this method returns null.
/// </summary>
public static TypeSymbol? VisitType<T>(
this TypeSymbol type,
Func<TypeSymbol, T, bool, bool> predicate,
T arg,
bool canDigThroughNullable = false,
bool visitCustomModifiers = false)
{
return VisitType(
typeWithAnnotationsOpt: default,
type: type,
typeWithAnnotationsPredicate: null,
typePredicate: predicate,
arg,
canDigThroughNullable,
visitCustomModifiers: visitCustomModifiers);
}
/// <summary>
/// Visit the given type and, in the case of compound types, visit all "sub type".
/// One of the predicates will be invoked at each type. If the type is a
/// TypeWithAnnotations, <paramref name="typeWithAnnotationsPredicate"/>
/// will be invoked; otherwise <paramref name="typePredicate"/> will be invoked.
/// If the corresponding predicate returns true for any type,
/// traversal stops and that type is returned from this method. Otherwise if traversal
/// completes without the predicate returning true for any type, this method returns null.
/// </summary>
/// <param name="useDefaultType">If true, use <see cref="TypeWithAnnotations.DefaultType"/>
/// instead of <see cref="TypeWithAnnotations.Type"/> to avoid early resolution of nullable types</param>
public static TypeSymbol? VisitType<T>(
this TypeWithAnnotations typeWithAnnotationsOpt,
TypeSymbol? type,
Func<TypeWithAnnotations, T, bool, bool>? typeWithAnnotationsPredicate,
Func<TypeSymbol, T, bool, bool>? typePredicate,
T arg,
bool canDigThroughNullable = false,
bool useDefaultType = false,
bool visitCustomModifiers = false)
{
RoslynDebug.Assert(typeWithAnnotationsOpt.HasType == (type is null));
RoslynDebug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types");
RoslynDebug.Assert(canDigThroughNullable == false || visitCustomModifiers == false);
RoslynDebug.Assert(visitCustomModifiers == false || typePredicate is { });
// In order to handle extremely "deep" types like "int[][][][][][][][][]...[]"
// or int*****************...* we implement manual tail recursion rather than
// doing the natural recursion.
while (true)
{
TypeSymbol current = type ?? (useDefaultType ? typeWithAnnotationsOpt.DefaultType : typeWithAnnotationsOpt.Type);
bool isNestedNamedType = false;
// Visit containing types from outer-most to inner-most.
switch (current.TypeKind)
{
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Interface:
case TypeKind.Enum:
case TypeKind.Delegate:
{
var containingType = current.ContainingType;
if ((object)containingType != null)
{
isNestedNamedType = true;
var result = VisitType(default, containingType, typeWithAnnotationsPredicate, typePredicate, arg, canDigThroughNullable, useDefaultType, visitCustomModifiers);
if (result is object)
{
return result;
}
}
}
break;
case TypeKind.Submission:
RoslynDebug.Assert((object)current.ContainingType == null);
break;
}
if (typeWithAnnotationsOpt.HasType && typeWithAnnotationsPredicate != null)
{
if (typeWithAnnotationsPredicate(typeWithAnnotationsOpt, arg, isNestedNamedType))
{
return current;
}
}
else if (typePredicate != null)
{
if (typePredicate(current, arg, isNestedNamedType))
{
return current;
}
}
if (visitCustomModifiers && typeWithAnnotationsOpt.HasType)
{
foreach (var customModifier in typeWithAnnotationsOpt.CustomModifiers)
{
var result = VisitType(
typeWithAnnotationsOpt: default, type: ((CSharpCustomModifier)customModifier).ModifierSymbol,
typeWithAnnotationsPredicate, typePredicate, arg,
canDigThroughNullable, useDefaultType, visitCustomModifiers);
if (result is object)
{
return result;
}
}
}
TypeWithAnnotations next;
switch (current.TypeKind)
{
case TypeKind.Dynamic:
case TypeKind.TypeParameter:
case TypeKind.Submission:
case TypeKind.Enum:
return null;
case TypeKindInternal.FunctionType:
if (((FunctionTypeSymbol)current).GetInternalDelegateType() is not { } delegateType)
{
return null;
}
current = delegateType;
goto case TypeKind.Delegate;
case TypeKind.Error:
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Interface:
case TypeKind.Delegate:
if (current.IsAnonymousType)
{
var anonymous = (AnonymousTypeManager.AnonymousTypeOrDelegatePublicSymbol)current;
var fields = anonymous.TypeDescriptor.Fields;
if (fields.IsEmpty)
{
return null;
}
int i;
for (i = 0; i < fields.Length - 1; i++)
{
// Let's try to avoid early resolution of nullable types
(TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(fields[i].TypeWithAnnotations, canDigThroughNullable);
var result = VisitType(
typeWithAnnotationsOpt: nextTypeWithAnnotations,
type: nextType,
typeWithAnnotationsPredicate,
typePredicate,
arg,
canDigThroughNullable,
useDefaultType,
visitCustomModifiers);
if (result is object)
{
return result;
}
}
next = fields[i].TypeWithAnnotations;
}
else
{
var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics;
if (typeArguments.IsEmpty)
{
return null;
}
int i;
for (i = 0; i < typeArguments.Length - 1; i++)
{
// Let's try to avoid early resolution of nullable types
(TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeArguments[i], canDigThroughNullable);
var result = VisitType(
typeWithAnnotationsOpt: nextTypeWithAnnotations,
type: nextType,
typeWithAnnotationsPredicate,
typePredicate,
arg,
canDigThroughNullable,
useDefaultType,
visitCustomModifiers);
if (result is object)
{
return result;
}
}
next = typeArguments[i];
}
break;
case TypeKind.Array:
next = ((ArrayTypeSymbol)current).ElementTypeWithAnnotations;
break;
case TypeKind.Pointer:
next = ((PointerTypeSymbol)current).PointedAtTypeWithAnnotations;
break;
case TypeKind.FunctionPointer:
{
var result = visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable, visitCustomModifiers, out next);
if (result is object)
{
return result;
}
break;
}
default:
throw ExceptionUtilities.UnexpectedValue(current.TypeKind);
}
// Let's try to avoid early resolution of nullable types
typeWithAnnotationsOpt = canDigThroughNullable ? default : next;
type = canDigThroughNullable ? next.NullableUnderlyingTypeOrSelf : null;
}
static (TypeWithAnnotations, TypeSymbol?) getNextIterationElements(TypeWithAnnotations type, bool canDigThroughNullable)
=> canDigThroughNullable ? (default(TypeWithAnnotations), type.NullableUnderlyingTypeOrSelf) : (type, null);
static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func<TypeWithAnnotations, T, bool, bool>? typeWithAnnotationsPredicate, Func<TypeSymbol, T, bool, bool>? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, bool visitCustomModifiers, out TypeWithAnnotations next)
{
MethodSymbol currentPointer = type.Signature;
if (currentPointer.ParameterCount == 0)
{
next = currentPointer.ReturnTypeWithAnnotations;
return null;
}
var result = VisitType(
typeWithAnnotationsOpt: canDigThroughNullable ? default : currentPointer.ReturnTypeWithAnnotations,
type: canDigThroughNullable ? currentPointer.ReturnTypeWithAnnotations.NullableUnderlyingTypeOrSelf : null,
typeWithAnnotationsPredicate,
typePredicate,
arg,
canDigThroughNullable,
useDefaultType,
visitCustomModifiers);
if (result is object)
{
next = default;
return result;
}
int i;
for (i = 0; i < currentPointer.ParameterCount - 1; i++)
{
(TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(currentPointer.Parameters[i].TypeWithAnnotations, canDigThroughNullable);
result = VisitType(
typeWithAnnotationsOpt: nextTypeWithAnnotations,
type: nextType,
typeWithAnnotationsPredicate,
typePredicate,
arg,
canDigThroughNullable,
useDefaultType,
visitCustomModifiers);
if (result is object)
{
next = default;
return result;
}
}
next = currentPointer.Parameters[i].TypeWithAnnotations;
return null;
}
}
internal static bool IsAsRestrictive(this Symbol s1, Symbol sym2, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Accessibility acc1 = s1.DeclaredAccessibility;
if (acc1 == Accessibility.Public)
{
return true;
}
for (Symbol s2 = sym2; s2.Kind != SymbolKind.Namespace; s2 = s2.ContainingSymbol)
{
Accessibility acc2 = s2.DeclaredAccessibility;
switch (acc1)
{
case Accessibility.Internal:
{
// If s2 is private or internal, and within the same assembly as s1,
// then this is at least as restrictive as s1's internal.
if ((acc2 == Accessibility.Private || acc2 == Accessibility.Internal || acc2 == Accessibility.ProtectedAndInternal) && s2.ContainingAssembly.HasInternalAccessTo(s1.ContainingAssembly))
{
return true;
}
break;
}
case Accessibility.ProtectedAndInternal:
// Since s1 is private protected, s2 must pass the test for being both more restrictive than internal and more restrictive than protected.
// We first do the "internal" test (copied from above), then if it passes we continue with the "protected" test.
if ((acc2 == Accessibility.Private || acc2 == Accessibility.Internal || acc2 == Accessibility.ProtectedAndInternal) && s2.ContainingAssembly.HasInternalAccessTo(s1.ContainingAssembly))
{
// passed the internal test; now do the test for the protected case
goto case Accessibility.Protected;
}
break;
case Accessibility.Protected:
{
var parent1 = s1.ContainingType;
if ((object)parent1 == null)
{
// not helpful
}
else if (acc2 == Accessibility.Private)
{
// if s2 is private and within s1's parent or within a subclass of s1's parent,
// then this is at least as restrictive as s1's protected.
for (var parent2 = s2.ContainingType; (object)parent2 != null; parent2 = parent2.ContainingType)
{
if (parent1.IsAccessibleViaInheritance(parent2, ref useSiteInfo))
{
return true;
}
}
}
else if (acc2 == Accessibility.Protected || acc2 == Accessibility.ProtectedAndInternal)
{
// if s2 is protected, and it's parent is a subclass (or the same as) s1's parent
// then this is at least as restrictive as s1's protected
var parent2 = s2.ContainingType;
if ((object)parent2 != null && parent1.IsAccessibleViaInheritance(parent2, ref useSiteInfo))
{
return true;
}
}
break;
}
case Accessibility.ProtectedOrInternal:
{
var parent1 = s1.ContainingType;
if ((object)parent1 == null)
{
break;
}
switch (acc2)
{
case Accessibility.Private:
// if s2 is private and within a subclass of s1's parent,
// or within the same assembly as s1
// then this is at least as restrictive as s1's internal protected.
if (s2.ContainingAssembly.HasInternalAccessTo(s1.ContainingAssembly))
{
return true;
}
for (var parent2 = s2.ContainingType; (object)parent2 != null; parent2 = parent2.ContainingType)
{
if (parent1.IsAccessibleViaInheritance(parent2, ref useSiteInfo))
{
return true;
}
}
break;
case Accessibility.Internal:
// If s2 is in the same assembly as s1, then this is more restrictive
// than s1's internal protected.
if (s2.ContainingAssembly.HasInternalAccessTo(s1.ContainingAssembly))
{
return true;
}
break;
case Accessibility.Protected:
// if s2 is protected, and it's parent is a subclass (or the same as) s1's parent
// then this is at least as restrictive as s1's internal protected
if (parent1.IsAccessibleViaInheritance(s2.ContainingType, ref useSiteInfo))
{
return true;
}
break;
case Accessibility.ProtectedAndInternal:
// if s2 is private protected, and it's parent is a subclass (or the same as) s1's parent
// or its in the same assembly as s1, then this is at least as restrictive as s1's protected
if (s2.ContainingAssembly.HasInternalAccessTo(s1.ContainingAssembly) ||
parent1.IsAccessibleViaInheritance(s2.ContainingType, ref useSiteInfo))
{
return true;
}
break;
case Accessibility.ProtectedOrInternal:
// if s2 is internal protected, and it's parent is a subclass (or the same as) s1's parent
// and its in the same assembly as s1, then this is at least as restrictive as s1's protected
if (s2.ContainingAssembly.HasInternalAccessTo(s1.ContainingAssembly) &&
parent1.IsAccessibleViaInheritance(s2.ContainingType, ref useSiteInfo))
{
return true;
}
break;
}
break;
}
case Accessibility.Private:
if (acc2 == Accessibility.Private)
{
// if s2 is private, and it is within s1's parent, then this is at
// least as restrictive as s1's private.
NamedTypeSymbol parent1 = s1.ContainingType;
if ((object)parent1 == null)
{
break;
}
var parent1OriginalDefinition = parent1.OriginalDefinition;
for (var parent2 = s2.ContainingType; (object)parent2 != null; parent2 = parent2.ContainingType)
{
if (ReferenceEquals(parent2.OriginalDefinition, parent1OriginalDefinition) || parent1OriginalDefinition.TypeKind == TypeKind.Submission && parent2.TypeKind == TypeKind.Submission)
{
return true;
}
}
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(acc1);
}
}
return false;
}
public static bool IsUnboundGenericType(this TypeSymbol type)
{
return type is NamedTypeSymbol { IsUnboundGenericType: true };
}
public static bool IsTopLevelType(this NamedTypeSymbol type)
{
return (object)type.ContainingType == null;
}
/// <summary>
/// (null TypeParameterSymbol "parameter"): Checks if the given type is a type parameter
/// or its referent type is a type parameter (array/pointer) or contains a type parameter (aggregate type)
/// (non-null TypeParameterSymbol "parameter"): above + also checks if the type parameter
/// is the same as "parameter"
/// </summary>
public static bool ContainsTypeParameter(this TypeSymbol type, TypeParameterSymbol? parameter = null)
{
var result = type.VisitType(s_containsTypeParameterPredicate, parameter);
return result is object;
}
private static readonly Func<TypeSymbol, TypeParameterSymbol?, bool, bool> s_containsTypeParameterPredicate =
(type, parameter, unused) => type.TypeKind == TypeKind.TypeParameter && (parameter is null || TypeSymbol.Equals(type, parameter, TypeCompareKind.ConsiderEverything2));
public static bool ContainsTypeParameter(this TypeSymbol type, MethodSymbol parameterContainer)
{
RoslynDebug.Assert((object)parameterContainer != null);
var result = type.VisitType(s_isTypeParameterWithSpecificContainerPredicate, parameterContainer);
return result is object;
}
private static readonly Func<TypeSymbol, Symbol, bool, bool> s_isTypeParameterWithSpecificContainerPredicate =
(type, parameterContainer, unused) => type.TypeKind == TypeKind.TypeParameter && (object)type.ContainingSymbol == (object)parameterContainer;
public static bool ContainsTypeParameters(this TypeSymbol type, HashSet<TypeParameterSymbol> parameters)
{
var result = type.VisitType(s_containsTypeParametersPredicate, parameters);
return result is object;
}
private static readonly Func<TypeSymbol, HashSet<TypeParameterSymbol>, bool, bool> s_containsTypeParametersPredicate =
(type, parameters, unused) => type.TypeKind == TypeKind.TypeParameter && parameters.Contains((TypeParameterSymbol)type);
public static bool ContainsMethodTypeParameter(this TypeSymbol type)
{
var result = type.VisitType(s_containsMethodTypeParameterPredicate, null);
return result is object;
}
private static readonly Func<TypeSymbol, object?, bool, bool> s_containsMethodTypeParameterPredicate =
(type, _, _) => type.TypeKind == TypeKind.TypeParameter && type.ContainingSymbol is MethodSymbol;
/// <summary>
/// Return true if the type contains any dynamic type reference.
/// </summary>
public static bool ContainsDynamic(this TypeSymbol type)
{
var result = type.VisitType(s_containsDynamicPredicate, null, canDigThroughNullable: true);
return result is object;
}
private static readonly Func<TypeSymbol, object?, bool, bool> s_containsDynamicPredicate = (type, unused1, unused2) => type.TypeKind == TypeKind.Dynamic;
internal static bool ContainsNativeIntegerWrapperType(this TypeSymbol type)
{
var result = type.VisitType((type, unused1, unused2) => type.IsNativeIntegerWrapperType, (object?)null, canDigThroughNullable: true);
return result is object;
}
internal static bool ContainsNativeIntegerWrapperType(this TypeWithAnnotations type)
{
return type.Type?.ContainsNativeIntegerWrapperType() == true;
}
internal static bool ContainsErrorType(this TypeSymbol type)
{
var result = type.VisitType((type, unused1, unused2) => type.IsErrorType(), (object?)null, canDigThroughNullable: true);
return result is object;
}
/// <summary>
/// Return true if the type contains any tuples.
/// </summary>
internal static bool ContainsTuple(this TypeSymbol type) =>
type.VisitType((TypeSymbol t, object? _1, bool _2) => t.IsTupleType, null) is object;
/// <summary>
/// Return true if the type contains any tuples with element names.
/// </summary>
internal static bool ContainsTupleNames(this TypeSymbol type) =>
type.VisitType((TypeSymbol t, object? _1, bool _2) => !t.TupleElementNames.IsDefault, null) is object;
/// <summary>
/// Return true if the type contains any function pointer types.
/// </summary>
internal static bool ContainsFunctionPointer(this TypeSymbol type) =>
type.VisitType((TypeSymbol t, object? _, bool _) => t.IsFunctionPointer(), null) is object;
internal static bool ContainsPointer(this TypeSymbol type) =>
type.VisitType((TypeSymbol t, object? _, bool _) => t.TypeKind is TypeKind.Pointer or TypeKind.FunctionPointer, null) is object;
/// <summary>
/// Guess the non-error type that the given type was intended to represent.
/// If the type itself is not an error type, then it will be returned.
/// Otherwise, the underlying type (if any) of the error type will be
/// returned.
/// </summary>
/// <remarks>
/// Any non-null type symbol returned is guaranteed not to be an error type.
///
/// It is possible to pass in a constructed type and received back an
/// unconstructed type. This can occur when the type passed in was
/// constructed from an error type - the underlying definition will be
/// available, but there won't be a good way to "re-substitute" back up
/// to the level of the specified type.
/// </remarks>
internal static TypeSymbol? GetNonErrorGuess(this TypeSymbol type)
{
var result = ExtendedErrorTypeSymbol.ExtractNonErrorType(type);
RoslynDebug.Assert((object?)result == null || !result.IsErrorType());
return result;
}
/// <summary>
/// Guess the non-error type kind that the given type was intended to represent,
/// if possible. If not, return TypeKind.Error.
/// </summary>
internal static TypeKind GetNonErrorTypeKindGuess(this TypeSymbol type)
{
return ExtendedErrorTypeSymbol.ExtractNonErrorTypeKind(type);
}
/// <summary>
/// Returns true if the type was a valid switch expression type in C# 6. We use this test to determine
/// whether or not we should attempt a user-defined conversion from the type to a C# 6 switch governing
/// type, which we support for compatibility with C# 6 and earlier.
/// </summary>
internal static bool IsValidV6SwitchGoverningType(this TypeSymbol type, bool isTargetTypeOfUserDefinedOp = false)
{
// SPEC: The governing type of a switch statement is established by the switch expression.
// SPEC: 1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
// SPEC: long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
// SPEC: corresponding to one of these types, then that is the governing type of the switch statement.
// SPEC: 2) Otherwise, exactly one user-defined implicit conversion must exist from the
// SPEC: type of the switch expression to one of the following possible governing types:
// SPEC: sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
// SPEC: corresponding to one of those types
RoslynDebug.Assert((object)type != null);
if (type.IsNullableType())
{
type = type.GetNullableUnderlyingType();
}
// User-defined implicit conversion with target type as Enum type is not valid.
if (!isTargetTypeOfUserDefinedOp)
{
type = type.EnumUnderlyingTypeOrSelf();
}
switch (type.SpecialType)
{
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_Char:
case SpecialType.System_String:
return true;
case SpecialType.System_Boolean:
// User-defined implicit conversion with target type as bool type is not valid.
return !isTargetTypeOfUserDefinedOp;
}
return false;
}
internal static bool IsSpan(this TypeSymbol type)
{
return type is NamedTypeSymbol
{
Name: "Span",
IsValueType: true,
IsRefLikeType: true,
Arity: 1,
ContainingType: null,
ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true },
};
}
internal static bool IsReadOnlySpan(this TypeSymbol type)
{
return type is NamedTypeSymbol
{
Name: "ReadOnlySpan",
IsValueType: true,
IsRefLikeType: true,
Arity: 1,
ContainingType: null,
ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true },
};
}
internal static bool IsSpanChar(this TypeSymbol type)
{
return type is NamedTypeSymbol
{
ContainingNamespace: { Name: "System", ContainingNamespace: { IsGlobalNamespace: true } },
MetadataName: "Span`1",
TypeArgumentsWithAnnotationsNoUseSiteDiagnostics: { Length: 1 } arguments,
}
&& arguments[0].SpecialType == SpecialType.System_Char;
}
internal static bool IsReadOnlySpanChar(this TypeSymbol type)
{
return type is NamedTypeSymbol
{
ContainingNamespace: { Name: "System", ContainingNamespace: { IsGlobalNamespace: true } },
MetadataName: "ReadOnlySpan`1",
TypeArgumentsWithAnnotationsNoUseSiteDiagnostics: { Length: 1 } arguments,
}
&& arguments[0].SpecialType == SpecialType.System_Char;
}
internal static bool IsSpanOrReadOnlySpanChar(this TypeSymbol type)
{
return type is NamedTypeSymbol
{
ContainingNamespace: { Name: "System", ContainingNamespace: { IsGlobalNamespace: true } },
MetadataName: "ReadOnlySpan`1" or "Span`1",
TypeArgumentsWithAnnotationsNoUseSiteDiagnostics: { Length: 1 } arguments,
}
&& arguments[0].SpecialType == SpecialType.System_Char;
}
#pragma warning disable CA1200 // Avoid using cref tags with a prefix
/// <summary>
/// Returns true if the type is one of the restricted types, namely: <see cref="T:System.TypedReference"/>,
/// <see cref="T:System.ArgIterator"/>, or <see cref="T:System.RuntimeArgumentHandle"/>.
/// or a ref-like type.
/// </summary>
#pragma warning restore CA1200 // Avoid using cref tags with a prefix
internal static bool IsRestrictedType(this TypeSymbol type,
bool ignoreSpanLikeTypes = false)
{
// See Dev10 C# compiler, "type.cpp", bool Type::isSpecialByRefType() const
RoslynDebug.Assert((object)type != null);
switch (type.SpecialType)
{
case SpecialType.System_TypedReference:
case SpecialType.System_ArgIterator:
case SpecialType.System_RuntimeArgumentHandle:
return true;
}
return ignoreSpanLikeTypes ? false : type.IsRefLikeOrAllowsRefLikeType();
}
public static bool IsIntrinsicType(this TypeSymbol type)
{
switch (type.SpecialType)
{
case SpecialType.System_Boolean:
case SpecialType.System_Char:
case SpecialType.System_SByte:
case SpecialType.System_Int16:
case SpecialType.System_Int32:
case SpecialType.System_Int64:
case SpecialType.System_Byte:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
case SpecialType.System_IntPtr when type.IsNativeIntegerType:
case SpecialType.System_UIntPtr when type.IsNativeIntegerType:
case SpecialType.System_Single:
case SpecialType.System_Double:
// NOTE: VB treats System.DateTime as an intrinsic, while C# does not.
//case SpecialType.System_DateTime:
case SpecialType.System_Decimal:
return true;
default:
return false;
}
}
public static bool IsPartial(this TypeSymbol type)
{
return type is SourceNamedTypeSymbol { IsPartial: true };
}
public static bool HasFileLocalTypes(this TypeSymbol type)
{
var foundType = type.VisitType(predicate: (type, _, _) => type is NamedTypeSymbol { IsFileLocal: true }, arg: (object?)null);
return foundType is not null;
}
internal static string? GetFileLocalTypeMetadataNamePrefix(this NamedTypeSymbol type)
{
if (type.AssociatedFileIdentifier is not FileIdentifier identifier)
{
return null;
}
return GeneratedNames.MakeFileTypeMetadataNamePrefix(identifier.DisplayFilePath, identifier.FilePathChecksumOpt);
}
public static bool IsPointerType(this TypeSymbol type)
{
return type is PointerTypeSymbol;
}
internal static int FixedBufferElementSizeInBytes(this TypeSymbol type)
{
return type.SpecialType.FixedBufferElementSizeInBytes();
}
// check that its type is allowed for Volatile
internal static bool IsValidVolatileFieldType(this TypeSymbol type)
{
switch (type.TypeKind)
{
case TypeKind.Struct:
return type.SpecialType.IsValidVolatileFieldType();
case TypeKind.Array:
case TypeKind.Class:
case TypeKind.Delegate:
case TypeKind.Dynamic:
case TypeKind.Error:
case TypeKind.Interface:
case TypeKind.Pointer:
case TypeKind.FunctionPointer:
return true;
case TypeKind.Enum:
return ((NamedTypeSymbol)type).EnumUnderlyingType.SpecialType.IsValidVolatileFieldType();
case TypeKind.TypeParameter:
return type.IsReferenceType;
case TypeKind.Submission:
throw ExceptionUtilities.UnexpectedValue(type.TypeKind);
}
return false;
}
/// <summary>
/// Add this instance to the set of checked types. Returns true
/// if this was added, false if the type was already in the set.
/// </summary>
public static bool MarkCheckedIfNecessary(this TypeSymbol type, ref HashSet<TypeSymbol> checkedTypes)
{
if (checkedTypes == null)
{
checkedTypes = new HashSet<TypeSymbol>();
}
return checkedTypes.Add(type);
}
internal static bool IsVoidPointer(this TypeSymbol type)
{
return type is PointerTypeSymbol p && p.PointedAtType.IsVoidType();
}
/// <summary>
/// These special types are structs that contain fields of the same type
/// (e.g. <see cref="System.Int32"/> contains an instance field of type <see cref="System.Int32"/>).
/// </summary>
internal static bool IsPrimitiveRecursiveStruct(this TypeSymbol t)
{
return t.SpecialType.IsPrimitiveRecursiveStruct();
}
/// <summary>
/// Compute a hash code for the constructed type. The return value will be
/// non-zero so callers can used zero to represent an uninitialized value.
/// </summary>
internal static int ComputeHashCode(this NamedTypeSymbol type)
{
RoslynDebug.Assert(!type.Equals(type.OriginalDefinition, TypeCompareKind.AllIgnoreOptions) || wasConstructedForAnnotations(type));
if (wasConstructedForAnnotations(type))
{
// A type that uses its own type parameters as type arguments was constructed only for the purpose of adding annotations.
// In that case we'll use the hash from the definition.
return type.OriginalDefinition.GetHashCode();
}
int code = type.OriginalDefinition.GetHashCode();
code = Hash.Combine(type.ContainingType, code);
// Unconstructed type may contain alpha-renamed type parameters while
// may still be considered equal, we do not want to give different hashcode to such types.
//
// Example:
// Having original type A<U>.B<V> we create two _unconstructed_ types
// A<int>.B<V'>
// A<int>.B<V">
// Note that V' and V" are type parameters substituted via alpha-renaming of original V
// These are different objects, but represent the same "type parameter at index 1"
//
// In short - we are not interested in the type parameters of unconstructed types.
if ((object)type.ConstructedFrom != (object)type)
{
foreach (var arg in type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics)
{
code = Hash.Combine(arg.Type, code);
}
}
// 0 may be used by the caller to indicate the hashcode is not
// initialized. If we computed 0 for the hashcode, tweak it.
if (code == 0)
{
code++;
}
return code;
static bool wasConstructedForAnnotations(NamedTypeSymbol type)
{
do
{
var typeArguments = type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics;
var typeParameters = type.OriginalDefinition.TypeParameters;
for (int i = 0; i < typeArguments.Length; i++)
{
if (!typeParameters[i].Equals(
typeArguments[i].Type.OriginalDefinition,
TypeCompareKind.ConsiderEverything))
{
return false;
}
}
type = type.ContainingType;
}
while (type is object && !type.IsDefinition);
return true;
}
}
/// <summary>
/// If we are in a COM PIA with embedInteropTypes enabled we should turn properties and methods
/// that have the type and return type of object, respectively, into type dynamic. If the requisite conditions
/// are fulfilled, this method returns a dynamic type. If not, it returns the original type.
/// </summary>
/// <param name="type">A property type or method return type to be checked for dynamification.</param>
/// <param name="containingType">Containing type.</param>
/// <returns></returns>
public static TypeSymbol AsDynamicIfNoPia(this TypeSymbol type, NamedTypeSymbol containingType)
{
return type.TryAsDynamicIfNoPia(containingType, out TypeSymbol? result) ? result : type;
}
public static bool TryAsDynamicIfNoPia(this TypeSymbol type, NamedTypeSymbol containingType, [NotNullWhen(true)] out TypeSymbol? result)
{
if (type.SpecialType == SpecialType.System_Object)
{
AssemblySymbol assembly = containingType.ContainingAssembly;
if ((object)assembly != null &&
assembly.IsLinked &&
containingType.IsComImport)
{
result = DynamicTypeSymbol.Instance;
return true;
}
}
result = null;
return false;
}
/// <summary>
/// Type variables are never considered reference types by the verifier.
/// </summary>
internal static bool IsVerifierReference(this TypeSymbol type)
{
return type.IsReferenceType && type.TypeKind != TypeKind.TypeParameter;
}
/// <summary>
/// Type variables are never considered value types by the verifier.
/// </summary>
internal static bool IsVerifierValue(this TypeSymbol type)
{
return type.IsValueType && type.TypeKind != TypeKind.TypeParameter;
}
/// <summary>
/// Return all of the type parameters in this type and enclosing types,
/// from outer-most to inner-most type.
/// </summary>
internal static ImmutableArray<TypeParameterSymbol> GetAllTypeParameters(this NamedTypeSymbol type)
{
// Avoid allocating a builder in the common case.
if ((object)type.ContainingType == null)
{
return type.TypeParameters;
}
var builder = ArrayBuilder<TypeParameterSymbol>.GetInstance();
type.GetAllTypeParameters(builder);
return builder.ToImmutableAndFree();
}
/// <summary>
/// Return all of the type parameters in this type and enclosing types,
/// from outer-most to inner-most type.
/// </summary>
internal static void GetAllTypeParameters(this NamedTypeSymbol type, ArrayBuilder<TypeParameterSymbol> result)
{
var containingType = type.ContainingType;
if ((object)containingType != null)
{
containingType.GetAllTypeParameters(result);
}
result.AddRange(type.TypeParameters);
}
/// <summary>
/// Return the nearest type parameter with the given name in
/// this type or any enclosing type.
/// </summary>
internal static TypeParameterSymbol? FindEnclosingTypeParameter(this NamedTypeSymbol type, string name)
{
var allTypeParameters = ArrayBuilder<TypeParameterSymbol>.GetInstance();
type.GetAllTypeParameters(allTypeParameters);
TypeParameterSymbol? result = null;
foreach (TypeParameterSymbol tpEnclosing in allTypeParameters)
{
if (name == tpEnclosing.Name)
{
result = tpEnclosing;
break;
}
}
allTypeParameters.Free();
return result;
}
/// <summary>
/// Return the nearest type parameter with the given name in
/// this symbol or any enclosing symbol.
/// </summary>
internal static TypeParameterSymbol? FindEnclosingTypeParameter(this Symbol methodOrType, string name)
{
while (methodOrType != null)
{
switch (methodOrType.Kind)
{
case SymbolKind.Method:
case SymbolKind.NamedType:
case SymbolKind.ErrorType:
case SymbolKind.Field:
case SymbolKind.Property:
case SymbolKind.Event:
break;
default:
return null;
}
foreach (var typeParameter in methodOrType.GetMemberTypeParameters())
{
if (typeParameter.Name == name)
{
return typeParameter;
}
}
methodOrType = methodOrType.ContainingSymbol;
}
return null;
}
/// <summary>
/// Return true if the fully qualified name of the type's containing symbol
/// matches the given name. This method avoids string concatenations
/// in the common case where the type is a top-level type.
/// </summary>
internal static bool HasNameQualifier(this NamedTypeSymbol type, string qualifiedName)
{
const StringComparison comparison = StringComparison.Ordinal;
var container = type.ContainingSymbol;
if (container.Kind != SymbolKind.Namespace)
{
// Nested type. For simplicity, compare qualified name to SymbolDisplay result.
return string.Equals(container.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat), qualifiedName, comparison);
}
var @namespace = (NamespaceSymbol)container;
if (@namespace.IsGlobalNamespace)
{
return qualifiedName.Length == 0;
}
return HasNamespaceName(@namespace, qualifiedName, comparison, length: qualifiedName.Length);
}
private static bool HasNamespaceName(NamespaceSymbol @namespace, string namespaceName, StringComparison comparison, int length)
{
if (length == 0)
{
return false;
}
var container = @namespace.ContainingNamespace;
int separator = namespaceName.LastIndexOf('.', length - 1, length);
int offset = 0;
if (separator >= 0)
{
if (container.IsGlobalNamespace)
{
return false;
}
if (!HasNamespaceName(container, namespaceName, comparison, length: separator))
{
return false;
}
int n = separator + 1;
offset = n;
length -= n;
}
else if (!container.IsGlobalNamespace)
{
return false;
}
var name = @namespace.Name;
return (name.Length == length) && (string.Compare(name, 0, namespaceName, offset, length, comparison) == 0);
}
internal static bool IsNonGenericTaskType(this TypeSymbol type, CSharpCompilation compilation)
{
var namedType = type as NamedTypeSymbol;
if (namedType is null || namedType.Arity != 0)
{
return false;
}
if ((object)namedType == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task))
{
return true;
}
if (namedType.IsVoidType())
{
return false;
}
return namedType.IsCustomTaskType(builderArgument: out _);
}
internal static bool IsGenericTaskType(this TypeSymbol type, CSharpCompilation compilation)
{
if (!(type is NamedTypeSymbol { Arity: 1 } namedType))
{
return false;
}
if ((object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T))
{
return true;
}
return namedType.IsCustomTaskType(builderArgument: out _);
}
internal static bool IsIAsyncEnumerableType(this TypeSymbol type, CSharpCompilation compilation)
{
if (!(type is NamedTypeSymbol { Arity: 1 } namedType))
{
return false;
}
return (object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T);
}
internal static bool IsIAsyncEnumeratorType(this TypeSymbol type, CSharpCompilation compilation)
{
if (!(type is NamedTypeSymbol { Arity: 1 } namedType))
{
return false;
}
return (object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T);
}
/// <summary>
/// Returns true if the type is generic or non-generic custom task-like type due to the
/// [AsyncMethodBuilder(typeof(B))] attribute. It returns the "B".
/// </summary>
/// <remarks>
/// For the Task types themselves, this method might return true or false depending on mscorlib.
/// The definition of "custom task-like type" is one that has an [AsyncMethodBuilder(typeof(B))] attribute,
/// no more, no less. Validation of builder type B is left for elsewhere. This method returns B
/// without validation of any kind.
/// </remarks>
internal static bool IsCustomTaskType(this NamedTypeSymbol type, [NotNullWhen(true)] out TypeSymbol? builderArgument)
{
RoslynDebug.Assert((object)type != null);
var arity = type.Arity;
if (arity < 2)
{
return type.HasAsyncMethodBuilderAttribute(out builderArgument);
}
builderArgument = null;
return false;
}
/// <summary>
/// Replace Task-like types with Task types.
/// </summary>
internal static TypeSymbol NormalizeTaskTypes(this TypeSymbol type, CSharpCompilation compilation)
{
NormalizeTaskTypesInType(compilation, ref type);
return type;
}
/// <summary>
/// Replace Task-like types with Task types. Returns true if there were changes.
/// </summary>
private static bool NormalizeTaskTypesInType(CSharpCompilation compilation, ref TypeSymbol type)
{
switch (type.Kind)
{
case SymbolKind.NamedType:
case SymbolKind.ErrorType:
{
var namedType = (NamedTypeSymbol)type;
var changed = NormalizeTaskTypesInNamedType(compilation, ref namedType);
type = namedType;
return changed;
}
case SymbolKind.ArrayType:
{
var arrayType = (ArrayTypeSymbol)type;
var changed = NormalizeTaskTypesInArray(compilation, ref arrayType);
type = arrayType;
return changed;
}
case SymbolKind.PointerType:
{
var pointerType = (PointerTypeSymbol)type;
var changed = NormalizeTaskTypesInPointer(compilation, ref pointerType);
type = pointerType;
return changed;
}
case SymbolKind.FunctionPointerType:
{
var functionPointerType = (FunctionPointerTypeSymbol)type;
var changed = NormalizeTaskTypesInFunctionPointer(compilation, ref functionPointerType);
type = functionPointerType;
return changed;
}
}
return false;
}
private static bool NormalizeTaskTypesInType(CSharpCompilation compilation, ref TypeWithAnnotations typeWithAnnotations)
{
var type = typeWithAnnotations.Type;
if (NormalizeTaskTypesInType(compilation, ref type))
{
typeWithAnnotations = TypeWithAnnotations.Create(type, customModifiers: typeWithAnnotations.CustomModifiers);
return true;
}
return false;
}
private static bool NormalizeTaskTypesInNamedType(CSharpCompilation compilation, ref NamedTypeSymbol type)
{
bool hasChanged = false;
if (!type.IsDefinition)
{
RoslynDebug.Assert(type.IsGenericType);
var typeArgumentsBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
type.GetAllTypeArguments(typeArgumentsBuilder, ref discardedUseSiteInfo);
for (int i = 0; i < typeArgumentsBuilder.Count; i++)
{
var typeWithModifier = typeArgumentsBuilder[i];
var typeArgNormalized = typeWithModifier.Type;
if (NormalizeTaskTypesInType(compilation, ref typeArgNormalized))
{
hasChanged = true;
// Preserve custom modifiers but without normalizing those types.
typeArgumentsBuilder[i] = TypeWithAnnotations.Create(typeArgNormalized, customModifiers: typeWithModifier.CustomModifiers);
}
}
if (hasChanged)
{
var originalType = type;
var originalDefinition = originalType.OriginalDefinition;
var typeParameters = originalDefinition.GetAllTypeParameters();
var typeMap = new TypeMap(typeParameters, typeArgumentsBuilder.ToImmutable(), allowAlpha: true);
type = typeMap.SubstituteNamedType(originalDefinition).WithTupleDataFrom(originalType);
}
typeArgumentsBuilder.Free();
}
if (type.OriginalDefinition.IsCustomTaskType(builderArgument: out _))
{
int arity = type.Arity;
RoslynDebug.Assert(arity < 2);
var taskType = compilation.GetWellKnownType(
arity == 0 ?
WellKnownType.System_Threading_Tasks_Task :
WellKnownType.System_Threading_Tasks_Task_T);
if (taskType.TypeKind == TypeKind.Error)
{
// Skip if Task types are not available.
return false;
}
type = arity == 0 ?
taskType :
taskType.Construct(
ImmutableArray.Create(type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]),
unbound: false);
hasChanged = true;
}
return hasChanged;
}
private static bool NormalizeTaskTypesInArray(CSharpCompilation compilation, ref ArrayTypeSymbol arrayType)
{
var elementType = arrayType.ElementTypeWithAnnotations;
if (!NormalizeTaskTypesInType(compilation, ref elementType))
{
return false;
}
arrayType = arrayType.WithElementType(elementType);
return true;
}
private static bool NormalizeTaskTypesInPointer(CSharpCompilation compilation, ref PointerTypeSymbol pointerType)
{
var pointedAtType = pointerType.PointedAtTypeWithAnnotations;
if (!NormalizeTaskTypesInType(compilation, ref pointedAtType))
{
return false;
}
// Preserve custom modifiers but without normalizing those types.
pointerType = new PointerTypeSymbol(pointedAtType);
return true;
}
private static bool NormalizeTaskTypesInFunctionPointer(CSharpCompilation compilation, ref FunctionPointerTypeSymbol funcPtrType)
{
var returnType = funcPtrType.Signature.ReturnTypeWithAnnotations;
var madeChanges = NormalizeTaskTypesInType(compilation, ref returnType);
var paramTypes = ImmutableArray<TypeWithAnnotations>.Empty;
if (funcPtrType.Signature.ParameterCount > 0)
{
var paramsBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(funcPtrType.Signature.ParameterCount);
bool madeParamChanges = false;
foreach (var param in funcPtrType.Signature.Parameters)
{
var paramType = param.TypeWithAnnotations;
madeParamChanges |= NormalizeTaskTypesInType(compilation, ref paramType);
paramsBuilder.Add(paramType);
}
if (madeParamChanges)
{
madeChanges = true;
paramTypes = paramsBuilder.ToImmutableAndFree();
}
else
{
paramTypes = funcPtrType.Signature.ParameterTypesWithAnnotations;
paramsBuilder.Free();
}
}
if (madeChanges)
{
funcPtrType = funcPtrType.SubstituteTypeSymbol(returnType, paramTypes, refCustomModifiers: default, paramRefCustomModifiers: default);
return true;
}
else
{
return false;
}
}
internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes(
this TypeWithAnnotations type,
Emit.PEModuleBuilder moduleBuilder,
Symbol declaringSymbol,
Cci.ITypeReference typeRef)
{
var builder = ArrayBuilder<Cci.ICustomAttribute>.GetInstance();
var compilation = declaringSymbol.DeclaringCompilation;
if (compilation != null)
{
if (type.Type.ContainsTupleNames())
{
addIfNotNull(builder, compilation.SynthesizeTupleNamesAttribute(type.Type));
}
if (compilation.ShouldEmitNativeIntegerAttributes(type.Type))
{
addIfNotNull(builder, moduleBuilder.SynthesizeNativeIntegerAttribute(declaringSymbol, type.Type));
}
if (compilation.ShouldEmitNullableAttributes(declaringSymbol))
{
addIfNotNull(builder, moduleBuilder.SynthesizeNullableAttributeIfNecessary(declaringSymbol, declaringSymbol.GetNullableContextValue(), type));
}
static void addIfNotNull(ArrayBuilder<Cci.ICustomAttribute> builder, SynthesizedAttributeData? attr)
{
if (attr != null)
{
builder.Add(attr);
}
}
}
return new Cci.TypeReferenceWithAttributes(typeRef, builder.ToImmutableAndFree());
}
internal static bool IsWellKnownTypeInAttribute(this TypeSymbol typeSymbol)
=> typeSymbol.IsWellKnownInteropServicesTopLevelType("InAttribute");
internal static bool IsWellKnownTypeRequiresLocationAttribute(this TypeSymbol typeSymbol)
=> typeSymbol.IsWellKnownCompilerServicesTopLevelType("RequiresLocationAttribute");
internal static bool IsWellKnownTypeUnmanagedType(this TypeSymbol typeSymbol)
=> typeSymbol.IsWellKnownInteropServicesTopLevelType("UnmanagedType");
internal static bool IsWellKnownTypeIsExternalInit(this TypeSymbol typeSymbol)
=> typeSymbol.IsWellKnownCompilerServicesTopLevelType("IsExternalInit");
internal static bool IsWellKnownTypeOutAttribute(this TypeSymbol typeSymbol) => typeSymbol.IsWellKnownInteropServicesTopLevelType("OutAttribute");
// Keep consistent with ISymbolExtensions.IsWellKnownTypeLock and VB equivalent.
internal static bool IsWellKnownTypeLock(this TypeSymbol typeSymbol)
{
return typeSymbol is NamedTypeSymbol { Name: WellKnownMemberNames.LockTypeName, Arity: 0, ContainingType: null } &&
typeSymbol.IsContainedInNamespace(nameof(System), nameof(System.Threading));
}
private static bool IsWellKnownInteropServicesTopLevelType(this TypeSymbol typeSymbol, string name)
{
if (typeSymbol.Name != name || typeSymbol.ContainingType is object)
{
return false;
}
return IsContainedInNamespace(typeSymbol, "System", "Runtime", "InteropServices");
}
private static bool IsWellKnownCompilerServicesTopLevelType(this TypeSymbol typeSymbol, string name)
{
if (typeSymbol.Name != name)
{
return false;
}
return IsCompilerServicesTopLevelType(typeSymbol);
}
internal static bool IsCompilerServicesTopLevelType(this TypeSymbol typeSymbol)
=> typeSymbol.ContainingType is null && IsContainedInNamespace(typeSymbol, "System", "Runtime", "CompilerServices");
internal static bool IsWellKnownSetsRequiredMembersAttribute(this TypeSymbol type)
=> type.Name == "SetsRequiredMembersAttribute" && type.IsWellKnownDiagnosticsCodeAnalysisTopLevelType();
internal static bool IsWellKnownINumberBaseType(this TypeSymbol type)
{
type = type.OriginalDefinition;
return type is NamedTypeSymbol { Name: "INumberBase", IsInterface: true, Arity: 1, ContainingType: null } &&
IsContainedInNamespace(type, "System", "Numerics");
}
internal static bool IsWellKnownDiagnosticsCodeAnalysisTopLevelType(this TypeSymbol typeSymbol)
=> typeSymbol.ContainingType is null && IsContainedInNamespace(typeSymbol, "System", "Diagnostics", "CodeAnalysis");
private static bool IsContainedInNamespace(this TypeSymbol typeSymbol, string outerNS, string midNS, string? innerNS = null)
{
NamespaceSymbol? midNamespace;
if (innerNS != null)
{
var innerNamespace = typeSymbol.ContainingNamespace;
if (innerNamespace?.Name != innerNS)
{
return false;
}
midNamespace = innerNamespace.ContainingNamespace;
}
else
{
midNamespace = typeSymbol.ContainingNamespace;
}
if (midNamespace?.Name != midNS)
{
return false;
}
var outerNamespace = midNamespace.ContainingNamespace;
if (outerNamespace?.Name != outerNS)
{
return false;
}
var globalNamespace = outerNamespace.ContainingNamespace;
return globalNamespace != null && globalNamespace.IsGlobalNamespace;
}
internal static int TypeToIndex(this TypeSymbol type)
{
switch (type.GetSpecialTypeSafe())
{
case SpecialType.System_Object: return 0;
case SpecialType.System_String: return 1;
case SpecialType.System_Boolean: return 2;
case SpecialType.System_Char: return 3;
case SpecialType.System_SByte: return 4;
case SpecialType.System_Int16: return 5;
case SpecialType.System_Int32: return 6;
case SpecialType.System_Int64: return 7;
case SpecialType.System_Byte: return 8;
case SpecialType.System_UInt16: return 9;
case SpecialType.System_UInt32: return 10;
case SpecialType.System_UInt64: return 11;
case SpecialType.System_IntPtr when type.IsNativeIntegerType: return 12;
case SpecialType.System_UIntPtr when type.IsNativeIntegerType: return 13;
case SpecialType.System_Single: return 14;
case SpecialType.System_Double: return 15;
case SpecialType.System_Decimal: return 16;
case SpecialType.None:
if ((object)type != null && type.IsNullableType())
{
TypeSymbol underlyingType = type.GetNullableUnderlyingType();
switch (underlyingType.GetSpecialTypeSafe())
{
case SpecialType.System_Boolean: return 17;
case SpecialType.System_Char: return 18;
case SpecialType.System_SByte: return 19;
case SpecialType.System_Int16: return 20;
case SpecialType.System_Int32: return 21;
case SpecialType.System_Int64: return 22;
case SpecialType.System_Byte: return 23;
case SpecialType.System_UInt16: return 24;
case SpecialType.System_UInt32: return 25;
case SpecialType.System_UInt64: return 26;
case SpecialType.System_IntPtr when underlyingType.IsNativeIntegerType: return 27;
case SpecialType.System_UIntPtr when underlyingType.IsNativeIntegerType: return 28;
case SpecialType.System_Single: return 29;
case SpecialType.System_Double: return 30;
case SpecialType.System_Decimal: return 31;
}
}
// fall through
goto default;
default: return -1;
}
}
internal static bool IsDisplayClassType(this TypeSymbol type)
{
if (type.Kind == SymbolKind.NamedType)
{
switch (GeneratedNameParser.GetKind(type.Name))
{
case GeneratedNameKind.LambdaDisplayClass:
case GeneratedNameKind.StateMachineType:
return true;
}
}
return false;
}
}
}
|