private static void ValidateImmutability( SyntaxNodeAnalysisContext context, ITypeSymbol typeSymbol ) { if (ImmutableContainerMethods.IsAnImmutableContainerType(typeSymbol)) { var namedTypeSymbol = typeSymbol as INamedTypeSymbol; if (namedTypeSymbol == default(INamedTypeSymbol)) { context.ReportDiagnostic(Diagnostic.Create( Diagnostics.GenericArgumentTypeMustBeImmutable, context.Node.GetLocation(), messageArgs: new object[] { namedTypeSymbol.Name })); } foreach (var typeArgument in namedTypeSymbol.TypeArguments) { ValidateImmutability(context, typeArgument); } } else { if ((!KnownImmutableTypes.IsTypeKnownImmutable(typeSymbol)) && (typeSymbol.GetImmutabilityScope() != ImmutabilityScope.SelfAndChildren)) { context.ReportDiagnostic(Diagnostic.Create( Diagnostics.GenericArgumentTypeMustBeImmutable, context.Node.GetLocation(), messageArgs: new object[] { typeSymbol.Name })); } } }
public void IsTypeKnownImmutable_NotDeclaredImmutableAndNotDefault_False() { var knownTypes = new KnownImmutableTypes(ImmutableHashSet <string> .Empty); var type = Field("System.IDisposable foo").Symbol.Type; var result = knownTypes.IsTypeKnownImmutable(type); Assert.False(result); }
public void IsTypeKnownImmutable_DefaultlyImmutable_True() { var knownTypes = new KnownImmutableTypes(ImmutableHashSet <string> .Empty); var type = Field("System.Version foo").Symbol.Type; var result = knownTypes.IsTypeKnownImmutable(type); Assert.True(result); }
public void IsTypeKnownImmutable_DeclaredImmutable_True() { var knownTypes = new KnownImmutableTypes(new HashSet <string> { "System.IDisposable" }.ToImmutableHashSet()); var type = Field("System.IDisposable foo").Symbol.Type; var result = knownTypes.IsTypeKnownImmutable(type); Assert.True(result); }
public void IsTypeKnownImmutable_AssemblyDeclaredImmutable_True() { var cs = @" using System; using D2L.CodeStyle.Annotations; [assembly: Types.Audited( typeof( Test.Foo ), ""Random Owner"", ""Random Date"", ""Random rationale"" )] namespace Test { public class Foo { } } namespace D2L.CodeStyle.Annotations { public static partial class Types { [AttributeUsage( validOn: AttributeTargets.Assembly, AllowMultiple = true )] public sealed class Audited : Attribute { public Audited( Type type, string owner, string auditedDate, string rationale ) { Type = type; Owner = owner; AuditedDate = auditedDate; Rationale = rationale; } public Type Type { get; } public string Owner { get; } public string AuditedDate { get; } public string Rationale { get; } } } } "; var compilation = Compile(cs); var knownTypes = new KnownImmutableTypes(compilation.Assembly); var fooType = compilation.GetSymbolsWithName( predicate: n => true, filter: SymbolFilter.Type ).OfType <ITypeSymbol>().FirstOrDefault(); Assert.IsNotNull(fooType); Assert.AreNotEqual(TypeKind.Error, fooType.TypeKind); var result = knownTypes.IsTypeKnownImmutable(fooType); Assert.True(result); }
private MutabilityInspectionResult InspectTypeImpl( ITypeSymbol type, MutabilityInspectionFlags flags, HashSet <ITypeSymbol> typeStack ) { if (type is IErrorTypeSymbol) { // This only happens for code that otherwise won't compile. Our // analyzer doesn't need to validate these types. It only needs // to be strict for valid code. return(MutabilityInspectionResult.NotMutable()); } if (type == null) { throw new Exception("Type cannot be resolved. Please ensure all dependencies " + "are referenced, including transitive dependencies."); } // If we're not verifying immutability, we might be able to bail out early var scope = type.GetImmutabilityScope(); if (!flags.HasFlag(MutabilityInspectionFlags.IgnoreImmutabilityAttribute) && scope == ImmutabilityScope.SelfAndChildren) { ImmutableHashSet <string> immutableExceptions = type.GetAllImmutableExceptions(); return(MutabilityInspectionResult.NotMutable(immutableExceptions)); } if (m_knownImmutableTypes.IsTypeKnownImmutable(type)) { return(MutabilityInspectionResult.NotMutable()); } if (IsAnImmutableContainerType(type)) { return(InspectImmutableContainerType(type, typeStack)); } if (!flags.HasFlag(MutabilityInspectionFlags.AllowUnsealed) && type.TypeKind == TypeKind.Class && !type.IsSealed ) { return(MutabilityInspectionResult.MutableType(type, MutabilityCause.IsNotSealed)); } switch (type.TypeKind) { case TypeKind.Array: // Arrays are always mutable because you can rebind the // individual elements. return(MutabilityInspectionResult.MutableType( type, MutabilityCause.IsAnArray )); case TypeKind.Delegate: // Delegates can hold state so are mutable in general. return(MutabilityInspectionResult.MutableType( type, MutabilityCause.IsADelegate )); case TypeKind.Dynamic: // Dynamic types are always mutable return(MutabilityInspectionResult.MutableType( type, MutabilityCause.IsDynamic )); case TypeKind.Enum: // Enums are just fancy ints. return(MutabilityInspectionResult.NotMutable()); case TypeKind.TypeParameter: return(InspectTypeParameter( type, typeStack )); case TypeKind.Interface: return(MutabilityInspectionResult.MutableType(type, MutabilityCause.IsAnInterface)); case TypeKind.Class: case TypeKind.Struct: // equivalent to TypeKind.Structure return(InspectClassOrStruct( type, typeStack )); case TypeKind.Error: // This only happens when the build is failing for other // (more fundamental) reasons. We only need to be strict // for otherwise-successful builds, so we bail analysis in // this case. return(MutabilityInspectionResult.NotMutable()); case TypeKind.Unknown: // Looking at the Roslyn source this doesn't appear to // happen outside their tests. It is value 0 in the enum so // it may just be a safety guard. throw new NotImplementedException(); default: // not handled: Module, Pointer, Submission. throw new NotImplementedException( $"TypeKind.{type.Kind} not handled by analysis" ); } }