Пример #1
0
            public void TrySet(string trySetCode, AnalysisResult expected)
            {
                var testCode = @"
namespace RoslynSandbox
{
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public class Foo : INotifyPropertyChanged
    {
        private int value;

        public event PropertyChangedEventHandler PropertyChanged;

        public int Value
        {
            get => this.value;
            set => this.TrySet(ref this.value, value, nameof(Value));
        }

        protected bool TrySet<T>(ref T field, T newValue, string propertyName)
        {
            if (EqualityComparer<T>.Default.Equals(field, newValue))
            {
                return false;
            }

            field = newValue;
            this.OnPropertyChanged(propertyName);
            return true;
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}";

                testCode = testCode.AssertReplace("this.TrySet(ref this.value, value, nameof(Value))", trySetCode);
                var syntaxTree    = CSharpSyntaxTree.ParseText(testCode);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var node          = syntaxTree.FindArgument("ref this.value");
                var property      = semanticModel.GetDeclaredSymbol(syntaxTree.FindPropertyDeclaration("Value"));

                Assert.AreEqual(expected, PropertyChanged.InvokesPropertyChangedFor(node, property, semanticModel, CancellationToken.None));
            }
Пример #2
0
            public void WhenRecursive(string signature, string propertyName)
            {
                var syntaxTree = CSharpSyntaxTree.ParseText(
                    @"
namespace RoslynSandbox
{
    using System;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using System.Runtime.CompilerServices;

    public class Foo : INotifyPropertyChanged
    {
        private int _value1;
        private int _value2;
        private int _value3;

        public event PropertyChangedEventHandler PropertyChanged;

        public int Value1
        {
            get
            {
                return _value1;
            }

            set
            {
                if (value == _value1)
                {
                    return;
                }

                _value1 = value;
                OnPropertyChanged();
            }
        }

        public int Value2
        {
            get
            {
                return _value2;
            }

            set
            {
                if (value == _value2)
                {
                    return;
                }

                _value2 = value;
                OnPropertyChanged(() => this.Value2);
            }
        }

        public int Value3
        {
            get { return _value3; }
            set { TrySet(ref _value3, value); }
        }

        protected bool TrySet<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
        {
            if (TrySet<T>(ref field, newValue, propertyName))
            {
                return false;
            }

            field = newValue;
            this.OnPropertyChanged(propertyName);
            return true;
        }

        protected virtual void OnPropertyChanged<T>(Expression<Func<T>> property)
        {
            this.OnPropertyChanged(property);
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
             this.OnPropertyChanged(e);
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.OnPropertyChanged(propertyName);
        }
    }
}");
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var node          = syntaxTree.Find <SyntaxNode>(signature);
                var property      = semanticModel.GetDeclaredSymbol(syntaxTree.FindPropertyDeclaration(propertyName));

                Assert.AreEqual(AnalysisResult.No, PropertyChanged.InvokesPropertyChangedFor(node, property, semanticModel, CancellationToken.None));
            }
Пример #3
0
        private static void Handle(SyntaxNodeAnalysisContext context, IFieldSymbol assignedField)
        {
            if (context.IsExcludedFromAnalysis())
            {
                return;
            }

            if (IsInIgnoredScope(context))
            {
                return;
            }

            var typeDeclaration = context.Node.FirstAncestorOrSelf <TypeDeclarationSyntax>();
            var typeSymbol      = context.SemanticModel.GetDeclaredSymbolSafe(typeDeclaration, context.CancellationToken);

            if (!typeSymbol.Is(KnownSymbol.INotifyPropertyChanged))
            {
                return;
            }

            if (!typeSymbol.Equals(assignedField.ContainingType))
            {
                return;
            }

            var inProperty = context.Node.FirstAncestorOrSelf <PropertyDeclarationSyntax>();

            if (inProperty != null)
            {
                if (Property.IsSimplePropertyWithBackingField(inProperty, context.SemanticModel, context.CancellationToken))
                {
                    return;
                }
            }

            using (var pooledSet = SetPool <IPropertySymbol> .Create())
            {
                foreach (var member in typeDeclaration.Members)
                {
                    var propertyDeclaration = member as PropertyDeclarationSyntax;
                    if (propertyDeclaration == null ||
                        Property.IsLazy(propertyDeclaration, context.SemanticModel, context.CancellationToken))
                    {
                        continue;
                    }

                    var property = context.SemanticModel.GetDeclaredSymbolSafe(propertyDeclaration, context.CancellationToken);
                    var getter   = GetterBody(propertyDeclaration);
                    if (getter == null || property == null || property.DeclaredAccessibility != Accessibility.Public)
                    {
                        continue;
                    }

                    var accessor = context.Node.FirstAncestorOrSelf <AccessorDeclarationSyntax>();
                    if (accessor?.IsKind(SyntaxKind.GetAccessorDeclaration) == true &&
                        accessor.FirstAncestorOrSelf <PropertyDeclarationSyntax>() == propertyDeclaration)
                    {
                        continue;
                    }

                    var expressionBody = context.Node.FirstAncestorOrSelf <ArrowExpressionClauseSyntax>();
                    if (expressionBody?.FirstAncestorOrSelf <PropertyDeclarationSyntax>() == propertyDeclaration)
                    {
                        continue;
                    }

                    using (var pooled = TouchedFieldsWalker.Create(getter, context.SemanticModel, context.CancellationToken))
                    {
                        if (pooled.Item.Contains(assignedField))
                        {
                            if (PropertyChanged.InvokesPropertyChangedFor(context.Node, property, context.SemanticModel, context.CancellationToken) == AnalysisResult.No)
                            {
                                if (pooledSet.Item.Add(property))
                                {
                                    var properties = ImmutableDictionary.CreateRange(new[] { new KeyValuePair <string, string>(PropertyNameKey, property.Name), });
                                    context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation(), properties, property.Name));
                                }
                            }
                        }
                    }
                }
            }
        }
        private static void HandleAssignment(SyntaxNodeAnalysisContext context)
        {
            var assignment = (AssignmentExpressionSyntax)context.Node;

            if (assignment?.IsMissing != false ||
                assignment.FirstAncestorOrSelf <ConstructorDeclarationSyntax>() != null ||
                assignment.FirstAncestorOrSelf <InitializerExpressionSyntax>() != null)
            {
                return;
            }

            var block = assignment.FirstAncestorOrSelf <BlockSyntax>();

            if (block == null)
            {
                return;
            }

            var typeDeclaration = assignment.FirstAncestorOrSelf <TypeDeclarationSyntax>();
            var typeSymbol      = context.SemanticModel.GetDeclaredSymbolSafe(typeDeclaration, context.CancellationToken);

            if (!typeSymbol.Is(KnownSymbol.INotifyPropertyChanged))
            {
                return;
            }

            var field = context.SemanticModel.GetSymbolSafe(assignment.Left, context.CancellationToken) as IFieldSymbol;

            if (field == null || !typeSymbol.Equals(field.ContainingType))
            {
                return;
            }

            foreach (var member in typeDeclaration.Members)
            {
                var propertyDeclaration = member as PropertyDeclarationSyntax;
                if (propertyDeclaration == null)
                {
                    continue;
                }

                var property = context.SemanticModel.GetDeclaredSymbolSafe(propertyDeclaration, context.CancellationToken);
                var getter   = Getter(propertyDeclaration);
                if (getter == null || property == null || property.DeclaredAccessibility != Accessibility.Public)
                {
                    continue;
                }

                using (var pooled = IdentifierNameWalker.Create(getter))
                {
                    foreach (var identifierName in pooled.Item.IdentifierNames)
                    {
                        var component      = context.SemanticModel.GetSymbolSafe(identifierName, context.CancellationToken);
                        var componentField = component as IFieldSymbol;
                        if (componentField == null)
                        {
                            var propertySymbol = component as IPropertySymbol;
                            if (propertySymbol == null)
                            {
                                continue;
                            }

                            if (!Property.TryGetBackingField(propertySymbol, context.SemanticModel, context.CancellationToken, out componentField))
                            {
                                continue;
                            }
                        }

                        if (!field.Equals(componentField))
                        {
                            continue;
                        }

                        if (PropertyChanged.InvokesPropertyChangedFor(assignment, property, context.SemanticModel, context.CancellationToken) == PropertyChanged.InvokesPropertyChanged.No)
                        {
                            var properties = ImmutableDictionary.CreateRange(new[] { new KeyValuePair <string, string>(PropertyNameKey, property.Name), });
                            context.ReportDiagnostic(Diagnostic.Create(Descriptor, assignment.GetLocation(), properties, property.Name));
                        }
                    }
                }
            }
        }