Skip to content

Commit

Permalink
Implement
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mikula-sonarsource committed Dec 18, 2023
1 parent 4e9ed5c commit 519a4c5
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 9 deletions.
18 changes: 14 additions & 4 deletions analyzers/src/SonarAnalyzer.Common/Helpers/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using Microsoft.CodeAnalysis;
using static Google.Protobuf.WellKnownTypes.Field;

namespace SonarAnalyzer.Helpers;

internal static class TypeHelper
Expand Down Expand Up @@ -105,6 +108,11 @@ public static bool IsAny(this ITypeSymbol typeSymbol, ImmutableArray<KnownType>

return false;
}
public static bool IsNullableOfAny(this ITypeSymbol type, ImmutableArray<KnownType> argumentTypes) =>
NullableTypeArgument(type).IsAny(argumentTypes);

public static bool IsNullableOf(this ITypeSymbol type, KnownType typeArgument) =>
NullableTypeArgument(type).Is(typeArgument);

public static bool IsType(this IParameterSymbol parameter, KnownType type) =>
parameter != null && parameter.Type.Is(type);
Expand All @@ -121,10 +129,7 @@ public static bool IsInType(this ISymbol symbol, ImmutableArray<KnownType> types
#endregion TypeName

public static bool IsNullableBoolean(this ITypeSymbol type) =>
type is INamedTypeSymbol namedType
&& namedType.OriginalDefinition.Is(KnownType.System_Nullable_T)
&& namedType.TypeArguments.Length == 1
&& namedType.TypeArguments[0].Is(KnownType.System_Boolean);
type.IsNullableOf(KnownType.System_Boolean);

public static bool Implements(this ITypeSymbol typeSymbol, KnownType type) =>
typeSymbol is { }
Expand Down Expand Up @@ -205,4 +210,9 @@ public static ITypeSymbol GetSymbolType(this ISymbol symbol) =>
ITypeSymbol x => x,
_ => null,
};

private static ITypeSymbol NullableTypeArgument(ITypeSymbol type) =>
type is INamedTypeSymbol namedType && namedType.OriginalDefinition.Is(KnownType.System_Nullable_T)
? namedType.TypeArguments[0]
: null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,24 @@ private static ProgramState LearnBranchingNumberConstraint(ProgramState state, I
var kind = falseBranch ? Opposite(binary.OperatorKind) : binary.OperatorKind;
var leftNumber = state[binary.LeftOperand]?.Constraint<NumberConstraint>();
var rightNumber = state[binary.RightOperand]?.Constraint<NumberConstraint>();
if (rightNumber is not null && !binary.LeftOperand.ConstantValue.HasValue && binary.LeftOperand.TrackedSymbol(state) is { } leftSymbol)
if (rightNumber is not null && OperandSymbol(binary.LeftOperand) is { } leftSymbol)
{
state = LearnBranching(leftSymbol, leftNumber, kind, rightNumber);
}
if (leftNumber is not null && !binary.RightOperand.ConstantValue.HasValue && binary.RightOperand.TrackedSymbol(state) is { } rightSymbol)
if (leftNumber is not null && OperandSymbol(binary.RightOperand) is { } rightSymbol)
{
state = LearnBranching(rightSymbol, rightNumber, Flip(kind), leftNumber);
}
return state;

ISymbol OperandSymbol(IOperation operand) =>
!operand.ConstantValue.HasValue
&& operand.TrackedSymbol(state) is { } symbol
&& symbol.GetSymbolType() is INamedTypeSymbol type
&& (type.IsAny(KnownType.IntegralNumbersIncludingNative) || type.IsNullableOfAny(KnownType.IntegralNumbersIncludingNative))
? symbol
: null;

ProgramState LearnBranching(ISymbol symbol, NumberConstraint existingNumber, BinaryOperatorKind kind, NumberConstraint comparedNumber) =>
!(falseBranch && symbol.GetSymbolType().IsNullableValueType()) // Don't learn opposite for "nullable > 0", because it could also be <null>.
&& RelationalNumberConstraint(existingNumber, kind, comparedNumber, isLoopCondition, visitCount) is { } newConstraint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3487,17 +3487,59 @@ public static void Foo()
// https://github.com/SonarSource/sonar-dotnet/issues/8470
public class Repro_8470
{
public string Go()
public string WithDouble()
{
double t = 0.5;
if (t <= 0)
{
return "a";
}
if (t >= 1) // Noncompliant FP
if (t >= 1) // Compliant, we don't track floating point numbers
{
return "b";
}
return "c"; // Secondary FP
return "c";
}

public string WithDoubleSwappedOperands()
{
double t = 0.5;
if (0 >= t)
{
return "a";
}
if (1 <= t) // Compliant, we don't track floating point numbers
{
return "b";
}
return "c";
}

public string WithDecimal()
{
decimal t = 0.5M;
if (t <= 0)
{
return "a";
}
if (t >= 1) // Compliant, we don't track floating point numbers
{
return "b";
}
return "c";
}

public string WithFloat()
{
float t = 0.5F;
if (t <= 0)
{
return "a";
}
if (t >= 1) // Compliant, we don't track floating point numbers
{
return "b";
}
return "c";
}
}

0 comments on commit 519a4c5

Please sign in to comment.