private static bool UsesValueAndMember(IfStatementSyntax ifStatement, SemanticModel semanticModel, CancellationToken cancellationToken, IParameterSymbol value, ISymbol member) { var usesValue = false; var usesMember = false; using (var pooledIdentifierNames = IdentifierNameWalker.Create(ifStatement.Condition)) { foreach (var identifierName in pooledIdentifierNames.Item.IdentifierNames) { var symbol = semanticModel.GetSymbolSafe(identifierName, cancellationToken); if (symbol == null) { continue; } if (symbol.Equals(value)) { usesValue = true; } if (symbol.Equals(member)) { usesMember = true; } } } return(usesMember && usesValue); }
/// <inheritdoc/> public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) .ConfigureAwait(false); var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { var token = syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start); if (string.IsNullOrEmpty(token.ValueText) || token.IsMissing) { continue; } var ctor = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <ConstructorDeclarationSyntax>(); var invocation = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <InvocationExpressionSyntax>(); if (ctor != null && invocation != null && invocation.Expression is MemberAccessExpressionSyntax memberAccess) { var condition = memberAccess.Expression; var symbol = semanticModel.GetSymbolSafe(condition, context.CancellationToken); if (symbol is IParameterSymbol parameter) { using (var pooled = IdentifierNameWalker.Create(ctor)) { foreach (var name in pooled.Item.IdentifierNames) { if (name.Identifier.ValueText == parameter.Name) { if (!condition.Contains(name)) { return; } } } } if (ctor.ParameterList.Parameters.TryGetFirst(p => p.Identifier.ValueText == parameter.Name, out ParameterSyntax parameterSyntax)) { context.RegisterCodeFix( CodeAction.Create( "Inject negated.", cancellationToken => ApplyInjectNegatedFixAsync(cancellationToken, context, parameterSyntax, invocation), nameof(InjectNegatedCodeFix)), diagnostic); continue; } } } } }
/// <inheritdoc/> public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) .ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { var token = syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start); if (string.IsNullOrEmpty(token.ValueText) || token.IsMissing) { continue; } var assignment = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) .FirstAncestorOrSelf <AssignmentExpressionSyntax>(); if (assignment == null || assignment.Left == null || assignment.Right == null) { continue; } if (assignment.Right is ParenthesizedLambdaExpressionSyntax lambda && lambda.Body != null) { using (var pooled = IdentifierNameWalker.Create(lambda.Body)) { var usesArg = false; foreach (var name in pooled.Item.IdentifierNames) { if (name.Identifier.ValueText == lambda.ParameterList.Parameters[0] .Identifier.ValueText) { return; } if (name.Identifier.ValueText == lambda.ParameterList.Parameters[1] .Identifier.ValueText) { usesArg = true; } } context.RegisterCodeFix( CodeAction.Create( "Observe.Event", cancellationToken => ApplyObserveEventLamdaFixAsync( cancellationToken, context, assignment, usesArg), nameof(EventSubscriptionToObserveFix)), diagnostic); } } if (assignment.Right is MemberAccessExpressionSyntax memberAccess) { var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (semanticModel.GetSymbolSafe(memberAccess, context.CancellationToken) is IMethodSymbol method && method.DeclaredAccessibility == Accessibility.Private && method.DeclaringSyntaxReferences.Length == 1) { var methodDeclaration = (MethodDeclarationSyntax)method.DeclaringSyntaxReferences[0].GetSyntax(context.CancellationToken); context.RegisterCodeFix( CodeAction.Create( "Observe.Event", cancellationToken => ApplyObserveEventMethodFixAsync(cancellationToken, context, assignment, methodDeclaration), nameof(EventSubscriptionToObserveFix)), diagnostic); } } } }
private static async Task <Document> ApplyObserveEventMethodFixAsync( CancellationToken cancellationToken, CodeFixContext context, AssignmentExpressionSyntax assignment, MethodDeclarationSyntax methodDeclaration) { var usesArg = false; if (methodDeclaration.ParameterList.Parameters.Any()) { using (var pooled = IdentifierNameWalker.Create((SyntaxNode)methodDeclaration.Body ?? methodDeclaration.ExpressionBody)) { foreach (var name in pooled.Item.IdentifierNames) { if (name.Identifier.ValueText == methodDeclaration.ParameterList.Parameters[0].Identifier.ValueText) { if (methodDeclaration.ParameterList.Parameters.Count == 1) { usesArg = true; continue; } return(context.Document); } if (methodDeclaration.ParameterList.Parameters.Count == 2 && name.Identifier.ValueText == methodDeclaration.ParameterList.Parameters[1] .Identifier.ValueText) { usesArg = true; } } } } var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken) .ConfigureAwait(false); var eventSymbol = (IEventSymbol)editor.SemanticModel.GetSymbolSafe(assignment.Left, cancellationToken); var observeSubscribe = GetObservableFromEventString(eventSymbol) .Replace("HANDLERTYPE", eventSymbol.Type.ToDisplayString()) .Replace("ARGTYPE", ArgType(eventSymbol)) .Replace("LEFT", assignment.Left.ToString()) .Replace("LAMBDA", Lambda(methodDeclaration, usesArg)); editor.ReplaceNode( assignment, SyntaxFactory.ParseExpression(observeSubscribe) .WithLeadingTrivia(assignment.GetLeadingTrivia()) .WithAdditionalAnnotations(Simplifier.Annotation)); if (methodDeclaration.ParameterList.Parameters.Count == 2) { editor.RemoveNode(methodDeclaration.ParameterList.Parameters[0]); } if (!usesArg && methodDeclaration.ParameterList.Parameters.Any()) { editor.RemoveNode(methodDeclaration.ParameterList.Parameters.Last()); } return(editor.GetChangedDocument()); }
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)); } } } } }