public void DocumentWithStaticField_TypeIsUnsafeInitializerIsImplicitConversionFromSafeValue_Diag()
        {
            const string test = @"
            using System;
            namespace test {
            class Tests {
            sealed class Foo {
                public Foo(int xx) { x = xx; }

                public static implicit operator Foo(int x) {
                    return new Foo(x);
                }

                public int x; // makes Foo mutable
            }

            private static readonly Foo foo = 3;
            }
            }";
            AssertSingleDiagnostic( s_preamble + test, 26, 32, "foo", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "foo.x",
                membersTypeName: "System.Int32",
                kind: MutabilityTarget.Member,
                cause: MutabilityCause.IsNotReadonly
            ) );
        }
        public void DocumentWithStaticProperty_InterfaceWithMutableConcreteInitializer_Diag()
        {
            const string test = @"
    using System;

    namespace test {
        class Tests {

            interface IFoo {}
            internal sealed class Foo : IFoo {
                public string ClientsName = ""YOLO"";
            }

            public static IFoo bad { get; } = new Foo();

        }
    }";

            AssertSingleDiagnostic(s_preamble + test, 23, 13, "bad", MutabilityInspectionResult.Mutable(
                                       mutableMemberPath: "bad.ClientsName",
                                       membersTypeName: "System.String",
                                       kind: MutabilityTarget.Member,
                                       cause: MutabilityCause.IsNotReadonly
                                       ));
        }
        public void DocumentWithStaticField_ReadonlyNotSealedImmutableUnknownConcreteType_NoDiag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {

            internal class Foo {
                public readonly string ClientsName = ""YOLO"";
            }

            public static readonly Foo bad = GetFoo();

            private static Foo GetFoo() {
                return new Foo();
            }

            }
            }";

            // Although a concrete instance of Foo is safe, we don't look
            // inside GetFoo to see that its returning a concrete Foo and
            // not some derived class.
            AssertSingleDiagnostic( s_preamble + test, 22, 40, "bad", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "bad",
                membersTypeName: "test.Tests.Foo",
                kind: MutabilityTarget.Type,
                cause: MutabilityCause.IsNotSealed
            ) );
        }
        public void DocumentWithStatic_ReadonlySelfReferencingStaticOfMutableType_Diag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {

            public sealed class Foo {
                private int uhoh = 1;
                public static readonly Foo Default = new Foo();
            }
            public static readonly Foo good = new Foo();

            }
            }";
            var diag1 = CreateDiagnosticResult( 20, 44, "Default", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "Default.uhoh",
                membersTypeName: "test.Tests.Foo",
                kind: MutabilityTarget.Member,
                cause: MutabilityCause.IsNotReadonly
            ) );
            var diag2 = CreateDiagnosticResult( 22, 40, "good", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "good.uhoh",
                membersTypeName: "test.Tests.Foo",
                kind: MutabilityTarget.Member,
                cause: MutabilityCause.IsNotReadonly
            ) );
            VerifyCSharpDiagnostic( s_preamble + test, diag1, diag2 );
        }
 public void NonReadOnlyProperty_Diagnostic()
 {
     const string test = @"
      namespace test {
     class tests {
     public static int PropertyWithSetter { get; set; }
     }
     }";
     AssertSingleDiagnostic( s_preamble + test, 15, 9, "PropertyWithSetter", MutabilityInspectionResult.Mutable(
         mutableMemberPath: "PropertyWithSetter",
         membersTypeName: "Widget",
         kind: MutabilityTarget.Member,
         cause: MutabilityCause.IsNotReadonly
     ) );
 }
        public void InspectType_ArrayType_True()
        {
            var field    = Field("int[] random");
            var expected = MutabilityInspectionResult.Mutable(
                null,
                "System.Int32[]",
                MutabilityTarget.Type,
                MutabilityCause.IsAnArray
                );

            var inspector = new MutabilityInspector(field.Compilation);

            var actual = inspector.InspectType(field.Symbol.Type);

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_LooksAtMembersInDeclaredType()
        {
            var field = Field("public string random");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.Mutable(
                "random",
                "System.String",
                MutabilityTarget.Member,
                MutabilityCause.IsNotReadonly
                );

            var actual = inspector.InspectType(field.Symbol.ContainingType);

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_DoesNotLookAtMembersInExternalType()
        {
            var field = Field("public readonly System.Text.StringBuilder random");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.Mutable(
                null,
                "System.Text.StringBuilder",
                MutabilityTarget.Type,
                MutabilityCause.IsAnExternalUnmarkedType
                );

            var actual = inspector.InspectType(field.Symbol.Type);

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_LooksAtPropertiesInNonExternalType()
        {
            var prop = Property("public string random { get; set; }");

            var inspector = new MutabilityInspector(prop.Compilation);

            var expected = MutabilityInspectionResult.Mutable(
                "random",
                "System.String",
                MutabilityTarget.Member,
                MutabilityCause.IsNotReadonly
                );

            var actual = inspector.InspectType(prop.Symbol.ContainingType);

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_TypeWithFuncProperty_ReturnsMutable()
        {
            var prop = Property("public Func<string> StringGetter { get; }");

            var inspector = new MutabilityInspector(prop.Compilation);

            var expected = MutabilityInspectionResult.Mutable(
                "StringGetter",
                "System.Func",
                MutabilityTarget.Type,
                MutabilityCause.IsADelegate
                );

            var actual = inspector.InspectType(prop.Symbol.ContainingType);

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_Interface_True()
        {
            var type = Type("interface foo {}");

            var inspector = new MutabilityInspector(type.Compilation);

            var expected = MutabilityInspectionResult.Mutable(
                null,
                $"{RootNamespace}.foo",
                MutabilityTarget.Type,
                MutabilityCause.IsAnInterface
                );

            var actual = inspector.InspectType(type.Symbol);

            AssertResultsAreEqual(expected, actual);
        }
        public void DocumentWithStaticCollectionField_NonGeneric_Diag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {
            public static readonly System.Collections.IList bad;

            }
            }";
            AssertSingleDiagnostic( s_preamble + test, 17, 61, "bad", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "bad",
                membersTypeName: "System.Collections.IList",
                kind: MutabilityTarget.Type,
                cause: MutabilityCause.IsAnInterface
            ) );
        }
        public void DocumentWithStaticImmutableCollectionField_GenericObject_Diag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {
            public static readonly System.Collections.Immutable.ImmutableList<object> bad;

            }
            }";
            AssertSingleDiagnostic( s_preamble + test, 17, 87, "bad", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "bad",
                membersTypeName: "System.Object",
                kind: MutabilityTarget.TypeArgument,
                cause: MutabilityCause.IsNotSealed
            ) );
        }
        public void InspectType_NonSealedClass_True()
        {
            var type = Type("class foo {}");

            var inspector = new MutabilityInspector(
                type.Compilation,
                KnownImmutableTypes.Default
                );

            var expected = MutabilityInspectionResult.Mutable(
                null,
                $"{RootNamespace}.foo",
                MutabilityTarget.Type,
                MutabilityCause.IsNotSealed
                );

            var actual = inspector.InspectType(type.Symbol);

            AssertResultsAreEqual(expected, actual);
        }
        public void DocumentWithStatic_ClassIsNotImmutableButImplementsImmutableInterface_Diag()
        {
            const string test = @"
	namespace test {
		[Immutable] interface IFoo {} 
		class Test : IFoo {

			public DateTime bad = DateTime.Now;
			public DateTime badToo { get; set; }

		}
	}"    ;

            AssertSingleDiagnostic(s_preamble + test, 13, 9, MutabilityInspectionResult.Mutable(
                                       "bad",
                                       "System.DateTime",
                                       MutabilityTarget.Member,
                                       MutabilityCause.IsNotReadonly
                                       ));
        }
        public void DocumentWithOneLevelRecurrsiveTypes_Mutable_Diag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {

            private readonly static Foo foo = new Foo();

            internal class Foo {
                public Foo Instance;
            }
            }
            }";

            AssertSingleDiagnostic( s_preamble + test, 18, 41, "foo", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "foo.Instance",
                membersTypeName: "test.Tests.Foo",
                kind: MutabilityTarget.Member,
                cause: MutabilityCause.IsNotReadonly
            ) );
        }
        public void DocumentWithStaticField_ReadonlyButMutable_Diag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {

            internal class Foo {
                public string ClientsName = ""YOLO"";
            }

            public static readonly Foo bad = new Foo();

            }
            }";
            AssertSingleDiagnostic( s_preamble + test, 22, 40, "bad", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "bad.ClientsName",
                membersTypeName: "test.Tests.Foo",
                kind: MutabilityTarget.Member,
                cause: MutabilityCause.IsNotReadonly
            ) );
        }
        public void DocumentWithStaticProperty_PrivateSetterImmutable_Diag()
        {
            const string test = @"
            using System;

            namespace test {
            class Tests {

            internal sealed class Foo {
                public readonly string ClientsName = ""YOLO"";
            }

            public static Foo bad { get; private set; }

            }
            }";
            AssertSingleDiagnostic( s_preamble + test, 22, 13, "bad", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "bad",
                membersTypeName: "test.Tests.Foo",
                kind: MutabilityTarget.Member,
                cause: MutabilityCause.IsNotReadonly
            ) );
        }
        public void DocumentWithStaticField_ReadonlyUnsafeBaseClassWithNonConstructorInitializerOfUnsealedType_Diag()
        {
            const string test = @"
            using System;
            namespace test {
            class Tests {
            interface IUnsafe { void Magic(); } // could be anythinggggggg

            class Safe : IUnsafe {
                void IUnsafe.Magic() {} // looks safe to me
                public static readonly Safe Instance { get; } = new Safe();
            }

            private readonly static IUnsafe foo = Safe.Instance; // bad, Safe is not sealed
            }
            }";

            AssertSingleDiagnostic( s_preamble + test, 23, 36, "foo", MutabilityInspectionResult.Mutable(
                mutableMemberPath: "foo",
                membersTypeName: "test.Tests.Safe",
                kind: MutabilityTarget.Type,
                cause: MutabilityCause.IsNotSealed
            ) );
        }
