private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (context.IsExcludedFromAnalysis())
            {
                return;
            }

            if (context.Node is AssignmentExpressionSyntax assignment &&
                !context.ContainingSymbol.IsStatic &&
                IsInConstructor(assignment) &&
                Property.TryGetAssignedProperty(assignment, out var propertyDeclaration) &&
                propertyDeclaration.TryGetSetter(out var setter) &&
                (setter.Body != null || setter.ExpressionBody != null) &&
                !ThrowWalker.Throws(setter))
            {
                using (var mutations = MutationWalker.Borrow(setter))
                {
                    if (mutations.Count > 1)
                    {
                        return;
                    }

                    if (mutations.TrySingle(out var mutation) &&
                        mutation is AssignmentExpressionSyntax setAssignment &&
                        setAssignment.Right is IdentifierNameSyntax identifierName &&
                        identifierName.Identifier.ValueText != "value")
                    {
                        return;
                    }
                }

                using (var walker = InvocationWalker.Borrow(setter))
                {
                    foreach (var invocation in walker.Invocations)
                    {
                        if (invocation.TryGetInvokedMethodName(out var name) &&
                            (name == "Equals" || name == "nameof"))
                        {
                            continue;
                        }

                        if (PropertyChanged.IsSetAndRaiseCall(invocation, context.SemanticModel, context.CancellationToken) != AnalysisResult.No)
                        {
                            continue;
                        }

                        if (PropertyChanged.IsOnPropertyChanged(invocation, context.SemanticModel, context.CancellationToken) != AnalysisResult.No ||
                            PropertyChanged.IsPropertyChangedInvoke(invocation, context.SemanticModel, context.CancellationToken))
                        {
                            continue;
                        }

                        return;
                    }
                }

                context.ReportDiagnostic(Diagnostic.Create(Descriptor, assignment.GetLocation()));
            }
        }
Пример #2
0
 internal static bool TryFindSingleMutation(AccessorDeclarationSyntax setter, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ExpressionSyntax?backing)
 {
     backing = null;
     using (var mutations = MutationWalker.Borrow(setter, SearchScope.Member, semanticModel, cancellationToken))
     {
         if (mutations.All().TrySingle(x => x.IsEither(SyntaxKind.SimpleAssignmentExpression, SyntaxKind.Argument), out var mutation))
         {
             return(mutation switch
             {
                 AssignmentExpressionSyntax assignment => IsMutation(assignment, semanticModel, cancellationToken, out _, out backing),
                 ArgumentSyntax {
                     Parent : ArgumentListSyntax {
                         Parent : InvocationExpressionSyntax invocation
                     }
                 } => IsMutation(invocation, semanticModel, cancellationToken, out _, out backing),
Пример #3
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is PropertyDeclarationSyntax propertyDeclaration &&
                context.ContainingSymbol is IPropertySymbol property)
            {
                using (var walker = ReturnExpressions(propertyDeclaration))
                {
                    foreach (var returnValue in walker.ReturnValues)
                    {
                        if (IsProperty(returnValue))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC015PropertyIsRecursive, returnValue.GetLocation(), "Getter returns property, infinite recursion"));
                        }
                    }

                    if (walker.ReturnValues.TrySingle(out var single))
                    {
                        if (single.IsEither(SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.IdentifierName) &&
                            MemberPath.TrySingle(single, out var path) &&
                            context.SemanticModel.TryGetSymbol(path, context.CancellationToken, out IFieldSymbol? backingField))
                        {
                            if (!HasMatchingName(backingField, property))
                            {
                                context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC017BackingFieldNameMisMatch, path.GetLocation()));
                            }

                            if (propertyDeclaration.TryGetSetter(out var setAccessor))
                            {
                                using var mutationWalker = MutationWalker.Borrow(setAccessor, SearchScope.Member, context.SemanticModel, context.CancellationToken);
                                if (mutationWalker.IsEmpty)
                                {
                                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC021SetBackingField, setAccessor.GetLocation()));
                                }
                            }
                        }

                        if (single is LiteralExpressionSyntax &&
                            propertyDeclaration.TryGetSetter(out var set) &&
                            Setter.TryFindSingleMutation(set, context.SemanticModel, context.CancellationToken, out var fieldAccess))
                        {
                            context.ReportDiagnostic(
                                Diagnostic.Create(
                                    Descriptors.INPC019GetBackingField,
                                    single.GetLocation(),
                                    additionalLocations: new[] { fieldAccess.GetLocation() }));
                        }
                    }
                }

                if (propertyDeclaration.TryGetSetter(out var setter))
                {
                    if (ShouldBeExpressionBody(setter))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC020PreferExpressionBodyAccessor, setter.GetLocation()));
                    }

                    if (Property.GetsAndSetsSame(propertyDeclaration, context.SemanticModel, context.CancellationToken, out _, out _) == false)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC010GetAndSetSame, propertyDeclaration.Identifier.GetLocation()));
                    }

                    using (var assignmentWalker = AssignmentWalker.Borrow(setter))
                    {
                        if (assignmentWalker.Assignments.TryFirst(x => IsProperty(x.Left) && !x.Parent.IsKind(SyntaxKind.ObjectInitializerExpression), out var recursiveAssignment))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC015PropertyIsRecursive, recursiveAssignment.Left.GetLocation(), "Setter assigns property, infinite recursion"));
                        }
                    }

                    if (propertyDeclaration.TryGetGetter(out var getter))
                    {
                        if (ShouldBeExpressionBody(getter))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC020PreferExpressionBodyAccessor, getter.GetLocation()));
                        }
                    }
                }

                bool IsProperty(ExpressionSyntax expression)
                {
                    if (property.ExplicitInterfaceImplementations.Any())
                    {
                        return(false);
                    }

                    return(expression switch
                    {
                        IdentifierNameSyntax {
                            Identifier: { ValueText : { } name }
                        } => property.Name == name,
                        MemberAccessExpressionSyntax {
                            Expression : ThisExpressionSyntax _, Name : { Identifier : { ValueText : { } name } }
                        } => property.Name == name,