Ejemplo n.º 1
0
        private void AnalyzeProperty(
            SyntaxNodeAnalysisContext context,
            MutabilityInspector inspector
            )
        {
            var root = context.Node as PropertyDeclarationSyntax;

            if (root == null)
            {
                throw new Exception("This should not happen if this function is wired up correctly");
            }

            bool isStatic = root.Modifiers.Any(SyntaxKind.StaticKeyword);

            var prop = context.SemanticModel.GetDeclaredSymbol(root);

            if (prop == null)
            {
                // Could this happen? We are not emitting diagnostics in this case
                // even when the property has an annotation.
                return;
            }

            InspectFieldOrProperty(
                context,
                inspector,
                fieldOrProperty: prop,
                location: root.GetLocation(),
                isStatic: isStatic,
                fieldOrPropertyType: prop.Type,
                fieldOrPropertyName: prop.Name
                );
        }
Ejemplo n.º 2
0
        public void IsTypeMarkedImmutable_No_ReturnsFalse()
        {
            var type = CompileAndGetFooType("class Foo {}");

            var inspector = new MutabilityInspector(type.Compilation);

            Assert.IsFalse(IsTypeMarkedFullyImmutable(type.Symbol));
        }
Ejemplo n.º 3
0
        public void IsTypeMarkedImmutable_Yes_ReturnsTrue()
        {
            var type = CompileAndGetFooType(@"
				[Objects.Immutable] class Foo {}"
                                            );

            var inspector = new MutabilityInspector(type.Compilation);

            Assert.IsTrue(IsTypeMarkedFullyImmutable(type.Symbol));
        }
