| File: Microsoft.NetCore.Analyzers\Performance\PreferIsEmptyOverCount.Fixer.cs | Web Access |
| Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Analyzer.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.NetAnalyzers; namespace Microsoft.NetCore.Analyzers.Performance { /// <summary> /// CA1836: Prefer IsEmpty over Count when available. /// </summary> public abstract class PreferIsEmptyOverCountFixer : SyntaxEditorBasedCodeFixProvider { public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(UseCountProperlyAnalyzer.CA1836); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); SyntaxNode node = root.FindNode(context.Span, getInnermostNodeForTie: true); if (node == null) { return; } if (context.Diagnostics[0].Properties is null) { return; } RegisterCodeFix(context, MicrosoftNetCoreAnalyzersResources.PreferIsEmptyOverCountTitle, MicrosoftNetCoreAnalyzersResources.PreferIsEmptyOverCountMessage); } protected sealed override Task ApplyFixAsync(Document document, Diagnostic diagnostic, SyntaxEditor editor, CancellationToken cancellationToken) { SyntaxNode node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); ImmutableDictionary<string, string?> properties = diagnostic.Properties; if (properties is null) { return Task.CompletedTask; } // Indicates whether the Count method or property is on the Right or Left side of a binary expression // OR if it is the argument or the instance of an Equals invocation. string operationKey = properties[UseCountProperlyAnalyzer.OperationKey]!; // Indicates if the replacing IsEmpty node should be negated. (!IsEmpty). bool shouldNegate = properties.ContainsKey(UseCountProperlyAnalyzer.ShouldNegateKey); // The object the Count belongs to is a descendant of the diagnosed node and can hold another // diagnosed comparison, so it is re-read from the node as the editor has rewritten it rather // than from the original tree. editor.ReplaceNode(node, (currentNode, generator) => { // The object that the Count property belongs to OR null if countAccessor is not a MemberAccessExpressionSyntax. SyntaxNode? objectExpression = GetObjectExpressionFromOperation(currentNode, operationKey); // The IsEmpty property meant to replace the binary expression. SyntaxNode isEmptyNode = objectExpression is null ? generator.IdentifierName(UseCountProperlyAnalyzer.IsEmpty) : generator.MemberAccessExpression(objectExpression, UseCountProperlyAnalyzer.IsEmpty); if (shouldNegate) { isEmptyNode = generator.LogicalNotExpression(isEmptyNode); } return isEmptyNode.WithTriviaFrom(currentNode); }); return Task.CompletedTask; } /// <summary> /// The object that the Count method or property belongs to OR null if the Count method or property is not a MemberAccessExpressionSyntax. /// </summary> protected abstract SyntaxNode? GetObjectExpressionFromOperation(SyntaxNode node, string operationKey); } }