Skip to content

Commit

Permalink
Select case for a mixture of strings and characters converts correctl…
Browse files Browse the repository at this point in the history
…y - fixes #1061
  • Loading branch information
GrahamTheCoder committed Dec 21, 2023
1 parent 480e4e8 commit 311e1e6
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

* Remove square brackets from identifiers [#1043](https://github.com/icsharpcode/CodeConverter/issues/1043)
* Conversion of parenthesized ref arguments no longer assigns back [#1046](https://github.com/icsharpcode/CodeConverter/issues/1046)
* Conversion of explicit interface implementations now converts optional parameters [#1062](https://github.com/icsharpcode/CodeConverter/issues/1062)
* Constant chars are converted to constant strings where needed
* Select case for a mixture of strings and characters converts correctly [#1062](https://github.com/icsharpcode/CodeConverter/issues/1062)


### C# -> VB

Expand Down
17 changes: 12 additions & 5 deletions CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,9 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta

// CSharp requires an explicit cast from the base type (e.g. int) in most cases switching on an enum
var isBooleanCase = caseTypeInfo.Type?.SpecialType == SpecialType.System_Boolean;
var csExpressionToUse = IsEnumOrNullableEnum(switchExprTypeInfo.ConvertedType) ^ IsEnumOrNullableEnum(caseTypeInfo.Type) && !isBooleanCase ? correctTypeExpressionSyntax.Expr : originalExpressionSyntax;
bool enumRelated = IsEnumOrNullableEnum(switchExprTypeInfo.ConvertedType) || IsEnumOrNullableEnum(caseTypeInfo.Type);
bool convertingEnum = IsEnumOrNullableEnum(switchExprTypeInfo.ConvertedType) ^ IsEnumOrNullableEnum(caseTypeInfo.Type);
var csExpressionToUse = !isBooleanCase && (convertingEnum || !enumRelated && correctTypeExpressionSyntax.IsConst) ? correctTypeExpressionSyntax.Expr : originalExpressionSyntax;

var caseSwitchLabelSyntax = !wrapForStringComparison && correctTypeExpressionSyntax.IsConst && notAlreadyUsed
? (SwitchLabelSyntax)SyntaxFactory.CaseSwitchLabel(csExpressionToUse)
Expand All @@ -783,14 +785,19 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta
var varName = CommonConversions.CsEscapedIdentifier(GetUniqueVariableNameInScope(node, "case"));
ExpressionSyntax csLeft = ValidSyntaxFactory.IdentifierName(varName);
var operatorKind = VBasic.VisualBasicExtensions.Kind(relational);
var relationalValue = await relational.Value.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
var binaryExp = SyntaxFactory.BinaryExpression(operatorKind.ConvertToken(), csLeft, relationalValue);
var csRelationalValue = await relational.Value.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
csRelationalValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(relational.Value, csRelationalValue);
var binaryExp = SyntaxFactory.BinaryExpression(operatorKind.ConvertToken(), csLeft, csRelationalValue);
labels.Add(VarWhen(varName, binaryExp));
} else if (c is VBSyntax.RangeCaseClauseSyntax range) {
var varName = CommonConversions.CsEscapedIdentifier(GetUniqueVariableNameInScope(node, "case"));
ExpressionSyntax csLeft = ValidSyntaxFactory.IdentifierName(varName);
var lowerBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, await range.LowerBound.AcceptAsync<ExpressionSyntax>(_expressionVisitor), csLeft);
var upperBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, csLeft, await range.UpperBound.AcceptAsync<ExpressionSyntax>(_expressionVisitor));
var lowerBound = await range.LowerBound.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
lowerBound = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(range.LowerBound, lowerBound);
var lowerBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, lowerBound, csLeft);
var upperBound = await range.UpperBound.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
upperBound = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(range.UpperBound, upperBound);
var upperBoundCheck = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, csLeft, upperBound);
var withinBounds = SyntaxFactory.BinaryExpression(SyntaxKind.LogicalAndExpression, lowerBoundCheck, upperBoundCheck);
labels.Add(VarWhen(varName, withinBounds));
} else {
Expand Down
36 changes: 36 additions & 0 deletions Tests/CSharp/TypeCastTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,42 @@ private string[] QuoteSplit(string text)
}");
}


[Fact]
public async Task TestSelectCaseComparesCharsAndStringsAsync()
{
await TestConversionVisualBasicToCSharpAsync(
@"
Class CharTestClass
Private Sub Q()
Select Case ""a""
Case ""x""c To ""y""c
Case ""b""c
End Select
End Sub
End Class", @"
internal partial class CharTestClass
{
private void Q()
{
switch (""a"")
{
case var @case when ""x"" <= @case && @case <= ""y"":
{
break;
}
case ""b"":
{
break;
}
}
}
}
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 TestSingleCharacterStringLiteralBecomesChar_WhenExplictCastAsync()
{
Expand Down

0 comments on commit 311e1e6

Please sign in to comment.