Ejemplo n.º 4
0
        private void AnalyzeClassOrStruct(
            SyntaxNodeAnalysisContext context,
            MutabilityInspector inspector
            )
        {
            // TypeDeclarationSyntax is the base class of
            // ClassDeclarationSyntax and StructDeclarationSyntax
            var root = (TypeDeclarationSyntax)context.Node;

            var symbol = context.SemanticModel
                         .GetDeclaredSymbol(root);

            // skip classes not marked immutable
            var scope = symbol.GetImmutabilityScope();

            if (scope == ImmutabilityScope.None)
            {
                return;
            }

            var mutabilityResult = inspector.InspectConcreteType(
                symbol,
                MutabilityInspectionFlags.IgnoreImmutabilityAttribute                 // we're _validating_ the attribute
                );

            if (mutabilityResult.IsMutable)
            {
                var reason     = m_resultFormatter.Format(mutabilityResult);
                var location   = GetLocationOfClassIdentifierAndGenericParameters(root);
                var diagnostic = Diagnostic.Create(
                    Diagnostics.ImmutableClassIsnt,
                    location,
                    reason
                    );
                context.ReportDiagnostic(diagnostic);
            }
            else if (mutabilityResult.SeenUnauditedReasons.Count > 0)
            {
                ImmutableHashSet <string> immutableExceptions = symbol.GetAllImmutableExceptions();

                if (!mutabilityResult.SeenUnauditedReasons.IsSubsetOf(immutableExceptions))
                {
                    string missingExceptions = string.Join(", ", mutabilityResult.SeenUnauditedReasons.Except(immutableExceptions));

                    var location   = GetLocationOfClassIdentifierAndGenericParameters(root);
                    var diagnostic = Diagnostic.Create(
                        Diagnostics.InvalidUnauditedReasonInImmutable,
                        location,
                        missingExceptions
                        );

                    context.ReportDiagnostic(diagnostic);
                }
            }
        }
        public void InspectType_Enum_False()
        {
            var type = Type("enum blah {}");

            var inspector = new MutabilityInspector(type.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_SealedClass_False()
        {
            var type = Type("sealed class foo {}");

            var inspector = new MutabilityInspector(type.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_NullablePrimitiveType_NotMutable()
        {
            var field = Field("uint? foo");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_IEnumerableGenericCollectionWithImmutableElement_ReturnsFalse()
        {
            var field = Field("private readonly System.Collections.Generic.IEnumerable<int> random");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_ParenthesizedLambda_NotMutable()
        {
            var field = Field("private readonly Func<int,int> m_addTwo = ( a ) => a + 2;");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_LambdaInitializedFromStaticMethod_NotMutable()
        {
            var field = Field("private readonly Func<int> m_func = () => int.Parse( \"1\" );");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        public void InspectType_SimpleLambdaMember_NotMutable()
        {
            var field = Field("private readonly Func<int> m_func = () => 1;");

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        private void AssertUnauditedReasonsResult(TestSymbol <ITypeSymbol> ty, params string[] expectedUnauditedReasons)
        {
            var inspector = new MutabilityInspector(ty.Compilation);

            var expected = MutabilityInspectionResult.NotMutable(
                ImmutableHashSet.Create(expectedUnauditedReasons)
                );

            var actual = inspector.InspectType(ty.Symbol, MutabilityInspectionFlags.IgnoreImmutabilityAttribute);

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

            var inspector = new MutabilityInspector(field.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
Ejemplo n.º 14
0
        public void IsTypeMarkedImmutable_InterfaceIs_ReturnsTrue()
        {
            var type = CompileAndGetFooType(@"
				class Foo : IFoo {} 
				[Objects.Immutable] interface IFoo {}"
                                            );

            var inspector = new MutabilityInspector(type.Compilation);

            // we have multiple types defined, so ensure that we're asserting on the correct one first.
            Assert.AreEqual("Foo", type.Symbol.MetadataName);
            Assert.IsTrue(IsTypeMarkedFullyImmutable(type.Symbol));
        }
Ejemplo n.º 15
0
        private void RegisterAnalysis(CompilationStartAnalysisContext context)
        {
            var inspector = new MutabilityInspector(context.Compilation);

            context.RegisterSyntaxNodeAction(
                ctx => AnalyzeClassOrStruct(ctx, inspector),
                SyntaxKind.ClassDeclaration
                );

            context.RegisterSyntaxNodeAction(
                ctx => AnalyzeClassOrStruct(ctx, inspector),
                SyntaxKind.StructDeclaration
                );
        }
Ejemplo n.º 16
0
        private void RegisterAnalysis(CompilationStartAnalysisContext context)
        {
            var inspector = new MutabilityInspector(context.Compilation);

            context.RegisterSyntaxNodeAction(
                ctx => AnalyzeField(ctx, inspector),
                SyntaxKind.FieldDeclaration
                );

            context.RegisterSyntaxNodeAction(
                ctx => AnalyzeProperty(ctx, inspector),
                SyntaxKind.PropertyDeclaration
                );
        }
        public void InspectType_ImmutableGenericCollectionWithValueTypeElement_ReturnsFalse()
        {
            var field = Field("private readonly System.Collections.Immutable.ImmutableArray<int> random");

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

            var expected = MutabilityInspectionResult.NotMutable();

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

            AssertResultsAreEqual(expected, actual);
        }
        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_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 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_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_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 IsTypeMarkedImmutable_SomeTopLevelParentClassIs_ReturnsTrue()
        {
            var type = CompileAndGetFooType(@"
				class Foo : FooBase {} 
				class FooBase : FooBaseOfBase {}
				[Objects.Immutable] class FooBaseOfBase { }"
                                            );

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

            // we have multiple types defined, so ensure that we're asserting on the correct one first.
            Assert.AreEqual("Foo", type.Symbol.MetadataName);
            Assert.IsTrue(IsTypeMarkedFullyImmutable(type.Symbol));
        }
        public void InspectType_LambdaInitializerReferencingField_NotMutable()
        {
            const string source = AnnotationsPreamble + @"
sealed class Foo {

	private readonly int m_int = 3;
	private readonly Func<int> m_func = () => { return m_int; };
}
";

            TestSymbol <ITypeSymbol> ty = CompileAndGetFooType(source);

            var inspector = new MutabilityInspector(ty.Compilation);

            var result = inspector.InspectType(ty.Symbol);

            Assert.IsFalse(result.IsMutable);
        }
Ejemplo n.º 26
0
        /// <summary>
        /// This helper method implements all of the shared logic. We have to
        /// split this for two reasons:
        ///
        /// 1. PropertyDeclarationSyntax and FieldDeclarationSyntaxs useful
        ///    members don't come from a base class/interface so we can't do
        ///    this generically.
        /// 2. Fields can define multiple variables and we may wish to output
        ///    multiple diagnostics (individual ariables in a field declaration
        ///    may be alright because of their initializers.)
        ///
        /// The arguments are organized roughly based on the common grammar of
        /// fields and properties: attributes, modifiers, type, name,
        /// initializer.
        /// </summary>
        private void InspectFieldOrProperty(
            SyntaxNodeAnalysisContext context,
            MutabilityInspector inspector,
            Location location,
            ISymbol fieldOrProperty,
            bool isStatic,
            bool isReadOnly,
            ITypeSymbol fieldOrPropertyType,
            string fieldOrPropertyName,
            ExpressionSyntax initializer,
            bool isProperty,
            bool isAutoImplementedProperty
            )
        {
            var diagnostics = GatherDiagnostics(
                context.SemanticModel,
                inspector,
                location: location,
                isStatic: isStatic,
                isReadOnly: isReadOnly,
                fieldOrPropertyType: fieldOrPropertyType,
                fieldOrPropertyName: fieldOrPropertyName,
                initializationExpression: initializer,
                isProperty: isProperty,
                isAutoImplementedProperty: isAutoImplementedProperty
                );

            // We're manually using enumerators here.
            // - if we used IEnumerable directly we'd re-compute the first
            //   diagnostic in the GatherDiagnostics generator
            // - if we did .ToArray() early then we would avoid multiple
            //   enumeration but would compute diagnostics even when we
            //   ultimately ignore them due to annotations
            using (var diagnosticsEnumerator = diagnostics.GetEnumerator()) {
                ProcessDiagnostics(
                    context,
                    diagnosticsEnumerator,
                    location,
                    fieldOrProperty,
                    fieldOrPropertyName
                    );
            }
        }
        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 InspectType_NullableNonPrimitiveType_NotMutable()
        {
            var type = Type(@"
				class Test {
					struct Hello { }
					Hello? nullable;
				}"
                            );
            var field = type.Symbol.GetMembers().FirstOrDefault(m => m is IFieldSymbol);

            Assert.IsNotNull(field);
            var realType = (field as IFieldSymbol).Type;

            var inspector = new MutabilityInspector(type.Compilation);

            var expected = MutabilityInspectionResult.NotMutable();

            var actual = inspector.InspectType(realType);

            AssertResultsAreEqual(expected, actual);
        }
Ejemplo n.º 29
0
        private void AnalyzeField(
            SyntaxNodeAnalysisContext context,
            MutabilityInspector inspector
            )
        {
            var root = context.Node as FieldDeclarationSyntax;

            if (root == null)
            {
                throw new Exception("This should not happen if this function is wired up correctly");
            }

            bool isStatic = root.Modifiers.Any(SyntaxKind.StaticKeyword);

            foreach (var variable in root.Declaration.Variables)
            {
                var symbol = context
                             .SemanticModel
                             .GetDeclaredSymbol(variable)
                             as IFieldSymbol;

                if (symbol == null)
                {
                    // Could this happen? We are not emitting diagnostics in this
                    // case even when the fields have an annotation.
                    continue;
                }

                InspectFieldOrProperty(
                    context,
                    inspector,
                    fieldOrProperty: symbol,
                    location: variable.GetLocation(),
                    isStatic: isStatic,
                    fieldOrPropertyType: symbol.Type,
                    fieldOrPropertyName: variable.Identifier.ValueText
                    );
            }
        }
Ejemplo n.º 30
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
                                 ));
            }
        }