public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            AssignmentExpressionSyntax assignment = root
                                                    .FindNode(context.Span, getInnermostNodeForTie: true)?
                                                    .FirstAncestorOrSelf <AssignmentExpressionSyntax>();

            if (assignment == null)
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case DiagnosticIdentifiers.UseCompoundAssignment:
                {
                    var binaryExpression = (BinaryExpressionSyntax)assignment.Right;

                    string operatorText = UseCompoundAssignmentRefactoring.GetCompoundOperatorText(binaryExpression);

                    CodeAction codeAction = CodeAction.Create(
                        $"Use {operatorText} operator",
                        cancellationToken => UseCompoundAssignmentRefactoring.RefactorAsync(context.Document, assignment, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.UsePostfixUnaryOperatorInsteadOfAssignment:
                {
                    SyntaxKind kind = UsePostfixUnaryOperatorInsteadOfAssignmentRefactoring.GetPostfixUnaryOperatorKind(assignment);

                    string operatorText = UsePostfixUnaryOperatorInsteadOfAssignmentRefactoring.GetOperatorText(kind);

                    CodeAction codeAction = CodeAction.Create(
                        $"Use {operatorText} operator",
                        c => UsePostfixUnaryOperatorInsteadOfAssignmentRefactoring.RefactorAsync(context.Document, assignment, kind, c),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.RemoveRedundantDelegateCreation:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Remove redundant delegate creation",
                        cancellationToken =>
                        {
                            return(RemoveRedundantDelegateCreationRefactoring.RefactorAsync(
                                       context.Document,
                                       (ObjectCreationExpressionSyntax)assignment.Right,
                                       cancellationToken));
                        },
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }
                }
            }
        }
        private static async Task <Document> GetFixAllAnalyzerAsync(FixAllScope scope, ImmutableArray <DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, int?codeFixIndex, Document document, int maxNumberOfIterations, CancellationToken cancellationToken)
        {
            var previousDiagnostics = ImmutableArray.Create <Diagnostic>();

            var fixAllProvider = codeFixProvider.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                return(null);
            }

            bool done;

            do
            {
                var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzers, new[] { document }, cancellationToken).ConfigureAwait(false);

                if (analyzerDiagnostics.Length == 0)
                {
                    break;
                }

                if (!AreDiagnosticsDifferent(analyzerDiagnostics, previousDiagnostics))
                {
                    break;
                }

                if (--maxNumberOfIterations < 0)
                {
                    Assert.True(false, "The upper limit for the number of fix all iterations was exceeded");
                }

                string equivalenceKey = null;
                foreach (var diagnostic in analyzerDiagnostics)
                {
                    if (!codeFixProvider.FixableDiagnosticIds.Contains(diagnostic.Id))
                    {
                        // do not pass unsupported diagnostics to a code fix provider
                        continue;
                    }

                    var actions = new List <CodeAction>();
                    var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), cancellationToken);
                    await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false);

                    if (actions.Count > (codeFixIndex ?? 0))
                    {
                        equivalenceKey = actions[codeFixIndex ?? 0].EquivalenceKey;
                        break;
                    }
                }

                previousDiagnostics = analyzerDiagnostics;

                done = true;

                FixAllContext.DiagnosticProvider fixAllDiagnosticProvider = TestDiagnosticProvider.Create(analyzerDiagnostics);

                FixAllContext fixAllContext = new FixAllContext(document, codeFixProvider, scope, equivalenceKey, codeFixProvider.FixableDiagnosticIds, fixAllDiagnosticProvider, cancellationToken);

                CodeAction action = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

                if (action == null)
                {
                    return(document);
                }

                var fixedDocument = await ApplyFixAsync(document, action, cancellationToken).ConfigureAwait(false);

                if (fixedDocument != document)
                {
                    done = false;
                    var newText = await fixedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    // workaround for issue #936 - force re-parsing to get the same sort of syntax tree as the original document.
                    document = document.WithText(newText);
                }
            }while (!done);

            return(document);
        }
Пример #3
0
        private CodeAction CreateCodeActionForIfElse(Document document, Diagnostic diagnostic, SyntaxNode node)
        {
            switch (node?.Kind())
            {
            case SyntaxKind.IfStatement:
            {
                var ifStatement = (IfStatementSyntax)node;

                StatementSyntax statement = ifStatement.Else?.Statement;

                if (statement != null)
                {
                    if (statement.IsKind(SyntaxKind.Block))
                    {
                        var block = (BlockSyntax)statement;

                        SyntaxList <StatementSyntax> statements = block.Statements;

                        if (statements.Any())
                        {
                            return(CreateCodeAction(document, diagnostic, ifStatement, statements));
                        }
                    }
                    else
                    {
                        return(CreateCodeAction(document, diagnostic, ifStatement, statement));
                    }
                }

                return(CodeAction.Create(
                           Title,
                           cancellationToken => document.RemoveStatementAsync(ifStatement, cancellationToken),
                           GetEquivalenceKey(diagnostic)));
            }

            case SyntaxKind.ElseClause:
            {
                var elseClause = (ElseClauseSyntax)node;

                if (elseClause.IsParentKind(SyntaxKind.IfStatement))
                {
                    var ifStatement = (IfStatementSyntax)elseClause.Parent;

                    if (ifStatement.IsTopmostIf())
                    {
                        StatementSyntax statement = ifStatement.Statement;

                        if (statement != null)
                        {
                            if (statement.IsKind(SyntaxKind.Block))
                            {
                                var block = (BlockSyntax)statement;

                                SyntaxList <StatementSyntax> statements = block.Statements;

                                if (statements.Any())
                                {
                                    return(CreateCodeAction(document, diagnostic, ifStatement, statements));
                                }
                            }
                            else
                            {
                                return(CreateCodeAction(document, diagnostic, ifStatement, statement));
                            }
                        }
                    }
                }

                return(CodeAction.Create(
                           Title,
                           cancellationToken => document.RemoveNodeAsync(elseClause, cancellationToken),
                           GetEquivalenceKey(diagnostic)));
            }

            case SyntaxKind.Block:
            {
                return(CreateCodeActionForIfElse(document, diagnostic, node.Parent));
            }
            }

            return(null);
        }
