Skip to content

Commit

Permalink
RCS1268 - Simplify Numeric Comparison (#1405)
Browse files Browse the repository at this point in the history
Co-authored-by: Josef Pihrt <josef@pihrt.net>
  • Loading branch information
jakubreznak and josefpihrt committed Feb 26, 2024
1 parent 32a2e9c commit 823d74b
Show file tree
Hide file tree
Showing 8 changed files with 385 additions and 6 deletions.
1 change: 1 addition & 0 deletions ChangeLog.md
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify LINQ query [RCS1077](https://josefpihrt.github.io/docs/roslynator/analyzers/1077) ([PR](https://github.com/dotnet/roslynator/pull/1384))
- `items.Select(selector).Average()` => `items.Average(selector)`
- `items.Select(selector).Sum()` => `items.Sum(selector)`
- Add analyzer "Simplify numeric comparison" [RCS1268](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1268) ([PR](https://github.com/dotnet/roslynator/pull/1405) by @jakubreznak)

### Fixed

Expand Down
Expand Up @@ -37,7 +37,8 @@ public override ImmutableArray<string> FixableDiagnosticIds
DiagnosticIdentifiers.UseExclusiveOrOperator,
DiagnosticIdentifiers.UnnecessaryNullCheck,
DiagnosticIdentifiers.UseShortCircuitingOperator,
DiagnosticIdentifiers.UnnecessaryOperator);
DiagnosticIdentifiers.UnnecessaryOperator,
DiagnosticIdentifiers.SimplifyNumericComparison);
}
}

