| File: src\roslyn\src\Analyzers\Core\CodeFixes\RemoveUnusedMembers\AbstractRemoveUnusedMembersCodeFixProvider.cs | Web Access |
| Project: src\roslyn\src\CodeStyle\Core\CodeFixes\Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.CodeStyle.Fixes) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.RemoveUnusedMembers; internal abstract class AbstractRemoveUnusedMembersCodeFixProvider<TFieldDeclarationSyntax> : SyntaxEditorBasedCodeFixProvider where TFieldDeclarationSyntax : SyntaxNode { public sealed override ImmutableArray<string> FixableDiagnosticIds => [IDEDiagnosticIds.RemoveUnusedMembersDiagnosticId]; /// <summary> /// This method adjusts the <paramref name="declarators"/> to remove based on whether or not all variable declarators /// within a field declaration should be removed, /// i.e. if all the fields declared within a field declaration are unused, /// we can remove the entire field declaration instead of individual variable declarators. /// </summary> protected abstract void AdjustAndAddAppropriateDeclaratorsToRemove(HashSet<TFieldDeclarationSyntax> fieldDeclarators, HashSet<SyntaxNode> declarators); public override async Task RegisterCodeFixesAsync(CodeFixContext context) { RegisterCodeFix(context, AnalyzersResources.Remove_unused_member, nameof(AnalyzersResources.Remove_unused_member)); } protected override async Task FixAllAsync( Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { var declarators = new HashSet<SyntaxNode>(); var fieldDeclarators = new HashSet<TFieldDeclarationSyntax>(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var declarationService = document.GetRequiredLanguageService<ISymbolDeclarationService>(); // Compute declarators to remove, and also track common field declarators. foreach (var diagnostic in diagnostics) { // Get symbol to be removed. var diagnosticNode = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken); var symbol = semanticModel.GetDeclaredSymbol(diagnosticNode, cancellationToken); Contract.ThrowIfNull(symbol); // Get symbol declarations to be removed. foreach (var declReference in declarationService.GetDeclarations(symbol)) { var node = await declReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false); declarators.Add(node); // For fields, the declaration node is the variable declarator. // We also track the ancestor FieldDeclarationSyntax which may declare more then one field. if (symbol.Kind == SymbolKind.Field) { var fieldDeclarator = node.FirstAncestorOrSelf<TFieldDeclarationSyntax>(); Contract.ThrowIfNull(fieldDeclarator); fieldDeclarators.Add(fieldDeclarator); } } } // If all the fields declared within a field declaration are unused, // we can remove the entire field declaration instead of individual variable declarators. if (fieldDeclarators.Count > 0) { AdjustAndAddAppropriateDeclaratorsToRemove(fieldDeclarators, declarators); } // Remove all the symbol declarator nodes. foreach (var declarator in declarators) { editor.RemoveNode(declarator); } } /// <summary> /// If all the <paramref name="childDeclarators"/> are contained in <paramref name="declarators"/>, /// the removes the <paramref name="childDeclarators"/> from <paramref name="declarators"/>, and /// adds the <paramref name="parentDeclaration"/> to the <paramref name="declarators"/>. /// </summary> protected static void AdjustAndAddAppropriateDeclaratorsToRemove(SyntaxNode parentDeclaration, IEnumerable<SyntaxNode> childDeclarators, HashSet<SyntaxNode> declarators) { if (declarators.Contains(parentDeclaration)) { Debug.Assert(childDeclarators.All(c => !declarators.Contains(c))); return; } var declaratorsContainsAllChildren = true; foreach (var childDeclarator in childDeclarators) { if (!declarators.Contains(childDeclarator)) { declaratorsContainsAllChildren = false; break; } } if (declaratorsContainsAllChildren) { // Remove the entire parent declaration instead of individual child declarators within it. declarators.Add(parentDeclaration); declarators.RemoveAll(childDeclarators); } } }