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 }));
                }
            }
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        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"
                          );
            }
        }