Expand Down Expand Up @@ -240,6 +241,16 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
},
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
break;
}
case DiagnosticIdentifiers.SimplifyNumericComparison:
{
CodeAction codeAction = CodeAction.Create(
"Simplify numeric comparison",
ct => SimplifyNumericComparisonAsync(document, binaryExpression, ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
break;
}
Expand Down Expand Up @@ -360,4 +371,44 @@ private static ConditionalAccessExpressionSyntax CreateConditionalAccess(Express

return await document.ReplaceNodeAsync(logicalAnd, newBinaryExpression, cancellationToken).ConfigureAwait(false);
}

private static Task<Document> SimplifyNumericComparisonAsync(
Document document,
BinaryExpressionSyntax binaryExpression,
CancellationToken cancellationToken)
{
BinaryExpressionSyntax subtractionExpression;
SyntaxKind kind = binaryExpression.Kind();

BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(binaryExpression);

if (info.Left.IsNumericLiteralExpression("0"))
{
subtractionExpression = (BinaryExpressionSyntax)info.Right;

kind = kind switch
{
SyntaxKind.GreaterThanExpression => SyntaxKind.LessThanExpression,
SyntaxKind.GreaterThanOrEqualExpression => SyntaxKind.LessThanOrEqualExpression,
SyntaxKind.LessThanExpression => SyntaxKind.GreaterThanExpression,
SyntaxKind.LessThanOrEqualExpression => SyntaxKind.GreaterThanOrEqualExpression,
_ => kind
};
}
else
{
subtractionExpression = (BinaryExpressionSyntax)info.Left;
}

BinaryExpressionSyntax newBinaryExpression = BinaryExpression(
kind,
subtractionExpression.Left,
subtractionExpression.Right);

newBinaryExpression = newBinaryExpression
.WithTriviaFrom(binaryExpression)
.WithFormatterAnnotation();

return document.ReplaceNodeAsync(binaryExpression, newBinaryExpression, cancellationToken);
}
}
30 changes: 30 additions & 0 deletions src/Analyzers.xml
Expand Up @@ -7727,6 +7727,36 @@ string s = """
</Sample>
</Samples>
</Analyzer>
<Analyzer>
<Id>RCS1268</Id>
<Identifier>SimplifyNumericComparison</Identifier>
<Title>Simplify numeric comparison</Title>
<DefaultSeverity>Info</DefaultSeverity>
<IsEnabledByDefault>true</IsEnabledByDefault>
<Description>Simplifies comparisons that involve subtracting two values and comparing the result to zero.</Description>
<Samples>
<Sample>
<Before><![CDATA[(a - b) == 0]]></Before>
<After><![CDATA[a == b]]></After>
</Sample>
<Sample>
<Before><![CDATA[(a - b) > 0]]></Before>
<After><![CDATA[a > b]]></After>
</Sample>
<Sample>
<Before><![CDATA[(a - b) >= 0]]></Before>
<After><![CDATA[a >= b]]></After>
</Sample>
<Sample>
<Before><![CDATA[(a - b) < 0]]></Before>
<After><![CDATA[a < b]]></After>
</Sample>
<Sample>
<Before><![CDATA[(a - b) <= 0]]></Before>
<After><![CDATA[a <= b]]></After>
</Sample>
</Samples>
</Analyzer>
<Analyzer>
<Id>RCS9001</Id>
<Identifier>UsePatternMatching</Identifier>
Expand Down
63 changes: 63 additions & 0 deletions src/Analyzers/CSharp/Analysis/SimplifyNumericComparisonAnalyzer.cs
@@ -0,0 +1,63 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslynator.CSharp.Analysis;
using Roslynator.CSharp.Syntax;

namespace Roslynator.CSharp.CSharp.Analysis;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class SimplifyNumericComparisonAnalyzer : BaseDiagnosticAnalyzer
{
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics;

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
if (_supportedDiagnostics.IsDefault)
{
Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.SimplifyNumericComparison);
}

return _supportedDiagnostics;
}
}

public override void Initialize(AnalysisContext context)
{
base.Initialize(context);

context.RegisterSyntaxNodeAction(
f => AnalyzeComparison(f),
SyntaxKind.EqualsExpression,
SyntaxKind.GreaterThanExpression,
SyntaxKind.GreaterThanOrEqualExpression,
SyntaxKind.LessThanExpression,
SyntaxKind.LessThanOrEqualExpression);
}

private static void AnalyzeComparison(SyntaxNodeAnalysisContext context)
{
var binaryExpression = (BinaryExpressionSyntax)context.Node;

BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(binaryExpression);

if (!info.Success)
return;

ExpressionSyntax leftExpression = info.Left;
ExpressionSyntax rightExpression = info.Right;

if ((leftExpression.IsNumericLiteralExpression("0") && rightExpression.IsKind(SyntaxKind.SubtractExpression))
|| (leftExpression.IsKind(SyntaxKind.SubtractExpression) && rightExpression.IsNumericLiteralExpression("0")))
{
DiagnosticHelpers.ReportDiagnostic(
context,
DiagnosticRules.SimplifyNumericComparison,
binaryExpression);
}
}
}
1 change: 1 addition & 0 deletions src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs
Expand Up @@ -224,5 +224,6 @@ public static partial class DiagnosticIdentifiers
public const string RemoveRedundantCatchBlock = "RCS1265";
public const string UseRawStringLiteral = "RCS1266";
public const string UseStringInterpolationInsteadOfStringConcat = "RCS1267";
public const string SimplifyNumericComparison = "RCS1268";
}
}
12 changes: 12 additions & 0 deletions src/Analyzers/CSharp/DiagnosticRules.Generated.cs
Expand Up @@ -2651,5 +2651,17 @@ public static partial class DiagnosticRules
helpLinkUri: DiagnosticIdentifiers.UseStringInterpolationInsteadOfStringConcat,
customTags: Array.Empty<string>());

/// <summary>RCS1268</summary>
public static readonly DiagnosticDescriptor SimplifyNumericComparison = DiagnosticDescriptorFactory.Create(
id: DiagnosticIdentifiers.SimplifyNumericComparison,
title: "Simplify numeric comparison",
messageFormat: "Simplify numeric comparison",
category: DiagnosticCategories.Roslynator,
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true,
description: null,
helpLinkUri: DiagnosticIdentifiers.SimplifyNumericComparison,
customTags: Array.Empty<string>());

}
}

0 comments on commit 823d74b

Please sign in to comment.