Пример #4
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            MemberDeclarationSyntax memberDeclaration = root
                                                        .FindNode(context.Span, getInnermostNodeForTie: true)?
                                                        .FirstAncestorOrSelf <MemberDeclarationSyntax>();

            Debug.Assert(memberDeclaration != null, $"{nameof(memberDeclaration)} is null");

            if (memberDeclaration == null)
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case DiagnosticIdentifiers.FormatDeclarationBraces:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Format braces",
                        cancellationToken => FormatDeclarationBracesRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.RemoveRedundantOverridingMember:
                {
                    CodeAction codeAction = CodeAction.Create(
                        $"Remove redundant overridding {memberDeclaration.GetTitle()}",
                        cancellationToken => context.Document.RemoveMemberAsync(memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.AddDefaultAccessModifier:
                {
                    var accessibility = (Accessibility)Enum.Parse(
                        typeof(Accessibility),
                        context.Diagnostics[0].Properties[nameof(Accessibility)]);

                    CodeAction codeAction = CodeAction.Create(
                        "Add default access modifier",
                        cancellationToken => AddDefaultAccessModifierRefactoring.RefactorAsync(context.Document, memberDeclaration, accessibility, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.AddEmptyLineBetweenDeclarations:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Add empty line",
                        cancellationToken => AddEmptyLineBetweenDeclarationsRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.RemoveRedundantSealedModifier:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Remove 'sealed' modifier",
                        cancellationToken => RemoveRedundantSealedModifierRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.AvoidSemicolonAtEndOfDeclaration:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Remove unnecessary semicolon",
                        cancellationToken => AvoidSemicolonAtEndOfDeclarationRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.ReorderModifiers:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Reorder modifiers",
                        cancellationToken => ReorderModifiersRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.MarkFieldAsReadOnly:
                {
                    var fieldDeclaration = (FieldDeclarationSyntax)memberDeclaration;

                    SeparatedSyntaxList <VariableDeclaratorSyntax> declarators = fieldDeclaration.Declaration.Variables;

                    string title = (declarators.Count == 1)
                                ? $"Mark '{declarators[0].Identifier.ValueText}' as read-only"
                                : "Mark fields as read-only";

                    CodeAction codeAction = CodeAction.Create(
                        title,
                        cancellationToken => MarkFieldAsReadOnlyRefactoring.RefactorAsync(context.Document, fieldDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.UseConstantInsteadOfField:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Use constant instead of field",
                        cancellationToken => UseConstantInsteadOfFieldRefactoring.RefactorAsync(context.Document, (FieldDeclarationSyntax)memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.UseReadOnlyAutoProperty:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Use read-only auto-property",
                        cancellationToken => UseReadOnlyAutoPropertyRefactoring.RefactorAsync(context.Document, (PropertyDeclarationSyntax)memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case DiagnosticIdentifiers.ReplaceCommentWithDocumentationComment:
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Replace comment with documentation comment",
                        cancellationToken => ReplaceCommentWithDocumentationCommentRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken),
                        diagnostic.Id + EquivalenceKeySuffix);

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }
                }
            }
        }
Пример #5
0
        private async Task <IEnumerable <CodeActionOperation> > GetFixAllOperationsAsync(CodeAction codeAction, FixAllContext fixAllContext, string fixAllPreviewChangesTitle, bool showPreviewChangesDialog)
        {
            // We have computed the fix all occurrences code fix.
            // Now fetch the new solution with applied fix and bring up the Preview changes dialog.

            var cancellationToken = fixAllContext.CancellationToken;
            var workspace         = fixAllContext.Project.Solution.Workspace;

            cancellationToken.ThrowIfCancellationRequested();
            var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false);

            if (operations == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();
            var newSolution = await codeAction.GetChangedSolutionInternalAsync(cancellationToken).ConfigureAwait(false);

            if (showPreviewChangesDialog)
            {
                newSolution = PreviewChanges(
                    fixAllContext.Project.Solution,
                    newSolution,
                    fixAllPreviewChangesTitle,
                    codeAction.Title,
                    fixAllContext.Project.Language,
                    workspace,
                    cancellationToken);
                if (newSolution == null)
                {
                    return(null);
                }
            }

            // Get a code action, with apply changes operation replaced with the newSolution.
            return(GetNewFixAllOperations(operations, newSolution, cancellationToken));
        }
Пример #6
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(
                    root,
                    context.Span,
                    out SyntaxNode node,
                    findInsideTrivia: true,
                    predicate: f => f.IsKind(SyntaxKind.UsingDirective, SyntaxKind.RegionDirectiveTrivia, SyntaxKind.EndRegionDirectiveTrivia)))
            {
                return;
            }

            Document   document   = context.Document;
            Diagnostic diagnostic = context.Diagnostics[0];

            switch (node)
            {
            case UsingDirectiveSyntax usingDirective:
            {
                if (context.Span.Start == usingDirective.SpanStart)
                {
                    CodeAction codeAction = CodeAction.Create(
                        CodeFixTitles.AddEmptyLine,
                        ct => AddEmptyLineBeforeUsingDirectiveAsync(document, usingDirective, ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }
                else
                {
                    CodeAction codeAction = CodeAction.Create(
                        CodeFixTitles.AddEmptyLine,
                        ct => CodeFixHelpers.AppendEndOfLineAsync(document, usingDirective, ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }

                break;
            }

            case RegionDirectiveTriviaSyntax regionDirective:
            {
                CodeAction codeAction = CodeAction.Create(
                    CodeFixTitles.AddEmptyLine,
                    ct => CodeFixHelpers.AddEmptyLineBeforeDirectiveAsync(document, regionDirective, ct),
                    GetEquivalenceKey(diagnostic));

                context.RegisterCodeFix(codeAction, diagnostic);
                break;
            }

            case EndRegionDirectiveTriviaSyntax endRegionDirective:
            {
                CodeAction codeAction = CodeAction.Create(
                    CodeFixTitles.AddEmptyLine,
                    ct => CodeFixHelpers.AddEmptyLineAfterDirectiveAsync(document, endRegionDirective, ct),
                    GetEquivalenceKey(diagnostic));

                context.RegisterCodeFix(codeAction, diagnostic);
                break;
            }
            }
        }
Пример #7
0
 public void RegisterRefactoring(CodeAction action) => RegisterRefactoring(action, applicableToSpan: null); // We could pass this.Span as applicableToSpan instead but that would cause these refactorings to always be closest to current selection
Пример #8
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsAnyCodeFixEnabled(
                    CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral,
                    CodeFixIdentifiers.UseYieldReturnInsteadOfReturn))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            SyntaxNode node = root.FindNode(context.Span, getInnermostNodeForTie: true);

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType:
                {
                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) &&
                        node?.Kind() == SyntaxKind.StringLiteralExpression)
                    {
                        var literalExpression = (LiteralExpressionSyntax)node;

                        if (literalExpression.Token.ValueText.Length == 1)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            if (semanticModel.GetTypeInfo(node, context.CancellationToken).ConvertedType?.SpecialType == SpecialType.System_Char)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Replace string literal with character literal",
                                    cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken),
                                    GetEquivalenceKey(diagnostic));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) &&
                        node.IsParentKind(SyntaxKind.ReturnStatement))
                    {
                        var returnStatement = (ReturnStatementSyntax)node.Parent;

                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken);

                        if (containingSymbol?.Kind == SymbolKind.Method &&
                            ((IMethodSymbol)containingSymbol).ReturnType.OriginalDefinition.IsIEnumerableOrIEnumerableOfT())
                        {
                            CodeAction codeAction = CodeAction.Create(
                                "Use yield return instead of return",
                                cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }
                }
            }
        }