Beispiel #20
0
        /// <summary>
        /// All logic relating to either emitting or not emitting diagnostics other
        /// than the ones about unnecessary annotations belong in this function.
        /// This allows InspectMember to implement the logic around the unnecessary
        /// annotations diagnostic. Any time we bail early in AnalyzeField or
        /// AnalyzeProperty we risk not emitting unnecessary annotation
        /// diagnostics.
        /// </summary>
        private IEnumerable <Diagnostic> GatherDiagnostics(
            SemanticModel model,
            MutabilityInspector inspector,
            Location location,
            bool isStatic,
            bool isReadOnly,
            ITypeSymbol fieldOrPropertyType,
            string fieldOrPropertyName,
            ExpressionSyntax initializationExpression,
            bool isProperty,
            bool isAutoImplementedProperty
            )
        {
            if (!isStatic)
            {
                yield break;
            }

            if (isProperty && !isAutoImplementedProperty)
            {
                // non-auto-implemented properties don't hold their own state.
                // We should never emit diagnostics for them.
                yield break;
            }

            // Auto-implemented properties should be treated similar to fields:
            // 1. They should not have a setter (i.e. isReadOnly)
            // 2. Their type should be immutable
            //    a. Unless an initializer ensures it isn't

            if (!isReadOnly)
            {
                yield return(CreateDiagnostic(
                                 location,
                                 fieldOrPropertyName,
                                 fieldOrPropertyType.GetFullTypeNameWithGenericArguments(),
                                 MutabilityInspectionResult.Mutable(
                                     fieldOrPropertyName,
                                     fieldOrPropertyType.GetFullTypeNameWithGenericArguments(),
                                     MutabilityTarget.Member,
                                     MutabilityCause.IsNotReadonly
                                     )
                                 ));

                // TODO: it'd probably be reasonable to not bail here. However
                // we need to update the unsafestatics report because it counts
                // the number of diagnostics that come out (it assumes at most one
                // error per-field.)
                yield break;
            }

            // Always prefer the type from the initializer if it exists because
            // it may be more specific.
            if (initializationExpression != null)
            {
                var initializerType = model.GetTypeInfo(initializationExpression).Type;

                // Fall back to the declaration type if we can't get a type for
                // the initializer.
                if (initializerType != null && !(initializerType is IErrorTypeSymbol))
                {
                    fieldOrPropertyType = initializerType;
                }
            }

            MutabilityInspectionResult result;

            // When we know the concrete type as in "new T()" we don't have to
            // be paranoid about mutable derived classes.
            if (initializationExpression is ObjectCreationExpressionSyntax)
            {
                result = inspector.InspectConcreteType(fieldOrPropertyType);
            }
            else
            {
                result = inspector.InspectType(fieldOrPropertyType);
            }

            if (result.IsMutable)
            {
                result = result.WithPrefixedMember(fieldOrPropertyName);
                yield return(CreateDiagnostic(
                                 location,
                                 fieldOrPropertyName,
                                 fieldOrPropertyType.GetFullTypeNameWithGenericArguments(),
                                 result
                                 ));
            }
        }