| File: Microsoft.NetCore.Analyzers\Runtime\TestForNaNCorrectly.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 Microsoft.CodeAnalysis.CodeFixes; using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using System.Threading; using Analyzer.Utilities; using Microsoft.CodeAnalysis.NetAnalyzers; namespace Microsoft.NetCore.Analyzers.Runtime { /// <summary> /// CA2242: Test for NaN correctly /// </summary> public abstract class TestForNaNCorrectlyFixer : SyntaxEditorBasedCodeFixProvider { public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(TestForNaNCorrectlyAnalyzer.RuleId); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); SemanticModel model = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); if (TryGetFixResolution(root.FindNode(context.Span), model, context.CancellationToken) is not null) { RegisterCodeFix(context, MicrosoftNetCoreAnalyzersResources.TestForNaNCorrectlyMessage, MicrosoftNetCoreAnalyzersResources.TestForNaNCorrectlyMessage); } } protected sealed override async Task ApplyFixAsync(Document document, Diagnostic diagnostic, SyntaxEditor editor, CancellationToken cancellationToken) { SemanticModel model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); SyntaxNode node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan); if (TryGetFixResolution(node, model, cancellationToken) is not FixResolution resolution) { return; } // The comparison operand is re-emitted, and `(a == float.NaN ? b : c) == float.NaN` diagnoses both // comparisons, so it is read off the node as an inner fix has already rewritten it. editor.ReplaceNode(resolution.BinaryExpressionSyntax, (currentNode, generator) => { SyntaxNode comparisonOperand = resolution.NanIsLeftOperand ? GetRightOperand(currentNode) : GetLeftOperand(currentNode); SyntaxNode typeNameSyntax = generator.TypeExpression(resolution.FloatingSystemType); SyntaxNode nanMemberSyntax = generator.MemberAccessExpression(typeNameSyntax, "IsNaN"); SyntaxNode nanMemberInvocationSyntax = generator.InvocationExpression(nanMemberSyntax, comparisonOperand); SyntaxNode replacementSyntax = resolution.UsesEqualsOperator ? nanMemberInvocationSyntax : generator.LogicalNotExpression(nanMemberInvocationSyntax); return replacementSyntax.WithAdditionalAnnotations(Formatter.Annotation); }); } private FixResolution? TryGetFixResolution(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken) { SyntaxNode binaryExpressionSyntax = GetBinaryExpression(node); bool isEqualsOperator = IsEqualsOperator(binaryExpressionSyntax); if (!isEqualsOperator && !IsNotEqualsOperator(binaryExpressionSyntax)) { return null; } ITypeSymbol? systemTypeLeft = TryGetSystemTypeForNanConstantExpression(GetLeftOperand(binaryExpressionSyntax), model, cancellationToken); if (systemTypeLeft != null) { return new FixResolution(binaryExpressionSyntax, systemTypeLeft, nanIsLeftOperand: true, isEqualsOperator); } ITypeSymbol? systemTypeRight = TryGetSystemTypeForNanConstantExpression(GetRightOperand(binaryExpressionSyntax), model, cancellationToken); if (systemTypeRight != null) { return new FixResolution(binaryExpressionSyntax, systemTypeRight, nanIsLeftOperand: false, isEqualsOperator); } return null; } private static ITypeSymbol? TryGetSystemTypeForNanConstantExpression(SyntaxNode expressionSyntax, SemanticModel model, CancellationToken cancellationToken) { var symbol = model.GetSymbolInfo(expressionSyntax, cancellationToken).Symbol; if (symbol is IFieldSymbol { HasConstantValue: true, Name: "NaN", Type: { SpecialType: SpecialType.System_Single or SpecialType.System_Double } type }) { return type; } return null; } protected abstract SyntaxNode GetBinaryExpression(SyntaxNode node); protected abstract bool IsEqualsOperator(SyntaxNode node); protected abstract bool IsNotEqualsOperator(SyntaxNode node); protected abstract SyntaxNode GetLeftOperand(SyntaxNode binaryExpressionSyntax); protected abstract SyntaxNode GetRightOperand(SyntaxNode binaryExpressionSyntax); private sealed class FixResolution { public SyntaxNode BinaryExpressionSyntax { get; } public ITypeSymbol FloatingSystemType { get; } public bool NanIsLeftOperand { get; } public bool UsesEqualsOperator { get; } public FixResolution(SyntaxNode binaryExpressionSyntax, ITypeSymbol floatingSystemType, bool nanIsLeftOperand, bool usesEqualsOperator) { BinaryExpressionSyntax = binaryExpressionSyntax; FloatingSystemType = floatingSystemType; NanIsLeftOperand = nanIsLeftOperand; UsesEqualsOperator = usesEqualsOperator; } } } }