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())); } }
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),
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,