Пример #9
0
        public static async Task <CodeAction> GetFixAsync(
            ImmutableArray <Diagnostic> diagnostics,
            DiagnosticDescriptor descriptor,
            CodeFixProvider fixer,
            Project project,
            CodeFixerOptions options,
            IFormatProvider formatProvider      = null,
            CancellationToken cancellationToken = default)
        {
            if (diagnostics.Length == 1)
            {
                return(await GetFixAsync(diagnostics[0], fixer, project, options, formatProvider, cancellationToken).ConfigureAwait(false));
            }

            FixAllProvider fixAllProvider = fixer.GetFixAllProvider();

            if (fixAllProvider == null)
            {
                if (options.DiagnosticIdsFixableOneByOne.Contains(descriptor.Id))
                {
                    return(await GetFixAsync(diagnostics[0], fixer, project, options, formatProvider, cancellationToken).ConfigureAwait(false));
                }

                WriteLine($"  '{fixer.GetType().FullName}' does not have FixAllProvider", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(null);
            }

            if (!fixAllProvider.GetSupportedFixAllDiagnosticIds(fixer).Any(f => f == descriptor.Id))
            {
                WriteLine($"  '{fixAllProvider.GetType().FullName}' does not support diagnostic '{descriptor.Id}'", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(null);
            }

            if (!fixAllProvider.GetSupportedFixAllScopes().Any(f => f == FixAllScope.Project))
            {
                WriteLine($"  '{fixAllProvider.GetType().FullName}' does not support scope '{FixAllScope.Project}'", ConsoleColor.Yellow, Verbosity.Diagnostic);
                return(null);
            }

            var multipleFixesInfos = new HashSet <MultipleFixesInfo>();

            CodeAction action = null;

            options.DiagnosticFixMap.TryGetValue(descriptor.Id, out string equivalenceKey);

            foreach (Diagnostic diagnostic in diagnostics)
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (!diagnostic.Location.IsInSource)
                {
                    continue;
                }

                Document document = project.GetDocument(diagnostic.Location.SourceTree);

                if (document == null)
                {
                    continue;
                }

                CodeAction fixCandidate = await GetFixAsync(diagnostic, fixer, document, multipleFixesInfos, options, cancellationToken).ConfigureAwait(false);

                if (fixCandidate == null)
                {
                    continue;
                }

                if (equivalenceKey != null &&
                    equivalenceKey != fixCandidate.EquivalenceKey)
                {
                    break;
                }

                action = fixCandidate;

                var fixAllContext = new FixAllContext(
                    document,
                    fixer,
                    FixAllScope.Project,
                    action.EquivalenceKey,
                    new string[] { descriptor.Id },
                    new FixAllDiagnosticProvider(diagnostics),
                    cancellationToken);

                CodeAction fix = await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false);

                if (fix != null)
                {
                    WriteLine($"  CodeFixProvider: '{fixer.GetType().FullName}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);

                    if (!string.IsNullOrEmpty(action.EquivalenceKey))
                    {
                        WriteLine($"  EquivalenceKey:  '{action.EquivalenceKey}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);
                    }

                    WriteLine($"  FixAllProvider:  '{fixAllProvider.GetType().FullName}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);

                    return(fix);
                }

                WriteLine($"  Fixer '{fixer.GetType().FullName}' registered no action for diagnostic '{descriptor.Id}'", ConsoleColor.DarkGray, Verbosity.Diagnostic);
                LogHelpers.WriteDiagnostics(diagnostics, baseDirectoryPath: Path.GetDirectoryName(project.FilePath), formatProvider: formatProvider, indentation: "    ", maxCount: 10, verbosity: Verbosity.Diagnostic);
            }

            return(null);
        }
Пример #10
0
        /// <inheritdoc/>
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            foreach (var diagnostic in context.Diagnostics)
            {
                if (!diagnostic.Id.Equals(SA1003SymbolsMustBeSpacedCorrectly.DiagnosticId))
                {
                    continue;
                }

                var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

                SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start);
                if (token.IsMissing)
                {
                    continue;
                }

                Dictionary <SyntaxToken, SyntaxToken> replacements = new Dictionary <SyntaxToken, SyntaxToken>();

                // always a space before unless at the beginning of a line or after certain tokens
                bool firstInLine = token.HasLeadingTrivia || token.GetLocation()?.GetMappedLineSpan().StartLinePosition.Character == 0;
                if (!firstInLine)
                {
                    SyntaxToken precedingToken            = token.GetPreviousToken();
                    SyntaxToken correctedPrecedingNoSpace = precedingToken.WithoutTrailingWhitespace();
                    switch (precedingToken.Kind())
                    {
                    case SyntaxKind.OpenParenToken:
                    case SyntaxKind.CloseParenToken:
                    case SyntaxKind.OpenBracketToken:
                    case SyntaxKind.CloseBracketToken:
                        // remove any whitespace before
                        replacements[precedingToken] = correctedPrecedingNoSpace;
                        break;

                    default:
                        if (!precedingToken.TrailingTrivia.Any(SyntaxKind.WhitespaceTrivia))
                        {
                            SyntaxToken correctedPreceding = correctedPrecedingNoSpace.WithTrailingTrivia(correctedPrecedingNoSpace.TrailingTrivia.Insert(0, SyntaxFactory.Whitespace(" ")));
                            replacements[precedingToken] = correctedPreceding;
                        }
                        break;
                    }
                }

                if (token.Parent is BinaryExpressionSyntax)
                {
                    // include a space after unless last on line
                    if (!token.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia))
                    {
                        SyntaxToken correctedOperatorNoSpace = token.WithoutTrailingWhitespace();
                        SyntaxToken correctedOperator        =
                            correctedOperatorNoSpace
                            .WithTrailingTrivia(correctedOperatorNoSpace.TrailingTrivia.Insert(0, SyntaxFactory.Whitespace(" ")))
                            .WithoutFormatting();
                        replacements[token] = correctedOperator;
                    }
                }
                else if (token.Parent is PrefixUnaryExpressionSyntax)
                {
                    // do not include a space after (includes new line characters)
                    SyntaxToken correctedOperatorNoSpace = token.WithoutTrailingWhitespace(removeEndOfLineTrivia: true).WithoutFormatting();
                    replacements[token] = correctedOperatorNoSpace;
                }

                if (replacements.Count == 0)
                {
                    continue;
                }

                var      transformed     = root.ReplaceTokens(replacements.Keys, (original, maybeRewritten) => replacements[original]);
                Document updatedDocument = context.Document.WithSyntaxRoot(transformed);
                context.RegisterCodeFix(CodeAction.Create("Fix spacing", t => Task.FromResult(updatedDocument)), diagnostic);
            }
        }
Пример #11
0
        private static async Task <Solution> ApplyFixAsync(CodeAction codeAction)
        {
            var operations = await codeAction.GetOperationsAsync(CancellationToken.None);

            return(Assert.Single(operations.OfType <ApplyChangesOperation>()).ChangedSolution);
        }
Пример #12
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddTypeArgument))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out TypeSyntax type))
            {
                return;
            }

            if (!type.IsKind(SyntaxKind.IdentifierName))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.UsingGenericTypeRequiresTypeArguments:
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(type, context.CancellationToken);

                    foreach (ISymbol symbol in symbolInfo.CandidateSymbols)
                    {
                        if (symbol is INamedTypeSymbol namedTypeSymbol)
                        {
                            ImmutableArray <ITypeParameterSymbol> typeParameters = namedTypeSymbol.TypeParameters;

                            if (typeParameters.Any())
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    GetTitle(typeParameters),
                                    cancellationToken =>
                                    {
                                        SeparatedSyntaxList <TypeSyntax> typeArguments = CreateTypeArguments(typeParameters, type.SpanStart, semanticModel).ToSeparatedSyntaxList();

                                        var identifierName = (IdentifierNameSyntax)type;

                                        GenericNameSyntax newNode = SyntaxFactory.GenericName(identifierName.Identifier, SyntaxFactory.TypeArgumentList(typeArguments));

                                        return(context.Document.ReplaceNodeAsync(type, newNode, cancellationToken));
                                    },
                                    GetEquivalenceKey(diagnostic, SymbolDisplay.GetString(namedTypeSymbol)));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }
                    }

                    break;
                }
                }
            }
        }
