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);
                        }
                    }
                }
            }
        }
Esempio n. 4
0
        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);
        }