private static void Handle(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } if (context.Node is PropertyDeclarationSyntax propertyDeclaration && context.ContainingSymbol is IPropertySymbol property) { if (propertyDeclaration.ExpressionBody is ArrowExpressionClauseSyntax expressionBody && IsProperty(expressionBody.Expression, property)) { context.ReportDiagnostic(Diagnostic.Create(INPC015PropertyIsRecursive.Descriptor, expressionBody.Expression.GetLocation(), "Expression body returns property, infinite recursion")); } if (propertyDeclaration.TryGetGetter(out var getter)) { using (var returnWalker = ReturnExpressionsWalker.Borrow(getter)) { if (returnWalker.ReturnValues.TryFirst(x => IsProperty(x, property), out var recursiveReturnValue)) { context.ReportDiagnostic(Diagnostic.Create(INPC015PropertyIsRecursive.Descriptor, recursiveReturnValue.GetLocation(), "Getter returns property, infinite recursion")); } } } if (propertyDeclaration.TryGetSetter(out var setter)) { using (var assignmentWalker = AssignmentWalker.Borrow(setter)) { if (assignmentWalker.Assignments.TryFirst(x => IsProperty(x.Left, property), out var recursiveAssignment)) { context.ReportDiagnostic(Diagnostic.Create(INPC015PropertyIsRecursive.Descriptor, recursiveAssignment.Left.GetLocation(), "Setter assigns property, infinite recursion")); } if (getter != null) { if (property.ContainingType.Is(KnownSymbol.INotifyPropertyChanged) && Property.ShouldNotify(propertyDeclaration, property, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(Diagnostic.Create(INPC002MutablePublicPropertyShouldNotify.Descriptor, propertyDeclaration.GetLocation(), property.Name)); } if (assignmentWalker.Assignments.TrySingle(out var singleAssignment) && ReturnExpressionsWalker.TryGetSingle(getter, out var singleReturnValue) && !MemberPath.Uses(singleAssignment.Left, singleReturnValue, context)) { context.ReportDiagnostic(Diagnostic.Create(INPC010GetAndSetSame.Descriptor, propertyDeclaration.GetLocation())); } } } } } }
internal static bool TryFindSingleAssignment(AccessorDeclarationSyntax setter, [NotNullWhen(true)] out AssignmentExpressionSyntax?assignment) { assignment = null; if (setter == null) { return(false); } using (var walker = AssignmentWalker.Borrow(setter)) { if (walker.Assignments.TrySingle <AssignmentExpressionSyntax>(out assignment) && assignment.Right is IdentifierNameSyntax identifierName && identifierName.Identifier.ValueText == "value") { return(true); } } assignment = null; return(false); }
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,