Пример #13
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            var nodeToFix = root?.FindNode(context.Span, getInnermostNodeForTie: true);

            if (nodeToFix == null)
            {
                return;
            }

            var diagnostic = context.Diagnostics.FirstOrDefault();

            if (diagnostic == null)
            {
                return;
            }

            if (!Enum.TryParse(diagnostic.Properties.GetValueOrDefault("Data", ""), out OptimizeLinqUsageData data) || data == OptimizeLinqUsageData.None)
            {
                return;
            }

            // If the so-called nodeToFix is a Name (most likely a method name such as 'Select' or 'Count'),
            // adjust it so that it refers to its InvocationExpression ancestor instead.
            if ((nodeToFix.IsKind(SyntaxKind.IdentifierName) || nodeToFix.IsKind(SyntaxKind.GenericName)) && !TryGetInvocationExpressionAncestor(ref nodeToFix))
            {
                return;
            }

            var title = "Optimize linq usage";

            switch (data)
            {
            case OptimizeLinqUsageData.UseLengthProperty:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseLengthProperty(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseLongLengthProperty:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseLongLengthProperty(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseCountProperty:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseCountProperty(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseFindMethod:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseFindMethod(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseIndexer:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseIndexer(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseIndexerFirst:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseIndexerFirst(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseIndexerLast:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseIndexerLast(context.Document, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.DuplicatedOrderBy:
                var useThenByTitle     = "Use " + diagnostic.Properties["ExpectedMethodName"];
                var removeOrderByTitle = "Remove " + diagnostic.Properties["MethodName"];
                context.RegisterCodeFix(CodeAction.Create(useThenByTitle, ct => UseThenBy(context.Document, diagnostic, ct), equivalenceKey: "UseThenBy"), context.Diagnostics);
                context.RegisterCodeFix(CodeAction.Create(removeOrderByTitle, ct => RemoveDuplicatedOrderBy(context.Document, diagnostic, ct), equivalenceKey: "RemoveOrderBy"), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.CombineWhereWithNextMethod:
                context.RegisterCodeFix(CodeAction.Create(title, ct => CombineWhereWithNextMethod(context.Document, diagnostic, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseTrue:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseConstantValue(context.Document, nodeToFix, constantValue: true, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseFalse:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseConstantValue(context.Document, nodeToFix, constantValue: false, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseNotAny:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseAny(context.Document, diagnostic, nodeToFix, constantValue: false, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseAny:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseAny(context.Document, diagnostic, nodeToFix, constantValue: true, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseTakeAndCount:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseTakeAndCount(context.Document, diagnostic, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseSkipAndAny:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseSkipAndAny(context.Document, diagnostic, nodeToFix, comparandValue: true, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseSkipAndNotAny:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseSkipAndAny(context.Document, diagnostic, nodeToFix, comparandValue: false, ct), equivalenceKey: title), context.Diagnostics);
                break;

            case OptimizeLinqUsageData.UseCastInsteadOfSelect:
                context.RegisterCodeFix(CodeAction.Create(title, ct => UseCastInsteadOfSelect(context.Document, diagnostic, nodeToFix, ct), equivalenceKey: title), context.Diagnostics);
                break;
            }
        }
Пример #14
0
		public ValidCodeAction (CodeAction codeAction, TextSpan validSegment)
		{
			this.CodeAction = codeAction;
			this.ValidSegment = validSegment;
		}
Пример #15
0
 internal void RegisterRefactoring(CodeAction action, TextSpan applicableToSpan) => RegisterRefactoring(action, new Nullable <TextSpan>(applicableToSpan));
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out ExpressionSyntax expression, predicate: f => f.IsKind(SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.ElementAccessExpression)))
            {
                return;
            }

            if (IsPartOfLeftSideOfAssignment())
            {
                return;
            }

            if (expression
                .WalkUp(f => f.IsKind(SyntaxKind.InvocationExpression, SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.ElementAccessExpression, SyntaxKind.ParenthesizedExpression))
                .IsParentKind(SyntaxKind.AwaitExpression))
            {
                return;
            }

            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

            if (expression.IsInExpressionTree(semanticModel, context.CancellationToken))
            {
                return;
            }

            SyntaxKind kind = expression.Kind();

            if (kind == SyntaxKind.SimpleMemberAccessExpression)
            {
                expression = ((MemberAccessExpressionSyntax)expression).Expression;
            }
            else if (kind == SyntaxKind.ElementAccessExpression)
            {
                expression = ((ElementAccessExpressionSyntax)expression).Expression;
            }

            CodeAction codeAction = CodeAction.Create(
                "Use conditional access",
                cancellationToken => RefactorAsync(context.Document, expression, cancellationToken),
                GetEquivalenceKey(DiagnosticIdentifiers.AvoidNullReferenceException));

            context.RegisterCodeFix(codeAction, context.Diagnostics);

            bool IsPartOfLeftSideOfAssignment()
            {
                for (SyntaxNode node = expression; node != null; node = node.Parent)
                {
                    var assignmentExpression = node.Parent as AssignmentExpressionSyntax;

                    if (assignmentExpression?.Left == node)
                    {
                        return(true);
                    }
                }

                return(false);
            }
        }
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out TypeDeclarationSyntax typeDeclaration))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.TypeDefinesEqualityOperatorButDoesNotOverrideObjectEquals:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.DefineObjectEquals))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    var typeSymbol = semanticModel.GetDeclaredSymbol(typeDeclaration, context.CancellationToken) as ITypeSymbol;

                    if (typeSymbol?.IsErrorType() != false)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Override object.Equals",
                        cancellationToken =>
                        {
                            TypeSyntax type = typeSymbol.ToMinimalTypeSyntax(semanticModel, typeDeclaration.Identifier.SpanStart);

                            MethodDeclarationSyntax methodDeclaration = ObjectEqualsMethodDeclaration(type);

                            TypeDeclarationSyntax newNode = MemberDeclarationInserter.Default.Insert(typeDeclaration, methodDeclaration);

                            return(context.Document.ReplaceNodeAsync(typeDeclaration, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.TypeDefinesEqualityOperatorButDoesNotOverrideObjectGetHashCode:
                case CompilerDiagnosticIdentifiers.TypeOverridesObjectEqualsButDoesNotOverrideObjectGetHashCode:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.DefineObjectGetHashCode))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    MethodDeclarationSyntax methodDeclaration = ObjectGetHashCodeMethodDeclaration();

                    CodeAction codeAction = CodeAction.Create(
                        "Override object.GetHashCode",
                        cancellationToken =>
                        {
                            TypeDeclarationSyntax newNode = MemberDeclarationInserter.Default.Insert(typeDeclaration, methodDeclaration);

                            return(context.Document.ReplaceNodeAsync(typeDeclaration, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }
                }
            }
        }
Пример #18
0
 public static void ApplyFix(CodeAction action, IRefactoringContext context)
 {
     using (var script = context.CreateScript()) {
         action.Run(context, script);
     }
 }
Пример #19
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out BaseListSyntax baseList))
            {
                return;
            }

            if (baseList.ContainsDiagnostics)
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.CS1722_BaseClassMustComeBeforeAnyInterface:
                {
                    if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.MoveBaseClassBeforeAnyInterface, context.Document, root.SyntaxTree))
                    {
                        return;
                    }

                    SeparatedSyntaxList <BaseTypeSyntax> types = baseList.Types;

                    if (types.Count > 1)
                    {
                        BaseTypeSyntax baseType = types.First(f => context.Span.Contains(f.Span));

                        CodeAction codeAction = CodeAction.Create(
                            $"Move '{baseType.Type}' before any interface",
                            ct =>
                            {
                                BaseTypeSyntax firstType = types[0];

                                SeparatedSyntaxList <BaseTypeSyntax> newTypes = types
                                                                                .Replace(baseType, firstType.WithTriviaFrom(baseType))
                                                                                .ReplaceAt(0, baseType.WithTriviaFrom(firstType));

                                BaseListSyntax newBaseList = baseList.WithTypes(newTypes);

                                return(context.Document.ReplaceNodeAsync(baseList, newBaseList, ct));
                            },
                            GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.CS0713_StaticClassCannotDeriveFromType:
                case CompilerDiagnosticIdentifiers.CS0714_StaticClassCannotImplementInterfaces:
                {
                    if (baseList.Parent is not ClassDeclarationSyntax classDeclaration)
                    {
                        break;
                    }

                    if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeClassNonStatic, context.Document, root.SyntaxTree))
                    {
                        ModifiersCodeFixRegistrator.RemoveModifier(
                            context,
                            diagnostic,
                            classDeclaration,
                            SyntaxKind.StaticKeyword,
                            title: "Make class non-static",
                            additionalKey: CodeFixIdentifiers.MakeClassNonStatic);
                    }

                    if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveBaseList, context.Document, root.SyntaxTree))
                    {
                        CodeAction codeAction = CodeAction.Create(
                            "Remove base list",
                            ct =>
                            {
                                SyntaxToken token = baseList.GetFirstToken().GetPreviousToken();

                                SyntaxTriviaList trivia = token.TrailingTrivia.EmptyIfWhitespace()
                                                          .AddRange(baseList.GetLeadingTrivia().EmptyIfWhitespace())
                                                          .AddRange(baseList.GetTrailingTrivia());

                                ClassDeclarationSyntax newNode = classDeclaration
                                                                 .ReplaceToken(token, token.WithTrailingTrivia(trivia))
                                                                 .WithBaseList(null);

                                return(context.Document.ReplaceNodeAsync(classDeclaration, newNode, ct));
                            },
                            GetEquivalenceKey(diagnostic, CodeFixIdentifiers.RemoveBaseList));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }
                }
            }
        }
Пример #20
0
 public UnifiedSuggestedAction(Workspace workspace, CodeAction codeAction, CodeActionPriority codeActionPriority)
 {
     Workspace          = workspace;
     OriginalCodeAction = codeAction;
     CodeActionPriority = codeActionPriority;
 }
Пример #21
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsAnyCodeFixEnabled(
                    CodeFixIdentifiers.UseYieldReturnInsteadOfReturn,
                    CodeFixIdentifiers.RemoveReturnKeyword,
                    CodeFixIdentifiers.RemoveReturnExpression,
                    CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out ReturnStatementSyntax returnStatement))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.CannotReturnValueFromIterator:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseYieldReturnInsteadOfReturn))
                    {
                        break;
                    }

                    ExpressionSyntax expression = returnStatement.Expression;

                    if (expression != null)
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken);

                        if (containingSymbol?.IsKind(SymbolKind.Method) == true)
                        {
                            var methodSymbol = (IMethodSymbol)containingSymbol;

                            ITypeSymbol returnType = methodSymbol.ReturnType;

                            var replacementKind = SyntaxKind.None;

                            if (returnType.SpecialType == SpecialType.System_Collections_IEnumerable)
                            {
                                if (semanticModel
                                    .GetTypeSymbol(expression, context.CancellationToken)
                                    .IsIEnumerableOrConstructedFromIEnumerableOfT())
                                {
                                    replacementKind = SyntaxKind.ForEachStatement;
                                }
                                else
                                {
                                    replacementKind = SyntaxKind.YieldReturnStatement;
                                }
                            }
                            else if (returnType.IsNamedType())
                            {
                                var namedTypeSymbol = (INamedTypeSymbol)returnType;

                                if (namedTypeSymbol.IsConstructedFromIEnumerableOfT())
                                {
                                    if (semanticModel.IsImplicitConversion(expression, namedTypeSymbol.TypeArguments[0]))
                                    {
                                        replacementKind = SyntaxKind.YieldReturnStatement;
                                    }
                                    else
                                    {
                                        replacementKind = SyntaxKind.ForEachStatement;
                                    }
                                }
                            }

                            if (replacementKind == SyntaxKind.YieldReturnStatement ||
                                (replacementKind == SyntaxKind.ForEachStatement && !returnStatement.SpanContainsDirectives()))
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Use yield return instead of return",
                                    cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, replacementKind, semanticModel, cancellationToken),
                                    GetEquivalenceKey(diagnostic));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.SinceMethodReturnsVoidReturnKeywordMustNotBeFollowedByObjectExpression:
                case CompilerDiagnosticIdentifiers.SinceMethodIsAsyncMethodThatReturnsTaskReturnKeywordMustNotBeFollowedByObjectExpression:
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression))
                    {
                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, returnStatement.Expression, semanticModel);
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveReturnExpression))
                    {
                        ISymbol symbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken);

                        if (symbol?.IsMethod() == true)
                        {
                            var methodSymbol = (IMethodSymbol)symbol;

                            if (methodSymbol.ReturnsVoid ||
                                methodSymbol.ReturnType.Equals(semanticModel.GetTypeByMetadataName(MetadataNames.System_Threading_Tasks_Task)))
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Remove return expression",
                                    cancellationToken =>
                                    {
                                        ReturnStatementSyntax newNode = returnStatement
                                                                        .WithExpression(null)
                                                                        .WithFormatterAnnotation();

                                        return(context.Document.ReplaceNodeAsync(returnStatement, newNode, cancellationToken));
                                    },
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.RemoveReturnExpression));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveReturnKeyword))
                    {
                        ExpressionSyntax expression = returnStatement.Expression;

                        if (expression.IsKind(
                                SyntaxKind.InvocationExpression,
                                SyntaxKind.ObjectCreationExpression,
                                SyntaxKind.PreDecrementExpression,
                                SyntaxKind.PreIncrementExpression,
                                SyntaxKind.PostDecrementExpression,
                                SyntaxKind.PostIncrementExpression) ||
                            expression is AssignmentExpressionSyntax)
                        {
                            CodeAction codeAction = CodeAction.Create(
                                "Remove 'return'",
                                cancellationToken =>
                                {
                                    SyntaxTriviaList leadingTrivia = returnStatement
                                                                     .GetLeadingTrivia()
                                                                     .AddRange(returnStatement.ReturnKeyword.TrailingTrivia.EmptyIfWhitespace())
                                                                     .AddRange(expression.GetLeadingTrivia().EmptyIfWhitespace());

                                    ExpressionStatementSyntax newNode = SyntaxFactory.ExpressionStatement(
                                        expression.WithLeadingTrivia(leadingTrivia),
                                        returnStatement.SemicolonToken);

                                    return(context.Document.ReplaceNodeAsync(returnStatement, newNode, cancellationToken));
                                },
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.RemoveReturnKeyword));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }
                }
            }
        }
