private static void ApplyObserveEventMethodFix( DocumentEditor editor, AssignmentExpressionSyntax assignment, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken) { var usesArg = false; if (methodDeclaration.ParameterList.Parameters.Any()) { using (var pooled = IdentifierNameWalker.Borrow((SyntaxNode)methodDeclaration.Body ?? methodDeclaration.ExpressionBody)) { foreach (var name in pooled.IdentifierNames) { if (name.Identifier.ValueText == methodDeclaration.ParameterList.Parameters[0].Identifier.ValueText) { if (methodDeclaration.ParameterList.Parameters.Count == 1) { usesArg = true; continue; } return; } if (methodDeclaration.ParameterList.Parameters.Count == 2 && name.Identifier.ValueText == methodDeclaration.ParameterList.Parameters[1] .Identifier.ValueText) { usesArg = true; } } } } var eventSymbol = (IEventSymbol)editor.SemanticModel.GetSymbolSafe(assignment.Left, cancellationToken); var observeSubscribe = GetObservableFromEventString(eventSymbol) .Replace("HANDLERTYPE", eventSymbol.Type.ToMinimalDisplayString(editor.SemanticModel, assignment.SpanStart)) .Replace("ARGTYPE", ArgType(eventSymbol)) .Replace("LEFT", assignment.Left.ToString()) .Replace("LAMBDA", Lambda(methodDeclaration, usesArg)); editor.AddUsing(SystemReactiveLinq) .ReplaceNode( assignment, SyntaxFactory.ParseExpression(observeSubscribe) .WithTriviaFrom(assignment) .WithSimplifiedNames() .WithAdditionalAnnotations(Formatter.Annotation, 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()); } }
/// <inheritdoc/> protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) { var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) .ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { if (syntaxRoot.TryFindNode(diagnostic, out AssignmentExpressionSyntax assignment)) { if (assignment.Right is ParenthesizedLambdaExpressionSyntax lambda && lambda.Body != null) { using (var pooled = IdentifierNameWalker.Borrow(lambda.Body)) { var usesArg = false; foreach (var name in pooled.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( "Observe.Event", (editor, cancellationToken) => ApplyObserveEventLambdaFix(editor, assignment, usesArg, cancellationToken), 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( "Observe.Event", (editor, cancellationToken) => ApplyObserveEventMethodFix(editor, assignment, methodDeclaration, cancellationToken), nameof(EventSubscriptionToObserveFix), diagnostic); } } } } }
/// <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.Borrow(ctor)) { foreach (var name in pooled.IdentifierNames) { if (name.Identifier.ValueText == parameter.Name) { if (!condition.Contains(name)) { return; } } } } if (ctor.ParameterList.Parameters.TryFirst(p => p.Identifier.ValueText == parameter.Name, out ParameterSyntax parameterSyntax)) { context.RegisterCodeFix( CodeAction.Create( "Inject negated.", cancellationToken => ApplyInjectNegatedFixAsync(context, parameterSyntax, invocation, cancellationToken), nameof(InjectNegatedCodeFix)), diagnostic); } } } } }
private static bool TryGetCanBeSlim(InvocationExpressionSyntax invocation, SyntaxNodeAnalysisContext context, out SyntaxNode path) { path = null; var argument = invocation.FirstAncestor <ArgumentSyntax>(); if (argument != null) { if (argument.TryGetParameter(context.SemanticModel, context.CancellationToken, out var parameter) && parameter.Type == KnownSymbol.IObservableOfT) { if (parameter.Type is INamedTypeSymbol namedType && namedType.TypeArguments[0] == KnownSymbol.Object) { path = invocation.Expression is MemberAccessExpressionSyntax memberAccess ? (SyntaxNode)memberAccess.Name : invocation; return(true); } } } var parentInvocation = invocation.FirstAncestor <InvocationExpressionSyntax>(); if (parentInvocation != null) { var parentMethod = context.SemanticModel.GetSymbolSafe(parentInvocation, context.CancellationToken); if (parentMethod == KnownSymbol.ObservableExtensions.Subscribe && parentInvocation.ArgumentList?.Arguments.TrySingle(out argument) == true) { if (argument.Expression is SimpleLambdaExpressionSyntax lambda) { using (var pooled = IdentifierNameWalker.Borrow(lambda.Body)) { if (pooled.IdentifierNames.TryFirst(x => x.Identifier.ValueText == lambda.Parameter.Identifier.ValueText, out IdentifierNameSyntax _)) { return(false); } path = invocation.Expression is MemberAccessExpressionSyntax memberAccess ? (SyntaxNode)memberAccess.Name : invocation; return(true); } } } } return(false); }