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