Пример #22
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            Diagnostic diagnostic = context.Diagnostics[0];

            if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveUnreachableCode))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out StatementSyntax statement))
            {
                return;
            }

            Debug.Assert(context.Span.Start == statement.SpanStart, statement.ToString());

            if (context.Span.Start != statement.SpanStart)
            {
                return;
            }

            CodeAction codeAction = CreateCodeActionForIfElse(context.Document, diagnostic, statement.Parent);

            if (codeAction != null)
            {
                context.RegisterCodeFix(codeAction, diagnostic);
                return;
            }

            StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(statement);

            if (statementsInfo.Success)
            {
                codeAction = CodeAction.Create(
                    Title,
                    cancellationToken =>
                {
                    SyntaxList <StatementSyntax> statements = statementsInfo.Statements;

                    int index = statements.IndexOf(statement);

                    if (index == statements.Count - 1)
                    {
                        return(context.Document.RemoveStatementAsync(statement, cancellationToken));
                    }
                    else
                    {
                        SyntaxRemoveOptions removeOptions = SyntaxRefactorings.DefaultRemoveOptions;

                        if (statement.GetLeadingTrivia().IsEmptyOrWhitespace())
                        {
                            removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia;
                        }

                        if (statements.Last().GetTrailingTrivia().IsEmptyOrWhitespace())
                        {
                            removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia;
                        }

                        return(context.Document.RemoveNodesAsync(statements.Skip(index), removeOptions, cancellationToken));
                    }
                },
                    GetEquivalenceKey(diagnostic));

                context.RegisterCodeFix(codeAction, diagnostic);
            }
        }
 protected static CodeAction CreateCodeAction(string title, Func <CancellationToken, Task <Solution> > callback, string equivalenceKey = null, params object[] arguments)
 {
     return(CodeAction.Create(string.Format(title, arguments), callback, equivalenceKey));
 }
