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(); } }
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)); } }
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 )); }
/// <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); } }