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); }
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); }
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; } } } }
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)); }
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; } } }
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
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; } } } }
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); }
/// <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); } }
private static async Task <Solution> ApplyFixAsync(CodeAction codeAction) { var operations = await codeAction.GetOperationsAsync(CancellationToken.None); return(Assert.Single(operations.OfType <ApplyChangesOperation>()).ChangedSolution); }
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; } } } }
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; } }
public ValidCodeAction (CodeAction codeAction, TextSpan validSegment) { this.CodeAction = codeAction; this.ValidSegment = validSegment; }
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; } } } }
public static void ApplyFix(CodeAction action, IRefactoringContext context) { using (var script = context.CreateScript()) { action.Run(context, script); } }
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; } } } }
public UnifiedSuggestedAction(Workspace workspace, CodeAction codeAction, CodeActionPriority codeActionPriority) { Workspace = workspace; OriginalCodeAction = codeAction; CodeActionPriority = codeActionPriority; }
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; } } } }
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)); }
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; } } }
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; }
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; } } } }