Пример #24
0
        public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            var(container, explicitName, name) = await GetContainerAsync(context).ConfigureAwait(false);

            if (container == null)
            {
                return;
            }

            if (!CheckExplicitNameAllowsConversion(explicitName))
            {
                return;
            }

            var(document, _, cancellationToken) = context;
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var member = semanticModel.GetDeclaredSymbol(container, cancellationToken);

            Contract.ThrowIfNull(member);

            if (!CheckMemberCanBeConverted(member))
            {
                return;
            }

            var project = document.Project;

            var directlyImplementedMembers = new MemberImplementationMap();

            // Grab the name off of the *interface* member being implemented, not the implementation
            // member.  Interface member names are the expected names that people expect to see
            // (like "GetEnumerator"), instead of the auto-generated names that the compiler makes
            // like: "System.IEnumerable.GetEnumerator"
            directlyImplementedMembers.AddRange(member, member.ExplicitOrImplicitInterfaceImplementations());

            var firstImplName = member.ExplicitOrImplicitInterfaceImplementations().First().Name;
            var codeAction    = CodeAction.Create(
                string.Format(Implement_0, firstImplName),
                c => ChangeImplementationAsync(project, directlyImplementedMembers, c),
                nameof(Implement_0) + firstImplName);

            var containingType = member.ContainingType;
            var interfaceTypes = directlyImplementedMembers.SelectMany(kvp => kvp.Value).Select(
                s => s.ContainingType).Distinct().ToImmutableArray();

            var implementedMembersFromSameInterfaces = GetImplementedMembers(containingType, interfaceTypes);
            var implementedMembersFromAllInterfaces  = GetImplementedMembers(containingType, containingType.AllInterfaces);

            var offerForSameInterface = TotalCount(implementedMembersFromSameInterfaces) > TotalCount(directlyImplementedMembers);
            var offerForAllInterfaces = TotalCount(implementedMembersFromAllInterfaces) > TotalCount(implementedMembersFromSameInterfaces);

            // If there's only one member in the interface we implement, and there are no other
            // interfaces, then just offer to switch the implementation for this single member
            if (!offerForSameInterface && !offerForAllInterfaces)
            {
                context.RegisterRefactoring(codeAction);
                return;
            }

            // Otherwise, create a top level action to change the implementation, and offer this
            // action, along with either/both of the other two.

            var nestedActions = ArrayBuilder <CodeAction> .GetInstance();

            nestedActions.Add(codeAction);

            if (offerForSameInterface)
            {
                var interfaceNames = interfaceTypes.Select(i => i.ToDisplayString(NameAndTypeParametersFormat));
                nestedActions.Add(CodeAction.Create(
                                      string.Format(Implement_0, string.Join(", ", interfaceNames)),
                                      c => ChangeImplementationAsync(project, implementedMembersFromSameInterfaces, c),
                                      nameof(Implement_0) + string.Join(", ", interfaceNames)));
            }

            if (offerForAllInterfaces)
            {
                nestedActions.Add(CodeAction.Create(
                                      Implement_all_interfaces,
                                      c => ChangeImplementationAsync(project, implementedMembersFromAllInterfaces, c),
                                      nameof(Implement_all_interfaces)));
            }

            context.RegisterRefactoring(CodeAction.CodeActionWithNestedActions.Create(
                                            Implement, nestedActions.ToImmutableAndFree(), isInlinable: true));
        }
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindToken(root, context.Span.Start, out SyntaxToken token))
            {
                return;
            }

            Document   document   = context.Document;
            Diagnostic diagnostic = context.Diagnostics[0];

            switch (diagnostic.Id)
            {
            case DiagnosticIdentifiers.AddEmptyLineBetweenBlockAndStatement:
            {
                CodeAction codeAction = CodeAction.Create(
                    CodeFixTitles.AddEmptyLine,
                    ct => CodeFixHelpers.AppendEndOfLineAsync(document, token, ct),
                    GetEquivalenceKey(diagnostic));

                context.RegisterCodeFix(codeAction, diagnostic);
                break;
            }

            case DiagnosticIdentifiers.AddNewLineBeforeConditionalOperatorInsteadOfAfterItOrViceVersa:
            {
                if (DiagnosticProperties.ContainsInvert(diagnostic.Properties))
                {
                    var conditionalExpression = (ConditionalExpressionSyntax)token.Parent;

                    string title = null;
                    if (token.IsKind(SyntaxKind.QuestionToken))
                    {
                        title = (SyntaxTriviaAnalysis.IsTokenFollowedWithNewLineAndNotPrecededWithNewLine(conditionalExpression.WhenTrue, conditionalExpression.ColonToken, conditionalExpression.WhenFalse))
                                    ? "Add newline after '?' and ':' instead of before it"
                                    : "Add newline after '?' instead of before it";
                    }
                    else
                    {
                        title = "Add newline after ':' instead of before it";
                    }

                    CodeAction codeAction = CodeAction.Create(
                        title,
                        ct => AddNewLineAfterConditionalOperatorInsteadOfBeforeItAsync(document, conditionalExpression, ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }
                else
                {
                    var conditionalExpression = (ConditionalExpressionSyntax)token.Parent;

                    string title = null;
                    if (token.IsKind(SyntaxKind.QuestionToken))
                    {
                        title = (SyntaxTriviaAnalysis.IsTokenFollowedWithNewLineAndNotPrecededWithNewLine(conditionalExpression.WhenTrue, conditionalExpression.ColonToken, conditionalExpression.WhenFalse))
                                    ? "Add newline before '?' and ':' instead of after it"
                                    : "Add newline before '?' instead of after it";
                    }
                    else
                    {
                        title = "Add newline before ':' instead of after it";
                    }

                    CodeAction codeAction = CodeAction.Create(
                        title,
                        ct => AddNewLineBeforeConditionalOperatorInsteadOfAfterItAsync(document, conditionalExpression, ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }

                break;
            }

            case DiagnosticIdentifiers.AddNewLineBeforeExpressionBodyArrowInsteadOfAfterItOrViceVersa:
            {
                if (DiagnosticProperties.ContainsInvert(diagnostic.Properties))
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Add newline after '=>' instead of before it",
                        ct =>
                        {
                            var expressionBody = (ArrowExpressionClauseSyntax)token.Parent;

                            return(CodeFixHelpers.AddNewLineAfterInsteadOfBeforeAsync(document, token.GetPreviousToken(), token, expressionBody.Expression, ct));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }
                else
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Add newline before '=>' instead of after it",
                        ct =>
                        {
                            var expressionBody = (ArrowExpressionClauseSyntax)token.Parent;

                            return(CodeFixHelpers.AddNewLineBeforeInsteadOfAfterAsync(document, token.GetPreviousToken(), token, expressionBody.Expression, ct));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }

                break;
            }

            case DiagnosticIdentifiers.AddNewLineAfterAttributeList:
            {
                CodeAction codeAction = CodeAction.Create(
                    CodeFixTitles.AddNewLine,
                    ct => CodeFixHelpers.AddNewLineBeforeAsync(document, token, ct),
                    GetEquivalenceKey(diagnostic));

                context.RegisterCodeFix(codeAction, diagnostic);
                break;
            }

            case DiagnosticIdentifiers.AddNewLineBetweenClosingBraceAndWhileKeywordOrViceVersa:
            {
                if (DiagnosticProperties.ContainsInvert(diagnostic.Properties))
                {
                    CodeAction codeAction = CodeAction.Create(
                        CodeFixTitles.RemoveNewLine,
                        ct => CodeFixHelpers.ReplaceTriviaBetweenAsync(document, token, token.GetNextToken(), cancellationToken: ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }
                else
                {
                    CodeAction codeAction = CodeAction.Create(
                        CodeFixTitles.AddNewLine,
                        ct => CodeFixHelpers.AddNewLineBeforeAsync(document, token, ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }

                break;
            }
            }
        }
Пример #26
0
        public void RegisterRefactoring(CodeAction codeAction)
        {
            Debug.WriteLine($"REGISTERING REFACTORING \"{codeAction.Title}\"");

            UnderlyingContext.RegisterRefactoring(codeAction);
        }
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsEnabled(CodeFixIdentifiers.AddDocumentationComment) &&
                !Settings.IsEnabled(CodeFixIdentifiers.ChangeMethodReturnType) &&
                !Settings.IsEnabled(CodeFixIdentifiers.MemberTypeMustMatchOverriddenMemberType) &&
                !Settings.IsEnabled(CodeFixIdentifiers.AddPartialModifier) &&
                !Settings.IsEnabled(CodeFixIdentifiers.MakeContainingClassAbstract) &&
                !Settings.IsEnabled(CodeFixIdentifiers.RemoveParametersFromStaticConstructor) &&
                !Settings.IsEnabled(CodeFixIdentifiers.RemoveMemberDeclaration) &&
                !Settings.IsEnabled(CodeFixIdentifiers.RenameDestructorToMatchClassName) &&
                !Settings.IsEnabled(CodeFixIdentifiers.RenameTupleElement) &&
                !Settings.IsEnabled(CodeFixIdentifiers.MarkDeclarationAsNonCLSCompliant))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out MemberDeclarationSyntax memberDeclaration))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.MissingXmlCommentForPubliclyVisibleTypeOrMember:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.AddDocumentationComment))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Add documentation comment",
                        cancellationToken => AddDocumentationCommentRefactoring.RefactorAsync(context.Document, memberDeclaration, false, cancellationToken),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);

                    CodeAction codeAction2 = CodeAction.Create(
                        "Add documentation comment (copy from base if available)",
                        cancellationToken => AddDocumentationCommentRefactoring.RefactorAsync(context.Document, memberDeclaration, true, cancellationToken),
                        GetEquivalenceKey(diagnostic, "CopyFromBaseIfAvailable"));

                    context.RegisterCodeFix(codeAction2, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.MethodReturnTypeMustMatchOverriddenMethodReturnType:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.ChangeMethodReturnType))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken);

                    ITypeSymbol typeSymbol = methodSymbol.OverriddenMethod.ReturnType;

                    CodeFixRegistrator.ChangeTypeOrReturnType(context, diagnostic, memberDeclaration, typeSymbol, semanticModel);

                    break;
                }

                case CompilerDiagnosticIdentifiers.PartialMethodsMustHaveVoidReturnType:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.ChangeMethodReturnType))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    var methodDeclaration = (MethodDeclarationSyntax)memberDeclaration;

                    MethodDeclarationSyntax otherPart = semanticModel.GetOtherPart(methodDeclaration, context.CancellationToken);

                    if (otherPart == null)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Change return type to 'void'",
                        cancellationToken =>
                        {
                            return(context.Document.Solution().ReplaceNodesAsync(
                                       new MethodDeclarationSyntax[] { methodDeclaration, otherPart },
                                       (node, _) => node.WithReturnType(CSharpFactory.VoidType().WithTriviaFrom(node.ReturnType)),
                                       cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.MemberTypeMustMatchOverriddenMemberType:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.MemberTypeMustMatchOverriddenMemberType))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    ITypeSymbol typeSymbol = null;

                    switch (memberDeclaration.Kind())
                    {
                    case SyntaxKind.PropertyDeclaration:
                    case SyntaxKind.IndexerDeclaration:
                    {
                        var propertySymbol = (IPropertySymbol)semanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken);

                        typeSymbol = propertySymbol.OverriddenProperty.Type;
                        break;
                    }

                    case SyntaxKind.EventDeclaration:
                    {
                        var eventSymbol = (IEventSymbol)semanticModel.GetDeclaredSymbol(memberDeclaration, context.CancellationToken);

                        typeSymbol = eventSymbol.OverriddenEvent.Type;
                        break;
                    }

                    case SyntaxKind.EventFieldDeclaration:
                    {
                        VariableDeclaratorSyntax declarator = ((EventFieldDeclarationSyntax)memberDeclaration).Declaration.Variables.First();

                        var eventSymbol = (IEventSymbol)semanticModel.GetDeclaredSymbol(declarator, context.CancellationToken);

                        typeSymbol = eventSymbol.OverriddenEvent.Type;
                        break;
                    }
                    }

                    CodeFixRegistrator.ChangeTypeOrReturnType(context, diagnostic, memberDeclaration, typeSymbol, semanticModel);

                    break;
                }

                case CompilerDiagnosticIdentifiers.MissingPartialModifier:
                case CompilerDiagnosticIdentifiers.PartialMethodMustBeDeclaredInPartialClassOrPartialStruct:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.AddPartialModifier))
                    {
                        break;
                    }

                    SyntaxNode node = null;

                    switch (memberDeclaration.Kind())
                    {
                    case SyntaxKind.MethodDeclaration:
                    {
                        if (memberDeclaration.IsParentKind(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration))
                        {
                            node = memberDeclaration.Parent;
                        }

                        break;
                    }

                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.StructDeclaration:
                    case SyntaxKind.InterfaceDeclaration:
                    {
                        node = memberDeclaration;
                        break;
                    }
                    }

                    Debug.Assert(node != null, memberDeclaration.ToString());

                    if (node == null)
                    {
                        break;
                    }

                    ModifiersCodeFixRegistrator.AddModifier(context, diagnostic, node, SyntaxKind.PartialKeyword);
                    break;
                }

                case CompilerDiagnosticIdentifiers.MemberIsAbstractButItIsContainedInNonAbstractClass:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.MakeContainingClassAbstract))
                    {
                        break;
                    }

                    if (!memberDeclaration.IsParentKind(SyntaxKind.ClassDeclaration))
                    {
                        break;
                    }

                    ModifiersCodeFixRegistrator.AddModifier(
                        context,
                        diagnostic,
                        memberDeclaration.Parent,
                        SyntaxKind.AbstractKeyword,
                        title: "Make containing class abstract");

                    break;
                }

                case CompilerDiagnosticIdentifiers.StaticConstructorMustBeParameterless:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.RemoveParametersFromStaticConstructor))
                    {
                        break;
                    }

                    var constructorDeclaration = (ConstructorDeclarationSyntax)memberDeclaration;

                    CodeAction codeAction = CodeAction.Create(
                        "Remove parameters",
                        cancellationToken =>
                        {
                            ParameterListSyntax parameterList = constructorDeclaration.ParameterList;

                            ParameterListSyntax newParameterList = parameterList
                                                                   .WithParameters(default(SeparatedSyntaxList <ParameterSyntax>))
                                                                   .WithOpenParenToken(parameterList.OpenParenToken.WithoutTrailingTrivia())
                                                                   .WithCloseParenToken(parameterList.CloseParenToken.WithoutLeadingTrivia());

                            ConstructorDeclarationSyntax newNode = constructorDeclaration.WithParameterList(newParameterList);

                            return(context.Document.ReplaceNodeAsync(constructorDeclaration, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.ExplicitInterfaceDeclarationCanOnlyBeDeclaredInClassOrStruct:
                case CompilerDiagnosticIdentifiers.InterfacesCannotContainFields:
                case CompilerDiagnosticIdentifiers.InterfacesCannotContainOperators:
                case CompilerDiagnosticIdentifiers.InterfacesCannotDeclareTypes:
                case CompilerDiagnosticIdentifiers.OnlyClassTypesCanContainDestructors:
                case CompilerDiagnosticIdentifiers.StructsCannotContainExplicitParameterlessConstructors:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.RemoveMemberDeclaration))
                    {
                        break;
                    }

                    CodeFixRegistrator.RemoveMemberDeclaration(context, diagnostic, memberDeclaration);
                    break;
                }

                case CompilerDiagnosticIdentifiers.NameOfDestructorMustMatchNameOfClass:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.RenameDestructorToMatchClassName))
                    {
                        break;
                    }

                    if (!(memberDeclaration is DestructorDeclarationSyntax destructorDeclaration))
                    {
                        break;
                    }

                    if (!(memberDeclaration.Parent is ClassDeclarationSyntax classDeclaration))
                    {
                        break;
                    }

                    if (classDeclaration.Identifier.ValueText.Length == 0)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Rename destructor to match class name",
                        cancellationToken =>
                        {
                            DestructorDeclarationSyntax newNode = destructorDeclaration.WithIdentifier(classDeclaration.Identifier.WithTriviaFrom(destructorDeclaration.Identifier));

                            return(context.Document.ReplaceNodeAsync(destructorDeclaration, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotChangeTupleElementNameWhenOverridingInheritedMember:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.RenameTupleElement))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
                    {
                        IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken);

                        if (!(methodSymbol.ReturnType is INamedTypeSymbol tupleType))
                        {
                            break;
                        }

                        if (!tupleType.IsTupleType)
                        {
                            break;
                        }

                        if (!(methodSymbol.OverriddenMethod?.ReturnType is INamedTypeSymbol baseTupleType))
                        {
                            break;
                        }

                        if (!baseTupleType.IsTupleType)
                        {
                            break;
                        }

                        ImmutableArray <IFieldSymbol> elements     = tupleType.TupleElements;
                        ImmutableArray <IFieldSymbol> baseElements = baseTupleType.TupleElements;

                        if (elements.Length != baseElements.Length)
                        {
                            break;
                        }

                        int i = 0;
                        while (i < elements.Length)
                        {
                            if (elements[i].Name != baseElements[i].Name)
                            {
                                break;
                            }

                            i++;
                        }

                        if (i == elements.Length)
                        {
                            break;
                        }

                        TupleElementSyntax tupleElement = ((TupleTypeSyntax)methodDeclaration.ReturnType).Elements[i];

                        CodeAction codeAction = CodeAction.Create(
                            $"Rename '{elements[i].Name}' to '{baseElements[i].Name}'",
                            ct => RenameTupleElementAsync(context.Document, methodDeclaration, tupleElement, elements[i], baseElements[i].Name, semanticModel, ct),
                            GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }
                    else if (memberDeclaration is PropertyDeclarationSyntax propertyDeclaration)
                    {
                        IPropertySymbol propertySymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration, context.CancellationToken);

                        if (!(propertySymbol.Type is INamedTypeSymbol tupleType))
                        {
                            break;
                        }

                        if (!tupleType.IsTupleType)
                        {
                            break;
                        }

                        if (!(propertySymbol.OverriddenProperty?.Type is INamedTypeSymbol baseTupleType))
                        {
                            break;
                        }

                        if (!baseTupleType.IsTupleType)
                        {
                            break;
                        }

                        ImmutableArray <IFieldSymbol> elements     = tupleType.TupleElements;
                        ImmutableArray <IFieldSymbol> baseElements = baseTupleType.TupleElements;

                        if (elements.Length != baseElements.Length)
                        {
                            break;
                        }

                        int i = 0;
                        while (i < elements.Length)
                        {
                            if (elements[i].Name != baseElements[i].Name)
                            {
                                break;
                            }

                            i++;
                        }

                        if (i == elements.Length)
                        {
                            break;
                        }

                        TupleElementSyntax tupleElement = ((TupleTypeSyntax)propertyDeclaration.Type).Elements[i];

                        CodeAction codeAction = CodeAction.Create(
                            $"Rename '{elements[i].Name}' to '{baseElements[i].Name}'",
                            ct => RenameTupleElementAsync(context.Document, propertyDeclaration, tupleElement, elements[i], baseElements[i].Name, semanticModel, ct),
                            GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.MethodsWithVariableArgumentsAreNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.ArgumentTypeIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.ReturnTypeIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.TypeOfVariableIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.IdentifierDifferingOnlyInCaseIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.OverloadedMethodDifferingOnlyInRefOrOutOrInArrayRankIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.OverloadedMethodDifferingOnlyByUnnamedArrayTypesIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.IdentifierIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.BaseTypeIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.ArraysAsAttributeArgumentsIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.ConstraintTypeIsNotCLSCompliant:
                case CompilerDiagnosticIdentifiers.TypeIsNotCLSCompliantBecauseBaseInterfaceIsNotCLSCompliant:
                {
                    if (!Settings.IsEnabled(CodeFixIdentifiers.MarkDeclarationAsNonCLSCompliant))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        $"Mark {CSharpFacts.GetTitle(memberDeclaration)} as non-CLS-compliant",
                        ct => MarkDeclarationAsNonCLSCompliantAsync(context.Document, memberDeclaration, ct),
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }
                }
            }
        }
 private CodeAction CreateAction(Document document, TextSpan span, Scope scope, CleanCodeGenerationOptionsProvider fallbackOptions, bool isRecord)
 => CodeAction.Create(GetTitle(scope), c => ConvertToStructAsync(document, span, scope, fallbackOptions, isRecord, c), scope.ToString());
        /// <summary>
        /// Verifies that specified source will produce compiler diagnostic.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="expected"></param>
        /// <param name="options"></param>
        /// <param name="cancellationToken"></param>
        public async Task VerifyFixAsync(
            CompilerDiagnosticFixTestState state,
            ExpectedTestState expected,
            TestOptions options = null,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            options ??= Options;

            TFixProvider fixProvider = Activator.CreateInstance <TFixProvider>();

            VerifyFixableDiagnostics(fixProvider, state.DiagnosticId);

            using (Workspace workspace = new AdhocWorkspace())
            {
                (Document document, ImmutableArray <ExpectedDocument> expectedDocuments) = CreateDocument(workspace.CurrentSolution, state.Source, state.AdditionalFiles, options);

                Project project = document.Project;

                document = project.GetDocument(document.Id);

                ImmutableArray <Diagnostic> previousDiagnostics = ImmutableArray <Diagnostic> .Empty;

                var fixRegistered = false;

                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    Compilation compilation = await document.Project.GetCompilationAsync(cancellationToken);

                    ImmutableArray <Diagnostic> diagnostics = compilation.GetDiagnostics(cancellationToken: cancellationToken);

                    int length = diagnostics.Length;

                    if (length == 0)
                    {
                        break;
                    }

                    if (previousDiagnostics.Any())
                    {
                        VerifyNoNewCompilerDiagnostics(previousDiagnostics, diagnostics, options);
                    }

                    if (DiagnosticDeepEqualityComparer.Equals(diagnostics, previousDiagnostics))
                    {
                        Assert.True(false, "Same diagnostics returned before and after the fix was applied." + diagnostics.ToDebugString());
                    }

                    Diagnostic diagnostic = FindDiagnosticToFix(diagnostics);

                    if (diagnostic == null)
                    {
                        break;
                    }

                    CodeAction action = null;

                    var context = new CodeFixContext(
                        document,
                        diagnostic,
                        (a, d) =>
                    {
                        if ((state.EquivalenceKey == null ||
                             string.Equals(state.EquivalenceKey, a.EquivalenceKey, StringComparison.Ordinal)) &&
                            d.Contains(diagnostic))
                        {
                            if (action != null)
                            {
                                Assert.True(false, $"Multiple fixes registered by '{fixProvider.GetType().Name}'.");
                            }

                            action = a;
                        }
                    },
                        cancellationToken);

                    await fixProvider.RegisterCodeFixesAsync(context);

                    if (action == null)
                    {
                        break;
                    }

                    fixRegistered = true;

                    document = await VerifyAndApplyCodeActionAsync(document, action, expected.CodeActionTitle);

                    previousDiagnostics = diagnostics;
                }

                Assert.True(fixRegistered, "No code fix has been registered.");

                await VerifyExpectedDocument(expected, document, cancellationToken);

                if (expectedDocuments.Any())
                {
                    await VerifyAdditionalDocumentsAsync(document.Project, expectedDocuments, cancellationToken);
                }
            }

            Diagnostic FindDiagnosticToFix(ImmutableArray <Diagnostic> diagnostics)
            {
                Diagnostic match = null;

                foreach (Diagnostic diagnostic in diagnostics)
                {
                    if (string.Equals(diagnostic.Id, state.DiagnosticId, StringComparison.Ordinal))
                    {
                        if (match == null ||
                            diagnostic.Location.SourceSpan.Start > match.Location.SourceSpan.Start)
                        {
                            match = diagnostic;
                        }
                    }
                }

                return(match);
            }
        }
		public ValidCodeDiagnosticAction (CodeDiagnosticFixDescriptor diagnostic, CodeAction codeAction, ImmutableArray<Diagnostic> validDiagnostics, TextSpan validSegment) : base (codeAction, validSegment)
		{
			this.Diagnostic = diagnostic;
			this.validDiagnostics = validDiagnostics;
		}
