Skip to content

Commit 1315673

Browse files
Merge pull request #1251 from GrahamTheCoder/claude/fix-issue-803-relational-switch-pattern
Fix #803: Use C# 9 relational patterns for VB `Case Is <op> constant`
2 parents 76b1e25 + bccf8e0 commit 1315673

File tree

4 files changed

+104
-9
lines changed

4 files changed

+104
-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);
@@ -1038,6 +1045,14 @@ private CasePatternSwitchLabelSyntax WrapInCasePatternSwitchLabelSyntax(VBSyntax
10381045
_ => throw new ArgumentOutOfRangeException(nameof(caseClauseKind), caseClauseKind, null)
10391046
};
10401047

1048+
private static CS.SyntaxKind GetRelationalPatternTokenKind(VBasic.SyntaxKind caseClauseKind) => caseClauseKind switch {
1049+
VBasic.SyntaxKind.CaseLessThanClause => CS.SyntaxKind.LessThanToken,
1050+
VBasic.SyntaxKind.CaseLessThanOrEqualClause => CS.SyntaxKind.LessThanEqualsToken,
1051+
VBasic.SyntaxKind.CaseGreaterThanOrEqualClause => CS.SyntaxKind.GreaterThanEqualsToken,
1052+
VBasic.SyntaxKind.CaseGreaterThanClause => CS.SyntaxKind.GreaterThanToken,
1053+
_ => throw new ArgumentOutOfRangeException(nameof(caseClauseKind), caseClauseKind, null)
1054+
};
1055+
10411056
private ExpressionSyntax ComparisonAdjustedForStringComparison(VBSyntax.SelectBlockSyntax node, VBSyntax.ExpressionSyntax vbCase, TypeInfo lhsTypeInfo, ExpressionSyntax csLeft, ExpressionSyntax csRight, TypeInfo rhsTypeInfo, ComparisonKind comparisonKind)
10421057
{
10431058
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(CS.CSharpExtensions.Kind(relational.OperatorToken));
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
}
@@ -1292,15 +1292,15 @@ public void DoesNotThrow()
12921292
var rand = new Random();
12931293
switch (rand.Next(8))
12941294
{
1295-
case var @case when @case < 4:
1295+
case < 4:
12961296
{
12971297
break;
12981298
}
12991299
case 4:
13001300
{
13011301
break;
13021302
}
1303-
case var case1 when case1 > 4:
1303+
case > 4:
13041304
{
13051305
break;
13061306
}
@@ -1311,9 +1311,46 @@ public void DoesNotThrow()
13111311
}
13121312
}
13131313
}
1314-
}
1315-
1 target compilation errors:
1316-
CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code");
1314+
}");
1315+
}
1316+
1317+
[Fact]
1318+
public async Task Issue803SelectCaseIsRelationalUsesC9PatternAsync()
1319+
{
1320+
await TestConversionVisualBasicToCSharpAsync(@"Public Class TestClass
1321+
Function Rollo(Breite As Integer) As Integer
1322+
Select Case Breite
1323+
Case Is < 1000
1324+
Return 12
1325+
Case Is < 1200
1326+
Return 15
1327+
Case Else
1328+
Return 28
1329+
End Select
1330+
End Function
1331+
End Class", @"
1332+
public partial class TestClass
1333+
{
1334+
public int Rollo(int Breite)
1335+
{
1336+
switch (Breite)
1337+
{
1338+
case < 1000:
1339+
{
1340+
return 12;
1341+
}
1342+
case < 1200:
1343+
{
1344+
return 15;
1345+
}
1346+
1347+
default:
1348+
{
1349+
return 28;
1350+
}
1351+
}
1352+
}
1353+
}");
13171354
}
13181355

13191356
[Fact]

Tests/VB/StatementTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,34 @@ End Function
12821282
End Class");
12831283
}
12841284

1285+
[Fact]
1286+
public async Task SelectCase_WithRelationalPatternAsync()
1287+
{
1288+
await TestConversionCSharpToVisualBasicAsync(@"class TestClass
1289+
{
1290+
public void Classify(int n)
1291+
{
1292+
switch (n) {
1293+
case < 0:
1294+
System.Console.Write(""negative"");
1295+
break;
1296+
case >= 0:
1297+
System.Console.Write(""non-negative"");
1298+
break;
1299+
}
1300+
}
1301+
}", @"Friend Class TestClass
1302+
Public Sub Classify(n As Integer)
1303+
Select Case n
1304+
Case < 0
1305+
Console.Write(""negative"")
1306+
Case >= 0
1307+
Console.Write(""non-negative"")
1308+
End Select
1309+
End Sub
1310+
End Class");
1311+
}
1312+
12851313
[Fact]
12861314
public async Task TryCatchAsync()
12871315
{

0 commit comments

Comments
 (0)