Example #1
0
        private void CompareConsistencyToBaseType(
            ImmutableTypeInfo typeInfo,
            ImmutableTypeInfo baseTypeInfo
            )
        {
            switch (baseTypeInfo.Kind)
            {
            case ImmutableTypeKind.None:
                return;

            case ImmutableTypeKind.Instance:
                return;

            case ImmutableTypeKind.Total:
                if (typeInfo.Kind == ImmutableTypeKind.Total)
                {
                    break;
                }

                // The docs say the only things that can have BaseType == null
                // are interfaces, System.Object itself (won't come up in our
                // analysis because (1) it !hasTheImmutableAttribute (2) you
                // can't explicitly list it as a base class anyway) and
                // pointer types (the base value type probably also doesn't
                // have it.)
                bool isInterface =
                    baseTypeInfo.Type.BaseType == null &&
                    baseTypeInfo.Type.SpecialType != SpecialType.System_Object &&
                    baseTypeInfo.Type.SpecialType != SpecialType.System_ValueType &&
                    baseTypeInfo.Type.Kind != SymbolKind.PointerType;

                TypeDeclarationSyntax syntax = FindDeclarationImplementingType(
                    typeSymbol: typeInfo.Type,
                    baseTypeSymbol: baseTypeInfo.Type
                    );

                m_diagnosticSink(
                    Diagnostic.Create(
                        Diagnostics.MissingTransitiveImmutableAttribute,
                        syntax.Identifier.GetLocation(),
                        properties: FixArgs,
                        typeInfo.Type.GetFullTypeName(),
                        baseTypeInfo.IsConditional ? " (or [ConditionallyImmutable])" : "",
                        isInterface ? "interface" : "base class",
                        baseTypeInfo.Type.GetFullTypeName()
                        )
                    );

                break;

            default:
                throw new NotImplementedException();
            }
        }
Example #2
0
        public void CheckTypeDeclaration(
            INamedTypeSymbol typeSymbol
            )
        {
            ImmutableTypeInfo typeInfo = m_context.GetImmutableTypeInfo(typeSymbol);

            if (typeSymbol.BaseType != null)
            {
                CompareConsistencyToBaseType(typeInfo, m_context.GetImmutableTypeInfo(typeSymbol.BaseType));
            }

            foreach (INamedTypeSymbol interfaceType in typeSymbol.Interfaces)
            {
                CompareConsistencyToBaseType(typeInfo, m_context.GetImmutableTypeInfo(interfaceType));
            }
        }
Example #3
0
        public ImmutableTypeInfo GetImmutableTypeInfo(
            INamedTypeSymbol type
            )
        {
            // Check for [Immutable] etc.
            ImmutableTypeKind fromAttributes = GetImmutabilityFromAttributes(type);

            if (fromAttributes != ImmutableTypeKind.None)
            {
                return(ImmutableTypeInfo.Create(
                           annotationsContext: m_annotationsContext,
                           kind: fromAttributes,
                           type: type
                           ));
            }

            if (type.IsTupleType)
            {
                return(ImmutableTypeInfo.CreateWithAllConditionalTypeParameters(
                           kind: ImmutableTypeKind.Total,
                           type: type.OriginalDefinition
                           ));
            }

            // Check if we were otherwise told that this type is immutable
            if (m_extraImmutableTypes.TryGetValue(type.OriginalDefinition, out ImmutableTypeInfo info))
            {
                return(info);
            }

            return(ImmutableTypeInfo.Create(
                       annotationsContext: m_annotationsContext,
                       kind: ImmutableTypeKind.None,
                       type: type
                       ));
        }
Example #4
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);
            }
        }