Пример #31
0
        private void RegisterUnwrapAction(CodeRefactoringContext context, SyntaxNode root, GenericNameSyntax generic)
        {
            TypeSyntax genericArgument = generic.TypeArgumentList.Arguments[0];

            context.RegisterRefactoring(CodeAction.Create($"Make {genericArgument}", _ => Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(generic, SyntaxFactory.IdentifierName(genericArgument.ToString()))))));
        }
			public ContextActionRunner (CodeAction act, TextEditor editor, DocumentContext documentContext)
			{
				this.editor = editor;
				this.act = act;
				this.documentContext = documentContext;
			}
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out SyntaxNode node, predicate: f => f.IsKind(
                                                SyntaxKind.VariableDeclaration,
                                                SyntaxKind.ForEachStatement,
                                                SyntaxKind.Parameter,
                                                SyntaxKind.DeclarationPattern,
                                                SyntaxKind.DeclarationExpression)))
            {
                return;
            }

            if (node.IsKind(SyntaxKind.ForEachStatement, SyntaxKind.Parameter, SyntaxKind.DeclarationPattern, SyntaxKind.DeclarationExpression))
            {
                return;
            }

            var variableDeclaration = (VariableDeclarationSyntax)node;

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.ImplicitlyTypedVariablesCannotHaveMultipleDeclarators:
                case CompilerDiagnosticIdentifiers.ImplicitlyTypedVariablesCannotBeConstant:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseExplicitTypeInsteadOfVar))
                    {
                        return;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    TypeSyntax type = variableDeclaration.Type;

                    ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(type, context.CancellationToken);

                    if (typeSymbol?.SupportsExplicitDeclaration() == true)
                    {
                        CodeAction codeAction = CodeActionFactory.ChangeType(context.Document, type, typeSymbol, semanticModel, equivalenceKey: GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.LocalVariableOrFunctionIsAlreadyDefinedInThisScope:
                case CompilerDiagnosticIdentifiers.LocalOrParameterCannotBeDeclaredInThisScopeBecauseThatNameIsUsedInEnclosingScopeToDefineLocalOrParameter:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceVariableDeclarationWithAssignment))
                    {
                        return;
                    }

                    if (!(variableDeclaration.Parent is LocalDeclarationStatementSyntax localDeclaration))
                    {
                        return;
                    }

                    VariableDeclaratorSyntax variableDeclarator = variableDeclaration.Variables.SingleOrDefault(shouldThrow: false);

                    if (variableDeclarator == null)
                    {
                        break;
                    }

                    ExpressionSyntax value = variableDeclarator.Initializer?.Value;

                    if (value == null)
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    VariableDeclaratorSyntax variableDeclarator2 = FindVariableDeclarator(
                        variableDeclarator.Identifier.ValueText,
                        semanticModel.GetEnclosingSymbolSyntax(localDeclaration.SpanStart, context.CancellationToken));

                    if (variableDeclarator2?.SpanStart < localDeclaration.SpanStart)
                    {
                        CodeAction codeAction = CodeAction.Create(
                            "Replace variable declaration with assignment",
                            cancellationToken =>
                            {
                                ExpressionStatementSyntax newNode = CSharpFactory.SimpleAssignmentStatement(
                                    SyntaxFactory.IdentifierName(variableDeclarator.Identifier),
                                    value);

                                newNode = newNode
                                          .WithTriviaFrom(localDeclaration)
                                          .WithFormatterAnnotation();

                                return(context.Document.ReplaceNodeAsync(localDeclaration, newNode, cancellationToken));
                            },
                            GetEquivalenceKey(diagnostic));
                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }
                }
            }
        }