Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,8 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta
lowerBoundCheck = ComparisonAdjustedForStringComparison(node, range.LowerBound, caseTypeInfo, lowerBound, csCaseVar, switchExprTypeInfo, ComparisonKind.LessThanOrEqual);
} else {
lowerBound = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(range.LowerBound, lowerBound);
lowerBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, lowerBound, csCaseVar);
var (lowerBoundForComparison, csCaseVarForLower) = CastBothToUnderlyingTypeIfEnum(switchExprTypeInfo.ConvertedType, lowerBound, csCaseVar);
lowerBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, lowerBoundForComparison, csCaseVarForLower);
}
var upperBound = await range.UpperBound.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
ExpressionSyntax upperBoundCheck;
Expand All @@ -870,7 +871,8 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta
upperBoundCheck = ComparisonAdjustedForStringComparison(node, range.UpperBound, switchExprTypeInfo, csCaseVar, upperBound, caseTypeInfo, ComparisonKind.LessThanOrEqual);
} else {
upperBound = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(range.UpperBound, upperBound);
upperBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, csCaseVar, upperBound);
var (csCaseVarForUpper, upperBoundForComparison) = CastBothToUnderlyingTypeIfEnum(switchExprTypeInfo.ConvertedType, csCaseVar, upperBound);
upperBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, csCaseVarForUpper, upperBoundForComparison);
}
var withinBounds = SyntaxFactory.BinaryExpression(SyntaxKind.LogicalAndExpression, lowerBoundCheck, upperBoundCheck);
labels.Add(VarWhen(varName, withinBounds));
Expand Down Expand Up @@ -930,6 +932,21 @@ bool IsExitStatement(StatementSyntax node)
private static bool IsEnumOrNullableEnum(ITypeSymbol convertedType) =>
convertedType?.IsEnumType() == true || convertedType?.GetNullableUnderlyingType()?.IsEnumType() == true;

/// <summary>
/// When the switch expression is an enum, C# does not support &lt;= comparisons directly on enum values.
/// Cast both sides to the enum's underlying integer type so the comparison compiles.
/// </summary>
private (ExpressionSyntax Left, ExpressionSyntax Right) CastBothToUnderlyingTypeIfEnum(ITypeSymbol switchExprType, ExpressionSyntax left, ExpressionSyntax right)
{
var enumType = switchExprType?.IsEnumType() == true ? switchExprType as INamedTypeSymbol
: switchExprType?.GetNullableUnderlyingType() as INamedTypeSymbol;
if (enumType?.EnumUnderlyingType is not { } underlyingType) {
return (left, right);
}
var typeSyntax = CommonConversions.GetTypeSyntax(underlyingType);
return (ValidSyntaxFactory.CastExpression(typeSyntax, left), ValidSyntaxFactory.CastExpression(typeSyntax, right));
}

private static CasePatternSwitchLabelSyntax VarWhen(SyntaxToken varName, ExpressionSyntax binaryExp)
{
var patternMatch = ValidSyntaxFactory.VarPattern(varName);
Expand Down
44 changes: 44 additions & 0 deletions Tests/CSharp/StatementTests/MethodStatementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,50 @@ public static string TimeAgo(int daysAgo)
CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code");
}

[Fact]
public async Task SelectCaseWithEnumRangeAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"Public Class TestClass
Enum UserLevel
City_Staff
Admin
Fixity_ROOT
End Enum

Shared Function IsPrivileged(level As UserLevel) As Boolean
Select Case level
Case UserLevel.City_Staff To UserLevel.Fixity_ROOT
Return True
End Select
Return False
End Function
End Class", @"
public partial class TestClass
{
public enum UserLevel
{
City_Staff,
Admin,
Fixity_ROOT
}

public static bool IsPrivileged(UserLevel level)
{
switch (level)
{
case var @case when (int)UserLevel.City_Staff <= (int)@case && (int)@case <= (int)UserLevel.Fixity_ROOT:
{
return true;
}
}

return false;
}
}
1 target compilation errors:
CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code");
}

[Fact]
public async Task SelectCaseWithStringAsync()
{
Expand Down
Loading