Skip to content

Commit 9319003

Browse files
committed
Fix #803: Use C# 9 relational patterns for VB Case Is <op> constant
When converting VB `Select Case` with `Case Is < value`, `Case Is > value`, etc. (RelationalCaseClauseSyntax), emit clean C# 9 relational patterns (`case < 1000:`) instead of the verbose `case var @case when @case < 1000:` pattern, for non-object, non-string comparisons where the case value is a constant expression. Also handle RelationalPatternSyntax in the C#→VB converter so that C# 9 relational patterns round-trip correctly back to VB `Case Is <op> value`. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX
1 parent 11a413a commit 9319003

File tree

3 files changed

+76
-9
lines changed

3 files changed

+76
-9
lines changed

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,13 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta
843843
if (isObjectComparison) {
844844
caseSwitchLabelSyntax = WrapInCasePatternSwitchLabelSyntax(node, relational.Value, csRelationalValue, false, operatorKind);
845845
}
846+
else if (!isStringComparison && _semanticModel.GetConstantValue(relational.Value).HasValue) {
847+
csRelationalValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(relational.Value, csRelationalValue);
848+
var operatorToken = SyntaxFactory.Token(GetRelationalPatternTokenKind(operatorKind));
849+
caseSwitchLabelSyntax = SyntaxFactory.CasePatternSwitchLabel(
850+
SyntaxFactory.RelationalPattern(operatorToken, csRelationalValue),
851+
SyntaxFactory.Token(SyntaxKind.ColonToken));
852+
}
846853
else {
847854
var varName = CommonConversions.CsEscapedIdentifier(GetUniqueVariableNameInScope(node, "case"));
848855
ExpressionSyntax csLeft = ValidSyntaxFactory.IdentifierName(varName);
@@ -1021,6 +1028,14 @@ private CasePatternSwitchLabelSyntax WrapInCasePatternSwitchLabelSyntax(VBSyntax
10211028
_ => throw new ArgumentOutOfRangeException(nameof(caseClauseKind), caseClauseKind, null)
10221029
};
10231030

1031+
private static CS.SyntaxKind GetRelationalPatternTokenKind(VBasic.SyntaxKind caseClauseKind) => caseClauseKind switch {
1032+
VBasic.SyntaxKind.CaseLessThanClause => CS.SyntaxKind.LessThanToken,
1033+
VBasic.SyntaxKind.CaseLessThanOrEqualClause => CS.SyntaxKind.LessThanEqualsToken,
1034+
VBasic.SyntaxKind.CaseGreaterThanOrEqualClause => CS.SyntaxKind.GreaterThanEqualsToken,
1035+
VBasic.SyntaxKind.CaseGreaterThanClause => CS.SyntaxKind.GreaterThanToken,
1036+
_ => throw new ArgumentOutOfRangeException(nameof(caseClauseKind), caseClauseKind, null)
1037+
};
1038+
10241039
private ExpressionSyntax ComparisonAdjustedForStringComparison(VBSyntax.SelectBlockSyntax node, VBSyntax.ExpressionSyntax vbCase, TypeInfo lhsTypeInfo, ExpressionSyntax csLeft, ExpressionSyntax csRight, TypeInfo rhsTypeInfo, ComparisonKind comparisonKind)
10251040
{
10261041
var vbEquality = CommonConversions.VisualBasicEqualityComparison;

CodeConverter/VB/NodesVisitor.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,12 @@ public override VisualBasicSyntaxNode VisitThrowExpression(CSSyntax.ThrowExpress
16431643

16441644
public override VisualBasicSyntaxNode VisitCasePatternSwitchLabel(CSSyntax.CasePatternSwitchLabelSyntax node)
16451645
{
1646+
if (node.Pattern is CSSyntax.RelationalPatternSyntax relational) {
1647+
var value = (ExpressionSyntax)relational.Expression.Accept(TriviaConvertingVisitor);
1648+
var (caseClauseKind, tokenKind) = GetRelationalCaseClauseKinds(relational.OperatorToken.Kind());
1649+
return SyntaxFactory.RelationalCaseClause(caseClauseKind,
1650+
SyntaxFactory.Token(tokenKind), value);
1651+
}
16461652
var condition = node.WhenClause.Condition.SkipIntoParens();
16471653
switch (condition) {
16481654
case CSSyntax.BinaryExpressionSyntax bes when node.Pattern.ToString().StartsWith("var", StringComparison.InvariantCulture): //VarPatternSyntax (not available in current library version)
@@ -1654,6 +1660,15 @@ public override VisualBasicSyntaxNode VisitCasePatternSwitchLabel(CSSyntax.CaseP
16541660
throw new NotSupportedException(condition.GetType() + " in switch case");
16551661
}
16561662
}
1663+
1664+
private static (SyntaxKind CaseClauseKind, SyntaxKind TokenKind) GetRelationalCaseClauseKinds(CS.SyntaxKind csTokenKind) => csTokenKind switch {
1665+
CS.SyntaxKind.LessThanToken => (SyntaxKind.CaseLessThanClause, SyntaxKind.LessThanToken),
1666+
CS.SyntaxKind.LessThanEqualsToken => (SyntaxKind.CaseLessThanOrEqualClause, SyntaxKind.LessThanEqualsToken),
1667+
CS.SyntaxKind.GreaterThanToken => (SyntaxKind.CaseGreaterThanClause, SyntaxKind.GreaterThanToken),
1668+
CS.SyntaxKind.GreaterThanEqualsToken => (SyntaxKind.CaseGreaterThanOrEqualClause, SyntaxKind.GreaterThanEqualsToken),
1669+
_ => throw new NotSupportedException($"Relational operator token {csTokenKind} not supported in VB case clause")
1670+
};
1671+
16571672
private INamedTypeSymbol GetStructOrClassSymbol(CS.CSharpSyntaxNode node) {
16581673
return (INamedTypeSymbol)_semanticModel.GetDeclaredSymbol(node.Ancestors().First(x => x is CSSyntax.ClassDeclarationSyntax || x is CSSyntax.StructDeclarationSyntax));
16591674
}

Tests/CSharp/StatementTests/MethodStatementTests.cs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,13 +1089,13 @@ public static string TimeAgo(int daysAgo)
10891089
{
10901090
case var @case when 0 <= @case && @case <= 3:
10911091
case 4:
1092-
case var case1 when case1 >= 5:
1093-
case var case2 when case2 < 6:
1094-
case var case3 when case3 <= 7:
1092+
case >= 5:
1093+
case < 6:
1094+
case <= 7:
10951095
{
10961096
return ""this week"";
10971097
}
1098-
case var case4 when case4 > 0:
1098+
case > 0:
10991099
{
11001100
return daysAgo / 7 + "" weeks ago"";
11011101
}
@@ -1249,15 +1249,15 @@ public void DoesNotThrow()
12491249
var rand = new Random();
12501250
switch (rand.Next(8))
12511251
{
1252-
case var @case when @case < 4:
1252+
case < 4:
12531253
{
12541254
break;
12551255
}
12561256
case 4:
12571257
{
12581258
break;
12591259
}
1260-
case var case1 when case1 > 4:
1260+
case > 4:
12611261
{
12621262
break;
12631263
}
@@ -1268,9 +1268,46 @@ public void DoesNotThrow()
12681268
}
12691269
}
12701270
}
1271-
}
1272-
1 target compilation errors:
1273-
CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code");
1271+
}");
1272+
}
1273+
1274+
[Fact]
1275+
public async Task Issue803SelectCaseIsRelationalUsesC9PatternAsync()
1276+
{
1277+
await TestConversionVisualBasicToCSharpAsync(@"Public Class TestClass
1278+
Function Rollo(Breite As Integer) As Integer
1279+
Select Case Breite
1280+
Case Is < 1000
1281+
Return 12
1282+
Case Is < 1200
1283+
Return 15
1284+
Case Else
1285+
Return 28
1286+
End Select
1287+
End Function
1288+
End Class", @"
1289+
public partial class TestClass
1290+
{
1291+
public int Rollo(int Breite)
1292+
{
1293+
switch (Breite)
1294+
{
1295+
case < 1000:
1296+
{
1297+
return 12;
1298+
}
1299+
case < 1200:
1300+
{
1301+
return 15;
1302+
}
1303+
1304+
default:
1305+
{
1306+
return 28;
1307+
}
1308+
}
1309+
}
1310+
}");
12741311
}
12751312

12761313
[Fact]

0 commit comments

Comments
 (0)