From 833e00385ab398c0a7b152a6af8058afd3b637bf Mon Sep 17 00:00:00 2001
From: Gregory Paidis
<115458417+gregory-paidis-sonarsource@users.noreply.github.com>
Date: Fri, 10 Feb 2023 14:32:31 +0100
Subject: [PATCH] New rule S2198: Silly mathematical comparisons should not be
made (#6695)
* Update RSPEC for S2198
* Remove a useless auto-generated json
* Add some testcases
* Remove a useless using
* Initial implementation of the main logic
* Initial working implementation of S2198
* Refactor implementation
* Move some code around
* Add a lot of testcases
* Cut branches: only implement absolute inequality for float
* Rewrite SillyMathematicalComparison, only out-of-range checks for floats for this step
* Apply PR review changes
* Enrichen testcases, add long and ulong to the checked types
* Add 'is' pattern testcases, refactored a .ToString to a .ToDisplayString
* Simple rename
* Implement a constant-in-both-sides version of the rule.
* Apply PR Review changes, some constant-check fixes and more testcases
* Add enum, char and reference type to the testcases
* Simplify the rule, only care about out-of-range mathematical checks
* Add char out-of-range detection
* Added some true negatives for const1 {{operator}} const2
* Bump coverage by adding a case I forgot about
* Apply PR Review changes
* Add an is-pattern edgecase in the testcases
* Fix a logical condition, refactor TryGetRange
* Minor naming refactor
* Minor refactor for OutOfRange check
* Annotate a testcase with an issue
---
analyzers/rspec/cs/S2198_c#.html | 18 +
analyzers/rspec/cs/S2198_c#.json | 17 +
analyzers/rspec/cs/Sonar_way_profile.json | 1 +
.../Rules/SillyMathematicalComparison.cs | 93 ++++
.../PackagingTests/RuleTypeMappingCS.cs | 2 +-
.../Rules/SillyMathematicalComparison.cs | 38 ++
.../SillyMathematicalComparison.CSharp9.cs | 25 +
.../TestCases/SillyMathematicalComparison.cs | 457 ++++++++++++++++++
8 files changed, 650 insertions(+), 1 deletion(-)
create mode 100644 analyzers/rspec/cs/S2198_c#.html
create mode 100644 analyzers/rspec/cs/S2198_c#.json
create mode 100644 analyzers/src/SonarAnalyzer.CSharp/Rules/SillyMathematicalComparison.cs
create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/Rules/SillyMathematicalComparison.cs
create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.CSharp9.cs
create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.cs
diff --git a/analyzers/rspec/cs/S2198_c#.html b/analyzers/rspec/cs/S2198_c#.html
new file mode 100644
index 00000000000..d0d070dc92b
--- /dev/null
+++ b/analyzers/rspec/cs/S2198_c#.html
@@ -0,0 +1,18 @@
+
Certain mathematical comparisons will always return the same value, and should simply not be made.
+This comparison will always return false
:
+
+ - comparing a
float
with a double
constant that’s outside the float
range
+
+These will always return true
:
+
+ - comparing
aByte <= Byte.MaxValue
and aByte >= Byte.MinValue
+ - comparing
anInt <= int.MaxValue
and anInt >= int.MinValue
+ - comparing
aLong <= long.MaxValue
and aLong >= long.MinValue
+
+Noncompliant Code Example
+
+float f = 42.0f;
+const double d = float.MaxValue + 1;
+if (f <= d) { } // Noncompliant
+
+
diff --git a/analyzers/rspec/cs/S2198_c#.json b/analyzers/rspec/cs/S2198_c#.json
new file mode 100644
index 00000000000..d7e7b900946
--- /dev/null
+++ b/analyzers/rspec/cs/S2198_c#.json
@@ -0,0 +1,17 @@
+{
+ "title": "Silly mathematical comparisons should not be made",
+ "type": "CODE_SMELL",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant\/Issue",
+ "constantCost": "15min"
+ },
+ "tags": [
+ "suspicious"
+ ],
+ "defaultSeverity": "Critical",
+ "ruleSpecification": "RSPEC-2198",
+ "sqKey": "S2198",
+ "scope": "Main",
+ "quickfix": "unknown"
+}
diff --git a/analyzers/rspec/cs/Sonar_way_profile.json b/analyzers/rspec/cs/Sonar_way_profile.json
index 82393f4f8fe..c91e8e43fef 100644
--- a/analyzers/rspec/cs/Sonar_way_profile.json
+++ b/analyzers/rspec/cs/Sonar_way_profile.json
@@ -68,6 +68,7 @@
"S2184",
"S2187",
"S2190",
+ "S2198",
"S2201",
"S2219",
"S2222",
diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/SillyMathematicalComparison.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/SillyMathematicalComparison.cs
new file mode 100644
index 00000000000..9f42bb27b65
--- /dev/null
+++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/SillyMathematicalComparison.cs
@@ -0,0 +1,93 @@
+/*
+ * SonarAnalyzer for .NET
+ * Copyright (C) 2015-2023 SonarSource SA
+ * mailto: contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+namespace SonarAnalyzer.Rules.CSharp
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public sealed class SillyMathematicalComparison : SonarDiagnosticAnalyzer
+ {
+ private const string DiagnosticId = "S2198";
+ private const string MathComparisonMessage = "Comparison to this constant is useless; the constant is outside the range of type '{0}'";
+
+ private static readonly DiagnosticDescriptor MathComparisonRule = DescriptorFactory.Create(DiagnosticId, MathComparisonMessage);
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(MathComparisonRule);
+
+ protected override void Initialize(SonarAnalysisContext context) =>
+ context.RegisterNodeAction(
+ CheckComparisonOutOfRange,
+ CSharpFacade.Instance.SyntaxKind.ComparisonKinds);
+
+ private static void CheckComparisonOutOfRange(SonarSyntaxNodeReportingContext context)
+ {
+ if (TryGetConstantValue(context.SemanticModel, (BinaryExpressionSyntax)context.Node, out var constant, out var other)
+ && context.SemanticModel.GetTypeInfo(other).Type is { } typeSymbolOfOther
+ && TryGetRange(typeSymbolOfOther) is { } range
+ && range.IsOutOfRange(constant))
+ {
+ var typeName = typeSymbolOfOther.ToMinimalDisplayString(context.SemanticModel, other.GetLocation().SourceSpan.Start);
+ context.ReportIssue(Diagnostic.Create(MathComparisonRule, other.Parent.GetLocation(), typeName));
+ }
+ }
+
+ private static bool TryGetConstantValue(SemanticModel model, BinaryExpressionSyntax binary, out double constant, out SyntaxNode other)
+ {
+ var optionalLeft = model.GetConstantValue(binary.Left);
+ var optionalRight = model.GetConstantValue(binary.Right);
+
+ if (optionalLeft.HasValue ^ optionalRight.HasValue)
+ {
+ if (optionalLeft.HasValue && TryConvertToDouble(optionalLeft.Value, out constant))
+ {
+ other = binary.Right;
+ return true;
+ }
+ else if (optionalRight.HasValue && TryConvertToDouble(optionalRight.Value, out constant))
+ {
+ other = binary.Left;
+ return true;
+ }
+ }
+ constant = default;
+ other = default;
+ return false;
+ }
+
+ // 'char' needs to roundtrip {{char -> int -> double}}, can't go {{char -> double}}
+ private static bool TryConvertToDouble(object constant, out double typedConstant) =>
+ ConversionHelper.TryConvertWith(constant is char ? Convert.ToInt32(constant) : constant, Convert.ToDouble, out typedConstant);
+
+ private static ValuesRange? TryGetRange(ITypeSymbol typeSymbol) =>
+ typeSymbol switch
+ {
+ _ when typeSymbol.Is(KnownType.System_Char) => new(char.MinValue, char.MaxValue),
+ _ when typeSymbol.Is(KnownType.System_Single) => new(float.MinValue, float.MaxValue),
+ _ when typeSymbol.Is(KnownType.System_Int64) => new(long.MinValue, long.MaxValue),
+ _ when typeSymbol.Is(KnownType.System_UInt64) => new(ulong.MinValue, ulong.MaxValue),
+ _ => null,
+ };
+
+ private readonly record struct ValuesRange(double Min, double Max)
+ {
+ public bool IsOutOfRange(double value) =>
+ value < Min || value > Max;
+ }
+ }
+}
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs
index b51630272dd..7477a3e62e6 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs
@@ -2122,7 +2122,7 @@ internal static class RuleTypeMappingCS
// ["S2195"],
// ["S2196"],
["S2197"] = "CODE_SMELL",
- // ["S2198"],
+ ["S2198"] = "CODE_SMELL",
// ["S2199"],
// ["S2200"],
["S2201"] = "BUG",
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/SillyMathematicalComparison.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/SillyMathematicalComparison.cs
new file mode 100644
index 00000000000..ae4d9ab8f52
--- /dev/null
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/SillyMathematicalComparison.cs
@@ -0,0 +1,38 @@
+/*
+ * SonarAnalyzer for .NET
+ * Copyright (C) 2015-2023 SonarSource SA
+ * mailto: contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using CS = SonarAnalyzer.Rules.CSharp;
+
+namespace SonarAnalyzer.UnitTest.Rules
+{
+ [TestClass]
+ public class SillyMathematicalComparisonTest
+ {
+ private readonly VerifierBuilder builderCS = new VerifierBuilder();
+
+ [TestMethod]
+ public void SillyMathematicalComparison_CS() =>
+ builderCS.AddPaths("SillyMathematicalComparison.cs").Verify();
+
+ [TestMethod]
+ public void SillyMathematicalComparison_CSharp9() =>
+ builderCS.AddPaths("SillyMathematicalComparison.CSharp9.cs").WithOptions(ParseOptionsHelper.FromCSharp9).Verify();
+ }
+}
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.CSharp9.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.CSharp9.cs
new file mode 100644
index 00000000000..bc0f781da1a
--- /dev/null
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.CSharp9.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SonarAnalyzer.UnitTest.TestCases
+{
+ internal class SillyMathematicalComparison
+ {
+ public void IsPatterns()
+ {
+ _ = 33 is 55; // Compliant
+ _ = 33 is < 55; // Compliant
+ _ = 33 is <= 55; // Compliant
+ _ = 33 is > 55; // Compliant
+ _ = 33 is >= 55; // Compliant
+ _ = 33 is not 55; // Compliant
+
+ const double d = double.MaxValue;
+ float x = 42;
+ _ = x is d; // Error [CS0266] - cannot implicitly convert type 'double' to 'float'
+ }
+ }
+}
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.cs
new file mode 100644
index 00000000000..238e34dcd72
--- /dev/null
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SillyMathematicalComparison.cs
@@ -0,0 +1,457 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SonarAnalyzer.UnitTest.TestCases
+{
+ internal class SillyMathematicalComparison
+ {
+ private T GetValue() => default(T);
+
+ public void Chars() // Rule applies
+ {
+ const char smallChar = char.MinValue;
+ const int smallInt = char.MinValue;
+ const long smallLong = char.MinValue;
+ const float smallFloat = char.MinValue;
+ const double smallDouble = char.MinValue;
+ const decimal smallDecimal = char.MinValue;
+
+ const char bigChar = char.MaxValue;
+ const int bigInt = char.MaxValue;
+ const long bigLong = char.MaxValue;
+ const float bigFloat = char.MaxValue;
+ const double bigDouble = char.MaxValue;
+ const decimal bigDecimal = char.MaxValue;
+
+ var c = GetValue();
+
+ _ = c >= bigChar; // Compliant, not (always true) or (always false)
+ _ = c >= bigInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= c; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= c; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= c; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= c; // Compliant, not (always true) or (always false)
+
+ _ = c <= smallChar; // Compliant, not (always true) or (always false)
+ _ = c <= smallInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= c; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= c; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= c; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= c; // Compliant, not (always true) or (always false)
+
+ const int veryBig = int.MaxValue;
+ const long verySmall = long.MinValue;
+
+ _ = c < veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'char'}}
+ _ = veryBig > c; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'char'}}
+ _ = c > verySmall; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'char'}}
+ _ = verySmall < c; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'char'}}
+ _ = verySmall == c; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'char'}}
+ _ = c == veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'char'}}
+ }
+
+ public void SBytes() // CS0652
+ {
+ const sbyte smallSByte = sbyte.MinValue;
+ const short smallShort = sbyte.MinValue;
+ const int smallInt = sbyte.MinValue;
+ const long smallLong = sbyte.MinValue;
+ const float smallFloat = sbyte.MinValue;
+ const double smallDouble = sbyte.MinValue;
+ const decimal smallDecimal = sbyte.MinValue;
+
+ const sbyte bigSByte = sbyte.MaxValue;
+ const short bigShort = sbyte.MaxValue;
+ const int bigInt = sbyte.MaxValue;
+ const long bigLong = sbyte.MaxValue;
+ const float bigFloat = sbyte.MaxValue;
+ const double bigDouble = sbyte.MaxValue;
+ const decimal bigDecimal = sbyte.MaxValue;
+
+ var sb = GetValue();
+
+ _ = sb >= bigSByte; // Compliant, not (always true) or (always false)
+ _ = sb >= bigShort; // Compliant, not (always true) or (always false)
+ _ = sb >= bigInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= sb; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= sb; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= sb; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= sb; // Compliant, not (always true) or (always false)
+
+ _ = sb <= smallSByte; // Compliant, not (always true) or (always false)
+ _ = sb <= smallShort; // Compliant, not (always true) or (always false)
+ _ = sb <= smallInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= sb; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= sb; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= sb; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= sb; // Compliant, not (always true) or (always false)
+
+ const int veryBig = int.MaxValue;
+ const long verySmall = long.MinValue;
+
+ _ = sb < veryBig; // Compliant, raised by CS0652
+ _ = veryBig > sb; // Compliant, raised by CS0652
+ _ = sb > verySmall; // Compliant, raised by CS0652
+ _ = verySmall < sb; // Compliant, raised by CS0652
+ _ = verySmall == sb; // Compliant, raised by CS0652
+ _ = sb == veryBig; // Compliant, raised by CS0652
+ }
+
+ public void Bytes() // CS0652
+ {
+ const byte smallByte = byte.MinValue;
+ const short smallShort = byte.MinValue;
+ const ushort smallUShort = byte.MinValue;
+ const int smallInt = byte.MinValue;
+ const uint smallUInt = byte.MinValue;
+ const long smallLong = byte.MinValue;
+ const ulong smallULong = byte.MinValue;
+ const float smallFloat = byte.MinValue;
+ const double smallDouble = byte.MinValue;
+ const decimal smallDecimal = byte.MinValue;
+
+ const byte bigByte = byte.MaxValue;
+ const short bigShort = byte.MaxValue;
+ const ushort bigUShort = byte.MaxValue;
+ const int bigInt = byte.MaxValue;
+ const uint bigUInt = byte.MaxValue;
+ const long bigLong = byte.MaxValue;
+ const ulong bigULong = byte.MaxValue;
+ const float bigFloat = byte.MaxValue;
+ const double bigDouble = byte.MaxValue;
+ const decimal bigDecimal = byte.MaxValue;
+
+ var b = GetValue();
+
+ _ = b >= bigByte; // Compliant, not (always true) or (always false)
+ _ = b >= bigShort; // Compliant, not (always true) or (always false)
+ _ = b >= bigUShort; // Compliant, not (always true) or (always false)
+ _ = b >= bigInt; // Compliant, not (always true) or (always false)
+ _ = b >= bigUInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= b; // Compliant, not (always true) or (always false)
+ _ = bigULong <= b; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= b; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= b; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= b; // Compliant, not (always true) or (always false)
+
+ _ = b <= smallByte; // Compliant, not (always true) or (always false)
+ _ = b <= smallShort; // Compliant, not (always true) or (always false)
+ _ = b <= smallUShort; // Compliant, not (always true) or (always false)
+ _ = b <= smallInt; // Compliant, not (always true) or (always false)
+ _ = b <= smallUInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= b; // Compliant, not (always true) or (always false)
+ _ = smallULong >= b; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= b; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= b; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= b; // Compliant, not (always true) or (always false)
+
+ const int veryBig = int.MaxValue;
+ const short verySmall = short.MinValue;
+
+ _ = b < veryBig; // Compliant, raised by CS0652
+ _ = veryBig > b; // Compliant, raised by CS0652
+ _ = b > verySmall; // Compliant, raised by CS0652
+ _ = verySmall < b; // Compliant, raised by CS0652
+ _ = verySmall == b; // Compliant, raised by CS0652
+ _ = b == veryBig; // Compliant, raised by CS0652
+ }
+
+ public void Shorts() // CS0652
+ {
+ const short smallShort = short.MinValue;
+ const int smallInt = short.MinValue;
+ const long smallLong = short.MinValue;
+ const float smallFloat = short.MinValue;
+ const double smallDouble = short.MinValue;
+ const decimal smallDecimal = short.MinValue;
+
+ const short bigShort = short.MaxValue;
+ const int bigInt = short.MaxValue;
+ const long bigLong = short.MaxValue;
+ const float bigFloat = short.MaxValue;
+ const double bigDouble = short.MaxValue;
+ const decimal bigDecimal = short.MaxValue;
+
+ var s = GetValue();
+
+ _ = s >= bigShort; // Compliant, not (always true) or (always false)
+ _ = s >= bigInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= s; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= s; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= s; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= s; // Compliant, not (always true) or (always false)
+
+ _ = s <= smallShort; // Compliant, not (always true) or (always false)
+ _ = s <= smallInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= s; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= s; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= s; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= s; // Compliant, not (always true) or (always false)
+
+ const int veryBig = int.MaxValue;
+ const long verySmall = long.MinValue;
+
+ _ = s < veryBig; // Compliant, raised by CS0652
+ _ = veryBig > s; // Compliant, raised by CS0652
+ _ = s > verySmall; // Compliant, raised by CS0652
+ _ = verySmall < s; // Compliant, raised by CS0652
+ _ = verySmall == s; // Compliant, raised by CS0652
+ _ = s == veryBig; // Compliant, raised by CS0652
+ }
+
+ public void UShorts() // CS0652
+ {
+ const ushort smallUShort = ushort.MinValue;
+ const int smallInt = ushort.MinValue;
+ const uint smallUInt = ushort.MinValue;
+ const long smallLong = ushort.MinValue;
+ const ulong smallULong = ushort.MinValue;
+ const float smallFloat = ushort.MinValue;
+ const double smallDouble = ushort.MinValue;
+ const decimal smallDecimal = ushort.MinValue;
+
+ const ushort bigUShort = ushort.MaxValue;
+ const int bigInt = ushort.MaxValue;
+ const uint bigUInt = ushort.MaxValue;
+ const long bigLong = ushort.MaxValue;
+ const ulong bigULong = ushort.MaxValue;
+ const float bigFloat = ushort.MaxValue;
+ const double bigDouble = ushort.MaxValue;
+ const decimal bigDecimal = ushort.MaxValue;
+
+ var us = GetValue();
+
+ _ = us >= bigUShort; // Compliant, not (always true) or (always false)
+ _ = us >= bigInt; // Compliant, not (always true) or (always false)
+ _ = us >= bigUInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= us; // Compliant, not (always true) or (always false)
+ _ = bigULong <= us; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= us; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= us; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= us; // Compliant, not (always true) or (always false)
+
+ _ = us <= smallUShort; // Compliant, not (always true) or (always false)
+ _ = us <= smallInt; // Compliant, not (always true) or (always false)
+ _ = us <= smallUInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= us; // Compliant, not (always true) or (always false)
+ _ = smallULong >= us; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= us; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= us; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= us; // Compliant, not (always true) or (always false)
+
+ const int veryBig = int.MaxValue;
+ const long verySmall = long.MinValue;
+
+ _ = us < veryBig; // Compliant, raised by CS0652
+ _ = veryBig > us; // Compliant, raised by CS0652
+ _ = us > verySmall; // Compliant, raised by CS0652
+ _ = verySmall < us; // Compliant, raised by CS0652
+ _ = verySmall == us; // Compliant, raised by CS0652
+ _ = us == veryBig; // Compliant, raised by CS0652
+ }
+
+ public void Ints() // CS0652
+ {
+ const int smallInt = int.MinValue;
+ const long smallLong = int.MinValue;
+ const float smallFloat = int.MinValue;
+ const double smallDouble = int.MinValue;
+ const decimal smallDecimal = int.MinValue;
+
+ const int bigInt = int.MaxValue;
+ const uint bigUInt = int.MaxValue;
+ const long bigLong = int.MaxValue;
+ const float bigFloat = int.MaxValue;
+ const double bigDouble = int.MaxValue;
+ const decimal bigDecimal = int.MaxValue;
+
+ var i = GetValue();
+
+ _ = i >= bigInt; // Compliant, not (always true) or (always false)
+ _ = i >= bigUInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= i; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= i; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= i; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= i; // Compliant, not (always true) or (always false)
+
+ _ = i <= smallInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= i; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= i; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= i; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= i; // Compliant, not (always true) or (always false)
+
+ const long veryBig = long.MaxValue;
+ const long verySmall = long.MinValue;
+
+ _ = i < veryBig; // Compliant, raised by CS0652
+ _ = veryBig > i; // Compliant, raised by CS0652
+ _ = i > verySmall; // Compliant, raised by CS0652
+ _ = verySmall < i; // Compliant, raised by CS0652
+ _ = verySmall == i; // Compliant, raised by CS0652
+ _ = i == veryBig; // Compliant, raised by CS0652
+ }
+
+ public void UInts() // CS0652
+ {
+ const uint smallUInt = uint.MinValue;
+ const long smallLong = uint.MinValue;
+ const ulong smallULong = uint.MinValue;
+ const float smallFloat = uint.MinValue;
+ const double smallDouble = uint.MinValue;
+ const decimal smallDecimal = uint.MinValue;
+
+ const uint bigUInt = uint.MaxValue;
+ const long bigLong = uint.MaxValue;
+ const ulong bigULong = uint.MaxValue;
+ const float bigFloat = uint.MaxValue;
+ const double bigDouble = uint.MaxValue;
+ const decimal bigDecimal = uint.MaxValue;
+
+ var ui = GetValue();
+
+ _ = ui >= bigUInt; // Compliant, not (always true) or (always false)
+ _ = bigLong <= ui; // Compliant, not (always true) or (always false)
+ _ = bigULong <= ui; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= ui; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= ui; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= ui; // Compliant, not (always true) or (always false)
+
+ _ = ui <= smallUInt; // Compliant, not (always true) or (always false)
+ _ = smallLong >= ui; // Compliant, not (always true) or (always false)
+ _ = smallULong >= ui; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= ui; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= ui; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= ui; // Compliant, not (always true) or (always false)
+
+ const long veryBig = long.MaxValue;
+ const long verySmall = long.MinValue;
+
+ _ = ui < veryBig; // Compliant, raised by CS0652
+ _ = veryBig > ui; // Compliant, raised by CS0652
+ _ = ui > verySmall; // Compliant, raised by CS0652
+ _ = verySmall < ui; // Compliant, raised by CS0652
+ _ = verySmall == ui; // Compliant, raised by CS0652
+ _ = ui == veryBig; // Compliant, raised by CS0652
+ }
+
+ public void Longs() // Rule applies
+ {
+ const long smallLong = long.MinValue;
+ const float smallFloat = long.MinValue;
+ const double smallDouble = long.MinValue;
+ const decimal smallDecimal = long.MinValue;
+
+ const long bigLong = long.MaxValue;
+ const float bigFloat = long.MaxValue;
+ const double bigDouble = long.MaxValue;
+ const decimal bigDecimal = long.MaxValue;
+
+ var l = GetValue();
+
+ _ = bigLong <= l; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= l; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= l; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= l; // Compliant, not (always true) or (always false)
+
+ _ = smallLong >= l; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= l; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= l; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= l; // Compliant, not (always true) or (always false)
+
+ const double veryBig = double.MaxValue;
+ const double verySmall = double.MinValue;
+
+ _ = l < veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'long'}}
+ _ = veryBig > l; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'long'}}
+ _ = l > verySmall; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'long'}}
+ _ = verySmall < l; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'long'}}
+ _ = verySmall == l; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'long'}}
+ _ = l == veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'long'}}
+ }
+
+ public void ULongs() // Rule applies
+ {
+ const ulong smallULong = ulong.MinValue;
+ const float smallFloat = ulong.MinValue;
+ const double smallDouble = ulong.MinValue;
+ const decimal smallDecimal = ulong.MinValue;
+
+ const ulong bigULong = ulong.MaxValue;
+ const float bigFloat = ulong.MaxValue;
+ const double bigDouble = ulong.MaxValue;
+ const decimal bigDecimal = ulong.MaxValue;
+
+ ulong ul = 42;
+
+ _ = bigULong <= ul; // Compliant, not (always true) or (always false)
+ _ = bigFloat <= ul; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= ul; // Compliant, not (always true) or (always false)
+ _ = bigDecimal <= ul; // Compliant, not (always true) or (always false)
+
+ _ = smallULong >= ul; // Compliant, not (always true) or (always false)
+ _ = smallFloat >= ul; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= ul; // Compliant, not (always true) or (always false)
+ _ = smallDecimal >= ul; // Compliant, not (always true) or (always false)
+
+ const float veryBig = float.MaxValue;
+ const float verySmall = float.MinValue;
+
+ _ = ul < veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'ulong'}}
+ _ = veryBig > ul; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'ulong'}}
+ _ = ul > verySmall; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'ulong'}}
+ _ = verySmall < ul; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'ulong'}}
+ _ = verySmall == ul; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'ulong'}}
+ _ = ul == veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'ulong'}}
+ }
+
+ public void Floats() // Rule applies
+ {
+ const float smallFloat = float.MinValue;
+ const double smallDouble = float.MinValue;
+
+ const float bigFloat = float.MaxValue;
+ const double bigDouble = float.MaxValue;
+
+
+ const long l = long.MaxValue;
+ var i = 42;
+
+ System.Single f = GetValue();
+
+ _ = bigFloat <= f; // Compliant, not (always true) or (always false)
+ _ = bigDouble <= f; // Compliant, not (always true) or (always false)
+
+ _ = smallFloat >= f; // Compliant, not (always true) or (always false)
+ _ = smallDouble >= f; // Compliant, not (always true) or (always false)
+
+ const double veryBig = double.MaxValue;
+ const double verySmall = double.MinValue;
+
+ _ = f < veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'float'}}
+ _ = veryBig > f; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'float'}}
+ _ = f > verySmall; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'float'}}
+ _ = verySmall < f; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'float'}}
+ _ = verySmall == f; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'float'}}
+ _ = f == veryBig; // Noncompliant {{Comparison to this constant is useless; the constant is outside the range of type 'float'}}
+ }
+
+ public void ConstantInBothOperands()
+ {
+ const float number1 = 42;
+ const double number2 = double.MaxValue;
+
+ // https://github.com/SonarSource/sonar-dotnet/issues/6745
+ _ = number1 != number2; // Compliant, constant in both operands
+ _ = number1 == number2; // Compliant
+ _ = number1 <= double.MaxValue; // Compliant
+ _ = double.MaxValue >= number2; // Compliant
+
+ _ = 42f != double.MaxValue; // Compliant
+ _ = 42f <= double.MaxValue; // Compliant
+ }
+ }
+}