Exemple #1
0
        /// <summary>
        /// Determines if a type is known to be immutable.
        /// </summary>
        /// <param name="query">The type to check (and what kind of check to do.)</param>
        /// <param name="diag">If this method returns false, an explaination for why its not known to be immutable.</param>
        /// <returns>Is the type immutable?</returns>
        public bool IsImmutable(
            ImmutabilityQuery query,
            Func <Location> getLocation,
            out Diagnostic diagnostic
            )
        {
            if (query.Kind == ImmutableTypeKind.None)
            {
                throw new ArgumentException(
                          "ImmutabilityKind.None is not a valid question to ask this function",
                          nameof(query.Kind)
                          );
            }

            diagnostic = null;

            // Things like int are totally OK
            if (m_totallyImmutableSpecialTypes.Contains(query.Type.SpecialType))
            {
                return(true);
            }

            // "new object()" are always immutable (and that's the only
            // constructor for System.Object) but in general things of type
            // System.Object (any reference type) may be mutable.
            //
            // This is hard-coded (rather than in m_extraImmutableTypes) to
            // avoid the ITypeSymbol lookup.
            if (query.Kind == ImmutableTypeKind.Instance && query.Type.SpecialType == SpecialType.System_Object)
            {
                return(true);
            }

            if (query.Type is INamedTypeSymbol namedType)
            {
                ImmutableTypeInfo info = GetImmutableTypeInfo(namedType);
                if (info.Kind.HasFlag(query.Kind))
                {
                    return(info.IsImmutableDefinition(
                               context: this,
                               definition: namedType,
                               getLocation: getLocation,
                               out diagnostic
                               ));
                }
            }

            switch (query.Type.TypeKind)
            {
            case TypeKind.Error:
                // Just say this is fine -- there is some other compiler
                // error in this case and we don't need to pile on.
                return(true);

            case TypeKind.Enum:
                // Enums are like ints -- always immutable
                return(true);

            case TypeKind.Array:
                diagnostic = Diagnostic.Create(
                    Diagnostics.ArraysAreMutable,
                    getLocation(),
                    (query.Type as IArrayTypeSymbol).ElementType.Name
                    );

                return(false);

            case TypeKind.Delegate:
                diagnostic = Diagnostic.Create(
                    Diagnostics.DelegateTypesPossiblyMutable,
                    getLocation()
                    );

                return(false);

            case TypeKind.Dynamic:
                diagnostic = Diagnostic.Create(
                    Diagnostics.DynamicObjectsAreMutable,
                    getLocation()
                    );

                return(false);

            case TypeKind.TypeParameter:
                if (GetImmutabilityFromAttributes(query.Type).HasFlag(ImmutableTypeKind.Total))
                {
                    return(true);
                }

                if (query.Type is ITypeParameterSymbol tp && m_conditionalTypeParameters.Contains(tp))
                {
                    return(true);
                }

                diagnostic = Diagnostic.Create(
                    Diagnostics.TypeParameterIsNotKnownToBeImmutable,
                    getLocation(),
                    query.Type.ToDisplayString()
                    );

                return(false);

            case TypeKind.Class:
                diagnostic = Diagnostic.Create(
                    Diagnostics.NonImmutableTypeHeldByImmutable,
                    getLocation(),
                    query.Type.TypeKind.ToString().ToLower(),
                    query.Type.ToDisplayString(),
                    query.Kind == ImmutableTypeKind.Instance && !query.Type.IsSealed ? " (or [ImmutableBaseClass])" : ""
                    );

                return(false);

            case TypeKind.Interface:
            case TypeKind.Struct:
                diagnostic = Diagnostic.Create(
                    Diagnostics.NonImmutableTypeHeldByImmutable,
                    getLocation(),
                    query.Type.TypeKind.ToString().ToLower(),
                    query.Type.ToDisplayString(),
                    ""
                    );

                return(false);

            case TypeKind.Unknown:
            default:
                diagnostic = Diagnostic.Create(
                    Diagnostics.UnexpectedTypeKind,
                    location: getLocation(),
                    query.Type.Kind
                    );

                return(false);
            }
        }